import * as path from 'path' ; import * as fs from 'fs' ; import * as os from 'os' ; import * as child_process from 'child_process' ; import * as vscode from 'vscode' ; import * as Telemetry from './telemetry' ; import HttpsProxyAgent = require ( 'https-proxy-agent' ) ; import * as url from 'url' ; import { PlatformInformation } from './platform' ; import { getOutputChannelLogger , showOutputChannel } from './logger' ; import * as assert from 'assert' ; import * as https from 'https' ; import * as tmp from 'tmp' ; import { ClientRequest , OutgoingHttpHeaders } from 'http' ; import { lookupString } from './nativeStrings' ; import * as nls from 'vscode-nls' ; import { Readable } from 'stream' ; import * as jsonc from 'comment-json' ; import { TargetPopulation } from 'vscode-tas-client' ; nls . config ( { messageFormat : nls . MessageFormat . bundle , bundleFormat : nls . BundleFormat . standalone } ) ( ) ; const localize = nls . loadMessageBundle ( ) ; export const failedToParseJson = localize ( "failed.to.parse.json" , "Failed to parse json file, possibly due to comments or trailing commas." ) ; export type Mutable < T > = { - readonly [ P in keyof T ] : T [ P ] extends ReadonlyArray < infer U > ? Mutable < U > [ ] : Mutable < T [ P ] > ; } ; export const envDelimiter = ( process . platform === 'win32' ) ? ";" : ":" ; export let extensionPath ; export let extensionContext ; export function setExtensionContext ( context ) { extensionContext = context ; extensionPath = extensionContext . extensionPath ; } export function setExtensionPath ( path ) { extensionPath = path ; } let cachedClangFormatPath ; export function getCachedClangFormatPath ( ) { return cachedClangFormatPath ; } export function setCachedClangFormatPath ( path ) { cachedClangFormatPath = path ; } let cachedClangTidyPath ; export function getCachedClangTidyPath ( ) { return cachedClangTidyPath ; } export function setCachedClangTidyPath ( path ) { cachedClangTidyPath = path ; } export const packageJson = vscode . extensions . getExtension ( "ms-vscode.cpptools" ) ?. packageJSON ; let rawPackageJson = null ; export function getRawPackageJson ( ) { if ( rawPackageJson === null || rawPackageJson === undefined ) { const fileContents = fs . readFileSync ( getPackageJsonPath ( ) ) ; rawPackageJson = JSON . parse ( fileContents . toString ( ) ) ; } return rawPackageJson ; } export async function getRawJson ( path ) { if ( ! path ) { return { } ; } const fileExists = await checkFileExists ( path ) ; if ( ! fileExists ) { return { } ; } const fileContents = await readFileText ( path ) ; let rawElement = { } ; try { rawElement = jsonc . parse ( fileContents ) ; } catch ( error ) { throw new Error ( failedToParseJson ) ; } return rawElement ; } export function fileIsCOrCppSource ( file ? ) { if ( file === undefined ) { return false ; } const fileExtLower = path . extname ( file ) . toLowerCase ( ) ; return [ ".cu" , ".c" , ".cpp" , ".cc" , ".cxx" , ".c++" , ".cp" , ".tcc" , ".mm" , ".ino" , ".ipp" , ".inl" ] . some ( ext => fileExtLower === ext ) ; } export function isEditorFileCpp ( file ) { const editor = vscode . window . visibleTextEditors . find ( e => e . document . uri . toString ( ) === file ) ; if ( ! editor ) { return false ; } return editor . document . languageId === "cpp" ; } export function stringifyPackageJson ( packageJson ) { return JSON . stringify ( packageJson , null , 2 ) ; } export function getExtensionFilePath ( extensionfile ) { return path . resolve ( extensionPath , extensionfile ) ; } export function getPackageJsonPath ( ) { return getExtensionFilePath ( "package.json" ) ; } export function getJsonPath ( jsonFilaName ) { const editor = vscode . window . activeTextEditor ; if ( ! editor ) { return undefined ; } const folder = vscode . workspace . getWorkspaceFolder ( editor . document . uri ) ; if ( ! folder ) { return undefined ; } return path . join ( folder . uri . fsPath , ".vscode" , jsonFilaName ) ; } export function getVcpkgPathDescriptorFile ( ) { if ( process . platform === 'win32' ) { const pathPrefix = process . env . LOCALAPPDATA ; if ( ! pathPrefix ) { throw new Error ( "Unable to read process.env.LOCALAPPDATA" ) ; } return path . join ( pathPrefix , "vcpkg/vcpkg.path.txt" ) ; } else { const pathPrefix = os . homedir ( ) ; return path . join ( pathPrefix , ".vcpkg/vcpkg.path.txt" ) ; } } let vcpkgRoot ; export function getVcpkgRoot ( ) { if ( ! vcpkgRoot && vcpkgRoot !== "" ) { vcpkgRoot = "" ; if ( fs . existsSync ( getVcpkgPathDescriptorFile ( ) ) ) { let vcpkgRootTemp = fs . readFileSync ( getVcpkgPathDescriptorFile ( ) ) . toString ( ) ; vcpkgRootTemp = vcpkgRootTemp . trim ( ) ; if ( fs . existsSync ( vcpkgRootTemp ) ) { vcpkgRoot = path . join ( vcpkgRootTemp , "/installed" ) . replace ( /\\/g , "/" ) ; } } } return vcpkgRoot ; } param document export function isHeader ( uri ) { const ext = path . extname ( uri . fsPath ) ; return ! ext || ext . startsWith ( ".h" ) || ext . startsWith ( ".H" ) ; } let isExtensionNotReadyPromptDisplayed = false ; export const extensionNotReadyString = localize ( "extension.not.ready" , 'The C/C++ extension is still installing. See the output window for more information.' ) ; export function displayExtensionNotReadyPrompt ( ) { if ( ! isExtensionNotReadyPromptDisplayed ) { isExtensionNotReadyPromptDisplayed = true ; showOutputChannel ( ) ; getOutputChannelLogger ( ) . showInformationMessage ( extensionNotReadyString ) . then ( ( ) => { isExtensionNotReadyPromptDisplayed = false ; } , ( ) => { isExtensionNotReadyPromptDisplayed = false ; } ) ; } } const progressInstallSuccess = 100 ; const progressExecutableStarted = 150 ; const progressExecutableSuccess = 200 ; const progressParseRootSuccess = 300 ; const progressIntelliSenseNoSquiggles = 1000 ; const installProgressStr = "CPP." + packageJson . version + ".Progress" ; const intelliSenseProgressStr = "CPP." + packageJson . version + ".IntelliSenseProgress" ; export function getProgress ( ) { return extensionContext ? extensionContext . globalState . get < number > ( installProgressStr , - 1 ) : - 1 ; } export function getIntelliSenseProgress ( ) { return extensionContext ? extensionContext . globalState . get < number > ( intelliSenseProgressStr , - 1 ) : - 1 ; } export function setProgress ( progress ) { if ( extensionContext && getProgress ( ) < progress ) { extensionContext . globalState . update ( installProgressStr , progress ) ; const telemetryProperties = { } ; let progressName ; switch ( progress ) { case 0 : progressName = "install started" ; break ; case progressInstallSuccess : progressName = "install succeeded" ; break ; case progressExecutableStarted : progressName = "executable started" ; break ; case progressExecutableSuccess : progressName = "executable succeeded" ; break ; case progressParseRootSuccess : progressName = "parse root succeeded" ; break ; } if ( progressName ) { telemetryProperties [ 'progress' ] = progressName ; } Telemetry . logDebuggerEvent ( "progress" , telemetryProperties ) ; } } export function setIntelliSenseProgress ( progress ) { if ( extensionContext && getIntelliSenseProgress ( ) < progress ) { extensionContext . globalState . update ( intelliSenseProgressStr , progress ) ; const telemetryProperties = { } ; let progressName ; switch ( progress ) { case progressIntelliSenseNoSquiggles : progressName = "IntelliSense no squiggles" ; break ; } if ( progressName ) { telemetryProperties [ 'progress' ] = progressName ; } Telemetry . logDebuggerEvent ( "progress" , telemetryProperties ) ; } } export function getProgressInstallSuccess ( ) { return progressInstallSuccess ; } export function getProgressExecutableStarted ( ) { return progressExecutableStarted ; } export function getProgressExecutableSuccess ( ) { return progressExecutableSuccess ; } export function getProgressParseRootSuccess ( ) { return progressParseRootSuccess ; } export function getProgressIntelliSenseNoSquiggles ( ) { return progressIntelliSenseNoSquiggles ; } export function isUri ( input ) { return input && input instanceof vscode . Uri ; } export function isString ( input ) { return typeof ( input ) === "string" ; } export function isNumber ( input ) { return typeof ( input ) === "number" ; } export function isBoolean ( input ) { return typeof ( input ) === "boolean" ; } export function isObject ( input ) { return typeof ( input ) === "object" ; } export function isArray ( input ) { return input instanceof Array ; } export function isOptionalString ( input ) { return input === undefined || isString ( input ) ; } export function isArrayOfString ( input ) { return isArray ( input ) && input . every ( isString ) ; } export function isOptionalArrayOfString ( input ) { return input === undefined || isArrayOfString ( input ) ; } export function resolveCachePath ( input , additionalEnvironment ) { let resolvedPath = "" ; if ( ! input ) { return resolvedPath ; } resolvedPath = resolveVariables ( input , additionalEnvironment ) ; return resolvedPath ; } export function resolveVariables ( input , additionalEnvironment ? ) { if ( ! input ) { return "" ; } let regexp = ( ) => /\$\{((env|config|workspaceFolder|file|fileDirname|fileBasenameNoExtension|execPath|pathSeparator)(\.|:))?(.*?)\}/g ; let ret = input ; const cycleCache = new Set ( ) ; while ( ! cycleCache . has ( ret ) ) { cycleCache . add ( ret ) ; ret = ret . replace ( regexp ( ) , ( match , ignored1 , varType , ignored2 , name ) => { if ( ! varType ) { varType = "env" ; } let newValue ; switch ( varType ) { case "env" : { if ( additionalEnvironment ) { const v = additionalEnvironment [ name ] ; if ( isString ( v ) ) { newValue = v ; } else if ( input === match && isArrayOfString ( v ) ) { newValue = v . join ( envDelimiter ) ; } } if ( newValue === undefined ) { newValue = process . env [ name ] ; } break ; } case "config" : { const config = vscode . workspace . getConfiguration ( ) ; if ( config ) { newValue = config . get < string > ( name ) ; } break ; } case "workspaceFolder" : { if ( name && vscode . workspace && vscode . workspace . workspaceFolders ) { const folder = vscode . workspace . workspaceFolders . find ( folder => folder . name . toLocaleLowerCase ( ) === name . toLocaleLowerCase ( ) ) ; if ( folder ) { newValue = folder . uri . fsPath ; } } break ; } default : { assert . fail ( "unknown varType matched" ) ; } } return newValue !== undefined ? newValue : match ; } ) ; } regexp = ( ) => /^\~/g ; ret = ret . replace ( regexp ( ) , ( match , name ) => os . homedir ( ) ) ; return ret ; } export function asFolder ( uri ) { let result = uri . toString ( ) ; if ( result . charAt ( result . length - 1 ) !== '/' ) { result += '/' ; } return result ; } /**
 * get the default open command for the current platform
 */ export function getOpenCommand ( ) { if ( os . platform ( ) === 'win32' ) { return 'explorer' ; } else if ( os . platform ( ) === 'darwin' ) { return '/usr/bin/open' ; } else { return '/usr/bin/xdg-open' ; } } export function getDebugAdaptersPath ( file ) { return path . resolve ( getExtensionFilePath ( "debugAdapters" ) , file ) ; } export function getHttpsProxyAgent ( ) { let proxy = vscode . workspace . getConfiguration ( ) . get < string > ( 'http.proxy' ) ; if ( ! proxy ) { proxy = process . env . HTTPS_PROXY || process . env . https_proxy || process . env . HTTP_PROXY || process . env . http_proxy ; if ( ! proxy ) { return undefined ; } } const proxyUrl = url . parse ( proxy ) ; if ( proxyUrl . protocol !== "https:" && proxyUrl . protocol !== "http:" ) { return undefined ; } const strictProxy = vscode . workspace . getConfiguration ( ) . get ( "http.proxyStrictSSL" , true ) ; const proxyOptions = { host : proxyUrl . hostname , port : parseInt ( proxyUrl . port , 10 ) , auth : proxyUrl . auth , rejectUnauthorized : strictProxy } ; return new HttpsProxyAgent ( proxyOptions ) ; } /** Test whether a file exists */ export function checkFileExists ( filePath ) { return new Promise ( ( resolve , reject ) => { fs . stat ( filePath , ( err , stats ) => { if ( stats && stats . isFile ( ) ) { resolve ( true ) ; } else { resolve ( false ) ; } } ) ; } ) ; } /** Test whether a directory exists */ export function checkDirectoryExists ( dirPath ) { return new Promise ( ( resolve , reject ) => { fs . stat ( dirPath , ( err , stats ) => { if ( stats && stats . isDirectory ( ) ) { resolve ( true ) ; } else { resolve ( false ) ; } } ) ; } ) ; } export function checkFileExistsSync ( filePath ) { try { return fs . statSync ( filePath ) . isFile ( ) ; } catch ( e ) { } return false ; } /** Test whether a directory exists */ export function checkDirectoryExistsSync ( dirPath ) { try { return fs . statSync ( dirPath ) . isDirectory ( ) ; } catch ( e ) { } return false ; } /** Test whether a relative path exists */ export function checkPathExistsSync ( path , relativePath , isWindows , isWSL , isCompilerPath ) { let pathExists = true ; const existsWithExeAdded = ( path ) => isCompilerPath && isWindows && ! isWSL && fs . existsSync ( path + ".exe" ) ; if ( ! fs . existsSync ( path ) ) { if ( existsWithExeAdded ( path ) ) { path += ".exe" ; } else if ( ! relativePath ) { pathExists = false ; } else { relativePath = relativePath + path ; if ( ! fs . existsSync ( relativePath ) ) { if ( existsWithExeAdded ( path ) ) { path += ".exe" ; } else { pathExists = false ; } } else { path = relativePath ; } } } return { pathExists , path } ; } /** Read the files in a directory */ export function readDir ( dirPath ) { return new Promise ( ( resolve ) => { fs . readdir ( dirPath , ( err , list ) => { resolve ( list ) ; } ) ; } ) ; } /** Reads the content of a text file */ export function readFileText ( filePath , encoding = "utf8" ) { return new Promise < string > ( ( resolve , reject ) => { fs . readFile ( filePath , encoding , ( err , data ) => { if ( err ) { reject ( err ) ; } else { resolve ( data ) ; } } ) ; } ) ; } /** Writes content to a text file */ export function writeFileText ( filePath , content , encoding = "utf8" ) { const folders = filePath . split ( path . sep ) . slice ( 0 , - 1 ) ; if ( folders . length ) { folders . reduce ( ( previous , folder ) => { const folderPath = previous + path . sep + folder ; if ( ! fs . existsSync ( folderPath ) ) { fs . mkdirSync ( folderPath ) ; } return folderPath ; } ) ; } return new Promise < void > ( ( resolve , reject ) => { fs . writeFile ( filePath , content , { encoding } , ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } ) ; } export function deleteFile ( filePath ) { return new Promise < void > ( ( resolve , reject ) => { if ( fs . existsSync ( filePath ) ) { fs . unlink ( filePath , ( err ) => { if ( err ) { reject ( err ) ; } else { resolve ( ) ; } } ) ; } else { resolve ( ) ; } } ) ; } export function getReadmeMessage ( ) { const readmePath = getExtensionFilePath ( "README.md" ) ; const readmeMessage = localize ( "refer.read.me" , "Please refer to {0} for troubleshooting information. Issues can be created at {1}" , readmePath , "https://github.com/Microsoft/vscode-cpptools/issues" ) ; return readmeMessage ; } /** Used for diagnostics only */ export function logToFile ( message ) { const logFolder = getExtensionFilePath ( "extension.log" ) ; fs . writeFileSync ( logFolder , `${ message }${ os . EOL }` , { flag : 'a' } ) ; } export function execChildProcess ( process , workingDirectory ? , channel ? ) { return new Promise < string > ( ( resolve , reject ) => { child_process . exec ( process , { cwd : workingDirectory , maxBuffer : 500 * 1024 } , ( error , stdout , stderr ) => { if ( channel ) { let message = "" ; let err = false ; if ( stdout && stdout . length > 0 ) { message += stdout ; } if ( stderr && stderr . length > 0 ) { message += stderr ; err = true ; } if ( error ) { message += error . message ; err = true ; } if ( err ) { channel . append ( message ) ; channel . show ( ) ; } } if ( error ) { reject ( error ) ; return ; } if ( stderr && stderr . length > 0 ) { reject ( new Error ( stderr ) ) ; return ; } resolve ( stdout ) ; } ) ; } ) ; } export function spawnChildProcess ( process , args , workingDirectory , dataCallback , errorCallback ) { return new Promise < void > ( function ( resolve , reject ) : void { const child = child_process . spawn ( process , args , { cwd : workingDirectory } ) ; const stdout = child . stdout ; if ( stdout ) { stdout . on ( 'data' , ( data ) => { dataCallback ( `${ data }` ) ; } ) ; } const stderr = child . stderr ; if ( stderr ) { stderr . on ( 'data' , ( data ) => { errorCallback ( `${ data }` ) ; } ) ; } child . on ( 'exit' , ( code ) => { if ( code !== 0 ) { reject ( new Error ( localize ( "process.exited.with.code" , "{0} exited with error code {1}" , process , code ) ) ) ; } else { resolve ( ) ; } } ) ; } ) ; } export function isExecutable ( file ) { return new Promise ( ( resolve ) => { fs . access ( file , fs . constants . X_OK , ( err ) => { if ( err ) { resolve ( false ) ; } else { resolve ( true ) ; } } ) ; } ) ; } export async function allowExecution ( file ) { if ( process . platform !== 'win32' ) { const exists = await checkFileExists ( file ) ; if ( exists ) { const isExec = await isExecutable ( file ) ; if ( ! isExec ) { await chmodAsync ( file , '755' ) ; } } else { getOutputChannelLogger ( ) . appendLine ( "" ) ; getOutputChannelLogger ( ) . appendLine ( localize ( "warning.file.missing" , "Warning: Expected file {0} is missing." , file ) ) ; } } } export async function chmodAsync ( path , mode ) { return new Promise < void > ( ( resolve , reject ) => { fs . chmod ( path , mode , ( err ) => { if ( err ) { return reject ( err ) ; } return resolve ( ) ; } ) ; } ) ; } export function removePotentialPII ( str ) { const words = str . split ( " " ) ; let result = "" ; for ( const word of words ) { if ( word . indexOf ( "." ) === - 1 && word . indexOf ( "/" ) === - 1 && word . indexOf ( "\\" ) === - 1 && word . indexOf ( ":" ) === - 1 ) { result += word + " " ; } else { result += "? " ; } } return result ; } export function checkDistro ( platformInfo ) { if ( platformInfo . platform !== 'win32' && platformInfo . platform !== 'linux' && platformInfo . platform !== 'darwin' ) { getOutputChannelLogger ( ) . appendLine ( localize ( "warning.debugging.not.tested" , "Warning: Debugging has not been tested for this platform." ) + " " + getReadmeMessage ( ) ) ; } } export async function unlinkAsync ( fileName ) { return new Promise < void > ( ( resolve , reject ) => { fs . unlink ( fileName , err => { if ( err ) { return reject ( err ) ; } return resolve ( ) ; } ) ; } ) ; } export async function renameAsync ( oldName , newName ) { return new Promise < void > ( ( resolve , reject ) => { fs . rename ( oldName , newName , err => { if ( err ) { return reject ( err ) ; } return resolve ( ) ; } ) ; } ) ; } export function promptForReloadWindowDueToSettingsChange ( ) { promptReloadWindow ( localize ( "reload.workspace.for.changes" , "Reload the workspace for the settings change to take effect." ) ) ; } export function promptReloadWindow ( message ) { const reload = localize ( "reload.string" , "Reload" ) ; vscode . window . showInformationMessage ( message , reload ) . then ( ( value ? ) => { if ( value === reload ) { vscode . commands . executeCommand ( "workbench.action.reloadWindow" ) ; } } ) ; } export function createTempFileWithPostfix ( postfix ) { return new Promise < tmp . FileResult > ( ( resolve , reject ) => { tmp . file ( { postfix : postfix } , ( err , path , fd , cleanupCallback ) => { if ( err ) { return reject ( err ) ; } return resolve ( < tmp . FileResult > { name : path , fd : fd , removeCallback : cleanupCallback } ) ; } ) ; } ) ; } export function downloadFileToDestination ( urlStr , destinationPath , headers ? ) { return new Promise < void > ( ( resolve , reject ) => { const parsedUrl = url . parse ( urlStr ) ; const request = https . request ( { host : parsedUrl . host , path : parsedUrl . path , agent : getHttpsProxyAgent ( ) , rejectUnauthorized : vscode . workspace . getConfiguration ( ) . get ( 'http.proxyStrictSSL' , true ) , headers : headers } , ( response ) => { if ( response . statusCode === 301 || response . statusCode === 302 ) { let redirectUrl ; if ( typeof response . headers . location === 'string' ) { redirectUrl = response . headers . location ; } else { if ( ! response . headers . location ) { return reject ( new Error ( localize ( "invalid.download.location.received" , 'Invalid download location received' ) ) ) ; } redirectUrl = response . headers . location [ 0 ] ; } return resolve ( downloadFileToDestination ( redirectUrl , destinationPath , headers ) ) ; } if ( response . statusCode !== 200 ) { return reject ( ) ; } const createdFile = fs . createWriteStream ( destinationPath ) ; createdFile . on ( 'finish' , ( ) => { resolve ( ) ; } ) ; response . on ( 'error' , ( error ) => { reject ( error ) ; } ) ; response . pipe ( createdFile ) ; } ) ; request . on ( 'error' , ( error ) => { reject ( error ) ; } ) ; request . end ( ) ; } ) ; } export function downloadFileToStr ( urlStr , headers ? ) { return new Promise < string > ( ( resolve , reject ) => { const parsedUrl = url . parse ( urlStr ) ; const request = https . request ( { host : parsedUrl . host , path : parsedUrl . path , agent : getHttpsProxyAgent ( ) , rejectUnauthorized : vscode . workspace . getConfiguration ( ) . get ( 'http.proxyStrictSSL' , true ) , headers : headers } , ( response ) => { if ( response . statusCode === 301 || response . statusCode === 302 ) { let redirectUrl ; if ( typeof response . headers . location === 'string' ) { redirectUrl = response . headers . location ; } else { if ( ! response . headers . location ) { return reject ( new Error ( localize ( "invalid.download.location.received" , 'Invalid download location received' ) ) ) ; } redirectUrl = response . headers . location [ 0 ] ; } return resolve ( downloadFileToStr ( redirectUrl , headers ) ) ; } if ( response . statusCode !== 200 ) { return reject ( ) ; } let downloadedData = '' ; response . on ( 'data' , ( data ) => { downloadedData += data ; } ) ; response . on ( 'error' , ( error ) => { reject ( error ) ; } ) ; response . on ( 'end' , ( ) => { resolve ( downloadedData ) ; } ) ; } ) ; request . on ( 'error' , ( error ) => { reject ( error ) ; } ) ; request . end ( ) ; } ) ; } /** CompilerPathAndArgs retains original casing of text input for compiler path and args */ export interface CompilerPathAndArgs { compilerPath ? : string ; compilerName : string ; additionalArgs : string [ ] ; } function extractArgs ( argsString ) { const isWindows = os . platform ( ) === 'win32' ; const result = [ ] ; let currentArg = "" ; let isWithinDoubleQuote = false ; let isWithinSingleQuote = false ; for ( let i = 0 ; i < argsString . length ; i ++ ) { const c = argsString [ i ] ; if ( c === '\\' ) { currentArg += c ; if ( ++ i === argsString . length ) { if ( currentArg !== "" ) { result . push ( currentArg ) ; } return result ; } currentArg += argsString [ i ] ; continue ; } if ( c === '"' ) { if ( ! isWithinSingleQuote ) { isWithinDoubleQuote = ! isWithinDoubleQuote ; } } else if ( c === '\'' ) { if ( ! isWindows ) { if ( ! isWithinDoubleQuote ) { isWithinSingleQuote = ! isWithinSingleQuote ; } } } else if ( c === ' ' ) { if ( ! isWithinDoubleQuote && ! isWithinSingleQuote ) { if ( currentArg !== "" ) { result . push ( currentArg ) ; currentArg = "" ; } continue ; } } currentArg += c ; } if ( currentArg !== "" ) { result . push ( currentArg ) ; } return result ; } export function extractCompilerPathAndArgs ( inputCompilerPath ? , inputCompilerArgs ? ) { let compilerPath = inputCompilerPath ; const compilerPathLowercase = inputCompilerPath ?. toLowerCase ( ) ; let compilerName = "" ; let additionalArgs = [ ] ; if ( compilerPath ) { if ( compilerPathLowercase ?. endsWith ( "\\cl.exe" ) || compilerPathLowercase ?. endsWith ( "/cl.exe" ) || ( compilerPathLowercase === "cl.exe" ) || compilerPathLowercase ?. endsWith ( "\\cl" ) || compilerPathLowercase ?. endsWith ( "/cl" ) || ( compilerPathLowercase === "cl" ) ) { compilerName = path . basename ( compilerPath ) ; } else if ( compilerPath . startsWith ( "\"" ) ) { const endQuote = compilerPath . substr ( 1 ) . search ( "\"" ) + 1 ; if ( endQuote !== - 1 ) { additionalArgs = extractArgs ( compilerPath . substr ( endQuote + 1 ) ) ; compilerPath = compilerPath . substr ( 1 , endQuote - 1 ) ; compilerName = path . basename ( compilerPath ) ; } } else { let spaceStart = compilerPath . lastIndexOf ( " " ) ; if ( checkFileExistsSync ( compilerPath ) ) { compilerName = path . basename ( compilerPath ) ; } else if ( spaceStart !== - 1 && ! checkFileExistsSync ( compilerPath ) ) { let potentialCompilerPath = compilerPath . substr ( 0 , spaceStart ) ; while ( ! checkFileExistsSync ( potentialCompilerPath ) ) { spaceStart = potentialCompilerPath . lastIndexOf ( " " ) ; if ( spaceStart === - 1 ) { potentialCompilerPath = compilerPath ; break ; } potentialCompilerPath = potentialCompilerPath . substr ( 0 , spaceStart ) ; } if ( compilerPath !== potentialCompilerPath ) { additionalArgs = extractArgs ( compilerPath . substr ( spaceStart + 1 ) ) ; compilerPath = potentialCompilerPath ; compilerName = path . basename ( potentialCompilerPath ) ; } } } } if ( inputCompilerArgs && inputCompilerArgs . length ) { additionalArgs = inputCompilerArgs . concat ( additionalArgs . filter ( function ( item ) : boolean { return inputCompilerArgs . indexOf ( item ) < 0 ; } ) ) ; } return { compilerPath , compilerName , additionalArgs } ; } export function escapeForSquiggles ( s ) { let newResults = "" ; let lastWasBackslash = false ; let lastBackslashWasEscaped = false ; for ( let i = 0 ; i < s . length ; i ++ ) { if ( s [ i ] === '\\' ) { if ( lastWasBackslash ) { newResults += "\\" ; lastBackslashWasEscaped = ! lastBackslashWasEscaped ; } else { lastBackslashWasEscaped = false ; } newResults += "\\" ; lastWasBackslash = true ; } else { if ( lastWasBackslash && ( lastBackslashWasEscaped || ( s [ i ] !== '"' ) ) ) { newResults += "\\" ; } lastWasBackslash = false ; lastBackslashWasEscaped = false ; newResults += s [ i ] ; } } if ( lastWasBackslash ) { newResults += "\\" ; } return newResults ; } export class BlockingTask < T > { private done : boolean = false ; private promise : Thenable < T > ; constructor ( task , dependency ? ) { if ( ! dependency ) { this . promise = task ( ) ; } else { this . promise = new Promise < T > ( ( resolve , reject ) => { const f1 = ( ) => { task ( ) . then ( resolve , reject ) ; } ; const f2 = ( err ) => { console . log ( err ) ; task ( ) . then ( resolve , reject ) ; } ; dependency . promise . then ( f1 , f2 ) ; } ) ; } this . promise . then ( ( ) => this . done = true , ( ) => this . done = true ) ; } public get Done ( ) : boolean { return this . done ; } public getPromise ( ) { return this . promise ; } } interface VSCodeNlsConfig { locale : string ; availableLanguages : { [ pack ] : string ; } ; } export function getLocaleId ( ) { if ( isString ( process . env . VSCODE_NLS_CONFIG ) ) { const vscodeOptions = JSON . parse ( process . env . VSCODE_NLS_CONFIG ) as VSCodeNlsConfig ; if ( vscodeOptions . availableLanguages ) { const value = vscodeOptions . availableLanguages [ '*' ] ; if ( isString ( value ) ) { return value ; } } if ( isString ( vscodeOptions . locale ) ) { return vscodeOptions . locale . toLowerCase ( ) ; } } return "en" ; } export function getLocalizedHtmlPath ( originalPath ) { const locale = getLocaleId ( ) ; const localizedFilePath = getExtensionFilePath ( path . join ( "dist/html/" , locale , originalPath ) ) ; if ( ! fs . existsSync ( localizedFilePath ) ) { return getExtensionFilePath ( originalPath ) ; } return localizedFilePath ; } export interface LocalizeStringParams { text : string ; stringId : number ; stringArgs : string [ ] ; indentSpaces : number ; } export function getLocalizedString ( params ) { let indent = "" ; if ( params . indentSpaces ) { indent = " " . repeat ( params . indentSpaces ) ; } let text = params . text ; if ( params . stringId !== 0 ) { text = lookupString ( params . stringId , params . stringArgs ) ; } return indent + text ; } export function getLocalizedSymbolScope ( scope , detail ) { return localize ( { key : "c.cpp.symbolscope.separator" , comment : [ "{0} is an untranslated C++ keyword (e.g. \"private\") and {1} is either another keyword (e.g. \"typedef\") or a localized property (e.g. a localized version of \"declaration\"" ] } , "{0}, {1}" , scope , detail ) ; } function decodeUCS16 ( input ) { const output = [ ] ; let counter = 0 ; const length = input . length ; let value ; let extra ; while ( counter < length ) { value = input . charCodeAt ( counter ++ ) ; if ( ( value & 0xF800 ) === 0xD800 && counter < length ) { extra = input . charCodeAt ( counter ++ ) ; if ( ( extra & 0xFC00 ) === 0xDC00 ) { output . push ( ( ( value & 0x3FF ) << 10 ) + ( extra & 0x3FF ) + 0x10000 ) ; } else { output . push ( value , extra ) ; } } else { output . push ( value ) ; } } return output ; } const allowedIdentifierUnicodeRanges = [ [ 0x0030 , 0x0039 ] , [ 0x0041 , 0x005A ] , [ 0x005F , 0x005F ] , [ 0x0061 , 0x007A ] , [ 0x00A8 , 0x00A8 ] , [ 0x00AA , 0x00AA ] , [ 0x00AD , 0x00AD ] , [ 0x00AF , 0x00AF ] , [ 0x00B2 , 0x00B5 ] , [ 0x00B7 , 0x00BA ] , [ 0x00BC , 0x00BE ] , [ 0x00C0 , 0x00D6 ] , [ 0x00D8 , 0x00F6 ] , [ 0x00F8 , 0x167F ] , [ 0x1681 , 0x180D ] , [ 0x180F , 0x1FFF ] , [ 0x200B , 0x200D ] , [ 0x202A , 0x202E ] , [ 0x203F , 0x2040 ] , [ 0x2054 , 0x2054 ] , [ 0x2060 , 0x218F ] , [ 0x2460 , 0x24FF ] , [ 0x2776 , 0x2793 ] , [ 0x2C00 , 0x2DFF ] , [ 0x2E80 , 0x2FFF ] , [ 0x3004 , 0x3007 ] , [ 0x3021 , 0x302F ] , [ 0x3031 , 0xD7FF ] , [ 0xF900 , 0xFD3D ] , [ 0xFD40 , 0xFDCF ] , [ 0xFDF0 , 0xFE44 ] , [ 0xFE47 , 0xFFFD ] , [ 0x10000 , 0x1FFFD ] , [ 0x20000 , 0x2FFFD ] , [ 0x30000 , 0x3FFFD ] , [ 0x40000 , 0x4FFFD ] , [ 0x50000 , 0x5FFFD ] , [ 0x60000 , 0x6FFFD ] , [ 0x70000 , 0x7FFFD ] , [ 0x80000 , 0x8FFFD ] , [ 0x90000 , 0x9FFFD ] , [ 0xA0000 , 0xAFFFD ] , [ 0xB0000 , 0xBFFFD ] , [ 0xC0000 , 0xCFFFD ] , [ 0xD0000 , 0xDFFFD ] , [ 0xE0000 , 0xEFFFD ] ] ; const disallowedFirstCharacterIdentifierUnicodeRanges = [ [ 0x0030 , 0x0039 ] , [ 0x0300 , 0x036F ] , [ 0x1DC0 , 0x1DFF ] , [ 0x20D0 , 0x20FF ] , [ 0xFE20 , 0xFE2F ] ] ; export function isValidIdentifier ( candidate ) { if ( ! candidate ) { return false ; } const decoded = decodeUCS16 ( candidate ) ; if ( ! decoded || ! decoded . length ) { return false ; } for ( let i = 0 ; i < disallowedFirstCharacterIdentifierUnicodeRanges . length ; i ++ ) { const disallowedCharacters = disallowedFirstCharacterIdentifierUnicodeRanges [ i ] ; if ( decoded [ 0 ] >= disallowedCharacters [ 0 ] && decoded [ 0 ] <= disallowedCharacters [ 1 ] ) { return false ; } } for ( let position = 0 ; position < decoded . length ; position ++ ) { let found = false ; for ( let i = 0 ; i < allowedIdentifierUnicodeRanges . length ; i ++ ) { const allowedCharacters = allowedIdentifierUnicodeRanges [ i ] ; if ( decoded [ position ] >= allowedCharacters [ 0 ] && decoded [ position ] <= allowedCharacters [ 1 ] ) { found = true ; break ; } } if ( ! found ) { return false ; } } return true ; } function getUniqueWorkspaceNameHelper ( workspaceFolder , addSubfolder ) { const workspaceFolderName = workspaceFolder ? workspaceFolder . name : "untitled" ; if ( ! workspaceFolder || workspaceFolder . index < 1 ) { return workspaceFolderName ; } for ( let i = 0 ; i < workspaceFolder . index ; ++ i ) { if ( vscode . workspace . workspaceFolders && vscode . workspace . workspaceFolders . length > 0 && vscode . workspace . workspaceFolders [ i ] . name === workspaceFolderName ) { return addSubfolder ? path . join ( workspaceFolderName , String ( workspaceFolder . index ) ) : workspaceFolderName + String ( workspaceFolder . index ) ; } } return workspaceFolderName ; } export function getUniqueWorkspaceName ( workspaceFolder ) { return getUniqueWorkspaceNameHelper ( workspaceFolder , false ) ; } export function getUniqueWorkspaceStorageName ( workspaceFolder ) { return getUniqueWorkspaceNameHelper ( workspaceFolder , true ) ; } export function isCodespaces ( ) { return ! ! process . env [ "CODESPACES" ] ; } export function sequentialResolve < T > ( items , promiseBuilder ) { return items . reduce ( async ( previousPromise , nextItem ) => { await previousPromise ; return promiseBuilder ( nextItem ) ; } , Promise . resolve ( ) ) ; } export function normalizeArg ( arg ) { arg = arg . trimLeft ( ) . trimRight ( ) ; if ( /^`.*`$/g . test ( arg ) || /.*[^\\]".*/g . test ( arg ) || ( process . platform . includes ( "win" ) && /.*[^\\]'.*/g . test ( arg ) ) || ( ! process . platform . includes ( "win" ) && /.*[^\\]'.*/g . test ( arg ) ) ) { return arg ; } const unescapedSpaces = arg . split ( '' ) . find ( ( char , index ) => index > 0 && char === " " && arg [ index - 1 ] !== "\\" ) ; if ( ! unescapedSpaces && ! process . platform . includes ( "win" ) ) { return arg ; } else if ( arg . includes ( " " ) ) { arg = arg . replace ( /\\\s/g , " " ) ; return "\"" + arg + "\"" ; } else { return arg ; } } /**
 * Find PowerShell executable from PATH (for Windows only).
 */ export function findPowerShell ( ) { const dirs = ( process . env . PATH || '' ) . replace ( /"+/g , '' ) . split ( ';' ) . filter ( x => x ) ; const exts = ( process . env . PATHEXT || '' ) . split ( ';' ) ; const names = [ 'pwsh' , 'powershell' ] ; for ( const name of names ) { const candidates = dirs . reduce < string [ ] > ( ( paths , dir ) => [ ... paths , ... exts . map ( ext => path . join ( dir , name + ext ) ) ] , [ ] ) ; for ( const candidate of candidates ) { try { if ( fs . statSync ( candidate ) . isFile ( ) ) { return name ; } } catch ( e ) { } } } } export function getCppToolsTargetPopulation ( ) { if ( checkFileExistsSync ( getExtensionFilePath ( "insiders.flag" ) ) ) { return TargetPopulation . Insiders ; } else if ( checkFileExistsSync ( getExtensionFilePath ( "release.flag" ) ) ) { return TargetPopulation . Public ; } return TargetPopulation . Internal ; } export function isVsCodeInsiders ( ) { return extensionPath . includes ( ".vscode-insiders" ) || extensionPath . includes ( ".vscode-server-insiders" ) || extensionPath . includes ( ".vscode-exploration" ) || extensionPath . includes ( ".vscode-server-exploration" ) ; }