"use strict" ; import * as vscode from "vscode" ; import * as os from "os" ; import * as crypto from "crypto" ; import * as path from "path" ; import * as fs from "fs" ; import * as fsx from "fs-extra" ; import { StatusBarAlignment , StatusBarItem , TextEditor , window , Disposable , commands , MarkdownString , ParameterInformation , SignatureInformation } from "vscode" ; import { start_syntaxer , Syntaxer } from "./syntaxer" ; export class vsc_config { public static get < T > ( section_value , defaultValue ? ) { let tokens = section_value . split ( '.' ) ; return vscode . workspace . getConfiguration ( tokens [ 0 ] ) . get ( tokens [ 1 ] , defaultValue ) ! ; } } let ext_dir = path . join ( __dirname , ".." ) ; let exec = require ( 'child_process' ) . exec ; let execSync = require ( 'child_process' ) . execSync ; let mkdirp = require ( 'mkdirp' ) ; let ver_file ; let _user_dir ; export let statusBarItem ; let _environment_ready = false ; let _ready = false ; let _busy = false ; export let ext_version ; export let omnisharp_dir ; export let isWin = ( os . platform ( ) == 'win32' ) ; export let settings ; export let diagnosticCollection ; let sources_temp_dir = path . join ( os . tmpdir ( ) , "Roslyn.Intellisense" , "sources" ) ; create_dir ( sources_temp_dir ) ; declare global { interface String { contains ( text ) : boolean ; lines ( limit ? ) : string [ ] ; pathNormalize ( ) : string ; } interface Array < T > { where < T > ( filter ) : Array < T > ; any < T > ( filter ? ) : boolean ; contains < T > ( item ) : boolean ; select < U > ( convert ) : Array < U > ; cast < U > ( ) : Array < U > ; first < T > ( filter ? ) : T ; firstOrDefault < T > ( filter ? ) : T ; last < T > ( filter ? ) : T ; remove < T > ( item ) : Array < T > ; lastOrDefault < T > ( filter ? ) : T ; } } String . prototype . lines = function ( limit ? ) { return this . split ( /\r?\n/g , limit ) ; } ; String . prototype . contains = function ( text ) { return this . indexOf ( text ) >= 0 ; } ; String . prototype . pathNormalize = function ( ) { return path . normalize ( this . toString ( ) ) . split ( /[\\\/]/g ) . join ( path . posix . sep ) ; } ; Array . prototype . firstOrDefault = function < T > ( predicate ) : T { for ( var index = 0 ; index < this . length ; index ++ ) { var element = this [ index ] ; if ( predicate == null || predicate ( element ) ) return element ; } return null ! ; } ; Array . prototype . first = function < T > ( predicate ) : T { for ( var index = 0 ; index < this . length ; index ++ ) { var element = this [ index ] ; if ( predicate == null || predicate ( element ) ) return element ; } throw new Error ( 'The collection is empty' ) ; } ; Array . prototype . last = function < T > ( predicate ) : T { for ( var index = this . length - 1 ; index >= 0 ; index -- ) { var element = this [ index ] ; if ( predicate == null || predicate ( element ) ) return element ; } throw new Error ( 'The collection is empty' ) ; } ; Array . prototype . lastOrDefault = function < T > ( predicate ) : T { for ( var index = this . length - 1 ; index >= 0 ; index -- ) { var element = this [ index ] ; if ( predicate == null || predicate ( element ) ) return < T > element ; } return null ! ; } ; Array . prototype . remove = function < T > ( item ) : Array < T > { while ( true ) { var index = this . indexOf ( item , 0 ) ; if ( index > - 1 ) this . splice ( index , 1 ) ; else break ; } return this ; } ; Array . prototype . where = function < T > ( predicate ) : Array < T > { return this . filter ( predicate ) ; } ; Array . prototype . cast = function < U > ( ) : Array < U > { return this . select ( x => x as U ) ; } ; Array . prototype . contains = function < T > ( item ) : boolean { return this . any ( x => x == item ) ; } ; Array . prototype . any = function < T > ( predicate ) : boolean { for ( var i = 0 ; i < this . length ; i ++ ) { let item = this [ i ] ; if ( predicate ) { if ( predicate ( item ) ) return true ; } else { return true ; } } return false ; } ; Array . prototype . select = function < T , U > ( convert ) : Array < U > { var result = [ ] ; for ( var i = 0 ; i < this . length ; i ++ ) { var item = this [ i ] ; var ci = convert ( item ) ; result . push ( ci ) ; } return result ; } ; export function with_lock ( callback ) { if ( lock ( ) ) try { callback ( ) ; } catch ( error ) { unlock ( ) ; } } export function get_line_indent ( text ) { let line_indent = 0 ; let curr_line = text ; for ( ; line_indent < curr_line . length ; line_indent ++ ) { if ( curr_line . charAt ( line_indent ) != " " ) break ; } return line_indent ; } export function css_unescape_linebreaks ( text , eol = "\n" ) { return text . replace ( /\${r}\${n}/g , "\n" ) . replace ( /\${n}/g , eol ) ; } export function is_ready ( ) { return _ready ; } export function is_busy ( ) { return _busy ; } export function run_async ( callback ) { setTimeout ( callback , 100 ) ; } export function lock ( ) { if ( ! _environment_ready ) { return false ; } if ( ! _ready ) { vscode . window . showInformationMessage ( 'CS-Script initialization is in progress.' ) ; return false ; } if ( _busy ) { vscode . window . showInformationMessage ( 'CS-Script is busy.' ) ; return false ; } _busy = true ; return true ; } export function unlock ( ) { _busy = false ; } export function create_dir ( dir ) { const allRWEPermissions = parseInt ( "0777" , 8 ) ; mkdirp . sync ( dir , allRWEPermissions ) ; } export function delete_dir ( dir ) { let success = true ; try { let files = fs . readdirSync ( dir ) ; for ( let i = 0 ; i < files . length ; i ++ ) { let item_path = path . join ( dir , files [ i ] ) ; if ( fs . lstatSync ( item_path ) . isFile ( ) ) try { fs . unlinkSync ( item_path ) ; } catch ( error ) { success = false ; console . log ( error ) ; } else delete_dir ( item_path ) ; } fs . rmdir ( dir , ( ) => { } ) ; } catch ( error ) { success = false ; console . log ( error ) ; } return success ; } export function copy_file ( src , dest ) { fs . createReadStream ( src ) . pipe ( fs . createWriteStream ( dest ) ) ; } export function copy_file_to ( fileName , srcDir , destDir ) { fs . createReadStream ( path . join ( srcDir , fileName ) ) . pipe ( fs . createWriteStream ( path . join ( destDir , fileName ) ) ) ; } export function copy_file_to_sync ( fileName , srcDir , destDir ) { try { fsx . copySync ( path . join ( srcDir , fileName ) , path . join ( destDir , fileName ) ) ; } catch ( error ) { console . log ( error . toString ( ) ) ; } } export function copy_dir_to_sync ( srcDir , destDir ) { try { fsx . copySync ( srcDir , destDir ) ; } catch ( error ) { console . log ( error . toString ( ) ) ; } } export function copy_file_to_sync2 ( fileName , newFileName , srcDir , destDir ) { try { fsx . copySync ( path . join ( srcDir , fileName ) , path . join ( destDir , newFileName ) ) ; } catch ( error ) { console . log ( error . toString ( ) ) ; } } export function del_file ( filePath ) { fs . unlink ( filePath , err => { if ( err ) console . log ( err ) ; } ) ; } export function user_dir ( ) { let extensionRoot = path . dirname ( path . dirname ( path . dirname ( __dirname ) ) ) ; let isPortable = path . basename ( extensionRoot ) . toLowerCase ( ) == "data" || path . basename ( extensionRoot ) . toLowerCase ( ) == "code-portable-data" ; if ( ! _user_dir ) { if ( isPortable ) { _user_dir = path . join ( extensionRoot , 'user-data' , 'cs-script.user' ) ; } else { if ( os . platform ( ) == 'win32' ) { _user_dir = path . join ( process . env . APPDATA ! , 'Code' , 'User' , 'cs-script.user' ) ; } else if ( os . platform ( ) == 'darwin' ) { _user_dir = path . join ( process . env . HOME ! , 'Library' , 'Application Support' , 'Code' , 'User' , 'cs-script.user' ) ; } else { _user_dir = path . join ( process . env . HOME ! , '.config' , 'Code' , 'User' , 'cs-script.user' ) ; } } } create_dir ( _user_dir ) ; return _user_dir ; } export function ActivateDiagnostics ( context ) { console . log ( "Loading CS-Script extension from " + __dirname ) ; if ( vscode . extensions . getExtension ( 'ms-vscode.csharp' ) == null && vscode . extensions . getExtension ( 'ms-dotnettools.csharp' ) == null ) { let message = 'The required extension "C# for Visual Studio Code" is not found. Ensure it is installed.' ; vscode . window . showErrorMessage ( message ) ; throw message ; } diagnosticCollection = vscode . languages . createDiagnosticCollection ( 'c#' ) ; statusBarItem = vscode . window . createStatusBarItem ( StatusBarAlignment . Left ) ; context . subscriptions . push ( diagnosticCollection ) ; ext_version = vscode . extensions . getExtension ( 'oleg-shilo.cs-script' ) ?. packageJSON . version ; omnisharp_dir = path . join ( vscode . extensions . getExtension ( 'ms-dotnettools.csharp' ) ?. extensionPath ! , '.omnisharp' , 'omnisharp' ) ; ver_file = path . join ( user_dir ( ) , 'vscode.css_version.txt' ) ; settings = Settings . Load ( ) ; check_environment ( ) ; deploy_engine ( ) ; return diagnosticCollection ; } function check_syntaxer_ready ( ms ) { let attempts_count = 0 ; setTimeout ( ( ) => Syntaxer . ping ( ( data ) => { attempts_count ++ ; if ( data == "ready" ) { statusBarItem . text = 'CS-Script Intellisense services are ready...' ; statusBarItem . show ( ) ; setTimeout ( ( ) => statusBarItem . hide ( ) , 5000 ) ; } else { statusBarItem . text = 'CS-Script initializing...' ; statusBarItem . show ( ) ; let delay = 1000 ; if ( attempts_count < ( 10000 / delay ) ) check_syntaxer_ready ( delay ) ; else statusBarItem . hide ( ) ; } } ) , ms ) ; } export function deploy_engine ( ) { try { let need_to_deploy = true ; if ( fs . existsSync ( ver_file ) ) { try { let version = fs . readFileSync ( ver_file , 'utf8' ) ; need_to_deploy = ( version != ext_version ) ; } catch ( error ) { } } if ( need_to_deploy ) { vscode . window . showInformationMessage ( 'Preparing new version of CS-Script for deployment.' ) ; statusBarItem . text = '$(versions) Deploying CS-Script...' ; statusBarItem . show ( ) ; run_async ( deploy_files ) ; } else { start_syntaxer ( ) ; _ready = true ; } } catch ( error ) { console . log ( error ) ; vscode . window . showErrorMessage ( 'CS-Script: ' + String ( error ) ) ; } } export function compare_versions ( a , b ) { let parts_a = a . split ( '.' ) ; let parts_b = b . split ( '.' ) ; let i = 0 ; for ( ; i < Math . min ( parts_a . length , parts_b . length ) ; i ++ ) { let v_a = parseInt ( parts_a [ i ] ) ; let v_b = parseInt ( parts_b [ i ] ) ; if ( v_a > v_b ) return 1 ; if ( v_a < v_b ) return - 1 ; } if ( parts_a . length > parts_b . length ) return 1 ; if ( parts_a . length < parts_b . length ) return - 1 ; else return 0 ; } function check_environment ( ) { _environment_ready = true ; return ; } function deploy_files ( ) { try { let dotnet_dir = path . join ( user_dir ( ) , "dotnet" ) ; let syntaxer_dir = path . join ( dotnet_dir , "syntaxer" ) ; let src_dir = path . join ( ext_dir , 'bin' , 'dotnet' ) ; Syntaxer . sentStopRequest ( ) ; let deleted_old = delete_dir ( syntaxer_dir ) ; deleted_old = deleted_old && delete_dir ( dotnet_dir ) ; copy_dir_to_sync ( src_dir , dotnet_dir ) ; ensure_default_config ( path . join ( user_dir ( ) , 'dotnet' , 'cscs.dll' ) ) ; start_syntaxer ( ) ; check_syntaxer_ready ( 500 ) ; fs . writeFileSync ( ver_file , ext_version , { encoding : 'utf8' } ) ; if ( deleted_old ) vscode . window . showInformationMessage ( 'New version of CS-Script binaries has been deployed.' ) ; else vscode . window . showInformationMessage ( 'New version of CS-Script binaries has been deployed.\n' + 'However "' + dotnet_dir + '" directory was not cleaned properly because it was locked.\n' + 'It is recommended that you close VSCode and remove the directory manually.' ) ; _ready = true ; } catch ( error ) { console . log ( error ) ; vscode . window . showErrorMessage ( 'CS-Script: ' + String ( error ) ) ; } } export function prepare_new_script ( ) { let template_file = path . join ( user_dir ( ) , 'new_script.tmpl' ) ; let template = 'using System;' + os . EOL + '$backup_comment$' + os . EOL + 'Console.WriteLine($"Hello {user()}...");' + os . EOL + os . EOL + 'string user()' + os . EOL + '    => Environment.UserName;' ; fs . writeFileSync ( template_file , template , { encoding : 'utf8' } ) ; try { template = fs . readFileSync ( template_file , { encoding : 'utf8' } ) ; } catch ( error ) { } return template ; } export function prepare_new_script_vb ( ) { let template_file = path . join ( user_dir ( ) , 'new_script_vb.tmpl' ) ; let template = "' //css_ref System" + os . EOL + "' //css_ref System.web" + os . EOL + "' //css_ref System.Windows.Forms" + os . EOL + "$backup_comment$" + os . EOL + "Imports System" + os . EOL + "" + os . EOL + "Imports System.Windows.Forms" + os . EOL + "" + os . EOL + "Module Module1" + os . EOL + "    Sub Main()" + os . EOL + "        Console.WriteLine(\"Hello World! (VB)\")" + os . EOL + "        MessageBox.Show(\"Hello World! (VB)\")" + os . EOL + "    End Sub" + os . EOL + "End Module" ; if ( ! fs . existsSync ( template_file ) ) fs . writeFileSync ( template_file , template , { encoding : 'utf8' } ) ; try { template = fs . readFileSync ( template_file , { encoding : 'utf8' } ) ; } catch ( error ) { } return template ; } export function clear_temp_file_suffixes ( content ) { return content . replace ( /.\$temp\$/g , '' ) ; } export function select_line ( line ) { let editor = vscode . window . activeTextEditor ! ; if ( line != - 1 ) { editor . selection = new vscode . Selection ( line , editor . document . lineAt ( line ) . text . length , line , 0 ) ; editor . revealRange ( editor . selection ) ; } } export function save_as_temp ( content , script_file , inject_source_info ) { let id = crypto . randomBytes ( 16 ) . toString ( "hex" ) ; let ext = path . extname ( script_file ) ; let link_info = "//css_syntaxer source:" + script_file ; let temp_file = path . join ( sources_temp_dir , id + ext ) ; if ( inject_source_info ) content = link_info + "\n" + content ; fs . writeFileSync ( temp_file , content , { encoding : 'utf8' } ) ; return temp_file ; } export function toSignatureInfo ( data ) { let result ; let sig_lines = css_unescape_linebreaks ( data ) . lines ( ) ; sig_lines . forEach ( line => { if ( line . length > 1 ) { if ( line . startsWith ( "label:" ) ) { result = new SignatureInformation ( line . substr ( "label:" . length ) ) ; } else if ( line . startsWith ( "doc:" ) ) { result . documentation = line . substr ( "doc:" . length ) ; } else if ( line . startsWith ( "param_label:" ) ) { result . parameters . push ( new ParameterInformation ( line . substr ( "param_label:" . length ) ) ) ; } else if ( line . startsWith ( "param_doc:" ) ) { let param = result . parameters . lastOrDefault ( ) ; if ( param != null ) { param . documentation = new MarkdownString ( `\`${ param . label }\` - ${ line . substr ( "param_doc:" . length ) }` ) ; } } else { if ( result != null && result . parameters . any ( ) && result . parameters . last < ParameterInformation > ( ) . documentation != null ) { result . parameters . last < ParameterInformation > ( ) . documentation += "\n" + line ; } else if ( result != null && result . parameters . any ( ) ) { result . parameters . last < ParameterInformation > ( ) . label += "\n" + line ; } else if ( result != null && result . documentation != null ) { result . documentation += "\n" + line ; } else if ( result != null ) { result . label += "\n" + line ; } } } } ) ; if ( result ! != null && result . documentation ) result . documentation = new MarkdownString ( `_${ result . documentation }_` ) ; return result ! ; } export class ErrorInfo { public file ! : string ; public description ! : string ; public range ! : vscode . Range ; public severity ! : vscode . DiagnosticSeverity ; public static parse ( data ) { let result = new ErrorInfo ( ) ; try { let cs_script_prefixes = [ "csscript.CompilerException: " , "file:" ] ; cs_script_prefixes . forEach ( element => { if ( data . startsWith ( element ) ) data = data . replace ( element , '' ) . trim ( ) ; } ) ; let parts = data . split ( /\):/g , 2 ) ; if ( parts . length != 2 ) { if ( fs . existsSync ( parts [ 0 ] ) ) { result . file = clear_temp_file_suffixes ( parts [ 0 ] ) ; result . range = new vscode . Range ( 0 , 0 , 0 , 0 ) ; } else { return null ! ; } } else { result . description = parts [ 1 ] . trim ( ) ; parts = parts [ 0 ] . split ( '(' ) ; result . file = parts [ 0 ] ; let nums = parts [ 1 ] . split ( ',' , 2 ) . map ( x => Number ( x ) ) ; result . range = new vscode . Range ( nums [ 0 ] - 1 , nums [ 1 ] - 1 , nums [ 0 ] - 1 , nums [ 1 ] - 1 ) ; parts = result . description . split ( ' ' ) ; result . description = parts . slice ( 1 ) . join ( ' ' ) ; if ( parts [ 0 ] == "error" ) result . severity = vscode . DiagnosticSeverity . Error ; else if ( parts [ 0 ] == "warning" ) result . severity = vscode . DiagnosticSeverity . Warning ; if ( parts [ 0 ] == "info" ) result . severity = vscode . DiagnosticSeverity . Information ; } } catch ( e ) { return null ! ; } return result ; } public constructor ( fields ? ) { if ( fields ) Object . assign ( this , fields ) ; } } export class Settings { public show_load_proj_info : boolean = true ; public show_readme : boolean = true ; public need_check_roslyn_on_OSX : boolean = true ; private _file ! : string ; public static Save ( _this , file ? ) { let file_path = path . join ( user_dir ( ) , 'settings.json' ) ; if ( file != null ) file_path = file ; else if ( _this . _file != null ) file_path = _this . _file ; fs . writeFileSync ( file_path , JSON . stringify ( _this ) , { encoding : 'utf8' } ) ; } public static Load ( file ? ) { let file_path = path . join ( user_dir ( ) , 'settings.json' ) ; if ( file != null ) file_path = file ; let settings ; try { settings = JSON . parse ( fs . readFileSync ( file_path , 'utf8' ) ) ; } catch ( e ) { settings = new Settings ( ) ; } settings . _file = file_path ; return settings ; } } export class Utils { public static IsScript ( file ) { if ( file == undefined ) return false ; else return file . toLowerCase ( ) . endsWith ( '.cs' ) || file . toLowerCase ( ) . endsWith ( '.vb' ) ; } public static getScriptName ( projectFile ) { if ( fs . existsSync ( projectFile ) ) { let lines = fs . readFileSync ( projectFile , 'utf8' ) . lines ( ) ; for ( var line of lines ) { if ( line . contains ( 'Compile' ) ) { return line . trim ( ) . replace ( '<Compile Include="' , '' ) . replace ( '"/>' , '' ) ; } } } return null ! ; } public static getScriptFiles ( projectFile ) { let files = [ ] ; if ( fs . existsSync ( projectFile ) ) { let lines = fs . readFileSync ( projectFile , 'utf8' ) . lines ( ) ; for ( var line of lines ) { if ( line . contains ( 'Compile' ) ) { files . push ( line . trim ( ) . replace ( '<Compile Include="' , '' ) . replace ( '"/>' , '' ) ) ; } } } return files ; } public static IsSamePath ( abs_path1 , abs_path2 ) { if ( abs_path1 != null && abs_path2 != null ) { if ( path . sep == "\\" ) return ( abs_path1 . toLowerCase ( ) == abs_path2 . toLowerCase ( ) ) ; else return ( abs_path1 == abs_path2 ) ; } return false ; } public static SentToDiagnostics ( errors ) { diagnosticCollection . clear ( ) ; let diagnosticMap = new Map ( ) ; errors . forEach ( error => { let fileUri = vscode . Uri . file ( error . file ) ; let canonicalFile = fileUri . toString ( ) ; let diagnostics = diagnosticMap . get ( canonicalFile ) ; if ( ! diagnostics ) { diagnostics = [ ] ; } diagnostics . push ( new vscode . Diagnostic ( error . range , error . description , error . severity ) ) ; diagnosticMap . set ( canonicalFile , diagnostics ) ; diagnosticMap . forEach ( ( diags , file ) => { diagnosticCollection . set ( fileUri , diags ) ; } ) ; } ) ; } public static RunSynch ( command ) { return execSync ( command ) . toString ( ) ; } public static Run ( command , on_done ? ) { let output = '' ; let p = exec ( command ) ; p . stdout . on ( 'data' , ( data ) => { let buf = data ; output += data ; if ( buf . indexOf ( 'Processing NuGet packages...' ) != - 1 ) vscode . window . showInformationMessage ( 'Restoring NuGet packages...' ) ; } ) ; p . stderr . on ( 'data' , ( data ) => output += data ) ; p . on ( 'close' , ( code ) => { if ( on_done ) on_done ( code , output ) ; } ) ; } public static Run2 ( command , on_data , on_done ? ) { let output = '' ; let p = exec ( command ) ; p . stdout . on ( 'data' , ( data ) => { output += data ; on_data ( data ) ; } ) ; p . stderr . on ( 'data' , ( data ) => output += data ) ; p . on ( 'close' , ( code ) => { if ( on_done ) on_done ( code , output ) ; } ) ; } } export function preload_roslyn ( ) { try { let exe = path . join ( user_dir ( ) , 'mono' , 'cscs.exe' ) ; let command = 'mono "' + exe + '" -preload' ; Utils . Run ( command ) ; } catch ( error ) { } } export function disable_roslyn_on_osx ( ) { if ( os . platform ( ) == 'darwin' && settings . need_check_roslyn_on_OSX ) { let config_file = path . join ( user_dir ( ) , 'mono' , 'css_config.mono.xml' ) ; if ( fs . existsSync ( config_file ) ) { let config_data = fs . readFileSync ( config_file , 'utf8' ) ; let patched = false ; if ( config_data . includes ( "<useAlternativeCompiler></useAlternativeCompiler>" ) ) { config_data = config_data . replace ( "<useAlternativeCompiler></useAlternativeCompiler>" , "<useAlternativeCompiler>none</useAlternativeCompiler>" ) ; patched = true ; } if ( config_data . includes ( "<useAlternativeCompiler>CSSRoslynProvider.dll</useAlternativeCompiler>" ) ) { config_data = config_data . replace ( "<useAlternativeCompiler>CSSRoslynProvider.dll</useAlternativeCompiler>" , "<useAlternativeCompiler>none</useAlternativeCompiler>" ) ; patched = true ; } if ( patched ) { window . showErrorMessage ( "CS-Script: Due to the problems with Mono implementation of 'ICodeCompiler' on OSX the supported script syntax is reduced to C#6." ) ; settings . need_check_roslyn_on_OSX = false ; Settings . Save ( settings ) ; fs . writeFileSync ( config_file , config_data , { encoding : 'utf8' } ) ; } } } } export function ensure_default_core_config ( cscs_exe , on_done ? ) { let config_file = path . join ( path . dirname ( cscs_exe ) , 'css_config.xml' ) ; if ( ! fs . existsSync ( config_file ) ) { let command = `dotnet "${ cscs_exe }" -config:default` ; Utils . Run ( command , ( code , output ) => { fs . writeFileSync ( config_file , output , { encoding : 'utf8' } ) ; if ( on_done ) on_done ( config_file ) ; } ) ; } else if ( on_done ) on_done ( config_file ) ; } export function ensure_default_config ( cscs_exe , on_done ? ) { let config_file = path . join ( path . dirname ( cscs_exe ) , 'css_config.mono.xml' ) ; if ( ! fs . existsSync ( config_file ) ) { let original_cscs_exe = path . join ( ext_dir , 'bin' , 'cscs.dll' ) ; let command = 'dotnet "' + original_cscs_exe + '" -config:default' ; Utils . Run ( command , ( code , output ) => { if ( on_done ) on_done ( config_file ) ; } ) ; } else { if ( on_done ) on_done ( config_file ) ; } } export function actual_output ( element , index , array ) { return ( ! element . startsWith ( 'failed to get 100ns ticks' ) && ! element . startsWith ( 'Mono pdb to mdb debug symbol store converter' ) && ! element . startsWith ( 'Usage: pdb2mdb assembly' ) ) ; } export type BuiltInCommands = 'vscode.open' | 'setContext' | 'workbench.action.closeActiveEditor' | 'workbench.action.nextEditor' ; export const BuiltInCommands = { CloseActiveEditor : 'workbench.action.closeActiveEditor' as BuiltInCommands , NextEditor : 'workbench.action.nextEditor' as BuiltInCommands , Open : 'vscode.open' as BuiltInCommands , SetContext : 'setContext' as BuiltInCommands } ; export class ActiveEditorTracker extends Disposable { private _disposable : vscode . Disposable ; private _resolver : ( ( value ? : TextEditor | PromiseLike < TextEditor > ) => void ) | undefined ; constructor ( ) { super ( ( ) => this . dispose ( ) ) ; this . _disposable = window . onDidChangeActiveTextEditor ( e => this . _resolver && this . _resolver ( e ) ) ; } dispose ( ) { this . _disposable && this . _disposable . dispose ( ) ; } async awaitClose ( timeout = 500 ) { this . close ( ) ; return this . wait ( timeout ) ; } async awaitNext ( timeout = 500 ) { this . next ( ) ; return this . wait ( timeout ) ; } async close ( ) { return commands . executeCommand ( BuiltInCommands . CloseActiveEditor ) ; } async next ( ) { return commands . executeCommand ( BuiltInCommands . NextEditor ) ; } async wait ( timeout = 500 ) { const editor = await new Promise < TextEditor > ( ( resolve , reject ) => { let timer ; this . _resolver = ( value ) => { if ( timer ) { clearTimeout ( timer as any ) ; timer = 0 ; resolve ( value ) ; } } ; timer = setTimeout ( ( ) => { resolve ( window . activeTextEditor ! ) ; timer = 0 ; } , timeout ) as any ; } ) ; this . _resolver = undefined ; return editor ; } }