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

Implement keyboard navigation for the error panel #1750

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

kaste
Copy link
Contributor

@kaste kaste commented Jun 18, 2020

Fixes #1717

As discussed in #1717, explore a keyboard centric error panel.

Ships with default bindings for Windows only. (This is just for convenience
here.)

  {
      "keys": ["ctrl+k", "ctrl+s"],
      "command": "sublime_linter_panel_toggle",
      "args": {"focus": true}
  },

Enable standard "show_panel" with "focus: True" to open the panel and focus
it. Our toggle panel takes this arg as well and passes it through.

Generally, you somehow focus the panel, then <up>/<down> will move
to next or previous error transiently, t.i. without storing this move in
Sublime's navigation history. <enter> will commit this new location to
said history, while <escape> will discard the previewing and return to the
original location. (Overall this mimics for example the Goto Symbol quick
panel.) Note that <escape> is not specially bound here, we instead
introspect or observe the standard "hide_command" with "cancel: true" command
and act upon it.

On top of this, we may close the panel only if you opened the panel with
"focus: true". (T.i. you did not move the focus to an already open panel.)

Note: just click and <enter> is not supported here (but could be added without
much code), click and then <up>/<down> is. Since this PR is about keyboard
workflows, enabling this wasn't a high priority. 😏

Allows navigation to a "next" file if the current file has dependent errors in other
files but not a previous!

EKREvTx8G1

  • Truly restore viewport on "discard"
  • Fix old standing bug when switching windows the active view gets out of sync. Generally we have a panel per window and we should track the active view per window and not globally here.

@kaste
Copy link
Contributor Author

kaste commented Jun 18, 2020

Updated the main comment to reflect the new behavior.

@kaste kaste force-pushed the panel-keyboard-navigation branch from 558c1f0 to 56c142d Compare July 10, 2020 17:11
@kaste kaste changed the base branch from master to dev July 10, 2020 17:11
@kaste
Copy link
Contributor Author

kaste commented Jul 10, 2020

Updated again, very slick, not completely trivial.

@kaste kaste added the 👀 Features and changes worth looking at label Jul 10, 2020
@kaste kaste marked this pull request as ready for review July 10, 2020 17:29
@kaste kaste force-pushed the panel-keyboard-navigation branch from 56c142d to e0fee64 Compare July 14, 2020 12:05
@braver
Copy link
Member

braver commented Jul 20, 2020

I don't know, but I don't see what this supposed to be doing, so I'm doing something wrong and I don't know what.

So, I've changed my key binding for sublime_linter_panel_toggle to include "args": {"focus": true}:

{ "keys": ["ctrl+super+a"], "command": "sublime_linter_panel_toggle", "args": {"focus": true} },

So, when I open the panel using that the panel should get focus? I do seem to see that focus shift sometimes but it looks completely random and it doesn't stick. As I understand it I should be able to use the arrow keys to then move between errors in the panel? That doesn't work at all for me, the arrows just move the cursor in the main window.

I also don't really 100% understand the goal here. Even without this PR, I can open the output panel and use prev_result/next_result (I bind them to ALT+arrows up/down) to move through all my errors. I can even close the output panel if it's in the way and continue using prev/next just as I would with e.g. a build system output or search output. I know we've been through all this before and there is a bug with prev_result/next_result in combination with jump back/forward. But it is the standard way to move through all sorts of output in Sublime Text and we already perfectly support that. I don't necessarily rejoice in adding this non-trivial feature that seems to replicate what I already have, but then again, I haven't actually used it yet because it doesn't work... and I will reserve judgement until I do 😉

But, maybe I just need to see this working to get it. So, what am I doing wrong and how do I get this:

Enable standard "show_panel" with "focus: True" to open the panel and focus it.

I'm at e0fee64 and I pass the args to the command as mentioned above. Why doesn't it focus?

@kaste
Copy link
Contributor Author

kaste commented Jul 20, 2020

The focus stuff is just in here.

SublimeLinter/panel_view.py

Lines 291 to 296 in e0fee64

panel = get_panel(window)
window.focus_view(panel)
if not args.get('focus', False):
window.focus_group(active_group)
window.focus_view(active_view)

