-
Notifications
You must be signed in to change notification settings - Fork 30k
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
Fix focus and configuration handling of the integrated terminal #15958
Conversation
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.
Thanks a lot for looking into this @kaiwood! I left a couple of comments.
@@ -80,6 +80,7 @@ export class TerminalService implements ITerminalService { | |||
this.setActiveInstanceByIndex(0); | |||
} | |||
this._onInstancesChanged.fire(); | |||
terminalInstance.setCommandsToSkipShell(this.configHelper.getCommandsToSkipShell()); |
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.
You've actually stumbled upon a bug bigger than this in that no settings values are updated correctly, including scrollback and cursorBlinking as well. Having a brief look at the code, I think the best way to fix this might be to move TerminalPanel._updateConfig
to TerminalService.updateConfig
and then call into that after creating the new instance.
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.
Ok, after taking a closer look, there is quite some refactoring involved to do this right. _updateCursorBlink, _updateCommandsToSkipShell and _updateScrollback have to move too, makes no sense to leave them in the panel. But _updateFont has to move out of the old config update and stay on its own, its basically the same "class" of responsibility as _updateTheme. That means the event listeners need to be modified too… I'll figure it out, will take a little while until complete…
@@ -127,6 +128,7 @@ export class TerminalService implements ITerminalService { | |||
terminalInstance.setVisible(i === terminalIndex); | |||
}); | |||
this._onActiveInstanceChanged.fire(); | |||
this.getActiveInstance().focus(true); |
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.
I believe the reason focus is not run in setActiveInstanceByIndex
is because on launch we do not want the terminal to be focused when the workbench is restored, instead the editor needs to be focused.
Could we isolate this particular case? When a terminal exits AND it is not the last terminal AND the terminal was focused previously. The last case prevents the terminal hijacking focus from the editor if the shell process dies for example.
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.
@Tyriar Done. All configurations update when changed and on instance creation, focus is kept when the terminal receives EOF via Feel free to review again. |
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.
This is shaping up nicely! A few more comments 😄
} | ||
|
||
private _updateCursorBlink(terminalInstance): void { | ||
terminalInstance.setCursorBlink(this.configHelper.getCursorBlink()); |
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.
I think it would make sense to just roll these 3 single line functions into updateConfig
now.
this._updateTheme(); | ||
} else { | ||
return super.setVisible(visible).then(() => { | ||
this._terminalService.createInstance(); | ||
this._updateConfig(); | ||
this._terminalService.updateConfig(); |
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.
This shouldn't be needed anymore as it's called within ITerminalService.createInstance
@@ -80,6 +83,9 @@ export class TerminalService implements ITerminalService { | |||
this.setActiveInstanceByIndex(0); | |||
} | |||
this._onInstancesChanged.fire(); | |||
this._updateCursorBlink(terminalInstance); |
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.
These calls would be more at home inside TerminalInstance.constructor
. Thinking about this further we may be able to get rid of both calls in TerminalPanel.setVisible
as well by updating the config at the end of TerminalInstance.attachToElement
. I believe that covers the cases we need to worry about for updating the config:
- When the terminal is created and the panel exists (
attachToElement
will trigger immediately) - When the terminal is created via the API and the panel does not exist (
attachToElement
call will be deferred) - When the config is updated (
TerminalService
now listens toIConfigurationservice.onDidUpdateConfiguration
)
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.
Absolutely correct, piggybacking on TerminalInstance.attachToElement
makes a lot of sense. That part of the config is now no longer a concern of TerminalPanel
, the TerminalInstance
is only responsible for itself and TerminalService
handles the global updates when settings change. Seems reasonable 👍
@@ -96,6 +102,9 @@ export class TerminalService implements ITerminalService { | |||
if (wasActiveInstance && this.terminalInstances.length > 0) { | |||
let newIndex = index < this.terminalInstances.length ? index : this.terminalInstances.length - 1; | |||
this.setActiveInstanceByIndex(newIndex); | |||
if (terminalInstance.hadFocusOnExit) { |
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.
Love it! 👍
this._updateTheme(); | ||
this._updateConfig(); | ||
this._terminalService.updateConfig(); |
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.
I think this can safely be removed.
@kaiwood let me know when this is ready for another review, I'd love to land it for this release (ideally merge this week) 😄 |
I was just about to comment above, hehe 👍 The latest commit should address everything that you mentioned, ready for the next round. |
Changes look good, I re-queued the Travis build to make sure 😃 |
Just noticed while checking for the cause of another issue that I included Last commit doesn't change any behaviour, just a cleanup. Travis was green before this, Appveyor timed out, maybe we are lucky and they get both green this time around 😆 |
Looks good, thanks again 😄 |
This PR fixes 2 problems with the focus handling of the integrated terminal (see #15103 and #14969) .
Although they result in a same usability/keybinding problem I initially described in #15103, my description was a little fuzzy because I didn't understand the cause fully.
There are currently 2 problems:
Problem Nr. 1
Whenever a second (or third, …) terminal instance gets exited via the shell command
exit
or by pressingctrl-d
(which basically is "exit" because it sends EOF), the terminal panel loses focus and the editor gets focus.This hinders the workflow when working with multiple terminals, because you can't close a second one without refocusing the panel again, worst case scenario would be that a user write
exit
into an editor window without being aware of it.The only case where focus has to change is if there is only 1 instance left, in this case the panel closes itself so the user has a visual clue about whats going on.
Just in case anyone asks, the reasoning I used focus(true) in setActiveInstanceByIndex() instead of inside _removeInstance() just in case there are some edge cases I didn't stumble upon yet. I'd guess you always want to focus your active instances, but correct me if I'm wrong.
Problem Nr. 2
To make the terminal work with various keyboard shortcuts, the setting
terminal.integrated.commandsToSkipShell
was introduced. The first part of my Issue I described in #15103 and I later found out to be described in #14969 as well is caused by the following:Currently, the settings are only handled in the panel itself when _updateCommandsToSkipShell() is called (whenever the panel gets opened). If you create a new instance with either the
+
-Button in the GUI or via a shortcut, a new terminal instance get's created, gets attached – but _skipTerminalKeybindings is empty.Calling the function to skip the commands from inside TerminalService#createInstance() made most sense to me because it's already aware of the settings, but correct me again if I'm wrong with this assumption.