Search code examples
emacselisp

Color variables inside parenthesis


I recently transitioned from vim to emacs and miss one key feature of shell scripting: variables being highlighted inside double quotes. How do I put this feature back in?

I read in another thread that you can use syntax-ppss to detect interior of quotes, but how do I actually change the color?

Note that the colors should be turned on for: name="$first $last" but NOT for name='$first $last'


Solution

  • Interestingly, this exact question was asked and answered in emacs.stackexchange.com just a couple of days ago:

    The code below use a font-lock rule with a function instead of a regexp, the function search for occurrences of $VAR but only when they are inside a double-quoted string. The function (syntax-ppss) is used to determine this.

    The font-lock rule use the prepend flag to add itself on top of the existing string highlighting. (Note that many packages use t for this. Unfortunately, this overwrites all aspects of the existing highlighting. For example, using prepend will retain a string background color (if there is one) while replacing the foreground color.)

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

    You can call use this by adding the last function to a suitable hook, for example:

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