Fix debounce and add onData listener#31155
Conversation
Tyriar
left a comment
There was a problem hiding this comment.
Please link the issue on the PR, it has a description of the timer system we want for onData
| debounceEvent(this._onCheckWindowsShell.event, (l, e) => e, 100, true)(() => { | ||
| this.checkWindowShell(); | ||
| }); | ||
| this.onData(() => this._onCheckWindowsShell.fire()); |
There was a problem hiding this comment.
I think we need a new mechanism for onData. Currently if a command runs for 10 seconds, it will launch 10/0.1=100 processes during its lifetimes. Instead of debounce like on enter we need something that will go off when the output has finished coming in.
There was a problem hiding this comment.
Doesn't debounce reset the timer whenever the function gets called again?
There was a problem hiding this comment.
I'm 90% sure it does
There was a problem hiding this comment.
You're right I got a little confused there. But doesn't that mean it will not fire at the beginning if output is returned within 100ms because onEnter is being debounced on the same event? We might actually want to throttle onEnter (fire immediate, but no more than every 100ms), and debounce onData (fire when output stops for 100ms).
It also occurs to me that onData will fire when you press any key while typing in the prompt which probably isn't what we want. Imagine typing "a" (wait 150ms) "b" (wait 150ms) "c": that would fire 3 processes because of the onData handler.
A more efficient approach could be to hook into when a new line is started in the terminal, this would require some new API in xterm.js though. You could probably test out the approach by monkey patching xterm.js temporarily to see if it does what we want. Something like this:
const originalLineFeed = (<any>this._xterm).inputHandler.lineFeed;
(<any>this._xterm).inputHandler.lineFeed = function () {
// ...fire event...
originalLineFeed();
}e39e0a5 to
1929b35
Compare
| debounceEvent(this._onCheckWindowsShell.event, (l, e) => e, 200, true)(() => { | ||
| setTimeout(() => { | ||
| this.checkWindowShell(); | ||
| }, 50); |
There was a problem hiding this comment.
I think you explained why there was a setTimeout inside the debounce, but I forget why, a comment would be nice
|
Not sure whether the CI ❌ 's are from your changes? Can you try to get it back to ✅ ? |
|
Fixed the tests, @roblourens |
|
The current failure looks unrelated. |
| this._createProcess(this._shellLaunchConfig); | ||
| this._createXterm(); | ||
| if (this._shellLaunchConfig) { | ||
| this.setTitle(path.basename(this._shellLaunchConfig.executable), true); |
There was a problem hiding this comment.
If this._shellLaunchConfig.name is set, we do not want to do this.
Also this applies to Linux/macOS too, I don't think we use basename for non-Windows yet. This also doesn't strip .exe, we should rely on the basename/.exe strip code inside setTitle.
| this.checkWindowShell(); | ||
| }); | ||
| this._xterm.on('lineFeed', () => this._onCheckWindowsShell.fire()); | ||
| this._xterm.on('keypress', () => this._onCheckWindowsShell.fire()); |
There was a problem hiding this comment.
If we're doing this here we still need the enter check and the this._messageTitleListener check.
There was a problem hiding this comment.
Actually, I check this._messageTitleListener in the method. For the keypress, won't lineFeed suffice? I think for the enter spam, the debounced event will suffice?
| this.setTitle(path.basename(this._shellLaunchConfig.executable), true); | ||
| } | ||
|
|
||
| if (platform.isWindows) { |
There was a problem hiding this comment.
This is getting very large, can we instead pass this._xterm to WindowsShellHelper and do everything in there? Encapsulating all this logic into WindowsShellHelper would be best as it's Windows specific and TerminalInstance is already quite large and complex.
There was a problem hiding this comment.
This solves the blank flickering that appears on Mac as well as the undefined in Windows.
There was a problem hiding this comment.
But if mac's executable is set to /bin/bash, it might be bash -> /bin/bash (some time in the future)?
There was a problem hiding this comment.
But that's more for the comment above. This one is pointing at the if (platform.Windows) section being too large
Tyriar
left a comment
There was a problem hiding this comment.
Just a few more small comments and good to go!
| private _onDataForApi: Emitter<{ instance: ITerminalInstance, data: string }>; | ||
| private _onProcessIdReady: Emitter<TerminalInstance>; | ||
| private _onTitleChanged: Emitter<string>; | ||
| private _onCheckWindowsShell: Emitter<TPromise<string>>; |
| import * as platform from 'vs/base/common/platform'; | ||
| import * as dom from 'vs/base/browser/dom'; | ||
| import Event, { Emitter } from 'vs/base/common/event'; | ||
| import Event, { Emitter, debounceEvent } from 'vs/base/common/event'; |
| this._createProcess(this._shellLaunchConfig); | ||
| this._createXterm(); | ||
|
|
||
| if (this._shellLaunchConfig && !this._shellLaunchConfig.name) { |
There was a problem hiding this comment.
Let's move this into _createProcess, just before this._messageTitleListener is set. That way it's right next to the part that sets it for _shellLaunchConfig.name.
| private _rootProcessId: number; | ||
| private _terminalInstance: ITerminalInstance; | ||
| private _onCheckShell: Emitter<TPromise<string>>; | ||
| private _xterm: any; |
There was a problem hiding this comment.
Pull in the type using import XTermTerminal = require('xterm');
Refer to #30152 for details