-
Notifications
You must be signed in to change notification settings - Fork 201
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Imenu support to store textDocument/documentSymbol information in overlays #760
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joaotavora Here is my work-in-progress on refactoring Imenu support. Do you mind taking a look at the code and providing some feedback before I get too far?
@@ -523,6 +523,20 @@ Pass TIMEOUT to `eglot--with-timeout'." | |||
(forward-line -1) | |||
(should (looking-at "Complete, but not unique")))))) | |||
|
|||
(ert-deftest basic-imenu () | |||
"Test basic imenu functionality in a python LSP" | |||
(skip-unless (executable-find "pyls")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think an additional dependency is fine: use skip-unless
to skip it if the gopls
binary is not available as you do here with pyls
.
(defun eglot--overlay-put-when (overlay prop value) | ||
(when value | ||
(overlay-put overlay prop value))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m not sure how much you like using cl-labels
to hide helper functions. I found that the backtraces were difficult to read when using cl-labels
, but I can also appreciate keeping a small API. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's largely a matter of taste. Personally, I'd use cl-labels
for cleaner code and live with the suboptimal backtrace.
(when children | ||
(setq overlays | ||
(mapcan (lambda (sym) | ||
(eglot--make-symbol-overlay sym (1+ depth) ov)) | ||
children)) | ||
(overlay-put ov 'eglot-symbol-children overlays)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎗️ I want to manage the recursion locally within eglot--make-symbol-overlay
to avoid stack overflows with deeply nested children.
(defvar-local eglot--priority-symbols | ||
'("File" "Module" | ||
"Namespace" "Package" "Class" | ||
"Method" | ||
"Constructor" "Enum" "Interface" | ||
"Function" | ||
"Struct") | ||
"Symbols returned by textDocument/documentSymbol will be given | ||
extra priority by `eglot--make-symbol-overlays' if their `:kind' | ||
matches this list.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 If we actually give variables and fields their proper priority because they are the most deeply nested, actually using (get-char-property (point) 'eglot-symbol-name)
becomes unusable. This is my hack to work around this. Would you do something differently?
(setcdr x (or (eglot--imenu-group-by-containerName overlays) | ||
(eglot--imenu-build-tree overlays)))))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🙁 I’m not particularly happy about this implementation, since it relies on the side-effect of a missing 'eglot-symbol-containerName
property to fallthrough. Also, this parses the overlays twice.
;; BUG: this only groups the lowest level of symbols. | ||
(setq parent (overlay-get parent 'eglot-symbol-name))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 I think I will have to build this tree by hand, but I wasn’t smart enough when drafting this.
Do you mind adding a require for map.el? It is built-in to Emacs 26.1, which I think is the minimum version you support?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no problem with requiring map.el
if we need it.
Yes, well, this is trickier. On the one hand, this solves a real problem IIRC which is that the advanced usage that Eglot makes of the So solving this problem is very good. On the other hand, it puts a lot of But even without that integration into Emacs's core, I think that we should still consider changes to In summary: I think we should idealize any feature/bugfix of Eglot holystically within Emacs, even if Eglot is not integrated there yet. But that's just one opinion, @skangas should chime in. |
I fully agree with what @joaotavora says above. @sfllaw Could you start looking into integrating the relevant changes into |
@skangas, @joaotavora: I'm not sure which bits of this patch should be native to imenu and which-func? This patch stores the result of LSP's textDocument/documentSymbol protocol in eglot-specific overlays. Then it renders those overlays into something However, I am unsure whether imenu or which-func should be using overlays by default. Are you proposing something else that I'm not understanding? |
IIUC, I guess the question is: why doesn't In other words, we should perhaps not just move any specific bits from this patch at this point, but rather take a step back and think about how we could move the more general (I have no idea if extending the imenu datastructures or using overlays is better.) |
Oooh! I think we have a misunderstanding! The point of this pull request is to remove the use of special elements in ;; The latest buffer index.
(defvar-local imenu--index-alist nil
"The buffer index alist computed for this buffer in Imenu.
Simple elements in the alist look like (INDEX-NAME . POSITION).
POSITION is the buffer position of the item; to go to the item
is simply to move point to that position.
POSITION is passed to `imenu-default-goto-function', so it can be
a non-number if that variable has been changed (e.g. Semantic
uses overlays for POSITIONs).
Special elements look like
\(INDEX-NAME POSITION FUNCTION ARGUMENTS...).
To \"go to\" a special element means applying FUNCTION to
INDEX-NAME, POSITION, and the ARGUMENTS.
A nested sub-alist element looks like (INDEX-NAME . SUB-ALIST).
The function `imenu--subalist-p' tests an element and returns t
if it is a sub-alist.
There is one simple element with negative POSITION; selecting that
element recalculates the buffer's index alist.") #758 (comment) suggested that I try replacing special elements with simple elements, which resulted in this patch. In that pull request, I suggested storing this information in a new If we don’t think it’s necessary to store the results of textDocument/documentSymbol anywhere, we could just throw it away after calculating Again, I don't believe there is any need to change imenu or which-func, because we can use imenu’s simple elements like almost every other package does. |
I hope that @joaotavora could explain what he had in mind, because I'm not familiar enough with this to have an opinion at this point. Thanks. |
@joaotavora It seems like you’ve chosen to punt on explicit imenu support, based on fdd87b7. If not, and you want me to work on this, please feel free to reopen this issue and we can discuss how you want to approach this. |
Hi @sfllaw ,
Yes and no. OT1H fdd87b7 is just fixing these bugs you mention in the description of this PR, and doing so in the simplest fashion I could think of. It actually removes code instead of adding. OTOH it is a bit of a punt, because I've lost sight of the broader problem (if any) that you were solving with this PR. This is exclusively my fault: my attention is divided among so many things that I simply lose track of things. So, if you could kindly remind me of what limitations the current version of the code fdd87b7 still has, I would be indebted. |
Hi @joaotavora,
The which-func.el package suggests using overlays, since those are significantly more efficient than searching through Imenu’s alist for large files with many symbols. ;; 2. This package should be realized with the help of overlay
;; properties instead of the `imenu--index-alist' variable. Above, I think you might have suggested that someone tackle this generally from inside Emacs itself. The reason I suggested this implementation is because eglot knows when new documentSymbol information has been loaded from LSP, whereas adding a variable watcher to Anyway, your implementation is Good Enough for small files, so maybe we shouldn’t do anything until someone complains? |
Alright, i get it. Seems like this is now an optimization issue. Thanks for the update. |
eglot-imenu
used to buildimenu--index-alist
with special elements, of the form(INDEX-NAME POSITION FUNCTION ARGUMENTS...)
which is not a great API. Most packages that integrate with Imenu cannot work with these elements, and expect the simple element of(INDEX-NAME . POSITION)
. See related issues: #535, #536, #547, #621, #758.This pull request is designed to tackle this problem by:
textDocument/documentSymbol
JSON-RPC calls in overlays, both in raw and parsed forms.imenu--index-alist
from these overlays with entirely simple elements.which-func-functions
hook to read the current function/class/named-scope from the best overlay.