diff options
Diffstat (limited to 'lisp/indent.el')
-rw-r--r-- | lisp/indent.el | 176 |
1 files changed, 122 insertions, 54 deletions
diff --git a/lisp/indent.el b/lisp/indent.el index d5ba0bd8491..c7ec5c9a3ed 100644 --- a/lisp/indent.el +++ b/lisp/indent.el @@ -1,6 +1,6 @@ ;;; indent.el --- indentation commands for Emacs -*- lexical-binding:t -*- -;; Copyright (C) 1985, 1995, 2001-2017 Free Software Foundation, Inc. +;; Copyright (C) 1985, 1995, 2001-2022 Free Software Foundation, Inc. ;; Maintainer: emacs-devel@gnu.org ;; Package: emacs @@ -39,8 +39,8 @@ (defvar indent-line-function 'indent-relative "Function to indent the current line. This function will be called with no arguments. -If it is called somewhere where auto-indentation cannot be done -\(e.g. inside a string), the function should simply return `noindent'. +If it is called somewhere where it cannot auto-indent, the function +should return `noindent' to signal that it didn't. Setting this function is all you need to make TAB indent appropriately. Don't rebind TAB unless you really need to.") @@ -52,6 +52,8 @@ or in the line's indentation, otherwise it inserts a \"real\" TAB character. If `complete', TAB first tries to indent the current line, and if the line was already indented, then try to complete the thing at point. +Also see `tab-first-completion'. + Some programming language modes have their own variable to control this, e.g., `c-tab-always-indent', and do not respect this variable." :group 'indent @@ -60,18 +62,49 @@ e.g., `c-tab-always-indent', and do not respect this variable." (const :tag "Indent if inside indentation, else TAB" nil) (const :tag "Indent, or if already indented complete" complete))) - -(defun indent-according-to-mode () +(defcustom tab-first-completion nil + "Governs the behavior of TAB completion on the first press of the key. +When nil, complete. When `eol', only complete if point is at the +end of a line. When `word', complete unless the next character +has word syntax (according to `syntax-after'). When +`word-or-paren', complete unless the next character is part of a +word or a parenthesis. When `word-or-paren-or-punct', complete +unless the next character is part of a word, parenthesis, or +punctuation. Typing TAB a second time always results in +completion. + +This variable has no effect unless `tab-always-indent' is `complete'." + :group 'indent + :type '(choice + (const :tag "Always complete" nil) + (const :tag "Only complete at the end of a line" eol) + (const :tag "Unless looking at a word" word) + (const :tag "Unless at a word or parenthesis" word-or-paren) + (const :tag "Unless at a word, parenthesis, or punctuation." + word-or-paren-or-punct)) + :version "28.1") + +(defvar indent-line-ignored-functions '(indent-relative + indent-relative-maybe + indent-relative-first-indent-point) + "Values that are ignored by `indent-according-to-mode'.") + +(defun indent-according-to-mode (&optional inhibit-widen) "Indent line in proper way for current major mode. Normally, this is done by calling the function specified by the variable `indent-line-function'. However, if the value of that -variable is `indent-relative' or `indent-relative-maybe', handle -it specially (since those functions are used for tabbing); in -that case, indent by aligning to the previous non-blank line." +variable is present in the `indent-line-ignored-functions' variable, +handle it specially (since those functions are used for tabbing); +in that case, indent by aligning to the previous non-blank line. + +Ignore restriction, unless the optional argument INHIBIT-WIDEN is +non-nil." (interactive) + (save-restriction + (unless inhibit-widen + (widen)) (syntax-propertize (line-end-position)) - (if (memq indent-line-function - '(indent-relative indent-relative-maybe)) + (if (memq indent-line-function indent-line-ignored-functions) ;; These functions are used for tabbing, but can't be used for ;; indenting. Replace with something ad-hoc. (let ((column (save-excursion @@ -84,7 +117,7 @@ that case, indent by aligning to the previous non-blank line." (indent-line-to column) (save-excursion (indent-line-to column)))) ;; The normal case. - (funcall indent-line-function))) + (funcall indent-line-function)))) (defun indent--default-inside-comment () (unless (or (> (current-column) (current-indentation)) @@ -109,7 +142,7 @@ or performs symbol completion, depending on `tab-always-indent'. The function called to actually indent the line or insert a tab is given by the variable `indent-line-function'. -If a prefix argument is given, after this function indents the +If a prefix argument is given (ARG), after this function indents the current line or inserts a tab, it also rigidly indents the entire balanced expression which starts at the beginning of the current line, to reflect the current line's indentation. @@ -137,20 +170,34 @@ prefix argument is ignored." (t (let ((old-tick (buffer-chars-modified-tick)) (old-point (point)) - (old-indent (current-indentation))) + (old-indent (current-indentation)) + (syn (syntax-after (point)))) ;; Indent the line. - (or (not (eq (funcall indent-line-function) 'noindent)) + (or (not (eq (indent--funcall-widened indent-line-function) 'noindent)) (indent--default-inside-comment) (when (or (<= (current-column) (current-indentation)) (not (eq tab-always-indent 'complete))) - (funcall (default-value 'indent-line-function)))) + (indent--funcall-widened (default-value 'indent-line-function)))) (cond ;; If the text was already indented right, try completion. ((and (eq tab-always-indent 'complete) - (eq old-point (point)) - (eq old-tick (buffer-chars-modified-tick))) + (eql old-point (point)) + (eql old-tick (buffer-chars-modified-tick)) + (or (null tab-first-completion) + (eq last-command this-command) + (and (eq tab-first-completion 'eol) + (eolp)) + (and (memq tab-first-completion + '(word word-or-paren word-or-paren-or-punct)) + (not (eql 2 syn))) + (and (memq tab-first-completion + '(word-or-paren word-or-paren-or-punct)) + (not (or (eql 4 syn) + (eql 5 syn)))) + (and (eq tab-first-completion 'word-or-paren-or-punct) + (not (eql 1 syn))))) (completion-at-point)) ;; If a prefix argument was given, rigidly indent the following @@ -166,6 +213,11 @@ prefix argument is ignored." (< (point) end-marker)) (indent-rigidly (point) end-marker indentation-change)))))))))) +(defun indent--funcall-widened (func) + (save-restriction + (widen) + (funcall func))) + (defun insert-tab (&optional arg) (let ((count (prefix-numeric-value arg))) (if (and abbrev-mode @@ -188,22 +240,26 @@ Blank lines are ignored." (current-indentation)))) indent)))) -(defvar indent-rigidly-map - (let ((map (make-sparse-keymap))) - (define-key map [left] 'indent-rigidly-left) - (define-key map [right] 'indent-rigidly-right) - (define-key map [S-left] 'indent-rigidly-left-to-tab-stop) - (define-key map [S-right] 'indent-rigidly-right-to-tab-stop) - map) - "Transient keymap for adjusting indentation interactively. -It is activated by calling `indent-rigidly' interactively.") +(defvar-keymap indent-rigidly-map + :doc "Transient keymap for adjusting indentation interactively. +It is activated by calling `indent-rigidly' interactively." + "TAB" #'indent-rigidly-right + "<left>" #'indent-rigidly-left + "<right>" #'indent-rigidly-right + "S-<left>" #'indent-rigidly-left-to-tab-stop + "S-<right>" #'indent-rigidly-right-to-tab-stop) +(put 'indent-rigidly-right :advertised-binding (kbd "<right>")) (defun indent-rigidly (start end arg &optional interactive) "Indent all lines starting in the region. If called interactively with no prefix argument, activate a transient mode in which the indentation can be adjusted interactively by typing \\<indent-rigidly-map>\\[indent-rigidly-left], \\[indent-rigidly-right], \\[indent-rigidly-left-to-tab-stop], or \\[indent-rigidly-right-to-tab-stop]. -Typing any other key deactivates the transient mode. +In addition, \\`TAB' is also bound (and calls `indent-rigidly-right'). + +Typing any other key exits this mode, and this key is then +acted upon as normally. If `transient-mark-mode' is enabled, +exiting also deactivates the mark. If called from a program, or interactively with prefix ARG, indent all lines starting in the region forward by ARG columns. @@ -214,11 +270,8 @@ Negative values of ARG indent backward, so you can remove all indentation by specifying a large negative ARG." (interactive "r\nP\np") (if (and (not arg) interactive) - (progn - (message - (substitute-command-keys - "Indent region with \\<indent-rigidly-map>\\[indent-rigidly-left], \\[indent-rigidly-right], \\[indent-rigidly-left-to-tab-stop], or \\[indent-rigidly-right-to-tab-stop].")) - (set-transient-map indent-rigidly-map t #'deactivate-mark)) + (set-transient-map indent-rigidly-map t #'deactivate-mark + "Indent region with %k") (save-excursion (goto-char end) (setq end (point-marker)) @@ -285,7 +338,8 @@ indentation by specifying a large negative ARG." "Indent current line to COLUMN. This function removes or adds spaces and tabs at beginning of line only if necessary. It leaves point at end of indentation." - (back-to-indentation) + (beginning-of-line 1) + (skip-chars-forward " \t") (let ((cur-col (current-column))) (cond ((< cur-col column) (if (>= (- column (* (/ cur-col tab-width) tab-width)) tab-width) @@ -293,8 +347,13 @@ only if necessary. It leaves point at end of indentation." (progn (skip-chars-backward " ") (point)))) (indent-to column)) ((> cur-col column) ; too far right (after tab?) - (delete-region (progn (move-to-column column t) (point)) - (progn (backward-to-indentation 0) (point))))))) + (delete-region (progn (move-to-column column t) (point)) + ;; The `move-to-column' call may replace + ;; tabs with spaces, so we can't reuse the + ;; previous start point. + (progn (beginning-of-line 1) + (skip-chars-forward " \t") + (point))))))) (defun current-left-margin () "Return the left margin to use for this line. @@ -408,7 +467,7 @@ Optional fifth argument OBJECT specifies the string or buffer to operate on." (put-text-property begin to prop (funcall func val) object)))) (defun increase-left-margin (from to inc) - "Increase or decrease the left-margin of the region. + "Increase or decrease the `left-margin' of the region. With no prefix argument, this adds `standard-indent' of indentation. A prefix arg (optional third arg INC noninteractively) specifies the amount to change the margin by, in characters. @@ -465,12 +524,15 @@ If `auto-fill-mode' is active, re-fills region to fit in new margin." (defun beginning-of-line-text (&optional n) "Move to the beginning of the text on this line. -With optional argument, move forward N-1 lines first. -From the beginning of the line, moves past the left-margin indentation, the -fill-prefix, and any indentation used for centering or right-justifying the -line, but does not move past any whitespace that was explicitly inserted -\(such as a tab used to indent the first line of a paragraph)." - (interactive "p") + +With optional argument N, move forward N-1 lines first. + +From the beginning of the line, moves past the `left-margin' +indentation, the `fill-prefix', and any indentation used for +centering or right-justifying the line, but does not move past +any whitespace that was explicitly inserted (such as a tab used +to indent the first line of a paragraph)." + (interactive "^p") (beginning-of-line n) (skip-chars-forward " \t") ;; Skip over fill-prefix. @@ -538,10 +600,15 @@ column to indent to; if it is nil, use one of the three methods above." (forward-line 1))))) ;; Use indent-region-function is available. (indent-region-function - (funcall indent-region-function start end)) + (save-restriction + (widen) + (funcall indent-region-function start end))) ;; Else, use a default implementation that calls indent-line-function on ;; each line. - (t (indent-region-line-by-line start end))) + (t + (save-restriction + (widen) + (indent-region-line-by-line start end)))) ;; In most cases, reindenting modifies the buffer, but it may also ;; leave it unmodified, in which case we have to deactivate the mark ;; by hand. @@ -555,7 +622,7 @@ column to indent to; if it is nil, use one of the three methods above." (make-progress-reporter "Indenting region..." (point) end)))) (while (< (point) end) (or (and (bolp) (eolp)) - (indent-according-to-mode)) + (indent-according-to-mode t)) (forward-line 1) (and pr (progress-reporter-update pr (point)))) (and pr (progress-reporter-done pr)) @@ -583,8 +650,9 @@ considered. If the previous nonblank line has no indent points beyond the column point starts at, then `tab-to-tab-stop' is done, if both -FIRST-ONLY and UNINDENTED-OK are nil, otherwise nothing is done -in this case. +FIRST-ONLY and UNINDENTED-OK are nil, otherwise nothing is done. +If there isn't a previous nonblank line and UNINDENTED-OK is nil, +call `tab-to-tab-stop'. See also `indent-relative-first-indent-point'." (interactive "P") @@ -627,12 +695,10 @@ A value of nil means a tab stop every `tab-width' columns." :safe 'listp :type '(repeat integer)) -(defvar edit-tab-stops-map - (let ((map (make-sparse-keymap))) - (define-key map "\C-x\C-s" 'edit-tab-stops-note-changes) - (define-key map "\C-c\C-c" 'edit-tab-stops-note-changes) - map) - "Keymap used in `edit-tab-stops'.") +(defvar-keymap edit-tab-stops-map + :doc "Keymap used in `edit-tab-stops'." + "C-x C-s" #'edit-tab-stops-note-changes + "C-c C-c" #'edit-tab-stops-note-changes) (defvar edit-tab-stops-buffer nil "Buffer whose tab stops are being edited. @@ -666,7 +732,9 @@ You can add or remove colons and then do \\<edit-tab-stops-map>\\[edit-tab-stops (while (> count 0) (insert "0123456789") (setq count (1- count)))) - (insert "\nTo install changes, type C-c C-c") + (insert (substitute-command-keys + (concat "\nTo install changes, type \\<edit-tab-stops-map>" + "\\[edit-tab-stops-note-changes]"))) (goto-char (point-min))) (defun edit-tab-stops-note-changes () |