Closed yyoncho closed 5 years ago
How to define a function to return icon for treemacs-define-expandable-node like this
Ran into the same thing when working on buffers. Not possible now, the plan is to extend the api to pass either an :icon-closed
that gets defvar
d, or a dynamic :icon-closed-form
.
How to define multiple top level roots when using treemacs to create standalone control?
Here I'll need to think of something.
Here I'll need to think of something.
Skipping the root node and adjusting the indentation will do the job for me.
treemacs--evade-image
moves the cursor in the begining of the line even if you have navigated to different buffer as a result of ret action.(with-current-buffer (get-buffer-create "*Showcase Buffer List*")
(treemacs-update-node (list 'Buffers)))
It seems like the code is not adjusted for that case.
Dynamic icons have been pushed now. Variadic top-level extensions are next. I'll also investigate points 3 and 4.
Can you give me an example of what kind of data you'd use to initialize a variadic extension - is it just a list of strings or symbols or is there some additional data you want to make use of?
Skipping the root node and adjusting the indentation will do the job for me.
You need to explain what you mean here. Which root node is being skipped? And how should indentation be adjusted? We're talking about top-level nodes so the indentation is just zero, right?
By the way, I made some changes to nomenclature to help avoid misunderstandings (if you use the old terms the compiler will warn you about their obsolescence). treemacs-define-root-extension
is replaced by treemacs-define-top-level-extension
, :project-marker
is replaced by :top-level-marker
.
The previous situation was inconsistent, bascically it's like this: nodes (and extensions) that sit at the very top, with zero indentation, on the same level as projects, are top-level nodes, while root nodes are simply whatever sits at the top of your hierarchy of node types, but they'd be displayed as part of some project or directory.
Can you give me an example of what kind of data you'd use to initialize a variadic extension - is it just a list of strings or symbols or is there some additional data you want to make use of?
Files, I want to use treemacs-icon-for-file
for rendering their icon. Here it is what I currently have, if you are lsp-mode
user you may try it: https://github.com/emacs-lsp/lsp-treemacs .
You need to explain what you mean here. Which root node is being skipped? And how should indentation be adjusted? We're talking about top-level nodes so the indentation is just zero, right?
Please ignore my comment: I was referring the fact that if you have a way to set the indentation of the second level extensions to 0 then one can use narrow-to-region
to have multiple top level extension.
What is the proper way to refresh standalone treemacs extension?
The first element of a custom node's path is always the project it's situated under. I need this to for example disambiguate the same dependency explorer extension in 2 different java projects. For top-level extensions it's probably not needed, but should not be a problem either, top-level extensions have their projects stored buffer-locally in treemacs-${name}-extension-project
.
For top-level extensions it's probably not needed, but should not be a problem either,
But variadic extensions will make that a royal pain in the ass. I'll try to get rid of it.
Pushed a first version of variadic extensions. Set :top-level-marker
to 'variadic
. Nothing's documented yet, I'll wait for feedback before getting into that. So far it'll only work if you call it yourself with the input you want, there's no way to give it to treemacs-define-top-level-extension
(you can of course, but it'll just throw an error).
The extension will expect to be called with a list of cons cells, each mapping a label to a key, available as extension-label
and extension-key
. The keys should be unique, so we can forego the whole project requirement and just use them for a path.
Ugly quick example code:
(treemacs-define-leaf-node "TESTLEAF" (treemacs-as-icon "x "))
(treemacs-define-expandable-node TESTROOT
:icon-open (treemacs-as-icon "A ")
:icon-closed (treemacs-as-icon "B ")
:query-function '("X" "Y" "Z")
:top-level-marker 'variadic
:root-label "TESTROOT"
:root-key-form extension-key
:root-face font-lock-keyword-face
:render-action
(treemacs-render-node
:icon "x "
:label-form item
:state treemacs-TESTLEAF-state
:face 'font-lock-doc-face
:key-form item))
(defun showcase-display-buffer-list ()
(interactive)
(let* ((buffer (get-buffer-create "*Showcase*"))
(window (display-buffer-in-side-window buffer '((side . right)))))
(select-window window)
(treemacs-initialize)
(treemacs-TESTROOT-extension '(("L1" . "FULLNAME1") ("L2" . "FULLNAME2") ("L3" . "FULLNAME3")))))
(showcase-display-buffer-list)
Btw updating won't work with this either. The goto-node code currently expects the first path item to be a project to detect it's a custom node we want to move to. I'll probably change the api to require the first element to be something like :custom
to tell things apart.
:root-label "TESTROOT"
Now that I look this over I see that this isn't used anywhere. I suppose we can drop the alist approach and use this to pass a form that calculates the label based on the key. What do you think?
I will need updating - I will list only the project roots that have errors. Also, I would like to update the error list when the diagnostics are changed which will result in adding removing roots.
Here it is unpolished screenshot of what I have.
Pushed update to variadic extensions: they're now identified by their key (so those keys must be unique). Updating them will work when the first element of the path is :custom
. To get back to the previous example:
(with-current-buffer (get-buffer-create "*Showcase*")
(treemacs-update-node '(:custom "FULLNAME1")))
will now do the trick.
Hopefully I didn't break anything in the process - my current priority is a fast feedback loop. Let me know if you see anything that looks like a bug.
Pushed more stuff. Now all top-level extensions' paths start with their :root-key-form
.
@Alexander-Miller
Here it is my code to render the roots:
(let* ((buffer (get-buffer-create "*LSP Errors*"))
(window (display-buffer-in-side-window buffer '((side . bottom)))))
(select-window window)
(treemacs-initialize)
(treemacs-LSP-ERROR-LIST-extension (lsp-treemacs--root-folders)))
What is the code needed to sync the new roots? If I have understood you correctly the code you have listed will update a single root node but I want to update all of the root nodes, e. g. something like treemacs-sync-and-update-root-nodes
which will delete all root nodes that are no longer present, it will add the new one and it will update the existing one?
You are asking for the big guns now. In fact I hadn't really been planning on adding something like this, I've always had the expectation of working with an event based update model with a mostly static number of roots, such as with file changes and buffer creation & deletion.
This kind of resync function will be a special version of treemacs--consolidate-projects
- if you want to get an idea of what that the implementation would look like. So yeah, I'll need a bit of time to get this right.
Would't adding flag :hide
, enforce node to be always open and do not increase :depth for that node be sufficient to cover that functionality?
Just noticed that most of the stuff I could do it with what I currently have(it pretty much works if there is at least one item initially)
(let* ((buffer (get-buffer-create "*Showcase Buffer List*"))
(window (display-buffer-in-side-window buffer '((side . bottom)))))
(select-window window)
(treemacs-initialize)
(treemacs-LSP-ERROR-LIST-extension)
(goto-char (point-min))
(treemacs-with-writable-buffer
(treemacs-button-put (treemacs-node-at-point) :depth -1))
(treemacs-expand-lsp-error-list)
(goto-char (point-min))
(next-line)
(narrow-to-region (point) (point-max)))
(treemacs-button-put (treemacs-node-at-point) :depth -1)
Interesting trick. But the whole approach seems all sorts of hacky. I have no idea how treemacs would deal with narrowing.
Would't adding flag
:hide
, enforce node to be always open and do not increase :depth for that node be sufficient to cover that functionality?
I think it really depends on what kind of update behavior you require here. The way you described things at first sounded like what's done in the consolidate-projects function. After an org-edit everything can have changed, new projects added, old ones deleted, their order can change as well. So I need to delete everything, render it all anew, with the addendum that whatever nodes had been expanded remain so.
That's not exactly the most efficient approach (and I can probably make some improvements), so if you can work with updating less than the whole thing you should definitely do so. And let me know if there's any invariants on your end, for example the nodes cannot change order when updating, that kinda stuff should make the implementation easier and the code faster.
If you think you need it I can push a treemacs-delete-single-node
next, it's mostly just missing documentation here.
In the meantime you can call treemacs-initialize
as a dumb update method. It'll kill the buffer and buffer-local variables, so you can just re-start your extension. Only downside is that it won't remember expanded nodes.
Btw this talk about depth has given me the idea to handle indentation like org-mode does, using a line-prefix
property. In practical terms nothing should change, but it might have an impact on performance.
Interesting trick. But the whole approach seems all sorts of hacky. I have no idea how treemacs would deal with narrowing.
I just wanted to illustrate the idea - refreshing the roots seems to be no different from refreshing nested buttons (for my case) . It seems to me that most of the code to support that is already there. Note that I do not want to threat them as real roots from treemacs perspective but only visually. Treemacs does not handle
In the meantime you can call
treemacs-initialize
as a dumb update method. It'll kill the buffer and buffer-local variables, so you can just re-start your extension. Only downside is that it won't remember expanded nodes.
This is no go - the errors will be refreshed as you type so no rememering the expanded notes will hurt usability. I would prefer to have artificial root instead of loosing this functionality.
And let me know if there's any invariants on your end, for example the nodes cannot change order when updating,
The code is pretty simple: in lsp-mode I have set of projects I want to display the projects that have errors, similar to what Eclipse Problems list is doing. When you expand the project you will see the files contaiining the errors and when you expand the file you will see the errors in that file. When user fixes a particular error I want to remove that project if there are no errors in that project and vice versa. So I will have the same project order but a project might be inserted in the list. I could insert it in the end but it wont feel natural.
As a side note, would you accept a PR with icons for error/info/warning in treemacs?
Note that I do not want to threat them as real roots from treemacs perspective but only visually.
That is the direction the current code is evolving as well. Top-level nodes in the main part of treemacs hold a special place as being your projects, but as you further describe your use-case I see that variadic extensions really don't need that kind of special treatment. Only updating them can be somewhat non-trivial, but in general the process really is no different than updating a hidden depth=-1 node.
Treating top-level extensions as project roots does have some internal advantages, it's less special-casing so I can, for example, reuse the code that deletes a project and makes sure that the right amount of lines is deleted based on treemacs-space-between-root-nodes
. The current state of the extension api makes me think that I tried to force something akin to a static type system on your tree hierarchy, what with the precise specifications how Node Type A renders Node Type B renders Node Type C etc. I'm sure I can make the whole thing much less strict, dynamic icons were already a good start.
And just to establish a common understanding when talking about performance: when I talk performance and efficiency I don't mean spending less than a full second in your widget lib, my standard is to not even cause a perceptible stutter.
A large part of the slowness of consolidating projects is re-opening everything by re-querying the file system. I assume your lsp integration would produce mostly static lists to render (as in 1 call to get the entire error info, at least per project), or at least be faster than caliing directory-files
and filtering and then sorting its output? If so our performance budget situation just got a lot better.
As a side note, would you accept a PR with icons for error/info/warning in treemacs?
Sure. There's probably no use for them in vanilla treemacs, but it's still good for additional icons to be in the main repo in case another extension needs them.
And just to establish a common understanding when talking about performance: when I talk performance and efficiency I don't mean spending less than a full second in your widget lib, my standard is to not even cause a perceptible stutter.
I have no IO calls, only counting errors/warnings/info which I already have in memory. I can precompute some of the stuff but for now everything is calculated on demand. In the future, this could be optimized further or I could add debounce of error processing.
I can precompute some of the stuff but for now everything is calculated on demand.
I think we're going to need this. There's an unpleasant issue with re-syncing something like your diagnostics buffer. Assume for example that on re-sync the "Errors" top-level node is still there, but the state has changed such that it should show errors of different projects than it did before, and the project nodes were also previously expanded to show errors in individual files.
After the sync treemacs will of course attempt to re-expand these projects that are no longer there, causing errors. Technically this also applies to my own dealings with the file system, but those edits are quite rare, helped by filewatch-mode, and I have the means to deal with them thanks to file-exists-p
.
However short of some ugly try/catch mechanism I do not see how to make this work in your case, at least not with the info I'd have now. But a good solution is possible: are you familiar with treemacs' pseudo-dom? Long story short it's a doubly linked tree (mostly) representing the expanded nodes in a given buffer. All the dom-nodes are part of a hash table (mostly that's why I need unique keys) so I can quickly jump in and start searching at whatever point I need. So if you can provide me with something I can use to construct a new dom you'd make my life a lot easier. What I need is some kind of nested structure - only the expandable part, not the leaves - that I can use to cull the current dom.
Full node paths would be best, but I think collecting those places too great a burden on you as the consumer of the extension api, so just the nodes' individual keys will be fine. As an example I can image the input to a resync function to look something like this:
'((Errors
((Project1
(File1
File2))
(Project2
File3)))
(Warnings
((Project3
(File4
File5
File6)))))
The code is available at https://github.com/emacs-lsp/lsp-treemacs/blob/master/lsp-treemacs.el .
The model is project -> file -> error/warnings/infos -> error/warnings/infos. For the record, at some point the refresh was working fine, e. g. when fixed an error it was removed from the tree and the state of the rest node was persisted. It does not work in the latest code but I am not sure whether I have changed something.
It does not work in the latest code but I am not sure whether I have changed something.
That's probably on me and the latest changes to top-level extensions. I'll try and investigate this based on your code now.
~So far I installed pyls, the lsp layer, and added my scripts directory as an lsp project. Most features work, but I'm not getting anything in flycheck. The lsp-ui checker is selected, flycheck's diagnostics say all is well, but bungling my scripts' syntax get me no reaction. Any idea what I'm missing?~ nvm it works after restrting emacs.
The code is available at https://github.com/emacs-lsp/lsp-treemacs/blob/master/lsp-treemacs.el
Sure, but I am also utterly unfamiliar with lsp's internals, that is why I'm asking how well we can transform lsp's data into something treemacs can digest properly.
But for now let's get the basics going. First I need to get your example going. Then I'll fix the current update problem. Then we'll on to making re-sync work.
Btw, treemacs-delete-single-node
has now made it to master.
Insta-update: your update code will work if you do it like this:
(defun lsp-treemacs--after-diagnostics ()
(with-demoted-errors "%s"
(with-current-buffer (get-buffer-create "*Showcase Buffer List*")
(treemacs-update-node '(:custom Buffers)))))
I added that :custom part to help me disambiguate things. TL;DR version is that treemacs-goto-node
which specialized goto-x function to call. It might not stick around in this form, so be ready so see it changed.
From treemacs point of view the models is just 3 functions returning (label . key) ((label . key)).
let me know if you need more info
So far so good. I've pushed some cleanups to my fork and will try building the dom for the resync next.
So far so good. I've pushed some cleanups to my fork and will try building the dom for the resync next.
Do I need to retest something?
Nope. I do think it'd be expedient for you to merge the changes I'm making - I have removed all compiler warnings and could turn the error view into something of a reference implementation - but that is up to you.
I'm also looking forward to your new icons, that ⚠ I'm using now isn't looking all that good.
Got a working example of what a dom computation for treemacs could look like:
(defun treemacs-lsp--recompute-dom ()
"TODO."
(-let [new-dom nil]
(treemacs--maphash (lsp-diagnostics) (file diag-entries)
(-let [pairs nil]
(dolist (diag diag-entries)
(push (cons file diag) pairs))
(push (cons file pairs) new-dom)))
(-let [session-folders (lsp-session-folders (lsp-session))]
(cons 'lsp-error-list
(-group-by
(lambda (dom-item)
(-let [folder (--first (treemacs-is-path (car dom-item) :in it)
session-folders)]
(treemacs--filename folder)))
new-dom)))))
The output would look like this:
(lsp-error-list
("scripts"
("/home/a/Documents/git/treemacs/src/scripts/treemacs-dirs-to-collapse.py"
("/home/a/Documents/git/treemacs/src/scripts/treemacs-dirs-to-collapse.py"
. #s(lsp-diagnostic 2 0 2 nil "pyflakes" "pyflakes: 'os.path.isfile' imported but unused"))
("/home/a/Documents/git/treemacs/src/scripts/treemacs-dirs-to-collapse.py"
. #s(lsp-diagnostic 1 0 2 nil "pyflakes" "pyflakes: redefinition of unused 'listdir' from line 1")))
("/home/a/Documents/git/treemacs/src/scripts/treemacs-git-status.py"
("/home/a/Documents/git/treemacs/src/scripts/treemacs-git-status.py"
. #s(lsp-diagnostic 57 47 1 nil "pyflakes" "pyflakes: invalid syntax")))))
I noticed that sorting diagnosis files to projects is a often used pattern, so if we get to optimizing that's where we should start.
@Alexander-Miller I am a bit concerned about the dom - will treemacs always require passing the whole tree on update? Also, having the dom tree upfront is not always an option since it might require remote calls. Another drawback is that it will force the clients to implement smart handling of the updates while in the current approach it is not needed.
will treemacs always require passing the whole tree on update?
No, not always. I am preparing for the most complex of use cases: a total update of a variadic extension where I cannot be certain which nodes were deleted, which are new, and what their order is. Having a dom to go by in such a case is a great relief. Anything other than that remains the same old TAB-TAB approach with treemacs-update-node
.
I hope I can make providing the dom optional, it depends on how well I can clean things up in the background on a resync (basically if I record node [A B C] as expanded with a bunch of child elements, but after an update it's deleted - how well can I find and removed that vestige, and how much trouble is it if it's left there).
We should go ahead here anyway, a proper reference implementation will be useful for both of us.
Thank you.
Do you have more comments on #354 ? Also, do you think that I should move lsp-treemacs forward using the artificial "Error" root and later when treemacs supports multiple dynamic roots to switch to that implementation or it would be better to wait for the full support?
Edit: It is not urgent on my side, I just want to plan my work.
Also, do you think that I should move lsp-treemacs forward using the artificial "Error" roo
That should be fine. It'll give me enough breathing room to take care of all the other issues that have since appeared.
With the latest version of I am getting the following error when I delete an branch (e. g. fix an exception which results in removing few nodes) lsp-treemacs.el
Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
get-text-property(nil :depth)
treemacs-goto-node((:custom LSP-Errors "/home/kyoncho/Sources/lsp/dap-mode/features/fixtur..."))
treemacs--reopen-at((:custom LSP-Errors))
(progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let ((result btn)) (if result (progn ...))))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path)))
(let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start (next-button (point-at-bol) t)) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* ((depth depth) (prefix (concat "\n" ...)) (item (cl-first items)) (strings)) (if item (progn (let ... ...))) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let (...) (if result ...)))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path))))
(save-excursion (let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start (next-button ... t)) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* ((depth depth) (prefix ...) (item ...) (strings)) (if item (progn ...)) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let ... ...))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path)))))
(let ((items (lsp-treemacs--root-folders)) (depth (1+ (get-text-property btn :depth)))) (save-excursion (let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start ...) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* (... ... ... ...) (if item ...) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result ...)) (if result (progn ...)))) (treemacs--reopen-at (get-text-property btn :path))))))
treemacs--do-expand-lsp-error-list(#<marker (moves after insertion) at 3 in *LSP Error List*>)
(let* ((btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* ((p ...)) (if (get-char-property p ...) (progn ...))))))) (if (null btn) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "There is nothing to do here.")))) (if (not (eq 'treemacs-lsp-error-list-closed-state (get-text-property btn :state))) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "This function cannot expand a node of type '%s'." (propertize (format "%s" (get-text-property btn :state)) 'face 'font-lock-type-face))))) (treemacs--do-expand-lsp-error-list btn))
(catch '--cl-block-__body__-- (let* ((btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* (...) (if ... ...)))))) (if (null btn) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "There is nothing to do here.")))) (if (not (eq 'treemacs-lsp-error-list-closed-state (get-text-property btn :state))) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "This function cannot expand a node of type '%s'." (propertize (format "%s" ...) 'face 'font-lock-type-face))))) (treemacs--do-expand-lsp-error-list btn)))
treemacs-expand-lsp-error-list()
treemacs-update-node((:custom LSP-Errors))
(save-current-buffer (set-buffer (get-buffer-create "*LSP Error List*")) (treemacs-update-node '(:custom LSP-Errors)))
(condition-case err (save-current-buffer (set-buffer (get-buffer-create "*LSP Error List*")) (treemacs-update-node '(:custom LSP-Errors))) ((debug error) (message "%s" err) nil))
lsp-treemacs--after-diagnostics()
Can you give me a rundown of what happened to the tree - what did it look like at the start, what got deleted, and which part did you update?
Ah, you found a pretty general bug, present in all parts of treemacs, that somehow managed to remain undiscovered till now. Basically treemacs remember the node to be expanded, and tries to re-expand it when you update, but it's deleted, so it fails. With a bit of luck it should be a quick enough fix.
BTW, some of the icons in company-box could be useful for us as well.
Yeah. I am going to implement treemacs versions of type hierarchy/call hierarchy when they are present in the java server and then I will add the icons in treemacs.
treemacs--evade-image
moves the cursor in the begining of the line even if you have navigated to different buffer as a result of ret action.
This is still reproducible.
This is still reproducible.
Should be fixed now.
Minor update: we are actually quite lucky that reopening works recursively by moving to a node's immediate children, otherwise this whole thing would've gotten really tricky. Without the lean, event-based interface of filenotify.el I have no means to determine that the node is gone, except for trial-and-error. Luckily this is basically what's supposed to happen anyway - we try to move to a node and expand it, and when it's not there the move should fail gracefully - no error, and we move back to where we started. It's only the latter part that is failing (I don't think I ever tested the goto-custom-node code quite so thoroughly). I'll make fixing that a priority now, so we should have a solution within the week.
Found an issue in lsp-treemacs: lsp returns directories with a final slash, but treemacs is completely geared towards paths without final slashes. As a start you can call treemacs--unslash
in the final step in lsp-treemacs--root-folders
, but it might be troublesome in the future.
Never mind, custom nodes' keys can look however you like, the final slash restriction only applies to treemacs' own file nodes.
Based on some cursory testing the update seems to be working now. At least for the most part, the hl-line overlay isn't moving quite right.
Left click yields(let me know if it is something that I had to fix):
Debugger entered--Lisp error: (wrong-type-argument window-live-p nil)
#<subr select-window>(nil nil)
ad-Advice-select-window(#<subr select-window> nil)
apply(ad-Advice-select-window #<subr select-window> nil)
select-window(nil)
#f(compiled-function (event) "Move focus to the clicked line.\nMust be bound to a mouse click, or EVENT will not be supplied." (interactive "e") #<bytecode 0x155950e1d199>)((mouse-1 (#<window 85 on *LSP Error List*> 48 (154 . 74) 483937292 nil 48 (38 . 1) nil (-73 . 48) (7 . 26))))
apply(#f(compiled-function (event) "Move focus to the clicked line.\nMust be bound to a mouse click, or EVENT will not be supplied." (interactive "e") #<bytecode 0x155950e1d199>) (mouse-1 (#<window 85 on *LSP Error List*> 48 (154 . 74) 483937292 nil 48 (38 . 1) nil (-73 . 48) (7 . 26))))
treemacs-leftclick-action((mouse-1 (#<window 85 on *LSP Error List*> 48 (154 . 74) 483937292 nil 48 (38 . 1) nil (-73 . 48) (7 . 26))))
funcall-interactively(treemacs-leftclick-action (mouse-1 (#<window 85 on *LSP Error List*> 48 (154 . 74) 483937292 nil 48 (38 . 1) nil (-73 . 48) (7 . 26))))
call-interactively(treemacs-leftclick-action nil nil)
command-execute(treemacs-leftclick-action)
Also, I am getting the following error when I have the whole tree open and I fix all of errors:
Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
get-text-property(nil :parent)
treemacs-goto-node((:custom LSP-Errors "/home/kyoncho/Sources/rust/hello/"))
treemacs--reopen-at((:custom LSP-Errors))
(progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let ((result btn)) (if result (progn ...))))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path)))
(let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start (next-button (point-at-bol) t)) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* ((depth depth) (prefix (concat "\n" ...)) (item (cl-first items)) (strings)) (if item (progn (let ... ...))) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let (...) (if result ...)))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path))))
(let* ((p (point))) (let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start (next-button ... t)) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* ((depth depth) (prefix ...) (item ...) (strings)) (if item (progn ...)) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result (let ... ...))) (if result (progn (get-text-property result :path))))) (treemacs--reopen-at (get-text-property btn :path)))) (count-lines p (point)))
(save-excursion (let* ((p (point))) (let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len (length treemacs-icon-lsp-error-list-open))) (goto-char (- (button-start ...) len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* (... ... ... ...) (if item ...) (nreverse strings))))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let ((result ...)) (if result (progn ...)))) (treemacs--reopen-at (get-text-property btn :path)))) (count-lines p (point))))
(let ((items (lsp-treemacs--root-folders)) (depth (1+ (get-text-property btn :depth)))) (save-excursion (let* ((p (point))) (let (buffer-read-only) (put-text-property (or (previous-single-property-change (1+ btn) 'button) (point-min)) (or (next-single-property-change btn 'button) (point-max)) :state 'treemacs-lsp-error-list-open-state) (beginning-of-line) (save-excursion (let ((len ...)) (goto-char (- ... len)) (insert treemacs-icon-lsp-error-list-open) (delete-char len))) (end-of-line) (progn (insert (apply #'concat (let* ... ... ...)))) (progn (treemacs-on-expand (get-text-property btn :path) btn (let (...) (if result ...))) (treemacs--reopen-at (get-text-property btn :path)))) (count-lines p (point)))))
treemacs--do-expand-lsp-error-list(#<marker (moves after insertion) at 3 in *LSP Error List*>)
(let* ((btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* ((p ...)) (if (get-char-property p ...) (progn ...))))))) (if (null btn) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "There is nothing to do here.")))) (if (not (eq 'treemacs-lsp-error-list-closed-state (get-text-property btn :state))) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "This function cannot expand a node of type '%s'." (propertize (format "%s" (get-text-property btn :state)) 'face 'font-lock-type-face))))) (treemacs--do-expand-lsp-error-list btn))
(catch '--cl-block-__body__-- (let* ((btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* (...) (if ... ...)))))) (if (null btn) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "There is nothing to do here.")))) (if (not (eq 'treemacs-lsp-error-list-closed-state (get-text-property btn :state))) (progn (throw '--cl-block-__body__-- (treemacs-pulse-on-failure "This function cannot expand a node of type '%s'." (propertize (format "%s" ...) 'face 'font-lock-type-face))))) (treemacs--do-expand-lsp-error-list btn)))
treemacs-expand-lsp-error-list()
treemacs-update-node((:custom LSP-Errors))
-> treemacs-goto-node((:custom LSP-Errors "/home/kyoncho/Sources/rust/hello/"))
This node is not present.
Overlay issue is that treemacs-update-node
does not save position, but does call hl-line-highlight
. I'll probably split it up like delete-node
into update
and do-update
, where the former saves position and re-highlights and the latter does not, so one version is faster and can easier be used in bulk.
The save-position
macro is not behaving quite right for an all-custom-node setup either, so I'll need to look at that as well.
treemacs-goto-node((:custom LSP-Errors "/home/kyoncho/Sources/rust/hello/")) treemacs--reopen-at((:custom LSP-Errors))
Chunks of the re-open logic still aren't quite right it seems.
Left click yields(let me know if it is something that I had to fix):
That's a bug in the mouseclick action, it is trying to select (treemacs-get-local-window)
, so I will need to fix that.
Both bugs should be fixed now.
Thank you, I retest and let you know. I have started the melpa onboarding for lsp-treemacs.
I am getting this error with the latest lsp-treemacs from time to time.
Debugger entered--Lisp error: (error "Node at path (:custom LSP-Errors) cannot be found")
signal(error ("Node at path (:custom LSP-Errors) cannot be found"))
error("Node at path %s cannot be found" (:custom LSP-Errors))
(if btn (progn (if (memq (get-text-property btn :state) treemacs--open-node-states) (progn (let* ((close-func (alist-get ... treemacs-TAB-actions-config))) (funcall close-func) (if (= 1 (funcall ...)) (progn (funcall close-func))))))) (error "Node at path %s cannot be found" path))
(let ((btn (treemacs-goto-node path))) (if btn (progn (if (memq (get-text-property btn :state) treemacs--open-node-states) (progn (let* ((close-func ...)) (funcall close-func) (if (= 1 ...) (progn ...)))))) (error "Node at path %s cannot be found" path)))
(let* ((curr-btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* ((p ...)) (if (get-char-property p ...) (progn ...)))))) (next-path (let ((result (let (...) (if result ...)))) (if result (progn (button-get result :path))))) (prev-path (let ((result (let (...) (if result ...)))) (if result (progn (button-get result :path))))) (curr-node-path (let ((result curr-btn)) (if result (progn (get-text-property result :path))))) (curr-state (let ((result curr-btn)) (if result (progn (get-text-property result :state))))) (curr-file (let ((result curr-btn)) (if result (progn (treemacs--nearest-path result))))) (curr-tagpath (let ((result curr-btn)) (if result (progn (treemacs--tags-path-of result))))) (curr-window (treemacs-get-local-window)) (curr-win-line (if curr-window (progn (let ((save-selected-window--state ...)) (save-current-buffer (unwind-protect ... ...))))))) (let ((btn (treemacs-goto-node path))) (if btn (progn (if (memq (get-text-property btn :state) treemacs--open-node-states) (progn (let* (...) (funcall close-func) (if ... ...))))) (error "Node at path %s cannot be found" path))) (cond ((memq curr-state '(file-node-closed file-node-open dir-node-closed dir-node-open root-node-closed root-node-open)) (if (and (file-exists-p curr-file) (or treemacs-show-hidden-files (not (s-matches\? treemacs-dotfiles-regex ...)))) (treemacs-goto-file-node curr-file) (let (--cl-can-move-to--) (setq --cl-can-move-to-- #'(lambda ... ...)) (cond ((and next-path ...) (treemacs-goto-file-node next-path)) ((and prev-path ...) (treemacs-goto-file-node prev-path)) (t (let* ... ... ...)))))) ((memq curr-state '(tag-node tag-node-closed tag-node-open)) (treemacs--goto-tag-button-at curr-tagpath)) ((null curr-state) (goto-char (point-min))) (t (condition-case _ (treemacs-goto-node curr-node-path) (error (ignore))))) (treemacs--evade-image) (if curr-win-line (progn (let ((save-selected-window--state (internal--before-with-selected-window curr-window))) (save-current-buffer (unwind-protect (progn (select-window ... ...) (recenter ...)) (internal--after-with-selected-window save-selected-window--state)))))))
(progn (let* ((curr-btn (let* ((point-at-bol (point-at-bol))) (if (get-text-property point-at-bol 'button) (copy-marker point-at-bol t) (let* (...) (if ... ...))))) (next-path (let ((result (let ... ...))) (if result (progn (button-get result :path))))) (prev-path (let ((result (let ... ...))) (if result (progn (button-get result :path))))) (curr-node-path (let ((result curr-btn)) (if result (progn (get-text-property result :path))))) (curr-state (let ((result curr-btn)) (if result (progn (get-text-property result :state))))) (curr-file (let ((result curr-btn)) (if result (progn (treemacs--nearest-path result))))) (curr-tagpath (let ((result curr-btn)) (if result (progn (treemacs--tags-path-of result))))) (curr-window (treemacs-get-local-window)) (curr-win-line (if curr-window (progn (let (...) (save-current-buffer ...)))))) (let ((btn (treemacs-goto-node path))) (if btn (progn (if (memq (get-text-property btn :state) treemacs--open-node-states) (progn (let* ... ... ...)))) (error "Node at path %s cannot be found" path))) (cond ((memq curr-state '(file-node-closed file-node-open dir-node-closed dir-node-open root-node-closed root-node-open)) (if (and (file-exists-p curr-file) (or treemacs-show-hidden-files (not ...))) (treemacs-goto-file-node curr-file) (let (--cl-can-move-to--) (setq --cl-can-move-to-- #'...) (cond (... ...) (... ...) (t ...))))) ((memq curr-state '(tag-node tag-node-closed tag-node-open)) (treemacs--goto-tag-button-at curr-tagpath)) ((null curr-state) (goto-char (point-min))) (t (condition-case _ (treemacs-goto-node curr-node-path) (error (ignore))))) (treemacs--evade-image) (if curr-win-line (progn (let ((save-selected-window--state (internal--before-with-selected-window curr-window))) (save-current-buffer (unwind-protect (progn ... ...) (internal--after-with-selected-window save-selected-window--state))))))))
(unwind-protect (progn (let* ((curr-btn (let* ((point-at-bol ...)) (if (get-text-property point-at-bol ...) (copy-marker point-at-bol t) (let* ... ...)))) (next-path (let ((result ...)) (if result (progn ...)))) (prev-path (let ((result ...)) (if result (progn ...)))) (curr-node-path (let ((result curr-btn)) (if result (progn ...)))) (curr-state (let ((result curr-btn)) (if result (progn ...)))) (curr-file (let ((result curr-btn)) (if result (progn ...)))) (curr-tagpath (let ((result curr-btn)) (if result (progn ...)))) (curr-window (treemacs-get-local-window)) (curr-win-line (if curr-window (progn (let ... ...))))) (let ((btn (treemacs-goto-node path))) (if btn (progn (if (memq ... treemacs--open-node-states) (progn ...))) (error "Node at path %s cannot be found" path))) (cond ((memq curr-state '(file-node-closed file-node-open dir-node-closed dir-node-open root-node-closed root-node-open)) (if (and (file-exists-p curr-file) (or treemacs-show-hidden-files ...)) (treemacs-goto-file-node curr-file) (let (--cl-can-move-to--) (setq --cl-can-move-to-- ...) (cond ... ... ...)))) ((memq curr-state '(tag-node tag-node-closed tag-node-open)) (treemacs--goto-tag-button-at curr-tagpath)) ((null curr-state) (goto-char (point-min))) (t (condition-case _ (treemacs-goto-node curr-node-path) (error (ignore))))) (treemacs--evade-image) (if curr-win-line (progn (let ((save-selected-window--state ...)) (save-current-buffer (unwind-protect ... ...))))))) (with-no-warnings (setq treemacs--ready-to-follow o)))
(let ((o (with-no-warnings treemacs--ready-to-follow))) (with-no-warnings (setq treemacs--ready-to-follow nil)) (unwind-protect (progn (let* ((curr-btn (let* (...) (if ... ... ...))) (next-path (let (...) (if result ...))) (prev-path (let (...) (if result ...))) (curr-node-path (let (...) (if result ...))) (curr-state (let (...) (if result ...))) (curr-file (let (...) (if result ...))) (curr-tagpath (let (...) (if result ...))) (curr-window (treemacs-get-local-window)) (curr-win-line (if curr-window (progn ...)))) (let ((btn (treemacs-goto-node path))) (if btn (progn (if ... ...)) (error "Node at path %s cannot be found" path))) (cond ((memq curr-state '...) (if (and ... ...) (treemacs-goto-file-node curr-file) (let ... ... ...))) ((memq curr-state '...) (treemacs--goto-tag-button-at curr-tagpath)) ((null curr-state) (goto-char (point-min))) (t (condition-case _ (treemacs-goto-node curr-node-path) (error ...)))) (treemacs--evade-image) (if curr-win-line (progn (let (...) (save-current-buffer ...)))))) (with-no-warnings (setq treemacs--ready-to-follow o))))
treemacs-update-node((:custom LSP-Errors))
(save-excursion (treemacs-update-node '(:custom LSP-Errors)))
(save-current-buffer (set-buffer (get-buffer-create "*LSP Error List*")) (save-excursion (treemacs-update-node '(:custom LSP-Errors))))
(condition-case err (save-current-buffer (set-buffer (get-buffer-create "*LSP Error List*")) (save-excursion (treemacs-update-node '(:custom LSP-Errors)))) ((debug error) (message "%s" err) nil))
lsp-treemacs--after-diagnostics()
run-hooks(lsp-after-diagnostics-hook)
Hi, I will use this PR for feedback/question on Extension API. Please let me know whether this is the preffered format for you.