O que o `setq-local` faz e quando devo usá-lo?

16

Não sou muito claro sobre todas as variações de variáveis ​​locais de buffer, mesmo depois de ler todo o documento e várias postagens aqui no SX.

Aqui está um resumo dos meus entendimentos:

(defvar foo ..)declara uma variável dinâmica para o arquivo. Mas a variável (1) não é conhecida por outros arquivos, a menos que inclua também uma defvar instrução e (2) a variável é de escopo global, não local de buffer.

(make-variable-buffer-local foo)após o que foi defvardito acima, diz ao compilador e a todos os demais que a variável foo deve ser tratada como local do buffer em qualquer lugar em que for definida, quando for definida. Portanto, esse padrão é um bom estilo para declarar uma variável local de buffer, colocando as duas instruções consecutivas no arquivo.

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

Por conveniência, o (defvar-local xxx ...)formulário pode ser usado como uma linha, no lugar das duas linhas acima:

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

Uma vez declarada como acima, a variável xxx pode ser usada como qualquer outra variável nas instruções setq.

Se eu apenas quero ter uma única instância de uma variável local buffer que já é uma variável dinâmica global, eu usaria as seguintes declarações. O primeiro declara a variável dinâmica de escopo global e a segunda instrução cria apenas uma instância de uma versão local do buffer dessa variável, no buffer atual:

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

Agora, para minhas perguntas explícitas (todas as perguntas acima foram implícitas sobre se meu entendimento está correto).

Ao definir o valor das variáveis, posso usar setqou setq-local. Quando deve setq-localser usado? Por quê?

O que acontece se eu usar setq-localem vars locais de buffer local ou vars não locais de buffer?

É setq-localnecessário para uma defvar-localvariável declarada?

Em setq-localuma defvarvariável declarada normal , a transformará em uma variável local de buffer? (Em outras palavras, é de setq-localalguma forma o equivalente a uma (make-variable-local xxx)declaração?

Kevin
fonte
Obrigado pelo trabalho extra Scott. Vou colocar as citações extras daqui em diante.
Kevin
2
(setq-local VAR VALUE)é apenas uma abreviação de (set (make-local-variable VAR) VALUE), que era (e ainda é) um idioma comum.
Tirou

Respostas:

18

A maioria de suas suposições está próxima. Mencionarei alguns depois. Mas, primeiro a questão principal.

O formulário setq-localé apenas uma conveniência, é o mesmo que fazer make-local-variableseguido por setq. Se você fez um C-h f setq-localpara ver a documentação e clicou na fonte, pode ter visto isso. Foi assim que verifiquei minha primeira impressão. O código é um pouco obscuro, porém, uma vez que está otimizando coisas como o fato de make-local-variablerealmente retornar a variável em si. Usar setq-localé uma maneira de apontar a localidade em algum código, para que outras pessoas saibam que o código não pode estar jogando com o valor global da variável. Você não precisa usá-lo para acessar variáveis ​​locais. Qualquer variável pode ser feita como local do buffer e isso afetará todo o código que toca a variável (exceto se ela se esforçar para obter a cópia global setq-defaultou similar).

Essa é a resposta principal. Agora, existem algumas coisas em seu histórico que estão um pouco fora. Desde que você perguntou sobre isso, vou abordar algumas coisas.

O primeiro é "desconhecido para outros arquivos". Isso não é verdade. Qualquer código pode fazer referência a qualquer variável global (é por isso que eles são chamados "globais") sem incluir o defvar(e C-h f defvardiz isso). De fato, deve haver apenas um principal defvar(com docstring e valor padrão) para cada variável global. Um defvarcom apenas o nome da variável pode ser usado para suprimir um aviso do compilador de bytes. O Emacs usa apenas o valor do primeiro que ele vê¹ e a sequência de caracteres do último. Se houver vários defvars com valor / sequência de caracteres, eles podem ser confusos para as pessoas que leem o código. E a ordem de carregamento dos arquivos será importante.

Algumas variáveis destinam - se a ser usadas apenas como local de buffer e essas são as que são usadas defvar-local(ou as duas partes defvar/ pelas make-variable-buffer-localquais é abreviada, principalmente no código antigo antes da adição do formulário abreviado). Isso faz com que seja local de buffer em todos os buffers. Para algumas variáveis, seu uso principal não é local do buffer, mas por algum motivo você pode desejar que um buffer tenha um valor diferente. É quando você usa make-local-variable, geralmente em algum código de configuração do buffer quase nunca logo após a defvar.

E como uma coisa geral, existem dois propósitos para os defvarformulários. Uma é ter alguma documentação, para que C-h vpossa ser usada por outras pessoas para saber para que serve a variável. O segundo é declarar um valor "padrão", para que a variável seja sempre definida. A declaração do valor padrão afeta apenas a instância global (ou padrão) de uma variável e é usada apenas se essa instância ainda não estiver configurada para algo. Isso significa que se você setqincluir alguma variável e incluir o arquivo com defvar(por exemplo, a require), o defvar não alterará o valor.

Precisely Mais precisamente, defvarnão modifica o valor da variável se ela já estiver configurada (porém, ela define a doutrina); isso é feito para que o arquivo init do usuário possa executar (setq some-variable)antes que um pacote seja carregado para substituir o valor padrão do pacote.

MAPA
fonte
11
Obrigado pela resposta muito clara, isso ajuda muito. Quando usei a frase "desconhecido para outros arquivos", pensei em avisos de variáveis ​​livres, porque esses outros arquivos pensam que a variável é livre, a menos que eu adicione um defvar a eles também. Gostei da sua declaração que set-localinforma aos leitores que um var local está sendo definido. Tudo o que posso fazer para melhorar a legibilidade do meu código é bom! PS. Vou tentar clicar na fonte com mais frequência; no meu nível atual de desenvolvimento, que muitas vezes não realmente chegar a semântica ... :-)
Kevin
11
"deve haver apenas um defvarpara cada variável global" não está estritamente correto - há situações em que (defvar VARNAME) sem um valor deve ser declarado, independentemente da definição adequada (com valor inicial e, idealmente, uma doutrina) desse VARNAME.
phils
2
Observe também isso setq-locale defvar-localsó foram introduzidos no Emacs 24.3.
Phd #
11
@phils, você poderia identificar as situações de que fala, onde (defvar varname)deve ser declarado sem valor? Estou pensando que foi o que Drew sugeriu em outro tópico quando estava perguntando como se livrar de avisos de variáveis ​​livres em meus arquivos para globais que eu havia declarado em outro lugar. Eu faço isso agora. É dessa situação que você fala?
30716 Kevin
11
Evitar avisos de compilação de bytes é um dos motivos, sim (consulte C-h i g (elisp) Warning Tips). O outro é para bibliotecas que usam ligação lexical, para garantir (se necessário) que uma variável seja vinculada dinamicamente, e não lexicamente.
phils