Skip to content

Commit 8f216f7

Browse files
committed
Remove custom terminal implementation
1 parent c01ae64 commit 8f216f7

File tree

12 files changed

+264
-1470
lines changed

12 files changed

+264
-1470
lines changed

extensions/gitpod-remote/src/extension.ts

Lines changed: 2 additions & 237 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,16 @@
44

55
/// <reference path='../../../src/vs/vscode.d.ts'/>
66

7-
import * as grpc from '@grpc/grpc-js';
87
import * as cp from 'child_process';
9-
import * as shared from 'gitpod-shared/out/extension';
10-
import { GitpodExtensionContext } from 'gitpod-shared/out/features';
11-
import { TaskStatus, TasksStatusRequest, TasksStatusResponse, TaskState } from '@gitpod/supervisor-api-grpc/lib/status_pb';
12-
import { Terminal, ListTerminalsRequest, TerminalSize, SetTerminalSizeRequest, ListenTerminalRequest, ListenTerminalResponse, ShutdownTerminalRequest, WriteTerminalRequest } from '@gitpod/supervisor-api-grpc/lib/terminal_pb';
8+
import { GitpodExtensionContext, setupGitpodContext } from 'gitpod-shared';
139
import * as http from 'http';
1410
import * as path from 'path';
1511
import * as util from 'util';
1612
import * as vscode from 'vscode';
1713

