From 990f4027131e4ff7d83db23d82de0d47f64d288e Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Thu, 23 Feb 2023 10:23:41 +0200 Subject: [PATCH 1/8] * doc/emacs/maintaining.texi: More mentions of marked files in Dired. (Basic VC Editing, Old Revisions, VC Change Log): Add more mentions of ability to include the marked files to the VC fileset in Dired (bug#61712). --- doc/emacs/maintaining.texi | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 5191bb2918d..8aee3380b71 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -471,7 +471,9 @@ collection of one or more files that a VC operation acts on. When you type VC commands in a buffer visiting a version-controlled file, the VC fileset is simply that one file. When you type them in a VC Directory buffer, and some files in it are marked, the VC fileset -consists of the marked files (@pxref{VC Directory Mode}). +consists of the marked files (@pxref{VC Directory Mode}). The VC +fileset also consists of the marked files in a Dired buffer +(@pxref{Dired}). On modern changeset-based version control systems (@pxref{VCS Changesets}), VC commands handle multi-file VC filesets as a group. @@ -840,7 +842,7 @@ details. If you invoke @kbd{C-x v =} or @kbd{C-u C-x v =} from a Dired buffer (@pxref{Dired}), the file listed on the current line is treated as the -current VC fileset. +current VC fileset. The VC fileset can also include several marked files. @ifnottex @findex vc-ediff @@ -1009,7 +1011,7 @@ and the maximum number of revisions to display. If you call @kbd{C-x v l} from a VC Directory buffer (@pxref{VC Directory Mode}) or a Dired buffer (@pxref{Dired}), it applies to the -file listed on the current line. +file listed on the current line or to all the marked files. @kindex C-x v L @findex vc-print-root-log From 5286111ea1fe442d861cd4a940d86db3fd832139 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 23 Feb 2023 14:26:06 +0200 Subject: [PATCH 2/8] Improve documentation of VC commands, including in Dired * doc/emacs/dired.texi (Operating on Files): * doc/emacs/maintaining.texi (VC Change Log, VC Directory Mode) (Version Control, Basic VC Editing): Document VC command invocation from Dired buffers. Improve documentation of vc-log commands. * lisp/dired-aux.el (dired-vc-next-action): * lisp/vc/vc.el (vc-print-log, vc-log-search, vc-log-mergebase) (vc-log-view-type, vc-print-root-log, vc-next-action): Doc fixes. --- doc/emacs/dired.texi | 4 +++ doc/emacs/maintaining.texi | 57 +++++++++++++++++++++++++++++--------- lisp/dired-aux.el | 21 ++++++++++---- lisp/vc/vc.el | 54 +++++++++++++++++++++++++----------- 4 files changed, 101 insertions(+), 35 deletions(-) diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 3f2c8d4afdf..77c4e09c826 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -715,6 +715,10 @@ Otherwise, the command operates on the current file only. Certain other Dired commands, such as @kbd{!} and the @samp{%} commands, use the same conventions to decide which files to work on. + In addition to Dired commands described here, you can also invoke +Version Control (VC) commands on one or more files shown in a Dired +buffer. @xref{Version Control}. + @vindex dired-dwim-target @cindex two directories (in Dired) Commands which ask for a destination directory, such as those which diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 8aee3380b71..f5bbc4d65c0 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -94,6 +94,20 @@ is useful when you perform version control commands outside Emacs different version control system, or remove it from version control entirely. +@cindex VC commands, in Dired buffers +@cindex filesets, VC, in Dired buffers + VC is also enabled automatically in Dired buffers (@pxref{Dired}) +showing directories whose files are controlled by a VCS@. All VC +commands described in this section can be invoked from any Dired +buffer showing a directory with VC-controlled files; any files that +are marked in a Dired buffer (@pxref{Marks vs Flags}) are considered +to belong to the current fileset, and VC commands operate on the files +in this fileset. This allows you to construct VC filesets including +any files you want, regardless of their VC state. (If no files are +marked when a VC command is invoked from a Dired buffer, the file +shown on the current line in the buffer is considered the only file in +the fileset.) + @menu * Introduction to VC:: How version control works in general. * VC Mode Line:: How the mode line shows version control status. @@ -471,9 +485,10 @@ collection of one or more files that a VC operation acts on. When you type VC commands in a buffer visiting a version-controlled file, the VC fileset is simply that one file. When you type them in a VC Directory buffer, and some files in it are marked, the VC fileset -consists of the marked files (@pxref{VC Directory Mode}). The VC -fileset also consists of the marked files in a Dired buffer -(@pxref{Dired}). +consists of the marked files (@pxref{VC Directory Mode}). Likewise, +when you invoke a VC command from a Dired buffer, the VC fileset +consists of the marked files (@pxref{Marks vs Flags}), defaulting to +the file shown on the current line if no files are marked. On modern changeset-based version control systems (@pxref{VCS Changesets}), VC commands handle multi-file VC filesets as a group. @@ -497,7 +512,9 @@ action on the current VC fileset: either registering it with a version control system, or committing it, or unlocking it, or merging changes into it. The precise actions are described in detail in the following subsections. You can use @kbd{C-x v v} either in a file-visiting -buffer, in a Dired buffer, or in a VC Directory buffer. +buffer, in a Dired buffer, or in a VC Directory buffer; in the latter +two cases the command operates on the fileset consisting of the marked +files. Note that VC filesets are distinct from the named filesets used for viewing and visiting files in functional groups @@ -1002,16 +1019,25 @@ Search the change history for a specified pattern. @findex vc-print-log @kbd{C-x v l} (@code{vc-print-log}) displays a buffer named @file{*vc-change-log*}, showing the history of changes made to the -current file, including who made the changes, the dates, and the log -entry for each change (these are the same log entries you would enter -via the @file{*vc-log*} buffer; @pxref{Log Buffer}). Point is -centered at the revision of the file currently being visited. With a -prefix argument, the command prompts for the revision to center on, -and the maximum number of revisions to display. +current fileset in the long form, including who made the changes, the +dates, and the log entry for each change (these are the same log +entries you would enter via the @file{*vc-log*} buffer; @pxref{Log +Buffer}). When invoked from a buffer visiting a file, the current +fileset consists of that single file, and point in the displayed +@file{*vc-change-log*} buffer is centered at the revision of that +file. When invoked from a VC Directory buffer (@pxref{VC Directory +Mode}) or from a Dired buffer (@pxref{Dired}), the fileset consists of +all the marked files, defaulting to the file shown on the current line +in the directory buffer if no file is marked. - If you call @kbd{C-x v l} from a VC Directory buffer (@pxref{VC -Directory Mode}) or a Dired buffer (@pxref{Dired}), it applies to the -file listed on the current line or to all the marked files. + If the fileset includes one or more directories, the resulting +@file{*vc-change-log*} buffer shows a short log of changes (one line +for each change), if the VC backend supports that; otherwise it shows +the log in the long form. + + With a prefix argument, the command prompts for the revision to +center on in the @file{*vc-change-log*} buffer and for the maximum +number of revisions to display. @kindex C-x v L @findex vc-print-root-log @@ -1217,6 +1243,11 @@ called PCL-CVS which is specialized for CVS@. @xref{Top, , About PCL-CVS, pcl-cvs, PCL-CVS---The Emacs Front-End to CVS}. @end ifnottex + You can also invoke VC commands from Dired buffers (@pxref{Dired}). +In that case, any VC command you invoke considers the marked files as +the current fileset (@pxref{Basic VC Editing}), defaulting to the file +on the current line if no files are marked. + @menu * Buffer: VC Directory Buffer. What the buffer looks like and means. * Commands: VC Directory Commands. Commands to use in a VC directory buffer. diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index c390017e190..fc3f6f4f04d 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -3741,12 +3741,21 @@ of the target of the link instead." ;;;###autoload (defun dired-vc-next-action (verbose) - "Do the next version control operation on marked files/directories. -When only files are marked then call `vc-next-action' with the -same value of the VERBOSE argument. -When also directories are marked then call `vc-dir' and mark -the same files/directories in the VC-Dir buffer that were marked -in the Dired buffer." + "Do the next logical version control operation on marked files/directories. +The VC control operation will operate on a fileset which includes +the marked files/directories. If no files/directories are marked, the +fileset will include the single file/directory shown on the current line. + +If only regular files are in the fileset, call `vc-next-action' with +the same value of the VERBOSE argument (interactively, the prefix +argument). + +If one or more directories are in the fileset, start `vc-dir' in the root +directory of the repository that includes the current directory, with +the same files/directories marked in the VC-Directory buffer that were +marked in the original Dired buffer. If the current directory doesn't +belong to a VCS repository, prompt for a repository directory. In this +case, the VERBOSE argument is ignored." (interactive "P") (let* ((marked-files (dired-get-marked-files nil nil nil nil t)) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 4ba62c0b3c7..0e3519e2e5c 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1239,7 +1239,11 @@ For old-style locking-based version control systems, like RCS: When using this command to register a new file (or files), it will automatically deduce which VC repository to register it -with, using the most specific one." +with, using the most specific one. + +If VERBOSE is non-nil (interactively, the prefix argument), +you can specify a VC backend or (for centralized VCS only) +the revision ID or branch ID." (interactive "P") (let* ((vc-fileset (vc-deduce-fileset nil t 'state-model-only-files)) (backend (car vc-fileset)) @@ -2696,7 +2700,16 @@ earlier revisions. Show up to LIMIT entries (non-nil means unlimited)." is-start-revision limit type))))) (defvar vc-log-view-type nil - "Set this to differentiate the different types of logs.") + "Set this to record the type of VC log shown in the current buffer. +Supported values are: + + `short' -- short log form, one line for each commit + `long' -- long log form, including full log message and author + `with-diff' -- log including diffs + `log-outgoing' -- log of changes to be pushed to upstream + `log-incoming' -- log of changes to be brought by pulling from upstream + `log-search' -- log entries matching a pattern; shown in long format + `mergebase' -- log created by `vc-log-mergebase'.") (put 'vc-log-view-type 'permanent-local t) (defvar vc-sentinel-movepoint) @@ -2753,13 +2766,20 @@ Each function runs in the log output buffer without args.") ;;;###autoload (defun vc-print-log (&optional working-revision limit) - "List the change log of the current fileset in a window. -If WORKING-REVISION is non-nil, leave point at that revision. + "Show in another window the VC change history of the current fileset. +If WORKING-REVISION is non-nil, it should be a revision ID; position +point in the change history buffer at that revision. If LIMIT is non-nil, it should be a number specifying the maximum number of revisions to show; the default is `vc-log-show-limit'. When called interactively with a prefix argument, prompt for -WORKING-REVISION and LIMIT." +WORKING-REVISION and LIMIT. + +This shows a short log (one line for each commit) if the current +fileset includes directories and the VC backend supports that; +otherwise it shows the detailed log of each commit, which includes +the full log message and the author. Additional control of the +shown log style is available via `vc-log-short-style'." (interactive (cond (current-prefix-arg @@ -2784,14 +2804,14 @@ WORKING-REVISION and LIMIT." ;;;###autoload (defun vc-print-root-log (&optional limit revision) - "List the revision history for the current VC controlled tree in a window. + "Show in another window VC change history of the current VC controlled tree. If LIMIT is non-nil, it should be a number specifying the maximum number of revisions to show; the default is `vc-log-show-limit'. -When called interactively with a prefix argument, prompt for LIMIT. -When the prefix argument is a number, use it as LIMIT. +When called interactively with a prefix argument, prompt for LIMIT, but +if the prefix argument is a number, use it as LIMIT. A special case is when the prefix argument is 1: in this case -the command asks for the ID of a revision, and shows that revision -with its diffs (if the underlying VCS supports that)." +the command prompts for the ID of a revision, and shows that revision +with its diffs (if the underlying VCS backend supports that)." (interactive (cond ((eq current-prefix-arg 1) @@ -2875,15 +2895,17 @@ In some version control systems REMOTE-LOCATION can be a remote branch name." ;;;###autoload (defun vc-log-search (pattern) - "Search the log of changes for PATTERN. + "Search the VC log of changes for PATTERN and show log of matching changes. PATTERN is usually interpreted as a regular expression. However, its exact semantics is up to the backend's log search command; some can only match fixed strings. -Display all entries that match log messages in long format. -With a prefix argument, ask for a command to run that will output -log entries." +This command displays in long format all the changes whose log messages +match PATTERN. + +With a prefix argument, the command asks for a shell command to run that +will output log entries, and displays those log entries instead." (interactive (list (unless current-prefix-arg (read-regexp "Search log with pattern: ")))) (let ((backend (vc-deduce-backend))) @@ -2894,8 +2916,8 @@ log entries." ;;;###autoload (defun vc-log-mergebase (_files rev1 rev2) - "Show a log of changes between the merge base of REV1 and REV2 revisions. -The merge base is a common ancestor between REV1 and REV2 revisions." + "Show a log of changes between the merge base of revisions REV1 and REV2. +The merge base is a common ancestor of revisions REV1 and REV2." (interactive (vc-diff-build-argument-list-internal (or (ignore-errors (vc-deduce-fileset t)) From e3be0dbf85c729447776d361ba56ada6b92f0149 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Thu, 23 Feb 2023 13:58:38 +0000 Subject: [PATCH 3/8] Eglot: display completion label when safe Originally reported in https://github.com/joaotavora/eglot/discussions/1141 by "Mintsoup". Eglot doesn't always show the LSP :label property of a CompletionItem in the completion candidates. That is because label is sometimes not what should be inserted in the buffer in the end, the :insertText property supercedes it. But the label is usually more suitable for display nevertheless and if the LSP CompletionItem contains either a snippet or a textEdit, it's safe to display the label, since :exit-function will guarantee that a suitable buffer insertion is performed. This change reflects that awareness that when a textEdit is available, it's acceptable to show the label. * lisp/progmodes/eglot.el (eglot-completion-at-point): Adjust. --- lisp/progmodes/eglot.el | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index df755dfa43a..7d61f6ad78e 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -2824,16 +2824,20 @@ for which LSP on-type-formatting should be requested." (mapcar (jsonrpc-lambda (&rest item &key label insertText insertTextFormat - &allow-other-keys) + textEdit &allow-other-keys) (let ((proxy - (cond ((and (eql insertTextFormat 2) - (eglot--snippet-expansion-fn)) + ;; Snippet or textEdit, it's safe to + ;; display/insert the label since + ;; it'll be adjusted. If no usable + ;; insertText at all, label is best, + ;; too. + (cond ((or (and (eql insertTextFormat 2) + (eglot--snippet-expansion-fn)) + textEdit + (null insertText) + (string-empty-p insertText)) (string-trim-left label)) - ((and insertText - (not (string-empty-p insertText))) - insertText) - (t - (string-trim-left label))))) + (t insertText)))) (unless (zerop (length proxy)) (put-text-property 0 1 'eglot--lsp-item item proxy)) proxy)) From 5c2be6a2632052b39b49899d1b19df2942ac6453 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 23 Feb 2023 17:18:28 +0200 Subject: [PATCH 4/8] ; Fix recently-added doc strings in eglot.el * lisp/progmodes/eglot.el (eglot-lazy-inlay-hints) (eglot-inlay-hints-mode): Doc fixes. --- lisp/progmodes/eglot.el | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 7d61f6ad78e..7b4251a1242 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3490,17 +3490,17 @@ If NOERROR, return predicate, else erroring function." "Face used for parameter inlay hint overlays.") (defcustom eglot-lazy-inlay-hints 0.3 - "If non-nil, restrict LSP inlay hints to visible portion of buffer. + "If non-nil, restrict LSP inlay hints to visible portion of the buffer. -Value is number specifying how many seconds to wait after a +Value is a number specifying how many seconds to wait after a window has been (re)scrolled before requesting new inlay hints -for the visible region of the window being manipulated. +for the now-visible portion of the buffer shown in the window. If nil, then inlay hints are requested for the entire buffer. +This could be slow. This value is only meaningful if the minor mode -`eglot-inlay-hints-mode' is true. -" +`eglot-inlay-hints-mode' is turned on in a buffer." :type 'number :version "29.1") @@ -3573,7 +3573,7 @@ This value is only meaningful if the minor mode (wsetq eglot--inlay-hints-timer nil)))))))))) (define-minor-mode eglot-inlay-hints-mode - "Minor mode annotating buffer with LSP inlay hints." + "Minor mode for annotating buffers with LSP server's inlay hints." :global nil (cond (eglot-inlay-hints-mode (cond From a0b273ef300adabac0cb656fcc3657ebaf6ed528 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 23 Feb 2023 17:45:52 +0200 Subject: [PATCH 5/8] ; Clarify documentation of 'file-modes-number-to-symbolic' * doc/lispref/files.texi (Changing Files): * lisp/files.el (file-modes-number-to-symbolic): Clarify the confusion with "symbolic" forms of file modes. (Bug#61709) --- doc/lispref/files.texi | 6 +++++- lisp/files.el | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 5062b1697b5..f8781d4895b 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -1985,7 +1985,11 @@ all. @defun file-modes-number-to-symbolic modes This function converts a numeric file mode specification in -@var{modes} into the equivalent symbolic form. +@var{modes} into the equivalent string form. The string which this +function returns is in the same format produced by the shell command +@kbd{ls -l} and by @code{file-attributes}, @emph{not} the symbolic +form accepted by @code{file-modes-symbolic-to-number} and the +@command{chmod} shell command. @end defun @defun set-file-times filename &optional time flag diff --git a/lisp/files.el b/lisp/files.el index 57e01340359..db3f348c4b5 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -8391,11 +8391,14 @@ as in \"og+rX-w\"." num-rights)) (defun file-modes-number-to-symbolic (mode &optional filetype) - "Return a string describing a file's MODE. + "Return a description of a file's MODE as a string of 10 letters and dashes. +The returned string is like the mode description produced by \"ls -l\". For instance, if MODE is #o700, then it produces `-rwx------'. -FILETYPE if provided should be a character denoting the type of file, -such as `?d' for a directory, or `?l' for a symbolic link and will override -the leading `-' char." +Note that this is NOT the same as the \"chmod\" style symbolic description +accepted by `file-modes-symbolic-to-number'. +FILETYPE, if provided, should be a character denoting the type of file, +such as `?d' for a directory, or `?l' for a symbolic link, and will override +the leading `-' character." (string (or filetype (pcase (ash mode -12) From 79fead1709f8913b6e529c5f4734fcd0c08129c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=ADn?= Date: Thu, 23 Feb 2023 18:30:36 +0100 Subject: [PATCH 6/8] ; Fix a typo in a doc string in c-ts-common.el (bug#61736). --- lisp/progmodes/c-ts-common.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/progmodes/c-ts-common.el b/lisp/progmodes/c-ts-common.el index 8262e6261d4..72df65a2287 100644 --- a/lisp/progmodes/c-ts-common.el +++ b/lisp/progmodes/c-ts-common.el @@ -268,7 +268,7 @@ particular major mode. This cannot be nil for `c-ts-common' statement indent functions to work.") (defvar c-ts-common-indent-type-regexp-alist nil - "An alist of of node type regexps. + "An alist of node type regexps. Each key in the alist is one of `if', `else', `do', `while', `for', `block', `close-bracket'. Each value in the alist From 91e24c5b5a69495bcd706a5287c05bb5fd282700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Thu, 23 Feb 2023 19:18:41 +0000 Subject: [PATCH 7/8] Eglot: update inlay hints on window configuration changes * lisp/progmodes/eglot.el (eglot--inlay-hints-after-window-config-change): New helper. (eglot-inlay-hints-mode): Use it. --- lisp/progmodes/eglot.el | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 7b4251a1242..eea8be6d1aa 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3572,6 +3572,9 @@ This value is only meaningful if the minor mode (window-end window)) (wsetq eglot--inlay-hints-timer nil)))))))))) +(defun eglot--inlay-hints-after-window-config-change () + (eglot--update-hints-1 (window-start) (window-end))) + (define-minor-mode eglot-inlay-hints-mode "Minor mode for annotating buffers with LSP server's inlay hints." :global nil @@ -3585,6 +3588,8 @@ This value is only meaningful if the minor mode #'eglot--inlay-hints-lazily t t) (add-hook 'window-scroll-functions #'eglot--inlay-hints-after-scroll nil t) + (add-hook 'window-configuration-change-hook + #'eglot--inlay-hints-after-window-config-change nil t) ;; Maybe there isn't a window yet for current buffer, ;; so `run-at-time' ensures this runs after redisplay. (run-at-time 0 nil #'eglot--inlay-hints-lazily)) @@ -3593,6 +3598,8 @@ This value is only meaningful if the minor mode #'eglot--inlay-hints-fully nil t) (eglot--inlay-hints-fully)))) (t + (remove-hook 'window-configuration-change-hook + #'eglot--inlay-hints-after-window-config-change) (remove-hook 'eglot--document-changed-hook #'eglot--inlay-hints-lazily t) (remove-hook 'eglot--document-changed-hook From b0cbd5590b238fa9001e3f07b7035704ef976722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20T=C3=A1vora?= Date: Thu, 23 Feb 2023 23:51:09 +0000 Subject: [PATCH 8/8] Eglot: simplify inlay hints implementation with jit-lock This implementation is much simpler than the one based on windows-scroll-functions. It's also supposedly safer, as long as jit-lock guarantees refontification of affected regions. It's not _trivially_ simple though, as simply adding 'eglot--update-hints-1' to jit-lock-functions, while possible, is going to request inlay hints from the LSP server for many small regions of the buffer, depending on what jit-lock thinks is best. So we keep coalescing these into a larger region until the time is suitable for a more bandwidth-efficient request. To do this, we use a jit-lock implementation detail, jit-lock-context-unfontify-pos, which is a proxy for knowing that the jit-lock-context-timer has run. Not sure how brittle it is, but it seems to work reasonably. We also get rid of the previous "get hints for entire buffer" implementation. * doc/misc/eglot.texi (Eglot Variables): Remove mention to deleted eglot-lazy-inlay-hints. * lisp/progmodes/eglot.el (eglot-lazy-inlay-hints) (eglot--inlay-hints-after-scroll) (eglot--inlay-hints-fully) (eglot--inlay-hints-lazily): Remove. (eglot--update-hints): Add function. (eglot-inlay-hints-mode): Simplify. --- doc/misc/eglot.texi | 8 --- lisp/progmodes/eglot.el | 110 ++++++++++++---------------------------- 2 files changed, 33 insertions(+), 85 deletions(-) diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index 38c6adaf131..eed9744b9f0 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi @@ -883,14 +883,6 @@ this map. For example: (define-key eglot-mode-map (kbd "") 'xref-find-definitions) @end lisp -@item eglot-lazy-inlay-hints -This variable controls the operation and performance of LSP Inlay -Hints (@pxref{Eglot Features}). If non-@code{nil}, it specifies how -much time to wait after a window is displayed or scrolled before -requesting hints for that visible portion of a given buffer. If -@code{nil}, inlay hints are always requested for the whole buffer, -even for parts of it not currently visible. - @end vtable Additional variables, which are relevant for customizing the server diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index eea8be6d1aa..9d722047b47 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3489,32 +3489,39 @@ If NOERROR, return predicate, else erroring function." (defface eglot-parameter-hint-face '((t (:inherit eglot-inlay-hint-face))) "Face used for parameter inlay hint overlays.") -(defcustom eglot-lazy-inlay-hints 0.3 - "If non-nil, restrict LSP inlay hints to visible portion of the buffer. +(defvar-local eglot--outstanding-inlay-hints-region (cons nil nil) + "Jit-lock-calculated (FROM . TO) region with potentially outdated hints") -Value is a number specifying how many seconds to wait after a -window has been (re)scrolled before requesting new inlay hints -for the now-visible portion of the buffer shown in the window. +(defvar-local eglot--outstanding-inlay-regions-timer nil + "Helper timer for `eglot--update-hints'") -If nil, then inlay hints are requested for the entire buffer. -This could be slow. - -This value is only meaningful if the minor mode -`eglot-inlay-hints-mode' is turned on in a buffer." - :type 'number - :version "29.1") - -(defun eglot--inlay-hints-fully () - (eglot--widening (eglot--update-hints-1 (point-min) (point-max)))) - -(cl-defun eglot--inlay-hints-lazily (&optional (buffer (current-buffer))) - (eglot--when-live-buffer buffer - (when eglot--managed-mode - (dolist (window (get-buffer-window-list nil nil 'visible)) - (eglot--update-hints-1 (window-start window) (window-end window)))))) +(defun eglot--update-hints (from to) + "Jit-lock function for Eglot inlay hints." + (cl-symbol-macrolet ((region eglot--outstanding-inlay-hints-region) + (timer eglot--outstanding-inlay-regions-timer)) + (setcar region (min (or (car region) (point-max)) from)) + (setcdr region (max (or (cdr region) (point-min)) to)) + ;; HACK: We're relying on knowledge of jit-lock internals here. The + ;; condition comparing `jit-lock-context-unfontify-pos' to + ;; `point-max' is a heuristic for telling whether this call to + ;; `jit-lock-functions' happens after `jit-lock-context-timer' has + ;; just run. Only after this delay should we start the smoothing + ;; timer that will eventually call `eglot--update-hints-1' with the + ;; coalesced region. I wish we didn't need the timer, but sometimes + ;; a lot of "non-contextual" calls come in all at once and do verify + ;; the condition. Notice it is a 0 second timer though, so we're + ;; not introducing any more delay over jit-lock's timers. + (when (= jit-lock-context-unfontify-pos (point-max)) + (if timer (cancel-timer timer)) + (setq timer (run-at-time + 0 nil + (lambda () + (eglot--update-hints-1 (max (car region) (point-min)) + (min (cdr region) (point-max))) + (setq region (cons nil nil) timer nil))))))) (defun eglot--update-hints-1 (from to) - "Request LSP inlay hints and annotate current buffer from FROM to TO." + "Do most work for `eglot--update-hints', including LSP request." (let* ((buf (current-buffer)) (paint-hint (eglot--lambda ((InlayHint) position kind label paddingLeft paddingRight) @@ -3545,67 +3552,16 @@ This value is only meaningful if the minor mode (mapc paint-hint hints)))) :deferred 'eglot--update-hints-1))) -(defun eglot--inlay-hints-after-scroll (window display-start) - (cl-macrolet ((wsetq (sym val) `(set-window-parameter window ',sym ,val)) - (wgetq (sym) `(window-parameter window ',sym))) - (let ((buf (window-buffer window)) - (timer (wgetq eglot--inlay-hints-timer)) - (last-display-start (wgetq eglot--last-inlay-hint-display-start))) - (when (and eglot-lazy-inlay-hints - ;; FIXME: If `window' is _not_ the selected window, - ;; then for some unknown reason probably related to - ;; the overlays added later to the buffer, the scroll - ;; function will be called indefinitely. Not sure if - ;; an Emacs bug, but prevent useless duplicate calls - ;; by saving and examining `display-start' fixes it. - (not (eql last-display-start display-start))) - (when timer (cancel-timer timer)) - (wsetq eglot--last-inlay-hint-display-start - display-start) - (wsetq eglot--inlay-hints-timer - (run-at-time - eglot-lazy-inlay-hints - nil (lambda () - (eglot--when-live-buffer buf - (when (eq buf (window-buffer window)) - (eglot--update-hints-1 (window-start window) - (window-end window)) - (wsetq eglot--inlay-hints-timer nil)))))))))) - -(defun eglot--inlay-hints-after-window-config-change () - (eglot--update-hints-1 (window-start) (window-end))) - (define-minor-mode eglot-inlay-hints-mode "Minor mode for annotating buffers with LSP server's inlay hints." :global nil (cond (eglot-inlay-hints-mode - (cond - ((not (eglot--server-capable :inlayHintProvider)) + (if (eglot--server-capable :inlayHintProvider) + (jit-lock-register #'eglot--update-hints 'contextual) (eglot--warn - "No :inlayHintProvider support. Inlay hints will not work.")) - (eglot-lazy-inlay-hints - (add-hook 'eglot--document-changed-hook - #'eglot--inlay-hints-lazily t t) - (add-hook 'window-scroll-functions - #'eglot--inlay-hints-after-scroll nil t) - (add-hook 'window-configuration-change-hook - #'eglot--inlay-hints-after-window-config-change nil t) - ;; Maybe there isn't a window yet for current buffer, - ;; so `run-at-time' ensures this runs after redisplay. - (run-at-time 0 nil #'eglot--inlay-hints-lazily)) - (t - (add-hook 'eglot--document-changed-hook - #'eglot--inlay-hints-fully nil t) - (eglot--inlay-hints-fully)))) + "No :inlayHintProvider support. Inlay hints will not work."))) (t - (remove-hook 'window-configuration-change-hook - #'eglot--inlay-hints-after-window-config-change) - (remove-hook 'eglot--document-changed-hook - #'eglot--inlay-hints-lazily t) - (remove-hook 'eglot--document-changed-hook - #'eglot--inlay-hints-fully t) - (remove-hook 'window-scroll-functions - #'eglot--inlay-hints-after-scroll t) + (jit-lock-unregister #'eglot--update-hints) (remove-overlays nil nil 'eglot--inlay-hint t))))