Question

Basically, I'm trying to syntax highlight the following piece of coffeescript code the way I want it. Explanation of the syntax of coffeescript functions can be found here.

nameHere = (tstamp, moo, boo) ->
    ...

The names tstamp, moo and boo should be colored pink (and nothing else, not the commas and not the brackets) because they are parameters to a lambda function.

highOrderFun ((x) -> x * x) someList

Here it is the first x that is the parameter. Parameters can have default arguments:

class Foo
    meth: (msg = "Hello", bar = "foo") ->
        ....

Default arguments can be variables themselves:

defColor = "red"
print = (msg, color = defColor) ->
    ...

So msg and color above should be highlighted, but not defColor. An even trickier case is functions with default arguments that themselves are functions. I think that is to hard for emacs' font-lock to highlight correctly, but I'm including it anyway:

funTakingFuns = (f1 = ((a, b) -> a*b), f2 = ((c, d) -> c/d)) ->
    ...

This appears to be pretty complicated to achieve in emacs because you want the highlighting to be context sensitive. I've read up on the documentation on font-lock but haven't been able to figure it out.

I'd be grateful if someone could show me what to set font-lock-defaults to make it syntax highlight the way I want it.

Update Showing more coffeescript syntax examples.

Was it helpful?

Solution

font-lock-keywords allows function values in the MATCHER field:

where MATCHER can be either the regexp to search for, or the function name to call to make the search (called with one argument, the limit of the search; it should return non-nil, move point, and set match-data appropriately if it succeeds; like re-search-forward would).

So we need to write a function that would search for the next function argument in the buffer.

Something like this:

    (defun coffee-match-next-argument (limit)
      (let ((start (point)))
        ;; Look for the arrow.
        (when (re-search-forward ") *->" limit t)
          ;; Save the position of the closing paren.
          (let ((stop (point)))
            (goto-char (match-beginning 0))
            ;; Go to the opening paren.
            (goto-char (nth 1 (syntax-ppss)))
            ;; If we're before our initial position, go forward.
            ;; We don't want to find the same symbols again.
            (when (> start (point))
              (goto-char start))
            ;; Look for the next symbol until the arrow.
            (or (re-search-forward "\\((\\|,\\) *\\(\\(\\sw\\|_\\)+\\)" stop 'mv)
                (coffee-match-next-argument limit))))))

And the setup, to use with existing coffee-mode:

(font-lock-add-keywords
 'coffee-mode
 '((coffee-match-next-argument 2 font-lock-variable-name-face)))

You can also use this in font-lock-defaults, of course.

This will likely use some other color than pink, but that's easy to change.

OTHER TIPS

This is more kind of a hack, it's far from optimal (as I am not familiar at all with coffeescript), but perhaps with a little tweaking yourself, you can get this done.

All the ingredients are there.

The triggering of the commands/functions are based on the assumption that you use coffee-mode. If you do not, this is not a big trouble, you'll just have to hook these things differently.

Put the following line in your .emacs:

(eval-after-load 'coffee '(load "/PATH/custom-coffee-font-lock.el"))

You can just save the below text as a file, and it will:

(1) Font lock when you trigger coffee-mode

(2) Font lock current line when you type the ">" as part of "->"

(3) Allow to font-lock the buffer by running M-x coffee-init-font-lock

;;;; custom-coffee-font-lock

;; Firstly, create a new font for this. 

(make-face 'font-lock-coffeescript-face)
(set-face-foreground 'font-lock-coffeescript-face "pink")


;; Next, one function that should be ran after a file is identified as 
;; a coffeescript file. It will do the font-locking you want on 
;; the whole buffer. It is also possible to run it manually. 


(defun coffee-init-font-lock ()
  (interactive)
  (save-excursion 
    (goto-char 1)
    (while (search-forward-regexp "=.+->" nil t)
      (search-backward-regexp "(")
      (forward-char 1)
      (add-text-properties 
       (point) (- (search-forward-regexp "," nil nil) 1) 
       '(font-lock-face font-lock-coffeescript-face))
      (add-text-properties 
       (point)  (- (search-forward-regexp "," nil nil) 1) 
       '(font-lock-face font-lock-coffeescript-face))
      (add-text-properties 
       (point)  (- (search-forward-regexp ")" nil nil) 1) 
       '(font-lock-face font-lock-coffeescript-face))
      (move-end-of-line 1)))
  )

  ;; This actually runs that function.
  (coffee-init-font-lock)


;; This advice will be ran everytime you write something. It will check 
;; whether "->" is before it, so when you type the final ">", it will
;; do the font locking for the current line (it also checks for your mode).

(defadvice self-insert-command (after coffee-font-lock activate)
  (when (and (looking-back "->") (eq major-mode 'coffee-mode))
    (save-excursion 
        (search-backward-regexp "(")
        (forward-char 1)
        (add-text-properties 
         (point)  (- (search-forward-regexp "," nil nil) 1) 
         '(font-lock-face font-lock-coffeescript-face))
        (add-text-properties 
         (point)  (- (search-forward-regexp "," nil nil) 1) 
         '(font-lock-face font-lock-coffeescript-face))
        (add-text-properties 
         (point)  (- (search-forward-regexp ")" nil nil) 1) 
         '(font-lock-face font-lock-coffeescript-face))))
  )

(provide 'custom-coffee-font-lock)
;;; custom-coffee-font-lock.el

If you have any requests, let me know. Like I said, I do not use CoffeeScript, so this might throw huge errors your way. At the very least it should help with some basic ideas.

Result:

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top