Evidenziazione delle variabili shell tra virgolette


13

In ogni caso, il seguente documento farà $PWDcolorare le righe 2 e 3 in due modi diversi:

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

La prima istanza di $PWDavrà un colore diverso dal resto della stringa in cui si trova. Ciò fornisce una chiara indicazione visiva che la variabile verrà espansa, anziché trattata come testo letterale. Al contrario, la seconda istanza di $PWDsarà colorata come il resto della stringa, poiché le virgolette singole lo fanno trattare come un testo letterale.

Esistono modalità emacs esistenti che forniscono questo tipo di "consapevolezza delle quotazioni di shell"?


1
Sicuramente, questo non sarebbe terribilmente difficile da aggiungere sh-mode? Forse può essere aggiunto a Emacs stesso.
PythonNut,

Risposte:


11

Il codice seguente utilizza una regola di blocco dei caratteri con una funzione anziché una regexp, la funzione cerca occorrenze $VARma solo quando si trovano all'interno di una stringa tra virgolette doppie. La funzione (syntax-ppss)viene utilizzata per determinare questo.

La regola di blocco dei caratteri usa il prependflag per aggiungersi sopra l'evidenziazione della stringa esistente. (Si noti che molti pacchetti usano tper questo. Sfortunatamente, questo sovrascrive tutti gli aspetti dell'evidenziazione esistente. Ad esempio, usando prependmanterrà un colore di sfondo della stringa (se ce n'è uno) durante la sostituzione del colore di primo piano.)

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

È possibile chiamare usare questo aggiungendo l'ultima funzione a un hook adatto, ad esempio:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)

Questo funziona per me, ma lascia "$" con l'evidenziazione della stringa.
erikstokes,

È in base alla progettazione, dal momento che è stata evidenziata una variabile all'esterno di una stringa. Tuttavia, questo può essere facilmente modificato. Se si sostituisce il 2nella regola di blocco dei caratteri con un 0dovrebbe funzionare. (Potrebbe essere necessario estendere la regexp per includere un finale }da evidenziare ${FOO}correttamente.) Questo numero si riferisce al sottogruppo regexp della partita, 0significa che l'intera partita deve essere evidenziata.
Lindydancer il

Preparato questo mentre lo aggiungevo al mio repository .emacs.d se qualcuno fosse interessato: github.com/moonlite/.emacs.d/blob/… @Lindydancer GPLv3 + e tu come autore in quel file andate bene? (In caso contrario invio un aggiornamento).
Mattias Bengtsson,

Suona bene. Probabilmente non avrei avuto il tempo di trasformarlo in un pacchetto adeguato. Tuttavia, vorrei che tu abbandonassi il mio indirizzo e-mail e invece aggiungessi una riga alla mia pagina EmacsWiki ( emacswiki.org/emacs/AndersLindgren ). Inoltre, è possibile rimuovere il segno di copyright in quanto non è necessario e rende il codice sorgente non ascii.
Lindydancer,

3

Ho migliorato la risposta di @ Lindydancer nei seguenti modi:

  • Inline la sh-script-extra-font-lock-is-in-double-quoted-stringfunzione, in quanto è stata utilizzata solo una volta
  • L'escape della variabile funziona.
  • Le variabili numeriche ( $10, $1, ecc) sono evidenziati.

Pausa per il codice

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

Il [^\\\\]potrebbe essere scritto come [^\\], è un insieme di caratteri che non dovrebbero essere abbinati, e il tuo codice include barra rovesciata due volte. Nelle versioni precedenti di Emacs è necessario utilizzare font-lock-fontify-buffer, nelle versioni più recenti è necessario chiamare font-lock-flushe la chiamata font-lock-fontify-bufferda elisp è obsoleta. Il mio codice originale ha seguito questo, il tuo codice no. Ad ogni modo, potrebbe essere un'idea migliore migrare questo in un archivio GitHub e unirti allo sforzo.
Lindydancer,

@Lindydancer Non [^\\]sfugge al ]? Ecco come funziona regex in Java, per quanto ne so.
Czipperz,

@Lindydancer Sembra che non sia così come ELisp non ti consente di usare i caratteri di escape nei gruppi di personaggi .
Czipperz,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.