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

add generic marker sequence and markers/decorations for problems #152671

Merged
merged 31 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
2bb881a
add markers for problems
meganrogge Jun 20, 2022
9d01a4f
fix tests
meganrogge Jun 20, 2022
5812177
actually fix tests
meganrogge Jun 20, 2022
1e58802
Update src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts
meganrogge Jun 20, 2022
b9579bf
Update src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts
meganrogge Jun 20, 2022
451b646
put marker in better spot
meganrogge Jun 20, 2022
4797afb
get much closer
meganrogge Jun 20, 2022
fed7318
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 20, 2022
69a565c
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 20, 2022
686b40c
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 22, 2022
5a3f512
register it as a command
meganrogge Jun 22, 2022
b1a4f1a
enable a custom hover
meganrogge Jun 22, 2022
b98d81d
invalidate command
meganrogge Jun 22, 2022
7887b6a
use number of matches
meganrogge Jun 22, 2022
04092be
tidy
meganrogge Jun 22, 2022
df5e8b4
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 22, 2022
7c9a7b3
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 22, 2022
60a7c31
fix test
meganrogge Jun 22, 2022
23d13aa
Update src/vs/platform/terminal/common/capabilities/capabilities.ts
meganrogge Jun 22, 2022
5040888
Merge branch 'main' into merogge/problem-marker
meganrogge Jun 22, 2022
b82fe91
Update src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts
meganrogge Jun 23, 2022
7a0271e
Update src/vs/workbench/contrib/terminal/browser/xterm/decorationAddo…
meganrogge Jun 23, 2022
be9e602
add e to file name
meganrogge Jun 23, 2022
3cb3fcb
cleanup
meganrogge Jun 23, 2022
5f80b23
big refactor
meganrogge Jun 23, 2022
66a8c5c
fix merge conflicts
meganrogge Jun 23, 2022
88ae78c
refactor
meganrogge Jun 23, 2022
29c5f3c
copy marker to capabilities file
meganrogge Jun 23, 2022
5316b57
handle marker in taskTerminalStatus
meganrogge Jun 23, 2022
4040a3b
Update src/vs/workbench/contrib/terminal/browser/terminal.ts
meganrogge Jun 23, 2022
c10717a
h
meganrogge Jun 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log';
import type { ITerminalAddon, Terminal } from 'xterm-headless';
import { ISerializedCommandDetectionCapability } from 'vs/platform/terminal/common/terminalProcess';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { Emitter } from 'vs/base/common/event';

/**
* Shell integration is a feature that enhances the terminal's understanding of what's happening
Expand Down Expand Up @@ -116,6 +117,17 @@ const enum VSCodeOscPt {
Property = 'P'
}

/**
* ITerm sequences
*/
const enum ITermOscPt {
/**
* Set a mark on the scroll bar `OSC 1337 ; SetMark`
* Based on ITerm's `OSC 1337 ; SetMark`
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
*/
SetMark = 'SetMark'
}

