summaryrefslogtreecommitdiff
path: root/lisp/simple.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/simple.el')
-rw-r--r--lisp/simple.el1337
1 files changed, 1017 insertions, 320 deletions
diff --git a/lisp/simple.el b/lisp/simple.el
index dca8589be46..042384bbe72 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -60,6 +60,24 @@ value of 1 means that nothing is amalgamated.")
(defgroup paren-matching nil
"Highlight (un)matching of parens and expressions."
:group 'matching)
+
+(defvar-local escaped-string-quote "\\"
+ "String to insert before a string quote character in a string to escape it.
+This is typically a backslash (in most languages):
+
+ \\='foo\\\\='bar\\='
+ \"foo\\\"bar\"
+
+But in SQL, for instance, it's \"\\='\":
+
+ \\='foo\\='\\='bar\\='
+
+This can also be a function, which is called with the string
+terminator as the argument, and should return a string to be
+used as the escape.
+
+This variable is used by the `yank-in-context' command.")
+
;;; next-error support framework
@@ -494,7 +512,7 @@ buffer causes automatic display of the corresponding source code location."
(error t))))
(defun next-error-message-highlight (error-buffer)
- "Highlight the current error message in the ‘next-error’ buffer."
+ "Highlight the current error message in the `next-error' buffer."
(when next-error-message-highlight
(with-current-buffer error-buffer
(when (and next-error--message-highlight-overlay
@@ -527,21 +545,28 @@ Other major modes are defined by comparison with this one."
(kill-all-local-variables)
(run-mode-hooks))
+(define-derived-mode clean-mode fundamental-mode "Clean"
+ "A mode that removes all overlays and text properties."
+ (kill-all-local-variables t)
+ (let ((inhibit-read-only t))
+ (dolist (overlay (overlays-in (point-min) (point-max)))
+ (delete-overlay overlay))
+ (set-text-properties (point-min) (point-max) nil)
+ (setq-local yank-excluded-properties t)))
+
;; Special major modes to view specially formatted data rather than files.
-(defvar special-mode-map
- (let ((map (make-sparse-keymap)))
- (suppress-keymap map)
- (define-key map "q" 'quit-window)
- (define-key map " " 'scroll-up-command)
- (define-key map [?\S-\ ] 'scroll-down-command)
- (define-key map "\C-?" 'scroll-down-command)
- (define-key map "?" 'describe-mode)
- (define-key map "h" 'describe-mode)
- (define-key map ">" 'end-of-buffer)
- (define-key map "<" 'beginning-of-buffer)
- (define-key map "g" 'revert-buffer)
- map))
+(defvar-keymap special-mode-map
+ :suppress t
+ "q" #'quit-window
+ "SPC" #'scroll-up-command
+ "S-SPC" #'scroll-down-command
+ "DEL" #'scroll-down-command
+ "?" #'describe-mode
+ "h" #'describe-mode
+ ">" #'end-of-buffer
+ "<" #'beginning-of-buffer
+ "g" #'revert-buffer)
(put 'special-mode 'mode-class 'special)
(define-derived-mode special-mode nil "Special"
@@ -703,9 +728,10 @@ When called from Lisp code, ARG may be a prefix string to copy."
:height 0.1 :background "#505050")
(((type graphic) (background light))
:height 0.1 :background "#a0a0a0")
- (t :foreground "ForestGreen"))
+ (t
+ :foreground "ForestGreen" :underline t))
"Face for separator lines."
- :version "28.1"
+ :version "29.1"
:group 'text)
(defun make-separator-line (&optional length)
@@ -713,11 +739,13 @@ When called from Lisp code, ARG may be a prefix string to copy."
This uses the `separator-line' face.
If LENGTH is nil, use the window width."
- (if (display-graphic-p)
+ (if (or (display-graphic-p)
+ (display-supports-face-attributes-p '(:underline t)))
(if length
(concat (propertize (make-string length ?\s) 'face 'separator-line)
"\n")
(propertize "\n" 'face '(:inherit separator-line :extend t)))
+ ;; In terminals (that don't support underline), use a line of dashes.
(concat (propertize (make-string (or length (1- (window-width))) ?-)
'face 'separator-line)
"\n")))
@@ -1062,15 +1090,26 @@ Leave one space or none, according to the context."
"Delete all spaces and tabs around point.
If BACKWARD-ONLY is non-nil, delete them only before point."
(interactive "*P")
+ (delete-space--internal " \t" backward-only))
+
+(defun delete-all-space (&optional backward-only)
+ "Delete all spaces, tabs, and newlines around point.
+If BACKWARD-ONLY is non-nil, delete them only before point."
+ (interactive "*P")
+ (delete-space--internal " \t\r\n" backward-only))
+
+(defun delete-space--internal (chars backward-only)
+ "Delete CHARS around point.
+If BACKWARD-ONLY is non-nil, delete them only before point."
(let ((orig-pos (point)))
(delete-region
(if backward-only
- orig-pos
+ orig-pos
(progn
- (skip-chars-forward " \t")
- (constrain-to-field nil orig-pos t)))
+ (skip-chars-forward chars)
+ (constrain-to-field nil orig-pos t)))
(progn
- (skip-chars-backward " \t")
+ (skip-chars-backward chars)
(constrain-to-field nil orig-pos)))))
(defun just-one-space (&optional n)
@@ -1078,73 +1117,225 @@ If BACKWARD-ONLY is non-nil, delete them only before point."
If N is negative, delete newlines as well, leaving -N spaces.
See also `cycle-spacing'."
(interactive "*p")
- (cycle-spacing n nil 'single-shot))
+ (let ((orig-pos (point))
+ (skip-characters (if (and n (< n 0)) " \t\n\r" " \t"))
+ (num (abs (or n 1))))
+ (skip-chars-backward skip-characters)
+ (constrain-to-field nil orig-pos)
+ (let* ((num (- num (skip-chars-forward " " (+ num (point)))))
+ (mid (point))
+ (end (progn
+ (skip-chars-forward skip-characters)
+ (constrain-to-field nil orig-pos t))))
+ (delete-region mid end)
+ (insert (make-string num ?\s)))))
(defvar cycle-spacing--context nil
- "Store context used in consecutive calls to `cycle-spacing' command.
-The first time `cycle-spacing' runs, it saves in this variable:
-its N argument, the original point position, and the original spacing
-around point.")
-
-(defun cycle-spacing (&optional n preserve-nl-back mode)
+ "Stored context used in consecutive calls to `cycle-spacing' command.
+The value is a property list with the following elements:
+- `:orig-pos' The original position of point when starting the
+ sequence.
+- `:whitespace-string' All whitespace characters around point
+ including newlines.
+- `:n' The prefix arg given to the initial invocation
+ which is reused for all actions in this cycle.
+- `:last-action' The last action performed in the cycle.")
+
+(defcustom cycle-spacing-actions
+ '( just-one-space
+ delete-all-space
+ restore)
+ "List of actions cycled through by `cycle-spacing'.
+Supported values are:
+- `just-one-space' Delete all but N (prefix arg) spaces.
+ See that command's docstring for details.
+- `delete-space-after' Delete spaces after point keeping only N.
+- `delete-space-before' Delete spaces before point keeping only N.
+- `delete-all-space' Delete all spaces around point.
+- `restore' Restore the original spacing.
+
+All actions make use of the prefix arg given to `cycle-spacing'
+in the initial invocation, i.e., `just-one-space' keeps this
+amount of spaces deleting surplus ones. `just-one-space' and all
+other actions have the contract that a positive prefix arg (or
+zero) only deletes tabs and spaces whereas a negative prefix arg
+also deletes newlines.
+
+The `delete-space-before' and `delete-space-after' actions handle
+the prefix arg \\[negative-argument] without a number provided
+specially: all spaces before/after point are deleted (as if N was
+0) including newlines (as if N was negative).
+
+In addition to the predefined actions listed above, any function
+which accepts one argument is allowed. It receives the raw
+prefix arg of this cycle.
+
+In addition, an action may take the form (ACTION ARG) where
+ACTION is one of the predefined actions (except for `restore')
+and ARG is either
+- an integer with the meaning that ACTION should always use this
+ fixed integer instead of the actual prefix arg or
+- the symbol `inverted-arg' with the meaning that ACTION should
+ be performed with the inverted actual prefix arg.
+- the symbol `-' with the meaning that ACTION should include
+ newlines but it's up to the ACTION to decide how to interpret
+ it as a number, e.g., `delete-space-before' and
+ `delete-space-after' treat it like 0 whereas `just-one-space'
+ treats it like -1 as is usual."
+ :group 'editing-basics
+ :type (let ((actions
+ '((const :tag "Just N (prefix arg) spaces" just-one-space)
+ (const :tag "Delete spaces after point" delete-space-after)
+ (const :tag "Delete spaces before point" delete-space-before)
+ (const :tag "Delete all spaces around point" delete-all-space)
+ (function :tag "Function receiving a numeric arg"))))
+ `(repeat
+ (choice
+ ,@actions
+ (list :tag "Action with modified arg"
+ (choice ,@actions)
+ (choice (const :tag "Inverted prefix arg" inverted-arg)
+ (integer :tag "Fixed numeric arg")
+ (const :tag "Negative arg" -)))
+ (const :tag "Restore the original spacing" restore))))
+ :version "29.1")
+
+(defun cycle-spacing (&optional n)
"Manipulate whitespace around point in a smart way.
-In interactive use, this function behaves differently in successive
-consecutive calls.
-
-The first call in a sequence acts like `just-one-space'.
-It deletes all spaces and tabs around point, leaving one space
-\(or N spaces). N is the prefix argument. If N is negative,
-it deletes newlines as well, leaving -N spaces.
-\(If PRESERVE-NL-BACK is non-nil, it does not delete newlines before point.)
-
-The second call in a sequence deletes all spaces.
-
-The third call in a sequence restores the original whitespace (and point).
-
-If MODE is `single-shot', it performs only the first step in the sequence.
-If MODE is `fast' and the first step would not result in any change
-\(i.e., there are exactly (abs N) spaces around point),
-the function goes straight to the second step.
-
-Repeatedly calling the function with different values of N starts a
-new sequence each time."
- (interactive "*p")
- (let ((orig-pos (point))
- (skip-characters (if (and n (< n 0)) " \t\n\r" " \t"))
- (num (abs (or n 1))))
- (skip-chars-backward (if preserve-nl-back " \t" skip-characters))
- (constrain-to-field nil orig-pos)
- (cond
- ;; Command run for the first time, single-shot mode or different argument
- ((or (eq 'single-shot mode)
- (not (equal last-command this-command))
- (not cycle-spacing--context)
- (not (eq (car cycle-spacing--context) n)))
- (let* ((start (point))
- (num (- num (skip-chars-forward " " (+ num (point)))))
- (mid (point))
- (end (progn
- (skip-chars-forward skip-characters)
- (constrain-to-field nil orig-pos t))))
- (setq cycle-spacing--context ;; Save for later.
- ;; Special handling for case where there was no space at all.
- (unless (= start end)
- (cons n (cons orig-pos (buffer-substring start (point))))))
- ;; If this run causes no change in buffer content, delete all spaces,
- ;; otherwise delete all excess spaces.
- (delete-region (if (and (eq mode 'fast) (zerop num) (= mid end))
- start mid) end)
- (insert (make-string num ?\s))))
-
- ;; Command run for the second time.
- ((not (equal orig-pos (point)))
- (delete-region (point) orig-pos))
-
- ;; Command run for the third time.
- (t
- (insert (cddr cycle-spacing--context))
- (goto-char (cadr cycle-spacing--context))
- (setq cycle-spacing--context nil)))))
+Repeated calls perform the actions in `cycle-spacing-actions' one
+after the other, wrapping around after the last one.
+
+All actions are amendable using a prefix arg N. In general, a
+zero or positive prefix arg allows only for deletion of tabs and
+spaces whereas a negative prefix arg also allows for deleting
+newlines.
+
+The prefix arg given at the first invocation starting a cycle is
+provided to all following actions, i.e.,
+ \\[negative-argument] \\[cycle-spacing] \\[cycle-spacing] \\[cycle-spacing]
+is equivalent to
+ \\[negative-argument] \\[cycle-spacing] \\[negative-argument] \\[cycle-spacing] \\[negative-argument] \\[cycle-spacing].
+
+A new sequence can be started by providing a different prefix arg
+than provided at the initial invocation (except for 1), or by
+doing any other command before the next \\[cycle-spacing]."
+ (interactive "*P")
+ ;; Initialize `cycle-spacing--context' if needed.
+ (when (or (not (equal last-command this-command))
+ (not cycle-spacing--context)
+ ;; With M-5 M-SPC M-SPC... we pass the prefix arg 5 to
+ ;; each action and only start a new cycle when a different
+ ;; prefix arg is given and which is not the default value
+ ;; 1.
+ (and n (not (equal (plist-get cycle-spacing--context :n)
+ n))))
+ (let ((orig-pos (point))
+ (skip-characters " \t\n\r"))
+ (save-excursion
+ (skip-chars-backward skip-characters)
+ (constrain-to-field nil orig-pos)
+ (let ((start (point))
+ (end (progn
+ (skip-chars-forward skip-characters)
+ (constrain-to-field nil orig-pos t))))
+ (setq cycle-spacing--context ;; Save for later.
+ (list :orig-pos orig-pos
+ :whitespace-string (buffer-substring start end)
+ :n n
+ :last-action nil))))))
+
+ ;; Cycle through the actions in `cycle-spacing-actions'.
+ (when cycle-spacing--context
+ (cl-labels ((next-action ()
+ (let* ((l cycle-spacing-actions)
+ (elt (plist-get cycle-spacing--context
+ :last-action)))
+ (if (null elt)
+ (car cycle-spacing-actions)
+ (catch 'found
+ (while l
+ (cond
+ ((null (cdr l))
+ (throw 'found
+ (when (eq elt (car l))
+ (car cycle-spacing-actions))))
+ ((and (eq elt (car l))
+ (cdr l))
+ (throw 'found (cadr l)))
+ (t (setq l (cdr l)))))))))
+ (skip-chars (chars max-dist direction)
+ (if (eq direction 'forward)
+ (skip-chars-forward
+ chars
+ (and max-dist (+ (point) max-dist)))
+ (skip-chars-backward
+ chars
+ (and max-dist (- (point) max-dist)))))
+ (delete-space (n include-newlines direction)
+ (let ((orig-point (point))
+ (chars (if include-newlines
+ " \t\r\n"
+ " \t")))
+ (when (or (zerop n)
+ (= n (abs (skip-chars chars n direction))))
+ (let ((start (point))
+ (end (progn
+ (skip-chars chars nil direction)
+ (point))))
+ (unless (= start end)
+ (delete-region start end))
+ (goto-char (if (eq direction 'forward)
+ orig-point
+ (+ n end)))))))
+ (restore ()
+ (delete-all-space)
+ (insert (plist-get cycle-spacing--context
+ :whitespace-string))
+ (goto-char (plist-get cycle-spacing--context
+ :orig-pos))))
+ (let ((action (next-action)))
+ (atomic-change-group
+ (restore)
+ (unless (eq action 'restore)
+ ;; action can be some-action or (some-action <arg>) where
+ ;; arg is either an integer, the arg to be always used for
+ ;; this action or - to use the inverted context n for this
+ ;; action.
+ (let* ((actual-action (if (listp action)
+ (car action)
+ action))
+ (arg (when (listp action)
+ (nth 1 action)))
+ (context-n (plist-get cycle-spacing--context :n))
+ (actual-n (cond
+ ((integerp arg) arg)
+ ((eq 'inverted-arg arg)
+ (* -1 (prefix-numeric-value context-n)))
+ ((eq '- arg) '-)
+ (t context-n)))
+ (numeric-n (prefix-numeric-value actual-n))
+ (include-newlines (or (eq actual-n '-)
+ (and (integerp actual-n)
+ (< actual-n 0)))))
+ (cond
+ ((eq actual-action 'just-one-space)
+ (just-one-space numeric-n))
+ ((eq actual-action 'delete-space-after)
+ (delete-space (if (eq actual-n '-) 0 (abs numeric-n))
+ include-newlines 'forward))
+ ((eq actual-action 'delete-space-before)
+ (delete-space (if (eq actual-n '-) 0 (abs numeric-n))
+ include-newlines 'backward))
+ ((eq actual-action 'delete-all-space)
+ (if include-newlines
+ (delete-all-space)
+ (delete-horizontal-space)))
+ ((functionp actual-action)
+ (funcall actual-action actual-n))
+ (t
+ (error "Don't know how to handle action %S" action)))))
+ (setf (plist-get cycle-spacing--context :last-action)
+ action))))))
(defun beginning-of-buffer (&optional arg)
"Move point to the beginning of the buffer.
@@ -1282,6 +1473,11 @@ If Transient Mark mode is enabled, the mark is active, and N is 1,
delete the text in the region and deactivate the mark instead.
To disable this, set variable `delete-active-region' to nil.
+If N is positive, characters composed into a single grapheme cluster
+count as a single character and are deleted together. Thus,
+\"\\[universal-argument] 2 \\[delete-forward-char]\" when two grapheme clusters follow point will
+delete the characters composed into both of the grapheme clusters.
+
Optional second arg KILLFLAG non-nil means to kill (save in kill
ring) instead of delete. If called interactively, a numeric
prefix argument specifies N, and KILLFLAG is also set if a prefix
@@ -1302,6 +1498,34 @@ the actual saved text might be different from what was killed."
(kill-region (region-beginning) (region-end) 'region)
(funcall region-extract-function 'delete-only)))
+ ;; For forward deletion, treat composed characters as a single
+ ;; character to delete.
+ ((>= n 1)
+ (let ((pos (point))
+ start cmp)
+ (setq start pos)
+ (while (> n 0)
+ ;; 'find-composition' will return (FROM TO ....) or nil.
+ (setq cmp (find-composition pos))
+ (setq pos
+ (if cmp
+ (let ((from (car cmp))
+ (to (cadr cmp)))
+ (cond
+ ((= (length cmp) 2) ; static composition
+ to)
+ ;; TO can be at POS, in which case we want
+ ;; to make sure we advance at least by 1
+ ;; character.
+ ((<= to pos)
+ (1+ pos))
+ (t
+ (lgstring-glyph-boundary (nth 2 cmp)
+ from (1+ pos)))))
+ (1+ pos)))
+ (setq n (1- n)))
+ (delete-char (- pos start) killflag)))
+
;; Otherwise, do simple deletion.
(t (delete-char n killflag))))
@@ -1447,48 +1671,64 @@ START and END."
(cond ((not (called-interactively-p 'any))
(count-words start end))
(arg
- (count-words--buffer-message))
+ (message "%s" (count-words--buffer-format)))
(t
- (count-words--message "Region" start end))))
+ (message "%s" (count-words--format "Region" start end)))))
-(defun count-words (start end)
+(defun count-words (start end &optional totals)
"Count words between START and END.
If called interactively, START and END are normally the start and
end of the buffer; but if the region is active, START and END are
the start and end of the region. Print a message reporting the
-number of lines, words, and chars.
+number of lines, sentences, words, and chars. With prefix
+argument, also include the data for the entire (un-narrowed)
+buffer.
If called from Lisp, return the number of words between START and
-END, without printing any message."
- (interactive (list nil nil))
- (cond ((not (called-interactively-p 'any))
- (let ((words 0)
- ;; Count across field boundaries. (Bug#41761)
- (inhibit-field-text-motion t))
- (save-excursion
- (save-restriction
- (narrow-to-region start end)
- (goto-char (point-min))
- (while (forward-word-strictly 1)
- (setq words (1+ words)))))
- words))
- ((use-region-p)
- (call-interactively 'count-words-region))
- (t
- (count-words--buffer-message))))
-
-(defun count-words--buffer-message ()
- (count-words--message
+END, without printing any message. TOTALS is ignored when called
+from Lisp."
+ (interactive (list nil nil current-prefix-arg))
+ ;; When called from Lisp, return the data.
+ (if (not (called-interactively-p 'any))
+ (let ((words 0)
+ ;; Count across field boundaries. (Bug#41761)
+ (inhibit-field-text-motion t))
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start end)
+ (goto-char (point-min))
+ (while (forward-word-strictly 1)
+ (setq words (1+ words)))))
+ words)
+ ;; When called interactively, message the data.
+ (let ((totals (if (and totals
+ (or (use-region-p)
+ (buffer-narrowed-p)))
+ (save-restriction
+ (widen)
+ (count-words--format "; buffer in total"
+ (point-min) (point-max)))
+ "")))
+ (if (use-region-p)
+ (message "%s%s" (count-words--format
+ "Region" (region-beginning) (region-end))
+ totals)
+ (message "%s%s" (count-words--buffer-format) totals)))))
+
+(defun count-words--buffer-format ()
+ (count-words--format
(if (buffer-narrowed-p) "Narrowed part of buffer" "Buffer")
(point-min) (point-max)))
-(defun count-words--message (str start end)
+(defun count-words--format (str start end)
(let ((lines (count-lines start end))
+ (sentences (count-sentences start end))
(words (count-words start end))
(chars (- end start)))
- (message "%s has %d line%s, %d word%s, and %d character%s."
+ (format "%s has %d line%s, %d sentence%s, %d word%s, and %d character%s"
str
lines (if (= lines 1) "" "s")
+ sentences (if (= sentences 1) "" "s")
words (if (= words 1) "" "s")
chars (if (= chars 1) "" "s"))))
@@ -1971,7 +2211,7 @@ to get different commands to edit and resubmit."
If it's nil, include all the commands.
If it's a function, it will be called with two parameters: the
symbol of the command and a buffer. The predicate should return
-non-nil if the command should be present when doing `M-x TAB'
+non-nil if the command should be present when doing \\`M-x TAB'
in that buffer."
:version "28.1"
:group 'completion
@@ -1980,9 +2220,53 @@ in that buffer."
command-completion-default-include-p)
(function :tag "Other function")))
-(defun read-extended-command ()
+(defun execute-extended-command-cycle ()
+ "Choose the next version of the extended command predicates.
+See `extended-command-versions'."
+ (interactive)
+ (throw 'cycle
+ (cons (minibuffer-contents)
+ (- (point) (minibuffer-prompt-end)))))
+
+(defvar extended-command-versions
+ (list (list "M-x " (lambda () read-extended-command-predicate))
+ (list "M-X " #'command-completion--command-for-this-buffer-function))
+ "Alist of prompts and what the extended command predicate should be.
+This is used by the \\<minibuffer-local-must-match-map>\\[execute-extended-command-cycle] command when reading an extended command.")
+
+(defun read-extended-command (&optional prompt)
"Read command name to invoke in `execute-extended-command'.
This function uses the `read-extended-command-predicate' user option."
+ (let ((default-predicate read-extended-command-predicate)
+ (read-extended-command-predicate read-extended-command-predicate)
+ already-typed ret)
+ ;; If we have a prompt (which is the name of the version of the
+ ;; command), then set up the predicate from
+ ;; `extended-command-versions'.
+ (if (not prompt)
+ (setq prompt (caar extended-command-versions))
+ (setq read-extended-command-predicate
+ (funcall (cadr (assoc prompt extended-command-versions)))))
+ ;; Normally this will only execute once.
+ (while (not (stringp ret))
+ (when (consp (setq ret (catch 'cycle
+ (read-extended-command-1 prompt
+ already-typed))))
+ ;; But if the user hit `M-X', then we `throw'ed out to that
+ ;; `catch', and we cycle to the next setting.
+ (let ((next (or (cadr (memq (assoc prompt extended-command-versions)
+ extended-command-versions))
+ ;; Last one; cycle back to the first.
+ (car extended-command-versions))))
+ ;; Restore the user's default predicate.
+ (setq read-extended-command-predicate default-predicate)
+ ;; Then calculate the next.
+ (setq prompt (car next)
+ read-extended-command-predicate (funcall (cadr next))
+ already-typed ret))))
+ ret))
+
+(defun read-extended-command-1 (prompt initial-input)
(let ((buffer (current-buffer)))
(minibuffer-with-setup-hook
(lambda ()
@@ -2007,8 +2291,8 @@ This function uses the `read-extended-command-predicate' user option."
(cons def (delete def all))
all)))))
;; Read a string, completing from and restricting to the set of
- ;; all defined commands. Don't provide any initial input.
- ;; Save the command read on the extended-command history list.
+ ;; all defined commands. Save the command read on the
+ ;; extended-command history list.
(completing-read
(concat (cond
((eq current-prefix-arg '-) "- ")
@@ -2026,9 +2310,7 @@ This function uses the `read-extended-command-predicate' user option."
;; but actually a prompt other than "M-x" would be confusing,
;; because "M-x" is a well-known prompt to read a command
;; and it serves as a shorthand for "Extended command: ".
- (if (memq 'shift (event-modifiers last-command-event))
- "M-X "
- "M-x "))
+ (or prompt "M-x "))
(lambda (string pred action)
(if (and suggest-key-bindings (eq action 'metadata))
'(metadata
@@ -2067,12 +2349,12 @@ This function uses the `read-extended-command-predicate' user option."
(funcall read-extended-command-predicate sym buffer)
(error (message "read-extended-command-predicate: %s: %s"
sym (error-message-string err))))))))
- t nil 'extended-command-history))))
+ t initial-input 'extended-command-history))))
(defun command-completion-using-modes-p (symbol buffer)
"Say whether SYMBOL has been marked as a mode-specific command in BUFFER."
;; Check the modes.
- (let ((modes (command-modes symbol)))
+ (when-let ((modes (command-modes symbol)))
;; Common fast case: Just a single mode.
(if (null (cdr modes))
(or (provided-mode-derived-p
@@ -2223,6 +2505,11 @@ invoking, give a prefix argument to `execute-extended-command'."
(find-shorter nil))
(unless (commandp function)
(error "`%s' is not a valid command name" command-name))
+ ;; If we're executing a command that's remapped, we can't actually
+ ;; execute that command with the keymapping we've found with
+ ;; `where-is-internal'.
+ (when (and binding (command-remapping function))
+ (setq binding nil))
;; Some features, such as novice.el, rely on this-command-keys
;; including M-x COMMAND-NAME RET.
(set--this-command-keys (concat "\M-x" (symbol-name function) "\r"))
@@ -2293,27 +2580,80 @@ minor modes), as well as commands bound in the active local key
maps."
(declare (interactive-only command-execute))
(interactive
- (let* ((execute-extended-command--last-typed nil)
- (keymaps
- ;; The major mode's keymap and any active minor modes.
- (nconc
- (and (current-local-map) (list (current-local-map)))
- (mapcar
- #'cdr
- (seq-filter
- (lambda (elem)
- (symbol-value (car elem)))
- minor-mode-map-alist))))
- (read-extended-command-predicate
- (lambda (symbol buffer)
- (or (command-completion-using-modes-p symbol buffer)
- (where-is-internal symbol keymaps)))))
+ (let ((execute-extended-command--last-typed nil))
(list current-prefix-arg
- (read-extended-command)
+ (read-extended-command "M-X ")
execute-extended-command--last-typed)))
(with-suppressed-warnings ((interactive-only execute-extended-command))
(execute-extended-command prefixarg command-name typed)))
+(defun command-completion--command-for-this-buffer-function ()
+ (let ((keymaps
+ ;; The major mode's keymap and any active minor modes.
+ (nconc
+ (and (current-local-map) (list (current-local-map)))
+ (mapcar
+ #'cdr
+ (seq-filter
+ (lambda (elem)
+ (symbol-value (car elem)))
+ minor-mode-map-alist)))))
+ (lambda (symbol buffer)
+ (or (command-completion-using-modes-p symbol buffer)
+ ;; Include commands that are bound in a keymap in the
+ ;; current buffer.
+ (and (where-is-internal symbol keymaps)
+ ;; But not if they have a command predicate that
+ ;; says that they shouldn't. (This is the case
+ ;; for `ignore' and `undefined' and similar
+ ;; commands commonly found in keymaps.)
+ (or (null (get symbol 'completion-predicate))
+ (funcall (get symbol 'completion-predicate)
+ symbol buffer)))))))
+
+(cl-defgeneric function-documentation (function)
+ "Extract the raw docstring info from FUNCTION.
+FUNCTION is expected to be a function value rather than, say, a mere symbol.
+This is intended to be specialized via `cl-defmethod' but not called directly:
+if you need a function's documentation use `documentation' which will call this
+function as needed."
+ (let ((docstring-p (lambda (doc)
+ ;; A docstring can be either a string or a reference
+ ;; into either the `etc/DOC' or a `.elc' file.
+ (or (stringp doc)
+ (fixnump doc) (fixnump (cdr-safe doc))))))
+ (pcase function
+ ((pred byte-code-function-p)
+ (when (> (length function) 4)
+ (let ((doc (aref function 4)))
+ (when (funcall docstring-p doc) doc))))
+ ((or (pred stringp) (pred vectorp)) "Keyboard macro.")
+ (`(keymap . ,_)
+ "Prefix command (definition is a keymap associating keystrokes with commands).")
+ ((or `(lambda ,_args . ,body) `(closure ,_env ,_args . ,body)
+ `(autoload ,_file . ,body))
+ (let ((doc (car body)))
+ (when (and (funcall docstring-p doc)
+ ;; Handle a doc reference--but these never come last
+ ;; in the function body, so reject them if they are last.
+ (or (cdr body) (eq 'autoload (car-safe function))))
+ doc)))
+ (_ (signal 'invalid-function (list function))))))
+
+(cl-defmethod function-documentation ((function accessor))
+ (oclosure--accessor-docstring function)) ;; FIXME: η-reduce!
+
+;; This should be in `oclosure.el' but that file is loaded before `cl-generic'.
+(cl-defgeneric oclosure-interactive-form (_function)
+ "Return the interactive form of FUNCTION or nil if none.
+This is called by `interactive-form' when invoked on OClosures.
+It should return either nil or a two-element list of the form (interactive FORM)
+where FORM is like the first arg of the `interactive' special form.
+Add your methods to this generic function, but always call `interactive-form'
+instead."
+ ;; (interactive-form function)
+ nil)
+
(defun command-execute (cmd &optional record-flag keys special)
;; BEWARE: Called directly from the C code.
"Execute CMD as an editor command.
@@ -2338,12 +2678,17 @@ don't clear it."
(setq current-prefix-arg prefix-arg)
(setq prefix-arg nil)
(when current-prefix-arg
- (prefix-command-update))))))
+ (prefix-command-update)))))
+ query)
(if (and (symbolp cmd)
(get cmd 'disabled)
- disabled-command-function)
- ;; FIXME: Weird calling convention!
- (run-hooks 'disabled-command-function)
+ (or (and (setq query (and (consp (get cmd 'disabled))
+ (eq (car (get cmd 'disabled)) 'query)))
+ (not (command-execute--query cmd)))
+ (and (not query) disabled-command-function)))
+ (when (not query)
+ ;; FIXME: Weird calling convention!
+ (run-hooks 'disabled-command-function))
(let ((final cmd))
(while
(progn
@@ -2367,6 +2712,21 @@ don't clear it."
(put cmd 'command-execute-obsolete-warned t)
(message "%s" (macroexp--obsolete-warning
cmd (get cmd 'byte-obsolete-info) "command"))))))))))
+
+(defun command-execute--query (command)
+ "Query the user whether to run COMMAND."
+ (let ((query (get command 'disabled)))
+ (funcall (if (nth 1 query) #'yes-or-no-p #'y-or-n-p)
+ (nth 2 query))))
+
+;;;###autoload
+(defun command-query (command query &optional verbose)
+ "Make executing COMMAND issue QUERY to the user.
+This will, by default, use `y-or-n-p', but if VERBOSE,
+`yes-or-no-p' is used instead."
+ (put command 'disabled
+ (list 'query (not (not verbose)) query)))
+
(defvar minibuffer-history nil
"Default minibuffer history list.
@@ -2777,6 +3137,7 @@ Intended to be added to `minibuffer-setup-hook'."
#'minibuffer-history-isearch-wrap)
(setq-local isearch-push-state-function
#'minibuffer-history-isearch-push-state)
+ (setq-local isearch-lazy-count nil)
(add-hook 'isearch-mode-end-hook 'minibuffer-history-isearch-end nil t))
(defun minibuffer-history-isearch-end ()
@@ -2912,12 +3273,12 @@ the minibuffer contents."
(defconst undo-equiv-table (make-hash-table :test 'eq :weakness t)
"Table mapping redo records to the corresponding undo one.
-A redo record for an undo in region maps to 'undo-in-region.
+A redo record for an undo in region maps to `undo-in-region'.
A redo record for ordinary undo maps to the following (earlier) undo.
A redo record that undoes to the beginning of the undo list maps to t.
In the rare case where there are (erroneously) consecutive nil's in
`buffer-undo-list', `undo' maps the previous valid undo record to
-'empty, if the previous record is a redo record, `undo' doesn't change
+`empty', if the previous record is a redo record, `undo' doesn't change
its mapping.
To be clear, a redo record is just an undo record, the only difference
@@ -3105,7 +3466,7 @@ Interactively, ARG is the prefix numeric argument and defaults to 1."
(let ((undo-in-progress t))
(while (and (consp ul) (eq (car ul) nil))
(setq ul (cdr ul)))
- (primitive-undo arg ul)))
+ (primitive-undo (or arg 1) ul)))
(new-pul (undo--last-change-was-undo-p new-ul)))
(message "Redo%s" (if undo-in-region " in region" ""))
(setq this-command 'undo)
@@ -3827,7 +4188,10 @@ to the end of the list of defaults just after the default value."
(defvar minibuffer-local-shell-command-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map minibuffer-local-map)
- (define-key map "\t" 'completion-at-point)
+ (define-key map "\t" #'completion-at-point)
+ (define-key map [M-up] #'minibuffer-previous-completion)
+ (define-key map [M-down] #'minibuffer-next-completion)
+ (define-key map [?\M-\r] #'minibuffer-choose-completion)
map)
"Keymap used for completing shell commands in minibuffer.")
@@ -4082,6 +4446,10 @@ interactively when the prefix argument is given), insert the
output in current buffer after point leaving mark after it. This
cannot be done asynchronously.
+If OUTPUT-BUFFER is a buffer or buffer name different from the
+current buffer, instead of outputting at point in that buffer,
+the output will be appended at the end of that buffer.
+
The user option `shell-command-dont-erase-buffer', which see, controls
whether the output buffer is erased and where to put point after
the shell command.
@@ -4193,25 +4561,21 @@ impose the use of a shell (with its need to quote arguments)."
(cond
((eq async-shell-command-buffer 'confirm-kill-process)
;; If will kill a process, query first.
- (if (yes-or-no-p "A command is running in the default buffer. Kill it? ")
- (kill-process proc)
- (user-error "Shell command in progress")))
+ (shell-command--same-buffer-confirm "Kill it")
+ (kill-process proc))
((eq async-shell-command-buffer 'confirm-new-buffer)
;; If will create a new buffer, query first.
- (if (yes-or-no-p "A command is running in the default buffer. Use a new buffer? ")
- (setq buffer (generate-new-buffer bname))
- (user-error "Shell command in progress")))
+ (shell-command--same-buffer-confirm "Use a new buffer")
+ (setq buffer (generate-new-buffer bname)))
((eq async-shell-command-buffer 'new-buffer)
;; It will create a new buffer.
(setq buffer (generate-new-buffer bname)))
((eq async-shell-command-buffer 'confirm-rename-buffer)
;; If will rename the buffer, query first.
- (if (yes-or-no-p "A command is running in the default buffer. Rename it? ")
- (progn
- (with-current-buffer buffer
- (rename-uniquely))
- (setq buffer (get-buffer-create bname)))
- (user-error "Shell command in progress")))
+ (shell-command--same-buffer-confirm "Rename it")
+ (with-current-buffer buffer
+ (rename-uniquely))
+ (setq buffer (get-buffer-create bname)))
((eq async-shell-command-buffer 'rename-buffer)
;; It will rename the buffer.
(with-current-buffer buffer
@@ -4259,6 +4623,24 @@ impose the use of a shell (with its need to quote arguments)."
(shell-command-on-region (point) (point) command
output-buffer nil error-buffer)))))))
+(defun shell-command--same-buffer-confirm (action)
+ (let ((help-form
+ (format
+ "There's a command already running in the default buffer,
+so we can't start a new one in the same one.
+
+Answering \"yes\" will %s.
+
+Answering \"no\" will exit without doing anything, and won't
+start the new command.
+
+Also see the `async-shell-command-buffer' variable."
+ (downcase action))))
+ (unless (yes-or-no-p
+ (format "A command is running in the default buffer. %s? "
+ action))
+ (user-error "Shell command in progress"))))
+
(defun max-mini-window-lines (&optional frame)
"Compute maximum number of lines for echo area in FRAME.
As defined by `max-mini-window-height'. FRAME defaults to the
@@ -4693,6 +5075,8 @@ File name handlers might not support pty association, if PROGRAM is nil."
(forward-line -1)
(beginning-of-line))))
+(declare-function thread-name "thread.c")
+
(defun list-processes--refresh ()
"Recompute the list of processes for the Process List buffer.
Also, delete any process that is exited or signaled."
@@ -5070,10 +5454,11 @@ interact nicely with `interprogram-cut-function' and
interaction; you may want to use them instead of manipulating the kill
ring directly.")
-(defcustom kill-ring-max 60
+(defcustom kill-ring-max 120
"Maximum length of kill ring before oldest elements are thrown away."
:type 'integer
- :group 'killing)
+ :group 'killing
+ :version "29.1")
(defvar kill-ring-yank-pointer nil
"The tail of the kill ring whose car is the last thing yanked.")
@@ -5351,7 +5736,7 @@ This command's old key binding has been given to `kill-ring-save'."
(let ((str (if region
(funcall region-extract-function nil)
(filter-buffer-substring beg end))))
- (if (eq last-command 'kill-region)
+ (if (eq last-command 'kill-region)
(kill-append str (< end beg))
(kill-new str)))
(setq deactivate-mark t)
@@ -5634,6 +6019,15 @@ See also `yank-handled-properties'."
:group 'killing
:version "24.3")
+(defvar yank-transform-functions nil
+ "Hook run on strings to be yanked.
+Each function in this list will be called (in order) with the
+string to be yanked as the sole argument, and should return the (possibly)
+transformed string.
+
+The functions will be called with the destination buffer as the current
+buffer, and with point at the place where the string is to be inserted.")
+
(defvar yank-window-start nil)
(defvar yank-undo-function nil
"If non-nil, function used by `yank-pop' to delete last stretch of yanked text.
@@ -5705,6 +6099,11 @@ property, as described below.
Properties listed in `yank-handled-properties' are processed,
then those listed in `yank-excluded-properties' are discarded.
+STRING will be run through `yank-transform-functions'.
+`yank-in-context' is a command that uses this mechanism to
+provide a `yank' alternative that conveniently preserves
+string/comment syntax.
+
If STRING has a non-nil `yank-handler' property anywhere, the
normal insert behavior is altered, and instead, for each contiguous
segment of STRING that has a given value of the `yank-handler'
@@ -5755,6 +6154,88 @@ With ARG, rotate that many kills forward (or backward, if negative)."
(interactive "p")
(current-kill arg))
+(defun yank-in-context (&optional arg)
+ "Insert the last stretch of killed text while preserving syntax.
+In particular, if point is inside a string, any quote characters
+in the killed text will be quoted, so that the string remains a
+valid string.
+
+If point is inside a comment, ensure that the inserted text is
+also marked as a comment.
+
+This command otherwise behaves as `yank'. See that command for
+explanation of ARG.
+
+This function uses the `escaped-string-quote' buffer-local
+variable to determine how strings should be escaped."
+ (interactive "*P")
+ (let ((yank-transform-functions (cons #'yank-in-context--transform
+ yank-transform-functions)))
+ (yank arg)))
+
+(defun yank-in-context--transform (string)
+ (let ((ppss (syntax-ppss)))
+ (cond
+ ;; We're in a string.
+ ((ppss-string-terminator ppss)
+ (string-replace
+ (string (ppss-string-terminator ppss))
+ (concat (if (functionp escaped-string-quote)
+ (funcall escaped-string-quote
+ (ppss-string-terminator ppss))
+ escaped-string-quote)
+ (string (ppss-string-terminator ppss)))
+ string))
+ ;; We're in a comment.
+ ((or (ppss-comment-depth ppss)
+ (and (bolp)
+ (not (eobp))
+ ;; If we're in the middle of a bunch of commented text,
+ ;; we probably want to be commented. This is quite DWIM.
+ (or (bobp)
+ (save-excursion
+ (forward-line -1)
+ (forward-char 1)
+ (ppss-comment-depth (syntax-ppss))))
+ (ppss-comment-depth
+ (setq ppss (save-excursion
+ (forward-char 1)
+ (syntax-ppss))))))
+ (cond
+ ((and (eq (ppss-comment-depth ppss) t)
+ (> (length comment-end) 0)
+ (string-search comment-end string))
+ (user-error "Can't insert a string containing a comment terminator in a comment"))
+ ;; If this is a comment syntax that has an explicit end, then
+ ;; we can just insert as is.
+ ((> (length comment-end) 0) string)
+ ;; Line-based comment formats.
+ ((or (string-search "\n" string)
+ (bolp))
+ (let ((mode major-mode)
+ (bolp (bolp))
+ (eolp (eolp))
+ (comment-style 'plain))
+ (with-temp-buffer
+ (funcall mode)
+ (insert string)
+ (when (string-match-p "\n\\'" string)
+ (cond
+ ((not eolp) (delete-char -1))
+ (bolp (insert "\n"))))
+ (comment-normalize-vars)
+ (comment-region-default-1
+ (if bolp
+ (point-min)
+ (save-excursion
+ (goto-char (point-min))
+ (forward-line 1)
+ (point)))
+ (point-max))
+ (buffer-string))))
+ (t string)))
+ (t string))))
+
(defvar read-from-kill-ring-history)
(defun read-from-kill-ring (prompt)
"Read a `kill-ring' entry using completion and minibuffer history.
@@ -5893,7 +6374,7 @@ Delete ARG chars, and kill (save in kill ring) if KILLP is non-nil.
If Transient Mark mode is enabled, the mark is active, and ARG is 1,
delete the text in the region and deactivate the mark instead.
-To disable this, set option ‘delete-active-region’ to nil.
+To disable this, set option `delete-active-region' to nil.
Interactively, ARG is the prefix arg (default 1)
and KILLP is t if a prefix arg was specified."
@@ -5923,21 +6404,34 @@ and KILLP is t if a prefix arg was specified."
;; Avoid warning about delete-backward-char
(with-no-warnings (delete-backward-char n killp))))
-(defun zap-to-char (arg char)
+(defun char-uppercase-p (char)
+ "Return non-nil if CHAR is an upper-case character.
+If the Unicode tables are not yet available, e.g. during bootstrap,
+then gives correct answers only for ASCII characters."
+ (cond ((unicode-property-table-internal 'lowercase)
+ (characterp (get-char-code-property char 'lowercase)))
+ ((and (>= char ?A) (<= char ?Z)))))
+
+(defun zap-to-char (arg char &optional interactive)
"Kill up to and including ARGth occurrence of CHAR.
+When run interactively, the argument INTERACTIVE is non-nil.
Case is ignored if `case-fold-search' is non-nil in the current buffer.
Goes backward if ARG is negative; error if CHAR not found.
-See also `zap-up-to-char'."
+See also `zap-up-to-char'.
+If called interactively, do a case sensitive search if CHAR
+is an upper-case character."
(interactive (list (prefix-numeric-value current-prefix-arg)
(read-char-from-minibuffer "Zap to char: "
- nil 'read-char-history)))
+ nil 'read-char-history)
+ t))
;; Avoid "obsolete" warnings for translation-table-for-input.
(with-no-warnings
(if (char-table-p translation-table-for-input)
(setq char (or (aref translation-table-for-input char) char))))
- (kill-region (point) (progn
- (search-forward (char-to-string char) nil nil arg)
- (point))))
+ (let ((case-fold-search (if (and interactive (char-uppercase-p char))
+ nil
+ case-fold-search)))
+ (kill-region (point) (search-forward (char-to-string char) nil nil arg))))
;; kill-line and its subroutines.
@@ -6412,27 +6906,38 @@ An example is a rectangular region handled as a list of
separate contiguous regions for each line."
(cdr (region-bounds)))
+(defun redisplay--unhighlight-overlay-function (rol)
+ "If ROL is an overlay, call `delete-overlay'."
+ (when (overlayp rol) (delete-overlay rol)))
+
(defvar redisplay-unhighlight-region-function
- (lambda (rol) (when (overlayp rol) (delete-overlay rol))))
+ #'redisplay--unhighlight-overlay-function
+ "Function to remove the region-highlight overlay.")
+
+(defun redisplay--highlight-overlay-function (start end window rol &optional face)
+ "Update the overlay ROL in WINDOW with FACE in range START-END."
+ (unless face (setq face 'region))
+ (if (not (overlayp rol))
+ (let ((nrol (make-overlay start end)))
+ (funcall redisplay-unhighlight-region-function rol)
+ (overlay-put nrol 'window window)
+ (overlay-put nrol 'face face)
+ ;; Normal priority so that a large region doesn't hide all the
+ ;; overlays within it, but high secondary priority so that if it
+ ;; ends/starts in the middle of a small overlay, that small overlay
+ ;; won't hide the region's boundaries.
+ (overlay-put nrol 'priority '(nil . 100))
+ nrol)
+ (unless (eq (overlay-get rol 'face) face)
+ (overlay-put rol 'face face))
+ (unless (and (eq (overlay-buffer rol) (current-buffer))
+ (eq (overlay-start rol) start)
+ (eq (overlay-end rol) end))
+ (move-overlay rol start end (current-buffer)))
+ rol))
(defvar redisplay-highlight-region-function
- (lambda (start end window rol)
- (if (not (overlayp rol))
- (let ((nrol (make-overlay start end)))
- (funcall redisplay-unhighlight-region-function rol)
- (overlay-put nrol 'window window)
- (overlay-put nrol 'face 'region)
- ;; Normal priority so that a large region doesn't hide all the
- ;; overlays within it, but high secondary priority so that if it
- ;; ends/starts in the middle of a small overlay, that small overlay
- ;; won't hide the region's boundaries.
- (overlay-put nrol 'priority '(nil . 100))
- nrol)
- (unless (and (eq (overlay-buffer rol) (current-buffer))
- (eq (overlay-start rol) start)
- (eq (overlay-end rol) end))
- (move-overlay rol start end (current-buffer)))
- rol))
+ #'redisplay--highlight-overlay-function
"Function to move the region-highlight overlay.
This function is called with four parameters, START, END, WINDOW
and OVERLAY. If OVERLAY is nil, a new overlay is created. In
@@ -6457,8 +6962,33 @@ The overlay is returned by the function.")
(funcall redisplay-highlight-region-function
start end window rol)))
(unless (equal new rol)
- (set-window-parameter window 'internal-region-overlay
- new))))))
+ (set-window-parameter window 'internal-region-overlay new))))))
+
+(defcustom cursor-face-highlight-nonselected-window nil
+ "Non-nil means highlight text with `cursor-face' even in nonselected windows.
+This variable is similar to `highlight-nonselected-windows'."
+ :local t
+ :type 'boolean
+ :version "29.1")
+
+(defun redisplay--update-cursor-face-highlight (window)
+ "Highlights the overlay used to highlight text with cursor-face."
+ (let ((rol (window-parameter window 'internal-cursor-face-overlay)))
+ (if-let* (((or cursor-face-highlight-nonselected-window
+ (eq window (selected-window))
+ (and (window-minibuffer-p)
+ (eq window (minibuffer-selected-window)))))
+ (pt (window-point window))
+ (cursor-face (get-text-property pt 'cursor-face)))
+ (let* ((start (previous-single-property-change
+ (1+ pt) 'cursor-face nil (point-min)))
+ (end (next-single-property-change
+ pt 'cursor-face nil (point-max)))
+ (new (redisplay--highlight-overlay-function
+ start end window rol cursor-face)))
+ (unless (equal new rol)
+ (set-window-parameter window 'internal-cursor-face-overlay new)))
+ (redisplay--unhighlight-overlay-function rol))))
(defvar pre-redisplay-functions (list #'redisplay--update-region-highlight)
"Hook run just before redisplay.
@@ -6466,6 +6996,15 @@ It is called in each window that is to be redisplayed. It takes one argument,
which is the window that will be redisplayed. When run, the `current-buffer'
is set to the buffer displayed in that window.")
+(define-minor-mode cursor-face-highlight-mode
+ "When enabled, respect the cursor-face property."
+ :global nil
+ (if cursor-face-highlight-mode
+ (add-hook 'pre-redisplay-functions
+ #'redisplay--update-cursor-face-highlight nil t)
+ (remove-hook 'pre-redisplay-functions
+ #'redisplay--update-cursor-face-highlight t)))
+
(defun redisplay--pre-redisplay-functions (windows)
(with-demoted-errors "redisplay--pre-redisplay-functions: %S"
(if (null windows)
@@ -6475,9 +7014,11 @@ is set to the buffer displayed in that window.")
(with-current-buffer (window-buffer win)
(run-hook-with-args 'pre-redisplay-functions win))))))
-(add-function :before pre-redisplay-function
- #'redisplay--pre-redisplay-functions)
-
+(when (eq pre-redisplay-function #'ignore)
+ ;; Override the default set in the C code.
+ ;; This is not done using `add-function' so as to loosen the bootstrap
+ ;; dependencies.
+ (setq pre-redisplay-function #'redisplay--pre-redisplay-functions))
(defvar-local mark-ring nil
"The list of former marks of the current buffer, most recent first.")
@@ -7607,31 +8148,28 @@ For motion by visual lines, see `beginning-of-visual-line'."
(put 'set-goal-column 'disabled t)
(defun set-goal-column (arg)
- "Set the current horizontal position as a goal for \\[next-line] and \\[previous-line].
+ "Set the current horizontal position as a goal column.
+This goal column will affect the \\[next-line] and \\[previous-line] commands,
+as well as the \\[scroll-up-command] and \\[scroll-down-command] commands.
+
Those commands will move to this position in the line moved to
rather than trying to keep the same horizontal position.
-With a non-nil argument ARG, clears out the goal column
-so that \\[next-line] and \\[previous-line] resume vertical motion.
-The goal column is stored in the variable `goal-column'.
-This is a buffer-local setting."
+
+With a non-nil argument ARG, clears out the goal column so that
+these commands resume normal motion.
+
+The goal column is stored in the variable `goal-column'. This is
+a buffer-local setting."
(interactive "P")
(if arg
(progn
(setq goal-column nil)
(message "No goal column"))
(setq goal-column (current-column))
- ;; The older method below can be erroneous if `set-goal-column' is bound
- ;; to a sequence containing %
- ;;(message (substitute-command-keys
- ;;"Goal column %d (use \\[set-goal-column] with an arg to unset it)")
- ;;goal-column)
- (message "%s"
- (concat
- (format "Goal column %d " goal-column)
- (substitute-command-keys
- "(use \\[set-goal-column] with an arg to unset it)")))
-
- )
+ (message "Goal column %d %s"
+ goal-column
+ (substitute-command-keys
+ "(use \\[set-goal-column] with an arg to unset it)")))
nil)
;;; Editing based on visual lines, as opposed to logical lines.
@@ -8266,7 +8804,8 @@ Just \\[universal-argument] as argument means to use the current column."
;; We used to use current-column silently, but C-x f is too easily
;; typed as a typo for C-x C-f, so we turned it into an error and
;; now an interactive prompt.
- (read-number "Set fill-column to: " (current-column)))))
+ (read-number (format "Change fill-column from %s to: " fill-column)
+ (current-column)))))
(if (consp arg)
(setq arg (current-column)))
(if (not (integerp arg))
@@ -8585,40 +9124,43 @@ The function should return non-nil if the two tokens do not match.")
(current-buffer))
(sit-for blink-matching-delay))
(delete-overlay blink-matching--overlay)))))
- (t
- (let ((open-paren-line-string
- (save-excursion
- (goto-char blinkpos)
- ;; Show what precedes the open in its line, if anything.
- (cond
- ((save-excursion (skip-chars-backward " \t") (not (bolp)))
- (buffer-substring (line-beginning-position)
- (1+ blinkpos)))
- ;; Show what follows the open in its line, if anything.
- ((save-excursion
- (forward-char 1)
- (skip-chars-forward " \t")
- (not (eolp)))
- (buffer-substring blinkpos
- (line-end-position)))
- ;; Otherwise show the previous nonblank line,
- ;; if there is one.
- ((save-excursion (skip-chars-backward "\n \t") (not (bobp)))
- (concat
- (buffer-substring (progn
- (skip-chars-backward "\n \t")
- (line-beginning-position))
- (progn (end-of-line)
- (skip-chars-backward " \t")
- (point)))
- ;; Replace the newline and other whitespace with `...'.
- "..."
- (buffer-substring blinkpos (1+ blinkpos))))
- ;; There is nothing to show except the char itself.
- (t (buffer-substring blinkpos (1+ blinkpos)))))))
- (minibuffer-message
- "Matches %s"
- (substring-no-properties open-paren-line-string))))))))
+ ((not show-paren-context-when-offscreen)
+ (minibuffer-message
+ "Matches %s"
+ (substring-no-properties
+ (blink-paren-open-paren-line-string blinkpos))))))))
+
+(defun blink-paren-open-paren-line-string (pos)
+ "Return the line string that contains the openparen at POS."
+ (save-excursion
+ (goto-char pos)
+ ;; Show what precedes the open in its line, if anything.
+ (cond
+ ((save-excursion (skip-chars-backward " \t") (not (bolp)))
+ (buffer-substring (line-beginning-position)
+ (1+ pos)))
+ ;; Show what follows the open in its line, if anything.
+ ((save-excursion
+ (forward-char 1)
+ (skip-chars-forward " \t")
+ (not (eolp)))
+ (buffer-substring pos
+ (line-end-position)))
+ ;; Otherwise show the previous nonblank line,
+ ;; if there is one.
+ ((save-excursion (skip-chars-backward "\n \t") (not (bobp)))
+ (concat
+ (buffer-substring (progn
+ (skip-chars-backward "\n \t")
+ (line-beginning-position))
+ (progn (end-of-line)
+ (skip-chars-backward " \t")
+ (point)))
+ ;; Replace the newline and other whitespace with `...'.
+ "..."
+ (buffer-substring pos (1+ pos))))
+ ;; There is nothing to show except the char itself.
+ (t (buffer-substring pos (1+ pos))))))
(defvar blink-paren-function 'blink-matching-open
"Function called, if non-nil, whenever a close parenthesis is inserted.
@@ -8911,7 +9453,7 @@ With a prefix argument, set VARIABLE to VALUE buffer-locally.
When called interactively, the user is prompted for VARIABLE and
then VALUE. The current value of VARIABLE will be put in the
-minibuffer history so that it can be accessed with `M-n', which
+minibuffer history so that it can be accessed with \\`M-n', which
makes it easier to edit it."
(interactive
(let* ((default-var (variable-at-point))
@@ -8979,6 +9521,7 @@ makes it easier to edit it."
(define-key map [down-mouse-2] nil)
(define-key map "\C-m" 'choose-completion)
(define-key map "\e\e\e" 'delete-completion-window)
+ (define-key map [remap keyboard-quit] #'delete-completion-window)
(define-key map [left] 'previous-completion)
(define-key map [right] 'next-completion)
(define-key map [?\t] 'next-completion)
@@ -9009,6 +9552,16 @@ Its value is a list of the form (START END) where START is the place
where the completion should be inserted and END (if non-nil) is the end
of the text to replace. If END is nil, point is used instead.")
+(defvar completion-base-affixes nil
+ "Base context of the text corresponding to the shown completions.
+This variable is used in the *Completions* buffer.
+Its value is a list of the form (PREFIX SUFFIX) where PREFIX is the text
+before the place where completion should be inserted, and SUFFIX is the text
+after the completion.")
+
+(defvar completion-use-base-affixes nil
+ "Non-nil means to restore original prefix and suffix in the minibuffer.")
+
(defvar completion-list-insert-choice-function #'completion--replace
"Function to use to insert the text chosen in *Completions*.
Called with three arguments (BEG END TEXT), it should replace the text
@@ -9026,73 +9579,160 @@ Go to the window from which completion was requested."
(if (get-buffer-window buf)
(select-window (get-buffer-window buf))))))
+(defcustom completion-auto-wrap t
+ "Non-nil means to wrap around when selecting completion options.
+This affects the commands `next-completion' and `previous-completion'.
+When `completion-auto-select' is t, it wraps through the minibuffer."
+ :type 'boolean
+ :version "29.1"
+ :group 'completion)
+
+(defcustom completion-auto-select nil
+ "Non-nil means to automatically select the *Completions* buffer.
+When the value is t, pressing TAB will switch to the completion list
+buffer when Emacs pops up a window showing that buffer.
+If the value is `second-tab', then the first TAB will pop up the
+window showing the completions list buffer, and the next TAB will
+switch to that window.
+See `completion-auto-help' for controlling when the window showing
+the completions is popped up and down."
+ :type '(choice (const :tag "Don't auto-select completions window" nil)
+ (const :tag "Select completions window on first TAB" t)
+ (const :tag "Select completions window on second TAB"
+ second-tab))
+ :version "29.1"
+ :group 'completion)
+
+(defun first-completion ()
+ "Move to the first item in the completion list."
+ (interactive)
+ (goto-char (point-min))
+ (unless (get-text-property (point) 'mouse-face)
+ (when-let ((pos (next-single-property-change (point) 'mouse-face)))
+ (goto-char pos))))
+
+(defun last-completion ()
+ "Move to the last item in the completion list."
+ (interactive)
+ (goto-char (previous-single-property-change
+ (point-max) 'mouse-face nil (point-min)))
+ ;; Move to the start of last one.
+ (unless (get-text-property (point) 'mouse-face)
+ (when-let ((pos (previous-single-property-change (point) 'mouse-face)))
+ (goto-char pos))))
+
(defun previous-completion (n)
- "Move to the previous item in the completion list."
+ "Move to the previous item in the completion list.
+With prefix argument N, move back N items (negative N means move
+forward).
+
+Also see the `completion-auto-wrap' variable."
(interactive "p")
(next-completion (- n)))
(defun next-completion (n)
"Move to the next item in the completion list.
-With prefix argument N, move N items (negative N means move backward)."
+With prefix argument N, move N items (negative N means move
+backward).
+
+Also see the `completion-auto-wrap' variable."
(interactive "p")
- (let ((beg (point-min)) (end (point-max)))
- (while (and (> n 0) (not (eobp)))
- ;; If in a completion, move to the end of it.
- (when (get-text-property (point) 'mouse-face)
- (goto-char (next-single-property-change (point) 'mouse-face nil end)))
- ;; Move to start of next one.
- (unless (get-text-property (point) 'mouse-face)
- (goto-char (next-single-property-change (point) 'mouse-face nil end)))
- (setq n (1- n)))
- (while (and (< n 0) (not (bobp)))
- (let ((prop (get-text-property (1- (point)) 'mouse-face)))
- ;; If in a completion, move to the start of it.
- (when (and prop (eq prop (get-text-property (point) 'mouse-face)))
- (goto-char (previous-single-property-change
- (point) 'mouse-face nil beg)))
- ;; Move to end of the previous completion.
- (unless (or (bobp) (get-text-property (1- (point)) 'mouse-face))
- (goto-char (previous-single-property-change
- (point) 'mouse-face nil beg)))
- ;; Move to the start of that one.
- (goto-char (previous-single-property-change
- (point) 'mouse-face nil beg))
- (setq n (1+ n))))))
-
-(defun choose-completion (&optional event)
+ (let ((tabcommand (member (this-command-keys) '("\t" [backtab])))
+ pos)
+ (catch 'bound
+ (while (> n 0)
+ (setq pos (point))
+ ;; If in a completion, move to the end of it.
+ (when (get-text-property pos 'mouse-face)
+ (setq pos (next-single-property-change pos 'mouse-face)))
+ (when pos (setq pos (next-single-property-change pos 'mouse-face)))
+ (if pos
+ ;; Move to the start of next one.
+ (goto-char pos)
+ ;; If at the last completion option, wrap or skip
+ ;; to the minibuffer, if requested.
+ (when completion-auto-wrap
+ (if (and (eq completion-auto-select t) tabcommand
+ (minibufferp completion-reference-buffer))
+ (throw 'bound nil)
+ (first-completion))))
+ (setq n (1- n)))
+
+ (while (< n 0)
+ (setq pos (point))
+ ;; If in a completion, move to the start of it.
+ (when (and (get-text-property pos 'mouse-face)
+ (not (bobp))
+ (get-text-property (1- pos) 'mouse-face))
+ (setq pos (previous-single-property-change pos 'mouse-face)))
+ (when pos (setq pos (previous-single-property-change pos 'mouse-face)))
+ (if pos
+ (progn
+ (goto-char pos)
+ ;; Move to the start of that one.
+ (unless (get-text-property (point) 'mouse-face)
+ (goto-char (previous-single-property-change
+ (point) 'mouse-face nil (point-min)))))
+ ;; If at the first completion option, wrap or skip
+ ;; to the minibuffer, if requested.
+ (when completion-auto-wrap
+ (if (and (eq completion-auto-select t) tabcommand
+ (minibufferp completion-reference-buffer))
+ (progn
+ (throw 'bound nil))
+ (last-completion))))
+ (setq n (1+ n))))
+
+ (when (/= 0 n)
+ (switch-to-minibuffer))))
+
+(defun choose-completion (&optional event no-exit no-quit)
"Choose the completion at point.
-If EVENT, use EVENT's position to determine the starting position."
- (interactive (list last-nonmenu-event))
+If EVENT, use EVENT's position to determine the starting position.
+With prefix argument NO-EXIT, insert the completion at point to the
+minibuffer, but don't exit the minibuffer. When the prefix argument
+is not provided, then whether to exit the minibuffer depends on the value
+of `completion-no-auto-exit'.
+If NO-QUIT is non-nil, insert the completion at point to the
+minibuffer, but don't quit the completions window."
+ (interactive (list last-nonmenu-event current-prefix-arg))
;; In case this is run via the mouse, give temporary modes such as
;; isearch a chance to turn off.
(run-hooks 'mouse-leave-buffer-hook)
(with-current-buffer (window-buffer (posn-window (event-start event)))
(let ((buffer completion-reference-buffer)
(base-position completion-base-position)
+ (base-affixes completion-base-affixes)
(insert-function completion-list-insert-choice-function)
+ (completion-no-auto-exit (if no-exit t completion-no-auto-exit))
(choice
(save-excursion
(goto-char (posn-point (event-start event)))
(let (beg)
(cond
- ((and (not (eobp)) (get-text-property (point) 'mouse-face))
+ ((and (not (eobp))
+ (get-text-property (point) 'completion--string))
(setq beg (1+ (point))))
((and (not (bobp))
- (get-text-property (1- (point)) 'mouse-face))
+ (get-text-property (1- (point)) 'completion--string))
(setq beg (point)))
(t (error "No completion here")))
- (setq beg (previous-single-property-change beg 'mouse-face))
+ (setq beg (or (previous-single-property-change
+ beg 'completion--string)
+ beg))
(substring-no-properties
(get-text-property beg 'completion--string))))))
(unless (buffer-live-p buffer)
(error "Destination buffer is dead"))
- (quit-window nil (posn-window (event-start event)))
+ (unless no-quit
+ (quit-window nil (posn-window (event-start event))))
(with-current-buffer buffer
(choose-completion-string
choice buffer
- (or base-position
+ (or (and completion-use-base-affixes base-affixes)
+ base-position
;; If all else fails, just guess.
(list (choose-completion-guess-base-position choice)))
insert-function)))))
@@ -9241,19 +9881,24 @@ Called from `temp-buffer-show-hook'."
;; - With fancy completion styles, the code below will not always
;; find the right base directory.
(if minibuffer-completing-file-name
- (file-name-as-directory
+ (file-name-directory
(expand-file-name
(buffer-substring (minibuffer-prompt-end) (point)))))))
(with-current-buffer standard-output
(let ((base-position completion-base-position)
+ (base-affixes completion-base-affixes)
(insert-fun completion-list-insert-choice-function))
(completion-list-mode)
(setq-local completion-base-position base-position)
+ (setq-local completion-base-affixes base-affixes)
(setq-local completion-list-insert-choice-function insert-fun))
(setq-local completion-reference-buffer mainbuf)
(if base-dir (setq default-directory base-dir))
(when completion-tab-width
(setq tab-width completion-tab-width))
+ ;; Maybe enable cursor completions-highlight.
+ (when completions-highlight-face
+ (cursor-face-highlight-mode 1))
;; Maybe insert help string.
(when completion-show-help
(goto-char (point-min))
@@ -9268,16 +9913,18 @@ select the completion near point.\n\n"))))))
(defun switch-to-completions ()
"Select the completion list window."
(interactive)
- (let ((window (or (get-buffer-window "*Completions*" 0)
- ;; Make sure we have a completions window.
- (progn (minibuffer-completion-help)
- (get-buffer-window "*Completions*" 0)))))
- (when window
- (select-window window)
- ;; In the new buffer, go to the first completion.
- ;; FIXME: Perhaps this should be done in `minibuffer-completion-help'.
- (when (bobp)
- (next-completion 1)))))
+ (when-let ((window (or (get-buffer-window "*Completions*" 0)
+ ;; Make sure we have a completions window.
+ (progn (minibuffer-completion-help)
+ (get-buffer-window "*Completions*" 0)))))
+ (select-window window)
+ (when (bobp)
+ (cond
+ ((and (memq this-command '(completion-at-point minibuffer-complete))
+ (equal (this-command-keys) [backtab]))
+ (goto-char (point-max))
+ (last-completion))
+ (t (first-completion))))))
(defun read-expression-switch-to-completions ()
"Select the completion list window while reading an expression."
@@ -9393,9 +10040,6 @@ PREFIX is the string that represents this modifier in an event type symbol."
(defvar clone-buffer-hook nil
"Normal hook to run in the new buffer at the end of `clone-buffer'.")
-(defvar clone-indirect-buffer-hook nil
- "Normal hook to run in the new buffer at the end of `clone-indirect-buffer'.")
-
(defun clone-process (process &optional newname)
"Create a twin copy of PROCESS.
If NEWNAME is nil, it defaults to PROCESS' name;
@@ -9548,8 +10192,6 @@ Returns the newly created indirect buffer."
(setq newname (substring newname 0 (match-beginning 0))))
(let* ((name (generate-new-buffer-name newname))
(buffer (make-indirect-buffer (current-buffer) name t)))
- (with-current-buffer buffer
- (run-hooks 'clone-indirect-buffer-hook))
(when display-flag
(pop-to-buffer buffer nil norecord))
buffer))
@@ -9615,7 +10257,7 @@ call `normal-erase-is-backspace-mode' (which see) instead."
(if (if (eq normal-erase-is-backspace 'maybe)
(and (not noninteractive)
(or (memq system-type '(ms-dos windows-nt))
- (memq window-system '(w32 ns))
+ (memq window-system '(w32 ns pgtk))
(and (eq window-system 'x)
(fboundp 'x-backspace-delete-keys-p)
(x-backspace-delete-keys-p))
@@ -9789,24 +10431,7 @@ If it does not exist, create it and switch it to `messages-buffer-mode'."
;; versions together with bad values. This is therefore not as
;; flexible as it could be. See the thread:
;; https://lists.gnu.org/r/emacs-devel/2007-08/msg00300.html
-(defconst bad-packages-alist
- ;; Not sure exactly which semantic versions have problems.
- ;; Definitely 2.0pre3, probably all 2.0pre's before this.
- '((semantic semantic-version "\\`2\\.0pre[1-3]\\'"
- "The version of `semantic' loaded does not work in Emacs 22.
-It can cause constant high CPU load.
-Upgrade to at least Semantic 2.0pre4 (distributed with CEDET 1.0pre4).")
- ;; CUA-mode does not work with GNU Emacs version 22.1 and newer.
- ;; Except for version 1.2, all of the 1.x and 2.x version of cua-mode
- ;; provided the `CUA-mode' feature. Since this is no longer true,
- ;; we can warn the user if the `CUA-mode' feature is ever provided.
- (CUA-mode t nil
-"CUA-mode is now part of the standard GNU Emacs distribution,
-so you can now enable CUA via the Options menu or by customizing `cua-mode'.
-
-You have loaded an older version of CUA-mode which does not work
-correctly with this version of Emacs. You should remove the old
-version and use the one distributed with Emacs."))
+(defconst bad-packages-alist nil
"Alist of packages known to cause problems in this version of Emacs.
Each element has the form (PACKAGE SYMBOL REGEXP STRING).
PACKAGE is either a regular expression to match file names, or a
@@ -9814,25 +10439,22 @@ symbol (a feature name), like for `with-eval-after-load'.
SYMBOL is either the name of a string variable, or t. Upon
loading PACKAGE, if SYMBOL is t or matches REGEXP, display a
warning using STRING as the message.")
+(make-obsolete-variable 'bad-packages-alist nil "29.1")
(defun bad-package-check (package)
"Run a check using the element from `bad-packages-alist' matching PACKAGE."
+ (declare (obsolete nil "29.1"))
(condition-case nil
(let* ((list (assoc package bad-packages-alist))
(symbol (nth 1 list)))
(and list
(boundp symbol)
(or (eq symbol t)
- (and (stringp (setq symbol (eval symbol)))
+ (and (stringp (setq symbol (symbol-value symbol)))
(string-match-p (nth 2 list) symbol)))
(display-warning package (nth 3 list) :warning)))
(error nil)))
-(dolist (elem bad-packages-alist)
- (let ((pkg (car elem)))
- (with-eval-after-load pkg
- (bad-package-check pkg))))
-
;;; Generic dispatcher commands
@@ -9869,6 +10491,7 @@ does not have any effect until this variable is set.
CUSTOMIZATIONS, if non-nil, should be composed of alternating
`defcustom' keywords and values to add to the declaration of
`COMMAND-alternatives' (typically :group and :version)."
+ (declare (indent defun))
(let* ((command-name (symbol-name command))
(varalt-name (concat command-name "-alternatives"))
(varalt-sym (intern varalt-name))
@@ -9965,15 +10588,89 @@ This is an integer between 1 and 12 (inclusive). January is 1.")
(year nil :documentation "This is a four digit integer.")
(weekday nil :documentation "\
This is a number between 0 and 6, and 0 is Sunday.")
- (dst nil :documentation "\
+ (dst -1 :documentation "\
This is t if daylight saving time is in effect, nil if it is not
-in effect, and -1 if daylight saving information is not
-available.")
+in effect, and -1 if daylight saving information is not available.
+Also see `decoded-time-dst'.")
(zone nil :documentation "\
This is an integer indicating the UTC offset in seconds, i.e.,
the number of seconds east of Greenwich.")
)
+;; Document that decoded-time-dst is problematic on 6-element lists.
+;; It should return -1 indicating unknown DST, but currently returns
+;; nil indicating standard time.
+(put 'decoded-time-dst 'function-documentation
+ "Access slot \"dst\" of `decoded-time' struct CL-X.
+This is t if daylight saving time is in effect, nil if it is not
+in effect, and -1 if daylight saving information is not available.
+As a special case, return an unspecified value when given a list
+too short to have a dst element.
+
+(fn CL-X)")
+
+(defun get-scratch-buffer-create ()
+ "Return the *scratch* buffer, creating a new one if needed."
+ (or (get-buffer "*scratch*")
+ (let ((scratch (get-buffer-create "*scratch*")))
+ ;; Don't touch the buffer contents or mode unless we know that
+ ;; we just created it.
+ (with-current-buffer scratch
+ (when initial-scratch-message
+ (insert (substitute-command-keys initial-scratch-message))
+ (set-buffer-modified-p nil))
+ (funcall initial-major-mode))
+ scratch)))
+
+(defun scratch-buffer ()
+ "Switch to the *scratch* buffer.
+If the buffer doesn't exist, create it first."
+ (interactive)
+ (pop-to-buffer-same-window (get-scratch-buffer-create)))
+
+(defun kill-buffer--possibly-save (buffer)
+ (let ((response
+ (cadr
+ (read-multiple-choice
+ (format "Buffer %s modified; kill anyway?"
+ (buffer-name))
+ '((?y "yes" "kill buffer without saving")
+ (?n "no" "exit without doing anything")
+ (?s "save and then kill" "save the buffer and then kill it"))
+ nil nil (not use-short-answers)))))
+ (if (equal response "no")
+ nil
+ (unless (equal response "yes")
+ (with-current-buffer buffer
+ (save-buffer)))
+ t)))
+
+(defsubst string-empty-p (string)
+ "Check whether STRING is empty."
+ (string= string ""))
+
+(defun read-signal-name ()
+ "Read a signal number or name."
+ (let ((value
+ (completing-read "Signal code or name: "
+ (signal-names)
+ nil
+ (lambda (value)
+ (or (string-match "\\`[0-9]+\\'" value)
+ (member value (signal-names)))))))
+ (if (string-match "\\`[0-9]+\\'" value)
+ (string-to-number value)
+ (intern (concat "sig" (downcase value))))))
+
+(defun lax-plist-get (plist prop)
+ "Extract a value from a property list, comparing with `equal'."
+ (declare (obsolete plist-get "29.1"))
+ (plist-get plist prop #'equal))
+
+(defun lax-plist-put (plist prop val)
+ "Change value in PLIST of PROP to VAL, comparing with `equal'."
+ (declare (obsolete plist-put "29.1"))
+ (plist-put plist prop val #'equal))
(provide 'simple)