compat-29.el (67275B)
1 ;;; compat-29.el --- Functionality added in Emacs 29.1 -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2021-2024 Free Software Foundation, Inc. 4 5 ;; This program is free software; you can redistribute it and/or modify 6 ;; it under the terms of the GNU General Public License as published by 7 ;; the Free Software Foundation, either version 3 of the License, or 8 ;; (at your option) any later version. 9 10 ;; This program is distributed in the hope that it will be useful, 11 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 12 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 ;; GNU General Public License for more details. 14 15 ;; You should have received a copy of the GNU General Public License 16 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 ;;; Commentary: 19 20 ;; Functionality added in Emacs 29.1, needed by older Emacs versions. 21 22 ;;; Code: 23 24 (eval-when-compile (load "compat-macs.el" nil t t)) 25 (compat-require compat-28 "28.1") 26 27 ;; Preloaded in loadup.el 28 (compat-require seq "29.1") ;; <compat-tests:seq> 29 30 (compat-version "29.1") 31 32 ;;;; Defined in startup.el 33 34 (compat-defvar lisp-directory ;; <compat-tests:lisp-directory> 35 (file-truename 36 (file-name-directory 37 (locate-file "simple" load-path (get-load-suffixes)))) 38 "Directory where Emacs's own *.el and *.elc Lisp files are installed.") 39 40 ;;;; Defined in window.c 41 42 (compat-defalias window-configuration-equal-p compare-window-configurations) ;; <compat-tests:window-configuration-equal-p> 43 44 ;;;; Defined in xdisp.c 45 46 (compat-defun get-display-property (position prop &optional object properties) ;; <compat-tests:get-display-property> 47 "Get the value of the `display' property PROP at POSITION. 48 If OBJECT, this should be a buffer or string where the property is 49 fetched from. If omitted, OBJECT defaults to the current buffer. 50 51 If PROPERTIES, look for value of PROP in PROPERTIES instead of 52 the properties at POSITION." 53 (if properties 54 (unless (listp properties) 55 (signal 'wrong-type-argument (list 'listp properties))) 56 (setq properties (get-text-property position 'display object))) 57 (cond 58 ((vectorp properties) 59 (catch 'found 60 (dotimes (i (length properties)) 61 (let ((ent (aref properties i))) 62 (when (eq (car ent) prop) 63 (throw 'found (cadr ent ))))))) 64 ((consp (car properties)) 65 (condition-case nil 66 (cadr (assq prop properties)) 67 ;; Silently handle improper lists: 68 (wrong-type-argument nil))) 69 ((and (consp (cdr properties)) 70 (eq (car properties) prop)) 71 (cadr properties)))) 72 73 ;;;; Defined in fns.c 74 75 (compat-defun ntake (n list) ;; <compat-tests:ntake> 76 "Modify LIST to keep only the first N elements. 77 If N is zero or negative, return nil. 78 If N is greater or equal to the length of LIST, return LIST unmodified. 79 Otherwise, return LIST after truncating it." 80 (and (> n 0) (let ((cons (nthcdr (1- n) list))) 81 (when cons (setcdr cons nil)) 82 list))) 83 84 (compat-defun take (n list) ;; <compat-tests:take> 85 "Return the first N elements of LIST. 86 If N is zero or negative, return nil. 87 If N is greater or equal to the length of LIST, return LIST (or a copy)." 88 (declare (pure t) (side-effect-free t)) 89 (let (copy) 90 (while (and (< 0 n) list) 91 (push (pop list) copy) 92 (setq n (1- n))) 93 (nreverse copy))) 94 95 (compat-defun string-equal-ignore-case (string1 string2) ;; <compat-tests:string-equal-ignore-case> 96 "Like `string-equal', but case-insensitive. 97 Upper-case and lower-case letters are treated as equal. 98 Unibyte strings are converted to multibyte for comparison." 99 (declare (pure t) (side-effect-free t)) 100 (eq t (compare-strings string1 0 nil string2 0 nil t))) 101 102 (compat-defun plist-get (plist prop &optional predicate) ;; <compat-tests:plist-get> 103 "Handle optional argument PREDICATE." 104 :extended t 105 (pcase predicate 106 ((or `nil `eq) (plist-get plist prop)) 107 (`equal (lax-plist-get plist prop)) 108 (_ (catch 'found 109 (while (consp plist) 110 (when (funcall predicate prop (car plist)) 111 (throw 'found (cadr plist))) 112 (setq plist (cddr plist))))))) 113 114 (compat-defun plist-put (plist prop val &optional predicate) ;; <compat-tests:plist-get> 115 "Handle optional argument PREDICATE." 116 :extended t 117 (pcase predicate 118 ((or `nil `eq) (plist-put plist prop val)) 119 (`equal (lax-plist-put plist prop val)) 120 (_ (catch 'found 121 (let ((tail plist)) 122 (while (consp tail) 123 (when (funcall predicate prop (car tail)) 124 (setcar (cdr tail) val) 125 (throw 'found plist)) 126 (setq tail (cddr tail)))) 127 (nconc plist (list prop val)))))) 128 129 (compat-defun plist-member (plist prop &optional predicate) ;; <compat-tests:plist-get> 130 "Handle optional argument PREDICATE." 131 :extended t 132 (pcase predicate 133 ((or `nil `eq) (plist-member plist prop)) 134 (_ (catch 'found 135 (while (consp plist) 136 (when (funcall predicate prop (car plist)) 137 (throw 'found plist)) 138 (setq plist (cddr plist))))))) 139 140 ;;;; Defined in gv.el 141 142 (compat-guard t ;; <compat-tests:plist-get-gv> 143 (gv-define-expander compat--plist-get 144 (lambda (do plist prop &optional predicate) 145 (macroexp-let2 macroexp-copyable-p key prop 146 (gv-letplace (getter setter) plist 147 (macroexp-let2 nil p `(cdr (compat--plist-member ,getter ,key ,predicate)) 148 (funcall do 149 `(car ,p) 150 (lambda (val) 151 `(if ,p 152 (setcar ,p ,val) 153 ,(funcall setter 154 `(cons ,key (cons ,val ,getter))))))))))) 155 (unless (get 'plist-get 'gv-expander) 156 (put 'plist-get 'gv-expander (get 'compat--plist-get 'gv-expander)))) 157 158 ;;;; Defined in editfns.c 159 160 (compat-defun pos-bol (&optional n) ;; <compat-tests:pos-bol> 161 "Return the position of the first character on the current line. 162 With optional argument N, scan forward N - 1 lines first. 163 If the scan reaches the end of the buffer, return that position. 164 165 This function ignores text display directionality; it returns the 166 position of the first character in logical order, i.e. the smallest 167 character position on the logical line. See `vertical-motion' for 168 movement by screen lines. 169 170 This function does not move point. Also see `line-beginning-position'." 171 (declare (side-effect-free t)) 172 (let ((inhibit-field-text-motion t)) 173 (line-beginning-position n))) 174 175 (compat-defun pos-eol (&optional n) ;; <compat-tests:pos-bol> 176 "Return the position of the last character on the current line. 177 With argument N not nil or 1, move forward N - 1 lines first. 178 If scan reaches end of buffer, return that position. 179 180 This function ignores text display directionality; it returns the 181 position of the last character in logical order, i.e. the largest 182 character position on the line. 183 184 This function does not move point. Also see `line-end-position'." 185 (declare (side-effect-free t)) 186 (let ((inhibit-field-text-motion t)) 187 (line-end-position n))) 188 189 ;;;; Defined in subr.el 190 191 (compat-defmacro with-delayed-message (_args &rest body) ;; <compat-tests:with-delayed-message> 192 "Like `progn', but display MESSAGE if BODY takes longer than TIMEOUT seconds. 193 The MESSAGE form will be evaluated immediately, but the resulting 194 string will be displayed only if BODY takes longer than TIMEOUT seconds. 195 196 NOTE: The compatibility function never displays the message, 197 which is not problematic since the only effect of the function is 198 to display a progress message to the user. Backporting this 199 feature is not possible, since the implementation is directly 200 baked into the Elisp interpreter. 201 202 \(fn (timeout message) &rest body)" 203 (declare (indent 1)) 204 (macroexp-progn body)) 205 206 (compat-defun funcall-with-delayed-message (timeout message function) ;; <compat-tests:with-delayed-message> 207 "Like `funcall', but display MESSAGE if FUNCTION takes longer than TIMEOUT. 208 TIMEOUT is a number of seconds, and can be an integer or a 209 floating point number. If FUNCTION takes less time to execute 210 than TIMEOUT seconds, MESSAGE is not displayed. 211 212 NOTE: The compatibility function never displays the message, 213 which is not problematic since the only effect of the function is 214 to display a progress message to the user. Backporting this 215 feature is not possible, since the implementation is directly 216 baked into the Elisp interpreter." 217 (ignore timeout message) 218 (funcall function)) 219 220 (compat-defun string-lines (string &optional omit-nulls keep-newlines) ;; <compat-tests:string-lines> 221 "Handle additional KEEP-NEWLINES argument." 222 :extended "28.1" 223 (if (equal string "") 224 (if omit-nulls 225 nil 226 (list "")) 227 (let ((lines nil) 228 (start 0)) 229 (while (< start (length string)) 230 (let ((newline (string-search "\n" string start))) 231 (if newline 232 (progn 233 (when (or (not omit-nulls) 234 (not (= start newline))) 235 (let ((line (substring string start 236 (if keep-newlines 237 (1+ newline) 238 newline)))) 239 (when (not (and keep-newlines omit-nulls 240 (equal line "\n"))) 241 (push line lines)))) 242 (setq start (1+ newline))) 243 (if (zerop start) 244 (push string lines) 245 (push (substring string start) lines)) 246 (setq start (length string))))) 247 (nreverse lines)))) 248 249 (compat-defun readablep (object) ;; <compat-tests:readablep> 250 "Say whether OBJECT has a readable syntax. 251 This means that OBJECT can be printed out and then read back 252 again by the Lisp reader. This function returns nil if OBJECT is 253 unreadable, and the printed representation (from `prin1') of 254 OBJECT if it is readable." 255 (declare (side-effect-free error-free)) 256 (ignore-errors (equal object (read (prin1-to-string object))))) 257 258 (compat-defun buffer-local-restore-state (states) ;; <compat-tests:buffer-local-set-state> 259 "Restore values of buffer-local variables recorded in STATES. 260 STATES should be an object returned by `buffer-local-set-state'." 261 (dolist (state states) 262 (if (cadr state) 263 (set (car state) (caddr state)) 264 (kill-local-variable (car state))))) 265 266 (compat-defun buffer-local-set-state--get (pairs) ;; <compat-tests:buffer-local-set-state> 267 "Internal helper function." 268 (let ((states nil)) 269 (while pairs 270 (push (list (car pairs) 271 (and (boundp (car pairs)) 272 (local-variable-p (car pairs))) 273 (and (boundp (car pairs)) 274 (symbol-value (car pairs)))) 275 states) 276 (setq pairs (cddr pairs))) 277 (nreverse states))) 278 279 (compat-defmacro buffer-local-set-state (&rest pairs) ;; <compat-tests:buffer-local-set-state> 280 "Like `setq-local', but allow restoring the previous state of locals later. 281 This macro returns an object that can be passed to `buffer-local-restore-state' 282 in order to restore the state of the local variables set via this macro. 283 284 \(fn [VARIABLE VALUE]...)" 285 (declare (debug setq)) 286 (unless (zerop (mod (length pairs) 2)) 287 (error "PAIRS must have an even number of variable/value members")) 288 `(prog1 289 (buffer-local-set-state--get ',pairs) 290 (,(if (fboundp 'compat--setq-local) 'compat--setq-local 'setq-local) 291 ,@pairs))) 292 293 (compat-defun list-of-strings-p (object) ;; <compat-tests:list-of-strings-p> 294 "Return t if OBJECT is nil or a list of strings." 295 (declare (pure t) (side-effect-free t)) 296 (while (and (consp object) (stringp (car object))) 297 (setq object (cdr object))) 298 (null object)) 299 300 (compat-defun plistp (object) ;; <compat-tests:plistp> 301 "Non-nil if and only if OBJECT is a valid plist." 302 (let ((len (proper-list-p object))) 303 (and len (zerop (% len 2))))) 304 305 (compat-defun delete-line () ;; <compat-tests:delete-line> 306 "Delete the current line." 307 (delete-region (pos-bol) (pos-bol 2))) 308 309 (compat-defmacro with-restriction (start end &rest rest) ;; <compat-tests:with-restriction> 310 "Execute BODY with restrictions set to START and END. 311 312 The current restrictions, if any, are restored upon return. 313 314 When the optional :label LABEL argument is present, in which 315 LABEL is a symbol, inside BODY, `narrow-to-region' and `widen' 316 can be used only within the START and END limits. To gain access 317 to other portions of the buffer, use `without-restriction' with the 318 same LABEL argument. 319 320 \(fn START END [:label LABEL] BODY)" 321 (declare (indent 0) (debug t)) 322 `(save-restriction 323 (narrow-to-region ,start ,end) 324 ;; Locking is ignored 325 ,@(if (eq (car rest) :label) (cddr rest) rest))) 326 327 (compat-defmacro without-restriction (&rest rest) ;; <compat-tests:without-restriction> 328 "Execute BODY without restrictions. 329 330 The current restrictions, if any, are restored upon return. 331 332 When the optional :label LABEL argument is present, the 333 restrictions set by `with-restriction' with the same LABEL argument 334 are lifted. 335 336 \(fn [:label LABEL] BODY)" 337 (declare (indent 0) (debug t)) 338 `(save-restriction 339 (widen) 340 ;; Locking is ignored 341 ,@(if (eq (car rest) :label) (cddr rest) rest))) 342 343 (compat-defmacro with-memoization (place &rest code) ;; <compat-tests:with-memoization> 344 "Return the value of CODE and stash it in PLACE. 345 If PLACE's value is non-nil, then don't bother evaluating CODE 346 and return the value found in PLACE instead." 347 (declare (indent 1)) 348 (gv-letplace (getter setter) place 349 `(or ,getter 350 ,(macroexp-let2 nil val (macroexp-progn code) 351 `(progn 352 ,(funcall setter val) 353 ,val))))) 354 355 (compat-defalias string-split split-string) ;; <compat-tests:string-split> 356 357 (compat-defun compiled-function-p (object) ;; <compat-tests:compiled-function-p> 358 "Return non-nil if OBJECT is a function that has been compiled. 359 Does not distinguish between functions implemented in machine code 360 or byte-code." 361 (or (subrp object) (byte-code-function-p object))) 362 363 (compat-defun function-alias-p (func &optional noerror) ;; <compat-tests:function-alias-p> 364 "Return nil if FUNC is not a function alias. 365 If FUNC is a function alias, return the function alias chain. 366 367 If the function alias chain contains loops, an error will be 368 signalled. If NOERROR, the non-loop parts of the chain is returned." 369 (declare (side-effect-free t)) 370 (let ((chain nil) 371 (orig-func func)) 372 (nreverse 373 (catch 'loop 374 (while (and (symbolp func) 375 (setq func (symbol-function func)) 376 (symbolp func)) 377 (when (or (memq func chain) 378 (eq func orig-func)) 379 (if noerror 380 (throw 'loop chain) 381 (signal 'cyclic-function-indirection (list orig-func)))) 382 (push func chain)) 383 chain)))) 384 385 (compat-defun buffer-match-p (condition buffer-or-name &optional arg) ;; <compat-tests:buffer-match-p> 386 "Return non-nil if BUFFER-OR-NAME matches CONDITION. 387 CONDITION is either: 388 - the symbol t, to always match, 389 - the symbol nil, which never matches, 390 - a regular expression, to match a buffer name, 391 - a predicate function that takes a buffer object and ARG as 392 arguments, and returns non-nil if the buffer matches, 393 - a cons-cell, where the car describes how to interpret the cdr. 394 The car can be one of the following: 395 * `derived-mode': the buffer matches if the buffer's major mode 396 is derived from the major mode in the cons-cell's cdr. 397 * `major-mode': the buffer matches if the buffer's major mode 398 is eq to the cons-cell's cdr. Prefer using `derived-mode' 399 instead when both can work. 400 * `not': the cadr is interpreted as a negation of a condition. 401 * `and': the cdr is a list of recursive conditions, that all have 402 to be met. 403 * `or': the cdr is a list of recursive condition, of which at 404 least one has to be met." 405 (letrec 406 ((buffer (get-buffer buffer-or-name)) 407 (match 408 (lambda (conditions) 409 (catch 'match 410 (dolist (condition conditions) 411 (when (cond 412 ((eq condition t)) 413 ((stringp condition) 414 (string-match-p condition (buffer-name buffer))) 415 ((functionp condition) 416 (condition-case nil 417 (funcall condition buffer) 418 (wrong-number-of-arguments 419 (funcall condition buffer arg)))) 420 ((eq (car-safe condition) 'major-mode) 421 (eq 422 (buffer-local-value 'major-mode buffer) 423 (cdr condition))) 424 ((eq (car-safe condition) 'derived-mode) 425 (provided-mode-derived-p 426 (buffer-local-value 'major-mode buffer) 427 (cdr condition))) 428 ((eq (car-safe condition) 'not) 429 (not (funcall match (cdr condition)))) 430 ((eq (car-safe condition) 'or) 431 (funcall match (cdr condition))) 432 ((eq (car-safe condition) 'and) 433 (catch 'fail 434 (dolist (c (cdr condition)) 435 (unless (funcall match (list c)) 436 (throw 'fail nil))) 437 t))) 438 (throw 'match t))))))) 439 (funcall match (list condition)))) 440 441 (compat-defun match-buffers (condition &optional buffers arg) ;; <compat-tests:match-buffers> 442 "Return a list of buffers that match CONDITION. 443 See `buffer-match' for details on CONDITION. By default all 444 buffers are checked, this can be restricted by passing an 445 optional argument BUFFERS, set to a list of buffers to check. 446 ARG is passed to `buffer-match', for predicate conditions in 447 CONDITION." 448 (let (bufs) 449 (dolist (buf (or buffers (buffer-list))) 450 (when (buffer-match-p condition (get-buffer buf) arg) 451 (push buf bufs))) 452 bufs)) 453 454 (compat-defvar set-transient-map-timeout nil ;; <compat-tests:set-transient-map> 455 "Timeout in seconds for deactivation of a transient keymap. 456 If this is a number, it specifies the amount of idle time 457 after which to deactivate the keymap set by `set-transient-map', 458 thus overriding the value of the TIMEOUT argument to that function.") 459 460 (compat-defvar set-transient-map-timer nil ;; <compat-tests:set-transient-map> 461 "Timer for `set-transient-map-timeout'.") 462 463 (declare-function format-spec "format-spec") 464 (compat-defun set-transient-map (map &optional keep-pred on-exit message timeout) ;; <compat-tests:set-transient-map> 465 "Handle the optional arguments MESSAGE and TIMEOUT." 466 :extended t 467 (unless (fboundp 'format-spec) 468 (require 'format-spec)) 469 (let* ((timeout (or set-transient-map-timeout timeout)) 470 (message 471 (when message 472 (let (keys) 473 (map-keymap (lambda (key cmd) (and cmd (push key keys))) map) 474 (format-spec (if (stringp message) message "Repeat with %k") 475 `((?k . ,(mapconcat 476 (lambda (key) 477 (substitute-command-keys 478 (format "\\`%s'" 479 (key-description (vector key))))) 480 keys ", "))))))) 481 (clearfun (make-symbol "clear-transient-map")) 482 (exitfun 483 (lambda () 484 (internal-pop-keymap map 'overriding-terminal-local-map) 485 (remove-hook 'pre-command-hook clearfun) 486 (when message (message "")) 487 (when set-transient-map-timer (cancel-timer set-transient-map-timer)) 488 (when on-exit (funcall on-exit))))) 489 (fset clearfun 490 (lambda () 491 (with-demoted-errors "set-transient-map PCH: %S" 492 (if (cond 493 ((null keep-pred) nil) 494 ((and (not (eq map (cadr overriding-terminal-local-map))) 495 (memq map (cddr overriding-terminal-local-map))) 496 t) 497 ((eq t keep-pred) 498 (let ((mc (lookup-key map (this-command-keys-vector)))) 499 (when (and mc (symbolp mc)) 500 (setq mc (or (command-remapping mc) mc))) 501 (and mc (eq this-command mc)))) 502 (t (funcall keep-pred))) 503 (when message (message "%s" message)) 504 (funcall exitfun))))) 505 (add-hook 'pre-command-hook clearfun) 506 (internal-push-keymap map 'overriding-terminal-local-map) 507 (when timeout 508 (when set-transient-map-timer (cancel-timer set-transient-map-timer)) 509 (setq set-transient-map-timer (run-with-idle-timer timeout nil exitfun))) 510 (when message (message "%s" message)) 511 exitfun)) 512 513 ;;;; Defined in simple.el 514 515 (compat-defun char-uppercase-p (char) ;; <compat-tests:char-uppercase-p> 516 "Return non-nil if CHAR is an upper-case character. 517 If the Unicode tables are not yet available, e.g. during bootstrap, 518 then gives correct answers only for ASCII characters." 519 (cond ((unicode-property-table-internal 'lowercase) 520 (characterp (get-char-code-property char 'lowercase))) 521 ((and (>= char ?A) (<= char ?Z))))) 522 523 (compat-defun use-region-noncontiguous-p () ;; <compat-tests:region-noncontiguous-p> 524 "Return non-nil for a non-contiguous region if `use-region-p'." 525 (and (use-region-p) (region-noncontiguous-p))) 526 527 (compat-defun use-region-beginning () ;; <compat-tests:use-region> 528 "Return the start of the region if `use-region-p'." 529 (and (use-region-p) (region-beginning))) 530 531 (compat-defun use-region-end () ;; <compat-tests:use-region> 532 "Return the end of the region if `use-region-p'." 533 (and (use-region-p) (region-end))) 534 535 (compat-defun get-scratch-buffer-create () ;; <compat-tests:get-scratch-buffer-create> 536 "Return the *scratch* buffer, creating a new one if needed." 537 (or (get-buffer "*scratch*") 538 (let ((scratch (get-buffer-create "*scratch*"))) 539 ;; Don't touch the buffer contents or mode unless we know that 540 ;; we just created it. 541 (with-current-buffer scratch 542 (when initial-scratch-message 543 (insert (substitute-command-keys initial-scratch-message)) 544 (set-buffer-modified-p nil)) 545 (funcall initial-major-mode)) 546 scratch))) 547 548 ;;;; Defined in subr-x.el 549 550 (compat-defmacro with-buffer-unmodified-if-unchanged (&rest body) ;; <compat-tests:with-buffer-unmodified-if-unchanged> 551 "Like `progn', but change buffer-modified status only if buffer text changes. 552 If the buffer was unmodified before execution of BODY, and 553 buffer text after execution of BODY is identical to what it was 554 before, ensure that buffer is still marked unmodified afterwards. 555 For example, the following won't change the buffer's modification 556 status: 557 558 (with-buffer-unmodified-if-unchanged 559 (insert \"a\") 560 (delete-char -1)) 561 562 Note that only changes in the raw byte sequence of the buffer text, 563 as stored in the internal representation, are monitored for the 564 purpose of detecting the lack of changes in buffer text. Any other 565 changes that are normally perceived as \"buffer modifications\", such 566 as changes in text properties, `buffer-file-coding-system', buffer 567 multibyteness, etc. -- will not be noticed, and the buffer will still 568 be marked unmodified, effectively ignoring those changes." 569 (declare (debug t) (indent 0)) 570 (let ((hash (gensym)) 571 (buffer (gensym))) 572 `(let ((,hash (and (not (buffer-modified-p)) 573 (buffer-hash))) 574 (,buffer (current-buffer))) 575 (prog1 576 (progn 577 ,@body) 578 ;; If we didn't change anything in the buffer (and the buffer 579 ;; was previously unmodified), then flip the modification status 580 ;; back to "unchanged". 581 (when (and ,hash (buffer-live-p ,buffer)) 582 (with-current-buffer ,buffer 583 (when (and (buffer-modified-p) 584 (equal ,hash (buffer-hash))) 585 (restore-buffer-modified-p nil)))))))) 586 587 (compat-defun add-display-text-property (start end prop value ;; <compat-tests:add-display-text-property> 588 &optional object) 589 "Add display property PROP with VALUE to the text from START to END. 590 If any text in the region has a non-nil `display' property, those 591 properties are retained. 592 593 If OBJECT is non-nil, it should be a string or a buffer. If nil, 594 this defaults to the current buffer." 595 (let ((sub-start start) 596 (sub-end 0) 597 disp) 598 (while (< sub-end end) 599 (setq sub-end (next-single-property-change sub-start 'display object 600 (if (stringp object) 601 (min (length object) end) 602 (min end (point-max))))) 603 (if (not (setq disp (get-text-property sub-start 'display object))) 604 ;; No old properties in this range. 605 (put-text-property sub-start sub-end 'display (list prop value) 606 object) 607 ;; We have old properties. 608 (let ((vector nil)) 609 ;; Make disp into a list. 610 (setq disp 611 (cond 612 ((vectorp disp) 613 (setq vector t) 614 (append disp nil)) 615 ((not (consp (car disp))) 616 (list disp)) 617 (t 618 disp))) 619 ;; Remove any old instances. 620 (when-let ((old (assoc prop disp))) 621 (setq disp (delete old disp))) 622 (setq disp (cons (list prop value) disp)) 623 (when vector 624 (setq disp (vconcat disp))) 625 ;; Finally update the range. 626 (put-text-property sub-start sub-end 'display disp object))) 627 (setq sub-start sub-end)))) 628 629 (compat-defmacro while-let (spec &rest body) ;; <compat-tests:while-let> 630 "Bind variables according to SPEC and conditionally evaluate BODY. 631 Evaluate each binding in turn, stopping if a binding value is nil. 632 If all bindings are non-nil, eval BODY and repeat. 633 634 The variable list SPEC is the same as in `if-let*'." 635 (declare (indent 1) (debug if-let)) 636 (let ((done (gensym "done"))) 637 `(catch ',done 638 (while t 639 (if-let* ,spec 640 (progn 641 ,@body) 642 (throw ',done nil)))))) 643 644 ;;;; Defined in files.el 645 646 (compat-defun directory-abbrev-make-regexp (directory) ;; <compat-tests:directory-abbrev-make-regexp> 647 "Create a regexp to match DIRECTORY for `directory-abbrev-alist'." 648 (let ((regexp 649 ;; We include a slash at the end, to avoid spurious 650 ;; matches such as `/usr/foobar' when the home dir is 651 ;; `/usr/foo'. 652 (concat "\\`" (regexp-quote directory) "\\(/\\|\\'\\)"))) 653 ;; The value of regexp could be multibyte or unibyte. In the 654 ;; latter case, we need to decode it. 655 (if (multibyte-string-p regexp) 656 regexp 657 (decode-coding-string regexp 658 (if (eq system-type 'windows-nt) 659 'utf-8 660 locale-coding-system))))) 661 662 (compat-defun directory-abbrev-apply (filename) ;; <compat-tests:directory-abbrev-apply> 663 "Apply the abbreviations in `directory-abbrev-alist' to FILENAME. 664 Note that when calling this, you should set `case-fold-search' as 665 appropriate for the filesystem used for FILENAME." 666 (dolist (dir-abbrev directory-abbrev-alist filename) 667 (when (string-match (car dir-abbrev) filename) 668 (setq filename (concat (cdr dir-abbrev) 669 (substring filename (match-end 0))))))) 670 671 (compat-defun file-name-split (filename) ;; <compat-tests:file-name-split> 672 "Return a list of all the components of FILENAME. 673 On most systems, this will be true: 674 675 (equal (string-join (file-name-split filename) \"/\") filename)" 676 (let ((components nil)) 677 ;; If this is a directory file name, then we have a null file name 678 ;; at the end. 679 (when (directory-name-p filename) 680 (push "" components) 681 (setq filename (directory-file-name filename))) 682 ;; Loop, chopping off components. 683 (while (length> filename 0) 684 (push (file-name-nondirectory filename) components) 685 (let ((dir (file-name-directory filename))) 686 (setq filename (and dir (directory-file-name dir))) 687 ;; If there's nothing left to peel off, we're at the root and 688 ;; we can stop. 689 (when (and dir (equal dir filename)) 690 (push (if (equal dir "") "" 691 ;; On Windows, the first component might be "c:" or 692 ;; the like. 693 (substring dir 0 -1)) 694 components) 695 (setq filename nil)))) 696 components)) 697 698 (compat-defun file-attribute-file-identifier (attributes) ;; <compat-tests:file-attribute-getters> 699 "The inode and device numbers in ATTRIBUTES returned by `file-attributes'. 700 The value is a list of the form (INODENUM DEVICE), where DEVICE could be 701 either a single number or a cons cell of two numbers. 702 This tuple of numbers uniquely identifies the file." 703 (nthcdr 10 attributes)) 704 705 (compat-defun file-name-parent-directory (filename) ;; <compat-tests:file-name-parent-directory> 706 "Return the directory name of the parent directory of FILENAME. 707 If FILENAME is at the root of the filesystem, return nil. 708 If FILENAME is relative, it is interpreted to be relative 709 to `default-directory', and the result will also be relative." 710 (let* ((expanded-filename (expand-file-name filename)) 711 (parent (file-name-directory (directory-file-name expanded-filename)))) 712 (cond 713 ;; filename is at top-level, therefore no parent 714 ((or (null parent) 715 ;; `equal' is enough, we don't need to resolve symlinks here 716 ;; with `file-equal-p', also for performance 717 (equal parent expanded-filename)) 718 nil) 719 ;; filename is relative, return relative parent 720 ((not (file-name-absolute-p filename)) 721 (file-relative-name parent)) 722 (t 723 parent)))) 724 725 (compat-defvar file-has-changed-p--hash-table ;; <compat-tests:file-has-changed-p> 726 (make-hash-table :test #'equal) 727 "Internal variable used by `file-has-changed-p'.") 728 729 (compat-defun file-has-changed-p (file &optional tag) ;; <compat-tests:file-has-changed-p> 730 "Return non-nil if FILE has changed. 731 The size and modification time of FILE are compared to the size 732 and modification time of the same FILE during a previous 733 invocation of `file-has-changed-p'. Thus, the first invocation 734 of `file-has-changed-p' always returns non-nil when FILE exists. 735 The optional argument TAG, which must be a symbol, can be used to 736 limit the comparison to invocations with identical tags; it can be 737 the symbol of the calling function, for example." 738 (let* ((file (directory-file-name (expand-file-name file))) 739 (remote-file-name-inhibit-cache t) 740 (fileattr (file-attributes file 'integer)) 741 (attr (and fileattr 742 (cons (file-attribute-size fileattr) 743 (file-attribute-modification-time fileattr)))) 744 (sym (concat (symbol-name tag) "@" file)) 745 (cachedattr (gethash sym file-has-changed-p--hash-table))) 746 (unless (equal attr cachedattr) 747 (puthash sym attr file-has-changed-p--hash-table)))) 748 749 ;;;; Defined in keymap.el 750 751 (compat-defun key-valid-p (keys) ;; <compat-tests:key-valid-p> 752 "Say whether KEYS is a valid key. 753 A key is a string consisting of one or more key strokes. 754 The key strokes are separated by single space characters. 755 756 Each key stroke is either a single character, or the name of an 757 event, surrounded by angle brackets. In addition, any key stroke 758 may be preceded by one or more modifier keys. Finally, a limited 759 number of characters have a special shorthand syntax. 760 761 Here's some example key sequences. 762 763 \"f\" (the key `f') 764 \"S o m\" (a three key sequence of the keys `S', `o' and `m') 765 \"C-c o\" (a two key sequence of the keys `c' with the control modifier 766 and then the key `o') 767 \"H-<left>\" (the key named \"left\" with the hyper modifier) 768 \"M-RET\" (the \"return\" key with a meta modifier) 769 \"C-M-<space>\" (the \"space\" key with both the control and meta modifiers) 770 771 These are the characters that have shorthand syntax: 772 NUL, RET, TAB, LFD, ESC, SPC, DEL. 773 774 Modifiers have to be specified in this order: 775 776 A-C-H-M-S-s 777 778 which is 779 780 Alt-Control-Hyper-Meta-Shift-super" 781 (declare (pure t) (side-effect-free t)) 782 (let ((case-fold-search nil)) 783 (and 784 (stringp keys) 785 (string-match-p "\\`[^ ]+\\( [^ ]+\\)*\\'" keys) 786 (save-match-data 787 (catch 'exit 788 (let ((prefixes 789 "\\(A-\\)?\\(C-\\)?\\(H-\\)?\\(M-\\)?\\(S-\\)?\\(s-\\)?")) 790 (dolist (key (split-string keys " ")) 791 ;; Every key might have these modifiers, and they should be 792 ;; in this order. 793 (when (string-match (concat "\\`" prefixes) key) 794 (setq key (substring key (match-end 0)))) 795 (unless (or (and (= (length key) 1) 796 ;; Don't accept control characters as keys. 797 (not (< (aref key 0) ?\s)) 798 ;; Don't accept Meta'd characters as keys. 799 (or (multibyte-string-p key) 800 (not (<= 127 (aref key 0) 255)))) 801 (and (string-match-p "\\`<[-_A-Za-z0-9]+>\\'" key) 802 ;; Don't allow <M-C-down>. 803 (= (progn 804 (string-match 805 (concat "\\`<" prefixes) key) 806 (match-end 0)) 807 1)) 808 (string-match-p 809 "\\`\\(NUL\\|RET\\|TAB\\|LFD\\|ESC\\|SPC\\|DEL\\)\\'" 810 key)) 811 ;; Invalid. 812 (throw 'exit nil))) 813 t)))))) 814 815 (compat-defun keymap--check (key) ;; <compat-tests:keymap--check> 816 "Signal an error if KEY doesn't have a valid syntax." 817 (unless (key-valid-p key) 818 (error "%S is not a valid key definition; see `key-valid-p'" key))) 819 820 (compat-defun key-parse (keys) ;; <compat-tests:key-parse> 821 "Convert KEYS to the internal Emacs key representation. 822 See `kbd' for a descripion of KEYS." 823 (declare (pure t) (side-effect-free t)) 824 ;; A pure function is expected to preserve the match data. 825 (save-match-data 826 (let ((case-fold-search nil) 827 (len (length keys)) ; We won't alter keys in the loop below. 828 (pos 0) 829 (res [])) 830 (while (and (< pos len) 831 (string-match "[^ \t\n\f]+" keys pos)) 832 (let* ((word-beg (match-beginning 0)) 833 (word-end (match-end 0)) 834 (word (substring keys word-beg len)) 835 (times 1) 836 key) 837 ;; Try to catch events of the form "<as df>". 838 (if (string-match "\\`<[^ <>\t\n\f][^>\t\n\f]*>" word) 839 (setq word (match-string 0 word) 840 pos (+ word-beg (match-end 0))) 841 (setq word (substring keys word-beg word-end) 842 pos word-end)) 843 (when (string-match "\\([0-9]+\\)\\*." word) 844 (setq times (string-to-number (substring word 0 (match-end 1)))) 845 (setq word (substring word (1+ (match-end 1))))) 846 (cond ((string-match "^<<.+>>$" word) 847 (setq key (vconcat (if (eq (key-binding [?\M-x]) 848 'execute-extended-command) 849 [?\M-x] 850 (or (car (where-is-internal 851 'execute-extended-command)) 852 [?\M-x])) 853 (substring word 2 -2) "\r"))) 854 ((and (string-match "^\\(\\([ACHMsS]-\\)*\\)<\\(.+\\)>$" word) 855 (progn 856 (setq word (concat (match-string 1 word) 857 (match-string 3 word))) 858 (not (string-match 859 "\\<\\(NUL\\|RET\\|LFD\\|ESC\\|SPC\\|DEL\\)$" 860 word)))) 861 (setq key (list (intern word)))) 862 ((or (equal word "REM") (string-match "^;;" word)) 863 (setq pos (string-match "$" keys pos))) 864 (t 865 (let ((orig-word word) (prefix 0) (bits 0)) 866 (while (string-match "^[ACHMsS]-." word) 867 (setq bits (+ bits 868 (cdr 869 (assq (aref word 0) 870 '((?A . ?\A-\0) (?C . ?\C-\0) 871 (?H . ?\H-\0) (?M . ?\M-\0) 872 (?s . ?\s-\0) (?S . ?\S-\0)))))) 873 (setq prefix (+ prefix 2)) 874 (setq word (substring word 2))) 875 (when (string-match "^\\^.$" word) 876 (setq bits (+ bits ?\C-\0)) 877 (setq prefix (1+ prefix)) 878 (setq word (substring word 1))) 879 (let ((found (assoc word '(("NUL" . "\0") ("RET" . "\r") 880 ("LFD" . "\n") ("TAB" . "\t") 881 ("ESC" . "\e") ("SPC" . " ") 882 ("DEL" . "\177"))))) 883 (when found (setq word (cdr found)))) 884 (when (string-match "^\\\\[0-7]+$" word) 885 (let ((n 0)) 886 (dolist (ch (cdr (string-to-list word))) 887 (setq n (+ (* n 8) ch -48))) 888 (setq word (vector n)))) 889 (cond ((= bits 0) 890 (setq key word)) 891 ((and (= bits ?\M-\0) (stringp word) 892 (string-match "^-?[0-9]+$" word)) 893 (setq key (mapcar (lambda (x) (+ x bits)) 894 (append word nil)))) 895 ((/= (length word) 1) 896 (error "%s must prefix a single character, not %s" 897 (substring orig-word 0 prefix) word)) 898 ((and (/= (logand bits ?\C-\0) 0) (stringp word) 899 ;; We used to accept . and ? here, 900 ;; but . is simply wrong, 901 ;; and C-? is not used (we use DEL instead). 902 (string-match "[@-_a-z]" word)) 903 (setq key (list (+ bits (- ?\C-\0) 904 (logand (aref word 0) 31))))) 905 (t 906 (setq key (list (+ bits (aref word 0))))))))) 907 (when key 908 (dolist (_ (number-sequence 1 times)) 909 (setq res (vconcat res key)))))) 910 res))) 911 912 (compat-defun keymap-set (keymap key definition) ;; <compat-tests:defvar-keymap> 913 "Set KEY to DEFINITION in KEYMAP. 914 KEY is a string that satisfies `key-valid-p'. 915 916 DEFINITION is anything that can be a key's definition: 917 nil (means key is undefined in this keymap), 918 a command (a Lisp function suitable for interactive calling), 919 a string (treated as a keyboard macro), 920 a keymap (to define a prefix key), 921 a symbol (when the key is looked up, the symbol will stand for its 922 function definition, which should at that time be one of the above, 923 or another symbol whose function definition is used, etc.), 924 a cons (STRING . DEFN), meaning that DEFN is the definition 925 (DEFN should be a valid definition in its own right) and 926 STRING is the menu item name (which is used only if the containing 927 keymap has been created with a menu name, see `make-keymap'), 928 or a cons (MAP . CHAR), meaning use definition of CHAR in keymap MAP, 929 or an extended menu item definition. 930 (See info node `(elisp)Extended Menu Items'.)" 931 (keymap--check key) 932 (when (stringp definition) 933 (keymap--check definition) 934 (setq definition (key-parse definition))) 935 (define-key keymap (key-parse key) definition)) 936 937 (compat-defun keymap-unset (keymap key &optional remove) ;; <compat-tests:keymap-unset> 938 "Remove key sequence KEY from KEYMAP. 939 KEY is a string that satisfies `key-valid-p'. 940 941 If REMOVE, remove the binding instead of unsetting it. This only 942 makes a difference when there's a parent keymap. When unsetting 943 a key in a child map, it will still shadow the same key in the 944 parent keymap. Removing the binding will allow the key in the 945 parent keymap to be used." 946 (keymap--check key) 947 (compat--define-key keymap (key-parse key) nil remove)) 948 949 (compat-defun keymap-global-set (key command) ;; <compat-tests:keymap-global-set> 950 "Give KEY a global binding as COMMAND. 951 COMMAND is the command definition to use; usually it is 952 a symbol naming an interactively-callable function. 953 954 KEY is a string that satisfies `key-valid-p'. 955 956 Note that if KEY has a local binding in the current buffer, 957 that local binding will continue to shadow any global binding 958 that you make with this function. 959 960 NOTE: The compatibility version is not a command." 961 (keymap-set (current-global-map) key command)) 962 963 (compat-defun keymap-local-set (key command) ;; <compat-tests:keymap-local-set> 964 "Give KEY a local binding as COMMAND. 965 COMMAND is the command definition to use; usually it is 966 a symbol naming an interactively-callable function. 967 968 KEY is a string that satisfies `key-valid-p'. 969 970 The binding goes in the current buffer's local map, which in most 971 cases is shared with all other buffers in the same major mode. 972 973 NOTE: The compatibility version is not a command." 974 (let ((map (current-local-map))) 975 (unless map 976 (use-local-map (setq map (make-sparse-keymap)))) 977 (keymap-set map key command))) 978 979 (compat-defun keymap-global-unset (key &optional remove) ;; <compat-tests:keymap-global-unset> 980 "Remove global binding of KEY (if any). 981 KEY is a string that satisfies `key-valid-p'. 982 983 If REMOVE (interactively, the prefix arg), remove the binding 984 instead of unsetting it. See `keymap-unset' for details. 985 986 NOTE: The compatibility version is not a command." 987 (keymap-unset (current-global-map) key remove)) 988 989 (compat-defun keymap-local-unset (key &optional remove) ;; <compat-tests:keymap-local-unset> 990 "Remove local binding of KEY (if any). 991 KEY is a string that satisfies `key-valid-p'. 992 993 If REMOVE (interactively, the prefix arg), remove the binding 994 instead of unsetting it. See `keymap-unset' for details. 995 996 NOTE: The compatibility version is not a command." 997 (when (current-local-map) 998 (keymap-unset (current-local-map) key remove))) 999 1000 (compat-defun keymap-substitute (keymap olddef newdef &optional oldmap prefix) ;; <compat-tests:keymap-substitute> 1001 "Replace OLDDEF with NEWDEF for any keys in KEYMAP now defined as OLDDEF. 1002 In other words, OLDDEF is replaced with NEWDEF wherever it appears. 1003 Alternatively, if optional fourth argument OLDMAP is specified, we redefine 1004 in KEYMAP as NEWDEF those keys that are defined as OLDDEF in OLDMAP. 1005 1006 If you don't specify OLDMAP, you can usually get the same results 1007 in a cleaner way with command remapping, like this: 1008 (define-key KEYMAP [remap OLDDEF] NEWDEF) 1009 \n(fn OLDDEF NEWDEF KEYMAP &optional OLDMAP)" 1010 ;; Don't document PREFIX in the doc string because we don't want to 1011 ;; advertise it. It's meant for recursive calls only. Here's its 1012 ;; meaning 1013 1014 ;; If optional argument PREFIX is specified, it should be a key 1015 ;; prefix, a string. Redefined bindings will then be bound to the 1016 ;; original key, with PREFIX added at the front. 1017 (unless prefix 1018 (setq prefix "")) 1019 (let* ((scan (or oldmap keymap)) 1020 (prefix1 (vconcat prefix [nil])) 1021 (key-substitution-in-progress 1022 (cons scan key-substitution-in-progress))) 1023 ;; Scan OLDMAP, finding each char or event-symbol that 1024 ;; has any definition, and act on it with hack-key. 1025 (map-keymap 1026 (lambda (char defn) 1027 (aset prefix1 (length prefix) char) 1028 (substitute-key-definition-key defn olddef newdef prefix1 keymap)) 1029 scan))) 1030 1031 (compat-defun keymap-set-after (keymap key definition &optional after) ;; <compat-tests:keymap-set-after> 1032 "Add binding in KEYMAP for KEY => DEFINITION, right after AFTER's binding. 1033 This is like `keymap-set' except that the binding for KEY is placed 1034 just after the binding for the event AFTER, instead of at the beginning 1035 of the map. Note that AFTER must be an event type (like KEY), NOT a command 1036 \(like DEFINITION). 1037 1038 If AFTER is t or omitted, the new binding goes at the end of the keymap. 1039 AFTER should be a single event type--a symbol or a character, not a sequence. 1040 1041 Bindings are always added before any inherited map. 1042 1043 The order of bindings in a keymap matters only when it is used as 1044 a menu, so this function is not useful for non-menu keymaps." 1045 (keymap--check key) 1046 (when (eq after t) (setq after nil)) ; nil and t are treated the same 1047 (when (stringp after) 1048 (keymap--check after) 1049 (setq after (key-parse after))) 1050 ;; If we're binding this key to another key, then parse that other 1051 ;; key, too. 1052 (when (stringp definition) 1053 (keymap--check definition) 1054 (setq definition (key-parse definition))) 1055 (define-key-after keymap (key-parse key) definition 1056 after)) 1057 1058 (compat-defun keymap-lookup ;; <compat-tests:keymap-lookup> 1059 (keymap key &optional accept-default no-remap position) 1060 "Return the binding for command KEY. 1061 KEY is a string that satisfies `key-valid-p'. 1062 1063 If KEYMAP is nil, look up in the current keymaps. If non-nil, it 1064 should either be a keymap or a list of keymaps, and only these 1065 keymap(s) will be consulted. 1066 1067 The binding is probably a symbol with a function definition. 1068 1069 Normally, `keymap-lookup' ignores bindings for t, which act as 1070 default bindings, used when nothing else in the keymap applies; 1071 this makes it usable as a general function for probing keymaps. 1072 However, if the optional second argument ACCEPT-DEFAULT is 1073 non-nil, `keymap-lookup' does recognize the default bindings, 1074 just as `read-key-sequence' does. 1075 1076 Like the normal command loop, `keymap-lookup' will remap the 1077 command resulting from looking up KEY by looking up the command 1078 in the current keymaps. However, if the optional third argument 1079 NO-REMAP is non-nil, `keymap-lookup' returns the unmapped 1080 command. 1081 1082 If KEY is a key sequence initiated with the mouse, the used keymaps 1083 will depend on the clicked mouse position with regard to the buffer 1084 and possible local keymaps on strings. 1085 1086 If the optional argument POSITION is non-nil, it specifies a mouse 1087 position as returned by `event-start' and `event-end', and the lookup 1088 occurs in the keymaps associated with it instead of KEY. It can also 1089 be a number or marker, in which case the keymap properties at the 1090 specified buffer position instead of point are used." 1091 (keymap--check key) 1092 (when (and keymap position) 1093 (error "Can't pass in both keymap and position")) 1094 (if keymap 1095 (let ((value (lookup-key keymap (key-parse key) accept-default))) 1096 (if (and (not no-remap) 1097 (symbolp value)) 1098 (or (command-remapping value) value) 1099 value)) 1100 (key-binding (key-parse key) accept-default no-remap position))) 1101 1102 (compat-defun keymap-local-lookup (keys &optional accept-default) ;; <compat-tests:keymap-local-lookup> 1103 "Return the binding for command KEYS in current local keymap only. 1104 KEY is a string that satisfies `key-valid-p'. 1105 1106 The binding is probably a symbol with a function definition. 1107 1108 If optional argument ACCEPT-DEFAULT is non-nil, recognize default 1109 bindings; see the description of `keymap-lookup' for more details 1110 about this." 1111 (when-let ((map (current-local-map))) 1112 (keymap-lookup map keys accept-default))) 1113 1114 (compat-defun keymap-global-lookup (keys &optional accept-default _message) ;; <compat-tests:keymap-global-lookup> 1115 "Return the binding for command KEYS in current global keymap only. 1116 KEY is a string that satisfies `key-valid-p'. 1117 1118 The binding is probably a symbol with a function definition. 1119 This function's return values are the same as those of `keymap-lookup' 1120 \(which see). 1121 1122 If optional argument ACCEPT-DEFAULT is non-nil, recognize default 1123 bindings; see the description of `keymap-lookup' for more details 1124 about this. 1125 1126 NOTE: The compatibility version is not a command." 1127 (keymap-lookup (current-global-map) keys accept-default)) 1128 1129 (compat-defun define-keymap (&rest definitions) ;; <compat-tests:defvar-keymap> 1130 "Create a new keymap and define KEY/DEFINITION pairs as key bindings. 1131 The new keymap is returned. 1132 1133 Options can be given as keywords before the KEY/DEFINITION 1134 pairs. Available keywords are: 1135 1136 :full If non-nil, create a chartable alist (see `make-keymap'). 1137 If nil (i.e., the default), create a sparse keymap (see 1138 `make-sparse-keymap'). 1139 1140 :suppress If non-nil, the keymap will be suppressed (see `suppress-keymap'). 1141 If `nodigits', treat digits like other chars. 1142 1143 :parent If non-nil, this should be a keymap to use as the parent 1144 (see `set-keymap-parent'). 1145 1146 :keymap If non-nil, instead of creating a new keymap, the given keymap 1147 will be destructively modified instead. 1148 1149 :name If non-nil, this should be a string to use as the menu for 1150 the keymap in case you use it as a menu with `x-popup-menu'. 1151 1152 :prefix If non-nil, this should be a symbol to be used as a prefix 1153 command (see `define-prefix-command'). If this is the case, 1154 this symbol is returned instead of the map itself. 1155 1156 KEY/DEFINITION pairs are as KEY and DEF in `keymap-set'. KEY can 1157 also be the special symbol `:menu', in which case DEFINITION 1158 should be a MENU form as accepted by `easy-menu-define'. 1159 1160 \(fn &key FULL PARENT SUPPRESS NAME PREFIX KEYMAP &rest [KEY DEFINITION]...)" 1161 (declare (indent defun)) 1162 (let (full suppress parent name prefix keymap) 1163 ;; Handle keywords. 1164 (while (and definitions 1165 (keywordp (car definitions)) 1166 (not (eq (car definitions) :menu))) 1167 (let ((keyword (pop definitions))) 1168 (unless definitions 1169 (error "Missing keyword value for %s" keyword)) 1170 (let ((value (pop definitions))) 1171 (pcase keyword 1172 (:full (setq full value)) 1173 (:keymap (setq keymap value)) 1174 (:parent (setq parent value)) 1175 (:suppress (setq suppress value)) 1176 (:name (setq name value)) 1177 (:prefix (setq prefix value)) 1178 (_ (error "Invalid keyword: %s" keyword)))))) 1179 1180 (when (and prefix 1181 (or full parent suppress keymap)) 1182 (error "A prefix keymap can't be defined with :full/:parent/:suppress/:keymap keywords")) 1183 1184 (when (and keymap full) 1185 (error "Invalid combination: :keymap with :full")) 1186 1187 (let ((keymap (cond 1188 (keymap keymap) 1189 (prefix (define-prefix-command prefix nil name)) 1190 (full (make-keymap name)) 1191 (t (make-sparse-keymap name)))) 1192 seen-keys) 1193 (when suppress 1194 (suppress-keymap keymap (eq suppress 'nodigits))) 1195 (when parent 1196 (set-keymap-parent keymap parent)) 1197 1198 ;; Do the bindings. 1199 (while definitions 1200 (let ((key (pop definitions))) 1201 (unless definitions 1202 (error "Uneven number of key/definition pairs")) 1203 (let ((def (pop definitions))) 1204 (if (eq key :menu) 1205 (easy-menu-define nil keymap "" def) 1206 (if (member key seen-keys) 1207 (error "Duplicate definition for key: %S %s" key keymap) 1208 (push key seen-keys)) 1209 (keymap-set keymap key def))))) 1210 keymap))) 1211 1212 (compat-defmacro defvar-keymap (variable-name &rest defs) ;; <compat-tests:defvar-keymap> 1213 "Define VARIABLE-NAME as a variable with a keymap definition. 1214 See `define-keymap' for an explanation of the keywords and KEY/DEFINITION. 1215 1216 In addition to the keywords accepted by `define-keymap', this 1217 macro also accepts a `:doc' keyword, which (if present) is used 1218 as the variable documentation string. 1219 1220 The `:repeat' keyword can also be specified; it controls the 1221 `repeat-mode' behavior of the bindings in the keymap. When it is 1222 non-nil, all commands in the map will have the `repeat-map' 1223 symbol property. 1224 1225 More control is available over which commands are repeatable; the 1226 value can also be a property list with properties `:enter' and 1227 `:exit', for example: 1228 1229 :repeat (:enter (commands ...) :exit (commands ...)) 1230 1231 `:enter' specifies the list of additional commands that only 1232 enter `repeat-mode'. When the list is empty, then only the 1233 commands defined in the map enter `repeat-mode'. Specifying a 1234 list of commands is useful when there are commands that have the 1235 `repeat-map' symbol property, but don't exist in this specific 1236 map. 1237 1238 `:exit' is a list of commands that exit `repeat-mode'. When the 1239 list is empty, no commands in the map exit `repeat-mode'. 1240 Specifying a list of commands is useful when those commands exist 1241 in this specific map, but should not have the `repeat-map' symbol 1242 property. 1243 1244 \(fn VARIABLE-NAME &key DOC FULL PARENT SUPPRESS NAME PREFIX KEYMAP REPEAT &rest [KEY DEFINITION]...)" 1245 (declare (indent 1)) 1246 (let ((opts nil) 1247 doc repeat props) 1248 (while (and defs 1249 (keywordp (car defs)) 1250 (not (eq (car defs) :menu))) 1251 (let ((keyword (pop defs))) 1252 (unless defs 1253 (error "Uneven number of keywords")) 1254 (cond 1255 ((eq keyword :doc) (setq doc (pop defs))) 1256 ((eq keyword :repeat) (setq repeat (pop defs))) 1257 (t (push keyword opts) 1258 (push (pop defs) opts))))) 1259 (unless (zerop (% (length defs) 2)) 1260 (error "Uneven number of key/definition pairs: %s" defs)) 1261 1262 (let ((defs defs) 1263 key seen-keys) 1264 (while defs 1265 (setq key (pop defs)) 1266 (pop defs) 1267 (unless (eq key :menu) 1268 (if (member key seen-keys) 1269 (error "Duplicate definition for key '%s' in keymap '%s'" 1270 key variable-name) 1271 (push key seen-keys))))) 1272 1273 (when repeat 1274 (let ((defs defs) 1275 def) 1276 (dolist (def (plist-get repeat :enter)) 1277 (push `(put ',def 'repeat-map ',variable-name) props)) 1278 (while defs 1279 (pop defs) 1280 (setq def (pop defs)) 1281 (when (and (memq (car def) '(function quote)) 1282 (not (memq (cadr def) (plist-get repeat :exit)))) 1283 (push `(put ,def 'repeat-map ',variable-name) props))))) 1284 1285 (let ((defvar-form 1286 `(defvar ,variable-name 1287 (define-keymap ,@(nreverse opts) ,@defs) 1288 ,@(and doc (list doc))))) 1289 (if props 1290 `(progn 1291 ,defvar-form 1292 ,@(nreverse props)) 1293 defvar-form)))) 1294 1295 ;;;; Defined in keymap.c 1296 1297 (compat-defun define-key (keymap key def &optional remove) ;; <compat-tests:define-key> 1298 "Handle optional argument REMOVE." 1299 :extended t 1300 (if (not remove) 1301 (define-key keymap key def) 1302 ;; Canonicalize key 1303 (setq key (key-parse (key-description key))) 1304 (define-key keymap key nil) 1305 ;; Split M-key in ESC key 1306 (setq key (mapcan (lambda (k) 1307 (if (and (integerp k) (/= (logand k ?\M-\0) 0)) 1308 (list ?\e (logxor k ?\M-\0)) 1309 (list k))) 1310 key)) 1311 ;; Delete single keys directly 1312 (if (length= key 1) 1313 (delete key keymap) 1314 ;; Lookup submap and delete key from there 1315 (let ((submap (lookup-key keymap (vconcat (butlast key))))) 1316 (unless (keymapp submap) 1317 (error "Not a keymap for %s" key)) 1318 (when (symbolp submap) 1319 (setq submap (symbol-function submap))) 1320 (delete (last key) submap))) 1321 def)) 1322 1323 ;;;; Defined in help.el 1324 1325 (compat-defun substitute-quotes (string) ;; <compat-tests:substitute-quotes> 1326 "Substitute quote characters for display. 1327 Each grave accent \\=` is replaced by left quote, and each 1328 apostrophe \\=' is replaced by right quote. Left and right quote 1329 characters are specified by `text-quoting-style'." 1330 (cond ((eq (text-quoting-style) 'curve) 1331 (string-replace "`" "‘" 1332 (string-replace "'" "’" string))) 1333 ((eq (text-quoting-style) 'straight) 1334 (string-replace "`" "'" string)) 1335 (t string))) 1336 1337 ;;;; Defined in button.el 1338 1339 (compat-defun button--properties (callback data help-echo) ;; <compat-tests:buttonize> 1340 "Helper function." 1341 (list 'font-lock-face 'button 1342 'mouse-face 'highlight 1343 'help-echo help-echo 1344 'button t 1345 'follow-link t 1346 'category t 1347 'button-data data 1348 'keymap button-map 1349 'action callback)) 1350 1351 (compat-defun buttonize (string callback &optional data help-echo) ;; <compat-tests:buttonize> 1352 "Make STRING into a button and return it. 1353 When clicked, CALLBACK will be called with the DATA as the 1354 function argument. If DATA isn't present (or is nil), the button 1355 itself will be used instead as the function argument. 1356 1357 If HELP-ECHO, use that as the `help-echo' property. 1358 1359 Also see `buttonize-region'." 1360 (let ((string 1361 (apply #'propertize string 1362 (button--properties callback data help-echo)))) 1363 ;; Add the face to the end so that it can be overridden. 1364 (add-face-text-property 0 (length string) 'button t string) 1365 string)) 1366 1367 (compat-defun buttonize-region (start end callback &optional data help-echo) ;; <compat-tests:buttonize-region> 1368 "Make the region between START and END into a button. 1369 When clicked, CALLBACK will be called with the DATA as the 1370 function argument. If DATA isn't present (or is nil), the button 1371 itself will be used instead as the function argument. 1372 1373 If HELP-ECHO, use that as the `help-echo' property. 1374 1375 Also see `buttonize'." 1376 (add-text-properties start end (button--properties callback data help-echo)) 1377 (add-face-text-property start end 'button t)) 1378 1379 ;;;; Defined in rmc.el 1380 1381 (compat-defun read-multiple-choice ;; <compat-tests:read-multiple-choice> 1382 (prompt choices &optional _help-str _show-help long-form) 1383 "Handle LONG-FORM argument." 1384 :extended t 1385 (if (not long-form) 1386 (read-multiple-choice prompt choices) 1387 (let ((answer 1388 (completing-read 1389 (concat prompt " (" 1390 (mapconcat #'identity (mapcar #'cadr choices) "/") 1391 ") ") 1392 (mapcar #'cadr choices) nil t))) 1393 (catch 'found 1394 (dolist (c choices) 1395 (when (equal answer (cadr c)) 1396 (throw 'found c))))))) 1397 1398 ;;;; Defined in paragraphs.el 1399 1400 (compat-defun count-sentences (start end) ;; <compat-tests:count-sentences> 1401 "Count sentences in current buffer from START to END." 1402 (let ((sentences 0) 1403 (inhibit-field-text-motion t)) 1404 (save-excursion 1405 (save-restriction 1406 (narrow-to-region start end) 1407 (goto-char (point-min)) 1408 (while (ignore-errors (forward-sentence)) 1409 (setq sentences (1+ sentences))) 1410 (when (/= (skip-chars-backward " \t\n") 0) 1411 (setq sentences (1- sentences))) 1412 sentences)))) 1413 1414 ;;;; Defined in cl-lib.el 1415 1416 (compat-defun cl-constantly (value) ;; <compat-tests:cl-constantly> 1417 "Return a function that takes any number of arguments, but returns VALUE." 1418 :feature cl-lib 1419 (lambda (&rest _) value)) 1420 1421 ;;;; Defined in cl-macs.el 1422 1423 (compat-defmacro cl-with-gensyms (names &rest body) ;; <compat-tests:cl-with-gensyms> 1424 "Bind each of NAMES to an uninterned symbol and evaluate BODY." 1425 ;; No :feature since macro is autoloaded 1426 (declare (debug (sexp body)) (indent 1)) 1427 `(let ,(cl-loop for name in names collect 1428 `(,name (gensym (symbol-name ',name)))) 1429 ,@body)) 1430 1431 (compat-defmacro cl-once-only (names &rest body) ;; <compat-tests:cl-once-only> 1432 "Generate code to evaluate each of NAMES just once in BODY. 1433 1434 This macro helps with writing other macros. Each of names is 1435 either (NAME FORM) or NAME, which latter means (NAME NAME). 1436 During macroexpansion, each NAME is bound to an uninterned 1437 symbol. The expansion evaluates each FORM and binds it to the 1438 corresponding uninterned symbol. 1439 1440 For example, consider this macro: 1441 1442 (defmacro my-cons (x) 1443 (cl-once-only (x) 1444 \\=`(cons ,x ,x))) 1445 1446 The call (my-cons (pop y)) will expand to something like this: 1447 1448 (let ((g1 (pop y))) 1449 (cons g1 g1)) 1450 1451 The use of `cl-once-only' ensures that the pop is performed only 1452 once, as intended. 1453 1454 See also `macroexp-let2'." 1455 ;; No :feature since macro is autoloaded 1456 (declare (debug (sexp body)) (indent 1)) 1457 (setq names (mapcar #'ensure-list names)) 1458 (let ((our-gensyms (cl-loop for _ in names collect (gensym)))) 1459 `(let ,(cl-loop for sym in our-gensyms collect `(,sym (gensym))) 1460 `(let ,(list 1461 ,@(cl-loop for name in names for gensym in our-gensyms 1462 for to-eval = (or (cadr name) (car name)) 1463 collect ``(,,gensym ,,to-eval))) 1464 ,(let ,(cl-loop for name in names for gensym in our-gensyms 1465 collect `(,(car name) ,gensym)) 1466 ,@body))))) 1467 1468 ;;;; Defined in ert-x.el 1469 1470 (compat-defmacro ert-with-temp-file (name &rest body) ;; <compat-tests:ert-with-temp-file> 1471 "Bind NAME to the name of a new temporary file and evaluate BODY. 1472 Delete the temporary file after BODY exits normally or 1473 non-locally. NAME will be bound to the file name of the temporary 1474 file. 1475 1476 The following keyword arguments are supported: 1477 1478 :prefix STRING If non-nil, pass STRING to `make-temp-file' as 1479 the PREFIX argument. Otherwise, use the value of 1480 `ert-temp-file-prefix'. 1481 1482 :suffix STRING If non-nil, pass STRING to `make-temp-file' as the 1483 SUFFIX argument. Otherwise, use the value of 1484 `ert-temp-file-suffix'; if the value of that 1485 variable is nil, generate a suffix based on the 1486 name of the file that `ert-with-temp-file' is 1487 called from. 1488 1489 :text STRING If non-nil, pass STRING to `make-temp-file' as 1490 the TEXT argument. 1491 1492 :buffer SYMBOL Open the temporary file using `find-file-noselect' 1493 and bind SYMBOL to the buffer. Kill the buffer 1494 after BODY exits normally or non-locally. 1495 1496 :coding CODING If non-nil, bind `coding-system-for-write' to CODING 1497 when executing BODY. This is handy when STRING includes 1498 non-ASCII characters or the temporary file must have a 1499 specific encoding or end-of-line format. 1500 1501 See also `ert-with-temp-directory'." 1502 :feature ert-x 1503 (declare (indent 1) (debug (symbolp body))) 1504 (cl-check-type name symbol) 1505 (let (keyw prefix suffix directory text extra-keywords buffer coding) 1506 (while (keywordp (setq keyw (car body))) 1507 (setq body (cdr body)) 1508 (pcase keyw 1509 (:prefix (setq prefix (pop body))) 1510 (:suffix (setq suffix (pop body))) 1511 ;; This is only for internal use by `ert-with-temp-directory' 1512 ;; and is therefore not documented. 1513 (:directory (setq directory (pop body))) 1514 (:text (setq text (pop body))) 1515 (:buffer (setq buffer (pop body))) 1516 (:coding (setq coding (pop body))) 1517 (_ (push keyw extra-keywords) (pop body)))) 1518 (when extra-keywords 1519 (error "Invalid keywords: %s" (mapconcat #'symbol-name extra-keywords " "))) 1520 (let ((temp-file (make-symbol "temp-file")) 1521 (prefix (or prefix "emacs-test-")) 1522 (suffix (or suffix 1523 (thread-last 1524 (file-name-base (or (macroexp-file-name) buffer-file-name)) 1525 (replace-regexp-in-string (rx string-start 1526 (group (+? not-newline)) 1527 (regexp "-?tests?") 1528 string-end) 1529 "\\1") 1530 (concat "-"))))) 1531 `(let* ((coding-system-for-write ,(or coding coding-system-for-write)) 1532 (,temp-file (,(if directory 'file-name-as-directory 'identity) 1533 (,(if (fboundp 'compat--make-temp-file) 1534 'compat--make-temp-file 'make-temp-file) 1535 ,prefix ,directory ,suffix ,text))) 1536 (,name ,(if directory 1537 `(file-name-as-directory ,temp-file) 1538 temp-file)) 1539 ,@(when buffer 1540 (list `(,buffer (find-file-literally ,temp-file))))) 1541 (unwind-protect 1542 (progn ,@body) 1543 (ignore-errors 1544 ,@(when buffer 1545 (list `(with-current-buffer ,buffer 1546 (set-buffer-modified-p nil)) 1547 `(kill-buffer ,buffer)))) 1548 (ignore-errors 1549 ,(if directory 1550 `(delete-directory ,temp-file :recursive) 1551 `(delete-file ,temp-file)))))))) 1552 1553 (compat-defmacro ert-with-temp-directory (name &rest body) ;; <compat-tests:ert-with-temp-directory> 1554 "Bind NAME to the name of a new temporary directory and evaluate BODY. 1555 Delete the temporary directory after BODY exits normally or 1556 non-locally. 1557 1558 NAME is bound to the directory name, not the directory file 1559 name. (In other words, it will end with the directory delimiter; 1560 on Unix-like systems, it will end with \"/\".) 1561 1562 The same keyword arguments are supported as in 1563 `ert-with-temp-file' (which see), except for :text." 1564 :feature ert-x 1565 (declare (indent 1) (debug (symbolp body))) 1566 (let ((tail body) keyw) 1567 (while (keywordp (setq keyw (car tail))) 1568 (setq tail (cddr tail)) 1569 (pcase keyw (:text (error "Invalid keyword for directory: :text"))))) 1570 `(ert-with-temp-file ,name 1571 :directory t 1572 ,@body)) 1573 1574 ;;;; Defined in wid-edit.el 1575 1576 (compat-guard (not (fboundp 'widget-key-validate)) ;; <compat-tests:widget-key> 1577 :feature wid-edit 1578 (defvar widget-key-prompt-value-history nil 1579 "History of input to `widget-key-prompt-value'.") 1580 (define-widget 'key 'editable-field 1581 "A key sequence." 1582 :prompt-value 'widget-field-prompt-value 1583 :match 'widget-key-valid-p 1584 :format "%{%t%}: %v" 1585 :validate 'widget-key-validate 1586 :keymap widget-key-sequence-map 1587 :help-echo "C-q: insert KEY, EVENT, or CODE; RET: enter value" 1588 :tag "Key") 1589 (defun widget-key-valid-p (_widget value) 1590 (key-valid-p value)) 1591 (defun widget-key-validate (widget) 1592 (unless (and (stringp (widget-value widget)) 1593 (key-valid-p (widget-value widget))) 1594 (widget-put widget :error (format "Invalid key: %S" 1595 (widget-value widget))) 1596 widget))) 1597 1598 (provide 'compat-29) 1599 ;;; compat-29.el ends here