¿Qué hace `setq-local` y cuándo debo usarlo?

16

No tengo muy claras todas las variaciones de las variables locales de búfer, incluso después de leer todo el documento y un montón de publicaciones aquí en SX.

Aquí hay un resumen de mis entendimientos:

(defvar foo ..)declara una variable dinámica para el archivo. Pero la variable es (1) desconocida para otros archivos a menos que también incluyan una defvar declaración, y (2) la variable tiene un alcance global, no un búfer local.

(make-variable-buffer-local foo)después de lo defvaranterior le dice al compilador y a todos los demás que la variable foo debe ser tratada como buffer-local en todos los lugares donde se establece, cuando se establece. Por lo tanto, este patrón es un buen estilo para declarar una variable local de búfer, colocando ambas declaraciones consecutivas en el archivo.

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

Por conveniencia, el (defvar-local xxx ...)formulario se puede usar como una línea, en lugar de las dos líneas anteriores:

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

Una vez declarada como arriba, la variable xxx se puede usar como cualquier otra variable en las instrucciones setq.

Si solo quiero tener una sola instancia de una variable local de búfer que ya sea una variable dinámica global, usaría las siguientes declaraciones. La primera declara la variable dinámica de alcance global, y la segunda declaración hace solo una instancia de una versión local de búfer de esa variable, en el búfer actual:

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

Ahora para mis preguntas explícitas (todo lo anterior fueron preguntas implícitas sobre si mi comprensión es correcta).

Al establecer el valor de las variables, puedo usar setqo setq-local. ¿Cuándo se debe setq-localusar? ¿Por qué?

¿Qué sucede si lo uso setq-localen vars locales de búfer o vars no locales de búfer?

¿Se setq-localrequiere para una defvar-localvariable declarada?

¿ setq-localEn una defvarvariable declarada normal la convertirá en una variable local de búfer? (En otras palabras, ¿es de setq-localalguna manera el equivalente de una (make-variable-local xxx)declaración?

Kevin
fuente
Gracias por el trabajo extra de Scott. Pondré en los acentos graves adicionales de aquí en adelante.
Kevin
2
(setq-local VAR VALUE)es solo una abreviatura de (set (make-local-variable VAR) VALUE), que era (y sigue siendo) un idioma común.
Dibujó el

Respuestas:

18

La mayoría de sus suposiciones son cercanas. Mencionaré algunos más tarde. Pero, primero la pregunta principal.

La forma setq-locales meramente una conveniencia, es lo mismo que hacer make-local-variableseguido de setq. Si hubiera hecho un C-h f setq-localpara ver la documentación y hecho clic en la fuente, es posible que haya visto esto. Así es como verifiqué mi primera impresión. Sin embargo, el código es un poco oscuro, ya que se está optimizando en cosas como el hecho de que en make-local-variablerealidad devuelve la variable en sí. Usar setq-locales una forma de señalar la localidad en algunos códigos, para que otros sepan que el código no puede jugar con el valor global de la variable. Usted no necesita utilizarlo para acceder a las variables locales. Cualquier variable se puede hacer local en el búfer y eso afectará a todo el código que toca la variable (excepto si se sale de su camino para obtener la copia global con setq-defaulto similar).

Esa es la respuesta principal. Ahora, hay algunas cosas en su fondo que están un poco apagadas. Como me preguntaste sobre eso, abordaré algunas cosas.

El primero es "no conocido por otros archivos". Esto no es realmente cierto. Cualquier código puede hacer referencia a cualquier variable global (es por eso que se llaman "globales") sin incluir el defvar(y lo C-h f defvardice). De hecho, solo debe haber un main defvar(con docstring y valor predeterminado) para cada variable global. Se defvarpuede usar un solo nombre de variable para suprimir una advertencia del compilador de bytes. Emacs solo usa el valor del primero que ve¹ y la cadena de documentos del último. Si hay varios defvarcorreos electrónicos con valor / cadena de documentación, pueden ser confusos para las personas que leen el código. Y el orden de carga de los archivos será importante.

Algunas variables están destinadas a usarse solo como buffer local y esas son las que usan defvar-local(o las dos partes defvar/ para make-variable-buffer-locallas que es corto, principalmente en código anterior antes de que se agregara el formulario corto). Esto hace que sea local en el búfer en todos los búferes. Para algunas variables, su uso principal no es local en el búfer, pero por alguna razón es posible que desee que un búfer tenga un valor diferente. Ahí es cuando lo usas make-local-variable, generalmente en algún código de configuración del búfer, casi nunca después de un defvar.

Y como una cosa general, los defvarformularios tienen dos propósitos . Una es tener alguna documentación, para que otros C-h vpuedan usarla para saber para qué sirve la variable. El segundo es declarar un valor "predeterminado", de modo que la variable siempre esté establecida. La declaración del valor predeterminado solo afecta a la instancia global (o predeterminada) de una variable, y solo se usa si esa instancia aún no está establecida en algo. Eso significa que si setqalguna variable y luego incluye el archivo con defvar(por ejemplo, a require), el defvar no cambiará el valor.

¹ Más precisamente, defvarno modifica el valor de la variable si ya está establecida (sin embargo, establece la cadena de documentación); esto está pensado para que el archivo init del usuario pueda hacerlo (setq some-variable)antes de cargar un paquete para anular el valor predeterminado del paquete.

MAPA
fuente
1
Gracias por la respuesta muy clara, ayuda mucho. Cuando utilicé la frase "no conocido por otros archivos", estaba pensando en advertencias de variables libres porque esos otros archivos pensarán que la variable es libre a menos que les agregue un defvar también. Me gusta su declaración que set-localle dice a los lectores que se está estableciendo una var local. ¡Todo lo que pueda hacer para mejorar la legibilidad de mi código es bueno! PD. Intentaré hacer clic en la fuente con más frecuencia; en mi nivel actual de desarrollo, a menudo no llega a la semántica ... :-)
Kevin
1
"solo debe haber uno defvarpara cada variable global" no es estrictamente correcto: hay situaciones en las que se debe declarar (defvar VARNAME) sin un valor, independientemente de la definición adecuada (con valor inicial e idealmente una cadena de documentos) de ese VARNAME.
phils
2
Tenga en cuenta también que setq-localy defvar-localsolo se introdujeron en Emacs 24.3.
phils
1
@phils, ¿podría identificar las situaciones de las que habla, dónde (defvar varname)deberían declararse sin valor? Estoy pensando que eso es lo que Drew sugirió en otro hilo cuando estaba preguntando cómo deshacerme de las advertencias de variables libres en mis archivos para los globales que había declarado en otro lugar. Hago eso ahora. ¿Es esa la situación de la que hablas?
Kevin
1
Evitar las advertencias de compilación de bytes es una razón, sí (ver C-h i g (elisp) Warning Tips). El otro es para las bibliotecas que usan enlace léxico, para asegurar (si es necesario) que una variable está unida dinámicamente en lugar de estar unida léxicamente.
phils