-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Use tree item path for tree node id. Fixes #12111 #12120
Use tree item path for tree node id. Fixes #12111 #12120
Conversation
const candidateId = this.buildTreeItemId(parentId, treeItem); | ||
if (this.nodes.has(candidateId)) { | ||
return chain.concat(candidateId); |
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.
Does this new code mean we'll never hit the cache for tree items for which we compute an ID?
The previous logic seemed to assume we could find items from some possibleIndex
, but now there's a case where buildTreeItemId
generates a new unique ID for each call, which means that those items will never be found here.
Is this an issue?
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.
Only if the tree item does not have an id set. This is correct, in my opinion, because if it doesn't have an id, there's no way to tell it's the same tree item, so any state we might have for the tree item at index n might not apply to the new item at index n.
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.
Also, the documentation of TreeItem
says:
If not provided, an id is generated using the tree item's label. Note that when labels change, ids will change and that selection and expansion state cannot be kept stable anymore.
If you look at the way id's are constructed, the id's will be the same for items that have the same label.
This seems unfortunate. Can you give an idea of the code to reparent a node that you think should work (in an ideal world), but doesn't? |
Any time you use the same id with a different parent, think dragging a file to a different folder (the "real" file explorer uses the full path as a key, which is what I'm doing in this PR). The problem is described in #12111 |
@paul-marechal @marcdumais ping? |
@colin-grant-work the code assumes that there is a 1:1 relationship from tree item to underlying domain element. Since we're doing an asynchronous update for the tree, this is not true when reparenting a domain object (because some of the tree is still using the "old" state, while other parts are using the "new" state). So having code that can handle n:1 between tree items and domain objects would work. It would also allow showing DAGs as a tree. |
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.
Functionally, things appear to be working. The only missing element there, not related to this PR, is that we get no drag-over highlight on tree nodes when dragging over them.
I have some other commends and questions about the code.
@@ -274,7 +277,7 @@ class TreeViewExtImpl<T> implements Disposable { | |||
this.proxy.$setDescription(this.treeViewId, this._description); | |||
} | |||
|
|||
getTreeItem(treeItemId: string): T | undefined { | |||
getElement(treeItemId: string): T | undefined { |
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.
Same here re: naming.
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.
And same answer ;-)
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.
Allows nodes to be moved among parents smoothly 👍
- Since our front end tree is not safe for reparenting nodes, use the path of tree item ids as the tree node id. - Rename TreeViewExt.getTreeItem to getElement, since that is what it does. Contributed on behalf of ST Microelectronics Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
6acbbdf
to
b07dec7
Compare
Use tree item path for tree node id. Fixes #12111 - Since our front end tree is not safe for reparenting nodes, use the path of tree item ids as the tree node id. - Rename TreeViewExt.getTreeItem to getElement, since that is what it does. Contributed on behalf of ST Microelectronics Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
private buildTreeItemId(parentId: string, item: TreeItem): string { | ||
// build tree id according to https://code.visualstudio.com/api/references/vscode-api#TreeItem | ||
// note: the front end tree implementation cannot handle reparenting items, hence the id is set to the "path" of individual ids | ||
let id = typeof item.id === 'string' ? item.id : this.getItemLabel(item); | ||
if (id) { | ||
// we use '' as the id of the root, we don't consider that a valid id | ||
// since '' is falsy, we'll never get '' in this branch | ||
id = TreeViewExtImpl.ID_ITEM + id; | ||
} else { | ||
id = TreeViewExtImpl.ID_COMPUTED + this.nextItemId++; | ||
} | ||
return `${parentId}/${id}`; |
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.
@tsmaeder, this turns out to be a dangerous way to build the ID. If getItemLabel
returns the same thing for several children of the same item - we've seen this downstream in the references view when the lines where something is referred to are identical - then this produces children with the exact same ID's, which isn't permitted in our core Tree
implementation. Should be able to reproduce in Theia in a moment.
Contributed on behalf of ST Microelectronics
Signed-off-by: Thomas Mäder t.s.maeder@gmail.com
What it does
Fixes #12111
Since the Theia front end tree cannot handle reparenting tree nodes with the same id, this PR fixes the problem for tree views contributed via the VS Code API. The idea is to concatenate the id of the parent
TreeItem
with the id of the tree item itself to use as the id of theTreeViewItem
that is sent to the front end. Thus, the id of the tree node in the front end will be different when being reparented, thus working around the problem.How to test
Install the extension from #12065 (comment) and test that elements can be properly reparented and that selection and expansion behaviour is as expected. Note that, since the id changes when reparenting, an item that is dragged to a different folder will not be selected after the move. I consider this an acceptable trade-off considering the current state may lead to an endless loop. I was not able to think of a fix that would avoid this without thoroughly refactoring the tree subsystem.
Review checklist
Reminder for reviewers