commit 5e5e74b17ac77e93bce7f2158c9d2af5ca2a2ff7 (HEAD, refs/remotes/origin/master) Author: Eshel Yaron Date: Wed Apr 3 08:35:18 2024 +0200 ; Autoload 'global-completion-preview-mode' * lisp/completion-preview.el (global-completion-preview-mode): Add autoload cookie. diff --git a/lisp/completion-preview.el b/lisp/completion-preview.el index 6abdfed51a3..a86c1ba1cc9 100644 --- a/lisp/completion-preview.el +++ b/lisp/completion-preview.el @@ -415,6 +415,7 @@ cycles backward." (remove-hook 'post-command-hook #'completion-preview--post-command t) (completion-preview-active-mode -1))) +;;;###autoload (define-globalized-minor-mode global-completion-preview-mode completion-preview-mode completion-preview-mode :predicate '((not minibuffer-mode special-mode) t)) commit 7563d571463838970418fca1f81237c389c258b1 Author: Randy Taylor Date: Tue Apr 2 21:29:55 2024 -0400 ; * src/json.c (Fjson_insert): Fix typo in doc (bug#70156). diff --git a/src/json.c b/src/json.c index b4ea84206fe..3c4d90d8d0a 100644 --- a/src/json.c +++ b/src/json.c @@ -637,7 +637,7 @@ DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, doc: /* Insert the JSON representation of OBJECT before point. This is the same as (insert (json-serialize OBJECT ...)), but potentially faster, and with the difference that Unicode characters are inserted as -themselves into multibyte buffers, and as UTF-8 byte sequencess into +themselves into multibyte buffers, and as UTF-8 byte sequences into unibyte buffers. See the function `json-serialize' for allowed values of OBJECT and ARGS. usage: (json-insert OBJECT &rest ARGS) */) commit 5eedf41b31ad8b471ffb430d26f15c66139ff7eb Author: Eli Zaretskii Date: Tue Apr 2 20:43:29 2024 +0300 ; * src/json.c (Fjson_insert): Doc fix. diff --git a/src/json.c b/src/json.c index 45dcdde98ea..b4ea84206fe 100644 --- a/src/json.c +++ b/src/json.c @@ -637,7 +637,8 @@ DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, doc: /* Insert the JSON representation of OBJECT before point. This is the same as (insert (json-serialize OBJECT ...)), but potentially faster, and with the difference that Unicode characters are inserted as -themselves into multibyte buffers, as UTF-8 bytes into unibyte buffers. +themselves into multibyte buffers, and as UTF-8 byte sequencess into +unibyte buffers. See the function `json-serialize' for allowed values of OBJECT and ARGS. usage: (json-insert OBJECT &rest ARGS) */) (ptrdiff_t nargs, Lisp_Object *args) commit c3781bf59edcd67769d068b5bc8f724e890d8e54 Author: Juri Linkov Date: Tue Apr 2 20:17:41 2024 +0300 New functions to set and use context of window points (bug#33871) * lisp/dired.el (dired-mode): Set buffer-local 'window-point-context-set-function' to remember 'dired-filename' or 'position' in the window with the Dired buffer. Set buffer-local 'window-point-context-use-function' to restore the remembered position of the window point. * lisp/tab-bar.el (tab-bar--tab): Use window-point-context-set. (tab-bar-select-restore-context): New user option. (tab-bar-select-tab): Use window-point-context-use. * lisp/window.el: Add '(context . writable)' to 'window-persistent-parameters'. (window-point-context-set, window-point-context-use): New functions. (window-point-context-set-default-function) (window-point-context-use-default-function): New functions. (window-point-context-set-function) (window-point-context-use-function): New variables. diff --git a/etc/NEWS b/etc/NEWS index 47275db47e3..2654d9d7995 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -309,8 +309,24 @@ It specifies how 'set-window-configuration' and 'window-state-put' should proceed with windows whose buffer was killed after the corresponding configuration or state was recorded. +*** New variable 'window-point-context-set-function'. +It can be used to set a context for window point in all windows by +'window-point-context-set' before calling 'current-window-configuration' +and 'window-state-get'. Then later another new variable +'window-point-context-use-function' can be used by +'window-point-context-use' after 'set-window-configuration' and +'window-state-put' to restore positions of window points +according to the context stored in a window parameter. + ** Tab Bars and Tab Lines +--- +*** New user option 'tab-bar-select-restore-context'. +It uses 'window-point-context-set' to save contexts where +window points were located before switching away from the tab, +and 'window-point-context-use' to restore positions of window +points after switching back to that tab. + --- *** New user option 'tab-bar-select-restore-windows'. It defines what to do with windows whose buffer was killed diff --git a/lisp/dired.el b/lisp/dired.el index 9e3b888df14..f0113c002a4 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2743,6 +2743,26 @@ Keybindings: '(dired-font-lock-keywords t nil nil beginning-of-line)) (setq-local desktop-save-buffer 'dired-desktop-buffer-misc-data) (setq-local grep-read-files-function #'dired-grep-read-files) + (setq-local window-point-context-set-function + (lambda (w) + (with-current-buffer (window-buffer w) + (let ((point (window-point w))) + (save-excursion + (goto-char point) + (if-let ((f (dired-get-filename nil t))) + `((dired-filename . ,f)) + `((position . ,(point))))))))) + (setq-local window-point-context-use-function + (lambda (w context) + (with-current-buffer (window-buffer w) + (let ((point (window-point w))) + (save-excursion + (if-let ((f (alist-get 'dired-filename context))) + (dired-goto-file f) + (when-let ((p (alist-get 'position context))) + (goto-char p))) + (setq point (point))) + (set-window-point w point))))) (setq dired-switches-alist nil) (hack-dir-local-variables-non-file-buffer) ; before sorting (dired-sort-other dired-actual-switches t) diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index fa22500a04e..05631c3c8f3 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -1292,6 +1292,9 @@ tab bar might wrap to the second line when it shouldn't.") frame 'buffer-list))) (bbl (seq-filter #'buffer-live-p (frame-parameter frame 'buried-buffer-list)))) + (when tab-bar-select-restore-context + (window-point-context-set)) + `(tab (name . ,(if tab-explicit-name (alist-get 'name tab) @@ -1442,6 +1445,16 @@ if it was visiting a file." (setq buffer-read-only t) (set-window-buffer window new-buffer)))))) +(defcustom tab-bar-select-restore-context t + "If this is non-nil, try to restore window points from their contexts. +This will try to find the same position in every window where point was +before switching away from this tab. After selecting this tab, +point in every window will be moved to its previous position +in the buffer even when the buffer was modified." + :type 'boolean + :group 'tab-bar + :version "30.1") + (defvar tab-bar-minibuffer-restore-tab nil "Tab number for `tab-bar-minibuffer-restore-tab'.") @@ -1539,6 +1552,9 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar." (select-window (get-mru-window))) (window-state-put ws nil 'safe))) + (when tab-bar-select-restore-context + (window-point-context-use)) + ;; Select the minibuffer when it was active before switching tabs (when (and minibuffer-was-active (active-minibuffer-window)) (select-window (active-minibuffer-window))) diff --git a/lisp/window.el b/lisp/window.el index 46de1819c69..3867f6fa6ef 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -10838,6 +10838,78 @@ displaying that processes's buffer." (set-process-window-size process (cdr size) (car size)))))))))) (add-hook 'window-configuration-change-hook 'window--adjust-process-windows) + + +;;; Window point context + +(defun window-point-context-set () + "Set context near the window point. +Call function specified by `window-point-context-set-function' for every +live window on the selected frame with that window as sole argument. +The function called is supposed to return a context of the window's point +that can be later used as argument for `window-point-context-use-function'. +Remember the returned context in the window parameter `context'." + (walk-windows + (lambda (w) + (when-let ((fn (buffer-local-value 'window-point-context-set-function + (window-buffer w))) + ((functionp fn)) + (context (funcall fn w))) + (set-window-parameter w 'context (cons (buffer-name) context)))) + 'nomini)) + +(defun window-point-context-use () + "Use context to relocate the window point. +Call function specified by `window-point-context-use-function' to move the +window point according to the previously saved context. For every live +window on the selected frame this function is called with two arguments: +the window and the context data structure saved by +`window-point-context-set-function' in the window parameter `context'. +The function called is supposed to set the window point to the location +found by the provided context." + (walk-windows + (lambda (w) + (when-let ((fn (buffer-local-value 'window-point-context-use-function + (window-buffer w))) + ((functionp fn)) + (context (window-parameter w 'context)) + ((equal (buffer-name) (car context)))) + (funcall fn w (cdr context)) + (set-window-parameter w 'context nil))) + 'nomini)) + +(add-to-list 'window-persistent-parameters '(context . writable)) + +(defun window-point-context-set-default-function (w) + "Set context of file buffers to the front and rear strings." + (with-current-buffer (window-buffer w) + (when buffer-file-name + (let ((point (window-point w))) + `((front-context-string + . ,(buffer-substring-no-properties + point (min (+ point 16) (point-max)))) + (rear-context-string + . ,(buffer-substring-no-properties + point (max (- point 16) (point-min))))))))) + +(defun window-point-context-use-default-function (w context) + "Restore context of file buffers by the front and rear strings." + (with-current-buffer (window-buffer w) + (let ((point (window-point w))) + (save-excursion + (goto-char point) + (when-let ((f (alist-get 'front-context-string context)) + ((search-forward f (point-max) t))) + (goto-char (match-beginning 0)) + (when-let ((r (alist-get 'rear-context-string context)) + ((search-backward r (point-min) t))) + (goto-char (match-end 0)) + (setq point (point))))) + (set-window-point w point)))) + +(defvar window-point-context-set-function 'window-point-context-set-default-function) +(defvar window-point-context-use-function 'window-point-context-use-default-function) + ;; Some of these are in tutorial--default-keys, so update that if you ;; change these. commit 617debf67392946b4b42fdf364c69da6f094a840 Author: Mattias Engdegård Date: Tue Apr 2 18:58:09 2024 +0200 Fix json-insert unibyte buffer bug (bug#70007) Previously, a unibyte target buffer could be put in an incorrect state if json-insert was used to insert non-ASCII characters. * src/json.c (Fjson_insert): Simplify. Don't attempt to decode the data being inserted: it is guaranteed to be correct UTF-8 and is correct for both unibyte and multibyte buffers. * test/src/json-tests.el (json-serialize/roundtrip) (json-serialize/roundtrip-scalars): Extend tests. diff --git a/src/json.c b/src/json.c index 0b4414956ee..45dcdde98ea 100644 --- a/src/json.c +++ b/src/json.c @@ -646,8 +646,6 @@ usage: (json-insert OBJECT &rest ARGS) */) json_out_t jo; json_serialize (&jo, args[0], nargs - 1, args + 1); - /* FIXME: All the work below just to insert a string into a buffer? */ - prepare_to_modify_buffer (PT, PT, NULL); move_gap_both (PT, PT_BYTE); if (GAP_SIZE < jo.size) @@ -657,48 +655,22 @@ usage: (json-insert OBJECT &rest ARGS) */) /* No need to keep allocation beyond this point. */ unbind_to (count, Qnil); - ptrdiff_t inserted = 0; + bool ub_buffer = NILP (BVAR (current_buffer, enable_multibyte_characters)); ptrdiff_t inserted_bytes = jo.size; + ptrdiff_t inserted = ub_buffer ? jo.size : jo.size - jo.chars_delta; + eassert (inserted > 0); - /* If required, decode the stuff we've read into the gap. */ - struct coding_system coding; - /* JSON strings are UTF-8 encoded strings. */ - setup_coding_system (Qutf_8_unix, &coding); - coding.dst_multibyte = !NILP (BVAR (current_buffer, - enable_multibyte_characters)); - if (CODING_MAY_REQUIRE_DECODING (&coding)) - { - /* Now we have all the new bytes at the beginning of the gap, - but `decode_coding_gap` needs them at the end of the gap, so - we need to move them. */ - memmove (GAP_END_ADDR - inserted_bytes, GPT_ADDR, inserted_bytes); - decode_coding_gap (&coding, inserted_bytes); - inserted = coding.produced_char; - } - else - { - /* Make the inserted text part of the buffer, as unibyte text. */ - eassert (NILP (BVAR (current_buffer, enable_multibyte_characters))); - insert_from_gap_1 (inserted_bytes, inserted_bytes, false); - - /* The target buffer is unibyte, so we don't need to decode. */ - invalidate_buffer_caches (current_buffer, - PT, PT + inserted_bytes); - adjust_after_insert (PT, PT_BYTE, - PT + inserted_bytes, - PT_BYTE + inserted_bytes, - inserted_bytes); - inserted = inserted_bytes; - } + insert_from_gap_1 (inserted, inserted_bytes, false); + invalidate_buffer_caches (current_buffer, PT, PT + inserted); + adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted_bytes, + inserted); /* Call after-change hooks. */ signal_after_change (PT, 0, inserted); - if (inserted > 0) - { - update_compositions (PT, PT, CHECK_BORDER); - /* Move point to after the inserted text. */ - SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes); - } + + update_compositions (PT, PT, CHECK_BORDER); + /* Move point to after the inserted text. */ + SET_PT_BOTH (PT + inserted, PT_BYTE + inserted_bytes); return Qnil; } diff --git a/test/src/json-tests.el b/test/src/json-tests.el index 8b730ef8c90..ebac70fb1c7 100644 --- a/test/src/json-tests.el +++ b/test/src/json-tests.el @@ -27,28 +27,44 @@ (require 'map) (require 'subr-x) -(declare-function json-serialize "json.c" (object &rest args)) -(declare-function json-insert "json.c" (object &rest args)) -(declare-function json-parse-string "json.c" (string &rest args)) -(declare-function json-parse-buffer "json.c" (&rest args)) - (define-error 'json-tests--error "JSON test error") (ert-deftest json-serialize/roundtrip () ;; The noncharacter U+FFFF should be passed through, ;; cf. https://www.unicode.org/faq/private_use.html#noncharacters. - (let ((lisp [:null :false t 0 123 -456 3.75 "abc\uFFFFαβγ𝔸𝐁𝖢\"\\"]) - (json "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]")) - (should (equal (json-serialize lisp) json)) + (let* ((lisp [:null :false t 0 123 -456 3.75 "abc\uFFFFαβγ𝔸𝐁𝖢\"\\"]) + (json + "[null,false,true,0,123,-456,3.75,\"abc\uFFFFαβγ𝔸𝐁𝖢\\\"\\\\\"]") + (json-bytes (encode-coding-string json 'utf-8))) + (should (equal (json-serialize lisp) json)) ; or `json-bytes'? (with-temp-buffer + ;; multibyte buffer (json-insert lisp) (should (equal (buffer-string) json)) + (should (equal (point) (1+ (length json)))) + (should (eobp))) + (with-temp-buffer + ;; unibyte buffer + (set-buffer-multibyte nil) + (json-insert lisp) + (should (equal (buffer-string) json-bytes)) + (should (equal (point) (1+ (length json-bytes)))) (should (eobp))) (should (equal (json-parse-string json) lisp)) (with-temp-buffer + ;; multibyte buffer (insert json) (goto-char 1) (should (equal (json-parse-buffer) lisp)) + (should (equal (point) (1+ (length json)))) + (should (eobp))) + (with-temp-buffer + ;; unibyte buffer + (set-buffer-multibyte nil) + (insert json-bytes) + (goto-char 1) + (should (equal (json-parse-buffer) lisp)) + (should (equal (point) (1+ (length json-bytes)))) (should (eobp))))) (ert-deftest json-serialize/roundtrip-scalars () @@ -71,11 +87,22 @@ (json-insert lisp) (should (equal (buffer-string) json)) (should (eobp))) + (with-temp-buffer + (set-buffer-multibyte nil) + (json-insert lisp) + (should (equal (buffer-string) (encode-coding-string json 'utf-8))) + (should (eobp))) (should (equal (json-parse-string json) lisp)) (with-temp-buffer (insert json) (goto-char 1) (should (equal (json-parse-buffer) lisp)) + (should (eobp))) + (with-temp-buffer + (set-buffer-multibyte nil) + (insert (encode-coding-string json 'utf-8)) + (goto-char 1) + (should (equal (json-parse-buffer) lisp)) (should (eobp))))))) (ert-deftest json-serialize/object () commit 0670032c0dc03c0df6e11b5411b8d10fc61a7d41 Author: Mattias Engdegård Date: Tue Apr 2 17:51:38 2024 +0200 json-insert doc fixes * src/json.c (Fjson_insert): Precise the behaviour when the current buffer is multibyte and unibyte, respectively. * doc/lispref/text.texi (Parsing JSON): Refer to the right function. diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 90e2c6ce882..18f0ee88fe5 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -5833,7 +5833,7 @@ keyword @code{false}. It defaults to the symbol @code{:false}. @defun json-insert object &rest args This function inserts the JSON representation of @var{object} into the current buffer before point. The argument @var{args} are interpreted -as in @code{json-parse-string}. +as in @code{json-serialize}. @end defun @defun json-parse-string string &rest args diff --git a/src/json.c b/src/json.c index c3244ad04d2..0b4414956ee 100644 --- a/src/json.c +++ b/src/json.c @@ -636,8 +636,9 @@ DEFUN ("json-insert", Fjson_insert, Sjson_insert, 1, MANY, NULL, doc: /* Insert the JSON representation of OBJECT before point. This is the same as (insert (json-serialize OBJECT ...)), but potentially -faster. See the function `json-serialize' for allowed values of -OBJECT and ARGS. +faster, and with the difference that Unicode characters are inserted as +themselves into multibyte buffers, as UTF-8 bytes into unibyte buffers. +See the function `json-serialize' for allowed values of OBJECT and ARGS. usage: (json-insert OBJECT &rest ARGS) */) (ptrdiff_t nargs, Lisp_Object *args) { commit 9af533dc751e5220a545ca7e15456992cbbfab98 Author: Juri Linkov Date: Tue Apr 2 19:51:51 2024 +0300 New condition/action entry 'category' for 'display-buffer' (bug#69983) * doc/lispref/windows.texi (Choosing Window): Provide an example of using '(category . comint)' in the condition of 'display-buffer-alist' and in the action of 'display-buffer'. (Buffer Display Action Alists): Add a new action alist entry 'category'. * lisp/subr.el (buffer-match-p): Add a new condition 'category'. * lisp/window.el (display-buffer): Document a new action alist entry 'category'. diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index eef05d94fdb..d3d6b854461 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -2638,6 +2638,29 @@ use @code{derived-mode} or @code{major-mode} as condition, @code{buffer-match-p} could fail to report a match if @code{display-buffer} is called before the major mode of the buffer is set. + +If the caller of @code{display-buffer} passes a category as a symbol +in its @var{action} argument, then you can use the same category in +@code{display-buffer-alist} to match buffers with different names, +for example: + +@example +@group +(setopt + display-buffer-alist + (cons '((category . comint) (display-buffer-same-window)) + display-buffer-alist)) + +(display-buffer (get-buffer-create "*my-shell*") + '(nil (category . comint))) +@end group +@end example + +Regardless of the displayed buffer's name the caller defines a category +as a symbol @code{comint}. Then @code{display-buffer-alist} matches +this category for all buffers displayed with the same category. +This avoids the need to construct a complex regular expression +that matches a buffer name. @end defopt @defopt display-buffer-base-action @@ -3354,6 +3377,13 @@ If the value is @code{nil}, the buffer selected by such functions as @code{pop-to-buffer} is deselected, and the window that was selected before calling this function will remain selected regardless of which windows were selected afterwards within this command. + +@vindex category@r{, a buffer display action alist entry} +@item category +If the caller of @code{display-buffer} passes an alist entry +@code{(category . symbol)} in its @var{action} argument, then you can +match the displayed buffer by using the same category in the condition +part of @code{display-buffer-alist} entries. @end table By convention, the entries @code{window-height}, @code{window-width} diff --git a/etc/NEWS b/etc/NEWS index 4b0f148dc5d..47275db47e3 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -291,6 +291,14 @@ right-aligned to is controlled by the new user option ** Windows ++++ +*** New action alist entry 'category' for 'display-buffer'. +If the caller of 'display-buffer' passes '(category . symbol)' +in its 'action' argument, you can match the displayed buffer +by adding '(category . symbol)' to the condition part of +'display-buffer-alist' entries. + ++++ *** New action alist entry 'post-command-select-window' for 'display-buffer'. It specifies whether the window of the displayed buffer should be selected or deselected at the end of executing the current command. diff --git a/lisp/subr.el b/lisp/subr.el index 50487e2c734..753c0144ca5 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -7475,6 +7475,9 @@ CONDITION is either: * `major-mode': the buffer matches if the buffer's major mode is eq to the cons-cell's cdr. Prefer using `derived-mode' instead when both can work. + * `category': the buffer matches a category as a symbol if + the caller of `display-buffer' provides `(category . symbol)' + in its action argument. * `not': the cadr is interpreted as a negation of a condition. * `and': the cdr is a list of recursive conditions, that all have to be met. @@ -7503,6 +7506,8 @@ CONDITION is either: (push condition buffer-match-p--past-warnings)) (apply condition buffer-or-name (if args nil '(nil))))))) + (`(category . ,category) + (eq (alist-get 'category (cdar args)) category)) (`(major-mode . ,mode) (eq (buffer-local-value 'major-mode buffer) diff --git a/lisp/window.el b/lisp/window.el index df55a7ca673..46de1819c69 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -7856,6 +7856,10 @@ Action alist entries are: window that was selected before calling this function will remain selected regardless of which windows were selected afterwards within this command. + `category' -- If the caller of `display-buffer' passes an alist entry + `(category . symbol)' in its action argument, then you can match + the displayed buffer by using the same category in the condition + part of `display-buffer-alist' entries. The entries `window-height', `window-width', `window-size' and `preserve-size' are applied only when the window used for