highlighting old style CL function names in Emacs Lisp

1 background

There are two built-in libraries for Common-Lisp-like functions in Emacs Lisp: cl.el and cl-lib.el. The former is sort of obsolete and defines lots of functions without cl- prefix. The latter defines lots of functions with cl- prefix. For example, Emacs Lisp mapcar takes only one sequence, Common Lisp mapcar can take many sequences, and that is implemented as mapcar* in cl.el, and as cl-mapcar in cl-lib.el. I will refer to functions like mapcar* defined in cl.el as old style CL functions, and functions like cl-mapcar as new style CL functions or CL-LIB functions.

Some old style CL functions are now aliases for new style CL functions. Some old style functions may be explicitly obsolete and their CL-LIB analogs may work differently.

When you use CL-LIB functions in some code, others reading that code know that it needs (require 'cl-lib) to work. When you use old style CL functions in some code, others copying the code may end up seeing error because you forgot to say that the code needs (require 'cl) to work. It is easy for you to forget this because it is hard to know whether a given part of code requires cl or not just from just reading it, unless you have memorized the names of all old style CL functions, or unless you have set up your Emacs so that it highlights names of old style CL functions.

2 highlighting old style CL function names

You can put the following code to your init file to have emacs-lisp-mode highlight all occurrences of old style CL functions with flyspell-incorrect face. I am using that face so that the face says “hey, this is an old style CL function. you must correct this by using new style function instead.” to me. Others may prefer different kind of face that says “hey, you used an old style CL function. nothing wrong with that, but let me highlight this so that you can easily see that this piece of code requires cl”

(defconst my-old-style-cl-functions
  '(acons adjoin assert assoc* assoc-if assoc-if-not block caaaar caaadr
          caaar caadar caaddr caadr cadaar cadadr cadar caddar cadddr caddr
          callf callf2 case cdaaar cdaadr cdaar cdadar cdaddr cdadr cddaar
          cddadr cddar cdddar cddddr cdddr ceiling* check-type coerce
          compiler-macroexpand concatenate copy-list copy-seq count count-if
          count-if-not decf declaim define-compiler-macro define-modify-macro
          define-setf-expander define-setf-method defmacro* defsetf defstruct
          defsubst* deftype defun* delete* delete-duplicates delete-if
          delete-if-not destructuring-bind do do* do-all-symbols do-symbols
          ecase eighth endp equalp etypecase eval-when evenp every fifth fill
          find find-if find-if-not first flet floatp-safe floor* fourth
          function* gcd gensym gentemp get* getf incf intersection isqrt labels
          lcm ldiff letf letf* lexical-let lexical-let* list* list-length
          load-time-value locally loop macrolet make-random-state map mapcan
          mapcar* mapcon mapl maplist member* member-if member-if-not merge
          minusp mismatch mod* multiple-value-apply multiple-value-bind
          multiple-value-call multiple-value-list multiple-value-setq
          nintersection ninth notany notevery nreconc nset-difference
          nset-exclusive-or nsublis nsubst nsubst-if nsubst-if-not nsubstitute
          nsubstitute-if nsubstitute-if-not nth-value nunion oddp pairlis plusp
          position position-if position-if-not proclaim progv psetf psetq
          pushnew random* random-state-p rassoc* rassoc-if rassoc-if-not reduce
          rem* remf remove* remove-duplicates remove-if remove-if-not remprop
          replace rest return return-from revappend rotatef round* search second
          set-difference set-exclusive-or seventh shiftf signum sixth some sort*
          stable-sort sublis subseq subsetp subst subst-if subst-if-not
          substitute substitute-if substitute-if-not svref symbol-macrolet tailp
          tenth the third tree-equal truncate* typecase typep union values
          values-list))
(defconst my-rx-old-style-cl-functions
  (eval `(rx bow (or ,@(mapcar #'symbol-name my-old-style-cl-functions)) eow)))
(eval-after-load 'lisp-mode
  '(progn
     (require 'flyspell) ; for the flyspell-incorrect face
     (font-lock-add-keywords 'emacs-lisp-mode
                             `((,my-rx-old-style-cl-functions . 'flyspell-incorrect)))))

3 some anomalies

There are three Emacs Lisp functions which can be used without loading cl, but are redefined as old style CL functions when you load cl. These are declare, dolist, and dotimes. The code I gave you does not highlight them.

4 how I got the list

I got the list of old style CL function names by running the following code from emacs -q

(defvar all-functions nil)
(mapatoms
 (lambda (sym)
   (if (fboundp sym)
       (push sym all-functions))))

(require 'cl)

(defvar all-obs nil)
(mapatoms
 (lambda (sym)
   (if (and (fboundp sym)
            (not (memq sym all-functions))
            (let* ((def (symbol-function sym))
                   (filename (find-lisp-object-file-name sym def)))
              (and (not (string-match-p (rx bos "cl-") (symbol-name sym)))
                   (string-match-p (rx "cl.el" eos) filename))))
       (push sym all-obs))))

(print (sort all-obs #'string<))
This entry was posted in Emacs and tagged . Bookmark the permalink.

One Response to highlighting old style CL function names in Emacs Lisp

  1. Pingback: Differences between Common Lisp and Emacs Lisp | Yoo Box

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s