Estoy modificando un código de elisp de linum.el:
(custom-set-variables '(linum-format 'dynamic))
(defadvice linum-update-window (around linum-dynamic activate)
(let* ((w (length (number-to-string
(+ (count-lines (point-min) (point-max)) 1))))
(linum-format (concat " %" (number-to-string w) "d ")))
ad-do-it))
Pude corregir un error donde la sangría estaba desactivada modificando (count-lines (point-min) (point-max))
a (+ (count-lines (point-min) (point-max)) 1)
. Eso fue fácil.
Pero ahora quiero modificarlo para que el ancho mínimo sea 2 agregando un if-condicional donde (concat " %" (number-to-string w) "2d ")
si el número de líneas es <10.
¡Esto debería ser fácil! Agregue un condicional y copie / pegue el concat. Pedazo de pastel, ¿verdad? Quiero decir, sé lo que se supone que debo hacer, pero rara vez toco elisp y siempre me siento intimidado cuando tengo que modificar algo con muchos paréntesis.
El estilo "correcto", por lo que entiendo, es estructurar el código basado en la sangría y envolver el paréntesis final al final de una línea en lugar de hacerlo solo. Al provenir de otros lenguajes de estilo 'C', lucho con leer y escribir código de esta manera. Entonces mi pregunta es: ¿qué estoy haciendo mal? ¿Cómo se supone que edite elisp y navegue por el código de modo que no tenga que sentarme allí y contar cada paréntesis?
Cuando trabajo con algo en elisp que se vuelve demasiado profundo, tengo que cerrar la puerta, cerrar las persianas y comenzar a colocar el paréntesis al estilo K y R para que no solo pueda leer sino modificar lo maldito sin enloquecer.
Obviamente estoy haciendo todo esto mal. ¿Cómo puedo tocar elisp así sin miedo?
Tenga en cuenta que mi pregunta es cómo navegar y editar elisp, no como una pregunta sobre el estilo. Ya estoy usando lo siguiente como guía de estilo: https://github.com/bbatsov/emacs-lisp-style-guide
Actualizaciones:
Cómo formatear correctamente elisp antes de avergonzarse en emacs.stackexchange:
Marque su elisp y actúe M-x indent-region
.
La solución del problema:
Para aquellos que desean saber cómo realizar una justificación correcta para linum con un ancho mínimo de dos, esta es la solución:
(defadvice linum-update-window (around linum-dynamic activate)
(let* ((w (length (number-to-string
(+ (count-lines (point-min) (point-max)) 1))))
(linum-format (if (> w 1)
(concat " %" (number-to-string w) "d ")
" %2d ")))
ad-do-it))
fuente
%2d
porque una vez que el ancho se desplaza a 3 o más caracteres, se mueve de justificado a la derecha a justificado a la izquierda.highlight-parentheses
;rainbow-delimiters
; etc. Aquí está mi propia versión simplificadahighlight-parentheses
que permite desplazarse sin quitar los paréntesis que se colorearon por última vez: stackoverflow.com/a/23998965/2112489 En el futuro, es una pregunta por cliente / hilo.Respuestas:
Hay varios paquetes de complementos que pueden ayudar, como paredit, smartparens y lispy. Estos paquetes hacen que sea más fácil navegar y manipular el código lisp para que pueda pensar en expresiones s y dejar que el editor se preocupe por equilibrar los parens.
Emacs también tiene muchos comandos incorporados para tratar sexps y listas que vale la pena aprender y que pueden ayudarlo a acostumbrarse más a la estructura del código lisp. Estos generalmente están vinculados con un
C-M-
prefijo, como:C-M-f
/C-M-b
para avanzar / retroceder por sexpC-M-n
/C-M-p
para avanzar / retroceder por listaC-M-u
/C-M-d
para subir / bajar un nivelC-M-t
intercambiar los dos sexps alrededor del puntoC-M-k
matar a un sexpC-M-SPC
para marcar un sexpC-M-q
volver a sangrar un sexpEl código de ejemplo que proporcionó podría ser más claro si corrige la sangría: coloque el punto al comienzo del
(defadvice...
y presioneC-M-q
para volver a sangrar la expresión completa. Para reemplazar el(concat...
, comenzaría colocando un punto al comienzo de ese sexp y presionandoC-M-o
para dividir la línea mientras se preserva la sangría. Luego agregue su(if...
, y use los comandos anteriores para saltar al final de una lista para agregar otro par de cierre, volver al principio para volver a sangrar, etc.También es posible que desee activar
show-paren-mode
, que resaltará el par correspondiente cuando el punto esté al principio o al final de una lista. Es posible que prefiera resaltar toda la expresión en lugar del par coincidente, así que intente personalizarloshow-paren-style
.Como @Tyler mencionó en los comentarios sobre otra respuesta, debería echar un vistazo al manual para obtener más información sobre estos y los comandos relacionados.
fuente
C-M-q
se puede invocar con un argumento prefijo (C-u C-M-q
) para imprimir la expresión en el punto. Esto dividirá todo en líneas separadas y sangría para que el anidamiento sea muy obvio. Por lo general, no desea que su código lisp se vea así, pero podría ser útil comprender un poco de código sin tener que completar manualmente K&R. (Y siempre puedesC-x u
deshacer).Una buena forma de editar LISP es manipulando el AST, en lugar de los caracteres o líneas individuales. He estado haciendo eso con mi paquete lispy que comencé hace 2 años. Creo que en realidad puede tomar bastante práctica aprender cómo hacerlo bien, pero incluso el uso de lo básico ya debería ayudarlo.
Puedo explicar cómo haría para hacer este cambio:
Paso 1
La posición inicial suele ser tener el punto antes del sexp de nivel superior. Aquí
|
está el punto.Querrás tener el código correctamente sangrado, así que presiona iuna vez. Lo volverá a sangrar bien.
Paso 2
Deberá marcar
"d "
con una región, ya que un objeto que puede manipularse enlispy
es una lista, que no necesita ser marcada, o una secuencia de símbolos o listas que deben marcarse con una región.Presione atpara hacerlo. El acomando permite marcar cualquier símbolo individual dentro de la lista principal y tes el índice de este símbolo en particular.
Paso 3
()
, presione (.if
.()
.> w 10
.Etapa 4
Para clonar la cadena:
Si desea desactivar la región de una manera elegante y poner el punto al comienzo de la cadena, puede presionar idm:
O podrías hacer m C-b C-b C-ben este caso, o C-g C-b C-b C-b. Creo que idmes mejor, ya que es lo mismo independientemente de la longitud de la cadena. Creo C-x C-x C-f C-g que también funcionaría independientemente de la longitud de la cadena.
Finalmente, inserte
2
para obtener el código final:Resumen
La secuencia completa fue: at( C-f,
if
, (,> w 10
, C-f C-m M-m cidm,2
.Tenga en cuenta que si cuenta la inserción del código, las únicas claves no relacionadas con la manipulación AST fueron dos C-fy una C-m.
fuente
Te acostumbrarás con el tiempo, pero, por supuesto, hay muchas cosas que puedes hacer para acelerarlo:
Sangría
Hay un método para esta locura. En lisp puedes hacer esto:
Suponga que
1
es realmente una expresión grande y desea sangrarla en su propia línea.Esto es engañoso.
1
¡está sangrado solo en una ranura, a pesar de que son tres niveles completos de anidamiento más altos! Mejor decirlo por su padre.Si usamos este esquema en su propio código, obtenemos esto:
Tenga en cuenta la fuerte correlación entre la sangría y el nivel de anidamiento. Las líneas con un mayor nivel de anidamiento siempre tienen sangría más que las líneas con menos.
Esto solo ayudará mucho. Puede ver fácilmente la "forma" del código, y la mayoría de los otros idiomas lo han entrenado para asociar sangría con bloques y bloques con alguna forma de anidamiento (ya sea explícito o implícito).
Como ejercicio, eliminé los paréntesis no esenciales del código. Debería poder leer el código y proporcionar los paréntesis faltantes, dado el conocimiento básico de Elisp (es decir, qué cosas son funciones y valores, y la estructura de
let*
).fuente
newline-and-indent
para emacs-lisp-mode y lisp-interactive-mode resuelve su primer ejemplo, PythonNut. Y TAB hará el trabajo el resto del tiempo.Estoy incluyendo esto como una respuesta diferente.
A veces, la sangría te fallará. Su recurso, entonces, es usar algo como
rainbow-delimiters
:Esto hace que el nivel de anidación de cualquier par sea explícito y escaneado fácilmente.
Sin embargo, hay un problema bastante grande: los padres distraen ridículamente. Hay un equilibrio de énfasis aquí.
rainbow-delimiters
¡normalmente pondrá mucho énfasis en los parens a expensas del resto de su código! Pero si tonifica el arco iris hacia abajo, se volverá progresivamente más difícil de escanear.Si puede encontrar un conjunto de caras que equilibre esto bien, entonces úselo.
Dicho esto, una solución (la que me hace reír a carcajadas) es tener dos pares de caras y cambiar entre ellas sobre la marcha. Cuando haces otras cosas, las caras se diluyen y se desvanecen en el fondo:
Pero cuando pones el punto en un par, golpea a los parens para que puedas ver los niveles de anidación:
Y aquí está el código para hacer eso:
fuente
Si sangra su código, entonces realmente necesita sangrarlo correctamente. Los desarrolladores de Lisp tienen expectativas de cómo debería ser una sangría adecuada. Esto no significa que el código se vea igual. Todavía hay diferentes formas de formatear el código.
Esperaría que la sangría muestre contención de alguna manera. Pero no en tu código.
Por defecto, su código puede tener sangría como esta. Otras respuestas describen cómo puede hacerlo con la ayuda de Emacs:
Esperaría que las
let
variables enlazadas comiencen en la misma columna vertical. También esperaría que el cuerpo dellet
es menos sangrado. Aprendemos cómo selet
debe sangrar a. Una vez que entrenas un poco, es un patrón visual que es fácil de reconocer.Los principales bloques de construcción visuales en el código son
defadvice
,let*
y un montón de llamadas a funciones. Eldef
en el nombre me dice que es una definición seguida de un nombre, una lista de argumentos y un cuerpo.Por eso me gusta ver
El
let*
sería:,let*
una lista de enlaces y un cuerpo.Por eso me gusta ver:
Más detallado:
Para una llamada de función me gusta ver:
o
o
El que se use depende de la duración de los argumentos. No queremos hacer líneas demasiado largas y cada argumento en su propia línea nos permite tener más estructura.
Por lo tanto, debe escribir su código utilizando esos patrones y debe asegurarse de que el lector pueda identificar fácilmente esos patrones en su código.
Los paréntesis deben encerrar directamente sus construcciones.
Evite los ejemplos de espacios en blanco:
o
o
En Lisp, los paréntesis no tienen una función sintáctica del lenguaje, son parte de la sintaxis de la estructura de datos de la lista (expresiones-s). Por lo tanto, escribimos listas y formateamos las listas. Para formatear el contenido de la lista, utilizamos el conocimiento sobre el lenguaje de programación Lisp. Una
let
expresión debe tener un formato diferente al de una llamada a función. Aún así, ambas son listas y los paréntesis deben encerrar directamente las listas. Hay algunos ejemplos en los que tiene sentido tener un paréntesis único en una línea, pero lo usa raramente.Usa los paréntesis para ayudarte a encontrar los límites de la expresión. Permita que lo ayuden a moverse de una lista a otra, editar listas, cortar y copiar listas, reemplazar listas, sangrar / formatear listas, seleccionar listas, ...
fuente
Para obtener ayuda básica con sangría, use TAB y "CMq" (
indent-pp-sexp
); enlazar "Cm"newline-and-indent
le ahorrará tener que presionar TAB después de una nueva línea.Puede navegar por sexp con "CM-left / right / up / down / home / end", que es muy útil.
Si le preocupa mantener el equilibrio entre paréntesis, use algo como smartparens (es un poco más indulgente ese crédito , que inicialmente puede parecer una camisa de fuerza). También viene con muchos enlaces para navegar y editar a nivel de sexp.
Por último, para resaltar parens, comience activando la opción (Menú Opciones-> Resaltar paréntesis coincidentes, o
show-paren-mode
). También puede usar uno de los paquetes mencionados por lawlist en su comentario, como "resaltar-paréntesis" o "delimitadores de arcoíris ".fuente
Como una adición a lo que otros han dado como respuestas, aquí están mis 2 centavos:
emacs-lisp-mode
es esencial.blink-matching-paren
está activado (nonil
) de forma predeterminada. Déjalo así.show-paren-mode
, para resaltar el par izquierdo que corresponde al par derecho antes del cursor.Yo no usar la asociación eléctrico o arco iris de cualquier cosa o cualquier otro "ayuda". Para mí eso es una molestia, no una ayuda.
En particular,
electric-pair-mode
es, para mí, una molestia inconveniente. Pero a algunas personas les encanta. E imagino que podría ser útil en otros contextos de emparejamiento además de los paréntesis de Lisp (me imagino que sí, pero tampoco estoy convencido de tales casos de uso).Encontrarás lo que funciona mejor para ti. Las cosas principales que quiero decir son: (1) la sangría automática es su amigo, ya que es una forma de detectar el paréntesis izquierdo que coincide con el par derecho antes del punto.
En particular, ver lo que la sangría automática hace por usted le enseña de inmediato que nunca más querrá poner a los padres correctos en una línea por sí mismos. Ese estilo de codificación, que es omnipresente en otros idiomas, es simplemente ruidoso.
Además de la sangría automática con la que te encuentras
RET
y te sientesTAB
cómodo usandoC-M-q
. (UsoC-h k
deemacs-lisp-mode
averiguar lo que hacen estas teclas.)fuente
Noté que algunas personas ya mencionaron delimitadores del arco iris, ese también es mi modo favorito cuando edito el código lisp.
En realidad, me encantan los paréntesis, lo mejor de Lisp son esos paréntesis, es divertido tratar con ellos si sabes cómo:
instale evil-mode y su plugin evil-surround, para que pueda editar los paréntesis fácilmente, por ejemplo, al presionar
ci(
se eliminará todo el contenido()
,va(
se seleccionará la cosa envuelta por "()" y el "()". y puedes presionar%
para saltar entre los paréntesis coincidentesuse flycheck o flymake, los paréntesis sin igual se subrayarán en tiempo real
fuente
La dificultad aquí es que desea agregar un código antes y después de cierto
sexp
. En esta situación, el sexp es(concat " %" (number-to-string w) "d ")
. Desea insertar la instrucción if(if (> w 1)
antes y la instrucción else" %2d ")
después.Una parte importante de la dificultad es aislar esto
sexp
, personalmente utilizo el siguiente comando para aislar unsexp
Simplemente coloque el cursor al principio de
(concat " %" (number-to-string w) "d ")
, es decir, al principio(
, luego aplique lo anteriorisolate-sexp
. Tu obtienesLuego agregue el código apropiado antes y después de esto
sexp
, es decirY voilá. El código obtenido de esta manera quizás no sea hermoso, pero la posibilidad de perderse es menor.
fuente