From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED!not-for-mail From: npostavs@users.sourceforge.net Newsgroups: gmane.emacs.bugs Subject: bug#26619: 26.0.50; Wrong indentation in emacs-lisp-mode Date: Sun, 23 Apr 2017 10:51:09 -0400 Message-ID: <877f2bp482.fsf@users.sourceforge.net> References: <87shkzsidm.fsf@calancha-pc> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1492959015 785 195.159.176.226 (23 Apr 2017 14:50:15 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Sun, 23 Apr 2017 14:50:15 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) Cc: 26619@debbugs.gnu.org To: Tino Calancha Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Sun Apr 23 16:50:08 2017 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by blaine.gmane.org with esmtp (Exim 4.84_2) (envelope-from ) id 1d2Iq0-0008Ti-9L for geb-bug-gnu-emacs@m.gmane.org; Sun, 23 Apr 2017 16:50:08 +0200 Original-Received: from localhost ([::1]:39490 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d2Iq5-0003jc-Rb for geb-bug-gnu-emacs@m.gmane.org; Sun, 23 Apr 2017 10:50:13 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:38633) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d2Ipy-0003gd-7I for bug-gnu-emacs@gnu.org; Sun, 23 Apr 2017 10:50:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d2Ipv-0007uT-2v for bug-gnu-emacs@gnu.org; Sun, 23 Apr 2017 10:50:06 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:38396) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1d2Ipu-0007uP-Tu for bug-gnu-emacs@gnu.org; Sun, 23 Apr 2017 10:50:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1d2Ipu-0001ap-D5 for bug-gnu-emacs@gnu.org; Sun, 23 Apr 2017 10:50:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: npostavs@users.sourceforge.net Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 23 Apr 2017 14:50:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 26619 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: Original-Received: via spool by 26619-submit@debbugs.gnu.org id=B26619.14929589906100 (code B ref 26619); Sun, 23 Apr 2017 14:50:02 +0000 Original-Received: (at 26619) by debbugs.gnu.org; 23 Apr 2017 14:49:50 +0000 Original-Received: from localhost ([127.0.0.1]:36595 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1d2Iph-0001aF-Eb for submit@debbugs.gnu.org; Sun, 23 Apr 2017 10:49:49 -0400 Original-Received: from mail-io0-f169.google.com ([209.85.223.169]:33450) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1d2Ipe-0001ZW-QK; Sun, 23 Apr 2017 10:49:47 -0400 Original-Received: by mail-io0-f169.google.com with SMTP id k87so159711561ioi.0; Sun, 23 Apr 2017 07:49:46 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=jinpW0hChPDZEH63uRVfW8EMmV1KpLmvn5UZCAodKIA=; b=HSJaQGBUo1lcNUR0LB6UQjNXbCPL71sdQRXPvCH9jkUjFTD+Pew+ZK2B9Rif8BDr9i tqtJtqtrwoqjRtimsYgsv1rWJnqmcFOxYYQ94svBq8pB22SSm8atr9eA0OMUmkzMlYXN mdO8sNc/jNQsl5wTiAmVKdDgBDzLEMuQCGnbs5sgkTgeyIrpkhtGPEARADgrnK5kGg1n Sy73vNTrmYbp14cWvivMqFXpBeMiPT2MsyWJmMFWwk29NNX5I03Fb7XHIUBho03gbGqo f4YmSeamkAfol3mWN9ilPse8dmsRkvUHVLMa+C/XQa5ZWQR0oGhiGVMvF75ZL7S+b1jG UPtQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:references:date :in-reply-to:message-id:user-agent:mime-version; bh=jinpW0hChPDZEH63uRVfW8EMmV1KpLmvn5UZCAodKIA=; b=MIUDr0Mz0KTzkSY2UV4kMwn2A5bZWBLlVqz+j9WCYA4VeR21Sac5H4FYLibMNSV2yS 3cfTbsTL8xW43GyA+QqH1YF45wb3ZLTMcwnl6W0+2QQ79PuYCzugAcPjXIzI+0pQdBfY 6Y0o+p9WhTuHy+61yzx8HR2XBS8FZ+MGXYl+evY/3/Jtge2SImrf4bIXcN2bzZ6Y5tyj v4ZZVS/NxHtCDK4k5H29S7a7QHHDuMRqXz8ipPbZd7rkiijwuvk0LXJ8HUeEEN110m7M u1PBj5DOIi45u5dszkz47IqO+AanTieCGHs7kaUQMkJlG3sL0mOc6EAJkFSlguuuQR2P Cdcg== X-Gm-Message-State: AN3rC/6M0KSTIYi9BaTW83E3roool29haXSJn0MAaJsCpgznurVcOaAl yH1ap9Tj9N8VIQ== X-Received: by 10.107.15.76 with SMTP id x73mr2180606ioi.124.1492958980992; Sun, 23 Apr 2017 07:49:40 -0700 (PDT) Original-Received: from zony ([45.2.7.65]) by smtp.googlemail.com with ESMTPSA id f19sm3063038itf.19.2017.04.23.07.49.39 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Sun, 23 Apr 2017 07:49:39 -0700 (PDT) In-Reply-To: <87shkzsidm.fsf@calancha-pc> (Tino Calancha's message of "Sun, 23 Apr 2017 16:17:09 +0900") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 208.118.235.43 X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.org gmane.emacs.bugs:131906 Archived-At: --=-=-= Content-Type: text/plain tags 26619 patch quit Tino Calancha writes: > Write a file /tmp/test.el with contents: > (defun test () > "This is a test. > Test indentation in emacs-lisp-mode" > (message "Hi!")) > > emacs -Q /tmp/test.el > C-x h TAB ; Wrong indentation. > > This happens after commit 6fa9cc0593150a318f0e08e69ec10672d548a7c1 > ; Merge: improve indent-sexp and lisp-indent-region performance Oops, right. I made lisp-indent-region keep a running parse state like indent-sexp, but I should have taken the indent-stack too. Here's a patch: --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=v1-0001-Fix-lisp-indent-region-with-multiline-string-lite.patch Content-Description: patch >From bb640d2f1570b38d98950fb0c374f66ef0988bd2 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 23 Apr 2017 10:43:05 -0400 Subject: [PATCH v1] Fix lisp-indent-region with multiline string literals (Bug#26619) * lisp/emacs-lisp/lisp-mode.el (lisp-mode--indent-cache): New function, extracted from `indent-sexp'. (lisp-indent-region, indent-sexp): Use it. * test/lisp/emacs-lisp/lisp-mode-tests.el (lisp-mode-tests--correctly-indented-sexp): Extract literal to constant. (lisp-indent-region, lisp-indent-region-defun-with-docstring): New tests. --- lisp/emacs-lisp/lisp-mode.el | 80 ++++++++++++++++++--------------- test/lisp/emacs-lisp/lisp-mode-tests.el | 57 ++++++++++++++++++++--- 2 files changed, 97 insertions(+), 40 deletions(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index fa931e76ad..d619dd8f4c 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/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,12 +803,13 @@ 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) @@ -788,16 +819,18 @@ lisp-indent-region (unless (nth 2 parse-state) (setf (nth 2 parse-state) last-sexp)) (setq last-syntax-point (point))) + ;; 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 +1150,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. @@ -1152,48 +1182,28 @@ indent-sexp (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)) + ;; 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) diff --git a/test/lisp/emacs-lisp/lisp-mode-tests.el b/test/lisp/emacs-lisp/lisp-mode-tests.el index 27f0bb5ec1..3c6364acc8 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 @@ 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,50 @@ (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))))) + + (provide 'lisp-mode-tests) ;;; lisp-mode-tests.el ends here -- 2.11.1 --=-=-=--