[slime-devel] [investigation] Font lock, reader macros and SLIME repl

Anton Kovalenko anton at sw4me.com
Mon Aug 30 18:03:01 UTC 2010

Hello SLIME developers,

Both in the lisp-mode and in slime REPL, there is currently one major
thing that Emacs isn't getting right: constructs using reader macros
like #P"/home/" and #2A((1 0) (0 1)) are regarded as two sexps instead
of one. E.g. command for wrapping a sexp may turn #P"/home/" into
(#P)"/home/", etc. It gets even more awful with paredit-mode (that I
prefer to use), but the problem is noticeable even without it. 

I have investigated one possible solution to it, and here are the

First, I decided to limit the scope of solution to some predefined
"shape" of reader macro: for starters, when #\# is used as dispatch
macro character, and its reader function calls READ to get the sexp
following #[numarg]<char>. This is the case with all standard
reader-macros and many implementation-specific and user-defined ones.
Furthermore, I discarded the possibility to consult SWANK for the
buffer's *readtable* (SWANK doesn't *know* it, anyway).

To get right behavior for #P, #2A and the like, the most appropriate
syntax for all macro-characters seems to be `expression prefix': we want
Emacs to regard the whole #P thing as it would regard a quote, backquote
or comma.

Let's use the feature of font-lock mode in modern emacsen: syntactic
keywords (in a nutshell, it's when a code used for text-coloring is
[ab]used to add syntax-table properties to some text fragments).
`parse-sexp-lookup-properties' set to true causes the sexp machinery
to look into syntax-table properties of individual characters.

Let's add a syntactic keyword to lisp-mode:

(defun aak:add-lisp-reader-macros-syntactic-keyword ()
  "Register # numarg macro-character as a font lock syntactic
keyword, turning it into expression prefix."
  (set (make-local-variable 'parse-sexp-lookup-properties) t)
  (set (make-local-variable 'font-lock-syntactic-keywords)
       '(("\\(\\W\\|$\\)\\#\\([0-9]*[A-Za-z]\\)" (2 "'")))))

(add-hook 'lisp-mode-hook 'aak:add-lisp-reader-macros-syntactic-keyword)

With the hook above, any lisp-mode buffer with font-lock mode enabled
gets good enough navigation/wrapping/unwrapping for previously
misinterpreted items.

I decided to leave macro-characters handled by this regexp to [A-Za-z],
because normal behavior of reading the following sexp can't really be
expected from other characters: readtable additions along the lines
of #"something" and #{something} are also popular, and we can't get
_them_ right without knowing a concrete syntaxt they expect. Another
decision that has its merits is to limit it further to _standard_
macro-characters only.   

Now I wanted the same thing to be active for slime-repl mode. The only
obstacle was that slime-repl mode is currently font-lock-unfriendly:
when some faces are set programmatically and some left to font-lock, the
programmatically-set faces should use font-lock-face property instead of
face property (the latter gets overridden by font-lock results).

A workaround that I use currently is the following advice to

(defadvice add-text-properties
  (before aak:slime-propertize-with-font-lock-face activate)
  (when (eq 'slime-repl-mode major-mode)
    (let* ((props (ad-get-arg 2))
	   (face (getf props 'face)))
      (when face
	(push face props)
	(push 'font-lock-face props)
	(when (memq 'face (getf props 'rear-nonsticky))
	  (push 'font-lock-face (getf props 'rear-nonsticky)))
	(ad-set-arg 2 props)))))
When in slime-repl mode, it copies face property to font-lock-face
property, and also makes it rear-nonsticky when face is requested to
be. It's the only part that really touches SLIME, and I think it would
better insert font-lock-face property itself, together with face property
(don't forget rear-nonstickyness). This duplication (face =>
font-lock-face) does no harm when font-lock mode is disabled, but
enables slime-repl buffer to be used with font-lock enabled as well. 

Another hook to enable font-lock mode in slime buffers:

(defun aak:enable-font-lock-for-slime-repl ()
  "Enable font lock mode in slime REPL buffer."
  (font-lock-fontify-buffer) ;; don't know why it's needed..
  (font-lock-mode 1))

(add-hook 'slime-repl-mode-hook
(add-hook 'slime-repl-mode-hook

NB. The problem that I'm trying to solve is even more important for
slime-repl buffers, where incorrect sexp wrapping tends to break
_presentations_ apart. 

Regards, Anton Kovalenko
+7(916)345-34-02 | Elektrostal' MO, Russia

More information about the slime-devel mailing list