diff --git a/admin/notes/tree-sitter/treesit_record_change b/admin/notes/tree-sitter/treesit_record_change new file mode 100644 index 00000000000..bb0f9edc353 --- /dev/null +++ b/admin/notes/tree-sitter/treesit_record_change @@ -0,0 +1,50 @@ +NOTES ON TREESIT_RECORD_CHANGE + +It is vital that Emacs informs tree-sitter of every change made to the +buffer, lest tree-sitter's parse tree would be corrupted/out of sync. + +All buffer changes in Emacs are made through functions in insdel.c +(and casefiddle.c), I augmented functions in those files with calls to +treesit_record_change. Below is a manifest of all the relavent +functions in insdel.c as of Emacs 29: + +Function Calls +---------------------------------------------------------------------- +copy_text (*1) +insert insert_1_both +insert_and_inherit insert_1_both +insert_char insert +insert_string insert +insert_before_markers insert_1_both +insert_before_markers_and_inherit insert_1_both +insert_1_both treesit_record_change +insert_from_string insert_from_string_1 +insert_from_string_before_markers insert_from_string_1 +insert_from_string_1 treesit_record_change +insert_from_gap_1 treesit_record_change +insert_from_gap insert_from_gap_1 +insert_from_buffer treesit_record_change +insert_from_buffer_1 (used by insert_from_buffer) (*2) +replace_range treesit_record_change +replace_range_2 (caller needs to call treesit_r_c) +del_range del_range_1 +del_range_1 del_range_2 +del_range_byte del_range_2 +del_range_both del_range_2 +del_range_2 treesit_record_change + +(*1) This functions is used only to copy from string to string when +used outside of insdel.c, and when used inside insdel.c, the caller +calls treesit_record_change. + +(*2) This function is a static function, and insert_from_buffer is its +only caller. So it should be fine to call treesit_record_change in +insert_from_buffer but not insert_from_buffer_1. I also left a +reminder comment. + + +As for casefiddle.c, do_casify_unibyte_region and +do_casify_multibyte_region modifies buffer, but they are static +functions and are called by casify_region, which calls +treesit_record_change. Other higher-level functions calls +casify_region to do the work. \ No newline at end of file diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi index 2cc45a8805e..a271cb65bdc 100644 --- a/doc/emacs/basic.texi +++ b/doc/emacs/basic.texi @@ -887,19 +887,40 @@ z z z}. The first @kbd{C-x z} repeats the command once, and each subsequent @kbd{z} repeats it once again. @findex repeat-mode +@findex describe-repeat-maps @vindex repeat-exit-key @vindex repeat-exit-timeout - Also you can activate @code{repeat-mode} that temporarily enables a -transient mode with short keys after a limited number of commands. -Currently supported shorter key sequences are @kbd{C-x u u} instead of -@kbd{C-x u C-x u} to undo many changes, @kbd{C-x o o} instead of -@kbd{C-x o C-x o} to switch several windows, @kbd{C-x @{ @{ @} @} ^ ^ -v v} to resize the selected window interactively, @kbd{M-g n n p p} to -navigate @code{next-error} matches, and @kbd{C-x ] ] [ [} to navigate -through pages. Any other key exits transient mode and then is -executed normally. The user option @code{repeat-exit-key} defines an -additional key to exit this transient mode. Also it's possible to -break the repetition chain automatically after some idle time by -customizing the user option @code{repeat-exit-timeout} to specify the -idle time in seconds after which this transient mode will be turned -off. + You can also activate @code{repeat-mode} which allows repeating +commands bound to sequences of two or more keys by typing a single +character. For example, after typing @w{@kbd{C-x u}} (@code{undo}, +@pxref{Undo}) to undo the most recent edits, you can undo many more +edits by typing @w{@kbd{u u u@dots{}}}. Similarly, type @w{@kbd{C-x o +o o@dots{}}} instead of @w{@kbd{C-x o C-x o C-x o@dots{}}} to switch +to the window several windows away. This works by entering a +transient repeating mode after you type the full key sequence that +invokes the command; the single-key shortcuts are shown in the echo +area. + +Only some commands support repetition in @code{repeat-mode}; type +@w{@kbd{M-x describe-repeat-maps @key{RET}}} to see which ones. + +The single-character shortcuts enabled by the transient repeating mode +do not need to be identical: for example, after typing @w{@kbd{C-x +@{}}, either @kbd{@{} or @kbd{@}} or @kbd{^} or @kbd{v}, or any series +that mixes these characters in any order, will resize the selected +window in respective ways. Similarly, after @w{@kbd{M-g n}} or +@kbd{M-g p}, typing any sequence of @kbd{n} and/or @kbd{p} in any mix +will repeat @code{next-error} and @code{previous-error} to navigate in +a @file{*compilation*} or @file{*grep*} buffer (@pxref{Compilation +Mode}). + +Typing any key other than those defined to repeat the previous command +exits the transient repeating mode, and then the key you typed is +executed normally. You can also define a key which will exit the +transient repeating mode @emph{without} executing the key which caused +the exit. To this end, customize the user option +@code{repeat-exit-key} to name a key; one natural value is @key{RET}. +Finally, it's possible to break the repetition chain automatically +after some amount of idle time: customize the user option +@code{repeat-exit-timeout} to specify the idle time in seconds after +which this transient repetition mode will be turned off automatically. diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi index 98f67ddd9d9..3f6a418de1a 100644 --- a/doc/emacs/building.texi +++ b/doc/emacs/building.texi @@ -961,9 +961,7 @@ the fringe of a source buffer to set a breakpoint there. @vindex gud-gdb-command-name To run GDB using just the GUD interaction buffer interface, without these additional features, use @kbd{M-x gud-gdb} (@pxref{Starting -GUD}). You must use this if you want to debug multiple programs -within one Emacs session, as that is currently unsupported by @kbd{M-x -gdb}. +GUD}). Internally, @kbd{M-x gdb} informs GDB that its screen size is unlimited; for correct operation, you must not change GDB's screen @@ -1051,9 +1049,9 @@ to restore only when @code{gdb-show-main} is non-@code{nil}. You may also specify additional GDB-related buffers to display, either in the same frame or a different one. Select the buffers you want by typing @kbd{M-x gdb-display-@var{buffertype}-buffer} or -@kbd{M-x gdb-frame-@var{buffertype}-buffer}, where @var{buffertype} -is the relevant buffer type, such as @samp{breakpoints}. You can do -the same with the menu bar, with the @samp{GDB-Windows} and +@kbd{M-x gdb-frame-@var{buffertype}-buffer}, where @var{buffertype} is +the relevant buffer type, such as @samp{breakpoints} or @samp{io}. +You can do the same from the menu bar, with the @samp{GDB-Windows} and @samp{GDB-Frames} sub-menus of the @samp{GUD} menu. @vindex gdb-max-source-window-count @@ -1273,10 +1271,14 @@ non-@code{nil} value. @node Other GDB Buffers @subsubsection Other GDB Buffers +Other buffers provided by @kbd{M-x gdb} whose display you can +optionally request include: + @table @asis +@findex gdb-display-locals-buffer @item Locals Buffer This buffer displays the values of local variables of the current -frame for simple data types (@pxref{Frame Info, Frame Info, +stack frame for simple data types (@pxref{Frame Info, Frame Info, Information on a frame, gdb, The GNU debugger}). Press @key{RET} or click @kbd{mouse-2} on the value if you want to edit it. @@ -1286,20 +1288,35 @@ you can examine the value of the local variable at point by typing GDB, use @key{RET} or @kbd{mouse-2} on the type description (@samp{[struct/union]} or @samp{[array]}). @xref{Watch Expressions}. -@item Registers Buffer -@findex toggle-gdb-all-registers -This buffer displays the values held by the registers -(@pxref{Registers,,, gdb, The GNU debugger}). Press @key{RET} or -click @kbd{mouse-2} on a register if you want to edit its value. With -GDB 6.4 or later, recently changed register values display with -@code{font-lock-warning-face}. +To display the Locals buffer, type @kbd{M-x gdb-display-locals-buffer}. +@findex gdb-display-io-buffer +@item I/O Buffer +If the program you are debugging uses standard input and output +streams for interaction with the user, or emits a significant amount +of output to its standard output, you may wish to separate its I/O +from interaction with GDB. Use the command @w{@kbd{M-x +gdb-display-io-buffer}} to show a window with a buffer to which Emacs +redirects the input and output from the program you are debugging. + +@findex gdb-display-registers-buffer +@item Registers Buffer +This buffer displays the values held by the registers +(@pxref{Registers,,, gdb, The GNU debugger}). Request the display of +this buffer with the command @kbd{M-x gdb-display-registers-buffer}. +Press @key{RET} or click @kbd{mouse-2} on a register if you want to +edit its value. With GDB 6.4 or later, recently changed register +values display with @code{font-lock-warning-face}. + +@findex gdb-display-disassembly-buffer @item Assembler Buffer The assembler buffer displays the current frame as machine code. An arrow points to the current instruction, and you can set and remove breakpoints as in a source buffer. Breakpoint icons also appear in -the fringe or margin. +the fringe or margin. To request the display of this buffer, use +@kbd{M-x gdb-display-disassembly-buffer}. +@findex gdb-display-memory-buffer @item Memory Buffer The memory buffer lets you examine sections of program memory (@pxref{Memory, Memory, Examining memory, gdb, The GNU debugger}). @@ -1307,7 +1324,8 @@ Click @kbd{mouse-1} on the appropriate part of the header line to change the starting address or number of data items that the buffer displays. Alternatively, use @kbd{S} or @kbd{N} respectively. Click @kbd{mouse-3} on the header line to select the display format or unit -size for these data items. +size for these data items. Use @w{@kbd{M-x +gdb-display-memory-buffer}} to request display of this buffer. @end table When @code{gdb-many-windows} is non-@code{nil}, the locals buffer diff --git a/etc/NEWS.29 b/etc/NEWS.29 index fb211f9b7d0..e3cbeb84d30 100644 --- a/etc/NEWS.29 +++ b/etc/NEWS.29 @@ -563,6 +563,20 @@ The variable 'font-lock-support-mode' is occasionally useful for debugging purposes. It is now a regular variable (instead of a user option) and can be set to nil to disable Just-in-time Lock mode. ++++ +** The 'utf-8-auto' coding-system now produces BOM on encoding. +This is actually a bugfix, since this is how 'utf-8-auto' was +documented from day one; it just didn't behave according to +documentation. It turns out some Lisp programs were using this +coding-system on the wrong assumption that the "auto" part means some +automagic handling of the end-of-line (EOL) format conversion; those +program will now start to fail, because BOM signature in UTF-8 encoded +text is rarely expected. That is the reason we mention this bugfix +here. + +In general, this coding-system should probably never be used for +encoding, only for decoding. + * Changes in Emacs 29.1 @@ -1339,6 +1353,18 @@ dragged. Customize this option to limit the number of entries in the menu "Edit → Paste from Kill Menu". The default is 60. +--- +** New user option 'copy-region-blink-predicate'. +By default, when copying a region with 'kill-ring-save', Emacs only +blinks point and mark when the region is not denoted visually, that +is, when either the region is inactive, or the 'region' face is +indistinguishable from the 'default' face. + +Users who would rather enable blinking unconditionally can now set +this user option to 'always'. To disable blinking unconditionally, +either set this option to 'ignore', or set 'copy-region-blink-delay' +to 0. + +++ ** Performing a pinch gesture on a touchpad now increases the text scale. diff --git a/lisp/bindings.el b/lisp/bindings.el index 99189d2e570..c77b64c05da 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -1011,7 +1011,7 @@ if `inhibit-field-text-motion' is non-nil." ;; no idea whereas to bind it. Any suggestion welcome. -stef ;; (define-key ctl-x-map "U" 'undo-only) (defvar-keymap undo-repeat-map - :doc "Keymap to repeat undo key sequences \\`C-x u u'. Used in `repeat-mode'." + :doc "Keymap to repeat `undo' commands. Used in `repeat-mode'." :repeat t "u" #'undo) @@ -1108,7 +1108,7 @@ if `inhibit-field-text-motion' is non-nil." (define-key ctl-x-map "`" 'next-error) (defvar-keymap next-error-repeat-map - :doc "Keymap to repeat `next-error' key sequences. Used in `repeat-mode'." + :doc "Keymap to repeat `next-error' and `previous-error'. Used in `repeat-mode'." :repeat t "n" #'next-error "M-n" #'next-error @@ -1470,7 +1470,7 @@ if `inhibit-field-text-motion' is non-nil." (define-key ctl-x-map "]" 'forward-page) (defvar-keymap page-navigation-repeat-map - :doc "Keymap to repeat page navigation key sequences. Used in `repeat-mode'." + :doc "Keymap to repeat `forward-page' and `backward-page'. Used in `repeat-mode'." :repeat t "]" #'forward-page "[" #'backward-page) diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 4d39e28fc8e..861cf95b1ff 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -72,34 +72,40 @@ (require 'macroexp) (eval-when-compile (require 'subr-x)) +(defun bytecomp--log-lap-arg (arg) + ;; Convert an argument that may be a LAP operation to something printable. + (cond + ;; Symbols are just stripped of their -byte prefix if any. + ((symbolp arg) + (intern (string-remove-prefix "byte-" (symbol-name arg)))) + ;; Conses are assumed to be LAP ops or tags. + ((and (consp arg) (symbolp (car arg))) + (let* ((head (car arg)) + (tail (cdr arg)) + (op (intern (string-remove-prefix "byte-" (symbol-name head))))) + (cond + ((eq head 'TAG) + (format "%d:" (car tail))) + ((memq head byte-goto-ops) + (format "(%s %d)" op (cadr tail))) + ((memq head byte-constref-ops) + (format "(%s %s)" + (if (eq op 'constant) 'const op) + (if (numberp tail) + (format "" tail) ; closure var reference + (format "%S" (car tail))))) ; actual constant + ;; Ops with an immediate argument. + ((memq op '( stack-ref stack-set call unbind + listN concatN insertN discardN discardN-preserve-tos)) + (format "(%s %S)" op tail)) + ;; Without immediate, print just the symbol. + (t op)))) + ;; Anything else is printed as-is. + (t arg))) + (defun byte-compile-log-lap-1 (format &rest args) (byte-compile-log-1 - (apply #'format-message format - (let (c a) - (mapcar (lambda (arg) - (if (not (consp arg)) - (if (and (symbolp arg) - (string-match "^byte-" (symbol-name arg))) - (intern (substring (symbol-name arg) 5)) - arg) - (if (integerp (setq c (car arg))) - (error "Non-symbolic byte-op %s" c)) - (if (eq c 'TAG) - (setq c arg) - (setq a (cond ((memq c byte-goto-ops) - (car (cdr (cdr arg)))) - ((memq c byte-constref-ops) - (car (cdr arg))) - (t (cdr arg)))) - (setq c (symbol-name c)) - (if (string-match "^byte-." c) - (setq c (intern (substring c 5))))) - (if (eq c 'constant) (setq c 'const)) - (if (and (eq (cdr arg) 0) - (not (memq c '(unbind call const)))) - c - (format "(%s %s)" c a)))) - args))))) + (apply #'format-message format (mapcar #'bytecomp--log-lap-arg args)))) (defmacro byte-compile-log-lap (format-string &rest args) `(and (memq byte-optimize-log '(t byte)) @@ -2036,31 +2042,29 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ;; optimized but sequences like "dup varset TAG1: discard" are not. ;; You may be tempted to change this; resist that temptation. (cond - ;; pop --> - ;; ...including: - ;; const-X pop --> - ;; varref-X pop --> - ;; dup pop --> - ;; - ((and (eq 'byte-discard (car lap1)) + ;; + ;; PUSH(K) discard(N) --> discard(N-K), N>K + ;; PUSH(K) discard(N) --> , N=K + ;; where PUSH(K) is a side-effect-free op such as const, varref, dup + ;; + ((and (memq (car lap1) '(byte-discard byte-discardN)) (memq (car lap0) side-effect-free)) (setq keep-going t) - (setq tmp (aref byte-stack+-info (symbol-value (car lap0)))) - (setq rest (cdr rest)) - (cond ((eql tmp 1) - (byte-compile-log-lap - " %s discard\t-->\t" lap0) - (setq lap (delq lap0 (delq lap1 lap)))) - ((eql tmp 0) - (byte-compile-log-lap - " %s discard\t-->\t discard" lap0) - (setq lap (delq lap0 lap))) - ((eql tmp -1) - (byte-compile-log-lap - " %s discard\t-->\tdiscard discard" lap0) - (setcar lap0 'byte-discard) - (setcdr lap0 0)) - (t (error "Optimizer error: too much on the stack")))) + (let* ((pushes (aref byte-stack+-info (symbol-value (car lap0)))) + (pops (if (eq (car lap1) 'byte-discardN) (cdr lap1) 1)) + (net-pops (- pops pushes))) + (cond ((= net-pops 0) + (byte-compile-log-lap " %s %s\t-->\t" lap0 lap1) + (setcdr rest (cddr rest)) + (setq lap (delq lap0 lap))) + ((> net-pops 0) + (byte-compile-log-lap + " %s %s\t-->\t discard(%d)" lap0 lap1 net-pops) + (setcar rest (if (eql net-pops 1) + (cons 'byte-discard nil) + (cons 'byte-discardN net-pops))) + (setcdr rest (cddr rest))) + (t (error "Optimizer error: too much on the stack"))))) ;; ;; goto*-X X: --> X: ;; @@ -2073,10 +2077,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (setcar lap0 (setq tmp 'byte-discard)) (setcdr lap0 0)) ((error "Depth conflict at tag %d" (nth 2 lap0)))) - (and (memq byte-optimize-log '(t byte)) - (byte-compile-log " (goto %s) %s:\t-->\t%s %s:" - (nth 1 lap1) (nth 1 lap1) - tmp (nth 1 lap1))) + (byte-compile-log-lap " %s %s\t-->\t%s %s" + lap0 lap1 tmp lap1) (setq keep-going t)) ;; ;; varset-X varref-X --> dup varset-X @@ -2165,7 +2167,7 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (eq (cdr lap0) lap2)) ; TAG X (let ((inverse (if (eq 'byte-goto-if-nil (car lap0)) 'byte-goto-if-not-nil 'byte-goto-if-nil))) - (byte-compile-log-lap " %s %s %s:\t-->\t%s %s:" + (byte-compile-log-lap " %s %s %s\t-->\t%s %s" lap0 lap1 lap2 (cons inverse (cdr lap1)) lap2) (setq lap (delq lap0 lap)) @@ -2238,9 +2240,8 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ;; ((and (eq (car lap0) 'TAG) (eq (car lap1) 'TAG)) - (and (memq byte-optimize-log '(t byte)) - (byte-compile-log " adjacent tags %d and %d merged" - (nth 1 lap1) (nth 1 lap0))) + (byte-compile-log-lap " adjacent tags %d and %d merged" + (nth 1 lap1) (nth 1 lap0)) (setq tmp3 lap) (while (setq tmp2 (rassq lap0 tmp3)) (setcdr tmp2 lap1) @@ -2262,8 +2263,7 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (cl-loop for table in byte-compile-jump-tables when (member lap0 (hash-table-values table)) return nil finally return t)) - (and (memq byte-optimize-log '(t byte)) - (byte-compile-log " unused tag %d removed" (nth 1 lap0))) + (byte-compile-log-lap " unused tag %d removed" (nth 1 lap0)) (setq lap (delq lap0 lap) keep-going t)) ;; @@ -2351,6 +2351,40 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (setcar lap0 'byte-return)) (setcdr lap0 (cdr tmp)) (setq keep-going t)))) + + ;; + ;; OP goto(X) Y: OP X: -> Y: OP X: + ;; + ((and (eq (car lap1) 'byte-goto) + (eq (car lap2) 'TAG) + (let ((lap3 (nth 3 rest))) + (and (eq (car lap0) (car lap3)) + (eq (cdr lap0) (cdr lap3)) + (eq (cdr lap1) (nth 4 rest))))) + (byte-compile-log-lap " %s %s %s %s %s\t-->\t%s %s %s" + lap0 lap1 lap2 + (nth 3 rest) (nth 4 rest) + lap2 (nth 3 rest) (nth 4 rest)) + (setcdr rest (cddr rest)) + (setq lap (delq lap0 lap)) + (setq keep-going t)) + + ;; + ;; OP const return --> const return + ;; where OP is side-effect-free (or mere stack manipulation). + ;; + ((and (eq (car lap1) 'byte-constant) + (eq (car (nth 2 rest)) 'byte-return) + (or (memq (car lap0) '( byte-discard byte-discardN + byte-discardN-preserve-tos + byte-stack-set)) + (memq (car lap0) side-effect-free))) + (setq keep-going t) + (setq add-depth 1) ; in case we get rid of too much stack reduction + (setq lap (delq lap0 lap)) + (byte-compile-log-lap " %s %s %s\t-->\t%s %s" + lap0 lap1 (nth 2 rest) lap1 (nth 2 rest))) + ;; ;; goto-*-else-pop X ... X: goto-if-* --> whatever ;; goto-*-else-pop X ... X: discard --> whatever @@ -2459,12 +2493,10 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (memq (car (car tmp)) '(byte-goto byte-goto-if-nil byte-goto-if-not-nil byte-goto-if-nil-else-pop))) - ;; (byte-compile-log-lap " %s %s, %s %s --> moved conditional" - ;; lap0 lap1 (cdr lap0) (car tmp)) (let ((newtag (byte-compile-make-tag))) (byte-compile-log-lap - "%s %s: ... %s: %s\t-->\t%s ... %s:" - lap0 (nth 1 lap1) (nth 1 (cdr lap0)) (car tmp) + " %s %s ... %s %s\t-->\t%s ... %s" + lap0 lap1 (cdr lap0) (car tmp) (cons (cdr (assq (car (car tmp)) '((byte-goto-if-nil . byte-goto-if-not-nil) (byte-goto-if-not-nil . byte-goto-if-nil) @@ -2474,8 +2506,7 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." byte-goto-if-nil-else-pop)))) newtag) - (nth 1 newtag) - ) + newtag) (setcdr tmp (cons (setcdr lap0 newtag) (cdr tmp))) (if (eq (car (car tmp)) 'byte-goto-if-nil-else-pop) ;; We can handle this case but not the -if-not-nil case, @@ -2492,6 +2523,24 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ) (setq keep-going t)) + ;; + ;; discardN-preserve-tos(X) discardN-preserve-tos(Y) + ;; --> discardN-preserve-tos(X+Y) + ;; where stack-set(1) is accepted as discardN-preserve-tos(1) + ;; + ((and (or (eq (car lap0) 'byte-discardN-preserve-tos) + (and (eq (car lap0) 'byte-stack-set) (eql (cdr lap0) 1))) + (or (eq (car lap1) 'byte-discardN-preserve-tos) + (and (eq (car lap1) 'byte-stack-set) (eql (cdr lap1) 1)))) + (setq keep-going t) + (let ((new-op (cons 'byte-discardN-preserve-tos + ;; This happens to work even when either + ;; op is stack-set(1). + (+ (cdr lap0) (cdr lap1))))) + (byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1 new-op) + (setcar rest new-op) + (setcdr rest (cddr rest)))) + ;; ;; stack-set-M [discard/discardN ...] --> discardN-preserve-tos ;; stack-set-M [discard/discardN ...] --> discardN @@ -2530,7 +2579,7 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ;; ;; discardN-preserve-tos return --> return ;; dup return --> return - ;; stack-set-N return --> return ; where N is TOS-1 + ;; stack-set(1) return --> return ;; ((and (eq (car lap1) 'byte-return) (or (memq (car lap0) '(byte-discardN-preserve-tos byte-dup)) @@ -2547,8 +2596,15 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ;; ((and (eq (car lap0) 'byte-goto) (setq tmp (cdr (memq (cdr lap0) lap))) - (memq (caar tmp) '(byte-discard byte-discardN - byte-discardN-preserve-tos))) + (or (memq (caar tmp) '(byte-discard byte-discardN)) + ;; Make sure we don't hoist a discardN-preserve-tos + ;; that really should be merged or deleted instead. + (and (eq (caar tmp) 'byte-discardN-preserve-tos) + (let ((next (cadr tmp))) + (not (or (memq (car next) '(byte-discardN-preserve-tos + byte-return)) + (and (eq (car next) 'byte-stack-set) + (eql (cdr next) 1)))))))) (byte-compile-log-lap " goto-X .. X: \t-->\t%s goto-X.. X: %s Y:" (car tmp) (car tmp)) @@ -2563,11 +2619,16 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." ;; ;; const discardN-preserve-tos ==> discardN const + ;; const stack-set(1) ==> discard const ;; ((and (eq (car lap0) 'byte-constant) - (eq (car lap1) 'byte-discardN-preserve-tos)) + (or (eq (car lap1) 'byte-discardN-preserve-tos) + (and (eq (car lap1) 'byte-stack-set) + (eql (cdr lap1) 1)))) (setq keep-going t) - (let ((newdiscard (cons 'byte-discardN (cdr lap1)))) + (let ((newdiscard (if (eql (cdr lap1) 1) + (cons 'byte-discard nil) + (cons 'byte-discardN (cdr lap1))))) (byte-compile-log-lap " %s %s\t-->\t%s %s" lap0 lap1 newdiscard lap0) (setf (car rest) newdiscard) @@ -2652,16 +2713,6 @@ If FOR-EFFECT is non-nil, the return value is assumed to be of no importance." (setcdr lap1 (+ (if (eq (car lap0) 'byte-discard) 1 (cdr lap0)) (if (eq (car lap1) 'byte-discard) 1 (cdr lap1)))) (setcar lap1 'byte-discardN)) - - ;; - ;; discardN-preserve-tos-X discardN-preserve-tos-Y --> - ;; discardN-preserve-tos-(X+Y) - ;; - ((and (eq (car lap0) 'byte-discardN-preserve-tos) - (eq (car lap1) 'byte-discardN-preserve-tos)) - (setq lap (delq lap0 lap)) - (setcdr lap1 (+ (cdr lap0) (cdr lap1))) - (byte-compile-log-lap " %s %s\t-->\t%s" lap0 lap1 (car rest))) ) (setq rest (cdr rest))) (setq byte-compile-maxdepth (+ byte-compile-maxdepth add-depth))) diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index bacc105a214..367f59e8785 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -182,6 +182,7 @@ to a package-local -loaddefs.el file.") ;; CL (put 'defconstant 'doc-string-elt 3) (put 'defparameter 'doc-string-elt 3) +(put 'defstruct 'doc-string-elt 2) (defvar lisp-doc-string-elt-property 'doc-string-elt "The symbol property that holds the docstring position info.") diff --git a/lisp/eshell/em-cmpl.el b/lisp/eshell/em-cmpl.el index acbf206a3c6..2439f1ed804 100644 --- a/lisp/eshell/em-cmpl.el +++ b/lisp/eshell/em-cmpl.el @@ -402,31 +402,6 @@ to writing a completion function." args) posns))) -(defun eshell--pcomplete-executables () - "Complete amongst a list of directories and executables. - -Wrapper for `pcomplete-executables' or `pcomplete-dirs-or-entries', -depending on the value of `eshell-force-execution'. - -Adds path prefix to candidates independent of `action' value." - ;; `pcomplete-entries' returns filenames without path on `action' to - ;; use current string directory as done in `completion-file-name-table' - ;; when `action' is nil to construct executable candidates. - (let ((table (if eshell-force-execution - (pcomplete-dirs-or-entries nil #'file-readable-p) - (pcomplete-executables)))) - (lambda (string pred action) - (let ((cands (funcall table string pred action))) - (if (eq action t) - (let ((specdir (file-name-directory string))) - (mapcar - (lambda (cand) - (if (stringp cand) - (file-name-concat specdir cand) - cand)) - cands)) - cands))))) - (defun eshell--complete-commands-list () "Generate list of applicable, visible commands." ;; Building the commands list can take quite a while, especially over Tramp @@ -437,11 +412,19 @@ Adds path prefix to candidates independent of `action' value." ;; we complete. Adjust `pcomplete-stub' accordingly! (if (and (> (length pcomplete-stub) 0) (eq (aref pcomplete-stub 0) eshell-explicit-command-char)) - (setq pcomplete-stub (substring pcomplete-stub 1))))) - (completion-table-dynamic - (lambda (filename) - (if (file-name-directory filename) - (eshell--pcomplete-executables) + (setq pcomplete-stub (substring pcomplete-stub 1)))) + (filename (pcomplete-arg))) + ;; Do not use `completion-table-dynamic' when completing a command file + ;; name since it doesn't know about boundaries and would end up doing silly + ;; things like adding a SPC char when completing to "/usr/sbin/". + ;; + ;; If you work on this function, be careful not to reintroduce bug#48995. + (if (file-name-directory filename) + (if eshell-force-execution + (pcomplete-dirs-or-entries nil #'file-readable-p) + (pcomplete-executables)) + (completion-table-dynamic + (lambda (filename) (let* ((paths (eshell-get-path)) (cwd (file-name-as-directory (expand-file-name default-directory))) diff --git a/lisp/faces.el b/lisp/faces.el index cc0b80ac45b..7fe2cccba90 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -304,7 +304,16 @@ If the optional argument FRAME is given, report on face FACE in that frame. If FRAME is t, report on the defaults for face FACE (for new frames). If FRAME is omitted or nil, use the selected frame." (let ((attrs - (delq :inherit (mapcar 'car face-attribute-name-alist))) + ;; The _value_ of :inherit teaches us nothing about how FACE + ;; looks compared to the default face. Instead, we will ask + ;; `face-attribute' to take inheritance into account when + ;; examining other attributes. + (delq :inherit + ;; A difference in extension past EOL only matters when + ;; relevant attributes (such as :background) also + ;; differ from the default; otherwise this difference + ;; is a false positive. + (delq :extend (mapcar 'car face-attribute-name-alist)))) (differs nil)) (while (and attrs (not differs)) (let* ((attr (pop attrs)) diff --git a/lisp/keymap.el b/lisp/keymap.el index de90b03ba64..201a49cef8c 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -290,26 +290,26 @@ See `kbd' for a descripion of KEYS." res))) (defun key-valid-p (keys) - "Say whether KEYS is a valid key. -A key is a string consisting of one or more key strokes. -The key strokes are separated by single space characters. + "Return non-nil if KEYS, a string, is a valid key sequence. +KEYS should be a string consisting of one or more key strokes, +with a single space character separating one key stroke from another. Each key stroke is either a single character, or the name of an -event, surrounded by angle brackets. In addition, any key stroke -may be preceded by one or more modifier keys. Finally, a limited -number of characters have a special shorthand syntax. +event, surrounded by angle brackets . In addition, any +key stroke may be preceded by one or more modifier keys. Finally, +a limited number of characters have a special shorthand syntax. -Here's some example key sequences. +Here are some example of valid key sequences. \"f\" (the key `f') - \"S o m\" (a three key sequence of the keys `S', `o' and `m') - \"C-c o\" (a two key sequence of the keys `c' with the control modifier - and then the key `o') - \"H-\" (the key named \"left\" with the hyper modifier) + \"S o m\" (a three-key sequence of the keys `S', `o' and `m') + \"C-c o\" (a two-key sequence: the key `c' with the control modifier + followed by the key `o') + \"H-\" (the function key named \"left\" with the hyper modifier) \"M-RET\" (the \"return\" key with a meta modifier) \"C-M-\" (the \"space\" key with both the control and meta modifiers) -These are the characters that have shorthand syntax: +These are the characters that have special shorthand syntax: NUL, RET, TAB, LFD, ESC, SPC, DEL. Modifiers have to be specified in this order: @@ -358,7 +358,7 @@ which is This function creates a `keyboard-translate-table' if necessary and then modifies one entry in it. -Both KEY and TO are strings that satisfy `key-valid-p'." +Both KEY and TO should be specified by strings that satisfy `key-valid-p'." (declare (compiler-macro (lambda (form) (keymap--compile-check from to) form))) (keymap--check from) @@ -369,7 +369,7 @@ Both KEY and TO are strings that satisfy `key-valid-p'." (aset keyboard-translate-table (key-parse from) (key-parse to))) (defun keymap-lookup (keymap key &optional accept-default no-remap position) - "Return the binding for command KEY. + "Return the binding for command KEY in KEYMAP. KEY is a string that satisfies `key-valid-p'. If KEYMAP is nil, look up in the current keymaps. If non-nil, it @@ -391,15 +391,15 @@ in the current keymaps. However, if the optional third argument NO-REMAP is non-nil, `keymap-lookup' returns the unmapped command. -If KEY is a key sequence initiated with the mouse, the used keymaps -will depend on the clicked mouse position with regard to the buffer -and possible local keymaps on strings. +If KEY is a mouse gesture, the keymaps used depend on the clicked +mouse position with regards to the buffer, and local keymaps, if any, +on display and overlay strings. If the optional argument POSITION is non-nil, it specifies a mouse position as returned by `event-start' and `event-end', and the lookup occurs in the keymaps associated with it instead of KEY. It can also be a number or marker, in which case the keymap properties at the -specified buffer position instead of point are used." +specified buffer position are used instead of point." (declare (compiler-macro (lambda (form) (keymap--compile-check key) form))) (keymap--check key) (when (and keymap position) @@ -475,7 +475,7 @@ If MESSAGE (and interactively), message the result." (defun define-keymap (&rest definitions) "Create a new keymap and define KEY/DEFINITION pairs as key bindings. -The new keymap is returned. +Return the new keymap. Options can be given as keywords before the KEY/DEFINITION pairs. Available keywords are: diff --git a/lisp/progmodes/c-ts-common.el b/lisp/progmodes/c-ts-common.el index c13b01aae5c..8729cae4ba7 100644 --- a/lisp/progmodes/c-ts-common.el +++ b/lisp/progmodes/c-ts-common.el @@ -194,7 +194,8 @@ comment." (when end-marker (goto-char end-marker) (delete-region (point) (+ end-len (point))) - (insert (make-string end-len ?\s)))))) + (insert (make-string end-len ?\s))) + (goto-char orig-point)))) (defun c-ts-common-comment-setup () "Set up local variables for C-like comment. @@ -280,7 +281,7 @@ special handling from our bracket-counting indent algorithm. This can be nil, meaning such special handling is not needed.") -(defun c-ts-common-statement-offset (node parent &rest _) +(defun c-ts-common-statement-offset (node parent bol &rest _) "This anchor is used for children of a statement inside a block. This function basically counts the number of block nodes (i.e., @@ -292,18 +293,21 @@ To support GNU style, on each block level, this function also checks whether the opening bracket { is on its own line, if so, it adds an extra level, except for the top-level. -PARENT is NODE's parent." +PARENT is NODE's parent, BOL is the beginning of non-whitespace +characters on the current line." (let ((level 0)) + ;; If NODE is a opening/closing bracket on its own line, take off + ;; one level because the code below assumes NODE is a statement + ;; _inside_ a {} block. + (when (and node + (or (string-match-p c-ts-common-indent-block-type-regexp + (treesit-node-type node)) + (save-excursion (goto-char bol) (looking-at-p "}")))) + (cl-decf level)) ;; If point is on an empty line, NODE would be nil, but we pretend ;; there is a statement node. (when (null node) (setq node t)) - ;; If NODE is a opening bracket on its own line, take off one - ;; level because the code below assumes NODE is a statement - ;; _inside_ a {} block. - (when (string-match-p c-ts-common-indent-block-type-regexp - (treesit-node-type node)) - (cl-decf level)) ;; Go up the tree and compute indent level. (while (if (eq node t) (setq node parent) @@ -321,9 +325,9 @@ PARENT is NODE's parent." (treesit-node-parent node)))) ;; Case (2). (and parent-type - (or (string-match-p - c-ts-common-indent-block-type-regexp - parent-type)))) + (string-match-p + c-ts-common-indent-block-type-regexp + parent-type))) nil) ;; Add a level. ((looking-back (rx bol (* whitespace)) @@ -350,13 +354,6 @@ the bracket in the body." (1+ level) level))) -(defun c-ts-mode--close-bracket-offset (node parent &rest _) - "Offset for the closing bracket, NODE. -It's basically one level less that the statements in the block. -PARENT is NODE's parent." - (- (c-ts-common-statement-offset node parent) - (symbol-value c-ts-common-indent-offset))) - (provide 'c-ts-common) ;;; c-ts-common.el ends here diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 884fe6c3f15..586849e9a3d 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -100,12 +100,11 @@ the value of SYM in `c-ts-mode' and `c++-ts-mode' buffers to VAL." (setq-local treesit-simple-indent-rules (treesit--indent-rules-optimize (c-ts-mode--get-indent-style - (if (eq major-mode 'c-ts-mode) 'c 'cpp)))))) + (if (derived-mode-p 'c-ts-mode) 'c 'cpp)))))) res) (let ((buffer (car buffers))) (with-current-buffer buffer - ;; FIXME: Should we use `derived-mode-p' here? - (if (or (eq major-mode 'c-ts-mode) (eq major-mode 'c++-ts-mode)) + (if (derived-mode-p 'c-ts-mode 'c++-ts-mode) (loop (append res (list buffer)) (cdr buffers)) (loop res (cdr buffers)))))))) @@ -134,24 +133,33 @@ MODE is either `c' or `cpp'." (alist-get c-ts-mode-indent-style (c-ts-mode--indent-styles mode))))) `((,mode ,@style)))) -(defun c-ts-mode-set-style () - "Set the indent style of C/C++ modes globally. +(defun c-ts-mode--prompt-for-style () + "Prompt for a indent style and return the symbol for it." + (let ((mode (if (derived-mode-p 'c-ts-mode) 'c 'c++))) + (intern + (completing-read + "Style: " + (mapcar #'car (c-ts-mode--indent-styles mode)) + nil t nil nil "gnu")))) + +(defun c-ts-mode-set-style (style) + "Set the indent style of C/C++ modes globally to STYLE. This changes the current indent style of every C/C++ buffer and the default C/C++ indent style in this Emacs session." - (interactive) - ;; FIXME: Should we use `derived-mode-p' here? - (or (eq major-mode 'c-ts-mode) (eq major-mode 'c++-ts-mode) - (error "Buffer %s is not a c-ts-mode (c-ts-mode-set-style)" - (buffer-name))) - (c-ts-mode--indent-style-setter - 'c-ts-mode-indent-style - ;; NOTE: We can probably use the interactive form for this. - (intern - (completing-read - "Select style: " - (mapcar #'car (c-ts-mode--indent-styles (if (eq major-mode 'c-ts-mode) 'c 'cpp))) - nil t nil nil "gnu")))) + (interactive (list (c-ts-mode--prompt-for-style))) + (c-ts-mode--indent-style-setter 'c-ts-mode-indent-style style)) + +(defun c-ts-mode-set-local-style (style) + "Set the C/C++ indent style of the current buffer to STYLE." + (interactive (list (c-ts-mode--prompt-for-style))) + (if (not (derived-mode-p 'c-ts-mode 'c++-ts-mode)) + (user-error "The current buffer is not in `c-ts-mode' nor `c++-ts-mode'") + (setq-local c-ts-mode-indent-style style) + (setq treesit-simple-indent-rules + (treesit--indent-rules-optimize + (c-ts-mode--get-indent-style + (if (derived-mode-p 'c-ts-mode) 'c 'cpp)))))) ;;; Syntax table @@ -254,12 +262,16 @@ MODE is either `c' or `cpp'." ;; int[5] a = { 0, 0, 0, 0 }; ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset) + ;; Statement in enum. ((parent-is "enumerator_list") point-min c-ts-common-statement-offset) + ;; Statement in struct and union. ((parent-is "field_declaration_list") point-min c-ts-common-statement-offset) - ;; {} blocks. - ((node-is "}") point-min c-ts-mode--close-bracket-offset) + ;; Statement in {} blocks. ((parent-is "compound_statement") point-min c-ts-common-statement-offset) + ;; Closing bracket. + ((node-is "}") point-min c-ts-common-statement-offset) + ;; Opening bracket. ((node-is "compound_statement") point-min c-ts-common-statement-offset) ,@(when (eq mode 'cpp) @@ -824,6 +836,8 @@ in your configuration." (c-ts-mode--get-indent-style 'c)) ;; Font-lock. (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c)) + ;; Navigation. + (setq-local treesit-defun-tactic 'top-level) (treesit-major-mode-setup))) ;;;###autoload diff --git a/lisp/progmodes/cc-engine.el b/lisp/progmodes/cc-engine.el index f1e93c1c23c..86bc35baa7c 100644 --- a/lisp/progmodes/cc-engine.el +++ b/lisp/progmodes/cc-engine.el @@ -10146,6 +10146,24 @@ This function might do hidden buffer changes." ;; This identifier is bound only in the inner let. '(setq start id-start)))) +(defmacro c-fdoc-assymetric-space-about-asterisk () + ;; We've got a "*" at `id-start' between two identifiers, the first at + ;; `type-start'. Return non-nil when there is either whitespace between the + ;; first id and the "*" or between the "*" and the second id, but not both. + `(let ((space-before-id + (save-excursion + (goto-char id-start) ; Position of "*". + (and (> (skip-chars-forward "* \t\n\r") 0) + (memq (char-before) '(?\ ?\t ?\n ?\r))))) + (space-after-type + (save-excursion + (goto-char type-start) + (and (c-forward-type nil t) + (or (eolp) + (memq (char-after) '(?\ ?\t))))))) + (not (eq (not space-before-id) + (not space-after-type))))) + (defun c-forward-decl-or-cast-1 (preceding-token-end context last-cast-end &optional inside-macro) ;; Move forward over a declaration or a cast if at the start of one. @@ -11166,19 +11184,25 @@ This function might do hidden buffer changes." ;; CASE 16 (when (and got-prefix-before-parens at-type - (or at-decl-end (looking-at "=[^=]")) (memq context '(nil top)) (or (not got-suffix) at-decl-start)) ;; Got something like "foo * bar;". Since we're not inside ;; an arglist it would be a meaningless expression because ;; the result isn't used. We therefore choose to recognize - ;; it as a declaration. We only allow a suffix (which makes - ;; the construct look like a function call) when - ;; `at-decl-start' provides additional evidence that we do - ;; have a declaration. + ;; it as a declaration when there's "symmetrical WS" around + ;; the "*" or the flag `c-assymetry-fontification-flag' is + ;; not set. We only allow a suffix (which makes the + ;; construct look like a function call) when `at-decl-start' + ;; provides additional evidence that we do have a + ;; declaration. (setq maybe-expression t) - (throw 'at-decl-or-cast t)) + (when (or (not c-asymmetry-fontification-flag) + (looking-at "=[^=]") + (c-fdoc-assymetric-space-about-asterisk)) + (when (eq at-type 'maybe) + (setq unsafe-maybe t)) + (throw 'at-decl-or-cast t))) ;; CASE 17 (when (and (or got-suffix-after-parens @@ -11197,24 +11221,12 @@ This function might do hidden buffer changes." got-prefix-before-parens at-type (or (not got-suffix) - at-decl-start)) - (let ((space-before-id - (save-excursion - (goto-char id-start) ; Position of "*". - (and (> (skip-chars-forward "* \t\n\r") 0) - (memq (char-before) '(?\ ?\t ?\n ?\r))))) - (space-after-type - (save-excursion - (goto-char type-start) - (and (c-forward-type nil t) - (or (eolp) - (memq (char-after) '(?\ ?\t))))))) - (when (not (eq (not space-before-id) - (not space-after-type))) - (when (eq at-type 'maybe) - (setq unsafe-maybe t)) - (setq maybe-expression t) - (throw 'at-decl-or-cast t))))) + at-decl-start) + (c-fdoc-assymetric-space-about-asterisk)) + (when (eq at-type 'maybe) + (setq unsafe-maybe t)) + (setq maybe-expression t) + (throw 'at-decl-or-cast t))) ;; CASE 18 (when (and at-decl-end diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index c160e6ad1df..b878986d7a4 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -256,10 +256,14 @@ This has effect only if `search-invisible' is set to `open'." (defvar hs-special-modes-alist (mapcar #'purecopy '((c-mode "{" "}" "/[*/]" nil nil) + (c-ts-mode "{" "}" "/[*/]" nil nil) (c++-mode "{" "}" "/[*/]" nil nil) + (c++-ts-mode "{" "}" "/[*/]" nil nil) (bibtex-mode ("@\\S(*\\(\\s(\\)" 1)) (java-mode "{" "}" "/[*/]" nil nil) + (java-ts-mode "{" "}" "/[*/]" nil nil) (js-mode "{" "}" "/[*/]" nil) + (js-ts-mode "{" "}" "/[*/]" nil) (mhtml-mode "{\\|<[^/>]*?" "}\\|]*[^/]>" "