Podkreślanie zmiennych powłoki w cudzysłowach

13

W vimie następujący dokument spowoduje, że $PWDlinie 2 i 3 zostaną pokolorowane na dwa różne sposoby:

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

Pierwsze wystąpienie $PWDbędzie miało inny kolor niż reszta ciągu, w którym się znajduje. Daje to wyraźne wizualne wskazanie, że zmienna zostanie rozwinięta, a nie traktowana jako dosłowny tekst. W przeciwieństwie do tego, druga instancja $PWDbędzie pokolorowana tak samo jak reszta ciągu, ponieważ pojedyncze cudzysłowy powodują, że jest ona traktowana jako dosłowny tekst.

Czy istnieją jakieś tryby emacs, które zapewniają tego rodzaju „przytaczanie świadomości powłoki”?

nispio
źródło
1
Z pewnością nie byłoby to trudne do dodania sh-mode? Może można go dodać do samego Emacsa.
PythonNut

Odpowiedzi:

11

Poniższy kod używa reguły blokowania czcionek z funkcją zamiast wyrażenia regularnego, funkcja szuka wystąpień, $VARale tylko wtedy, gdy znajdują się one w ciągu cudzysłowu. Funkcja (syntax-ppss)służy do ustalenia tego.

Reguła blokowania czcionek używa prependflagi, aby dodać się do istniejącego podświetlenia łańcucha. (Zauważ, że używa tego wiele pakietów t. Niestety, to zastępuje wszystkie aspekty istniejącego podświetlenia. Na przykład użycie prependzachowuje kolor tła łańcucha (jeśli taki istnieje) podczas zastępowania koloru pierwszego planu.)

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

Możesz to wywołać, dodając ostatnią funkcję do odpowiedniego zaczepu, na przykład:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
źródło
To działa dla mnie, ale pozostawia „$” z podświetleniem łańcucha.
erikstokes
Jest to zgodne z projektem, ponieważ w ten sposób podkreślono zmienną poza ciągiem znaków. Można to jednak łatwo zmienić. Jeśli 2zastąpisz regułę blokowania czcionek regułą 0, powinna działać. (Konieczne może być rozszerzenie wyrażenia regularnego, aby zawierało znak końca, aby poprawnie }podświetlić ${FOO}.) Liczba ta odnosi się do podgrupy wyrażeń regularnych dopasowania, co 0oznacza, że ​​całe dopasowanie powinno być podświetlone.
Lindydancer
Spakowałem to podczas dodawania tego do mojego repozytorium .emacs.d, jeśli ktoś jest zainteresowany: github.com/moonlite/.emacs.d/blob/… @Lindydancer Czy GPLv3 + i jako autor w tym pliku jest w porządku? (Popchnę aktualizację, jeśli nie).
Mattias Bengtsson
Brzmi dobrze. Prawdopodobnie nie miałbym czasu, aby zrobić to w odpowiedni pakiet. Chciałbym jednak, abyś usunął mój adres e-mail i zamiast tego dodał wiersz na mojej stronie EmacsWiki ( emacswiki.org/emacs/AndersLindgren ). Możesz także usunąć znak praw autorskich, ponieważ nie jest to konieczne, co powoduje, że kod źródłowy nie jest ascii.
Lindydancer
3

Poprawiłem odpowiedź @ Lindydancer na następujące sposoby:

  • Wprowadzono sh-script-extra-font-lock-is-in-double-quoted-stringfunkcję, ponieważ była używana tylko raz
  • Ucieczka ze zmiennej działa.
  • Zmienne liczbowe ( $10, $1itp) są podświetlone.

Przerwa na kod

(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
źródło
[^\\\\]Można zapisać jako [^\\], że jest to zestaw znaków, które nie powinny być dopasowane, a kod zawiera odwrotny ukośnik dwukrotnie. W starszych wersjach Emacsa należy używać font-lock-fontify-buffer, w nowszych należy dzwonić, font-lock-flusha dzwonienie font-lock-fontify-bufferz elisp jest przestarzałe. Mój oryginalny kod poszedł za tym, twój kod nie. W każdym razie lepszym pomysłem może być migracja tego do archiwum GitHub i dołączenie do wysiłku.
Lindydancer
@Lindydancer Nie [^\\]ucieka ]? Tak wiem, jak regex działa w Javie.
Czipperz,
@Lindydancer Wydaje się, że tak nie jest, ponieważ ELisp nie pozwala na używanie znaków ucieczki w grupach postaci .
Czipperz,