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"?


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)

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)))))

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.