Basically, the first focus line on L292. Before we always moved focus back to the view here which is now in a conditional. The arrow keys don't work if you don't get the focus to the panel. (Actually, it is completely unimportant how a user gets the focus to the panel, and I wasn't sure if there is a more standard way. So this was actually the least I'm-confident part of the PR 😁.) Anyhow, this is one line and works 100% reliable on my ST3. Maybe add some print statements here. Probably ST4 bug? Does it unfocus the panel even if you click in it with the mouse? (Randomly, or when new results fly in?) Or does it unfocus when moving up and down? For movements, I really just use window.open_file which is used in the "Goto symbol" code. It doesn't steal the focus here. 🤷

I'm using this all the time now, almost 100%.

@braver
Copy link
Member

braver commented Jul 21, 2020

Ok, sounds easy enough, I’ll go bug hunting .

@braver
Copy link
Member

braver commented Jul 21, 2020

It does window.focus_view(panel), and for a second the focus does seem to have shifted to the panel. When I hit ⬆️ though, it takes forever to respond and focus seems to go back to the main view. For some reason the binding you introduce for the up arrow wreaks havoc on my machine, it's back to normal if I go back to the dev branch.

@kaste
Copy link
Contributor Author

kaste commented Jul 21, 2020

So generally window.open_file now steals the focus. I added a commit to bring the focus back to the panel.

More generally, ST4 is laggy. It is slower for this operation, not instant. I can replicate this for the "Goto Symbol" quick panel as well. The "Goto Symbol" is kinda fast in safe mode but also not instant. With plugins enabled there is noticeable lag. (T.i. up/down is not instant anymore; easily to reproduce if you hold down the up/down key it does not update whereby ST3 scrolls through the items.) It looks like they delegate to the worker or yield back the UI thread at least so that plugins get a chance to work here in between. In ST3 open_file isfeels more blocking, and since the task/work Sublime has to do here is rather small (or ST3 is rather fast) it is usually instant.

@kaste
Copy link
Contributor Author

kaste commented Jul 21, 2020

The focus management is a bit special in ST3 because output panels behave special here. For example if a panel has focus, you cannot just focus_view a normal view, you have to "force" focus, first focusing the group then focusing the view. (As an explanation, because for open_file of course changing the focus seems natural, but stealing from an output panel is something different.)

@kaste
Copy link
Contributor Author

kaste commented Jul 22, 2020

Let's call it what it is. You use alpha software here.

  1. window.open_file activates the view iff the file is the same as the already active_view (t.i. a "real"/"normal" view) but not on the first call, it starts with the second call. "transient" views only see "selection change" events.

  2. However, there is no preview anymore, every move participates in the history. So "jump back" is broken. This can be reproduced using the normal "Goto Reference" or "Goto Symbol" as well. Just walk around the items in the quick panel, hit "ESC" or "enter", doesn't matter as all visited locations are in the history. (So basically the distinction this PR uses between "commit" and "discard" or generally "previewing" files is fundamentally broken.)

  3. Generally, "Goto Reference" and "Goto File" are laggy, easy to reproduce if you hold down the arrow up or down keys. Using "Goto File" ST3 was capable of blasting through all possible files. ST4 blocks updating the UI ("freezes" and stutters).

...

Erm, "Goto Reference side-by-side", splits the view, and then navigating the quick panel both panes move and show the same content. 💩 And how do I unsplit, or move the cursor between panes?

@braver
Copy link
Member

braver commented Jul 22, 2020

Well it’s still in “closed beta”, so sure, probably the issue is there. Maybe you can file the bug reports upstream? I can throw them in the chat somewhere and they’re usually pretty good at following those things up, but I probably can’t give any explanation or details if they need them.

I’ll have to test again using ST3 then. Darn.

@kaste
Copy link
Contributor Author

kaste commented Jul 27, 2020

Okay, the innocuous looking panel.find_all_results() I use here (because it was there and did what I needed) has a performance excess even for rather few items in the panel (or in the Find in Files Results buffer; t.i. it's not our regex, it's rather the implementation on the Sublime side). For example having 350 errors in the panel already yields to 600ms execution time for that API call. (Calling this on a find buffer with 1000 matches, freezes Sublime for several seconds.)

@braver
Copy link
Member

braver commented Jul 28, 2020