/**
* The shell integration addon extends xterm by reading shell integration sequences and creating
* capabilities and passing along relevant sequences to the capabilities. This is meant to
Expand All @@ -126,6 +138,8 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
readonly capabilities = new TerminalCapabilityStore();
private _hasUpdatedTelemetry: boolean = false;
private _activationTimeout: any;
private readonly _onRequestCreateGenericMarker = new Emitter<void>();
readonly onRequestCreateGenericMarker = this._onRequestCreateGenericMarker.event;
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

constructor(
private readonly _disableTelemetry: boolean | undefined,
Expand All @@ -140,6 +154,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
this._terminal = xterm;
this.capabilities.add(TerminalCapability.PartialCommandDetection, new PartialCommandDetectionCapability(this._terminal));
this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.VSCode, data => this._handleVSCodeSequence(data)));
this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.ITerm, data => this._doHandleITermSequence(data)));
this._ensureCapabilitiesOrAddFailureTelemetry();
}

Expand Down Expand Up @@ -249,6 +264,24 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati
return false;
}

private _doHandleITermSequence(data: string): boolean {
if (!this._terminal) {
return false;
}

// Pass the sequence along to the capability
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
const [command,] = data.split(';');
switch (command) {

case ITermOscPt.SetMark: {
this._onRequestCreateGenericMarker.fire();
}
}

// Unrecognized sequence
return false;
}

serialize(): ISerializedCommandDetectionCapability {
if (!this._terminal || !this.capabilities.has(TerminalCapability.CommandDetection)) {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class TaskTerminalStatus extends Disposable {
addTerminal(task: Task, terminal: ITerminalInstance, problemMatcher: AbstractProblemCollector) {
const status: ITerminalStatus = { id: TASK_TERMINAL_STATUS_ID, severity: Severity.Info };
terminal.statusList.add(status);
problemMatcher.onDidAddMatch(() => terminal.addGenericMarker());
this.terminalMap.set(task._id, { terminal, task, status, problemMatcher, taskRunEnded: false });
}

Expand Down
7 changes: 4 additions & 3 deletions src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings';
import { GroupKind } from 'vs/workbench/contrib/tasks/common/taskConfiguration';
import { Codicon } from 'vs/base/common/codicons';
import { VSCodeOscProperty, VSCodeOscPt, VSCodeSequence } from 'vs/workbench/contrib/terminal/browser/terminalEscapSequences';

const taskShellIntegrationStartSequence = '\x1b]633;A\x07' + '\x1b]633;P;Task=\x07' + '\x1b]633;B\x07';
const taskShellIntegrationOutputSequence = '\x1b]633;C\x07';
const taskShellIntegrationStartSequence = VSCodeSequence(VSCodeOscPt.PromptStart) + VSCodeSequence(VSCodeOscPt.Property, VSCodeOscProperty.Task) + VSCodeSequence(VSCodeOscPt.CommandStart);
const taskShellIntegrationOutputSequence = VSCodeSequence(VSCodeOscPt.CommandExecuted);

interface ITerminalData {
terminal: ITerminalInstance;
Expand Down Expand Up @@ -1709,6 +1710,6 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem {

function taskShellIntegrationWaitOnExitSequence(message: string): (exitCode: number) => string {
return (exitCode) => {
return `\x1b]633;D;${exitCode}\x07${message}`;
return `${VSCodeSequence(VSCodeOscPt.CommandFinished, exitCode.toString())}${message}`;
};
}
4 changes: 4 additions & 0 deletions src/vs/workbench/contrib/tasks/common/problemCollectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ export abstract class AbstractProblemCollector implements IDisposable {

protected _onDidStateChange: Emitter<IProblemCollectorEvent>;

private readonly _onDidAddMatch = new Emitter<void>();
readonly onDidAddMatch = this._onDidAddMatch.event;

constructor(public readonly problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, protected modelService: IModelService, fileService?: IFileService) {
this.matchers = Object.create(null);
this.bufferLength = 1;
Expand Down Expand Up @@ -205,6 +208,7 @@ export abstract class AbstractProblemCollector implements IDisposable {
if (this._maxMarkerSeverity === undefined || match.marker.severity > this._maxMarkerSeverity) {
this._maxMarkerSeverity = match.marker.severity;
}
this._onDidAddMatch.fire();
}

private clearBuffer(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class TestTask extends CommonTask {
}

class TestProblemCollector implements Partial<AbstractProblemCollector> {

private readonly _onDidAddMatch = new Emitter<void>();
readonly onDidAddMatch = this._onDidAddMatch.event;
}

suite('Task Terminal Status', () => {
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,11 @@ export interface ITerminalInstance {
*/
showEnvironmentInfoHover(): void;

/**
* Adds a generic marker and decoration to the buffer
*/
addGenericMarker(): void;
meganrogge marked this conversation as resolved.
Show resolved Hide resolved

/**
* Dispose the terminal instance, removing it from the panel/service and freeing up resources.
*
Expand Down
112 changes: 112 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminalEscapSequences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*---------------------------------------------------------------------------------------------
meganrogge marked this conversation as resolved.
Show resolved Hide resolved
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

/**
* The identifier for the first numeric parameter (`Ps`) for OSC commands used by shell integration.
*/
const enum ShellIntegrationOscPs {
/**
* Sequences pioneered by FinalTerm.
*/
FinalTerm = 133,
/**
* Sequences pioneered by VS Code. The number is derived from the least significant digit of
* "VSC" when encoded in hex ("VSC" = 0x56, 0x53, 0x43).
*/
VSCode = 633,
/**
* Sequences pioneered by iTerm.
*/
ITerm = 1337
}

/**
* VS Code-specific shell integration sequences. Some of these are based on common alternatives like
* those pioneered in FinalTerm. The decision to move to entirely custom sequences was to try to
* improve reliability and prevent the possibility of applications confusing the terminal.
*/
export const enum VSCodeOscPt {
/**
* The start of the prompt, this is expected to always appear at the start of a line.
* Based on FinalTerm's `OSC 133 ; A ST`.
*/
PromptStart = 'A',

/**
* The start of a command, ie. where the user inputs their command.
* Based on FinalTerm's `OSC 133 ; B ST`.
*/
CommandStart = 'B',

/**
* Sent just before the command output begins.
* Based on FinalTerm's `OSC 133 ; C ST`.
*/
CommandExecuted = 'C',

/**
* Sent just after a command has finished. The exit code is optional, when not specified it
* means no command was run (ie. enter on empty prompt or ctrl+c).
* Based on FinalTerm's `OSC 133 ; D [; <ExitCode>] ST`.
*/
CommandFinished = 'D',

/**
* Explicitly set the command line. This helps workaround problems with conpty not having a
* passthrough mode by providing an option on Windows to send the command that was run. With
* this sequence there's no need for the guessing based on the unreliable cursor positions that
* would otherwise be required.
*/
CommandLine = 'E',

/**
* Similar to prompt start but for line continuations.
*/
ContinuationStart = 'F',

/**
* Similar to command start but for line continuations.
*/
ContinuationEnd = 'G',

/**
* The start of the right prompt.
*/
RightPromptStart = 'H',

/**
* The end of the right prompt.
*/
RightPromptEnd = 'I',

/**
* Set an arbitrary property: `OSC 633 ; P ; <Property>=<Value> ST`, only known properties will
* be handled.
*/
Property = 'P'
}

