import * as vscode from 'vscode' ; export interface TaskToken { readonly isCanceled : boolean ; finished ( ) : void ; } export type CancelCallback = ( ) => void ; /**
 * Task with async operation. It will be enqueued to and managed by
 * TaskQueue. Useful for spawning ChildProcess.
 */ export class Task { public readonly uri : vscode . Uri ; public isEnqueued = false ; private body : ( token : TaskToken ) => CancelCallback ; private isCanceled = false ; private resolver ? : ( ) => void ; private onCancel ? : CancelCallback ; param body constructor ( uri , body ) { this . uri = uri ; this . body = body ; } public run ( ) { if ( this . isCanceled ) { return ; } const task = this ; return new Promise < void > ( ( resolve ) => { task . resolver = ( ) => resolve ( ) ; const token = { get isCanceled ( ) : boolean { return task . isCanceled ; } , finished ( ) { task . resolveOnce ( ) ; } , } ; task . onCancel = this . body ( token ) ; } ) ; } public cancel ( ) { if ( this . isCanceled ) { return ; } this . isCanceled = true ; if ( this . onCancel ) { this . onCancel ( ) ; } this . resolveOnce ( ) ; } private resolveOnce ( ) { if ( this . resolver ) { this . resolver ( ) ; this . resolver = undefined ; } } } /**
 * Provides single-threaded task queue which runs single asynchronous
 * Task at a time. This restricts concurrent execution of rubocop
 * processes to prevent from running out machine resource.
 */ export class TaskQueue { private tasks : Task [ ] = [ ] ; private busy = false ; public get length ( ) : number { return this . tasks . length ; } public enqueue ( task ) { if ( task . isEnqueued ) { throw new Error ( 'Task is already enqueued. (uri: ' + task . uri + ')' ) ; } this . cancel ( task . uri ) ; task . isEnqueued = true ; this . tasks . push ( task ) ; this . kick ( ) ; } public cancel ( uri ) { const uriString = uri . toString ( true ) ; this . tasks . forEach ( ( task ) => { if ( task . uri . toString ( true ) === uriString ) { task . cancel ( ) ; } } ) ; } private async kick ( ) { if ( this . busy ) { return ; } this . busy = true ; while ( true ) { const task = this . tasks [ 0 ] ; if ( ! task ) { this . busy = false ; return ; } try { await task . run ( ) ; } catch ( e ) { console . error ( 'Error while running rubocop: ' , e . message , e . stack ) ; } this . tasks . shift ( ) ; } } }