From e8adf68824178ea25a5fda0c53233a42883de861 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Thu, 13 Apr 2017 20:15:34 -0400 Subject: Remove duplicate lisp-eval-defun definition * lisp/emacs-lisp/lisp-mode.el (lisp-eval-defun): Autoload rather than defining a stub. --- lisp/emacs-lisp/lisp-mode.el | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index b7a5eb774da..2e6e13f1dd1 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -725,11 +725,7 @@ or to switch back to an existing one." ;; Used in old LispM code. (defalias 'common-lisp-mode 'lisp-mode) -;; This will do unless inf-lisp.el is loaded. -(defun lisp-eval-defun (&optional _and-go) - "Send the current defun to the Lisp process made by \\[run-lisp]." - (interactive) - (error "Process lisp does not exist")) +(autoload 'lisp-eval-defun "inf-lisp" nil t) ;; May still be used by some external Lisp-mode variant. (define-obsolete-function-alias 'lisp-comment-indent -- cgit v1.2.3 From 43c84577a3055d5ddf1f5d1b999e6ecca6139f60 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 12 Mar 2017 23:59:19 -0400 Subject: Don't reparse the sexp in indent-sexp (Bug#25122) * lisp/emacs-lisp/lisp-mode.el (calculate-lisp-indent): Let PARSE-START be a parse state that can be reused. (indent-sexp): Pass the running parse state to calculate-lisp-indent instead of the sexp beginning position. Saving the CONTAINING-SEXP-START returned by `calculate-lisp-indent' is no longer needed. Don't bother stopping if we don't descend below init-depth, since we now alway scan the whole buffer (via syntax-ppss) anyway. * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp): Add blank line to test case. --- lisp/emacs-lisp/lisp-mode.el | 76 +++++++++++++++++---------------- test/lisp/emacs-lisp/lisp-mode-tests.el | 5 ++- 2 files changed, 43 insertions(+), 38 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 2e6e13f1dd1..607a4c3d11d 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -785,6 +785,10 @@ In usual case returns an integer: the column to indent to. If the value is nil, that means don't change the indentation because the line starts inside a string. +PARSE-START may be a buffer position to start parsing from, or a +parse state as returned by calling `parse-partial-sexp' up to the +beginning of the current line. + The value can also be a list of the form (COLUMN CONTAINING-SEXP-START). This means that following lines at the same level of indentation should not necessarily be indented the same as this line. @@ -798,12 +802,14 @@ is the buffer position of the start of the containing expression." (desired-indent nil) (retry t) calculate-lisp-indent-last-sexp containing-sexp) - (if parse-start - (goto-char parse-start) - (beginning-of-defun)) - ;; Find outermost containing sexp - (while (< (point) indent-point) - (setq state (parse-partial-sexp (point) indent-point 0))) + (cond ((or (markerp parse-start) (integerp parse-start)) + (goto-char parse-start)) + ((null parse-start) (beginning-of-defun)) + (t (setq state parse-start))) + (unless state + ;; Find outermost containing sexp + (while (< (point) indent-point) + (setq state (parse-partial-sexp (point) indent-point 0)))) ;; Find innermost containing sexp (while (and retry state @@ -1074,11 +1080,6 @@ If optional arg ENDPOS is given, indent each line, stopping when ENDPOS is encountered." (interactive) (let* ((indent-stack (list nil)) - ;; If ENDPOS is non-nil, use beginning of defun as STARTING-POINT. - ;; If ENDPOS is nil, it is safe not to scan before point - ;; since every line we indent is more deeply nested than point is. - (starting-point (save-excursion (if endpos (beginning-of-defun)) - (point))) ;; Use `syntax-ppss' to get initial state so we don't get ;; confused by starting inside a string. We don't use ;; `syntax-ppss' in the loop, because this is measurably @@ -1087,8 +1088,7 @@ ENDPOS is encountered." (init-depth (car state)) (next-depth init-depth) (last-depth init-depth) - (last-syntax-point (point)) - (real-endpos endpos)) + (last-syntax-point (point))) (unless endpos ;; Get error now if we don't have a complete sexp after point. (save-excursion (forward-sexp 1) @@ -1098,16 +1098,21 @@ ENDPOS is encountered." (save-excursion (while (< (point) endpos) ;; Parse this line so we can learn the state to indent the - ;; next line. - (while (progn - (setq state (parse-partial-sexp - last-syntax-point (progn (end-of-line) (point)) - nil nil state)) - ;; Skip over newlines within strings. - (nth 3 state)) - (setq state (parse-partial-sexp (point) (point-max) - nil nil state 'syntax-table)) - (setq last-syntax-point (point))) + ;; next line. Preserve element 2 of the state (last sexp) for + ;; `calculate-lisp-indent'. + (let ((last-sexp (nth 2 state))) + (while (progn + (setq state (parse-partial-sexp + last-syntax-point (progn (end-of-line) (point)) + nil nil state)) + (setq last-sexp (or (nth 2 state) last-sexp)) + ;; Skip over newlines within strings. + (nth 3 state)) + (setq state (parse-partial-sexp (point) (point-max) + nil nil state 'syntax-table)) + (setq last-sexp (or (nth 2 state) last-sexp)) + (setq last-syntax-point (point))) + (setf (nth 2 state) last-sexp)) (setq next-depth (car state)) ;; If the line contains a comment indent it now with ;; `indent-for-comment'. @@ -1120,9 +1125,9 @@ ENDPOS is encountered." (make-list (- init-depth next-depth) nil)) last-depth (- last-depth next-depth) next-depth init-depth)) + ;; Now indent the next line according to what we learned from + ;; parsing the previous one. (forward-line 1) - (when (and (not real-endpos) (<= next-depth init-depth)) - (goto-char endpos)) (when (< (point) endpos) (let ((depth-delta (- next-depth last-depth))) (cond ((< depth-delta 0) @@ -1131,28 +1136,25 @@ ENDPOS is encountered." (setq indent-stack (nconc (make-list depth-delta nil) indent-stack)))) (setq last-depth next-depth)) - ;; Now indent the next line according - ;; to what we learned from parsing the previous one. - (skip-chars-forward " \t") ;; But not if the line is blank, or just a comment (we ;; already called `indent-for-comment' above). + (skip-chars-forward " \t") (unless (or (eolp) (eq (char-syntax (char-after)) ?<)) - (let ((this-indent (car indent-stack))) - (when (listp this-indent) - (let ((val (calculate-lisp-indent - (or (car this-indent) starting-point)))) - (setq - this-indent + (indent-line-to + (or (car indent-stack) + ;; The state here is actually to the end of the + ;; previous line, but that's fine for our purposes. + ;; And parsing over the newline would only destroy + ;; element 2 (last sexp position). + (let ((val (calculate-lisp-indent state))) (cond ((integerp val) (setf (car indent-stack) val)) ((consp val) ; (COLUMN CONTAINING-SEXP-START) - (setf (car indent-stack) (cdr val)) (car val)) ;; `calculate-lisp-indent' only returns nil ;; when we're in a string, but this won't ;; happen because we skip strings above. - (t (error "This shouldn't happen!")))))) - (indent-line-to this-indent)))))))) + (t (error "This shouldn't happen!")))))))))))) (defun indent-pp-sexp (&optional arg) "Indent each line of the list starting just after point, or prettyprint it. diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el index 8e3f2e185cf..27f0bb5ec13 100644 --- a/test/lisp/emacs-lisp/lisp-mode-tests.el +++ b/test/lisp/emacs-lisp/lisp-mode-tests.el @@ -31,6 +31,9 @@ 1 2) 2) + (fun arg1 + + arg2) (1 \"string noindent\" (\"string2 @@ -58,7 +61,7 @@ noindent\" 3 (save-excursion (let ((n 0)) (while (not (eobp)) - (unless (looking-at "noindent") + (unless (looking-at "noindent\\|^[[:blank:]]*$") (insert (make-string n ?\s))) (cl-incf n) (forward-line)))) -- cgit v1.2.3 From 8bb5d7adaf45264900385530c7f76175ba490a77 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 22:35:47 -0400 Subject: * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Clean up marker. --- lisp/emacs-lisp/lisp-mode.el | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 607a4c3d11d..810fc95614d 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1089,12 +1089,13 @@ ENDPOS is encountered." (next-depth init-depth) (last-depth init-depth) (last-syntax-point (point))) - (unless endpos - ;; Get error now if we don't have a complete sexp after point. - (save-excursion (forward-sexp 1) - ;; We need a marker because we modify the buffer - ;; text preceding endpos. - (setq endpos (point-marker)))) + ;; We need a marker because we modify the buffer + ;; text preceding endpos. + (setq endpos (copy-marker + (if endpos endpos + ;; Get error now if we don't have a complete sexp + ;; after point. + (save-excursion (forward-sexp 1) (point))))) (save-excursion (while (< (point) endpos) ;; Parse this line so we can learn the state to indent the @@ -1154,7 +1155,8 @@ ENDPOS is encountered." ;; `calculate-lisp-indent' only returns nil ;; when we're in a string, but this won't ;; happen because we skip strings above. - (t (error "This shouldn't happen!")))))))))))) + (t (error "This shouldn't happen!")))))))))) + (move-marker endpos nil))) (defun indent-pp-sexp (&optional arg) "Indent each line of the list starting just after point, or prettyprint it. -- cgit v1.2.3 From 2f6769f9cdb799e880fdcc09057353a0a2349bfc Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 21:59:13 -0400 Subject: Remove ignored argument from lisp-indent-line * lisp/emacs-lisp/lisp-mode.el (lisp-indent-line): Remove WHOLE-EXP argument, the behavior has long since been handled in `indent-for-tab-command'. Also remove redundant `beg' and `shift-amt' variables and use `indent-line-to'. --- lisp/emacs-lisp/lisp-mode.el | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 810fc95614d..89d5659f300 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -748,14 +748,12 @@ function is `common-lisp-indent-function'." :type 'function :group 'lisp) -(defun lisp-indent-line (&optional _whole-exp) - "Indent current line as Lisp code. -With argument, indent any additional lines of the same expression -rigidly along with this one." - (interactive "P") - (let ((indent (calculate-lisp-indent)) shift-amt - (pos (- (point-max) (point))) - (beg (progn (beginning-of-line) (point)))) +(defun lisp-indent-line () + "Indent current line as Lisp code." + (interactive) + (let ((indent (calculate-lisp-indent)) + (pos (- (point-max) (point)))) + (beginning-of-line) (skip-chars-forward " \t") (if (or (null indent) (looking-at "\\s<\\s<\\s<")) ;; Don't alter indentation of a ;;; comment line @@ -767,11 +765,7 @@ rigidly along with this one." ;; as comment lines, not as code. (progn (indent-for-comment) (forward-char -1)) (if (listp indent) (setq indent (car indent))) - (setq shift-amt (- indent (current-column))) - (if (zerop shift-amt) - nil - (delete-region beg (point)) - (indent-to indent))) + (indent-line-to indent)) ;; If initial point was within line's indentation, ;; position after the indentation. Else stay at same point in text. (if (> (- (point-max) pos) (point)) -- cgit v1.2.3 From 4713dd425beac5cb459704e67dcb8f6faf714375 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 22:27:27 -0400 Subject: Add new `lisp-indent-region' that doesn't reparse the code. Both `lisp-indent-region' and `lisp-indent-line' now use `syntax-ppss' to get initial state, so they will no longer indent string literal contents. * lisp/emacs-lisp/lisp-mode.el (lisp-ppss): New function, like `syntax-ppss', but with a more dependable item 2. (lisp-indent-region): New function, like `indent-region-line-by-line' but additionally keep a running parse state to avoid reparsing the code repeatedly. Use `lisp-ppss' to get initial state. (lisp-indent-line): Take optional PARSE-STATE argument, pass it to `calculate-lisp-indent', use `lisp-ppss' if not given. (lisp-mode-variables): Set `indent-region-function' to `lisp-indent-region'. --- lisp/emacs-lisp/lisp-mode.el | 48 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 89d5659f300..54d916887cd 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -594,6 +594,7 @@ font-lock keywords will not be case sensitive." ;; I believe that newcomment's auto-fill code properly deals with it -stef ;;(set (make-local-variable 'adaptive-fill-mode) nil) (setq-local indent-line-function 'lisp-indent-line) + (setq-local indent-region-function 'lisp-indent-region) (setq-local outline-regexp ";;;\\(;* [^ \t\n]\\|###autoload\\)\\|(") (setq-local outline-level 'lisp-outline-level) (setq-local add-log-current-defun-function #'lisp-current-defun-name) @@ -748,12 +749,51 @@ function is `common-lisp-indent-function'." :type 'function :group 'lisp) -(defun lisp-indent-line () +(defun lisp-ppss (&optional pos) + "Return Parse-Partial-Sexp State at POS, defaulting to point. +Like to `syntax-ppss' but includes the character address of the +last complete sexp in the innermost containing list at position +2 (counting from 0). This is important for lisp indentation." + (unless pos (setq pos (point))) + (let ((pss (syntax-ppss pos))) + (if (nth 9 pss) + (parse-partial-sexp (car (last (nth 9 pss))) pos) + pss))) + +(defun lisp-indent-region (start end) + "Indent region as Lisp code, efficiently." + (save-excursion + (setq end (copy-marker end)) + (goto-char start) + ;; The default `indent-region-line-by-line' doesn't hold a running + ;; parse state, which forces each indent call to reparse from the + ;; beginning. That has O(n^2) complexity. + (let* ((parse-state (lisp-ppss start)) + (last-syntax-point start) + (pr (unless (minibufferp) + (make-progress-reporter "Indenting region..." (point) end)))) + (while (< (point) end) + (unless (and (bolp) (eolp)) + (lisp-indent-line parse-state)) + (forward-line 1) + (let ((last-sexp (nth 2 parse-state))) + (setq parse-state (parse-partial-sexp last-syntax-point (point) + nil nil parse-state)) + ;; It's important to preserve last sexp location for + ;; `calculate-lisp-indent'. + (unless (nth 2 parse-state) + (setf (nth 2 parse-state) last-sexp)) + (setq last-syntax-point (point))) + (and pr (progress-reporter-update pr (point)))) + (and pr (progress-reporter-done pr)) + (move-marker end nil)))) + +(defun lisp-indent-line (&optional parse-state) "Indent current line as Lisp code." (interactive) - (let ((indent (calculate-lisp-indent)) - (pos (- (point-max) (point)))) - (beginning-of-line) + (let ((pos (- (point-max) (point))) + (indent (progn (beginning-of-line) + (calculate-lisp-indent (or parse-state (lisp-ppss)))))) (skip-chars-forward " \t") (if (or (null indent) (looking-at "\\s<\\s<\\s<")) ;; Don't alter indentation of a ;;; comment line -- cgit v1.2.3 From 4af24317b4c043ffa4ce303e57276954920bf204 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Thu, 4 May 2017 23:15:53 -0700 Subject: Fontify the doc-string in some CL forms as such * lisp/emacs-lisp/lisp-mode.el (defconstant, defparameter): Add the doc-string-elt property. (Bug#26778) --- lisp/emacs-lisp/lisp-mode.el | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 54d916887cd..7448864ff99 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -164,6 +164,9 @@ (put 'defalias 'doc-string-elt 3) (put 'defvaralias 'doc-string-elt 3) (put 'define-category 'doc-string-elt 2) +;; CL +(put 'defconstant 'doc-string-elt 3) +(put 'defparameter 'doc-string-elt 3) (defvar lisp-doc-string-elt-property 'doc-string-elt "The symbol property that holds the docstring position info.") -- cgit v1.2.3 From e7b6751c0a74f24c14cd207d57a4e1a95f409256 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 23 Apr 2017 10:43:05 -0400 Subject: Fix lisp-indent-region and indent-sexp (Bug#26619) The new lisp-indent-region introduced in 2017-04-22 "Add new `lisp-indent-region' that doesn't reparse the code." is broken because it doesn't save the calculated indent amounts for already seen sexp depths. Fix this by unifying the indent-sexp and lisp-indent-region code. Furthermore, only preserve position 2 of the running parse when the depth doesn't change. * lisp/emacs-lisp/lisp-mode.el (lisp-ppss): Use an OLDSTATE that corresponds with the start point when calling parse-partial-sexp. (lisp-indent-state): New struct. (lisp-indent-calc-next): New function, extracted from indent-sexp. (indent-sexp, lisp-indent-region): Use it. (lisp-indent-line): Take indentation, instead of parse state. * test/lisp/emacs-lisp/lisp-mode-tests.el (lisp-mode-tests--correctly-indented-sexp): New constant. (lisp-indent-region, lisp-indent-region-defun-with-docstring): (lisp-indent-region-open-paren, lisp-indent-region-in-sexp): New tests. --- lisp/emacs-lisp/lisp-mode.el | 176 +++++++++++++++++--------------- test/lisp/emacs-lisp/lisp-mode-tests.el | 85 ++++++++++++++- 2 files changed, 171 insertions(+), 90 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 7448864ff99..6287f27b139 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -754,49 +754,108 @@ function is `common-lisp-indent-function'." (defun lisp-ppss (&optional pos) "Return Parse-Partial-Sexp State at POS, defaulting to point. -Like to `syntax-ppss' but includes the character address of the -last complete sexp in the innermost containing list at position +Like `syntax-ppss' but includes the character address of the last +complete sexp in the innermost containing list at position 2 (counting from 0). This is important for lisp indentation." (unless pos (setq pos (point))) (let ((pss (syntax-ppss pos))) (if (nth 9 pss) - (parse-partial-sexp (car (last (nth 9 pss))) pos) + (let ((sexp-start (car (last (nth 9 pss))))) + (parse-partial-sexp sexp-start pos nil nil (syntax-ppss sexp-start))) pss))) +(cl-defstruct (lisp-indent-state + (:constructor nil) + (:constructor lisp-indent-initial-state + (&aux (ppss (lisp-ppss)) + (ppss-point (point)) + (depth (car ppss)) + (stack (make-list (1+ depth) nil))))) + stack ;; Cached indentation, per depth. + ppss + depth + ppss-point) + +(defun lisp-indent-calc-next (state) + "Move to next line and return calculated indent for it. +STATE is updated by side effect, the first state should be +created by `lisp-indent-initial-state'. This function may move +by more than one line to cross a string literal." + (pcase-let (((cl-struct lisp-indent-state + (stack indent-stack) ppss depth ppss-point) + state)) + ;; Parse this line so we can learn the state to indent the + ;; next line. + (while (let ((last-sexp (nth 2 ppss))) + (setq ppss (parse-partial-sexp + ppss-point (progn (end-of-line) (point)) + nil nil ppss)) + ;; Preserve last sexp of state (position 2) for + ;; `calculate-lisp-indent', if we're at the same depth. + (if (and (not (nth 2 ppss)) (= depth (car ppss))) + (setf (nth 2 ppss) last-sexp) + (setq last-sexp (nth 2 ppss))) + ;; Skip over newlines within strings. + (nth 3 ppss)) + (let ((string-start (nth 8 ppss))) + (setq ppss (parse-partial-sexp (point) (point-max) + nil nil ppss 'syntax-table)) + (setf (nth 2 ppss) string-start)) ; Finished a complete string. + (setq ppss-point (point))) + (setq ppss-point (point)) + (let* ((next-depth (car ppss)) + (depth-delta (- next-depth depth))) + (cond ((< depth-delta 0) + (setq indent-stack (nthcdr (- depth-delta) indent-stack))) + ((> depth-delta 0) + (setq indent-stack (nconc (make-list depth-delta nil) + indent-stack)))) + (setq depth next-depth)) + (prog1 + (let (indent) + (cond ((= (forward-line 1) 1) nil) + ((car indent-stack)) + ((integerp (setq indent (calculate-lisp-indent ppss))) + (setf (car indent-stack) indent)) + ((consp indent) ; (COLUMN CONTAINING-SEXP-START) + (car indent)) + ;; This only happens if we're in a string. + (t (error "This shouldn't happen")))) + (setf (lisp-indent-state-stack state) indent-stack) + (setf (lisp-indent-state-depth state) depth) + (setf (lisp-indent-state-ppss-point state) ppss-point) + (setf (lisp-indent-state-ppss state) ppss)))) + (defun lisp-indent-region (start end) "Indent region as Lisp code, efficiently." (save-excursion (setq end (copy-marker end)) (goto-char start) + (beginning-of-line) ;; The default `indent-region-line-by-line' doesn't hold a running ;; parse state, which forces each indent call to reparse from the ;; beginning. That has O(n^2) complexity. - (let* ((parse-state (lisp-ppss start)) - (last-syntax-point start) + (let* ((parse-state (lisp-indent-initial-state)) (pr (unless (minibufferp) (make-progress-reporter "Indenting region..." (point) end)))) - (while (< (point) end) - (unless (and (bolp) (eolp)) - (lisp-indent-line parse-state)) - (forward-line 1) - (let ((last-sexp (nth 2 parse-state))) - (setq parse-state (parse-partial-sexp last-syntax-point (point) - nil nil parse-state)) - ;; It's important to preserve last sexp location for - ;; `calculate-lisp-indent'. - (unless (nth 2 parse-state) - (setf (nth 2 parse-state) last-sexp)) - (setq last-syntax-point (point))) - (and pr (progress-reporter-update pr (point)))) + (let ((ppss (lisp-indent-state-ppss parse-state))) + (unless (or (and (bolp) (eolp)) (nth 3 ppss)) + (lisp-indent-line (calculate-lisp-indent ppss)))) + (let ((indent nil)) + (while (progn (setq indent (lisp-indent-calc-next parse-state)) + (< (point) end)) + (unless (or (and (bolp) (eolp)) (not indent)) + (lisp-indent-line indent)) + (and pr (progress-reporter-update pr (point))))) (and pr (progress-reporter-done pr)) (move-marker end nil)))) -(defun lisp-indent-line (&optional parse-state) +(defun lisp-indent-line (&optional indent) "Indent current line as Lisp code." (interactive) (let ((pos (- (point-max) (point))) (indent (progn (beginning-of-line) - (calculate-lisp-indent (or parse-state (lisp-ppss)))))) + (or indent (calculate-lisp-indent (lisp-ppss)))))) (skip-chars-forward " \t") (if (or (null indent) (looking-at "\\s<\\s<\\s<")) ;; Don't alter indentation of a ;;; comment line @@ -1116,16 +1175,7 @@ Lisp function does not specify a special indentation." If optional arg ENDPOS is given, indent each line, stopping when ENDPOS is encountered." (interactive) - (let* ((indent-stack (list nil)) - ;; Use `syntax-ppss' to get initial state so we don't get - ;; confused by starting inside a string. We don't use - ;; `syntax-ppss' in the loop, because this is measurably - ;; slower when we're called on a long list. - (state (syntax-ppss)) - (init-depth (car state)) - (next-depth init-depth) - (last-depth init-depth) - (last-syntax-point (point))) + (let* ((parse-state (lisp-indent-initial-state))) ;; We need a marker because we modify the buffer ;; text preceding endpos. (setq endpos (copy-marker @@ -1135,64 +1185,20 @@ ENDPOS is encountered." (save-excursion (forward-sexp 1) (point))))) (save-excursion (while (< (point) endpos) - ;; Parse this line so we can learn the state to indent the - ;; next line. Preserve element 2 of the state (last sexp) for - ;; `calculate-lisp-indent'. - (let ((last-sexp (nth 2 state))) - (while (progn - (setq state (parse-partial-sexp - last-syntax-point (progn (end-of-line) (point)) - nil nil state)) - (setq last-sexp (or (nth 2 state) last-sexp)) - ;; Skip over newlines within strings. - (nth 3 state)) - (setq state (parse-partial-sexp (point) (point-max) - nil nil state 'syntax-table)) - (setq last-sexp (or (nth 2 state) last-sexp)) - (setq last-syntax-point (point))) - (setf (nth 2 state) last-sexp)) - (setq next-depth (car state)) - ;; If the line contains a comment indent it now with - ;; `indent-for-comment'. - (when (nth 4 state) - (indent-for-comment) - (end-of-line)) - (setq last-syntax-point (point)) - (when (< next-depth init-depth) - (setq indent-stack (nconc indent-stack - (make-list (- init-depth next-depth) nil)) - last-depth (- last-depth next-depth) - next-depth init-depth)) - ;; Now indent the next line according to what we learned from - ;; parsing the previous one. - (forward-line 1) - (when (< (point) endpos) - (let ((depth-delta (- next-depth last-depth))) - (cond ((< depth-delta 0) - (setq indent-stack (nthcdr (- depth-delta) indent-stack))) - ((> depth-delta 0) - (setq indent-stack (nconc (make-list depth-delta nil) - indent-stack)))) - (setq last-depth next-depth)) + (let ((indent (lisp-indent-calc-next parse-state))) + ;; If the line contains a comment indent it now with + ;; `indent-for-comment'. + (when (nth 4 (lisp-indent-state-ppss parse-state)) + (save-excursion + (goto-char (lisp-indent-state-ppss-point parse-state)) + (indent-for-comment) + (setf (lisp-indent-state-ppss-point parse-state) + (line-end-position)))) ;; But not if the line is blank, or just a comment (we ;; already called `indent-for-comment' above). (skip-chars-forward " \t") - (unless (or (eolp) (eq (char-syntax (char-after)) ?<)) - (indent-line-to - (or (car indent-stack) - ;; The state here is actually to the end of the - ;; previous line, but that's fine for our purposes. - ;; And parsing over the newline would only destroy - ;; element 2 (last sexp position). - (let ((val (calculate-lisp-indent state))) - (cond ((integerp val) - (setf (car indent-stack) val)) - ((consp val) ; (COLUMN CONTAINING-SEXP-START) - (car val)) - ;; `calculate-lisp-indent' only returns nil - ;; when we're in a string, but this won't - ;; happen because we skip strings above. - (t (error "This shouldn't happen!")))))))))) + (unless (or (eolp) (eq (char-syntax (char-after)) ?<) (not indent)) + (indent-line-to indent))))) (move-marker endpos nil))) (defun indent-pp-sexp (&optional arg) diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el index 27f0bb5ec13..1f78eb30105 100644 --- a/test/lisp/emacs-lisp/lisp-mode-tests.el +++ b/test/lisp/emacs-lisp/lisp-mode-tests.el @@ -21,10 +21,7 @@ (require 'cl-lib) (require 'lisp-mode) -(ert-deftest indent-sexp () - "Test basics of \\[indent-sexp]." - (with-temp-buffer - (insert "\ +(defconst lisp-mode-tests--correctly-indented-sexp "\ \(a (prog1 (prog1 @@ -42,9 +39,14 @@ noindent\" 3 2) ; comment ;; comment b)") + +(ert-deftest indent-sexp () + "Test basics of \\[indent-sexp]." + (with-temp-buffer + (insert lisp-mode-tests--correctly-indented-sexp) (goto-char (point-min)) (let ((indent-tabs-mode nil) - (correct (buffer-string))) + (correct lisp-mode-tests--correctly-indented-sexp)) (dolist (mode '(fundamental-mode emacs-lisp-mode)) (funcall mode) (indent-sexp) @@ -97,5 +99,78 @@ noindent\" 3 (indent-sexp) (should (equal (buffer-string) correct))))) +(ert-deftest lisp-indent-region () + "Test basics of `lisp-indent-region'." + (with-temp-buffer + (insert lisp-mode-tests--correctly-indented-sexp) + (goto-char (point-min)) + (let ((indent-tabs-mode nil) + (correct lisp-mode-tests--correctly-indented-sexp)) + (emacs-lisp-mode) + (indent-region (point-min) (point-max)) + ;; Don't mess up correctly indented code. + (should (string= (buffer-string) correct)) + ;; Correctly add indentation. + (save-excursion + (while (not (eobp)) + (delete-horizontal-space) + (forward-line))) + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) correct)) + ;; Correctly remove indentation. + (save-excursion + (let ((n 0)) + (while (not (eobp)) + (unless (looking-at "noindent\\|^[[:blank:]]*$") + (insert (make-string n ?\s))) + (cl-incf n) + (forward-line)))) + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) correct))))) + + +(ert-deftest lisp-indent-region-defun-with-docstring () + "Test Bug#26619." + (with-temp-buffer + (insert "\ +\(defun test () + \"This is a test. +Test indentation in emacs-lisp-mode\" + (message \"Hi!\"))") + (let ((indent-tabs-mode nil) + (correct (buffer-string))) + (emacs-lisp-mode) + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) correct))))) + +(ert-deftest lisp-indent-region-open-paren () + (with-temp-buffer + (insert "\ +\(with-eval-after-load 'foo + (setq bar `( + baz)))") + (let ((indent-tabs-mode nil) + (correct (buffer-string))) + (emacs-lisp-mode) + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) correct))))) + +(ert-deftest lisp-indent-region-in-sexp () + (with-temp-buffer + (insert "\ +\(when t + (when t + (list 1 2 3) + 'etc) + (quote etc) + (quote etc))") + (let ((indent-tabs-mode nil) + (correct (buffer-string))) + (emacs-lisp-mode) + (search-backward "1") + (indent-region (point) (point-max)) + (should (equal (buffer-string) correct))))) + + (provide 'lisp-mode-tests) ;;; lisp-mode-tests.el ends here -- cgit v1.2.3 From 750f0e2e79e1bdc3246b07aa3219cab34ebde6e7 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Thu, 11 May 2017 18:12:40 -0400 Subject: Make sure indent-sexp stops at end of sexp (Bug#26878) * lisp/emacs-lisp/lisp-mode.el (indent-sexp): Check endpos before indenting. * test/lisp/emacs-lisp/lisp-mode-tests.el (indent-sexp-stop): New test. --- lisp/emacs-lisp/lisp-mode.el | 33 ++++++++++++++++++--------------- test/lisp/emacs-lisp/lisp-mode-tests.el | 14 ++++++++++++++ 2 files changed, 32 insertions(+), 15 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 6287f27b139..3334471d251 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1184,21 +1184,24 @@ ENDPOS is encountered." ;; after point. (save-excursion (forward-sexp 1) (point))))) (save-excursion - (while (< (point) endpos) - (let ((indent (lisp-indent-calc-next parse-state))) - ;; If the line contains a comment indent it now with - ;; `indent-for-comment'. - (when (nth 4 (lisp-indent-state-ppss parse-state)) - (save-excursion - (goto-char (lisp-indent-state-ppss-point parse-state)) - (indent-for-comment) - (setf (lisp-indent-state-ppss-point parse-state) - (line-end-position)))) - ;; But not if the line is blank, or just a comment (we - ;; already called `indent-for-comment' above). - (skip-chars-forward " \t") - (unless (or (eolp) (eq (char-syntax (char-after)) ?<) (not indent)) - (indent-line-to indent))))) + (while (let ((indent (lisp-indent-calc-next parse-state)) + (ppss (lisp-indent-state-ppss parse-state))) + ;; If the line contains a comment indent it now with + ;; `indent-for-comment'. + (when (and (nth 4 ppss) (<= (nth 8 ppss) endpos)) + (save-excursion + (goto-char (lisp-indent-state-ppss-point parse-state)) + (indent-for-comment) + (setf (lisp-indent-state-ppss-point parse-state) + (line-end-position)))) + (when (< (point) endpos) + ;; Indent the next line, unless it's blank, or just a + ;; comment (we will `indent-for-comment' the latter). + (skip-chars-forward " \t") + (unless (or (eolp) (not indent) + (eq (char-syntax (char-after)) ?<)) + (indent-line-to indent)) + t)))) (move-marker endpos nil))) (defun indent-pp-sexp (&optional arg) diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el index 1f78eb30105..f2fe7a6cf41 100644 --- a/test/lisp/emacs-lisp/lisp-mode-tests.el +++ b/test/lisp/emacs-lisp/lisp-mode-tests.el @@ -99,6 +99,20 @@ noindent\" 3 (indent-sexp) (should (equal (buffer-string) correct))))) +(ert-deftest indent-sexp-stop () + "Make sure `indent-sexp' stops at the end of the sexp." + ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=26878. + (with-temp-buffer + (emacs-lisp-mode) + (insert "(a ()\n)") + (let ((original (buffer-string))) + (search-backward "a ") + (goto-char (match-end 0)) + (indent-sexp) + ;; The final paren should not be indented, because the sexp + ;; we're indenting ends on the previous line. + (should (equal (buffer-string) original))))) + (ert-deftest lisp-indent-region () "Test basics of `lisp-indent-region'." (with-temp-buffer -- cgit v1.2.3 From d158629cb6d0dd7cf0227d993d59ea6faa4438c9 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Fri, 21 Apr 2017 00:00:26 -0400 Subject: Give a name to lisp-mode's adaptive-fill-function (Bug#22730) * lisp/emacs-lisp/lisp-mode.el (lisp-adaptive-fill): New function. (lisp-mode-variables): Use it. --- lisp/emacs-lisp/lisp-mode.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 3334471d251..1e38d44e1b1 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -576,6 +576,13 @@ Lisp font lock syntactic face function." font-lock-string-face)))) font-lock-comment-face)) +(defun lisp-adaptive-fill () + "Return fill prefix found at point. +Value for `adaptive-fill-function'." + ;; Adaptive fill mode gets the fill wrong for a one-line paragraph made of + ;; a single docstring. Let's fix it here. + (if (looking-at "\\s-+\"[^\n\"]+\"\\s-*$") "")) + (defun lisp-mode-variables (&optional lisp-syntax keywords-case-insensitive elisp) "Common initialization routine for lisp modes. @@ -587,10 +594,7 @@ font-lock keywords will not be case sensitive." (set-syntax-table lisp-mode-syntax-table)) (setq-local paragraph-ignore-fill-prefix t) (setq-local fill-paragraph-function 'lisp-fill-paragraph) - ;; Adaptive fill mode gets the fill wrong for a one-line paragraph made of - ;; a single docstring. Let's fix it here. - (setq-local adaptive-fill-function - (lambda () (if (looking-at "\\s-+\"[^\n\"]+\"\\s-*$") ""))) + (setq-local adaptive-fill-function #'lisp-adaptive-fill) ;; Adaptive fill mode gets in the way of auto-fill, ;; and should make no difference for explicit fill ;; because lisp-fill-paragraph should do the job. -- cgit v1.2.3 From cc8aa484cdab6b2f33a8c95a5778193c762412b9 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sat, 10 Jun 2017 09:50:48 -0400 Subject: Fix wrong indentation after string literal (Bug#27306) * lisp/emacs-lisp/lisp-mode.el (lisp-indent-state) (lisp-indent-calc-next): Remove `depth' field, use (car ppss) instead. * test/lisp/emacs-lisp/lisp-mode-tests.el (lisp-indent-region-after-string-literal): New test. --- lisp/emacs-lisp/lisp-mode.el | 27 +++++++++++++-------------- test/lisp/emacs-lisp/lisp-mode-tests.el | 13 +++++++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) (limited to 'lisp/emacs-lisp/lisp-mode.el') diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 1e38d44e1b1..59db00d5f96 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -773,11 +773,9 @@ complete sexp in the innermost containing list at position (:constructor lisp-indent-initial-state (&aux (ppss (lisp-ppss)) (ppss-point (point)) - (depth (car ppss)) - (stack (make-list (1+ depth) nil))))) + (stack (make-list (1+ (car ppss)) nil))))) stack ;; Cached indentation, per depth. ppss - depth ppss-point) (defun lisp-indent-calc-next (state) @@ -785,9 +783,11 @@ complete sexp in the innermost containing list at position STATE is updated by side effect, the first state should be created by `lisp-indent-initial-state'. This function may move by more than one line to cross a string literal." - (pcase-let (((cl-struct lisp-indent-state - (stack indent-stack) ppss depth ppss-point) - state)) + (pcase-let* (((cl-struct lisp-indent-state + (stack indent-stack) ppss ppss-point) + state) + (indent-depth (car ppss)) ; Corresponding to indent-stack. + (depth indent-depth)) ;; Parse this line so we can learn the state to indent the ;; next line. (while (let ((last-sexp (nth 2 ppss))) @@ -799,22 +799,22 @@ by more than one line to cross a string literal." (if (and (not (nth 2 ppss)) (= depth (car ppss))) (setf (nth 2 ppss) last-sexp) (setq last-sexp (nth 2 ppss))) + (setq depth (car ppss)) ;; Skip over newlines within strings. (nth 3 ppss)) (let ((string-start (nth 8 ppss))) - (setq ppss (parse-partial-sexp (point) (point-max) - nil nil ppss 'syntax-table)) - (setf (nth 2 ppss) string-start)) ; Finished a complete string. + (setq ppss (parse-partial-sexp (point) (point-max) + nil nil ppss 'syntax-table)) + (setf (nth 2 ppss) string-start) ; Finished a complete string. + (setq depth (car ppss))) (setq ppss-point (point))) (setq ppss-point (point)) - (let* ((next-depth (car ppss)) - (depth-delta (- next-depth depth))) + (let* ((depth-delta (- depth indent-depth))) (cond ((< depth-delta 0) (setq indent-stack (nthcdr (- depth-delta) indent-stack))) ((> depth-delta 0) (setq indent-stack (nconc (make-list depth-delta nil) - indent-stack)))) - (setq depth next-depth)) + indent-stack))))) (prog1 (let (indent) (cond ((= (forward-line 1) 1) nil) @@ -826,7 +826,6 @@ by more than one line to cross a string literal." ;; This only happens if we're in a string. (t (error "This shouldn't happen")))) (setf (lisp-indent-state-stack state) indent-stack) - (setf (lisp-indent-state-depth state) depth) (setf (lisp-indent-state-ppss-point state) ppss-point) (setf (lisp-indent-state-ppss state) ppss)))) diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el index f2fe7a6cf41..582041cfc2d 100644 --- a/test/lisp/emacs-lisp/lisp-mode-tests.el +++ b/test/lisp/emacs-lisp/lisp-mode-tests.el @@ -185,6 +185,19 @@ Test indentation in emacs-lisp-mode\" (indent-region (point) (point-max)) (should (equal (buffer-string) correct))))) +(ert-deftest lisp-indent-region-after-string-literal () + (with-temp-buffer + (insert "\ +\(user-error \"Unexpected initialization file: `%s' +Expected initialization file: `%s'\" + (abbreviate-file-name user-init-file) + (abbreviate-file-name this-init-file))") + (let ((indent-tabs-mode nil) + (correct (buffer-string))) + (emacs-lisp-mode) + (indent-region (point-min) (point-max)) + (should (equal (buffer-string) correct))))) + (provide 'lisp-mode-tests) ;;; lisp-mode-tests.el ends here -- cgit v1.2.3