Destacando variáveis ​​de shell entre aspas

13

No vim, o documento a seguir fará com que as $PWDlinhas 2 e 3 sejam coloridas de duas maneiras diferentes:

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

A primeira instância de $PWDterá uma cor diferente do restante da string em que está. Isso fornece uma indicação visual clara de que a variável será expandida, em vez de tratada como texto literal. Por outro lado, a segunda instância de $PWDserá colorida da mesma forma que o restante da string, porque as aspas simples fazem com que seja tratada como texto literal.

Existe algum modo emacs existente que forneça esse tipo de "reconhecimento de cotação de shell"?

nispio
fonte
1
Certamente, isso não seria terrivelmente difícil de adicionar sh-mode? Talvez ele possa ser adicionado ao próprio Emacs.
PythonNut

Respostas:

11

O código abaixo usa uma regra de bloqueio de fonte com uma função em vez de uma regexp, a função procura por ocorrências, $VARmas somente quando elas estão dentro de uma cadeia de caracteres entre aspas duplas. A função (syntax-ppss)é usada para determinar isso.

A regra de bloqueio de fonte usa o prependsinalizador para adicionar-se sobre o realce de string existente. (Observe que muitos pacotes usam tisso. Infelizmente, isso substitui todos os aspectos do realce existente. Por exemplo, o uso prependreterá uma cor de fundo da string (se houver) ao substituir a cor do primeiro 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)))))

Você pode chamar usar isso adicionando a última função a um gancho adequado, por exemplo:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
fonte
Isso funciona para mim, mas deixa o "$" com a sequência destacada.
erikstokes
É por design, desde que foi destacada uma variável fora de uma string. No entanto, isso pode ser facilmente alterado. Se você substituir a 2regra de bloqueio de fonte por uma regra, 0ela funcionará. (Pode ser necessário estender a regexp para incluir um final }para destacar ${FOO}adequadamente.) Esse número refere-se ao subgrupo regexp da correspondência, 0significa que a correspondência inteira deve ser destacada.
Lindydancer
Empacotou isso ao adicionar isso ao meu repositório .emacs.d, se alguém estiver interessado: github.com/moonlite/.emacs.d/blob/… @Lindydancer O GPLv3 + e você como autor nesse arquivo estão bem? (Vou enviar uma atualização, se não).
precisa
Parece bom. Eu provavelmente não teria tempo para transformá-lo em um pacote adequado. No entanto, gostaria que você soltasse meu endereço de e-mail e, em vez disso, adicione uma linha à minha página do EmacsWiki ( emacswiki.org/emacs/AndersLindgren ). Além disso, você pode remover o símbolo de direitos autorais, pois não é necessário e isso torna o código fonte não-ascii.
Lindydancer
3

Melhorei a resposta da @ Lindydancer das seguintes maneiras:

  • Inline a sh-script-extra-font-lock-is-in-double-quoted-stringfunção, pois ela foi usada apenas uma vez
  • Escapar da variável funciona.
  • As variáveis numéricas ( $10, $1, etc) estão destacados.

Quebra de 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
fonte
A [^\\\\]poderia ser escrito como [^\\], é um conjunto de caracteres que não devem ser combinados, e seu código inclui barra invertida duas vezes. Nas versões mais antigas do Emacs, é necessário usar as versões font-lock-fontify-buffermais recentes, e você deve chamar mais font-lock-flushe a chamada font-lock-fontify-bufferdo elisp está obsoleta. Meu código original seguiu isso, seu código não. De qualquer forma, pode ser uma idéia melhor migrar isso para um arquivo GitHub e participar do esforço.
precisa saber é o seguinte
@Lindydancer Não [^\\]escapa da ]? É assim que o regex funciona em Java, como eu sei.
Czipperz
@Lindydancer Parece que não, pois o ELisp não permite que você use caracteres de escape em grupos de caracteres .
Czipperz