[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Emacs-diffs] master 4713dd4 4/5: Add new `lisp-indent-region' that does
From: |
Noam Postavsky |
Subject: |
[Emacs-diffs] master 4713dd4 4/5: Add new `lisp-indent-region' that doesn't reparse the code. |
Date: |
Sat, 22 Apr 2017 14:17:52 -0400 (EDT) |
branch: master
commit 4713dd425beac5cb459704e67dcb8f6faf714375
Author: Noam Postavsky <address@hidden>
Commit: Noam Postavsky <address@hidden>
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 89d5659..54d9168 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