Que fait `setq-local` et quand dois-je l'utiliser?

16

Je ne suis pas tout à fait clair sur toutes les variations des variables locales du tampon, même après avoir lu tous les documents et un tas de publications ici sur SX.

Voici un résumé de mes compréhensions:

(defvar foo ..)déclare une variable dynamique pour le fichier. Mais la variable n'est (1) pas connue des autres fichiers à moins qu'ils n'incluent également une defvar instruction, et (2) la variable a une portée globale, pas un tampon local.

(make-variable-buffer-local foo)après ce qui defvarprécède, il indique au compilateur et à tous les autres que la variable foo doit être traitée comme tampon local partout où elle est définie, lorsqu'elle est définie. Ce modèle est donc un bon style pour déclarer une variable locale de tampon, en mettant les deux instructions dos à dos dans le fichier.

(defvar xxx ...)                    ;declare xxx with global scope
(make-variable-buffer-local 'xxx)   ;but now make it buffer-local everywhere

Pour plus de commodité, le (defvar-local xxx ...)formulaire peut être utilisé sur une seule ligne, à la place des deux lignes ci-dessus:

(defvar-local xxx ...)       ;make xxx buffer local everywhere

Une fois déclarée comme ci-dessus, la variable xxx peut être utilisée comme n'importe quelle autre variable dans les instructions setq.

Si je veux juste avoir une seule instance d'une variable tampon locale qui est déjà une variable dynamique globale, j'utiliserais les déclarations suivantes. La première déclare la variable dynamique de portée globale, et la deuxième instruction crée une seule instance d'une version tampon locale de cette variable, dans le tampon actuel:

(defvar xxx ...)             ;declare xxx with global scope
(make-local-variable 'xxx)   ;make xxx local in this buffer only

Maintenant pour mes questions explicites (toutes les questions ci-dessus étaient des questions implicites pour savoir si ma compréhension est correcte).

Lors de la définition de la valeur des variables, je peux utiliser setqou setq-local. Quand faut- setq-localil l'utiliser? Pourquoi?

Que se passe-t-il si j'utilise setq-localdes vars tampons locaux ou des vars non tampons locaux?

Est setq-localrequis pour une defvar-localvariable déclarée?

Est-ce setq-localqu'une defvarvariable déclarée normale la transformera en une variable locale tampon? (En d'autres termes, est-ce en setq-localquelque sorte l'équivalent d'une (make-variable-local xxx)déclaration?

Kevin
la source
Merci pour le travail supplémentaire Scott. Je mettrai les citations supplémentaires à partir d'ici.
Kevin
2
(setq-local VAR VALUE)est juste un raccourci pour (set (make-local-variable VAR) VALUE), qui était (et est toujours) un idiome commun.
Drew

Réponses:

18

La plupart de vos hypothèses sont proches. J'en mentionnerai quelques-uns plus tard. Mais, d'abord la question principale.

Le formulaire setq-localest simplement une commodité, c'est la même chose que faire make-local-variablesuivi de setq. Si vous aviez fait un C-h f setq-localpour voir la documentation et cliqué sur la source, vous avez peut-être vu cela. C'est ainsi que j'ai vérifié ma première impression. Le code est un peu obscur, cependant, car il optimise des choses comme le fait qu'il make-local-variableretourne réellement la variable elle-même. L'utilisation setq-localest un moyen de souligner le caractère local d'un code, afin que d'autres sachent que le code ne peut pas jouer avec la valeur globale de la variable. Vous n'avez pas besoin de l'utiliser pour accéder aux variables locales. N'importe quelle variable peut être mise en mémoire tampon locale et cela affectera tout le code qui touche la variable (sauf s'il fait tout son possible pour obtenir la copie globale avec setq-defaultou similaire).

Voilà la réponse principale. Maintenant, il y a quelques choses dans votre fond qui sont un peu décalées. Puisque vous avez posé des questions à ce sujet, je vais aborder certaines choses.

Le premier est "inconnu des autres fichiers". Ce n'est pas vraiment vrai. Tout code peut référencer n'importe quelle variable globale (c'est pourquoi elles sont appelées "globales") sans inclure le defvar(et le C-h f defvardit). En fait, il ne devrait y avoir qu'un seul main defvar(avec docstring et valeur par défaut) pour chaque variable globale. Un defvaravec juste le nom de la variable peut être utilisé pour supprimer un avertissement du compilateur d'octets. Emacs utilise uniquement la valeur du premier qu'il voit¹ et la docstring du dernier. S'il y a plusieurs defvars avec valeur / docstring, ils peuvent être déroutants pour les personnes qui lisent le code. Et l'ordre de chargement des fichiers importera.

Certaines variables sont destinées à être utilisées uniquement en tant que tampon local et ce sont celles qui utilisent defvar-local(ou les deux parties defvar/ pour make-variable-buffer-locallesquelles il est abrégé, principalement dans un code plus ancien avant l'ajout de la forme courte). Cela en fait un tampon local dans tous les tampons. Pour certaines variables, leur utilisation principale n'est pas locale au tampon, mais pour une raison quelconque, vous souhaiterez peut-être qu'un tampon ait une valeur différente. C'est à ce moment que vous utilisez make-local-variable, généralement dans un code de configuration de tampon presque jamais juste après a defvar.

Et en général, les defvarformulaires ont deux objectifs . La première consiste à avoir une documentation, qui C-h vpeut être utilisée par d'autres pour savoir à quoi sert la variable. La seconde consiste à déclarer une valeur "par défaut", afin que la variable soit toujours définie. La déclaration de la valeur par défaut affecte uniquement l'instance globale (ou par défaut) d'une variable et n'est utilisée que si cette instance n'est pas déjà définie sur quelque chose. Cela signifie que si vous avez setqune variable et que vous incluez ensuite le fichier avec defvar(par exemple via a require), le defvar ne changera pas la valeur.

¹ Plus précisément, defvarne modifie pas la valeur de la variable si elle est déjà définie (elle définit cependant la docstring); cela signifie que le fichier init de l'utilisateur peut faire (setq some-variable)avant le chargement d'un package pour remplacer la valeur par défaut du package.

CARTE
la source
1
Merci pour la réponse très claire, ça aide beaucoup. Lorsque j'ai utilisé l'expression «inconnu des autres fichiers», je pensais aux avertissements de variables libres, car ces autres fichiers penseront que la variable est libre, à moins que je ne leur ajoute également un defvar. J'aime votre déclaration qui set-localdit aux lecteurs qu'une variable locale est définie. Tout ce que je peux faire pour améliorer la lisibilité de mon code est bon! PS. J'essaierai de cliquer sur la source plus souvent; à mon niveau de développement actuel, ça n'entre pas vraiment dans la sémantique ... :-)
Kevin
1
"il ne devrait y en avoir qu'une defvarpour chaque variable globale" n'est pas strictement correct - il y a des situations où (defvar VARNAME) sans valeur doit être déclarée, indépendamment de la bonne définition (avec la valeur initiale et idéalement une docstring) de ce VARNAME.
phils
2
Notez également que setq-localet defvar-localn'ont été introduits que dans Emacs 24.3.
phils
1
@phils, pourriez-vous s'il vous plaît identifier les situations dont vous parlez, où (defvar varname)doivent être déclarées sans valeur? Je pense que c'est ce que Drew a suggéré dans un autre fil quand je demandais comment se débarrasser des avertissements de variables gratuits dans mes fichiers pour les globaux que j'avais déclarés ailleurs. Je fais ça maintenant. Est-ce la situation dont vous parlez?
Kevin
1
Éviter les avertissements de compilation d'octets est une des raisons, oui (voir C-h i g (elisp) Warning Tips). L'autre est pour les bibliothèques utilisant la liaison lexicale, pour garantir (si nécessaire) qu'une variable est liée dynamiquement plutôt que liée lexicalement.
phils