1814
let gitpodContext: GitpodExtensionContext | undefined;
1915
export async function activate(context: vscode.ExtensionContext) {
20-
gitpodContext = await shared.createContext(context);
16+
gitpodContext = await setupGitpodContext(context);
2117
if (!gitpodContext) {
2218
return;
2319
}
@@ -36,7 +32,6 @@ export async function activate(context: vscode.ExtensionContext) {
3632

3733
installInitialExtensions(gitpodContext);
3834
registerHearbeat(gitpodContext);
39-
registerTasks(gitpodContext);
4035
registerCLI(gitpodContext);
4136

4237
// For port tunneling we rely on Remote SSH capabilities
@@ -254,233 +249,3 @@ export function registerHearbeat(context: GitpodExtensionContext): void {
254249
vscode.workspace.onDidChangeConfiguration(updateLastActivitiy)
255250
);
256251
}
257-
258-
async function registerTasks(context: GitpodExtensionContext): Promise<void> {
259-
const tokenSource = new vscode.CancellationTokenSource();
260-
const token = tokenSource.token;
261-
context.subscriptions.push({
262-
dispose: () => tokenSource.cancel()
263-
});
264-
265-
const tasks = new Map<string, TaskStatus>();
266-
let synched = false;
267-
while (!synched) {
268-
let listener: vscode.Disposable | undefined;
269-
try {
270-
const req = new TasksStatusRequest();
271-
req.setObserve(true);
272-
const stream = context.supervisor.status.tasksStatus(req, context.supervisor.metadata);
273-
function done() {
274-
synched = true;
275-
stream.cancel();
276-
}
277-
listener = token.onCancellationRequested(() => done());
278-
await new Promise((resolve, reject) => {
279-
stream.on('end', resolve);
280-
stream.on('error', reject);
281-
stream.on('data', (response: TasksStatusResponse) => {
282-
if (response.getTasksList().every(status => {
283-
tasks.set(status.getTerminal(), status);
284-
return status.getState() !== TaskState.OPENING;
285-
})) {
286-
done();
287-
}
288-
});
289-
});
290-
} catch (err) {
291-
if (!('code' in err && err.code === grpc.status.CANCELLED)) {
292-
console.error('code server: listening task updates failed:', err);
293-
}
294-
} finally {
295-
listener?.dispose();
296-
}
297-
if (!synched) {
298-
await new Promise(resolve => setTimeout(resolve, 1000));
299-
}
300-
}
301-
if (token.isCancellationRequested) {
302-
return;
303-
}
304-
305-
const terminals = new Map<string, Terminal>();
306-
try {
307-
const response = await util.promisify(context.supervisor.terminal.list.bind(context.supervisor.terminal, new ListTerminalsRequest(), context.supervisor.metadata, {
308-
deadline: Date.now() + context.supervisor.deadlines.long
309-
}))();
310-
for (const terminal of response.getTerminalsList()) {
311-
terminals.set(terminal.getAlias(), terminal);
312-
}
313-
} catch (e) {
314-
console.error('failed to list terminals:', e);
315-
}
316-
317-
for (const alias of tasks.keys()) {
318-
const terminal = terminals.get(alias);
319-
if (!terminal) {
320-
return;
321-
}
322-
regsiterTask(alias, terminal.getTitle(), context, token);
323-
}
324-
}
325-
326-
function regsiterTask(alias: string, initialTitle: string, context: GitpodExtensionContext, contextToken: vscode.CancellationToken): void {
327-
const tokenSource = new vscode.CancellationTokenSource();
328-
contextToken.onCancellationRequested(() => tokenSource.cancel());
329-
const token = tokenSource.token;
330-
331-
const onDidWriteEmitter = new vscode.EventEmitter<string>();
332-
const onDidCloseEmitter = new vscode.EventEmitter<void | number>();
333-
const onDidChangeNameEmitter = new vscode.EventEmitter<string>();
334-
const toDispose = vscode.Disposable.from(onDidWriteEmitter, onDidCloseEmitter, onDidChangeNameEmitter);
335-
token.onCancellationRequested(() => toDispose.dispose());
336-
337-
let pendingWrite = Promise.resolve();
338-
let pendinResize = Promise.resolve();
339-
function setDimensions(dimensions: vscode.TerminalDimensions): void {
340-
if (token.isCancellationRequested) {
341-
return;
342-
}
343-
pendinResize = pendinResize.then(async () => {
344-
if (token.isCancellationRequested) {
345-
return;
346-
}
347-
try {
348-
const size = new TerminalSize();
349-
size.setCols(dimensions.columns);
350-
size.setRows(dimensions.rows);
351-
352-
const request = new SetTerminalSizeRequest();
353-
request.setAlias(alias);
354-
request.setSize(size);
355-
request.setForce(true);
356-
await util.promisify(context.supervisor.terminal.setSize.bind(context.supervisor.terminal, request, context.supervisor.metadata, {
357-
deadline: Date.now() + context.supervisor.deadlines.short
358-
}))();
359-
} catch (e) {
360-
if (e && e.code !== grpc.status.NOT_FOUND) {
361-
console.error(`${alias} terminal: resize failed:`, e);
362-
}
363-
}
364-
});
365-
}
366-
const terminal = vscode.window.createTerminal({
367-
name: initialTitle,
368-
pty: {
369-
onDidWrite: onDidWriteEmitter.event,
370-
onDidClose: onDidCloseEmitter.event,
371-
onDidChangeName: onDidChangeNameEmitter.event,
372-
open: async (dimensions: vscode.TerminalDimensions | undefined) => {
373-
if (dimensions) {
374-
setDimensions(dimensions);
375-
}
376-
while (!token.isCancellationRequested) {
377-
let notFound = false;
378-
let exitCode: number | undefined;
379-
let listener: vscode.Disposable | undefined;
380-
try {
381-
await new Promise((resolve, reject) => {
382-
const request = new ListenTerminalRequest();
383-
request.setAlias(alias);
384-
const stream = context.supervisor.terminal.listen(request, context.supervisor.metadata);
385-
listener = token.onCancellationRequested(() => stream.cancel());
386-
stream.on('end', resolve);
387-
stream.on('error', reject);
388-
stream.on('data', (response: ListenTerminalResponse) => {
389-
if (response.hasTitle()) {
390-
const title = response.getTitle();
391-
if (title) {
392-
onDidChangeNameEmitter.fire(title);
393-
}
394-
} else if (response.hasData()) {
395-
let data = '';
396-
const buffer = response.getData();
397-
if (typeof buffer === 'string') {
398-
data += buffer;
399-
} else {
400-
data += Buffer.from(buffer).toString();
401-
}
402-
if (data !== '') {
403-
onDidWriteEmitter.fire(data);
404-
}
405-
} else if (response.hasExitCode()) {
406-
exitCode = response.getExitCode();
407-
}
408-
});
409-
});
410-
} catch (e) {
411-
notFound = 'code' in e && e.code === grpc.status.NOT_FOUND;
412-
if (!token.isCancellationRequested && !notFound && !('code' in e && e.code === grpc.status.CANCELLED)) {
413-
console.error(`${alias} terminal: listening failed:`, e);
414-
}
415-
} finally {
416-
listener?.dispose();
417-
}
418-
if (token.isCancellationRequested) {
419-
return;
420-
}
421-
if (notFound) {
422-
onDidCloseEmitter.fire();
423-
} else if (typeof exitCode === 'number') {
424-
onDidCloseEmitter.fire(exitCode);
425-
}
426-
await new Promise(resolve => setTimeout(resolve, 2000));
427-
}
428-
},
429-
close: async () => {
430-
if (token.isCancellationRequested) {
431-
return;
432-
}
433-
tokenSource.cancel();
434-
435-
// await to make sure that close is not cause by the extension host process termination
436-
// in such case we don't want to stop supervisor terminals
437-
setTimeout(async () => {
438-
if (contextToken.isCancellationRequested) {
439-
return;
440-
}
441-
// Attempt to kill the pty, it may have already been killed at this
442-
// point but we want to make sure
443-
try {
444-
const request = new ShutdownTerminalRequest();
445-
request.setAlias(alias);
446-
await util.promisify(context.supervisor.terminal.shutdown.bind(context.supervisor.terminal, request, context.supervisor.metadata, {
447-
deadline: Date.now() + context.supervisor.deadlines.short
448-
}))();
449-
} catch (e) {
450-
if (e && e.code === grpc.status.NOT_FOUND) {
451-
// Swallow, the pty has already been killed
452-
} else {
453-
console.error(`${alias} terminal: shutdown failed:`, e);
454-
}
455-
}
456-
}, 1000);
457-
458-
},
459-
handleInput: async (data: string) => {
460-
if (token.isCancellationRequested) {
461-
return;
462-
}
463-
pendingWrite = pendingWrite.then(async () => {
464-
if (token.isCancellationRequested) {
465-
return;
466-
}
467-
try {
468-
const request = new WriteTerminalRequest();
469-
request.setAlias(alias);
470-
request.setStdin(Buffer.from(data, 'utf8'));
471-
await util.promisify(context.supervisor.terminal.write.bind(context.supervisor.terminal, request, context.supervisor.metadata, {
472-
deadline: Date.now() + context.supervisor.deadlines.short
473-
}))();
474-
} catch (e) {
475-
if (e && e.code !== grpc.status.NOT_FOUND) {
476-
console.error(`${alias} terminal: write failed:`, e);
477-
}
478-
}
479-
});
480-
},
481-
setDimensions: (dimensions: vscode.TerminalDimensions) => setDimensions(dimensions)
482-
}
483-
});
484-
terminal.show();
485-
}
486-

extensions/gitpod-remote/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"compilerOptions": {
44
"outDir": "./out",
55
"rootDir": "./src",
6-
"esModuleInterop": true
6+
"esModuleInterop": true,
7+
"sourceMap": true,
78
},
89
"references": [
910
{ "path": "../gitpod-shared" }

extensions/gitpod-shared/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"engines": {
88
"vscode": "^1.58.2"
99
},
10+
"main": "./out/extension.js",
1011
"scripts": {
1112
"prepare": "node scripts/inflate.js"
1213
},

extensions/gitpod-shared/src/extension.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
*--------------------------------------------------------------------------------------------*/
44

55
import * as vscode from 'vscode';
6-
import { createGitpodExtensionContext, GitpodExtensionContext, registerDefaultLayout, registerNotifications, registerWorkspaceCommands, registerWorkspaceSharing, registerWorkspaceTimeout } from './features';
6+
import { createGitpodExtensionContext, GitpodExtensionContext, registerDefaultLayout, registerNotifications, registerWorkspaceCommands, registerWorkspaceSharing, registerWorkspaceTimeout, registerTasks } from './features';
77
import { performance } from 'perf_hooks';
88

9-
export async function createContext(context: vscode.ExtensionContext): Promise<GitpodExtensionContext | undefined> {
9+
export { GitpodExtensionContext, SupervisorConnection } from './features';
10+
export * from './gitpod-plugin-model';
11+
12+
export async function setupGitpodContext(context: vscode.ExtensionContext): Promise<GitpodExtensionContext | undefined> {
1013
if (typeof vscode.env.remoteName === 'undefined' || context.extension.extensionKind !== vscode.ExtensionKind.Workspace) {
1114
return undefined;
1215
}
@@ -31,6 +34,7 @@ export async function createContext(context: vscode.ExtensionContext): Promise<G
3134
registerWorkspaceTimeout(gitpodContext);
3235
registerNotifications(gitpodContext);
3336
registerDefaultLayout(gitpodContext);
37+
registerTasks(gitpodContext);
3438
return gitpodContext;
3539
}
3640

0 commit comments

Comments
 (0)