3

I'm trying to define some emacs font faces to do some custom highlighting. This seems to work when I define them individually:

(defface my-r-face `((t (:foreground "red")))  "Red highlight")
(defvar m-r-face 'my-r-face "Red.")
(defface my-g-face `((t (:foreground "green")))  "Green highlight")
(defvar m-g-face 'my-g-face "Green.")
(defface my-b-face `((t (:foreground "#0088ff")))  "Blue highlight")
(defvar m-b-face 'my-b-face "Blue.")
....etc

However I have a few dozen of these and I want to define them all in one go from some kind of colour table:

(setq ctable '(("red" "r")
           ("orange" "o")
           ("yellow" "y")
           ("#88ff00" "gy")
           ("green" "g")
           ("#00ff88" "gc")
           ("cyan" "c")
           ("#0088ff" "bc")
           ("blue" "b")
           ("purple" "bm")
           ("magenta" "m")
           ("#ff0088" "rm")
           ("grey" "lg")
           ("white" "w") ))

My difficulty is with assembling the symbol names for each face, ie concatenating "my-" and "-face" onto either side of an entry from the table. I have discovered (intern) that can make a new symbol from a string, however this symbol is then not acceptable to (defface), as it seems what I am doing is equivalent to (defface 'my-r-face ..., and defface doesn't like the quoted symbol, and expects (defface my-r-face .. instead. My attempt is as follows:

(dolist (tpl ctable)
  (defvar (intern (concat "my-" (nth 1 tpl) "-face"))
    (quote (intern (concat "my-" (nth 1 tpl) "-face"))) "colour")
  (defface (intern (concat "my-" (nth 1 tpl) "-face"))
    `((t (:foreground ,(car tpl)))) "Highlight" :group 'fortran)
)

Running this results in

Lisp error: (wrong-type-argument symbolp (intern (concat "fegs-" (nth 1 tpl) "-face")))
  (defvar (intern (concat "fegs-" ... "-face")) (quote (intern ...)) "colour")

Can anyone shed some light on what I'm doing wrong, or if I'm barking up the wrong tree entirely and there is a better way of doing this?

Thanks.

Drew
  • 29,895
  • 7
  • 74
  • 104
RFairey
  • 736
  • 3
  • 9
  • Don't do the "defvar" thing, please: you only need the "defface". For various reasons, this was done for the original faces used by font-lock and since then everyone has copied this mistake. – Stefan Nov 15 '13 at 13:32
  • 1
    If I remove the defvar (or the variation of it in my code below) then none of the highlighting works properly. Is this due to the mechanism used in the rest of the mode? It proceeds by adding the faces and some keyword regexps to a list that is then used in the guts of font-lock, and without the defvar this stops working. – RFairey Nov 15 '13 at 16:20
  • 2
    That's because your font-lock rules are wrong: they probably need to say `'my-r-face` instead of `my-r-face`. – Stefan Nov 16 '13 at 01:39

3 Answers3

2

You can avoid eval:

(defconst my-ctable '(...))

(defmacro my-init-cfaces ()
  `(progn
    ,@(mapcar (lambda (tpl)
               `(defface ,(intern (format "my-%s-face" (nth 1 tpl)))
                  '((t :foreground ,(car tpl)))
                  ,(format "Face for color %s." (car tpl))
                  :group 'fortran))
              my-ctable)))

(my-init-cfaces)
Stefan
  • 27,908
  • 4
  • 53
  • 82
1

defvar is an special form, defface is a macro (so arguments are passed unevaluated). Have you tried to use something in the line of

(eval `(defface ,(intern "foo") '((t (:foreground "red"))) "Highlight" :group 'fortran))

(eval `(defvar ,(intern "bar")))
juanleon
  • 9,220
  • 30
  • 41
  • I was trying to avoid eval, is this a situation where it's really the right tool for the job? – RFairey Nov 15 '13 at 12:13
  • I don't know enough lisp to claim (or even know) eval is THE right tool for that job. I just think it is A right tool for it :-). – juanleon Nov 15 '13 at 13:05
0

The complete code that worked in the end is as follows:

(setq ctable '(("red" "r")
           ("orange" "o")
           ("yellow" "y")
           ("#88ff00" "gy")
           ("green" "g")
           ("#00ff88" "gc")
           ("cyan" "c")
           ("#0088ff" "bc")
           ("blue" "b")
           ("purple" "bm")
           ("magenta" "m")
           ("#ff0088" "rm")
           ("grey" "lg")
           ("white" "w") ))

(dolist (tpl ctable)
  (let ((fname (concat "fegs-" (nth 1 tpl) "-face")))
    (eval `(defface ,(intern fname) '((t (:foreground ,(car tpl)))) "Highlight" :group 'fortran))
    (eval `(defvar ,(intern fname) ',(intern fname)))
  )
)

My defvar line is slightly different as this allows the face to be picked up by the highlighting code elsewhere as a global variable.

RFairey
  • 736
  • 3
  • 9
  • As noted by @Stefan above, the defvar line is not necessary as long as the face name is quoted in the font-lock definitions that use it. – RFairey Nov 21 '13 at 11:46