aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lisp/emacs-lisp/lisp-mode.el168
-rw-r--r--test/lisp/emacs-lisp/lisp-mode-tests.el94
2 files changed, 166 insertions, 96 deletions
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index c2f5f42ace8..5faa6a50ae5 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -1069,103 +1069,79 @@ Lisp function does not specify a special indentation."
1069If optional arg ENDPOS is given, indent each line, stopping when 1069If optional arg ENDPOS is given, indent each line, stopping when
1070ENDPOS is encountered." 1070ENDPOS is encountered."
1071 (interactive) 1071 (interactive)
1072 (let ((indent-stack (list nil)) 1072 (let* ((indent-stack (list nil))
1073 (next-depth 0) 1073 ;; If ENDPOS is non-nil, use beginning of defun as STARTING-POINT.
1074 ;; If ENDPOS is non-nil, use nil as STARTING-POINT 1074 ;; If ENDPOS is nil, it is safe not to scan before point
1075 ;; so that calculate-lisp-indent will find the beginning of 1075 ;; since every line we indent is more deeply nested than point is.
1076 ;; the defun we are in. 1076 (starting-point (save-excursion (if endpos (beginning-of-defun))
1077 ;; If ENDPOS is nil, it is safe not to scan before point 1077 (point)))
1078 ;; since every line we indent is more deeply nested than point is. 1078 (state nil)
1079 (starting-point (if endpos nil (point))) 1079 (init-depth 0)
1080 (last-point (point)) 1080 (next-depth 0)
1081 last-depth bol outer-loop-done inner-loop-done state this-indent) 1081 (last-depth 0)
1082 (or endpos 1082 (last-syntax-point (point)))
1083 ;; Get error now if we don't have a complete sexp after point. 1083 (unless endpos
1084 (save-excursion (forward-sexp 1))) 1084 ;; Get error now if we don't have a complete sexp after point.
1085 (save-excursion (forward-sexp 1)
1086 ;; We need a marker because we modify the buffer
1087 ;; text preceding endpos.
1088 (setq endpos (point-marker))))
1085 (save-excursion 1089 (save-excursion
1086 (setq outer-loop-done nil) 1090 (while (< (point) endpos)
1087 (while (if endpos (< (point) endpos) 1091 ;; Parse this line so we can learn the state to indent the
1088 (not outer-loop-done)) 1092 ;; next line.
1089 (setq last-depth next-depth 1093 (while (progn
1090 inner-loop-done nil) 1094 (setq state (parse-partial-sexp
1091 ;; Parse this line so we can learn the state 1095 last-syntax-point (progn (end-of-line) (point))
1092 ;; to indent the next line. 1096 nil nil state))
1093 ;; This inner loop goes through only once 1097 ;; Skip over newlines within strings.
1094 ;; unless a line ends inside a string. 1098 (nth 3 state))
1095 (while (and (not inner-loop-done) 1099 (setq state (parse-partial-sexp (point) (point-max)
1096 (not (setq outer-loop-done (eobp)))) 1100 nil nil state 'syntax-table))
1097 (setq state (parse-partial-sexp (point) (progn (end-of-line) (point)) 1101 (setq last-syntax-point (point)))
1098 nil nil state)) 1102 (setq next-depth (car state))
1099 (setq next-depth (car state)) 1103 ;; If the line contains a comment indent it now with
1100 ;; If the line contains a comment other than the sort 1104 ;; `indent-for-comment'.
1101 ;; that is indented like code, 1105 (when (nth 4 state)
1102 ;; indent it now with indent-for-comment. 1106 (indent-for-comment)
1103 ;; Comments indented like code are right already. 1107 (end-of-line))
1104 ;; In any case clear the in-comment flag in the state 1108 (setq last-syntax-point (point))
1105 ;; because parse-partial-sexp never sees the newlines. 1109 (when (< next-depth init-depth)
1106 (if (car (nthcdr 4 state)) 1110 (setq indent-stack (nconc indent-stack
1107 (progn (indent-for-comment) 1111 (make-list (- init-depth next-depth) nil))
1108 (end-of-line) 1112 last-depth (- last-depth next-depth)
1109 (setcar (nthcdr 4 state) nil))) 1113 next-depth init-depth))
1110 ;; If this line ends inside a string, 1114 (forward-line 1)
1111 ;; go straight to next line, remaining within the inner loop, 1115 (when (< (point) endpos)
1112 ;; and turn off the \-flag. 1116 (let ((depth-delta (- next-depth last-depth)))
1113 (if (car (nthcdr 3 state)) 1117 (cond ((< depth-delta 0)
1114 (progn 1118 (setq indent-stack (nthcdr (- depth-delta) indent-stack)))
1115 (forward-line 1) 1119 ((> depth-delta 0)
1116 (setcar (nthcdr 5 state) nil)) 1120 (setq indent-stack (nconc (make-list depth-delta nil)
1117 (setq inner-loop-done t))) 1121 indent-stack))))
1118 (and endpos 1122 (setq last-depth next-depth))
1119 (<= next-depth 0) 1123 ;; Now indent the next line according
1120 (progn 1124 ;; to what we learned from parsing the previous one.
1121 (setq indent-stack (nconc indent-stack 1125 (skip-chars-forward " \t")
1122 (make-list (- next-depth) nil)) 1126 ;; But not if the line is blank, or just a comment (we
1123 last-depth (- last-depth next-depth) 1127 ;; already called `indent-for-comment' above).
1124 next-depth 0))) 1128 (unless (or (eolp) (eq (char-syntax (char-after)) ?<))
1125 (forward-line 1) 1129 (let ((this-indent (car indent-stack)))
1126 ;; Decide whether to exit. 1130 (when (listp this-indent)
1127 (if endpos 1131 (let ((val (calculate-lisp-indent
1128 ;; If we have already reached the specified end, 1132 (or (car this-indent) starting-point))))
1129 ;; give up and do not reindent this line. 1133 (setq
1130 (if (<= endpos (point)) 1134 this-indent
1131 (setq outer-loop-done t)) 1135 (cond ((integerp val)
1132 ;; If no specified end, we are done if we have finished one sexp. 1136 (setf (car indent-stack) val))
1133 (if (<= next-depth 0) 1137 ((consp val) ; (COLUMN CONTAINING-SEXP-START)
1134 (setq outer-loop-done t))) 1138 (setf (car indent-stack) (cdr val))
1135 (unless outer-loop-done 1139 (car val))
1136 (while (> last-depth next-depth) 1140 ;; `calculate-lisp-indent' only returns nil
1137 (setq indent-stack (cdr indent-stack) 1141 ;; when we're in a string, but this won't
1138 last-depth (1- last-depth))) 1142 ;; happen because we skip strings above.
1139 (while (< last-depth next-depth) 1143 (t (error "This shouldn't happen!"))))))
1140 (setq indent-stack (cons nil indent-stack) 1144 (indent-line-to this-indent))))))))
1141 last-depth (1+ last-depth)))
1142 ;; Now indent the next line according
1143 ;; to what we learned from parsing the previous one.
1144 (setq bol (point))
1145 (skip-chars-forward " \t")
1146 ;; But not if the line is blank, or just a comment
1147 ;; (except for double-semi comments; indent them as usual).
1148 (if (or (eobp) (looking-at "\\s<\\|\n"))
1149 nil
1150 (if (and (car indent-stack)
1151 (>= (car indent-stack) 0))
1152 (setq this-indent (car indent-stack))
1153 (let ((val (calculate-lisp-indent
1154 (if (car indent-stack) (- (car indent-stack))
1155 starting-point))))
1156 (if (null val)
1157 (setq this-indent val)
1158 (if (integerp val)
1159 (setcar indent-stack
1160 (setq this-indent val))
1161 (setcar indent-stack (- (car (cdr val))))
1162 (setq this-indent (car val))))))
1163 (if (and this-indent (/= (current-column) this-indent))
1164 (progn (delete-region bol (point))
1165 (indent-to this-indent)))))
1166 (or outer-loop-done
1167 (setq outer-loop-done (= (point) last-point))
1168 (setq last-point (point)))))))
1169 1145
1170(defun indent-pp-sexp (&optional arg) 1146(defun indent-pp-sexp (&optional arg)
1171 "Indent each line of the list starting just after point, or prettyprint it. 1147 "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
new file mode 100644
index 00000000000..2801f23df63
--- /dev/null
+++ b/test/lisp/emacs-lisp/lisp-mode-tests.el
@@ -0,0 +1,94 @@
1;;; lisp-mode-tests.el --- Test Lisp editing commands -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2017 Free Software Foundation, Inc.
4
5;; GNU Emacs is free software: you can redistribute it and/or modify
6;; it under the terms of the GNU General Public License as published by
7;; the Free Software Foundation, either version 3 of the License, or
8;; (at your option) any later version.
9
10;; GNU Emacs is distributed in the hope that it will be useful,
11;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13;; GNU General Public License for more details.
14
15;; You should have received a copy of the GNU General Public License
16;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
17
18;;; Code:
19
20(require 'ert)
21(require 'cl-lib)
22(require 'lisp-mode)
23
24(ert-deftest indent-sexp ()
25 "Test basics of \\[indent-sexp]."
26 (with-temp-buffer
27 (insert "\
28\(a
29 (prog1
30 (prog1
31 1
32 2)
33 2)
34 (1
35 \"string
36noindent\" (\"string2
37noindent\" 3
384)
39 2) ; comment
40 ;; comment
41 b)")
42 (goto-char (point-min))
43 (let ((indent-tabs-mode nil)
44 (correct (buffer-string)))
45 (dolist (mode '(fundamental-mode emacs-lisp-mode))
46 (funcall mode)
47 (indent-sexp)
48 ;; Don't mess up correctly indented code.
49 (should (string= (buffer-string) correct))
50 ;; Correctly add indentation.
51 (save-excursion
52 (while (not (eobp))
53 (delete-horizontal-space)
54 (forward-line)))
55 (indent-sexp)
56 (should (equal (buffer-string) correct))
57 ;; Correctly remove indentation.
58 (save-excursion
59 (let ((n 0))
60 (while (not (eobp))
61 (unless (looking-at "noindent")
62 (insert (make-string n ?\s)))
63 (cl-incf n)
64 (forward-line))))
65 (indent-sexp)
66 (should (equal (buffer-string) correct))))))
67
68(ert-deftest indent-subsexp ()
69 "Make sure calling `indent-sexp' inside a sexp works."
70 (with-temp-buffer
71 (insert "\
72\(d1 xx
73 (d2 yy
74 zz)
75 11)")
76 (let ((correct (buffer-string)))
77 (search-backward "d2")
78 (up-list -1)
79 (indent-sexp)
80 (should (equal (buffer-string) correct)))))
81
82(ert-deftest indent-sexp-in-string ()
83 "Make sure calling `indent-sexp' inside a string works."
84 ;; See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=21343.
85 (with-temp-buffer
86 (emacs-lisp-mode)
87 (insert "\";\"")
88 (let ((correct (buffer-string)))
89 (search-backward ";")
90 (indent-sexp)
91 (should (equal (buffer-string) correct)))))
92
93(provide 'lisp-mode-tests)
94;;; lisp-mode-tests.el ends here