yasnippet: ¿cómo expandir un fragmento de una cadena y reconocer los campos?

7

P: ¿cómo puedo yasnippetexpandir una cadena de texto correctamente (reconociendo los campos de fragmentos) desde el código elisp?

Un fragmento muy simple es solo una cadena de caracteres con algunos caracteres de control entremezclados. También podemos usar formularios elisp para crear fragmentos más ricos, pero no tengo claro cómo expandirlos adecuadamente.

Aquí hay un ejemplo simple que captura el problema. Estoy creando un org-modefragmento para insertar un esqueleto de enlace, que se ve así [[source][description]]. El siguiente fragmento simple produce el comportamiento deseado de "comenzar en el campo de origen, terminar en el campo de descripción":

[[$1][$0]]

Podemos mezclar el código elisp en fragmentos (envueltos en `s). Pensé que lo siguiente produciría exactamente el mismo comportamiento que la versión más simple, porque ifsimplemente se evaluará [[$1][$0]]como una cadena:

`(if t "[[$1][$0]]")`

Sin embargo, no lo hace. En cambio, actúa como si los campos $0y $1no fueran campos, finaliza el fragmento y coloca el punto después de la final ]. Entonces: ¿cómo se obtiene el comportamiento deseado cuando un formulario elisp se evalúa como una cadena de texto con yasnippetcampos incrustados en esa cadena?

EDITAR: según lo solicitado, aquí está el caso de uso particular que inspiró la pregunta. Si el portapapeles X contiene un enlace de URL, me gustaría que llene el campo de origen en el enlace de la organización ([[ fuente ] [descripción]]) y me deje en el campo de destino. Utiliza una función auxiliar ( dan-xclipboard-link-p) que devuelve la url si existe, o si no nil. Me parece que debería poder hacer algo como esto:

`(let ((link (dan-xclipboard-link-p)))
   (if link
       (concat "[[" link "][$0]]")
     "[[$1][$0]]"))`

De hecho, con una versión anterior de yasnippet, una versión de este código funcionó como lo pretendía.

EDITAR 2: Curiosamente, este fragmento funciona cuando hay un enlace en el portapapeles X, pero falla cuando no (es decir, no se reconoce $1como un campo:

[[`(or (dan-xclipboard-link-p) "$1")`][$0]]
Dan
fuente
¿Qué tal (if t (yas-insert-snippet))? ¿Puedes dar un condicional más realista para que podamos entender el caso de uso real? Esto podría ser un problema XY con soluciones más elegantes.
Vamsi
No veo cómo se yas-insert-snippetaplica aquí. En cualquier caso, un caso de uso real (que solía funcionar) ahora está en la pregunta.
Dan
@Dan No funciona porque la forma elisp se evalúa cuando se está expandiendo , en ese momento yasnippet ya ha leído el fragmento. Creo que lo que quiere hacer es crear plantillas de la plantilla en sí, no estoy seguro de que es posible con yasnippet en este punto de tiempo
Iqbal Ansari
@IqbalAnsari Tengo una forma cruda de modelar los fragmentos ellos mismos. Mira mi solución más larga. Gracias.
Vamsi

Respuestas:

11

SOLUCIÓN CORTA

Si no le importa una TABpulsación de tecla adicional , debería definir su fragmento como se muestra a continuación.

 [[${1:`(dan-xclipboard-link-p)`}][$0]]

El lisp incrustado devuelve la url del portapapeles si existe y lo establece como el texto predeterminado en el campo $1. Puede escribir sobre el enlace para cambiarlo o simplemente TAB (yas-next-field)para mover el punto $0.

SOLUCIÓN DESTACADA COMPLETA MÁS LARGA

Tengo la idea basada en el comentario de @ IqbalAnsari después de escribir la solución más corta. Puede usar el predicado de condición para elegir qué fragmento expandir. Por ejemplo, defina dos fragmentos con el mismo activador

SNIPPET 1

  # -*- mode: snippet; require-final-newline: nil -*-
  # name: org-link
  # key: [[
  # binding: direct-keybinding
  # type: snippet
  # condition: (not (dan-xclipboard-link-p))
  # --
  [[$1][$0]]

SNIPPET 2

  # -*- mode: snippet; require-final-newline: nil -*-
  # name: org-link-with-default-paste
  # key: [[
  # binding: direct-keybinding
  # type: snippet
  # condition: (dan-xclipboard-link-p)
  # --
  [[`(dan-xclipboard-link-p)`][$0]]

Ambos fragmentos tienen el mismo desencadenador, pero tienen predicados invertidos denotados por condition:. Como tal, solo uno de los fragmentos se expandirá según el valor devuelto por (dan-xclipboard-link-p). De esta manera puede posicionar el punto $0directamente.

Para responder a la pregunta general, no, no es posible que yasnippet reconozca los campos dentro de elisp, ya que analiza los bloques de elisp incrustados por separado sin preocuparse por la sintaxis de plantilla normal. Sin embargo, no estoy seguro de cómo funcionaban las versiones anteriores.

Vamsi
fuente
Genial, estaba pensando en algo en la misma línea. Aunque hubiera preferido la 'pestaña' adicional en lugar de escribir dos fragmentos. +1
Iqbal Ansari
Gracias por los pensamientos Por cierto, se me ocurrió algo similar a la solución corta y arreglé un error tipográfico con los corchetes para referencia futura. Los fragmentos condicionales son un buen toque.
Dan