44
55/// <reference path='../../../src/vs/vscode.d.ts'/>
66
7- import * as grpc from '@grpc/grpc-js' ;
87import * 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' ;
139import * as http from 'http' ;
1410import * as path from 'path' ;
1511import * as util from 'util' ;
1612import * as vscode from 'vscode' ;
1713
1814let gitpodContext : GitpodExtensionContext | undefined ;
1915export 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-
0 commit comments