export const enum VSCodeOscProperty {
Task = 'Task'
}

/**
* ITerm sequences
*/
export const enum ITermOscPt {
/**
* Set a mark on the scroll bar `OSC 1337 ; SetMark`
* Based on ITerm's `OSC 1337 ; SetMark`
*/
SetMark = 'SetMark'
}

export function VSCodeSequence(osc: VSCodeOscPt, data?: string | VSCodeOscProperty): string {
return `\x1b]${ShellIntegrationOscPs.VSCode};${osc};${data}\x07`;
}

export function ITermSequence(data: ITermOscPt): string {
return `\x1b]${ShellIntegrationOscPs.ITerm};${data};\x07`;
}
5 changes: 5 additions & 0 deletions src/vs/workbench/contrib/terminal/browser/terminalInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import type { ITerminalAddon, Terminal as XTermTerminal } from 'xterm';
import { ITermOscPt, ITermSequence } from 'vs/workbench/contrib/terminal/browser/terminalEscapSequences';

const enum Constants {
/**
Expand Down Expand Up @@ -1580,6 +1581,10 @@ export class TerminalInstance extends Disposable implements ITerminalInstance {
}
}

public addGenericMarker(): void {
this.xterm?.raw.write(ITermSequence(ITermOscPt.SetMark));
}

private _onProcessData(ev: IProcessDataEvent): void {
const messageId = ++this._latestXtermWriteData;
if (ev.trackCommit) {
Expand Down
42 changes: 39 additions & 3 deletions src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ const enum DecorationSelector {
DefaultColor = 'default',
Codicon = 'codicon',
XtermDecoration = 'xterm-decoration',
OverviewRuler = 'xterm-decoration-overview-ruler'
OverviewRuler = 'xterm-decoration-overview-ruler',
GenericMarkerIcon = 'codicon-circle-small-filled'
}

const enum DecorationStyles {
Expand Down Expand Up @@ -193,6 +194,39 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
this._attachToCommandCapability();
}

registerGenericMarkerDecoration(): IDecoration | undefined {
const marker = this._terminal?.registerMarker(-1);
if (!marker || !this._terminal || !defaultColor) {
return;
}
const decoration = this._terminal.registerDecoration({
marker,
overviewRulerOptions: { color: defaultColor.toString(), position: 'center' }
});
if (!decoration) {
return undefined;
}
decoration.onRender(element => {
if (element.classList.contains(DecorationSelector.OverviewRuler)) {
return;
}
if (!this._decorations.get(decoration.marker.id)) {
decoration.onDispose(() => this._decorations.delete(decoration.marker.id));
this._decorations.set(decoration.marker.id,
{
decoration,
disposables: []
});
}
if (!element.classList.contains(DecorationSelector.Codicon) || marker?.line === 0) {
// first render or buffer was cleared
this._updateLayout(element);
this._updateClasses(element, undefined, true);
}
});
return decoration;
}

registerCommandDecoration(command: ITerminalCommand, beforeCommandExecution?: boolean): IDecoration | undefined {
if (!this._terminal) {
return undefined;
Expand Down Expand Up @@ -257,15 +291,17 @@ export class DecorationAddon extends Disposable implements ITerminalAddon {
}
}

private _updateClasses(element?: HTMLElement, exitCode?: number): void {
private _updateClasses(element?: HTMLElement, exitCode?: number, generic?: boolean): void {
if (!element) {
return;
}
for (const classes of element.classList) {
element.classList.remove(classes);
}
element.classList.add(DecorationSelector.CommandDecoration, DecorationSelector.Codicon, DecorationSelector.XtermDecoration);
if (exitCode === undefined) {
if (generic) {
element.classList.add(DecorationSelector.DefaultColor, DecorationSelector.GenericMarkerIcon);
} else if (exitCode === undefined) {
element.classList.add(DecorationSelector.DefaultColor);
element.classList.add(`codicon-${this._configurationService.getValue(TerminalSettingId.ShellIntegrationDecorationIcon)}`);
} else if (exitCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal {
private _createDecorationAddon(): void {
this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities);
this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e));
this.add(this._shellIntegrationAddon?.onRequestCreateGenericMarker(() => this._decorationAddon?.registerGenericMarkerDecoration()));
this.raw.loadAddon(this._decorationAddon);
}

Expand Down