Hervorheben von Shell-Variablen in Anführungszeichen

13

In vim bewirkt das folgende Dokument, dass die $PWDZeilen 2 und 3 auf zwei verschiedene Arten gefärbt werden:

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

Die erste Instanz von hat $PWDeine andere Farbe als der Rest der Zeichenfolge, in der sie sich befindet. Dies gibt einen klaren visuellen Hinweis darauf, dass die Variable erweitert wird, anstatt als wörtlicher Text behandelt zu werden. Im Gegensatz dazu wird die zweite Instanz von $PWDgenauso gefärbt wie der Rest der Zeichenfolge, da sie durch einfache Anführungszeichen als wörtlicher Text behandelt wird.

Gibt es irgendwelche Emacs-Modi, die diese Art von "Shell-Quoting-Awareness" bieten?

Nispio
quelle
1
Sicherlich wäre das nicht furchtbar schwer hinzuzufügen sh-mode? Vielleicht kann es zu Emacs selbst hinzugefügt werden.
PythonNut

Antworten:

11

Der folgende Code verwendet eine Schriftsperrregel mit einer Funktion anstelle eines regulären Ausdrucks, wobei die Funktion $VARnur dann nach Vorkommen sucht, wenn sie sich in einer Zeichenfolge in doppelten Anführungszeichen befinden. Die Funktion (syntax-ppss)wird verwendet, um dies zu bestimmen.

Die Schriftsperrregel verwendet das prependFlag, um sich über die vorhandene Zeichenfolgenhervorhebung zu setzen. (Beachten Sie, dass dies in vielen Paketen verwendet twird. Leider werden dadurch alle Aspekte der vorhandenen Hervorhebung überschrieben. Wenn Sie beispielsweise verwenden, prependwird eine Zeichenfolgenhintergrundfarbe beibehalten (sofern vorhanden), während die Vordergrundfarbe ersetzt wird.)

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

Sie können use this aufrufen, indem Sie die letzte Funktion einem geeigneten Hook hinzufügen, zum Beispiel:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
quelle
Dies funktioniert bei mir, lässt aber das "$" mit der hervorgehobenen Zeichenfolge.
erikstokes
Es ist beabsichtigt, seitdem eine Variable außerhalb eines Strings hervorgehoben wurde. Dies kann jedoch leicht geändert werden. Wenn Sie die 2in der Schriftsperre enthaltene Regel durch eine ersetzen 0, sollte dies funktionieren. (Möglicherweise müssen Sie den regulären Ausdruck so erweitern, dass er einen abschließenden Text enthält }, damit er ${FOO}richtig hervorgehoben wird.) Diese Zahl bezieht sich auf die reguläre Ausdruck-Untergruppe der Übereinstimmung, dh 0, die gesamte Übereinstimmung sollte hervorgehoben werden.
Lindydancer
Packte dies zusammen und fügte es meinem .emacs.d-Repository hinzu, wenn jemand daran interessiert ist: github.com/moonlite/.emacs.d/blob/… @Lindydancer Ist GPLv3 + und Sie als Autor in dieser Datei in Ordnung? (Ich werde ein Update pushen, wenn nicht).
Mattias Bengtsson,
Hört sich gut an. Ich würde wahrscheinlich nicht die Zeit brauchen, um es zu einem richtigen Paket zu machen. Ich möchte jedoch, dass Sie meine E-Mail-Adresse löschen und stattdessen eine Zeile zu meiner EmacsWiki-Seite hinzufügen ( emacswiki.org/emacs/AndersLindgren ). Sie können auch das Copyright-Zeichen entfernen, da es nicht erforderlich ist und der Quellcode nicht mehr ASCII-konform ist.
Lindydancer
3

Ich habe die Antwort von @ Lindydancer folgendermaßen verbessert:

  • Inline die sh-script-extra-font-lock-is-in-double-quoted-stringFunktion, da sie nur einmal verwendet wurde
  • Das Escapieren der Variablen funktioniert.
  • Numerische Variablen ( $10, $1usw.) werden hervorgehoben.

Pause für Code

(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
quelle
Das [^\\\\]könnte so geschrieben werden [^\\], es ist ein Satz von Zeichen, die nicht übereinstimmen sollten, und Ihr Code enthält zweimal einen Backslash. In älteren Emacs-Versionen muss man verwenden font-lock-fontify-buffer, in neueren soll man anrufen font-lock-flushund font-lock-fontify-bufferaus elisp anrufen ist veraltet. Mein ursprünglicher Code folgte diesem, Ihr Code nicht. Auf jeden Fall ist es möglicherweise eine bessere Idee, dies in ein GitHub-Archiv zu migrieren und sich den Bemühungen anzuschließen.
Lindydancer
@Lindydancer Hat nicht [^\\]die entkommen ]? So funktioniert Regex in Java, wie ich weiß.
Czipperz
@Lindydancer Scheint so, als ob dies nicht der Fall ist, da ELisp die Verwendung von Escape-Zeichen in Zeichengruppen nicht zulässt .
Czipperz