summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp/lisp-mode.el
diff options
context:
space:
mode:
authorNoam Postavsky <npostavs@gmail.com>2017-03-15 22:27:27 -0400
committerNoam Postavsky <npostavs@gmail.com>2017-04-22 14:09:58 -0400
commit4713dd425beac5cb459704e67dcb8f6faf714375 (patch)
tree5f3ba3603a4bc724180d9b872b50c183c39a664b /lisp/emacs-lisp/lisp-mode.el
parent2f6769f9cdb799e880fdcc09057353a0a2349bfc (diff)
downloademacs-4713dd425beac5cb459704e67dcb8f6faf714375.tar.gz
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'.
Diffstat (limited to 'lisp/emacs-lisp/lisp-mode.el')
-rw-r--r--lisp/emacs-lisp/lisp-mode.el48
1 files changed, 44 insertions, 4 deletions
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