Ok, so now what? It won't work until they improve stuff upstream?

Base automatically changed from dev to master August 3, 2020 14:29
@kaste kaste force-pushed the panel-keyboard-navigation branch from 6af0981 to ea566d8 Compare August 3, 2020 14:33
@kaste
Copy link
Contributor Author

kaste commented Aug 3, 2020

Well, no, I just used find_all_results because it was there. We just use our error store. I mean sure we know which error we just draw in the panel.

@braver
Copy link
Member

braver commented Aug 5, 2020

Oh, now it works. And I get it now. Some bugs/quirks though:

If I click into the panel it doesn't work the first time I hit an arrow key. After a few hits it starts moving correctly though.

What feels kinda weird is that it doesn't start at the line I clicked, when I click into the panel.

It breaks the prev/next navigation in the output panel, in the sense that I can only ever go "up"/"prev" to results in other files, not down. Does it rewrite the output panel when you switch files in a way that the current file is always at the bottom?

@kaste
Copy link
Contributor Author

kaste commented Aug 5, 2020

Well, the combination of mouse interaction and keyboard navigation is not well defined. I basically focused on the keyboard driven workflow because it is the initial workflow this PR wants to make better. ("preview"/"commit"/"discard")

There are design questions here.

What would be a typical way to actually bring the focus to a panel? For now I went with two shortcuts and by listening for "show_panel" with focus: True. I surprisingly haven't found (in the forum etc.) a common way to do this. (I'm looking for a common keybindign to bring the focus to the Terminus bash panel, the ST console, the SL error panel etc...)

How do we visually show that the panel has focus? Here again I searched the forum because I would have thought there must be some (newer) theme thing I never implemented in my own hacky theme. For example, making the background a bit lighter on focus, or draw the thin border in a color. But there is none.

The SL panel is a bit strange here because it actively destroys any cursor. One of the rare instances of that you actually can't just use view.sel()[0] because for the panel there might be no cursor. Instead we draw a dangle. But maybe we should draw a normal cursor iff the panel has focus. Just for the visual feedback.

Regarding the mouse, if you click, do we expect to scroll to that error you clicked on? If so, transiently or committing to that new position? In this first implementation I just take the click as focus event, I never actually read the click position; so we actually jump always relative to the main active editor window.

And yes, "next_result" is bound to the active view; we don't wrap and jump around since v4.14 (iirc). That's halfway a bug and a feature; I always found the jumping-around to be really confusing. (And prior that the next file was just the next file in an alphabet sort. Now you usually stop at the end of the current file except the file has dependencies. Then you can actually jump to them using "next".)

Especially here, I really spent an extra mile in this PR to not jump to different files (not even wrap). So I even forbid going "prev" on top of a file.

@braver
Copy link
Member

braver commented Aug 6, 2020

What would be a typical way to actually bring the focus to a panel?

I believe the design of ST intends output panels to never get focus. You can switch to the tree via CTRL+0 and to editor panes via CTRL+1 (CTRL+2 etc if you have a split view). But there is no shortcut to go to the output panel. You can usually click into the panel to copy-paste something or to double-click a line, but that's it. Browsing goes via prev/next-result, which keeps the focus in the editor. Which is nice, because you can start editing immediately without switching focus back to the editor.

So your solution with the command param is good. Clicking into a panel of course also moves focus, so that should also work.

How do we visually show that the panel has focus?

ST is actually not good here. I tend to look for a cursor or the outlines around a selection. I don't even think you can do anything in the color scheme to indicate focus. Unless they added something in ST4, but I can't remember and don't think so.

So I even forbid going "prev" on top of a file.

Huh. I did truly "go prev" on top a file just now:

Screen-Recording-2020-08-06-at-0

@kaste
Copy link
Contributor Author

kaste commented Aug 6, 2020

Huh. I did truly "go prev" on top a file just now:

The native "prev_result" goes to the previous file, but the "prev_commit" (arrow ⬆️) I implemented here doesn't. (Iirc it snapshots the file/view when you first press up or down, for example to restore that view on "discard", and then you can also never go above that file. But you can go into a file below.)

On the panels, I think I barely used panels 3 years ago, but today with Terminus, GitSavvy, SL... these are super popular and ST is a bit behind here supporting that dev move.

