Подсветка переменных оболочки в кавычках

13

В vim следующий документ будет $PWDокрашивать строки 2 и 3 двумя разными способами:

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

Первый экземпляр $PWDбудет отличаться по цвету от остальной строки, в которой он находится. Это дает четкую визуальную индикацию того, что переменная будет расширена, а не будет рассматриваться как буквальный текст. Напротив, второй экземпляр $PWDбудет окрашен так же, как и остальная часть строки, потому что одинарные кавычки приводят к тому, что он обрабатывается как буквальный текст.

Существуют ли какие-либо существующие режимы emacs, обеспечивающие этот тип "осведомленности о цитировании оболочки"?

nispio
источник
1
Конечно, это не будет ужасно трудно добавить sh-mode? Может быть, его можно добавить в сам Emacs.
PythonNut

Ответы:

11

В приведенном ниже коде используется правило блокировки шрифта с функцией вместо регулярного выражения, функция ищет вхождения, $VARно только когда они находятся внутри строки в двойных кавычках. Функция (syntax-ppss)используется для определения этого.

Правило font-lock использует prependфлаг, чтобы добавить себя поверх существующей подсветки строки. (Обратите внимание, что tдля этого используются многие пакеты . К сожалению, это перезаписывает все аспекты существующей подсветки. Например, при использовании prependбудет сохраняться цвет фона строки (если он есть) при замене цвета переднего плана.)

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

Вы можете вызвать использование этого, добавив последнюю функцию к подходящему хуку, например:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
источник
Это работает для меня, но оставляет «$» с подсветкой строки.
erikstokes
Это так, потому что переменная вне строки была подсвечена. Однако это можно легко изменить. Если вы замените 2правило блокировки шрифтов на, 0оно должно работать. (Возможно, вам придется расширить регулярное выражение, чтобы включить трейлинг }для ${FOO}правильного выделения .) Это число относится к подгруппе регулярного выражения соответствия, 0что означает, что все совпадение должно быть выделено.
Lindydancer
Скомпоновал это, добавляя его в мой репозиторий .emacs.d, если кому-то интересно: github.com/moonlite/.emacs.d/blob/… @Lindydancer В порядке ли GPLv3 + и вы как автор в этом файле? (Я буду публиковать обновления, если нет).
Маттиас Бенгтссон
Звучит хорошо. У меня, вероятно, не было бы времени, чтобы сделать это в надлежащей упаковке. Однако я хотел бы, чтобы вы оставили мой адрес электронной почты и вместо этого добавили строку на мою страницу EmacsWiki ( emacswiki.org/emacs/AndersLindgren ). Кроме того, вы можете удалить знак авторского права, так как он не нужен, и это делает исходный код не ascii.
Lindydancer
3

Я улучшил ответ @ Lindydancer следующими способами:

  • Подчеркнул sh-script-extra-font-lock-is-in-double-quoted-stringфункцию, так как она использовалась только один раз
  • Выход из переменной работает.
  • Числовые переменные ( $10, $1и т. Д.) Подсвечиваются.

Перерыв на код

(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
источник
Это [^\\\\]можно записать как [^\\], это набор символов, которые не должны совпадать, и ваш код содержит обратную косую черту дважды. В более старых версиях Emacs, которые вы должны использовать font-lock-fontify-buffer, в более новых версиях вы должны вызывать, font-lock-flushа вызов font-lock-fontify-bufferиз elisp считается устаревшим. Мой оригинальный код следовал этому, ваш код - нет. В любом случае, может быть, лучше перенести это в архив GitHub и объединить усилия.
Lindydancer
@Lindydancer не [^\\]ускользает от ]? Вот как я знаю регулярное выражение в Java.
Чипперз
@Lindydancer Кажется, что это не так, поскольку ELisp не позволяет вам использовать escape-символы в группах символов .
Чипперз