Continuation of previous change.
[bpt/emacs.git] / lisp / indent.el
CommitLineData
b7d5bd82 1;;; indent.el --- indentation commands for Emacs -*- lexical-binding:t -*-
1a06eabd 2
ab422c4d 3;; Copyright (C) 1985, 1995, 2001-2013 Free Software Foundation, Inc.
483e630e 4
9750e079 5;; Maintainer: FSF
bd78fa1d 6;; Package: emacs
9750e079 7
483e630e
RM
8;; This file is part of GNU Emacs.
9
eb3fa2cf 10;; GNU Emacs is free software: you can redistribute it and/or modify
483e630e 11;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
483e630e
RM
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
eb3fa2cf 21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
483e630e 22
e41b2db1
ER
23;;; Commentary:
24
25;; Commands for making and changing indentation in text. These are
26;; described in the Emacs manual.
27
e5167999 28;;; Code:
483e630e 29
ddb3136e 30(defgroup indent nil
21751995 31 "Indentation commands."
ddb3136e 32 :group 'editing)
106b6d0e 33
ddb3136e 34(defcustom standard-indent 4
9201cc28 35 "Default number of columns for margin-changing functions to indent."
ddb3136e
RS
36 :group 'indent
37 :type 'integer)
38
f059f703 39(defvar indent-line-function 'indent-relative
a17b712b
SM
40 "Function to indent the current line.
41This function will be called with no arguments.
42If it is called somewhere where auto-indentation cannot be done
59b5f3a8 43\(e.g. inside a string), the function should simply return `noindent'.
a17b712b
SM
44Setting this function is all you need to make TAB indent appropriately.
45Don't rebind TAB unless you really need to.")
483e630e 46
20b1d079 47(defcustom tab-always-indent t
9201cc28 48 "Controls the operation of the TAB key.
20b1d079
SM
49If t, hitting TAB always just indents the current line.
50If nil, hitting TAB indents the current line if point is at the left margin
9450aaa0 51or in the line's indentation, otherwise it inserts a \"real\" TAB character.
51ef56c4
SM
52If `complete', TAB first tries to indent the current line, and if the line
53was already indented, then try to complete the thing at point.
54
9450aaa0 55Some programming language modes have their own variable to control this,
f321348b 56e.g., `c-tab-always-indent', and do not respect this variable."
20b1d079 57 :group 'indent
e073d74a
GM
58 :type '(choice
59 (const :tag "Always indent" t)
60 (const :tag "Indent if inside indentation, else TAB" nil)
61 (const :tag "Indent, or if already indented complete" complete)))
62
20b1d079 63
483e630e 64(defun indent-according-to-mode ()
1a2f8b73 65 "Indent line in proper way for current major mode.
e70b5064
CY
66Normally, this is done by calling the function specified by the
67variable `indent-line-function'. However, if the value of that
68variable is `indent-relative' or `indent-relative-maybe', handle
69it specially (since those functions are used for tabbing); in
70that case, indent by aligning to the previous non-blank line."
483e630e 71 (interactive)
f5632fb6 72 (syntax-propertize (line-end-position))
a17b712b
SM
73 (if (memq indent-line-function
74 '(indent-relative indent-relative-maybe))
75 ;; These functions are used for tabbing, but can't be used for
76 ;; indenting. Replace with something ad-hoc.
77 (let ((column (save-excursion
78 (beginning-of-line)
79 (skip-chars-backward "\n \t")
80 (beginning-of-line)
81 (current-indentation))))
82 (if (<= (current-column) (current-indentation))
83 (indent-line-to column)
84 (save-excursion (indent-line-to column))))
85 ;; The normal case.
86 (funcall indent-line-function)))
483e630e 87
a17b712b 88(defun indent-for-tab-command (&optional arg)
e70b5064
CY
89 "Indent the current line or region, or insert a tab, as appropriate.
90This function either inserts a tab, or indents the current line,
91or performs symbol completion, depending on `tab-always-indent'.
92The function called to actually indent the line or insert a tab
93is given by the variable `indent-line-function'.
94
95If a prefix argument is given, after this function indents the
96current line or inserts a tab, it also rigidly indents the entire
ae8ba409 97balanced expression which starts at the beginning of the current
e70b5064 98line, to reflect the current line's indentation.
1225a933 99
e70b5064
CY
100In most major modes, if point was in the current line's
101indentation, it is moved to the first non-whitespace character
102after indenting; otherwise it stays at the same position relative
103to the text.
1225a933 104
e70b5064
CY
105If `transient-mark-mode' is turned on and the region is active,
106this function instead calls `indent-region'. In this case, any
107prefix argument is ignored."
1225a933 108 (interactive "P")
a17b712b 109 (cond
17ee4625 110 ;; The region is active, indent it.
361a81d9 111 ((use-region-p)
17ee4625 112 (indent-region (region-beginning) (region-end)))
fe552b4c 113 ((or ;; indent-to-left-margin is only meant for indenting,
a17b712b
SM
114 ;; so we force it to always insert a tab here.
115 (eq indent-line-function 'indent-to-left-margin)
116 (and (not tab-always-indent)
f202a7a2
SM
117 (or (> (current-column) (current-indentation))
118 (eq this-command last-command))))
a17b712b 119 (insert-tab arg))
6dd697d9 120 (t
51ef56c4
SM
121 (let ((old-tick (buffer-chars-modified-tick))
122 (old-point (point))
123 (old-indent (current-indentation)))
1225a933
MB
124
125 ;; Indent the line.
126 (funcall indent-line-function)
127
51ef56c4
SM
128 (cond
129 ;; If the text was already indented right, try completion.
130 ((and (eq tab-always-indent 'complete)
131 (eq old-point (point))
132 (eq old-tick (buffer-chars-modified-tick)))
133 (completion-at-point))
134
135 ;; If a prefix argument was given, rigidly indent the following
136 ;; sexp to match the change in the current line's indentation.
137 (arg
138 (let ((end-marker
139 (save-excursion
140 (forward-line 0) (forward-sexp) (point-marker)))
141 (indentation-change (- (current-indentation) old-indent)))
142 (save-excursion
143 (forward-line 1)
144 (when (and (not (zerop indentation-change))
145 (< (point) end-marker))
146 (indent-rigidly (point) end-marker indentation-change))))))))))
a17b712b
SM
147
148(defun insert-tab (&optional arg)
149 (let ((count (prefix-numeric-value arg)))
5e9f80a8
RS
150 (if (and abbrev-mode
151 (eq (char-syntax (preceding-char)) ?w))
10992b01
RS
152 (expand-abbrev))
153 (if indent-tabs-mode
4424ce6f 154 (insert-char ?\t count)
10992b01 155 (indent-to (* tab-width (+ count (/ (current-column) tab-width)))))))
483e630e 156
b7d5bd82
TL
157(defun indent-rigidly--current-indentation (beg end)
158 "Return the smallest indentation in range from BEG to END.
159Blank lines are ignored."
160 (save-excursion
161 (save-match-data
162 (let ((beg (progn (goto-char beg) (line-beginning-position)))
163 indent)
164 (goto-char beg)
165 (while (re-search-forward "^\\s-*[[:print:]]" end t)
166 (setq indent (min (or indent (current-indentation))
167 (current-indentation))))
168 indent))))
169
170(defvar indent-rigidly-map
171 (let ((map (make-sparse-keymap)))
172 (define-key map [left]
173 (lambda (beg end) (interactive "r") (indent-rigidly beg end -1)))
174
175 (define-key map [right]
176 (lambda (beg end) (interactive "r") (indent-rigidly beg end 1)))
177
178 (define-key map [S-right]
179 (lambda (beg end) (interactive "r")
180 (let* ((current (indent-rigidly--current-indentation beg end))
181 (next (indent--next-tab-stop current)))
182 (indent-rigidly beg end (- next current)))))
183
184 (define-key map [S-left]
185 (lambda (beg end) (interactive "r")
186 (let* ((current (indent-rigidly--current-indentation beg end))
187 (next (indent--next-tab-stop current 'prev)))
188 (indent-rigidly beg end (- next current)))))
189 map))
190
191(defun indent-rigidly (start end arg &optional interactive)
483e630e 192 "Indent all lines starting in the region sideways by ARG columns.
5a0f9388 193Called from a program, takes three arguments, START, END and ARG.
b7d5bd82
TL
194You can remove all indentation from a region by giving a large negative ARG.
195If used interactively and no prefix argument is given, use a transient
196mode that lets you move the text with cursor keys."
197 (interactive "r\nP\np")
198 (if (and (not arg) interactive)
199 (progn
200 (message "Edit region indentation with <left>, <right>, <S-left> \
201and <S-right>.")
202 (set-temporary-overlay-map indent-rigidly-map t))
203 (save-excursion
204 (goto-char end)
205 (setq end (point-marker))
206 (goto-char start)
207 (or (bolp) (forward-line 1))
208 (while (< (point) end)
209 (let ((indent (current-indentation))
210 eol-flag)
211 (save-excursion
212 (skip-chars-forward " \t")
213 (setq eol-flag (eolp)))
214 (or eol-flag
215 (indent-to (max 0 (+ indent arg)) 0))
216 (delete-region (point) (progn (skip-chars-forward " \t") (point))))
217 (forward-line 1))
218 (move-marker end nil))))
483e630e 219
106b6d0e
RS
220(defun indent-line-to (column)
221 "Indent current line to COLUMN.
222This function removes or adds spaces and tabs at beginning of line
223only if necessary. It leaves point at end of indentation."
ca2a3cb7
BG
224 (back-to-indentation)
225 (let ((cur-col (current-column)))
226 (cond ((< cur-col column)
cd6d305e 227 (if (>= (- column (* (/ cur-col tab-width) tab-width)) tab-width)
a80a30b3
RS
228 (delete-region (point)
229 (progn (skip-chars-backward " ") (point))))
ca2a3cb7
BG
230 (indent-to column))
231 ((> cur-col column) ; too far right (after tab?)
34c3f2b8 232 (delete-region (progn (move-to-column column t) (point))
ca2a3cb7 233 (progn (back-to-indentation) (point)))))))
106b6d0e
RS
234
235(defun current-left-margin ()
236 "Return the left margin to use for this line.
237This is the value of the buffer-local variable `left-margin' plus the value
238of the `left-margin' text-property at the start of the line."
239 (save-excursion
240 (back-to-indentation)
241 (max 0
34c3f2b8
BG
242 (+ left-margin (or (get-text-property
243 (if (and (eobp) (not (bobp)))
244 (1- (point)) (point))
245 'left-margin) 0)))))
106b6d0e 246
34c3f2b8 247(defun move-to-left-margin (&optional n force)
106b6d0e
RS
248 "Move to the left margin of the current line.
249With optional argument, move forward N-1 lines first.
34c3f2b8
BG
250The column moved to is the one given by the `current-left-margin' function.
251If the line's indentation appears to be wrong, and this command is called
252interactively or with optional argument FORCE, it will be fixed."
253 (interactive (list (prefix-numeric-value current-prefix-arg) t))
106b6d0e 254 (beginning-of-line n)
54505d72 255 (skip-chars-forward " \t")
1d1c5af9
RS
256 (if (minibufferp (current-buffer))
257 (if (save-excursion (beginning-of-line) (bobp))
258 (goto-char (minibuffer-prompt-end))
259 (beginning-of-line))
260 (let ((lm (current-left-margin))
261 (cc (current-column)))
262 (cond ((> cc lm)
263 (if (> (move-to-column lm force) lm)
264 ;; If lm is in a tab and we are not forcing, move before tab
265 (backward-char 1)))
266 ((and force (< cc lm))
267 (indent-to-left-margin))))))
106b6d0e 268
f202a7a2 269;; This used to be the default indent-line-function,
483e630e
RM
270;; used in Fundamental Mode, Text Mode, etc.
271(defun indent-to-left-margin ()
34c3f2b8 272 "Indent current line to the column given by `current-left-margin'."
caadec43
RS
273 (save-excursion (indent-line-to (current-left-margin)))
274 ;; If we are within the indentation, move past it.
275 (when (save-excursion
276 (skip-chars-backward " \t")
277 (bolp))
278 (skip-chars-forward " \t")))
106b6d0e 279
34c3f2b8
BG
280(defun delete-to-left-margin (&optional from to)
281 "Remove left margin indentation from a region.
282This deletes to the column given by `current-left-margin'.
283In no case will it delete non-whitespace.
284Args FROM and TO are optional; default is the whole buffer."
106b6d0e 285 (save-excursion
34c3f2b8 286 (goto-char (or to (point-max)))
106b6d0e 287 (setq to (point-marker))
34c3f2b8 288 (goto-char (or from (point-min)))
106b6d0e
RS
289 (or (bolp) (forward-line 1))
290 (while (< (point) to)
34c3f2b8 291 (delete-region (point) (progn (move-to-left-margin nil t) (point)))
106b6d0e
RS
292 (forward-line 1))
293 (move-marker to nil)))
294
a5b3d1b8 295(defun set-left-margin (from to width)
106b6d0e 296 "Set the left margin of the region to WIDTH.
a5b3d1b8
LT
297If `auto-fill-mode' is active, re-fill the region to fit the new margin.
298
299Interactively, WIDTH is the prefix argument, if specified.
300Without prefix argument, the command prompts for WIDTH."
106b6d0e 301 (interactive "r\nNSet left margin to column: ")
106b6d0e
RS
302 (save-excursion
303 ;; If inside indentation, start from BOL.
304 (goto-char from)
305 (skip-chars-backward " \t")
306 (if (bolp) (setq from (point)))
34c3f2b8 307 ;; Place end after whitespace
106b6d0e 308 (goto-char to)
34c3f2b8 309 (skip-chars-forward " \t")
106b6d0e 310 (setq to (point-marker)))
34c3f2b8
BG
311 ;; Delete margin indentation first, but keep paragraph indentation.
312 (delete-to-left-margin from to)
a5b3d1b8
LT
313 (put-text-property from to 'left-margin width)
314 (indent-rigidly from to width)
34c3f2b8 315 (if auto-fill-function (save-excursion (fill-region from to nil t t)))
106b6d0e
RS
316 (move-marker to nil))
317
a5b3d1b8 318(defun set-right-margin (from to width)
106b6d0e 319 "Set the right margin of the region to WIDTH.
a5b3d1b8
LT
320If `auto-fill-mode' is active, re-fill the region to fit the new margin.
321
322Interactively, WIDTH is the prefix argument, if specified.
323Without prefix argument, the command prompts for WIDTH."
8cf19007 324 (interactive "r\nNSet right margin to width: ")
106b6d0e
RS
325 (save-excursion
326 (goto-char from)
327 (skip-chars-backward " \t")
328 (if (bolp) (setq from (point))))
a5b3d1b8 329 (put-text-property from to 'right-margin width)
34c3f2b8 330 (if auto-fill-function (save-excursion (fill-region from to nil t t))))
106b6d0e
RS
331
332(defun alter-text-property (from to prop func &optional object)
333 "Programmatically change value of a text-property.
334For each region between FROM and TO that has a single value for PROPERTY,
335apply FUNCTION to that value and sets the property to the function's result.
336Optional fifth argument OBJECT specifies the string or buffer to operate on."
337 (let ((begin from)
338 end val)
339 (while (setq val (get-text-property begin prop object)
340 end (text-property-not-all begin to prop val object))
341 (put-text-property begin end prop (funcall func val) object)
342 (setq begin end))
343 (if (< begin to)
344 (put-text-property begin to prop (funcall func val) object))))
345
346(defun increase-left-margin (from to inc)
347 "Increase or decrease the left-margin of the region.
348With no prefix argument, this adds `standard-indent' of indentation.
349A prefix arg (optional third arg INC noninteractively) specifies the amount
350to change the margin by, in characters.
351If `auto-fill-mode' is active, re-fill the region to fit the new margin."
352 (interactive "*r\nP")
353 (setq inc (if inc (prefix-numeric-value inc) standard-indent))
354 (save-excursion
355 (goto-char from)
356 (skip-chars-backward " \t")
357 (if (bolp) (setq from (point)))
358 (goto-char to)
359 (setq to (point-marker)))
106b6d0e 360 (alter-text-property from to 'left-margin
34c3f2b8
BG
361 (lambda (v) (max (- left-margin) (+ inc (or v 0)))))
362 (indent-rigidly from to inc)
363 (if auto-fill-function (save-excursion (fill-region from to nil t t)))
106b6d0e
RS
364 (move-marker to nil))
365
366(defun decrease-left-margin (from to inc)
367 "Make the left margin of the region smaller.
368With no prefix argument, decrease the indentation by `standard-indent'.
369A prefix arg (optional third arg INC noninteractively) specifies the amount
370to change the margin by, in characters.
371If `auto-fill-mode' is active, re-fill the region to fit the new margin."
372 (interactive "*r\nP")
373 (setq inc (if inc (prefix-numeric-value inc) standard-indent))
374 (increase-left-margin from to (- inc)))
375
376(defun increase-right-margin (from to inc)
377 "Increase the right-margin of the region.
378With no prefix argument, increase the right margin by `standard-indent'.
379A prefix arg (optional third arg INC noninteractively) specifies the amount
380to change the margin by, in characters. A negative argument decreases
381the right margin width.
382If `auto-fill-mode' is active, re-fill the region to fit the new margin."
383 (interactive "r\nP")
6faab05f 384 (setq inc (if inc (prefix-numeric-value inc) standard-indent))
106b6d0e
RS
385 (save-excursion
386 (alter-text-property from to 'right-margin
6faab05f 387 (lambda (v) (+ inc (or v 0))))
106b6d0e
RS
388 (if auto-fill-function
389 (fill-region from to nil t t))))
390
391(defun decrease-right-margin (from to inc)
392 "Make the right margin of the region smaller.
393With no prefix argument, decrease the right margin by `standard-indent'.
394A prefix arg (optional third arg INC noninteractively) specifies the amount
395of width to remove, in characters. A negative argument increases
396the right margin width.
397If `auto-fill-mode' is active, re-fills region to fit in new margin."
398 (interactive "*r\nP")
399 (setq inc (if inc (prefix-numeric-value inc) standard-indent))
400 (increase-right-margin from to (- inc)))
483e630e 401
34c3f2b8
BG
402(defun beginning-of-line-text (&optional n)
403 "Move to the beginning of the text on this line.
404With optional argument, move forward N-1 lines first.
405From the beginning of the line, moves past the left-margin indentation, the
406fill-prefix, and any indentation used for centering or right-justifying the
f1180544 407line, but does not move past any whitespace that was explicitly inserted
34c3f2b8
BG
408\(such as a tab used to indent the first line of a paragraph)."
409 (interactive "p")
410 (beginning-of-line n)
411 (skip-chars-forward " \t")
412 ;; Skip over fill-prefix.
f1180544 413 (if (and fill-prefix
34c3f2b8
BG
414 (not (string-equal fill-prefix "")))
415 (if (equal fill-prefix
f1180544 416 (buffer-substring
34c3f2b8
BG
417 (point) (min (point-max) (+ (length fill-prefix) (point)))))
418 (forward-char (length fill-prefix)))
7d43e2ad 419 (if (and adaptive-fill-mode adaptive-fill-regexp
34c3f2b8
BG
420 (looking-at adaptive-fill-regexp))
421 (goto-char (match-end 0))))
422 ;; Skip centering or flushright indentation
423 (if (memq (current-justification) '(center right))
424 (skip-chars-forward " \t")))
425
483e630e 426(defvar indent-region-function nil
3704e77e
RS
427 "Short cut function to indent region using `indent-according-to-mode'.
428A value of nil means really run `indent-according-to-mode' on each line.")
483e630e 429
1d9cc345 430(defun indent-region (start end &optional column)
483e630e 431 "Indent each nonblank line in the region.
1a2f8b73
RS
432A numeric prefix argument specifies a column: indent each line to that column.
433
434With no prefix argument, the command chooses one of these methods and
435indents all the lines with it:
436
437 1) If `fill-prefix' is non-nil, insert `fill-prefix' at the
438 beginning of each line in the region that does not already begin
439 with it.
440 2) If `indent-region-function' is non-nil, call that function
441 to indent the region.
e70b5064 442 3) Indent each line via `indent-according-to-mode'.
1a2f8b73
RS
443
444Called from a program, START and END specify the region to indent.
445If the third argument COLUMN is an integer, it specifies the
446column to indent to; if it is nil, use one of the three methods above."
483e630e 447 (interactive "r\nP")
e70b5064 448 (cond
b7d5bd82 449 ;; If a numeric prefix is given, indent to that column.
e70b5064 450 (column
88a2603a 451 (setq column (prefix-numeric-value column))
483e630e
RM
452 (save-excursion
453 (goto-char end)
454 (setq end (point-marker))
455 (goto-char start)
456 (or (bolp) (forward-line 1))
457 (while (< (point) end)
458 (delete-region (point) (progn (skip-chars-forward " \t") (point)))
459 (or (eolp)
106b6d0e 460 (indent-to column 0))
b7d5bd82 461 (forward-line 1))
2a96c2a7 462 (move-marker end nil)))
b7d5bd82 463 ;; If a fill-prefix is specified, use it.
e70b5064
CY
464 (fill-prefix
465 (save-excursion
466 (goto-char end)
467 (setq end (point-marker))
468 (goto-char start)
469 (let ((regexp (regexp-quote fill-prefix)))
470 (while (< (point) end)
471 (or (looking-at regexp)
472 (and (bolp) (eolp))
473 (insert fill-prefix))
474 (forward-line 1)))))
b7d5bd82 475 ;; Use indent-region-function is available.
e70b5064
CY
476 (indent-region-function
477 (funcall indent-region-function start end))
b7d5bd82
TL
478 ;; Else, use a default implementation that calls indent-line-function on
479 ;; each line.
e70b5064
CY
480 (t
481 (save-excursion
482 (setq end (copy-marker end))
483 (goto-char start)
b7d5bd82 484 (let ((pr (make-progress-reporter "Indenting region..." (point) end)))
e70b5064
CY
485 (while (< (point) end)
486 (or (and (bolp) (eolp))
487 (indent-according-to-mode))
b7d5bd82
TL
488 (forward-line 1)
489 (progress-reporter-update pr (point)))
490 (progress-reporter-done pr)
491 (move-marker end nil)))))
2a96c2a7
SM
492 ;; In most cases, reindenting modifies the buffer, but it may also
493 ;; leave it unmodified, in which case we have to deactivate the mark
494 ;; by hand.
495 (deactivate-mark))
483e630e
RM
496
497(defun indent-relative-maybe ()
1fd63d9b
EZ
498 "Indent a new line like previous nonblank line.
499If the previous nonblank line has no indent points beyond the
500column point starts at, this command does nothing.
501
502See also `indent-relative'."
483e630e
RM
503 (interactive)
504 (indent-relative t))
505
506(defun indent-relative (&optional unindented-ok)
507 "Space out to under next indent point in previous nonblank line.
508An indent point is a non-whitespace character following whitespace.
f6f53207
RS
509The following line shows the indentation points in this line.
510 ^ ^ ^ ^ ^ ^ ^ ^ ^
483e630e 511If the previous nonblank line has no indent points beyond the
1fd63d9b
EZ
512column point starts at, `tab-to-tab-stop' is done instead, unless
513this command is invoked with a numeric argument, in which case it
514does nothing.
515
516See also `indent-relative-maybe'."
483e630e 517 (interactive "P")
5e9f80a8
RS
518 (if (and abbrev-mode
519 (eq (char-syntax (preceding-char)) ?w))
520 (expand-abbrev))
483e630e
RM
521 (let ((start-column (current-column))
522 indent)
523 (save-excursion
524 (beginning-of-line)
525 (if (re-search-backward "^[^\n]" nil t)
526 (let ((end (save-excursion (forward-line 1) (point))))
527 (move-to-column start-column)
528 ;; Is start-column inside a tab on this line?
529 (if (> (current-column) start-column)
530 (backward-char 1))
531 (or (looking-at "[ \t]")
532 unindented-ok
533 (skip-chars-forward "^ \t" end))
534 (skip-chars-forward " \t" end)
535 (or (= (point) end) (setq indent (current-column))))))
536 (if indent
537 (let ((opoint (point-marker)))
483e630e
RM
538 (indent-to indent 0)
539 (if (> opoint (point))
540 (goto-char opoint))
541 (move-marker opoint nil))
542 (tab-to-tab-stop))))
543
ddb3136e 544(defcustom tab-stop-list
b7d5bd82 545 nil
9201cc28 546 "List of tab stop positions used by `tab-to-tab-stop'.
b7d5bd82
TL
547This should be a list of integers, ordered from smallest to largest.
548It implicitly extends to infinity by repeating the last step (e.g. '(1 2 5)
549is equivalent to '(1 2 5 8 11)).
550If the list has less than 2 elements, `tab-width' is used as the \"last step\"."
ddb3136e
RS
551 :group 'indent
552 :type '(repeat integer))
44b275c4 553(put 'tab-stop-list 'safe-local-variable 'listp)
483e630e 554
a17b712b
SM
555(defvar edit-tab-stops-map
556 (let ((map (make-sparse-keymap)))
557 (define-key map "\C-x\C-s" 'edit-tab-stops-note-changes)
558 (define-key map "\C-c\C-c" 'edit-tab-stops-note-changes)
559 map)
560 "Keymap used in `edit-tab-stops'.")
483e630e
RM
561
562(defvar edit-tab-stops-buffer nil
bc57b735
LT
563 "Buffer whose tab stops are being edited.
564This matters if the variable `tab-stop-list' is local in that buffer.")
483e630e
RM
565
566(defun edit-tab-stops ()
567 "Edit the tab stops used by `tab-to-tab-stop'.
568Creates a buffer *Tab Stops* containing text describing the tab stops.
569A colon indicates a column where there is a tab stop.
570You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops-note-changes] to make changes take effect."
571 (interactive)
572 (setq edit-tab-stops-buffer (current-buffer))
573 (switch-to-buffer (get-buffer-create "*Tab Stops*"))
574 (use-local-map edit-tab-stops-map)
b7d5bd82 575 (setq-local indent-tabs-mode nil)
483e630e
RM
576 (overwrite-mode 1)
577 (setq truncate-lines t)
578 (erase-buffer)
579 (let ((tabs tab-stop-list))
580 (while tabs
581 (indent-to (car tabs) 0)
582 (insert ?:)
583 (setq tabs (cdr tabs))))
584 (let ((count 0))
585 (insert ?\n)
586 (while (< count 8)
587 (insert (+ count ?0))
588 (insert " ")
589 (setq count (1+ count)))
590 (insert ?\n)
591 (while (> count 0)
592 (insert "0123456789")
593 (setq count (1- count))))
594 (insert "\nTo install changes, type C-c C-c")
595 (goto-char (point-min)))
596
597(defun edit-tab-stops-note-changes ()
598 "Put edited tab stops into effect."
599 (interactive)
600 (let (tabs)
601 (save-excursion
602 (goto-char 1)
603 (end-of-line)
604 (while (search-backward ":" nil t)
605 (setq tabs (cons (current-column) tabs))))
606 (bury-buffer (prog1 (current-buffer)
607 (switch-to-buffer edit-tab-stops-buffer)))
608 (setq tab-stop-list tabs))
609 (message "Tab stops installed"))
610
b7d5bd82
TL
611(defun indent--next-tab-stop (column &optional prev)
612 "Return the next tab stop after COLUMN.
613If PREV is non-nil, return the previous one instead."
614 (let ((tabs tab-stop-list))
615 (while (and tabs (>= column (car tabs)))
616 (setq tabs (cdr tabs)))
617 (if tabs
618 (if (not prev)
619 (car tabs)
620 (let ((prevtabs (cdr (memq (car tabs) (reverse tab-stop-list)))))
621 (if (null prevtabs) 0
622 (if (= column (car prevtabs))
623 (or (nth 1 prevtabs) 0)
624 (car prevtabs)))))
625 ;; We passed the end of tab-stop-list: guess a continuation.
626 (let* ((last2 (last tab-stop-list 2))
627 (step (if (cdr last2) (- (cadr last2) (car last2)) tab-width))
628 (last (or (cadr last2) (car last2) 0)))
629 ;; Repeat the last tab's length.
630 (+ last (* step (if prev
631 (if (<= column last) -1 (/ (- column last 1) step))
632 (1+ (/ (- column last) step)))))))))
633
483e630e
RM
634(defun tab-to-tab-stop ()
635 "Insert spaces or tabs to next defined tab-stop column.
636The variable `tab-stop-list' is a list of columns at which there are tab stops.
637Use \\[edit-tab-stops] to edit them interactively."
638 (interactive)
a2964053
RS
639 (and abbrev-mode (= (char-syntax (preceding-char)) ?w)
640 (expand-abbrev))
b7d5bd82
TL
641 (let ((nexttab (indent--next-tab-stop (current-column))))
642 (delete-horizontal-space t)
643 (indent-to nexttab)))
483e630e
RM
644
645(defun move-to-tab-stop ()
646 "Move point to next defined tab-stop column.
fbf8f564
RS
647The variable `tab-stop-list' is a list of columns at which there are tab stops.
648Use \\[edit-tab-stops] to edit them interactively."
649 (interactive)
b7d5bd82
TL
650 (let ((nexttab (indent--next-tab-stop (current-column))))
651 (let ((before (point)))
652 (move-to-column nexttab t)
653 (save-excursion
654 (goto-char before)
655 ;; If we just added a tab, or moved over one,
656 ;; delete any superfluous spaces before the old point.
657 (if (and (eq (preceding-char) ?\s)
658 (eq (following-char) ?\t))
659 (let ((tabend (* (/ (current-column) tab-width) tab-width)))
660 (while (and (> (current-column) tabend)
661 (eq (preceding-char) ?\s))
662 (forward-char -1))
663 (delete-region (point) before)))))))
fbf8f564 664
483e630e 665(define-key global-map "\t" 'indent-for-tab-command)
a17b712b 666(define-key esc-map "\C-\\" 'indent-region)
483e630e
RM
667(define-key ctl-x-map "\t" 'indent-rigidly)
668(define-key esc-map "i" 'tab-to-tab-stop)
1a06eabd
ER
669
670;;; indent.el ends here