O valor padrão da variável buffer-local não é definido até o primeiro `setq`

7

Digamos que eu defina uma variável local buffer fooe seu valor padrão seja "a":

(defvar foo "a")
(make-variable-buffer-local 'foo)
(default-value 'foo) ;; => "a"

Imediatamente após isso, eu executo o seguinte código:

(let ((foo "b"))
  (with-temp-buffer
    (message "foo: %s" foo))) ;; => "b"

O resultado é "b", que é o valor que eu defini let.

Agora, se eu usar setqpara definir a variável, execute novamente exatamente o mesmo código de antes:

(setq foo "c") ;; => "c"

(let ((foo "b"))
  (with-temp-buffer
    (message "foo: %s" foo))) ;; => "a"

O resultado é "a", que é o valor padrão agora.

A pergunta : para um buffer temporário, o valor padrão de foonão é definido até que eu use setq? E enquanto eu não usar setq, posso usar letpara alterar o valor padrão em outros buffers?

EDIT : como disse @npostavs, é isso que make-varible-buffer-localrealmente significa. Se eu make-variable-buffer-localme usar , sempre posso usá-lo setqdepois disso. Mas isso se torna realmente complicado para as variáveis ​​locais do buffer "internas", como case-fold-search. se eu, como autor do pacote, vincular- case-fold-searchme nilao exterior lete desejar usar o valor padrão (ele pode ou não ser definido pelo usuário) no with-temp-buffer, tenho que usar setqantes with-temp-bufferpara garantir que o valor padrão seja realmente sendo usado no caso do usuário não tem que setqem seu / sua init.el. Para variáveis ​​locais de buffer, provavelmente significa que setqé sempre mais seguro do que letquando queremos definir o valor. Gostaria de saber se o design ou a documentação poderiam ser melhorados.

cutejumper
fonte
Se você colocar a parte liberada dentro do corpo with-temp-buffer(em vez de antes dela), isso ajuda? with-temp-bufferé uma macro e se comporta um pouco diferente de uma função padrão. Exemplo:(with-temp-buffer (let ((foo "b")) (message "foo: %s" foo)))
lawlist
@lawlist Isso deve funcionar como esperado, o que eu entendo. Mas não consigo encontrar nenhuma documentação explicando o exemplo que eu disse na pergunta.
cutejumper
Eu provavelmente faria a pergunta - Como posso penetrar a parte do corpo de uma macro com uma variável previamente vinculada (por exemplo, com uma abordagem de back-tick / vírgula, ou lexical-let, ou talvez outra coisa)? ... Mas, não quero invadir sua pergunta ... um macro maven deve
aparecer em
11
@lawlist Não sei se isso está relacionado à macro. Na verdade, eu testo localmente usando a forma expandida de with-temp-buffer(que disse, sem macros). Eu acho que é mais como um comportamento específico para variáveis ​​locais de buffer.
cutejumper

Respostas:

6

Eu, como autor do pacote, vinculo case-fold-search- nilo ao let externo e quero usar o valor padrão (pode ou não ser definido pelo usuário) no with-temp-buffer,

Nesse caso, eu recomendaria

(let ((case-fold-search (progn (make-local-variable 'case-fold-search)
                               nil)))
  (with-temp-buffer
    ...))

O importante a ser observado aqui é que make-variable-buffer-localnão torna uma variável buffer-local! Ele apenas organiza para que ele se torne local de buffer após ser definido . Você pode usar em seu make-local-variablelugar.

(make-variable-buffer-local VARIABLE)

Make VARIABLE become buffer-local whenever it is set.

A segunda coisa a se notar é a interação letcom variáveis ​​locais buffer, de (elisp) Intro to Buffer-Local:

Aviso: Quando uma variável possui ligações locais de buffer em um ou mais buffers, letreliga a ligação atualmente em vigor. Por exemplo, se o buffer atual tiver um valor local de buffer, ele será let religado temporariamente. Se nenhuma letligação local do buffer estiver em vigor, religará o valor padrão.

Assim, no primeiro caso, você ligar o valor mundial para "b"e que aparece no buffer temporário. No segundo caso, depois de ter definido o valor local em *scratch*que "c", então você vincular o valor local para "b", mas outros buffers ainda ver o valor global "a".

npostavs
fonte
Acho que não entendi a frase Make VARIABLE become buffer-local whenever it is set.. Eu pensei que estava dizendo sempre que definimos o valor, apenas o valor do buffer-local é definido. Então, para evitar esse problema, provavelmente devemos sempre usar setqimediatamente depois make-variable-buffer-local?
cutejumper
Na verdade, eu encontro esse problema para variáveis ​​locais "internas" do buffer, como case-fold-searche fill-column. O Emacs em si não os define depois de definir essas variáveis ​​locais de buffer e exige que o usuário as defina para torná-las realmente locais de buffer. Esse design é intencional? Eu não acho que a maioria das pessoas esteja ciente disso.
cutejumper
Exigir que o usuário use make-local-variableou setqenquanto diz que essa variável é um buffer-local é realmente confuso. Mas talvez essa seja outra pergunta sobre o design.
cutejumper
Bem, se ele foi feito imediatamente como local de buffer, a questão seria como deixar vincular o valor padrão em todos os ...
npostavs
Obrigado. Ainda me sinto estranho de usar make-local-variablequando vejo que a variável já é "buffer-local" do manual. Eu esperaria que o letpoderia vincular a cópia do buffer-local. Mas isso pode ser mais adequado para ser solicitado no Emacs-devel.
cutejumper
0

Como sua solução proposta, podemos vincular isso a uma variável arbitrária interna e let, em seguida, alterar conforme desejado, permanecendo interna let, antes de voltar ao valor original.

O problema surge ao alterar o buffer interno let- devido ao escopo dinâmico, o valor das variáveis ​​locais não será transferido para o novo buffer, mesmo quando interno lexical-let.

Aqui está uma função que verifica esse problema e também define e reverte o valor padrão, que é o usado por um buffer temporário:

(defun f1 (VAR TEMP-VALUE) "
Sets VAR to TEMP-VALUE; works with local variables also." 
   (interactive)
   ;; starting value
   (insert (prin1-to-string VAR))
   (let ((var1 VAR))
     (setq VAR TEMP-VALUE)
     (when (local-variable-p 'VAR)
       (setq-default VAR TEMP-VALUE))
     ;; current buffer-local value
     (insert (prin1-to-string VAR))
     ;; value in new buffer
     (with-temp-buffer
       (message (prin1-to-string VAR)))
     ;; back to current buffer - unchanged
     (insert (prin1-to-string VAR))
     ;; revert
     (setq VAR var1)
     (when (local-variable-p 'VAR)
       (setq-default VAR var1))
   ;; back to original value
   (insert (prin1-to-string VAR)))
   ;; exit let and re-check
   (insert (prin1-to-string VAR)))

então

(f1 case-fold-search "xyz") --> t"xyz""xyz"tt

e "xyz" aparecerá no *Messages*buffer.

Outras variáveis ​​locais que vêm à mente neste contexto são default-directorye uniquify-managed...

Em resumo, você pode definir uma função como essa e usá-la por dentro let, portanto, evite o incômodo de verificar se a variável (símbolo) é local ou não:

(defun set-ld (SYM VAL) "set local (and default if applicable)"
       (interactive)
       (setq SYM VAL)
       (when (local-variable-p 'SYM)
     (setq-default SYM VAL)))
dardisco
fonte