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#25122: 24.5; function describe-variable hangs on large variables Date: Mon, 17 Apr 2017 23:53:30 -0400 Message-ID: <87fuh6s75x.fsf@users.sourceforge.net> References: <20161206022112.GF25778@E15-2016.optimum.net> <87twahk19y.fsf@gmail.com> <87d1h4fld5.fsf@users.sourceforge.net> <871sxkyv2m.fsf@gmail.com> <87mvcs8j7w.fsf@users.sourceforge.net> <87lgsbtxwe.fsf@gmail.com> <871su38ogm.fsf@users.sourceforge.net> <87d1dnrq96.fsf@gmail.com> <87wpbu7f9i.fsf@users.sourceforge.net> <87r32278wf.fsf@users.sourceforge.net> <87lgs97pg5.fsf@users.sourceforge.net> <87innd6zti.fsf@users.sourceforge.net> <878to653tr.fsf@users.sourceforge.net> NNTP-Posting-Host: blaine.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: blaine.gmane.org 1492487593 12512 195.159.176.226 (18 Apr 2017 03:53:13 GMT) X-Complaints-To: usenet@blaine.gmane.org NNTP-Posting-Date: Tue, 18 Apr 2017 03:53:13 +0000 (UTC) User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/25.2 (gnu/linux) Cc: 25122@debbugs.gnu.org, Stefan Monnier , Boruch Baum To: Thierry Volpiatto Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane.org@gnu.org Tue Apr 18 05:53:09 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 1d0KCS-00037P-Mn for geb-bug-gnu-emacs@m.gmane.org; Tue, 18 Apr 2017 05:53:09 +0200 Original-Received: from localhost ([::1]:39905 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d0KCY-0008TL-CT for geb-bug-gnu-emacs@m.gmane.org; Mon, 17 Apr 2017 23:53:14 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:58448) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1d0KCQ-0008Sy-Gq for bug-gnu-emacs@gnu.org; Mon, 17 Apr 2017 23:53:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1d0KCN-0000Y0-9O for bug-gnu-emacs@gnu.org; Mon, 17 Apr 2017 23:53:06 -0400 Original-Received: from debbugs.gnu.org ([208.118.235.43]:55120) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1d0KCN-0000Xp-2D for bug-gnu-emacs@gnu.org; Mon, 17 Apr 2017 23:53:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1d0KCM-00049p-Ja for bug-gnu-emacs@gnu.org; Mon, 17 Apr 2017 23:53: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: Tue, 18 Apr 2017 03:53:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 25122 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 25122-submit@debbugs.gnu.org id=B25122.149248753115925 (code B ref 25122); Tue, 18 Apr 2017 03:53:02 +0000 Original-Received: (at 25122) by debbugs.gnu.org; 18 Apr 2017 03:52:11 +0000 Original-Received: from localhost ([127.0.0.1]:53319 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1d0KBW-00048m-HF for submit@debbugs.gnu.org; Mon, 17 Apr 2017 23:52:11 -0400 Original-Received: from mail-io0-f173.google.com ([209.85.223.173]:36801) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1d0KBV-00048X-9w for 25122@debbugs.gnu.org; Mon, 17 Apr 2017 23:52:10 -0400 Original-Received: by mail-io0-f173.google.com with SMTP id o22so54579013iod.3 for <25122@debbugs.gnu.org>; Mon, 17 Apr 2017 20:52:09 -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=3hYMgKYSeO0+jB7tNiNvUFNgokEJXoz9+Lj9ej8VNds=; b=gqkLFI9GCu3metJoQ0bmtlS4GVFydf7dlIiV8T6nIY76y4jmAhKZywMVOWOYMPvh3d O2B3zzHmr1zJrY3zGeUEQM6VEKZhAn1CjTGl1YqHl2GRp30+mDxLMy+phUptCXcpFUi8 FTWDlTa5iQBbvC2xuBc+KsekYRmcvwvbH3oBw2g2qw+5qwOxyviFcuB4tx43420QMFQg +VJQ/liotQ2dVI7bZfPCqUvSEvgNT2u1RJgCFTfVj8xX0dTexx/C/QgvpJ6eIQPDFa0t yaepEBwrs6FpkJ9dksezGdozg5GUG1Ucu2ev4oMzfu+OM9br1+jtDnzUe0iA4AoizCBQ j4/Q== 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=3hYMgKYSeO0+jB7tNiNvUFNgokEJXoz9+Lj9ej8VNds=; b=YVLX9VcNqaDzNmkmtsgCi61ludmI1ywLonUpJWsDcr55Oa8EBm1ge39LsLysM09Fo4 FoZjKOsAT7zMhmZpO+WExl5Nmg6LpYjYRa/h1YnnWhhjMxrI4wlnAumd3H8wakPk+0N+ n8vOkFfA4oU+7awKw1rdUO+sXvm3PE6HNWqpZkf64/sElLrWng6zSsitOtKK/aNzozyO UnLmrq6SvWe9pLAhXNvqD5qnAasiTKZ06appeqWSdcQuzgOHCr5GSSHoZWygY1juJecx 6D0I/r5vu7lR2+pMSU2PCsMU/3hB+kDUxHTQ+ND2fOLNAVw6oGSR/PpFrAsnx7udfbY7 INIg== X-Gm-Message-State: AN3rC/5SV8Fw1WQ100rBs5m21hvGmMOM+hh+mtLeC24pJfOYAtIA6wt5 aaYhiYIqDXcv1g== X-Received: by 10.107.20.87 with SMTP id 84mr12885905iou.59.1492487523711; Mon, 17 Apr 2017 20:52:03 -0700 (PDT) Original-Received: from zony ([45.2.7.65]) by smtp.googlemail.com with ESMTPSA id j193sm5905204ioe.59.2017.04.17.20.52.01 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Mon, 17 Apr 2017 20:52:02 -0700 (PDT) In-Reply-To: <878to653tr.fsf@users.sourceforge.net> (npostavs@users.sourceforge.net's message of "Wed, 15 Mar 2017 22:54:24 -0400") 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:131717 Archived-At: --=-=-= Content-Type: text/plain npostavs@users.sourceforge.net writes: >>> npostavs@users.sourceforge.net writes: >>> >>> Okay, I think I found the real fix now: > > Same issue with indent-region. The gains are not so dramatic for > typical lisp code that has normal size sexps, but C-x h C-M-\ on > subr.el's text runs twice as fast for me with the new lisp-indent-region > function. Had some test failures due to some corner cases, should all be fixed now. I think this is the final patch set. This also fixes lisp-indent-region and lisp-indent-line to not indent string literal contents. I intend to close this bug as fixed after merging these, as this does fix the performance bug. I will probably pursue the alternate pretty printer separately; it doesn't help performance (after the indent-sexp performance is fixed), but gives prettier results in some cases. The threading idea may also be worth looking at, but can also be considered separately, as this bug report is already long enough. --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v3-0001-Don-t-reparse-the-sexp-in-indent-sexp-Bug-25122.patch Content-Description: patch >From 9ae3809189d08c15d64363c963eeeae62efae242 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 12 Mar 2017 23:59:19 -0400 Subject: [PATCH v3 1/4] 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(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 2e6e13f1dd..607a4c3d11 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -785,6 +785,10 @@ calculate-lisp-indent 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 @@ calculate-lisp-indent (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 @@ indent-sexp 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 @@ indent-sexp (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 @@ indent-sexp (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 @@ indent-sexp (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 @@ indent-sexp (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 8e3f2e185c..27f0bb5ec1 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 @@ (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)))) -- 2.11.1 --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v3-0002-lisp-emacs-lisp-lisp-mode.el-indent-sexp-Clean-up.patch Content-Description: patch >From b14e7d088ef23074841cb1384503dd60c1e939e9 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 22:35:47 -0400 Subject: [PATCH v3 2/4] * 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(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 607a4c3d11..810fc95614 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -1089,12 +1089,13 @@ indent-sexp (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 @@ indent-sexp ;; `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. -- 2.11.1 --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v3-0003-Remove-ignored-argument-from-lisp-indent-line.patch Content-Description: patch >From 6cbe2b3acbb22e75b373d39d716ab4dc1a0f276f Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 21:59:13 -0400 Subject: [PATCH v3 3/4] 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(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 810fc95614..89d5659f30 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -748,14 +748,12 @@ 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 @@ lisp-indent-line ;; 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)) -- 2.11.1 --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=v3-0004-Add-new-lisp-indent-region-that-doesn-t-reparse-t.patch Content-Description: patch >From d2f3101d88eb9601a52c06d1171c413fc23e3d8d Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 22:27:27 -0400 Subject: [PATCH v3 4/4] 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(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 89d5659f30..54d916887c 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -594,6 +594,7 @@ lisp-mode-variables ;; 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 @@ 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 -- 2.11.1 --=-=-=--