summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/lisp-mode.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/emacs-lisp/lisp-mode.el')
-rw-r--r--lisp/emacs-lisp/lisp-mode.el191
1 files changed, 154 insertions, 37 deletions
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index c6fcc06e38d..6d5391d1e90 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -29,6 +29,7 @@
;;; Code:
(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'subr-x))
(defvar font-lock-comment-face)
(defvar font-lock-doc-face)
@@ -118,6 +119,15 @@
t))
"\\s-+\\(" lisp-mode-symbol-regexp "\\)"))
2)
+ ;; Like the previous, but uses a quoted symbol as the name.
+ (list nil
+ (purecopy (concat "^\\s-*("
+ (eval-when-compile
+ (regexp-opt
+ '("defalias" "define-obsolete-function-alias")
+ t))
+ "\\s-+'\\(" lisp-mode-symbol-regexp "\\)"))
+ 2)
(list (purecopy "Variables")
(purecopy (concat "^\\s-*("
(eval-when-compile
@@ -155,6 +165,12 @@
"Imenu generic expression for Lisp mode. See `imenu-generic-expression'.")
+(defconst lisp-mode-autoload-regexp
+ "^;;;###\\(\\([-[:alnum:]]+?\\)-\\)?\\(autoload\\)"
+ "Regexp to match autoload cookies.
+The second group matches package names used to redirect autoloads
+to a package-local <package>-loaddefs.el file.")
+
;; This was originally in autoload.el and is still used there.
(put 'autoload 'doc-string-elt 3)
(put 'defmethod 'doc-string-elt 3)
@@ -234,6 +250,9 @@
('let
(forward-sexp 1)
(>= pos (point)))
+ ((or 'defun 'defmacro 'cl-defmethod 'cl-defun)
+ (forward-sexp 2)
+ (>= pos (point)))
('condition-case
;; If (cdr paren-posns), then we're in the BODY
;; of HANDLERS.
@@ -417,7 +436,8 @@ This will generate compile-time constants from BINDINGS."
nil t))
;; Emacs Lisp autoload cookies. Supports the slightly different
;; forms used by mh-e, calendar, etc.
- ("^;;;###\\([-a-z]*autoload\\)" 1 font-lock-warning-face prepend))
+ (,lisp-mode-autoload-regexp (3 font-lock-warning-face prepend)
+ (2 font-lock-function-name-face prepend t)))
"Subdued level highlighting for Emacs Lisp mode.")
(defconst lisp-cl-font-lock-keywords-1
@@ -465,6 +485,9 @@ This will generate compile-time constants from BINDINGS."
;; Words inside ‘’, '' and `' tend to be symbol names.
(,(concat "[`‘']\\(" lisp-mode-symbol-regexp "\\)['’]")
(1 font-lock-constant-face prepend))
+ ;; \\= tends to be an escape in doc strings.
+ ("\\\\\\\\="
+ (0 font-lock-builtin-face prepend))
;; Constant values.
(,(concat "\\_<:" lisp-mode-symbol-regexp "\\_>")
(0 font-lock-builtin-face))
@@ -590,6 +613,8 @@ containing STARTPOS."
(defun lisp-string-after-doc-keyword-p (listbeg startpos)
"Return non-nil if `:documentation' symbol ends at STARTPOS inside a list.
+`:doc' can also be used.
+
LISTBEG is the position of the start of the innermost list
containing STARTPOS."
(and listbeg ; We are inside a Lisp form.
@@ -597,7 +622,7 @@ containing STARTPOS."
(goto-char startpos)
(ignore-errors
(progn (backward-sexp 1)
- (looking-at ":documentation\\_>"))))))
+ (looking-at ":documentation\\_>\\|:doc\\_>"))))))
(defun lisp-font-lock-syntactic-face-function (state)
"Return syntactic face function for the position represented by STATE.
@@ -645,7 +670,9 @@ font-lock keywords will not be case sensitive."
(setq-local indent-line-function 'lisp-indent-line)
(setq-local indent-region-function 'lisp-indent-region)
(setq-local comment-indent-function #'lisp-comment-indent)
- (setq-local outline-regexp ";;;\\(;* [^ \t\n]\\|###autoload\\)\\|(")
+ (setq-local outline-regexp (concat ";;;;* [^ \t\n]\\|(\\|\\("
+ lisp-mode-autoload-regexp
+ "\\)"))
(setq-local outline-level 'lisp-outline-level)
(setq-local add-log-current-defun-function #'lisp-current-defun-name)
(setq-local comment-start ";")
@@ -685,7 +712,8 @@ font-lock keywords will not be case sensitive."
;; Expects outline-regexp is ";;;\\(;* [^ \t\n]\\|###autoload\\)\\|("
;; and point is at the beginning of a matching line.
(let ((len (- (match-end 0) (match-beginning 0))))
- (cond ((looking-at "(\\|;;;###autoload")
+ (cond ((or (looking-at-p "(")
+ (looking-at-p lisp-mode-autoload-regexp))
1000)
((looking-at ";;\\(;+\\) ")
(- (match-end 1) (match-beginning 1)))
@@ -1106,6 +1134,53 @@ is the buffer position of the start of the containing expression."
(t
normal-indent))))))
+(defun lisp--local-defform-body-p (state)
+ "Return non-nil when at local definition body according to STATE.
+STATE is the `parse-partial-sexp' state for current position."
+ (when-let ((start-of-innermost-containing-list (nth 1 state)))
+ (let* ((parents (nth 9 state))
+ (first-cons-after (cdr parents))
+ (second-cons-after (cdr first-cons-after))
+ first-order-parent second-order-parent)
+ (while second-cons-after
+ (when (= start-of-innermost-containing-list
+ (car second-cons-after))
+ (setq second-order-parent (pop parents)
+ first-order-parent (pop parents)
+ ;; Leave the loop.
+ second-cons-after nil))
+ (pop second-cons-after)
+ (pop parents))
+ (when second-order-parent
+ (let (local-definitions-starting-point)
+ (and (save-excursion
+ (goto-char (1+ second-order-parent))
+ (when-let ((head (ignore-errors
+ ;; FIXME: This does not distinguish
+ ;; between reading nil and a read error.
+ ;; We don't care but still, better fix this.
+ (read (current-buffer)))))
+ (when (memq head '( cl-flet cl-labels cl-macrolet cl-flet*
+ cl-symbol-macrolet))
+ ;; In what follows, we rely on (point) returning non-nil.
+ (setq local-definitions-starting-point
+ (progn
+ (parse-partial-sexp
+ (point) first-order-parent nil
+ ;; From docstring of `parse-partial-sexp':
+ ;; Fourth arg non-nil means stop
+ ;; when we come to any character
+ ;; that starts a sexp.
+ t)
+ (point))))))
+ (save-excursion
+ (when (ignore-errors
+ ;; We rely on `backward-up-list' working
+ ;; even when sexp is incomplete “to the right”.
+ (backward-up-list 2)
+ t)
+ (= local-definitions-starting-point (point))))))))))
+
(defun lisp-indent-function (indent-point state)
"This function is the normal value of the variable `lisp-indent-function'.
The function `calculate-lisp-indent' calls this to determine
@@ -1139,16 +1214,19 @@ Lisp function does not specify a special indentation."
(if (and (elt state 2)
(not (looking-at "\\sw\\|\\s_")))
;; car of form doesn't seem to be a symbol
- (progn
+ (if (lisp--local-defform-body-p state)
+ ;; We nevertheless check whether we are in flet-like form
+ ;; as we presume local function names could be non-symbols.
+ (lisp-indent-defform state indent-point)
(if (not (> (save-excursion (forward-line 1) (point))
calculate-lisp-indent-last-sexp))
- (progn (goto-char calculate-lisp-indent-last-sexp)
- (beginning-of-line)
- (parse-partial-sexp (point)
- calculate-lisp-indent-last-sexp 0 t)))
- ;; Indent under the list or under the first sexp on the same
- ;; line as calculate-lisp-indent-last-sexp. Note that first
- ;; thing on that line has to be complete sexp since we are
+ (progn (goto-char calculate-lisp-indent-last-sexp)
+ (beginning-of-line)
+ (parse-partial-sexp (point)
+ calculate-lisp-indent-last-sexp 0 t)))
+ ;; Indent under the list or under the first sexp on the same
+ ;; line as calculate-lisp-indent-last-sexp. Note that first
+ ;; thing on that line has to be complete sexp since we are
;; inside the innermost containing sexp.
(backward-prefix-chars)
(current-column))
@@ -1159,15 +1237,14 @@ Lisp function does not specify a special indentation."
'lisp-indent-function)
(get (intern-soft function) 'lisp-indent-hook)))
(cond ((or (eq method 'defun)
- (and (null method)
- (> (length function) 3)
- (string-match "\\`def" function)))
+ ;; Check whether we are in flet-like form.
+ (lisp--local-defform-body-p state))
(lisp-indent-defform state indent-point))
((integerp method)
(lisp-indent-specform method state
indent-point normal-indent))
(method
- (funcall method indent-point state)))))))
+ (funcall method indent-point state)))))))
(defcustom lisp-body-indent 2
"Number of columns to indent the second line of a `(def...)' form."
@@ -1235,6 +1312,13 @@ Lisp function does not specify a special indentation."
(put 'autoload 'lisp-indent-function 'defun) ;Elisp
(put 'progn 'lisp-indent-function 0)
+(put 'defvar 'lisp-indent-function 'defun)
+(put 'defalias 'lisp-indent-function 'defun)
+(put 'defvaralias 'lisp-indent-function 'defun)
+(put 'defconst 'lisp-indent-function 'defun)
+(put 'define-category 'lisp-indent-function 'defun)
+(put 'define-charset-internal 'lisp-indent-function 'defun)
+(put 'define-fringe-bitmap 'lisp-indent-function 'defun)
(put 'prog1 'lisp-indent-function 1)
(put 'save-excursion 'lisp-indent-function 0) ;Elisp
(put 'save-restriction 'lisp-indent-function 0) ;Elisp
@@ -1249,6 +1333,7 @@ Lisp function does not specify a special indentation."
(put 'handler-bind 'lisp-indent-function 1) ;CL
(put 'unwind-protect 'lisp-indent-function 1)
(put 'with-output-to-temp-buffer 'lisp-indent-function 1)
+(put 'closure 'lisp-indent-function 2)
(defun indent-sexp (&optional endpos)
"Indent each line of the list starting just after point.
@@ -1341,6 +1426,9 @@ and initial semicolons."
;; a comment: Point is on a program line; we are interested
;; particularly in docstring lines.
;;
+ ;; FIXME: The below bindings are probably mostly irrelevant
+ ;; since we're now narrowing to a region before filling.
+ ;;
;; We bind `paragraph-start' and `paragraph-separate' temporarily. They
;; are buffer-local, but we avoid changing them so that they can be set
;; to make `forward-paragraph' and friends do something the user wants.
@@ -1376,29 +1464,58 @@ and initial semicolons."
(derived-mode-p 'emacs-lisp-mode))
emacs-lisp-docstring-fill-column
fill-column)))
- (save-restriction
+ (let ((ppss (syntax-ppss))
+ (start (point)))
(save-excursion
- (let ((ppss (syntax-ppss))
- (start (point)))
- ;; If we're in a string, then narrow (roughly) to that
- ;; string before filling. This avoids filling Lisp
- ;; statements that follow the string.
- (when (ppss-string-terminator ppss)
- (goto-char (ppss-comment-or-string-start ppss))
- (beginning-of-line)
- ;; The string may be unterminated -- in that case, don't
- ;; narrow.
- (when (ignore-errors
- (progn
- (forward-sexp 1)
- t))
- (narrow-to-region (ppss-comment-or-string-start ppss)
- (point))))
- ;; Move back to where we were.
+ (save-restriction
+ ;; If we're not inside a string, then do very basic
+ ;; filling. This avoids corrupting embedded strings in
+ ;; code.
+ (if (not (ppss-comment-or-string-start ppss))
+ (lisp--fill-line-simple)
+ ;; If we're in a string, then narrow (roughly) to that
+ ;; string before filling. This avoids filling Lisp
+ ;; statements that follow the string.
+ (when (ppss-string-terminator ppss)
+ (goto-char (ppss-comment-or-string-start ppss))
+ ;; The string may be unterminated -- in that case, don't
+ ;; narrow.
+ (when (ignore-errors
+ (progn
+ (forward-sexp 1)
+ t))
+ (narrow-to-region (1+ (ppss-comment-or-string-start ppss))
+ (1- (point)))))
+ ;; Move back to where we were.
+ (goto-char start)
+ ;; We should fill the first line of a string
+ ;; separately (since it's usually a doc string).
+ (if (= (line-number-at-pos) 1)
+ (narrow-to-region (line-beginning-position)
+ (line-beginning-position 2))
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line 1)
+ (narrow-to-region (point) (point-max))))
+ (fill-paragraph justify)))))))
+ ;; Never return nil.
+ t)
+
+(defun lisp--fill-line-simple ()
+ (narrow-to-region (line-beginning-position) (line-end-position))
+ (goto-char (point-min))
+ (while (and (not (eobp))
+ (re-search-forward "\\_>" nil t))
+ (when (> (current-column) fill-column)
+ (let ((start (point)))
+ (backward-sexp)
+ (if (looking-back "[[(]" (point-min))
(goto-char start)
- (fill-paragraph justify)))))
- ;; Never return nil.
- t))
+ (skip-chars-backward " \t")
+ (insert "\n")
+ (forward-sexp))))
+ (unless (eobp)
+ (forward-char 1))))
(defun indent-code-rigidly (start end arg &optional nochange-regexp)
"Indent all lines of code, starting in the region, sideways by ARG columns.