If I click into the panel it doesn't work the first time I hit an arrow key. After a few hits it starts moving correctly though.

Can't repro this one. Don't know what it even means. It doesn't move at all? It moves to a weird error?

@braver
Copy link
Member

braver commented Aug 7, 2020

but the "prev_commit" (arrow ⬆️) I implemented here doesn't.

exactly, which is good. 👍

It doesn't move at all? It moves to a weird error?

I had a better look today. It moves, but not the error near where I clicked in the panel, but the error near my cursor in de main editor. Which I guess makes a lot of sense. But because my eyes are now on the cursor in the output panel, it's weird that it sometimes jumps down several lines to the next error.

Screen-Recording-2020-08-07-at-1

Fixes #1717

Enable standard "show_panel" with "focus: True" to open the panel and focus
it.  Our toggle panel takes this arg as well and passes it through.

Generally, you *somehow* focus the panel, then `<up>`/`<down>` will move
to next or previous error transiently, t.i. without storing this move in
Sublime's navigation history.  `<enter>` will commit this new location to
said history, while `<escape>` will discard the previewing and return to the
original location.  (Overall this mimics for example the Goto Symbol quick
panel.)  Note that `<escape>` is not specially bound here, we instead
introspect or observe the standard "hide_command" with "cancel: true" command
and act upon it.

On top of this, we may close the panel only if you opened the panel with
"focus: true".  (T.i. you did not move the focus to an already open panel.)

Further notes:

If the panel has focus:

- Change the "SNAP" value to (basically) 0 to the effect that the line
  gets highlighted only if the cursor is exactly on an error.  (T.i.
  turn off the "snap to nearest value" feature.)

- Change the color of our virtual cursor to get *some* visual feedback.
  (For the panel, we choosed total control over the cursor, so basically
	the user never sees a caret. We use a _selection_ (not a region!) to
  highlight error lines; and completely remove any selection otherwise.)
  Note that we will have a normal caret now if the panel has the focus.
  That's normal behavior.
If a file has "dependent" files with errors we show them right below
the current file.  In that case (and only then) it seems very reasonable
to move to these next errors, probably opening transient views.

This is relatively straight forward to implement.  Since we already had
implicitly the notion of a "top" file, which was always the active file,
we now set the top file to the file the navigation started with, t.i. the
"original view".  (Simply put, "follow active view" is off in quick panel
mode.)  We do this because otherwise, upon navigation, the panel would
wildly redraw, always showing the active file on top.
Note that we don't allow navigating to errors *above* the orignal, top view.

Noteable for "commit":
We assume that the cursor is already in the correct place.  We actually
trigger `open_location` to maybe turn the transient into a normal view,
and in any case put the new location in Sublime's navigation history.
But we don't actually jump anymore or read the location a user might have
*clicked*.
T.i. we remove the click + `<enter>` feature for now.

Partially reverts c673a6da00ff19480f21de9dbf4b3d309c9ec705 ("Implement
keyboard navigation for the error panel")

This is now unused since we switched to `open_location` solely.
Since a not so recent change we actually show the "active" file always
on the bottom of the panel.  It is thus not necessary to compute a
`fbottom` (file bottom = last line of the file) as a bottom boundary
the scroller must not overscroll.  Instead the bottom is always the
last drawn line of the panel.  (Still computing with the `fbottom` is
nice because Sublime can overscroll and show more whitespace at the end
of each view. We avoid that here and stick the last line to the bottom
of the viewport.)

With that we can simplify the types here as well.  We don't send all
`errors_from_active_view` around but instead just the one line of the
filename.  Unfortunately, if there are no errors for a particular file
we still need to query the panel/view for a good line.
On ST4, `open_location` moves the focus to the view, in ST3 not.
We fix by moving the focus back.
The panel closes on "hide_panel" but also implicitly if the user
shows a different panel.

This is important because it affects the "top_view" the panel shows.
We found that `find_all_results` is actually terrible slow, so we
use the data we have in memory and compute.
@kaste kaste marked this pull request as draft October 1, 2022 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
👀 Features and changes worth looking at
Development

Successfully merging this pull request may close these issues.

Goto/highlight target error when moving around in the panel
2 participants