mirror of
https://github.com/Microsoft/vscode
synced 2024-10-06 03:17:00 +00:00
Merge pull request #152995 from microsoft/tyriar/146587
Support (degraded) shell integration for powerlevel10k and other prompts by supporting the common protocol
This commit is contained in:
commit
4c60b2fd34
|
@ -107,7 +107,7 @@ export interface ICommandDetectionCapability {
|
|||
handleContinuationEnd(): void;
|
||||
handleRightPromptStart(): void;
|
||||
handleRightPromptEnd(): void;
|
||||
handleCommandStart(): void;
|
||||
handleCommandStart(options?: IHandleCommandStartOptions): void;
|
||||
handleCommandExecuted(): void;
|
||||
handleCommandFinished(exitCode: number | undefined): void;
|
||||
/**
|
||||
|
@ -118,6 +118,14 @@ export interface ICommandDetectionCapability {
|
|||
deserialize(serialized: ISerializedCommandDetectionCapability): void;
|
||||
}
|
||||
|
||||
export interface IHandleCommandStartOptions {
|
||||
/**
|
||||
* Whether to allow an empty command to be registered. This should be used to support certain
|
||||
* shell integration scripts/features where tracking the command line may not be possible.
|
||||
*/
|
||||
ignoreCommandLine?: boolean;
|
||||
}
|
||||
|
||||
export interface INaiveCwdDetectionCapability {
|
||||
readonly type: TerminalCapability.NaiveCwdDetection;
|
||||
readonly onDidChangeCwd: Event<string>;
|
||||
|
|
|
@ -7,7 +7,7 @@ import { timeout } from 'vs/base/common/async';
|
|||
import { debounce } from 'vs/base/common/decorators';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ICommandDetectionCapability, TerminalCapability, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { ICommandDetectionCapability, TerminalCapability, ITerminalCommand, IHandleCommandStartOptions } from 'vs/platform/terminal/common/capabilities/capabilities';
|
||||
import { ISerializedCommand, ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/terminalProcess';
|
||||
// Importing types is safe in any layer
|
||||
// eslint-disable-next-line code-import-patterns
|
||||
|
@ -61,6 +61,8 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
private _commandMarkers: IMarker[] = [];
|
||||
private _dimensions: ITerminalDimensions;
|
||||
private __isCommandStorageDisabled: boolean = false;
|
||||
private _handleCommandStartOptions?: IHandleCommandStartOptions;
|
||||
|
||||
get commands(): readonly ITerminalCommand[] { return this._commands; }
|
||||
get executingCommand(): string | undefined { return this._currentCommand.command; }
|
||||
// TODO: as is unsafe here and it duplicates behavor of executingCommand
|
||||
|
@ -300,7 +302,8 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
this._logService.debug('CommandDetectionCapability#handleRightPromptEnd', this._currentCommand.commandRightPromptEndX);
|
||||
}
|
||||
|
||||
handleCommandStart(): void {
|
||||
handleCommandStart(options?: IHandleCommandStartOptions): void {
|
||||
this._handleCommandStartOptions = options;
|
||||
// Only update the column if the line has already been set
|
||||
if (this._currentCommand.commandStartMarker?.line === this._terminal.buffer.active.cursorY) {
|
||||
this._currentCommand.commandStartX = this._terminal.buffer.active.cursorX;
|
||||
|
@ -419,13 +422,13 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
return;
|
||||
}
|
||||
|
||||
if (command !== undefined && !command.startsWith('\\')) {
|
||||
if ((command !== undefined && !command.startsWith('\\')) || this._handleCommandStartOptions?.ignoreCommandLine) {
|
||||
const buffer = this._terminal.buffer.active;
|
||||
const timestamp = Date.now();
|
||||
const executedMarker = this._currentCommand.commandExecutedMarker;
|
||||
const endMarker = this._currentCommand.commandFinishedMarker;
|
||||
const newCommand: ITerminalCommand = {
|
||||
command,
|
||||
command: this._handleCommandStartOptions?.ignoreCommandLine ? '' : (command || ''),
|
||||
marker: this._currentCommand.commandStartMarker,
|
||||
endMarker,
|
||||
executedMarker,
|
||||
|
@ -446,6 +449,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability {
|
|||
}
|
||||
this._currentCommand.previousCommandMarker = this._currentCommand.commandStartMarker;
|
||||
this._currentCommand = {};
|
||||
this._handleCommandStartOptions = undefined;
|
||||
}
|
||||
|
||||
private _preHandleCommandFinishedWindows(): void {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IShellIntegration } from 'vs/platform/terminal/common/terminal';
|
||||
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore';
|
||||
import { CommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability';
|
||||
import { CwdDetectionCapability } from 'vs/platform/terminal/common/capabilities/cwdDetectionCapability';
|
||||
|
@ -126,6 +126,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
|
|||
readonly capabilities = new TerminalCapabilityStore();
|
||||
private _hasUpdatedTelemetry: boolean = false;
|
||||
private _activationTimeout: any;
|
||||
private _commonProtocolDisposables: IDisposable[] = [];
|
||||
|
||||
constructor(
|
||||
private readonly _disableTelemetry: boolean | undefined,
|
||||
|
@ -133,16 +134,58 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
|
|||
@ILogService private readonly _logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this._register(toDisposable(() => this._clearActivationTimeout()));
|
||||
this._register(toDisposable(() => {
|
||||
this._clearActivationTimeout();
|
||||
this._disposeCommonProtocol();
|
||||
}));
|
||||
}
|
||||
|
||||
private _disposeCommonProtocol(): void {
|
||||
dispose(this._commonProtocolDisposables);
|
||||
this._commonProtocolDisposables.length = 0;
|
||||
}
|
||||
|
||||
activate(xterm: Terminal) {
|
||||
this._terminal = xterm;
|
||||
this.capabilities.add(TerminalCapability.PartialCommandDetection, new PartialCommandDetectionCapability(this._terminal));
|
||||
this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.VSCode, data => this._handleVSCodeSequence(data)));
|
||||
this._commonProtocolDisposables.push(
|
||||
xterm.parser.registerOscHandler(ShellIntegrationOscPs.FinalTerm, data => this._handleFinalTermSequence(data))
|
||||
);
|
||||
this._ensureCapabilitiesOrAddFailureTelemetry();
|
||||
}
|
||||
|
||||
private _handleFinalTermSequence(data: string): boolean {
|
||||
if (!this._terminal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass the sequence along to the capability
|
||||
// It was considered to disable the common protocol in order to not confuse the VS Code
|
||||
// shell integration if both happen for some reason. This doesn't work for powerlevel10k
|
||||
// when instant prompt is enabled though. If this does end up being a problem we could pass
|
||||
// a type flag through the capability calls
|
||||
const [command, ...args] = data.split(';');
|
||||
switch (command) {
|
||||
case 'A':
|
||||
this._createOrGetCommandDetection(this._terminal).handlePromptStart();
|
||||
return true;
|
||||
case 'B':
|
||||
// Ignore the command line for these sequences as it's unreliable for example in powerlevel10k
|
||||
this._createOrGetCommandDetection(this._terminal).handleCommandStart({ ignoreCommandLine: true });
|
||||
return true;
|
||||
case 'C':
|
||||
this._createOrGetCommandDetection(this._terminal).handleCommandExecuted();
|
||||
return true;
|
||||
case 'D': {
|
||||
const exitCode = args.length === 1 ? parseInt(args[0]) : undefined;
|
||||
this._createOrGetCommandDetection(this._terminal).handleCommandFinished(exitCode);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _handleVSCodeSequence(data: string): boolean {
|
||||
const didHandle = this._doHandleVSCodeSequence(data);
|
||||
if (!this._hasUpdatedTelemetry && didHandle) {
|
||||
|
|
Loading…
Reference in a new issue