-
Notifications
You must be signed in to change notification settings - Fork 4.5k
Expand file tree
/
Copy pathrunCommandInTerminal.ts
More file actions
133 lines (108 loc) · 3.5 KB
/
runCommandInTerminal.ts
File metadata and controls
133 lines (108 loc) · 3.5 KB
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
import type { TerminalOptions } from "core";
import * as vscode from "vscode";
const REMOTE_TERMINAL_TIMEOUT_MS = 5000;
const terminalCacheByName = new Map<string, vscode.Terminal>();
function getNewActiveTerminal(
existingTerminals: Set<vscode.Terminal>,
): vscode.Terminal | undefined {
const activeTerminal = vscode.window.activeTerminal;
if (!activeTerminal || existingTerminals.has(activeTerminal)) {
return undefined;
}
return activeTerminal;
}
function getReusableTerminal(
options: TerminalOptions,
): vscode.Terminal | undefined {
if (!vscode.window.terminals.length || !options.reuseTerminal) {
return undefined;
}
if (options.terminalName) {
const cachedTerminal = terminalCacheByName.get(options.terminalName);
if (cachedTerminal && vscode.window.terminals.includes(cachedTerminal)) {
return cachedTerminal;
}
terminalCacheByName.delete(options.terminalName);
return vscode.window.terminals.find(
(terminal) => terminal?.name === options.terminalName,
);
}
return vscode.window.activeTerminal ?? vscode.window.terminals[0];
}
async function createTerminal(
options: TerminalOptions,
): Promise<vscode.Terminal> {
if (!vscode.env.remoteName) {
return vscode.window.createTerminal(options.terminalName);
}
const existingTerminals = new Set(vscode.window.terminals);
await vscode.commands.executeCommand("workbench.action.terminal.new");
const newActiveTerminal = getNewActiveTerminal(existingTerminals);
if (newActiveTerminal) {
return newActiveTerminal;
}
return await new Promise<vscode.Terminal>((resolve, reject) => {
let settled = false;
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
const cleanup = () => {
terminalOpenListener.dispose();
activeTerminalListener.dispose();
if (timeoutHandle) {
clearTimeout(timeoutHandle);
}
};
const resolveIfNewActiveTerminalExists = () => {
const newTerminal = getNewActiveTerminal(existingTerminals);
if (!newTerminal) {
return false;
}
settled = true;
cleanup();
resolve(newTerminal);
return true;
};
const terminalOpenListener = vscode.window.onDidOpenTerminal(() => {
if (settled) {
return;
}
resolveIfNewActiveTerminalExists();
});
const activeTerminalListener = vscode.window.onDidChangeActiveTerminal(
(terminal) => {
if (settled || !terminal || existingTerminals.has(terminal)) {
return;
}
settled = true;
cleanup();
resolve(terminal);
},
);
if (resolveIfNewActiveTerminalExists()) {
return;
}
// `workbench.action.terminal.new` should focus the new terminal in remote
// workspaces. If another terminal opens concurrently, wait until VS Code
// actually switches the active terminal instead of grabbing the first one
// that appears.
timeoutHandle = setTimeout(() => {
if (settled) {
return;
}
settled = true;
cleanup();
reject(new Error("Timed out waiting for remote terminal to open"));
}, REMOTE_TERMINAL_TIMEOUT_MS);
});
}
export async function runCommandInTerminal(
command: string,
options: TerminalOptions = { reuseTerminal: true },
): Promise<void> {
const terminal =
getReusableTerminal(options) ?? (await createTerminal(options));
if (options.terminalName) {
terminalCacheByName.set(options.terminalName, terminal);
}
terminal.show();
terminal.sendText(command, true);
}