Skip to content

add focus state to minihtml tree view#2954

Open
rchl wants to merge 9 commits into
mainfrom
feat/tree-focus-state
Open

add focus state to minihtml tree view#2954
rchl wants to merge 9 commits into
mainfrom
feat/tree-focus-state

Conversation

@rchl

@rchl rchl commented Jun 13, 2026

Copy link
Copy Markdown
Member

The idea: implement focus handling in tree view (used by call hierarchy) so that it's possible to navigate quickly using keyboard. I kinda want this functionality for another use case that doesn't exist yet (navigating symbol list) but it would be nice to have for call hierarchy also.

Problem with implementing keyboard navigation for call hierarchy is that clicking a caller focuses another view which is interfering with being able to navigate quickly. This is not a solved problem and it means that supporting keyboard navigation in it might not be feasible (though I haven't thought hard enough about it yet). That said, supporting focus state would be useful anyway.

I've included some keybindings and a command for navigating using keyboard for testing but those are not meant to be integrated in their current form, most likely. This PR mostly just concerns adding focus state. The keyboard navigation is what it allows to build on top of that but this PR is not really meant to add that, it's here just to be able to test the focus state.

Screen.Recording.2026-06-13.at.12.26.47.mov

@rchl

rchl commented Jun 16, 2026

Copy link
Copy Markdown
Member Author

(forgot to add test keybindings)

@netlify

netlify Bot commented Jun 16, 2026

Copy link
Copy Markdown

Deploy Preview for sublime-lsp ready!

Name Link
🔨 Latest commit 9be808d
🔍 Latest deploy log https://app.netlify.com/projects/sublime-lsp/deploys/6a32e079620774000969e47c
😎 Deploy Preview https://deploy-preview-2954--sublime-lsp.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@jwortmann

Copy link
Copy Markdown
Member

It's not really clear from the description and code comments which parts of this PR are intended to be included in LSP, but a few things that I saw from a quick look over the code are:

  • instead of using is_enabled, I think LSP should better provide a context lsp.tree_view_sheet_has_focus, which I think would be possible from

    LSP/boot.py

    Line 226 in 8b579d8

    class Listener(sublime_plugin.EventListener):
  • that context should be enabled for all instances of
    class TreeViewSheet(sublime.HtmlSheet):
    """A special HtmlSheet which can render interactive tree data structures."""
    and not only for "Call Hierarchy" (we also have a "Type Hierarchy" implementation)
  • I would probably not add the "use ... to move around" line, because there also doesn't exist such a hint in the ST sidebar. But that's not too important to me, because space isn't really a limiting factor in the TreeViewSheet.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

The idea was to not include key bindings and the command for navigating.
That said, I could make it so that navigating using keyboard doesn't "click" the item but just activates it and then make enter trigger "click". Then including those would be fine.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author
  • instead of using is_enabled, I think LSP should better provide a context lsp.tree_view_sheet_has_focus, which I think would be possible from

I thought about it but couldn't think of pros and cons of either.
I thought that for key bindings it doesn't matter and for potential menu items, it would even be more correct to use "is_enabled".

@jwortmann

Copy link
Copy Markdown
Member

I thought that for key bindings it doesn't matter and for potential menu items, it would even be more correct to use "is_enabled".

Yes, but we don't have any menu item for up/down/left/right (LspMoveFocusCommand) and hopefully also won't add that in the future.

Adding key bindings for such essential keys up/down/left/right without any context restriction would feel too dangerous from my side. I doubt that something like that would be accepted if this were a new package for https://github.com/sublimehq/package_control_channel

That said, I could make it so that navigating using keyboard doesn't "click" the item but just activates it and then make enter trigger "click". Then including those would be fine.

I haven't tested the feature, but that seems like a better idea. That is also the behavior in the sidebar.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

Adding key bindings for such essential keys up/down/left/right without any context restriction would feel too dangerous from my side. I doubt that something like that would be accepted if this were a new package for sublimehq/package_control_channel

I am not sure but I think is_enabled and context have essentially equivalent behavior so I don't think that is_enabled is more dangerous. As I've realized in sublimelsp/LSP-json#231 (comment), a context doesn't have any advantage if the command is not available for some reason - ST still wouldn't ignore it.

That said, I'm not against changing to context, if only for better transparency.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author
  • instead of using is_enabled, I think LSP should better provide a context lsp.tree_view_sheet_has_focus, which I think would be possible from
  1. HtmlSheets don't have views so ViewListener doesn't trigger for those.
  2. is_enabled is indeed not working as expected - keybinding is not ignored when it returns false.

@jwortmann

Copy link
Copy Markdown
Member
1. HtmlSheets don't have views so `ViewListener` doesn't trigger for those.

EventListener should be triggered when checking context for key bindings executing a WindowCommand, I assume. But I haven't tested that.

2. `is_enabled` is indeed not working as expected - keybinding is not ignored when it returns false.

That was what I assumed. Because the key binding from the sublime-keymap file would just override the corresponding bindings from the Default package, if not restricted by a context.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

EventListener should be triggered when checking context for key bindings executing a WindowCommand, I assume. But I haven't tested that.

No, on_query_context in ViewListener has a view argument so it triggers in context of a view (which is not present here).

HtmlSheets are half baked... I don't see any way to ensure that key binding can only trigger in their context.

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

@rchl

rchl commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

Pushed my keybinding updates which DON'T WORK given the above. I will have to remove those from the PR but maybe ST will wake up one day and make it possible...

Comment thread Default.sublime-keymap
Comment on lines +247 to +283
// Navigate in Tree Views
{
"keys": ["left"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_left"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["right"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_right"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["up"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_up"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["down"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_down"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["enter"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "activate"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["escape"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "close"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since those use context that is never set, we could keep those in the code, waiting for better times?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably comment them out for now. It seems that ST considers the context as "false" if there is no plugin that handles this context key. If we want to keep the key bindings, maybe we should already add a handler like

def on_query_context(self, view: sublime.View, key: str, operator: sublime.QueryOperator, operand: str, match_all: bool) → bool | None:
    if key == 'lsp.tree_view_sheet_has_focus':
        return False
    return None

Comment thread plugin/core/tree_view.py
self.id = str(uuid.uuid4())

def html(self, sheet_name: str, indent_level: int) -> str:
def html(self, sheet_name: str, indent_level: int, is_active: bool = False) -> str:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe has_focus is better?

Comment thread plugin/core/tree_view.py
padding-left: 0.5rem;
}}
.active {{
background-color: color(var(--accent));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think something like color(var(--accent) alpha(0.3)) would be better because otherwise the text foreground might not have sufficient contrast to the accent background color.

Comment thread Default.sublime-keymap
Comment on lines +247 to +283
// Navigate in Tree Views
{
"keys": ["left"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_left"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["right"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_right"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["up"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_up"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["down"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "move_down"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["enter"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "activate"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},
{
"keys": ["escape"],
"command": "lsp_handle_tree_view_action",
"args": {"action": "close"},
"context": [{"key": "lsp.tree_view_sheet_has_focus"}]
},

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would probably comment them out for now. It seems that ST considers the context as "false" if there is no plugin that handles this context key. If we want to keep the key bindings, maybe we should already add a handler like

def on_query_context(self, view: sublime.View, key: str, operator: sublime.QueryOperator, operand: str, match_all: bool) → bool | None:
    if key == 'lsp.tree_view_sheet_has_focus':
        return False
    return None

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants