231 lines
9.3 KiB
EmacsLisp
231 lines
9.3 KiB
EmacsLisp
(require 'slime)
|
||
(require 'slime-parse)
|
||
(require 'slime-autodoc)
|
||
(require 'font-lock)
|
||
(require 'cl-lib)
|
||
|
||
;;; Fontify WITH-FOO, DO-FOO, and DEFINE-FOO like standard macros.
|
||
;;; Fontify CHECK-FOO like CHECK-TYPE.
|
||
(defvar slime-additional-font-lock-keywords
|
||
'(("(\\(\\(\\s_\\|\\w\\)*:\\(define-\\|do-\\|with-\\|without-\\)\\(\\s_\\|\\w\\)*\\)" 1 font-lock-keyword-face)
|
||
("(\\(\\(define-\\|do-\\|with-\\)\\(\\s_\\|\\w\\)*\\)" 1 font-lock-keyword-face)
|
||
("(\\(check-\\(\\s_\\|\\w\\)*\\)" 1 font-lock-warning-face)
|
||
("(\\(assert-\\(\\s_\\|\\w\\)*\\)" 1 font-lock-warning-face)))
|
||
|
||
;;;; Specially fontify forms suppressed by a reader conditional.
|
||
(defcustom slime-highlight-suppressed-forms t
|
||
"Display forms disabled by reader conditionals as comments."
|
||
:type '(choice (const :tag "Enable" t) (const :tag "Disable" nil))
|
||
:group 'slime-mode)
|
||
|
||
(define-slime-contrib slime-fontifying-fu
|
||
"Additional fontification tweaks:
|
||
Fontify WITH-FOO, DO-FOO, DEFINE-FOO like standard macros.
|
||
Fontify CHECK-FOO like CHECK-TYPE."
|
||
(:authors "Tobias C. Rittweiler <tcr@freebits.de>")
|
||
(:license "GPL")
|
||
(:on-load
|
||
(font-lock-add-keywords
|
||
'lisp-mode slime-additional-font-lock-keywords)
|
||
(when slime-highlight-suppressed-forms
|
||
(slime-activate-font-lock-magic)))
|
||
(:on-unload
|
||
;; FIXME: remove `slime-search-suppressed-forms', and remove the
|
||
;; extend-region hook.
|
||
(font-lock-remove-keywords
|
||
'lisp-mode slime-additional-font-lock-keywords)))
|
||
|
||
(defface slime-reader-conditional-face
|
||
'((t (:inherit font-lock-comment-face)))
|
||
"Face for compiler notes while selected."
|
||
:group 'slime-mode-faces)
|
||
|
||
(defvar slime-search-suppressed-forms-match-data (list nil nil))
|
||
|
||
(defun slime-search-suppressed-forms-internal (limit)
|
||
(when (search-forward-regexp slime-reader-conditionals-regexp limit t)
|
||
(let ((start (match-beginning 0)) ; save match data
|
||
(state (slime-current-parser-state)))
|
||
(if (or (nth 3 state) (nth 4 state)) ; inside string or comment?
|
||
(slime-search-suppressed-forms-internal limit)
|
||
(let* ((char (char-before))
|
||
(expr (read (current-buffer)))
|
||
(val (slime-eval-feature-expression expr)))
|
||
(when (<= (point) limit)
|
||
(if (or (and (eq char ?+) (not val))
|
||
(and (eq char ?-) val))
|
||
;; If `slime-extend-region-for-font-lock' did not
|
||
;; fully extend the region, the assertion below may
|
||
;; fail. This should only happen on XEmacs and older
|
||
;; versions of GNU Emacs.
|
||
(ignore-errors
|
||
(forward-sexp) (backward-sexp)
|
||
;; Try to suppress as far as possible.
|
||
(slime-forward-sexp)
|
||
(cl-assert (<= (point) limit))
|
||
(let ((md (match-data nil slime-search-suppressed-forms-match-data)))
|
||
(setf (cl-first md) start)
|
||
(setf (cl-second md) (point))
|
||
(set-match-data md)
|
||
t))
|
||
(slime-search-suppressed-forms-internal limit))))))))
|
||
|
||
(defun slime-search-suppressed-forms (limit)
|
||
"Find reader conditionalized forms where the test is false."
|
||
(when (and slime-highlight-suppressed-forms
|
||
(slime-connected-p))
|
||
(let ((result 'retry))
|
||
(while (and (eq result 'retry) (<= (point) limit))
|
||
(condition-case condition
|
||
(setq result (slime-search-suppressed-forms-internal limit))
|
||
(end-of-file ; e.g. #+(
|
||
(setq result nil))
|
||
;; We found a reader conditional we couldn't process for
|
||
;; some reason; however, there may still be other reader
|
||
;; conditionals before `limit'.
|
||
(invalid-read-syntax ; e.g. #+#.foo
|
||
(setq result 'retry))
|
||
(scan-error ; e.g. #+nil (foo ...
|
||
(setq result 'retry))
|
||
(slime-incorrect-feature-expression ; e.g. #+(not foo bar)
|
||
(setq result 'retry))
|
||
(slime-unknown-feature-expression ; e.g. #+(foo)
|
||
(setq result 'retry))
|
||
(error
|
||
(setq result nil)
|
||
(slime-display-warning
|
||
(concat "Caught error during fontification while searching for forms\n"
|
||
"that are suppressed by reader-conditionals. The error was: %S.")
|
||
condition))))
|
||
result)))
|
||
|
||
|
||
(defun slime-search-directly-preceding-reader-conditional ()
|
||
"Search for a directly preceding reader conditional. Return its
|
||
position, or nil."
|
||
;;; We search for a preceding reader conditional. Then we check that
|
||
;;; between the reader conditional and the point where we started is
|
||
;;; no other intervening sexp, and we check that the reader
|
||
;;; conditional is at the same nesting level.
|
||
(condition-case nil
|
||
(let* ((orig-pt (point))
|
||
(reader-conditional-pt
|
||
(search-backward-regexp slime-reader-conditionals-regexp
|
||
;; We restrict the search to the
|
||
;; beginning of the /previous/ defun.
|
||
(save-excursion
|
||
(beginning-of-defun)
|
||
(point))
|
||
t)))
|
||
(when reader-conditional-pt
|
||
(let* ((parser-state
|
||
(parse-partial-sexp
|
||
(progn (goto-char (+ reader-conditional-pt 2))
|
||
(forward-sexp) ; skip feature expr.
|
||
(point))
|
||
orig-pt))
|
||
(paren-depth (car parser-state))
|
||
(last-sexp-pt (cl-caddr parser-state)))
|
||
(if (and paren-depth
|
||
(not (cl-plusp paren-depth)) ; no '(' in between?
|
||
(not last-sexp-pt)) ; no complete sexp in between?
|
||
reader-conditional-pt
|
||
nil))))
|
||
(scan-error nil))) ; improper feature expression
|
||
|
||
|
||
;;; We'll push this onto `font-lock-extend-region-functions'. In past,
|
||
;;; we didn't do so which made our reader-conditional font-lock magic
|
||
;;; pretty unreliable (it wouldn't highlight all suppressed forms, and
|
||
;;; worked quite non-deterministic in general.)
|
||
;;;
|
||
;;; Cf. _Elisp Manual_, 23.6.10 Multiline Font Lock Constructs.
|
||
;;;
|
||
;;; We make sure that `font-lock-beg' and `font-lock-end' always point
|
||
;;; to the beginning or end of a toplevel form. So we never miss a
|
||
;;; reader-conditional, or point in mid of one.
|
||
(defvar font-lock-beg) ; shoosh compiler
|
||
(defvar font-lock-end)
|
||
|
||
(defun slime-extend-region-for-font-lock ()
|
||
(when slime-highlight-suppressed-forms
|
||
(condition-case c
|
||
(let (changedp)
|
||
(cl-multiple-value-setq (changedp font-lock-beg font-lock-end)
|
||
(slime-compute-region-for-font-lock font-lock-beg font-lock-end))
|
||
changedp)
|
||
(error
|
||
(slime-display-warning
|
||
(concat "Caught error when trying to extend the region for fontification.\n"
|
||
"The error was: %S\n"
|
||
"Further: font-lock-beg=%d, font-lock-end=%d.")
|
||
c font-lock-beg font-lock-end)))))
|
||
|
||
(defun slime-beginning-of-tlf ()
|
||
(let ((pos (syntax-ppss-toplevel-pos (slime-current-parser-state))))
|
||
(if pos (goto-char pos))))
|
||
|
||
(defun slime-compute-region-for-font-lock (orig-beg orig-end)
|
||
(let ((beg orig-beg)
|
||
(end orig-end))
|
||
(goto-char beg)
|
||
(inline (slime-beginning-of-tlf))
|
||
(cl-assert (not (cl-plusp (nth 0 (slime-current-parser-state)))))
|
||
(setq beg (let ((pt (point)))
|
||
(cond ((> (- beg pt) 20000) beg)
|
||
((slime-search-directly-preceding-reader-conditional))
|
||
(t pt))))
|
||
(goto-char end)
|
||
(while (search-backward-regexp slime-reader-conditionals-regexp beg t)
|
||
(setq end (max end (save-excursion
|
||
(ignore-errors (slime-forward-reader-conditional))
|
||
(point)))))
|
||
(cl-values (or (/= beg orig-beg) (/= end orig-end)) beg end)))
|
||
|
||
|
||
(defun slime-activate-font-lock-magic ()
|
||
(if (featurep 'xemacs)
|
||
(let ((pattern `((slime-search-suppressed-forms
|
||
(0 slime-reader-conditional-face t)))))
|
||
(dolist (sym '(lisp-font-lock-keywords
|
||
lisp-font-lock-keywords-1
|
||
lisp-font-lock-keywords-2))
|
||
(set sym (append (symbol-value sym) pattern))))
|
||
(font-lock-add-keywords
|
||
'lisp-mode
|
||
`((slime-search-suppressed-forms 0 ,''slime-reader-conditional-face t)))
|
||
|
||
(add-hook 'lisp-mode-hook
|
||
#'(lambda ()
|
||
(add-hook 'font-lock-extend-region-functions
|
||
'slime-extend-region-for-font-lock t t)))))
|
||
|
||
(let ((byte-compile-warnings '()))
|
||
(mapc (lambda (sym)
|
||
(cond ((fboundp sym)
|
||
(unless (byte-code-function-p (symbol-function sym))
|
||
(byte-compile sym)))
|
||
(t (error "%S is not fbound" sym))))
|
||
'(slime-extend-region-for-font-lock
|
||
slime-compute-region-for-font-lock
|
||
slime-search-directly-preceding-reader-conditional
|
||
slime-search-suppressed-forms
|
||
slime-beginning-of-tlf)))
|
||
|
||
(cl-defun slime-initialize-lisp-buffer-for-test-suite
|
||
(&key (font-lock-magic t) (autodoc t))
|
||
(let ((hook lisp-mode-hook))
|
||
(unwind-protect
|
||
(progn
|
||
(set (make-local-variable 'slime-highlight-suppressed-forms)
|
||
font-lock-magic)
|
||
(setq lisp-mode-hook nil)
|
||
(lisp-mode)
|
||
(slime-mode 1)
|
||
(when (boundp 'slime-autodoc-mode)
|
||
(if autodoc
|
||
(slime-autodoc-mode 1)
|
||
(slime-autodoc-mode -1))))
|
||
(setq lisp-mode-hook hook))))
|
||
|
||
(provide 'slime-fontifying-fu)
|