-
Notifications
You must be signed in to change notification settings - Fork 29.1k
/
shellIntegrationAddon.ts
140 lines (129 loc) · 4.61 KB
/
shellIntegrationAddon.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ITerminalAddon, Terminal } from 'xterm';
import { IShellIntegration } from 'vs/workbench/contrib/terminal/common/terminal';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { TerminalCapability } from 'vs/platform/terminal/common/terminal';
/**
* Shell integration is a feature that enhances the terminal's understanding of what's happening
* in the shell by injecting special sequences into the shell's prompt using the "Set Text
* Parameters" sequence (`OSC Ps ; Pt ST`).
*
* Definitions:
* - OSC: `\x1b]`
* - Ps: A single (usually optional) numeric parameter, composed of one or more digits.
* - Pt: A text parameter composed of printable characters.
* - ST: `\x7`
*
* This is inspired by a feature of the same name in the FinalTerm, iTerm2 and kitty terminals.
*/
/**
* 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 iTerm.
*/
ITerm = 1337
}
/**
* The identifier for the textural parameter (`Pt`) for OSC commands used by shell integration.
*/
const enum ShellIntegrationOscPt {
/**
* The start of the prompt, this is expected to always appear at the start of a line.
*/
PromptStart = 'A',
/**
* The start of a command, ie. where the user inputs their command.
*/
CommandStart = 'B',
/**
* Sent just before the command output begins.
*/
CommandExecuted = 'C',
// TODO: Understand this sequence better and add docs
CommandFinished = 'D',
// TODO: This is a VS Code-specific sequence? Do we need this? Should it have a version?
EnableShellIntegration = 'E',
}
export const enum ShellIntegrationInfo {
CurrentDir = 'CurrentDir',
}
export const enum ShellIntegrationInteraction {
PromptStart = 'PROMPT_START',
CommandStart = 'COMMAND_START',
CommandExecuted = 'COMMAND_EXECUTED',
CommandFinished = 'COMMAND_FINISHED'
}
export class ShellIntegrationAddon extends Disposable implements IShellIntegration, ITerminalAddon {
private _terminal?: Terminal;
readonly capabilities: TerminalCapability[] = [];
private readonly _onCapabilityDisabled = new Emitter<TerminalCapability>();
readonly onCapabilityDisabled = this._onCapabilityDisabled.event;
private readonly _onCapabilityEnabled = new Emitter<TerminalCapability>();
readonly onCapabilityEnabled = this._onCapabilityEnabled.event;
private readonly _onIntegratedShellChange = new Emitter<{ type: string, value: string }>();
readonly onIntegratedShellChange = this._onIntegratedShellChange.event;
activate(xterm: Terminal) {
this._terminal = xterm;
this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.FinalTerm, data => this._handleShellIntegration(data)));
this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.ITerm, data => this._updateCwd(data)));
}
private _handleShellIntegration(data: string): boolean {
if (!this._terminal) {
return false;
}
let type: ShellIntegrationInteraction | undefined;
const [command, exitCode] = data.split(';');
switch (command) {
case ShellIntegrationOscPt.PromptStart:
type = ShellIntegrationInteraction.PromptStart;
break;
case ShellIntegrationOscPt.CommandStart:
type = ShellIntegrationInteraction.CommandStart;
break;
case ShellIntegrationOscPt.CommandExecuted:
type = ShellIntegrationInteraction.CommandExecuted;
break;
case ShellIntegrationOscPt.CommandFinished:
type = ShellIntegrationInteraction.CommandFinished;
break;
case ShellIntegrationOscPt.EnableShellIntegration:
this.capabilities.push(TerminalCapability.CommandDetection);
this._onCapabilityEnabled.fire(TerminalCapability.CommandDetection);
return true;
default:
return false;
}
const value = exitCode || type;
if (!value) {
return false;
}
this._onIntegratedShellChange.fire({ type, value });
return true;
}
private _updateCwd(data: string): boolean {
let value: string | undefined;
const [type, info] = data.split('=');
switch (type) {
case ShellIntegrationInfo.CurrentDir:
value = info;
break;
default:
return false;
}
if (!value) {
return false;
}
this._onIntegratedShellChange.fire({ type, value });
return true;
}
}