Destacando variables de shell entre comillas

13

En vim, el siguiente documento hará que las $PWDlíneas 2 y 3 se coloreen de dos maneras diferentes:

#/bin/sh
echo "Current Directory: $PWD"
echo 'Current Directory: $PWD'

La primera instancia de $PWDestará en un color diferente del resto de la cadena en la que se encuentra. Esto proporciona una indicación visual clara de que la variable se expandirá, en lugar de tratarse como texto literal. Por el contrario, la segunda instancia de $PWDtendrá el mismo color que el resto de la cadena, porque las comillas simples hacen que se trate como texto literal.

¿Existen modos emacs existentes que proporcionen este tipo de "conciencia de citación de shell"?

nispio
fuente
1
¿Seguramente, esto no sería terriblemente difícil de agregar sh-mode? Tal vez se pueda agregar a Emacs.
PythonNut

Respuestas:

11

El siguiente código utiliza una regla de bloqueo de fuente con una función en lugar de una expresión regular, la función busca ocurrencias de, $VARpero solo cuando están dentro de una cadena entre comillas dobles. La función (syntax-ppss)se usa para determinar esto.

La regla de bloqueo de fuente usa la prependbandera para agregarse encima del resaltado de cadena existente. (Tenga en cuenta que muchos paquetes se usan tpara esto. Desafortunadamente, esto sobrescribe todos los aspectos del resaltado existente. Por ejemplo, el uso prependretendrá un color de fondo de cadena (si lo hay) mientras reemplaza el color de primer plano).

(defun sh-script-extra-font-lock-is-in-double-quoted-string ()
  "Non-nil if point in inside a double-quoted string."
  (let ((state (syntax-ppss)))
    (eq (nth 3 state) ?\")))

(defun sh-script-extra-font-lock-match-var-in-double-quoted-string (limit)
  "Search for variables in double-quoted strings."
  (let (res)
    (while
        (and (setq res
                   (re-search-forward
                    "\\$\\({#?\\)?\\([[:alpha:]_][[:alnum:]_]*\\|[-#?@!]\\)"
                    limit t))
             (not (sh-script-extra-font-lock-is-in-double-quoted-string))))
    res))

(defvar sh-script-extra-font-lock-keywords
  '((sh-script-extra-font-lock-match-var-in-double-quoted-string
     (2 font-lock-variable-name-face prepend))))

(defun sh-script-extra-font-lock-activate ()
  (interactive)
  (font-lock-add-keywords nil sh-script-extra-font-lock-keywords)
  (if (fboundp 'font-lock-flush)
      (font-lock-flush)
    (when font-lock-mode
      (with-no-warnings
        (font-lock-fontify-buffer)))))

Puede llamar a usar esto agregando la última función a un enlace adecuado, por ejemplo:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
fuente
Esto funciona para mí, pero deja el "$" con el resaltado de cadena.
erikstokes
Es por diseño, ya que es cómo se resaltó una variable fuera de una cadena. Sin embargo, esto se puede cambiar fácilmente. Si reemplaza la 2regla en el bloqueo de fuente con una 0, debería funcionar. (Es posible que deba extender la expresión regular para incluir un final }para resaltar ${FOO}correctamente). Este número se refiere al subgrupo de expresiones regulares de la coincidencia, 0lo que significa que toda la coincidencia debe resaltarse.
Lindydancer
Empaquetado esto mientras lo agrego a mi repositorio .emacs.d si alguien está interesado: github.com/moonlite/.emacs.d/blob/… @Lindydancer ¿Está GPLv3 + y usted como autor en ese archivo? (Enviaré una actualización si no).
Mattias Bengtsson
Suena bien. Probablemente no tendría tiempo para convertirlo en un paquete adecuado. Sin embargo, me gustaría que suelte mi dirección de correo electrónico y, en su lugar, agregue una línea a mi página de EmacsWiki ( emacswiki.org/emacs/AndersLindgren ). Además, puede eliminar el signo de copyright ya que no es necesario y hace que el código fuente no sea ascii.
Lindydancer
3

Mejoré la respuesta de @ Lindydancer de las siguientes maneras:

  • Inline la sh-script-extra-font-lock-is-in-double-quoted-stringfunción, ya que solo se usó una vez
  • Escapar de la variable funciona.
  • Las variables numéricas ( $10, $1, etc) se resaltan.

Romper para el código

(defun sh-script-extra-font-lock-match-var-in-double-quoted-string (limit)
  "Search for variables in double-quoted strings."
  (let (res)
    (while
        (and (setq res (progn (if (eq (get-byte) ?$) (backward-char))
                              (re-search-forward
                               "[^\\]\\$\\({#?\\)?\\([[:alpha:]_][[:alnum:]_]*\\|[-#?@!]\\|[[:digit:]]+\\)"
                               limit t)))
             (not (eq (nth 3 (syntax-ppss)) ?\")))) res))

(defvar sh-script-extra-font-lock-keywords
  '((sh-script-extra-font-lock-match-var-in-double-quoted-string
     (2 font-lock-variable-name-face prepend))))

(defun sh-script-extra-font-lock-activate ()
  (interactive)
  (font-lock-add-keywords nil sh-script-extra-font-lock-keywords)
  (if (fboundp 'font-lock-flush)
      (font-lock-flush)
    (when font-lock-mode (with-no-warnings (font-lock-fontify-buffer)))))
Czipperz
fuente
El [^\\\\]podría escribirse como [^\\], es un conjunto de caracteres que no deben coincidir, y su código incluye la barra invertida dos veces. En versiones anteriores de Emacs se debe usar font-lock-fontify-buffer, en las nuevas se supone que debe llamar font-lock-flushy las llamadas font-lock-fontify-bufferdesde elisp están en desuso. Mi código original siguió esto, su código no. De todos modos, podría ser una mejor idea migrar esto a un archivo GitHub y unirse al esfuerzo.
Lindydancer
@Lindydancer ¿No [^\\]escapa del ]? Así es como funciona la expresión regular en Java, como sé.
Czipperz
@Lindydancer Parece que no, ya que ELisp no te permite usar caracteres de escape en grupos de personajes .
Czipperz