Skip to content
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

"vscode.TreeItemCollapsibleState" Never Changes #13946

Open
rroessler opened this issue Jul 24, 2024 · 7 comments
Open

"vscode.TreeItemCollapsibleState" Never Changes #13946

rroessler opened this issue Jul 24, 2024 · 7 comments
Labels
tree issues related to the tree (ex: tree widget) vscode issues related to VSCode compatibility

Comments

@rroessler
Copy link

rroessler commented Jul 24, 2024

Bug Description:

In VSC extensions, whenever a refresh is requested by a vscode.TreeDataProvider, trees are refreshed to an "initialized" collapsible state instead of maintaining their current collapsible state.

This is an issue for tree-views that show real-time data (eg: custom debugger panels) as refreshes will force all nodes to snap back to their original collapsible state.

Steps to Reproduce:

This issue can be reproduced by implementing a simple tree-view provider that refreshes every 5-seconds. If the root-node is clicked, anytime the interval fires the root-node will reset to the original collapsed state as opposed to adhering to the current expanded state.

import vscode from 'vscode';

class Provider implements vscode.TreeDataProvider {
    private m_root = new vscode.TreeItem('Root', vscode.TreeItemCollapsibleState.Collapsed);
    private m_children = Array.from(Array(5), (_, ii) => new vscode.TreeItem(`Node: ${ii}`));

    readonly emitter = new vscode.EventEmitter<vscode.TreeItem | vscode.TreeItem[] | undefined | null | void>();
    readonly onDidChangeTreeData = this.emitter.event.bind(this.emitter);

    getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
        return element;
    }

    getChildren(element?: vscode.TreeItem | undefined): vscode.ProviderResult<vscode.TreeItem[]> {
        if (typeof element === 'undefined') return [this.m_root];
        return element === this.m_root ? this.m_children : [];
    }
}

export function activate(context: vscode.ExtensionContext) {
    const treeDataProvider = new Provider();
    context.subscriptions.push(vscode.window.createTreeView('exampleTreeProvider', { treeDataProvider }));

    // set an interval to trigger refreshes every 5 seconds
    const interval = setInterval(() => treeDataProvider.emitter.fire(), 5000);
    context.subscriptions.push(() => clearInterval(interval));
}

Steps to Fix:

This could be fixed by adding a toggle for expanded and collapsed states of nodes at the following:

 async onExpanded(treeItemId: string): Promise<any> {
    // get element from a cache
    const cachedElement = this.getElement(treeItemId);

    // fire an event
    if (cachedElement) {
        // ensure we cache the expanded state now
        if (cachedElement.collapsibleState) cachedElement.collapsibleState = TreeItemCollapsibleState.Expanded;

        this.onDidExpandElementEmitter.fire({
            element: cachedElement
        });
    }
}

async onCollapsed(treeItemId: string): Promise<any> {
    // get element from a cache
    const cachedElement = this.getElement(treeItemId);

    // fire an event
    if (cachedElement) {
        // ensure we cache the collapsed state now
        if (cachedElement.collapsibleState) cachedElement.collapsibleState = TreeItemCollapsibleState.Collapsed;

        this.onDidCollapseElementEmitter.fire({
            element: cachedElement
        });
    }
}
@rroessler rroessler changed the title "vscode.TreeItemCollapsible" Never Changes "vscode.TreeItemCollapsibleState" Never Changes Jul 24, 2024
@rroessler
Copy link
Author

Alongside this, triggering refreshes via "onDidChangeTreeData" causes the current scroll-state to reset to an initial position.

@tsmaeder
Copy link
Contributor

@rroessler do you know what the behavior in VS Code is?

@rroessler
Copy link
Author

@tsmaeder The original behaviour in VS Code is that when onDidChangeTreeData is called with specific node as arguments, only those nodes are refreshed. This means that parent nodes would not be refreshed, maintaining their current expansion state.

Currently the Theia extension wrapper forces a refresh on the entire TreeView widget. As such, nodes that may have been created with a collapsed state (eg: at getChildren or getTreeItem) but where then expanded before a refresh occurs, are all refreshed causing flickering/odd effects if refreshing occurs frequently.

Alongside this, the VS Code behaviour has the added bonus of being more efficient as only the nodes requested are refreshed, as opposed to the Theia implementation of refresh all from the root.

@tsmaeder
Copy link
Contributor

@rroessler just so we're talking about the same thing: the example extension you give refreshes the root of the tree, though, right?

@rroessler
Copy link
Author

@tsmaeder you are correct as the issue also arises from here too. The difference between the two behaviours stems from:

VS Code:

  • When a tree-item "change" event occurs, only that node and its children are refreshed.
  • Additionally, tree-items are have an associated state that is maintained between refreshes (eg: expansion, scroll-top).

Theia:

  • When a tree-item "change" event occurs, the entire tree is refreshed/rebuilt.
  • This means that every node is recreated from their source and the associated state is not maintained between refreshes.

@tsmaeder
Copy link
Contributor

@rroessler since you seem to know how what to do, would you be ready to contribute the fix?

@msujew msujew added tree issues related to the tree (ex: tree widget) vscode issues related to VSCode compatibility labels Jul 30, 2024
@rroessler
Copy link
Author

@tsmaeder I will try my best to get a fix soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
tree issues related to the tree (ex: tree widget) vscode issues related to VSCode compatibility
Projects
None yet
Development

No branches or pull requests

3 participants