1

Lsp-mode has a function lsp-document-highlight bound by default to s-l a h which picks the symbol at point and highlights all (semantic) occurrences.

Is there a goto-next-highlight. I checked the source of lsp-mode.el and lsp-ui-*.el but did not find candidates so far, probably grepping for the wrong strings.

EDIT: While the answer of @Hi-Angel is great, it can only fully work if this feature request for lsp-mode is implemented.

2
  • For reference (not a lsp-mode solution): it sounds like Emacs built-in function isearch-forward-thing-at-point can be used for the described behavior. It's bound to M-s M-. by default.
    – Y. E.
    Feb 17 at 12:30
  • The whole point of using the language server's support is to not use a text search, but to jump around the uses of the semantically same symbol. I use isearch a lot, but it is a different use-case.
    – Harald
    Feb 17 at 15:03

1 Answer 1

1

I'm not aware of such, but if you want to navigate highlights within a file, here's an interactive function (lsp-next-document-highlight-in-file) that implements that.

(defun lsp-is-lsp-highlighted (pos)
  (seq-find (lambda (elem) (overlay-get elem 'lsp-highlight))
            (overlays-at pos)))

(defun lsp-lsp-highlight-past-end (pos)
  (overlay-end (seq-find (lambda (elem) (overlay-get elem 'lsp-highlight))
                         (overlays-at pos))))

(defun lsp-next-document-highlight-in-file (&optional recurse)
  "Finds next text highlighted with `lsp-document-highlight'.

Wraps around when no highlight found till the end of the buffer."
  (interactive)
  (let* ((orig (point))
         (prev-pos -1)
         (pos (if (lsp-is-lsp-highlighted orig)
                  ;; skip current highlight if it's at point
                  (lsp-lsp-highlight-past-end orig)
                orig))
         (found nil))
    (while (and (/= prev-pos pos)
                (not (setq found (lsp-is-lsp-highlighted pos))))
      (setq prev-pos pos
            pos (next-overlay-change pos)))
    (if (and (not recurse)
             (not found)
             (goto-char (point-min))
             (not (lsp-next-document-highlight-in-file t)))
        (unless recurse
          (message "No next highlight found")
          (goto-char orig))
      (when found
        (goto-char pos)))))

NOTE: as @Harald found (see the comments), as of writing the words lsp-document-highlight has a drawback that it only highlights symbols that are on the screen. It can be worked around by applying the following change to lsp-mode.el:

--- a/lsp-mode.el
+++ b/lsp-mode.el
@@ -6119,8 +6119,8 @@ A reference is highlighted only if it is visible in a window."
   (lsp--remove-overlays 'lsp-highlight)
 
   (let* ((wins-visible-pos (-map (lambda (win)
-                                   (cons (1- (line-number-at-pos (window-start win) t))
-                                         (1+ (line-number-at-pos (window-end win) t))))
+                                   (cons (point-min)
+                                         (point-max)))
                                  (get-buffer-window-list nil nil 'visible))))
     (setq lsp--have-document-highlights t)
     (-map

You can save the modified function to your init file in a section that gets run after lsp-mode is fully initialized. Also, if you're annoyed by this, please report a bug or create a PR to fix that.

7
  • 1
    Just right! Great. I started toying with emacs-lisp, but this would have taken me a whole day as I never really programmed with it.
    – Harald
    Feb 16 at 16:45
  • 1
    Sadly it turned out that the overlay is the wrong thing to follow. It is only valid on the visual part of a buffer, i.e. what we see on the screen. The instances of the symbol "below the bottom of the window" are not found :-/
    – Harald
    Feb 17 at 15:05
  • @Harald actually, now that I'm testing it, I think it's not a problem with the code but an actual bug in either the lsp-mode or the lsp-server, because the highlight does not appear even after I scroll buffer. So, here's what I'm testing: I created a test.sh with a bunch of # lines that end with a foo="hello" line and then a echo ${foo}. Enabled lsp. And then if I execute lsp-document-highlight over the first foo, the foo on the second line gets only highlighted when it's on the screen. But if it's not, then it does not get highlighted. Pls test if you see the same.
    – Hi-Angel
    Feb 17 at 16:44
  • 1
    Right, yes, not a "feature" of overlays. But I think I found the reason and it seems to be intended. See lsp-mode.el function lsp--document-highlight-callback where the doc says: "A reference is highlighted only if it is visible in a window." :-/
    – Harald
    Feb 17 at 19:17
  • 1
    @Harald oh, I see. Well, this limitation seems to be very arbitrary, and for me at least, copying the function and replacing the two calls after the (cons … (there's only one) to (point-min) and (point-max) makes it work. Again, I think you can bring it up with lsp-mode maintainers; in the meantime for your personal use at least you can just put the modified copy of the function to your init file, right after lsp-mode got fully initialized (so if you're using use-package, I think it should be the :config section). UPD: and if you do report it, pls leave a link here.
    – Hi-Angel
    Feb 17 at 19:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.