>From 9cf8f32910c0c899f108c3907d099a18fff2cec4 Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Wed, 15 Mar 2017 22:27:27 -0400 Subject: [PATCH v2 3/4] Add new `lisp-indent-region' that doesn't reparse the code. * lisp/emacs-lisp/lisp-mode.el (lisp-indent-region): New function, like `indent-region-line-by-line' but additionally keep a running parse state to avoid reparsing the code repeatedly. (lisp-indent-line): Take optional PARSE-STATE argument, pass it to `calculate-lisp-indent'. (lisp-mode-variables): Set `indent-region-function' to `lisp-indent-region'. --- lisp/emacs-lisp/lisp-mode.el | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index 7eb8b986be..7577267903 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -586,6 +586,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) @@ -744,10 +745,38 @@ lisp-indent-function :type 'function :group 'lisp) -(defun lisp-indent-line () +(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 (syntax-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)) shift-amt + (let ((indent (calculate-lisp-indent parse-state)) shift-amt (pos (- (point-max) (point))) (beg (progn (beginning-of-line) (point)))) (skip-chars-forward " \t") -- 2.11.1