Menyoroti variabel shell dalam tanda kutip

13

Dalam vim, dokumen berikut ini akan menyebabkan $PWDon line 2 dan 3 diwarnai dengan dua cara berbeda:

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

Contoh pertama dari $PWDakan berada dalam warna yang berbeda dari sisa string itu. Ini memberikan indikasi visual yang jelas bahwa variabel akan diperluas, daripada diperlakukan sebagai teks literal. Sebaliknya, instance kedua $PWDakan diwarnai sama dengan string yang lain, karena tanda kutip tunggal menyebabkannya diperlakukan sebagai teks literal.

Apakah ada mode emacs yang ada yang menyediakan jenis "kesadaran mengutip-shell?"

nispio
sumber
1
Tentunya, ini tidak akan terlalu sulit untuk ditambahkan sh-mode? Mungkin itu dapat ditambahkan ke Emacs sendiri.
PythonNut

Jawaban:

11

Kode di bawah ini menggunakan aturan font-lock dengan fungsi bukan regexp, fungsi mencari kejadian $VARtetapi hanya ketika mereka berada di dalam string yang dikutip ganda. Fungsi (syntax-ppss)ini digunakan untuk menentukan ini.

Aturan font-lock menggunakan prependbendera untuk menambahkan dirinya di atas penyorotan string yang ada. (Perhatikan bahwa banyak paket menggunakan tuntuk ini. Sayangnya, ini menimpa semua aspek dari penyorotan yang ada. Misalnya, menggunakan prependakan mempertahankan warna latar belakang string (jika ada) saat mengganti warna latar depan.)

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

Anda dapat memanggil menggunakan ini dengan menambahkan fungsi terakhir ke pengait yang sesuai, misalnya:

(add-hook 'sh-mode-hook 'sh-script-extra-font-lock-activate)
Lindydancer
sumber
Ini berfungsi untuk saya, tetapi meninggalkan "$" dengan menyoroti string.
erikstokes
Ini dengan desain, karena itu bagaimana variabel di luar string disorot. Namun, ini dapat dengan mudah diubah. Jika Anda mengganti 2aturan kunci-huruf dengan aturan 0itu seharusnya berfungsi. (Anda mungkin perlu memperpanjang regexp untuk menyertakan trailing }untuk disorot ${FOO}dengan benar.) Angka ini mengacu pada subkelompok regexp dari pertandingan, 0artinya seluruh pertandingan harus disorot.
Lindydancer
Mengemas ini sambil menambahkan ini ke repositori .emacs.d saya jika ada yang tertarik: github.com/moonlite/.emacs.d/blob/... @Lindydancer Apakah GPLv3 + dan Anda sebagai penulis dalam file tersebut boleh? (Saya akan mendorong pembaruan jika tidak).
Mattias Bengtsson
Kedengarannya bagus. Saya mungkin tidak akan punya waktu untuk membuatnya menjadi paket yang tepat. Namun, saya ingin Anda memasukkan alamat email saya dan sebagai gantinya menambahkan baris ke halaman EmacsWiki saya ( emacswiki.org/emacs/AndersLindgren ). Selain itu, Anda dapat menghapus tanda hak cipta karena tidak perlu dan itu membuat kode sumber non-ascii.
Lindydancer
3

Saya meningkatkan jawaban @ Lindydancer dengan cara berikut:

  • Fungsi digariskan sh-script-extra-font-lock-is-in-double-quoted-string, karena hanya digunakan sekali
  • Melarikan diri dari variabel berfungsi.
  • Variabel numerik ( $10, $1, dll) yang disorot.

Istirahat untuk kode

(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
sumber
The [^\\\\]dapat ditulis sebagai [^\\], itu satu set karakter yang tidak cocok, dan kode Anda termasuk backslash dua kali. Di versi Emacs yang lebih lama harus digunakan font-lock-fontify-buffer, di yang lebih baru Anda seharusnya menelepon font-lock-flushdan menelepon font-lock-fontify-bufferdari elisp sudah usang. Kode asli saya mengikuti ini, kode Anda tidak. Bagaimanapun, mungkin ide yang lebih baik untuk memigrasikan ini ke arsip GitHub dan bergabung dengan upaya tersebut.
Lindydancer
@ Lindydancer Tidak [^\\]luput dari itu ]? Begitulah cara regex bekerja di Jawa seperti yang saya tahu.
Czipperz
@Lindydancer Sepertinya tidak karena ELisp tidak memungkinkan Anda menggunakan karakter pelarian dalam grup karakter .
Czipperz