all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: npostavs@users.sourceforge.net
To: Michael Heerdegen <michael_heerdegen@web.de>
Cc: 26619@debbugs.gnu.org, Kaushal Modi <kaushal.modi@gmail.com>
Subject: bug#26619: 26.0.50; Wrong indentation in emacs-lisp-mode
Date: Tue, 25 Apr 2017 23:53:47 -0400	[thread overview]
Message-ID: <87mvb3omd0.fsf@users.sourceforge.net> (raw)
In-Reply-To: <87vaprlucs.fsf@drachen> (Michael Heerdegen's message of "Wed, 26 Apr 2017 05:29:23 +0200")

[-- Attachment #1: Type: text/plain, Size: 258 bytes --]

Michael Heerdegen <michael_heerdegen@web.de> writes:

> npostavs@users.sourceforge.net writes:
>
> Thanks for the quick response.  I'll keep my eyes open and try to test
> some more.  Could you please send me a cumulative patch relative to
> master?

Sure.


[-- Attachment #2: cumulative patch --]
[-- Type: text/plain, Size: 9317 bytes --]

diff --git c/lisp/emacs-lisp/lisp-mode.el w/lisp/emacs-lisp/lisp-mode.el
index fa931e76ad..12fd53140c 100644
--- c/lisp/emacs-lisp/lisp-mode.el
+++ w/lisp/emacs-lisp/lisp-mode.el
@@ -764,6 +764,36 @@ lisp-ppss
         (parse-partial-sexp (car (last (nth 9 pss))) pos)
       pss)))
 
+(defun lisp-mode--indent-cache (init-depth)
+  "Returns a closure that computes indentation, caching by depth."
+  (let ((indent-stack (list nil))
+        (last-depth init-depth))
+    (lambda (&optional depth-or-state)
+      "Pass depth to update cache, or parse state for indentation."
+      (if (listp depth-or-state)        ; It's a parse state.
+          (let ((val (if (car indent-stack) indent-stack
+                       (calculate-lisp-indent depth-or-state))))
+            (cond ((nth 3 depth-or-state) nil) ; Inside a string.
+                  ((integerp val)
+                   (setf (car indent-stack) val))
+                  ((consp val)    ; (COLUMN CONTAINING-SEXP-START)
+                   (car val))
+                  ;; This only happens if we're in a string.
+                  (t (error "This shouldn't happen"))))
+        (let ((depth depth-or-state))   ; It's a depth.
+          (when (< depth init-depth)
+            (setq indent-stack (nconc indent-stack
+                                      (make-list (- init-depth depth) nil))
+                  last-depth (- last-depth depth)
+                  depth init-depth))
+          (let ((depth-delta (- 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 depth)))))))
+
 (defun lisp-indent-region (start end)
   "Indent region as Lisp code, efficiently."
   (save-excursion
@@ -773,31 +803,45 @@ lisp-indent-region
     ;; parse state, which forces each indent call to reparse from the
     ;; beginning.  That has O(n^2) complexity.
     (let* ((parse-state (lisp-ppss start))
+           (calc-indent (lisp-mode--indent-cache (car parse-state)))
            (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))
+          (lisp-indent-line (funcall calc-indent 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)))
+        (let ((oldstate parse-state)
+              (target-point (point)))
+          (while
+              (progn
+                (setq parse-state (parse-partial-sexp last-syntax-point target-point
+                                                      nil t oldstate))
+                (if (>= (point) target-point)
+                    nil                   ; Done.
+                  (when (= (nth 0 parse-state) (nth 0 oldstate)) ; Stopped before open paren.
+                    (setq parse-state (parse-partial-sexp last-syntax-point target-point
+                                                          (1+ (nth 0 parse-state)) nil parse-state)))
+                  (setq last-syntax-point (point))
+                  ;; It's important to preserve last sexp location for
+                  ;; `calculate-lisp-indent', but it's only relevant at the
+                  ;; same depth.
+                  (unless (or (nth 2 parse-state) (/= (nth 0 parse-state) (nth 0 oldstate)))
+                    (setf (nth 2 parse-state) (nth 2 oldstate)))
+                  t))
+            (setq oldstate parse-state)))
+        ;; Update cache's depth stack.
+        (funcall calc-indent (car parse-state))
         (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
@@ -1117,15 +1161,12 @@ indent-sexp
 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
+  (let* (;; 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)
+         (calc-indent (lisp-mode--indent-cache (car state)))
          (last-syntax-point (point)))
     ;; We need a marker because we modify the buffer
     ;; text preceding endpos.
@@ -1139,7 +1180,8 @@ indent-sexp
         ;; 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)))
+        (let ((last-depth (nth 0 state))
+              (last-sexp (nth 2 state)))
           (while (progn
                    (setq state (parse-partial-sexp
                                 last-syntax-point (progn (end-of-line) (point))
@@ -1149,51 +1191,33 @@ indent-sexp
                    (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))
+            (when (nth 2 state)
+              (setq last-sexp (nth 2 state))
+              (setq last-depth (nth 0 state)))
             (setq last-syntax-point (point)))
           (setf (nth 2 state) last-sexp))
-        (setq next-depth (car state))
+        ;; Update cache's depth stack.
+        (funcall calc-indent (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))
           ;; 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!"))))))))))
+             ;; 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).
+             (funcall calc-indent state))))))
     (move-marker endpos nil)))
 
 (defun indent-pp-sexp (&optional arg)

  reply	other threads:[~2017-04-26  3:53 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-04-23  7:17 bug#26619: 26.0.50; Wrong indentation in emacs-lisp-mode Tino Calancha
2017-04-23 14:51 ` npostavs
2017-04-24 21:03 ` Kaushal Modi
2017-04-26  3:05   ` Michael Heerdegen
2017-04-26  3:22   ` npostavs
2017-04-26  3:29     ` Michael Heerdegen
2017-04-26  3:53       ` npostavs [this message]
2017-04-26  4:00         ` Michael Heerdegen
2017-04-26 18:27         ` Kaushal Modi
2017-04-26 22:31           ` npostavs
2017-04-27 11:52             ` Michael Heerdegen
2017-04-29  2:20               ` npostavs
2017-04-29 12:00                 ` Michael Heerdegen
2017-05-05 15:55                 ` Kaushal Modi
2017-05-05 22:43                   ` npostavs
2017-05-06  1:58                     ` Kaushal Modi
2017-05-06  2:42                       ` npostavs
2017-05-08 12:46                       ` Michael Heerdegen
2017-05-10  0:57                         ` npostavs

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87mvb3omd0.fsf@users.sourceforge.net \
    --to=npostavs@users.sourceforge.net \
    --cc=26619@debbugs.gnu.org \
    --cc=kaushal.modi@gmail.com \
    --cc=michael_heerdegen@web.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.