El valor predeterminado de la variable local del búfer no se establece hasta el primer `setq`

7

Digamos que defino una variable local de búfer foo, y su valor predeterminado es "a":

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

Inmediatamente después de esto ejecuto el siguiente código:

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

El resultado es "b", que es el valor que establecí let.

Ahora, si uso setqpara configurar la variable, vuelva a ejecutar exactamente el mismo código que antes:

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

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

El resultado es "a", que es el valor predeterminado ahora.

La pregunta : por un buffer temporal, el valor por defecto de foono se establece hasta que utilizo setq? ¿Y mientras no lo use setq, puedo usarlo letpara cambiar el valor predeterminado en otros búferes?

EDITAR : como dijo @npostavs, esto es lo que make-varible-buffer-localrealmente significa. Si me uso make-variable-buffer-local, siempre puedo usar setqdespués de eso. Pero esto se vuelve realmente complicado para las variables locales de búfer "incorporadas" como case-fold-search. si yo, como autor del paquete, se unen case-fold-searcha nilen el exterior let, y quiero usar el valor por defecto (que puede o no puede ser establecido por el usuario) en el with-temp-buffer, tengo que usar setqantes with-temp-bufferpara asegurarse de que el valor por defecto es en realidad siendo utilizado en caso de que el usuario no tenga eso setqen su / ella init.el. Para las variables locales de búfer, probablemente significa setqque siempre es más seguro que letcuando queremos establecer el valor. Me pregunto si el diseño o la documentación podrían mejorarse.

lindo puente
fuente
Si coloca la parte ligada dentro del cuerpo de with-temp-buffer(en lugar de antes), ¿eso ayuda? with-temp-bufferes una macro y se comporta de manera un poco diferente a una función estándar. Por ejemplo,(with-temp-buffer (let ((foo "b")) (message "foo: %s" foo)))
lawlist
@lawlist Eso debería funcionar como se esperaba, lo cual entiendo. Pero no puedo encontrar ninguna documentación que explique el ejemplo que dije en la pregunta.
cutejumper
Probablemente haría la pregunta: ¿cómo puedo penetrar en la parte del cuerpo de una macro con una variable previamente limitada (por ejemplo, con un enfoque de retroceso / coma, o lexical-let, o tal vez algo más)? ... Pero, no quiero secuestrar a su pregunta ... un experto macro debe ser a lo largo de poco explicarlo todo ... :)
lawlist
1
@lawlist No sé si esto está relacionado con la macro. De hecho, lo pruebo localmente usando la forma expandida de with-temp-buffer(dicho esto, sin macros). Creo que es más como un comportamiento específico para las variables locales de búfer.
cutejumper

Respuestas:

6

Yo, como autor del paquete, me ato case-fold-searchal nillet externo, y quiero usar el valor predeterminado (el usuario puede establecerlo o no) en el with-temp-buffer,

En este caso recomendaría

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

¡Lo importante a tener en cuenta aquí es que make-variable-buffer-localno crea una variable local de búfer! Solo se arregla para que se convierta en buffer local después de que se establece . Es posible que desee utilizar make-local-variableen su lugar.

(make-variable-buffer-local VARIABLE)

Make VARIABLE become buffer-local whenever it is set.

La segunda cosa a tener en cuenta es la interacción de letcon las variables locales de búfer, de (elisp) Intro to Buffer-Local:

Advertencia: cuando una variable tiene enlaces locales de búfer en uno o más búferes, letvuelve a enlazar el enlace que está actualmente vigente. Por ejemplo, si el búfer actual tiene un valor local de búfer, lo let vuelve a vincular temporalmente. Si no hay enlaces locales de búfer vigentes, letvuelve a unir el valor predeterminado.

Así, en el primer caso que enlaza el valor mundial de "b"y que se muestra en el buffer temporal. En el segundo caso, después de haber ajustado el valor local en el *scratch*que "c", a continuación, se unen el valor local a "b", pero otros tampones todavía ven el valor global de "a".

npostavs
fuente
Supongo que no entendí la oración Make VARIABLE become buffer-local whenever it is set.. Pensé que estaba diciendo que cada vez que establecemos el valor, solo se establece el valor local del búfer. Entonces, para evitar tal problema, ¿deberíamos usarlo siempre setqinmediatamente después make-variable-buffer-local?
cutejumper
De hecho, me encuentro con este problema para las variables locales de búfer "integradas" como case-fold-searchy fill-column. Emacs en sí no los configura después de definir estas variables locales de búfer, y requiere que el usuario las configure para que sean realmente locales de búfer. ¿Es este diseño intencional? No creo que la mayoría de la gente lo sepa.
cutejumper
Requerir que el usuario lo use make-local-variableo setqmientras dice que esta variable es un buffer local es realmente confuso. Pero tal vez esa sea otra pregunta con respecto al diseño.
cutejumper
Bueno, si se hizo memoria intermedia inmediatamente local, entonces la pregunta sería cómo dejar-se unen el valor por defecto en todos los ...
npostavs
Gracias. Todavía me siento extraño por usarlo make-local-variablecuando veo que la variable ya es "local en el búfer" del manual. Esperaría que letpudiera enlazar la copia local del búfer. Pero esto puede ser más adecuado para preguntar en Emacs-devel.
cutejumper
0

Al igual que su solución propuesta, podemos vincular esto a una variable arbitraria dentro let, luego cambiarlo como desee mientras permanece dentro let, antes de volver al valor original.

El problema surge cuando se cambia el búfer en el interior let: debido al alcance dinámico, el valor de las variables locales no se 'transferirá' al nuevo búfer, incluso dentro lexical-let.

Aquí hay una función que comprueba este problema y también establece y revierte el valor predeterminado, que es el utilizado por un búfer temporal:

(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)))

entonces

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

y "xyz" aparecerá en el *Messages*búfer.

Otras variables locales que vienen a la mente en este contexto son default-directoryy uniquify-managed...

En resumen, podría definir una función como esta y usarla en su interior let, así que ahorre la molestia de verificar si la variable (símbolo) es local o no:

(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
fuente