'use strict' ; import * as path from 'path' ; import * as fs from "fs" ; import * as vscode from 'vscode' ; import * as util from '../common' ; import * as telemetry from '../telemetry' ; import { PersistentFolderState } from './persistentState' ; import { CppSettings , OtherSettings } from './settings' ; import { CustomConfigurationProviderCollection , getCustomConfigProviders } from './customProviders' ; import { SettingsPanel } from './settingsPanel' ; import * as os from 'os' ; import escapeStringRegExp = require ( 'escape-string-regexp' ) ; import * as jsonc from 'comment-json' ; import * as nls from 'vscode-nls' ; import { setTimeout } from 'timers' ; import * as which from 'which' ; import { WorkspaceBrowseConfiguration } from 'vscode-cpptools' ; nls . config ( { messageFormat : nls . MessageFormat . bundle , bundleFormat : nls . BundleFormat . standalone } ) ( ) ; const localize = nls . loadMessageBundle ( ) ; const configVersion = 4 ; type Environment = { [ key ] : string | string [ ] ; } ; function getDefaultConfig ( ) { if ( process . platform === 'darwin' ) { return { name : "Mac" } ; } else if ( process . platform === 'win32' ) { return { name : "Win32" } ; } else { return { name : "Linux" } ; } } function getDefaultCppProperties ( ) { return { configurations : [ getDefaultConfig ( ) ] , version : configVersion } ; } export interface ConfigurationJson { configurations : Configuration [ ] ; env ? : { [ key ] : string | string [ ] ; } ; version : number ; enableConfigurationSquiggles ? : boolean ; } export interface Configuration { name : string ; compilerPath ? : string ; compilerPathIsExplicit ? : boolean ; compilerArgs ? : string [ ] ; cStandard ? : string ; cStandardIsExplicit ? : boolean ; cppStandard ? : string ; cppStandardIsExplicit ? : boolean ; includePath ? : string [ ] ; macFrameworkPath ? : string [ ] ; windowsSdkVersion ? : string ; defines ? : string [ ] ; intelliSenseMode ? : string ; intelliSenseModeIsExplicit ? : boolean ; compileCommands ? : string ; forcedInclude ? : string [ ] ; configurationProvider ? : string ; mergeConfigurations ? : boolean ; browse ? : Browse ; customConfigurationVariables ? : { [ key ] : string ; } ; } export interface ConfigurationErrors { name ? : string ; compilerPath ? : string ; includePath ? : string ; intelliSenseMode ? : string ; macFrameworkPath ? : string ; forcedInclude ? : string ; compileCommands ? : string ; browsePath ? : string ; databaseFilename ? : string ; } export interface Browse { path ? : string [ ] ; limitSymbolsToIncludedHeaders ? : boolean | string ; databaseFilename ? : string ; } export interface KnownCompiler { path : string ; isC : boolean ; } export interface CompilerDefaults { compilerPath : string ; compilerArgs : string [ ] ; knownCompilers : KnownCompiler [ ] ; cStandard : string ; cppStandard : string ; includes : string [ ] ; frameworks : string [ ] ; windowsSdkVersion : string ; intelliSenseMode : string ; rootfs : string ; } export class CppProperties { private rootUri : vscode . Uri | undefined ; private propertiesFile : vscode . Uri | undefined | null = undefined ; private readonly configFolder : string ; private configurationJson ? : ConfigurationJson ; private currentConfigurationIndex : PersistentFolderState < number > | undefined ; private configFileWatcher : vscode . FileSystemWatcher | null = null ; private configFileWatcherFallbackTime : Date = new Date ( ) ; private compileCommandsFile : vscode . Uri | undefined | null = undefined ; private compileCommandsFileWatchers : fs . FSWatcher [ ] = [ ] ; private compileCommandsFileWatcherFallbackTime : Date = new Date ( ) ; private defaultCompilerPath : string | null = null ; private knownCompilers ? : KnownCompiler [ ] ; private defaultCStandard : string | null = null ; private defaultCppStandard : string | null = null ; private defaultIncludes : string [ ] | null = null ; private defaultFrameworks ? : string [ ] ; private defaultWindowsSdkVersion : string | null = null ; private vcpkgIncludes : string [ ] = [ ] ; private vcpkgPathReady : boolean = false ; private nodeAddonIncludes : string [ ] = [ ] ; private defaultIntelliSenseMode ? : string ; private defaultCustomConfigurationVariables ? : { [ key ] : string ; } ; private readonly configurationGlobPattern : string = "c_cpp_properties.json" ; private disposables : vscode . Disposable [ ] = [ ] ; private configurationsChanged = new vscode . EventEmitter < CppProperties > ( ) ; private selectionChanged = new vscode . EventEmitter < number > ( ) ; private compileCommandsChanged = new vscode . EventEmitter < string > ( ) ; private diagnosticCollection : vscode . DiagnosticCollection ; private prevSquiggleMetrics : Map < string , { [ key ] : number ; } > = new Map < string , { [ key ] : number ; } > ( ) ; private rootfs : string | null = null ; private settingsPanel ? : SettingsPanel ; private lastCustomBrowseConfiguration : PersistentFolderState < WorkspaceBrowseConfiguration | undefined > | undefined ; private lastCustomBrowseConfigurationProviderId : PersistentFolderState < string | undefined > | undefined ; private configurationIncomplete : boolean = true ; constructor ( rootUri ? , workspaceFolder ? ) { this . rootUri = rootUri ; const rootPath = rootUri ? rootUri . fsPath : "" ; if ( workspaceFolder ) { this . currentConfigurationIndex = new PersistentFolderState < number > ( "CppProperties.currentConfigurationIndex" , - 1 , workspaceFolder ) ; this . lastCustomBrowseConfiguration = new PersistentFolderState < WorkspaceBrowseConfiguration | undefined > ( "CPP.lastCustomBrowseConfiguration" , undefined , workspaceFolder ) ; this . lastCustomBrowseConfigurationProviderId = new PersistentFolderState < string | undefined > ( "CPP.lastCustomBrowseConfigurationProviderId" , undefined , workspaceFolder ) ; } this . configFolder = path . join ( rootPath , ".vscode" ) ; this . diagnosticCollection = vscode . languages . createDiagnosticCollection ( rootPath ) ; this . buildVcpkgIncludePath ( ) ; const userSettings = new CppSettings ( ) ; if ( userSettings . addNodeAddonIncludePaths ) { this . readNodeAddonIncludeLocations ( rootPath ) ; } this . disposables . push ( vscode . Disposable . from ( this . configurationsChanged , this . selectionChanged , this . compileCommandsChanged ) ) ; } public get ConfigurationsChanged ( ) : vscode . Event < CppProperties > { return this . configurationsChanged . event ; } public get SelectionChanged ( ) : vscode . Event < number > { return this . selectionChanged . event ; } public get CompileCommandsChanged ( ) : vscode . Event < string > { return this . compileCommandsChanged . event ; } public get Configurations ( ) : Configuration [ ] | undefined { return this . configurationJson ? this . configurationJson . configurations : undefined ; } public get CurrentConfigurationIndex ( ) : number { return this . currentConfigurationIndex === undefined ? 0 : this . currentConfigurationIndex . Value ; } public get CurrentConfiguration ( ) : Configuration | undefined { return this . Configurations ? this . Configurations [ this . CurrentConfigurationIndex ] : undefined ; } public get KnownCompiler ( ) : KnownCompiler [ ] | undefined { return this . knownCompilers ; } public get LastCustomBrowseConfiguration ( ) : PersistentFolderState < WorkspaceBrowseConfiguration | undefined > | undefined { return this . lastCustomBrowseConfiguration ; } public get LastCustomBrowseConfigurationProviderId ( ) : PersistentFolderState < string | undefined > | undefined { return this . lastCustomBrowseConfigurationProviderId ; } public get CurrentConfigurationProvider ( ) : string | undefined { if ( this . CurrentConfiguration ?. configurationProvider ) { return this . CurrentConfiguration . configurationProvider ; } return new CppSettings ( this . rootUri ) . defaultConfigurationProvider ; } public get ConfigurationNames ( ) : string [ ] | undefined { const result = [ ] ; if ( this . configurationJson ) { this . configurationJson . configurations . forEach ( ( config ) => { result . push ( config . name ) ; } ) ; } return result ; } public set CompilerDefaults ( compilerDefaults ) { this . defaultCompilerPath = compilerDefaults . compilerPath ; this . knownCompilers = compilerDefaults . knownCompilers ; this . defaultCStandard = compilerDefaults . cStandard ; this . defaultCppStandard = compilerDefaults . cppStandard ; this . defaultIncludes = compilerDefaults . includes ; this . defaultFrameworks = compilerDefaults . frameworks ; this . defaultWindowsSdkVersion = compilerDefaults . windowsSdkVersion ; this . defaultIntelliSenseMode = compilerDefaults . intelliSenseMode ; this . rootfs = compilerDefaults . rootfs ; const configFilePath = path . join ( this . configFolder , "c_cpp_properties.json" ) ; if ( this . rootUri !== null && fs . existsSync ( configFilePath ) ) { this . propertiesFile = vscode . Uri . file ( configFilePath ) ; } else { this . propertiesFile = null ; } const settingsPath = path . join ( this . configFolder , this . configurationGlobPattern ) ; this . configFileWatcher = vscode . workspace . createFileSystemWatcher ( settingsPath ) ; this . disposables . push ( this . configFileWatcher ) ; this . configFileWatcher . onDidCreate ( ( uri ) => { this . propertiesFile = uri ; this . handleConfigurationChange ( ) ; } ) ; this . configFileWatcher . onDidDelete ( ( ) => { this . propertiesFile = null ; this . resetToDefaultSettings ( true ) ; this . handleConfigurationChange ( ) ; } ) ; this . configFileWatcher . onDidChange ( ( ) => { let alreadyTracking = false ; for ( let i = 0 ; i < vscode . workspace . textDocuments . length ; i ++ ) { if ( vscode . workspace . textDocuments [ i ] . uri . fsPath === settingsPath ) { alreadyTracking = true ; break ; } } if ( ! alreadyTracking ) { this . handleConfigurationChange ( ) ; } } ) ; vscode . workspace . onDidChangeTextDocument ( ( e ) => { if ( e . document . uri . fsPath === settingsPath ) { this . handleConfigurationChange ( ) ; } } ) ; vscode . workspace . onDidSaveTextDocument ( ( doc ) => { const savedDocWorkspaceFolder = vscode . workspace . getWorkspaceFolder ( doc . uri ) ; const notifyingWorkspaceFolder = vscode . workspace . getWorkspaceFolder ( vscode . Uri . file ( settingsPath ) ) ; if ( ( ! savedDocWorkspaceFolder && vscode . workspace . workspaceFolders && vscode . workspace . workspaceFolders . length > 0 && notifyingWorkspaceFolder === vscode . workspace . workspaceFolders [ 0 ] ) || savedDocWorkspaceFolder === notifyingWorkspaceFolder ) { let fileType ; const documentPath = doc . uri . fsPath . toLowerCase ( ) ; if ( documentPath . endsWith ( "cmakelists.txt" ) ) { fileType = "CMakeLists" ; } else if ( documentPath . endsWith ( "cmakecache.txt" ) ) { fileType = "CMakeCache" ; } else if ( documentPath . endsWith ( ".cmake" ) ) { fileType = ".cmake" ; } if ( fileType ) { telemetry . logLanguageServerEvent ( "cmakeFileWrite" , { filetype : fileType , outside : ( savedDocWorkspaceFolder === undefined ) . toString ( ) } ) ; } } } ) ; this . handleConfigurationChange ( ) ; } public get VcpkgInstalled ( ) : boolean { return this . vcpkgIncludes . length > 0 ; } private onConfigurationsChanged ( ) { if ( this . Configurations ) { this . configurationsChanged . fire ( this ) ; } } private onSelectionChanged ( ) { this . selectionChanged . fire ( this . CurrentConfigurationIndex ) ; this . handleSquiggles ( ) ; } private onCompileCommandsChanged ( path ) { this . compileCommandsChanged . fire ( path ) ; } public onDidChangeSettings ( ) { if ( ! this . propertiesFile ) { this . resetToDefaultSettings ( true ) ; this . handleConfigurationChange ( ) ; } else if ( ! this . configurationIncomplete ) { this . handleConfigurationChange ( ) ; } } private resetToDefaultSettings ( resetIndex ) { this . configurationJson = getDefaultCppProperties ( ) ; if ( resetIndex || this . CurrentConfigurationIndex < 0 || this . CurrentConfigurationIndex >= this . configurationJson . configurations . length ) { const index = this . getConfigIndexForPlatform ( this . configurationJson ) ; if ( this . currentConfigurationIndex !== undefined ) { if ( index === undefined ) { this . currentConfigurationIndex . setDefault ( ) ; } else { this . currentConfigurationIndex . Value = index ; } } } this . configurationIncomplete = true ; } private applyDefaultIncludePathsAndFrameworks ( ) { if ( this . configurationIncomplete && this . defaultIncludes && this . defaultFrameworks && this . vcpkgPathReady ) { const configuration = this . CurrentConfiguration ; if ( configuration ) { this . applyDefaultConfigurationValues ( configuration ) ; this . configurationIncomplete = false ; } } } private applyDefaultConfigurationValues ( configuration ) { const settings = new CppSettings ( this . rootUri ) ; const isUnset = ( input ) => input === null || input === undefined ; const rootFolder = "${workspaceFolder}/**" ; const defaultFolder = "${default}" ; if ( isUnset ( settings . defaultIncludePath ) ) { configuration . includePath = [ rootFolder ] . concat ( this . vcpkgIncludes ) ; } else { configuration . includePath = [ defaultFolder ] ; } if ( isUnset ( settings . defaultDefines ) ) { configuration . defines = ( process . platform === 'win32' ) ? [ "_DEBUG" , "UNICODE" , "_UNICODE" ] : [ ] ; } if ( isUnset ( settings . defaultMacFrameworkPath ) && process . platform === 'darwin' ) { configuration . macFrameworkPath = this . defaultFrameworks ; } if ( ( isUnset ( settings . defaultWindowsSdkVersion ) || settings . defaultWindowsSdkVersion === "" ) && this . defaultWindowsSdkVersion && process . platform === 'win32' ) { configuration . windowsSdkVersion = this . defaultWindowsSdkVersion ; } if ( isUnset ( settings . defaultCompilerPath ) && this . defaultCompilerPath && ( isUnset ( settings . defaultCompileCommands ) || settings . defaultCompileCommands === "" ) && ! configuration . compileCommands ) { configuration . compilerPath = this . defaultCompilerPath ; } if ( ( isUnset ( settings . defaultCStandard ) || settings . defaultCStandard === "" ) && this . defaultCStandard ) { configuration . cStandard = this . defaultCStandard ; } if ( ( isUnset ( settings . defaultCppStandard ) || settings . defaultCppStandard === "" ) && this . defaultCppStandard ) { configuration . cppStandard = this . defaultCppStandard ; } if ( isUnset ( settings . defaultIntelliSenseMode ) || settings . defaultIntelliSenseMode === "" ) { configuration . intelliSenseMode = this . defaultIntelliSenseMode ; } if ( isUnset ( settings . defaultCustomConfigurationVariables ) || settings . defaultCustomConfigurationVariables === { } ) { configuration . customConfigurationVariables = this . defaultCustomConfigurationVariables ; } } private get ExtendedEnvironment ( ) : Environment { const result = { } ; if ( this . configurationJson ?. env ) { Object . assign ( result , this . configurationJson . env ) ; } result [ "workspaceFolderBasename" ] = this . rootUri ? path . basename ( this . rootUri . fsPath ) : "" ; result [ "execPath" ] = process . execPath ; result [ "pathSeparator" ] = ( os . platform ( ) === 'win32' ) ? "\\" : "/" ; return result ; } private async buildVcpkgIncludePath ( ) { try { const vcpkgRoot = util . getVcpkgRoot ( ) ; if ( vcpkgRoot ) { const list = await util . readDir ( vcpkgRoot ) ; if ( list !== undefined ) { list . forEach ( ( entry ) => { if ( entry !== "vcpkg" ) { const pathToCheck = path . join ( vcpkgRoot , entry ) ; if ( fs . existsSync ( pathToCheck ) ) { let p = path . join ( pathToCheck , "include" ) ; if ( fs . existsSync ( p ) ) { p = p . replace ( /\\/g , "/" ) ; p = p . replace ( vcpkgRoot , "${vcpkgRoot}" ) ; this . vcpkgIncludes . push ( p ) ; } } } } ) ; } } } catch ( error ) { } finally { this . vcpkgPathReady = true ; this . handleConfigurationChange ( ) ; } } public nodeAddonIncludesFound ( ) { return this . nodeAddonIncludes . length ; } private async readNodeAddonIncludeLocations ( rootPath ) { let error ; let pdjFound = false ; let packageJson ; try { packageJson = JSON . parse ( await fs . promises . readFile ( path . join ( rootPath , "package.json" ) , "utf8" ) ) ; pdjFound = true ; } catch ( errJS ) { const err = errJS as Error ; error = err ; } if ( ! error ) { try { const pathToNode = which . sync ( "node" ) ; const nodeAddonMap = [ [ "node-addon-api" , `"${ pathToNode }" --no-warnings -p "require('node-addon-api').include"` ] , [ "nan" , `"${ pathToNode }" --no-warnings -e "require('nan')"` ] ] ; const pathToYarn = which . sync ( "yarn" , { nothrow : true } ) ; if ( pathToYarn && await util . checkDirectoryExists ( path . join ( rootPath , ".yarn/cache" ) ) ) { nodeAddonMap . push ( [ "node-addon-api" , `"${ pathToYarn }" node --no-warnings -p "require('node-addon-api').include"` ] , [ "nan" , `"${ pathToYarn }" node --no-warnings -e "require('nan')"` ] ) ; } for ( const [ dep , execCmd ] of nodeAddonMap ) { if ( dep in packageJson . dependencies ) { try { let stdout = await util . execChildProcess ( execCmd , rootPath ) ; if ( ! stdout ) { continue ; } if ( stdout [ stdout . length - 1 ] === "\n" ) { stdout = stdout . slice ( 0 , - 1 ) ; } if ( stdout [ 0 ] === "\"" && stdout [ stdout . length - 1 ] === "\"" ) { stdout = stdout . slice ( 1 , - 1 ) ; } if ( ! await util . checkDirectoryExists ( stdout ) ) { stdout = path . join ( rootPath , stdout ) ; if ( ! await util . checkDirectoryExists ( stdout ) ) { error = new Error ( `${ dep } directory ${ stdout } doesn't exist` ) ; stdout = '' ; } } if ( stdout ) { this . nodeAddonIncludes . push ( stdout ) ; } } catch ( errJS ) { const err = errJS as Error ; console . log ( 'readNodeAddonIncludeLocations' , err . message ) ; } } } } catch ( errJS ) { const e = errJS as Error ; error = e ; } } if ( error ) { if ( pdjFound ) { console . log ( 'readNodeAddonIncludeLocations' , error . message ) ; } } else { this . handleConfigurationChange ( ) ; } } private getConfigIndexForPlatform ( config ) { if ( ! this . configurationJson ) { return undefined ; } let plat ; if ( process . platform === 'darwin' ) { plat = "Mac" ; } else if ( process . platform === 'win32' ) { plat = "Win32" ; } else { plat = "Linux" ; } for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { if ( config . configurations [ i ] . name === plat ) { return i ; } } return this . configurationJson . configurations . length - 1 ; } private getIntelliSenseModeForPlatform ( name ? ) { if ( name === "Linux" ) { return "linux-gcc-x64" ; } else if ( name === "Mac" ) { return "macos-clang-x64" ; } else if ( name === "Win32" ) { return "windows-msvc-x64" ; } else if ( process . platform === 'win32' ) { return "windows-msvc-x64" ; } else if ( process . platform === 'darwin' ) { return "macos-clang-x64" ; } else { return "linux-gcc-x64" ; } } private validateIntelliSenseMode ( configuration ) { if ( configuration . compilerPath === undefined || configuration . compilerPath === "" || configuration . compilerPath === "${default}" || configuration . intelliSenseMode === undefined || configuration . intelliSenseMode === "" || configuration . intelliSenseMode === "${default}" ) { return "" ; } const resolvedCompilerPath = this . resolvePath ( configuration . compilerPath , true ) ; const compilerPathAndArgs = util . extractCompilerPathAndArgs ( resolvedCompilerPath ) ; const isValid = ( ( compilerPathAndArgs . compilerName . toLowerCase ( ) === "cl.exe" || compilerPathAndArgs . compilerName . toLowerCase ( ) === "cl" ) === configuration . intelliSenseMode . includes ( "msvc" ) || ( compilerPathAndArgs . compilerName . toLowerCase ( ) === "nvcc.exe" ) || ( compilerPathAndArgs . compilerName . toLowerCase ( ) === "nvcc" ) ) ; if ( isValid ) { return "" ; } else { return localize ( "incompatible.intellisense.mode" , "IntelliSense mode {0} is incompatible with compiler path." , configuration . intelliSenseMode ) ; } } public addToIncludePathCommand ( path ) { this . handleConfigurationEditCommand ( ( ) => { this . parsePropertiesFile ( ) ; const config = this . CurrentConfiguration ; if ( config ) { telemetry . logLanguageServerEvent ( "addToIncludePath" ) ; if ( config . includePath === undefined ) { config . includePath = [ "${default}" ] ; } config . includePath . splice ( config . includePath . length , 0 , path ) ; this . writeToJson ( ) ; this . handleConfigurationChange ( ) ; } } , ( ) => { } ) ; } public updateCustomConfigurationProvider ( providerId ) { return new Promise < void > ( ( resolve ) => { if ( this . propertiesFile ) { this . handleConfigurationEditJSONCommand ( ( ) => { this . parsePropertiesFile ( ) ; const config = this . CurrentConfiguration ; if ( config ) { if ( providerId ) { config . configurationProvider = providerId ; } else { delete config . configurationProvider ; } this . writeToJson ( ) ; this . handleConfigurationChange ( ) ; } resolve ( ) ; } , ( ) => { } ) ; } else { const settings = new CppSettings ( this . rootUri ) ; if ( providerId ) { settings . update ( "default.configurationProvider" , providerId ) ; } else { settings . update ( "default.configurationProvider" , undefined ) ; } const config = this . CurrentConfiguration ; if ( config ) { config . configurationProvider = providerId ; } resolve ( ) ; } } ) ; } public setCompileCommands ( path ) { this . handleConfigurationEditJSONCommand ( ( ) => { this . parsePropertiesFile ( ) ; const config = this . CurrentConfiguration ; if ( config ) { config . compileCommands = path ; this . writeToJson ( ) ; this . handleConfigurationChange ( ) ; } } , ( ) => { } ) ; } public select ( index ) { if ( this . configurationJson ) { if ( index === this . configurationJson . configurations . length ) { this . handleConfigurationEditUICommand ( ( ) => { } , vscode . window . showTextDocument ) ; return ; } if ( index === this . configurationJson . configurations . length + 1 ) { this . handleConfigurationEditJSONCommand ( ( ) => { } , vscode . window . showTextDocument ) ; return ; } } if ( this . currentConfigurationIndex !== undefined ) { this . currentConfigurationIndex . Value = index ; } this . onSelectionChanged ( ) ; } private resolveDefaults ( entries , defaultValue ? ) { let result = [ ] ; entries . forEach ( entry => { if ( entry === "${default}" ) { if ( defaultValue ) { result = result . concat ( defaultValue ) ; } } else { result . push ( entry ) ; } } ) ; return result ; } private resolveDefaultsDictionary ( entries , defaultValue , env ) { const result = { } ; for ( const property in entries ) { if ( property === "${default}" ) { if ( defaultValue ) { for ( const defaultProperty in defaultValue ) { if ( ! ( defaultProperty in entries ) ) { result [ defaultProperty ] = util . resolveVariables ( defaultValue [ defaultProperty ] , env ) ; } } } } else { result [ property ] = util . resolveVariables ( entries [ property ] , env ) ; } } return result ; } private resolve ( entries , defaultValue , env ) { let result = [ ] ; if ( entries ) { entries = this . resolveDefaults ( entries , defaultValue ) ; entries . forEach ( entry => { const entryResolved = util . resolveVariables ( entry , env ) ; result = result . concat ( entryResolved ) ; } ) ; } return result ; } private resolveAndSplit ( paths , defaultValue , env ) { let result = [ ] ; if ( paths ) { paths = this . resolveDefaults ( paths , defaultValue ) ; paths . forEach ( entry => { const entries = util . resolveVariables ( entry , env ) . split ( util . envDelimiter ) . filter ( e => e ) ; result = result . concat ( entries ) ; } ) ; } return result ; } private updateConfigurationString ( property , defaultValue , env , acceptBlank ? ) { if ( property === null || property === undefined || property === "${default}" ) { property = defaultValue ; } if ( property === null || property === undefined || ( acceptBlank !== true && property === "" ) ) { return undefined ; } return util . resolveVariables ( property , env ) ; } private updateConfigurationStringArray ( property , defaultValue , env ) { if ( property ) { return this . resolve ( property , defaultValue , env ) ; } if ( ! property && defaultValue ) { return this . resolve ( defaultValue , [ ] , env ) ; } return property ; } private updateConfigurationPathsArray ( paths , defaultValue , env ) { if ( paths ) { return this . resolveAndSplit ( paths , defaultValue , env ) ; } if ( ! paths && defaultValue ) { return this . resolveAndSplit ( defaultValue , [ ] , env ) ; } return paths ; } private updateConfigurationStringOrBoolean ( property , defaultValue , env ) { if ( ! property || property === "${default}" ) { property = defaultValue ; } if ( ! property || property === "" ) { return undefined ; } if ( typeof property === "boolean" ) { return property ; } return util . resolveVariables ( property , env ) ; } private updateConfigurationBoolean ( property , defaultValue ) { if ( property === null || property === undefined ) { property = defaultValue ; } if ( property === null ) { return undefined ; } return property ; } private updateConfigurationStringDictionary ( property , defaultValue , env ) { if ( ! property || property === { } ) { property = defaultValue ; } if ( ! property || property === { } ) { return undefined ; } return this . resolveDefaultsDictionary ( property , defaultValue , env ) ; } private updateServerOnFolderSettingsChange ( ) { if ( ! this . configurationJson ) { return ; } const settings = new CppSettings ( this . rootUri ) ; const userSettings = new CppSettings ( ) ; const env = this . ExtendedEnvironment ; for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { const configuration = this . configurationJson . configurations [ i ] ; configuration . includePath = this . updateConfigurationPathsArray ( configuration . includePath , settings . defaultIncludePath , env ) ; const origIncludePath = configuration . includePath ; if ( userSettings . addNodeAddonIncludePaths ) { const includePath = origIncludePath || [ ] ; configuration . includePath = includePath . concat ( this . nodeAddonIncludes . filter ( i => includePath . indexOf ( i ) < 0 ) ) ; } configuration . defines = this . updateConfigurationStringArray ( configuration . defines , settings . defaultDefines , env ) ; configuration . macFrameworkPath = this . updateConfigurationPathsArray ( configuration . macFrameworkPath , settings . defaultMacFrameworkPath , env ) ; configuration . windowsSdkVersion = this . updateConfigurationString ( configuration . windowsSdkVersion , settings . defaultWindowsSdkVersion , env ) ; configuration . forcedInclude = this . updateConfigurationPathsArray ( configuration . forcedInclude , settings . defaultForcedInclude , env ) ; configuration . compileCommands = this . updateConfigurationString ( configuration . compileCommands , settings . defaultCompileCommands , env ) ; configuration . compilerArgs = this . updateConfigurationStringArray ( configuration . compilerArgs , settings . defaultCompilerArgs , env ) ; configuration . cStandard = this . updateConfigurationString ( configuration . cStandard , settings . defaultCStandard , env ) ; configuration . cppStandard = this . updateConfigurationString ( configuration . cppStandard , settings . defaultCppStandard , env ) ; configuration . intelliSenseMode = this . updateConfigurationString ( configuration . intelliSenseMode , settings . defaultIntelliSenseMode , env ) ; configuration . intelliSenseModeIsExplicit = configuration . intelliSenseModeIsExplicit || settings . defaultIntelliSenseMode !== "" ; configuration . cStandardIsExplicit = configuration . cStandardIsExplicit || settings . defaultCStandard !== "" ; configuration . cppStandardIsExplicit = configuration . cppStandardIsExplicit || settings . defaultCppStandard !== "" ; configuration . mergeConfigurations = this . updateConfigurationBoolean ( configuration . mergeConfigurations , settings . defaultMergeConfigurations ) ; if ( ! configuration . compileCommands ) { configuration . compilerPath = this . updateConfigurationString ( configuration . compilerPath , settings . defaultCompilerPath , env , true ) ; configuration . compilerPathIsExplicit = configuration . compilerPathIsExplicit || settings . defaultCompilerPath !== undefined ; if ( configuration . compilerPath === undefined ) { if ( ! ! this . defaultCompilerPath ) { configuration . compilerPath = this . defaultCompilerPath ; if ( ! configuration . cStandard && ! ! this . defaultCStandard ) { configuration . cStandard = this . defaultCStandard ; configuration . cStandardIsExplicit = false ; } if ( ! configuration . cppStandard && ! ! this . defaultCppStandard ) { configuration . cppStandard = this . defaultCppStandard ; configuration . cppStandardIsExplicit = false ; } if ( ! configuration . intelliSenseMode && ! ! this . defaultIntelliSenseMode ) { configuration . intelliSenseMode = this . defaultIntelliSenseMode ; configuration . intelliSenseModeIsExplicit = false ; } if ( ! configuration . windowsSdkVersion && ! ! this . defaultWindowsSdkVersion ) { configuration . windowsSdkVersion = this . defaultWindowsSdkVersion ; } if ( ! origIncludePath && ! ! this . defaultIncludes ) { const includePath = configuration . includePath || [ ] ; configuration . includePath = includePath . concat ( this . defaultIncludes ) ; } if ( ! configuration . macFrameworkPath && ! ! this . defaultFrameworks ) { configuration . macFrameworkPath = this . defaultFrameworks ; } } } } else { if ( configuration . compilerPath === "${default}" ) { configuration . compilerPath = settings . defaultCompilerPath ; } if ( configuration . compilerPath === null ) { configuration . compilerPath = undefined ; configuration . compilerPathIsExplicit = true ; } else if ( configuration . compilerPath !== undefined ) { configuration . compilerPath = util . resolveVariables ( configuration . compilerPath , env ) ; configuration . compilerPathIsExplicit = true ; } else { configuration . compilerPathIsExplicit = false ; } } configuration . customConfigurationVariables = this . updateConfigurationStringDictionary ( configuration . customConfigurationVariables , settings . defaultCustomConfigurationVariables , env ) ; configuration . configurationProvider = this . updateConfigurationString ( configuration . configurationProvider , settings . defaultConfigurationProvider , env ) ; if ( ! configuration . browse ) { configuration . browse = { } ; } if ( ! configuration . browse . path ) { if ( settings . defaultBrowsePath ) { configuration . browse . path = settings . defaultBrowsePath ; } else if ( configuration . includePath ) { configuration . browse . path = configuration . includePath . slice ( 0 ) ; if ( configuration . includePath . findIndex ( ( value , index ) => ! ! value . match ( /^\$\{(workspaceRoot|workspaceFolder)\}(\\\*{0,2}|\/\*{0,2})?$/g ) ) === - 1 ) { configuration . browse . path . push ( "${workspaceFolder}" ) ; } } } else { configuration . browse . path = this . updateConfigurationPathsArray ( configuration . browse . path , settings . defaultBrowsePath , env ) ; } configuration . browse . limitSymbolsToIncludedHeaders = this . updateConfigurationStringOrBoolean ( configuration . browse . limitSymbolsToIncludedHeaders , settings . defaultLimitSymbolsToIncludedHeaders , env ) ; configuration . browse . databaseFilename = this . updateConfigurationString ( configuration . browse . databaseFilename , settings . defaultDatabaseFilename , env ) ; if ( i === this . CurrentConfigurationIndex ) { const providers = getCustomConfigProviders ( ) ; const hasEmptyConfiguration = ! this . propertiesFile && ! settings . defaultCompilerPath && settings . defaultCompilerPath !== "" && ! settings . defaultIncludePath && ! settings . defaultDefines && ! settings . defaultMacFrameworkPath && settings . defaultWindowsSdkVersion === "" && ! settings . defaultForcedInclude && settings . defaultCompileCommands === "" && ! settings . defaultCompilerArgs && settings . defaultCStandard === "" && settings . defaultCppStandard === "" && settings . defaultIntelliSenseMode === "" && settings . defaultConfigurationProvider === "" ; let keepCachedBrowseConfig = true ; if ( hasEmptyConfiguration ) { if ( providers . size === 1 ) { providers . forEach ( provider => { configuration . configurationProvider = provider . extensionId ; } ) ; if ( this . lastCustomBrowseConfigurationProviderId !== undefined ) { keepCachedBrowseConfig = configuration . configurationProvider === this . lastCustomBrowseConfigurationProviderId . Value ; } } else if ( this . lastCustomBrowseConfigurationProviderId !== undefined && ! ! this . lastCustomBrowseConfigurationProviderId . Value ) { configuration . configurationProvider = this . lastCustomBrowseConfigurationProviderId . Value ; } } else if ( this . lastCustomBrowseConfigurationProviderId !== undefined ) { keepCachedBrowseConfig = configuration . configurationProvider === this . lastCustomBrowseConfigurationProviderId . Value ; } if ( ! keepCachedBrowseConfig && this . lastCustomBrowseConfiguration !== undefined ) { this . lastCustomBrowseConfiguration . Value = undefined ; } } } this . updateCompileCommandsFileWatchers ( ) ; if ( ! this . configurationIncomplete ) { this . onConfigurationsChanged ( ) ; } } private compileCommandsFileWatcherTimer ? : NodeJS . Timer ; private compileCommandsFileWatcherFiles : Set < string > = new Set < string > ( ) ; public updateCompileCommandsFileWatchers ( ) { if ( this . configurationJson ) { this . compileCommandsFileWatchers . forEach ( ( watcher ) => watcher . close ( ) ) ; this . compileCommandsFileWatchers = [ ] ; const filePaths = new Set < string > ( ) ; this . configurationJson . configurations . forEach ( c => { if ( c . compileCommands ) { const fileSystemCompileCommandsPath = this . resolvePath ( c . compileCommands , os . platform ( ) === "win32" ) ; if ( fs . existsSync ( fileSystemCompileCommandsPath ) ) { filePaths . add ( fileSystemCompileCommandsPath ) ; } } } ) ; try { filePaths . forEach ( ( path ) => { this . compileCommandsFileWatchers . push ( fs . watch ( path , ( event , filename ) => { if ( this . compileCommandsFileWatcherTimer ) { clearInterval ( this . compileCommandsFileWatcherTimer ) ; } this . compileCommandsFileWatcherFiles . add ( path ) ; this . compileCommandsFileWatcherTimer = setTimeout ( ( ) => { this . compileCommandsFileWatcherFiles . forEach ( ( path ) => { this . onCompileCommandsChanged ( path ) ; } ) ; if ( this . compileCommandsFileWatcherTimer ) { clearInterval ( this . compileCommandsFileWatcherTimer ) ; } this . compileCommandsFileWatcherFiles . clear ( ) ; this . compileCommandsFileWatcherTimer = undefined ; } , 1000 ) ; } ) ) ; } ) ; } catch ( e ) { } } } public handleConfigurationEditCommand ( onBeforeOpen , showDocument , viewColumn ? ) { const otherSettings = new OtherSettings ( this . rootUri ) ; if ( otherSettings . settingsEditor === "ui" ) { this . handleConfigurationEditUICommand ( onBeforeOpen , showDocument , viewColumn ) ; } else { this . handleConfigurationEditJSONCommand ( onBeforeOpen , showDocument , viewColumn ) ; } } public async handleConfigurationEditJSONCommand ( onBeforeOpen , showDocument , viewColumn ? ) { await this . ensurePropertiesFile ( ) ; console . assert ( this . propertiesFile ) ; if ( onBeforeOpen ) { onBeforeOpen ( ) ; } if ( this . propertiesFile ) { const document = await vscode . workspace . openTextDocument ( this . propertiesFile ) ; if ( showDocument ) { showDocument ( document , viewColumn ) ; } } } private ensureSettingsPanelInitlialized ( ) { if ( this . settingsPanel === undefined ) { const settings = new CppSettings ( this . rootUri ) ; this . settingsPanel = new SettingsPanel ( ) ; this . settingsPanel . setKnownCompilers ( this . knownCompilers , settings . preferredPathSeparator ) ; this . settingsPanel . SettingsPanelActivated ( ( ) => { if ( this . settingsPanel ?. initialized ) { this . onSettingsPanelActivated ( ) ; } } ) ; this . settingsPanel . ConfigValuesChanged ( ( ) => this . saveConfigurationUI ( ) ) ; this . settingsPanel . ConfigSelectionChanged ( ( ) => this . onConfigSelectionChanged ( ) ) ; this . settingsPanel . AddConfigRequested ( ( e ) => this . onAddConfigRequested ( e ) ) ; this . disposables . push ( this . settingsPanel ) ; } } public async handleConfigurationEditUICommand ( onBeforeOpen , showDocument , viewColumn ? ) { await this . ensurePropertiesFile ( ) ; if ( this . propertiesFile ) { if ( onBeforeOpen ) { onBeforeOpen ( ) ; } if ( this . parsePropertiesFile ( ) ) { this . ensureSettingsPanelInitlialized ( ) ; if ( this . settingsPanel ) { const configNames = this . ConfigurationNames ; if ( configNames && this . configurationJson ) { this . settingsPanel . selectedConfigIndex = this . CurrentConfigurationIndex ; this . settingsPanel . createOrShow ( configNames , this . configurationJson . configurations [ this . settingsPanel . selectedConfigIndex ] , this . getErrorsForConfigUI ( this . settingsPanel . selectedConfigIndex ) , viewColumn ) ; } } } else { const document = await vscode . workspace . openTextDocument ( this . propertiesFile ) ; if ( showDocument ) { showDocument ( document , viewColumn ) ; } } } } private async onSettingsPanelActivated ( ) { if ( this . configurationJson ) { await this . ensurePropertiesFile ( ) ; if ( this . propertiesFile ) { if ( this . parsePropertiesFile ( ) ) { const configNames = this . ConfigurationNames ; if ( configNames && this . settingsPanel && this . configurationJson ) { if ( this . settingsPanel . selectedConfigIndex >= this . configurationJson . configurations . length ) { this . settingsPanel . selectedConfigIndex = this . CurrentConfigurationIndex ; } this . settingsPanel . updateConfigUI ( configNames , this . configurationJson . configurations [ this . settingsPanel . selectedConfigIndex ] , this . getErrorsForConfigUI ( this . settingsPanel . selectedConfigIndex ) ) ; } else { vscode . workspace . openTextDocument ( this . propertiesFile ) ; } } } } } private saveConfigurationUI ( ) { this . parsePropertiesFile ( ) ; if ( this . settingsPanel && this . configurationJson ) { const config = this . settingsPanel . getLastValuesFromConfigUI ( ) ; this . configurationJson . configurations [ this . settingsPanel . selectedConfigIndex ] = config ; this . settingsPanel . updateErrors ( this . getErrorsForConfigUI ( this . settingsPanel . selectedConfigIndex ) ) ; this . writeToJson ( ) ; } } private onConfigSelectionChanged ( ) { const configNames = this . ConfigurationNames ; if ( configNames && this . settingsPanel && this . configurationJson ) { this . settingsPanel . updateConfigUI ( configNames , this . configurationJson . configurations [ this . settingsPanel . selectedConfigIndex ] , this . getErrorsForConfigUI ( this . settingsPanel . selectedConfigIndex ) ) ; } } private onAddConfigRequested ( configName ) { this . parsePropertiesFile ( ) ; const newConfig = { name : configName } ; this . applyDefaultConfigurationValues ( newConfig ) ; const configNames = this . ConfigurationNames ; if ( configNames && this . settingsPanel && this . configurationJson ) { this . configurationJson . configurations . push ( newConfig ) ; this . settingsPanel . selectedConfigIndex = this . configurationJson . configurations . length - 1 ; this . settingsPanel . updateConfigUI ( configNames , this . configurationJson . configurations [ this . settingsPanel . selectedConfigIndex ] , null ) ; this . writeToJson ( ) ; } } public handleConfigurationChange ( ) { if ( this . propertiesFile === undefined ) { return ; } this . configFileWatcherFallbackTime = new Date ( ) ; if ( this . propertiesFile ) { this . parsePropertiesFile ( ) ; if ( this . configurationJson ) { if ( this . CurrentConfigurationIndex < 0 || this . CurrentConfigurationIndex >= this . configurationJson . configurations . length ) { const index = this . getConfigIndexForPlatform ( this . configurationJson ) ; if ( this . currentConfigurationIndex !== undefined ) { if ( ! index ) { this . currentConfigurationIndex . setDefault ( ) ; } else { this . currentConfigurationIndex . Value = index ; } } } } } if ( ! this . configurationJson ) { this . resetToDefaultSettings ( true ) ; } this . applyDefaultIncludePathsAndFrameworks ( ) ; this . updateServerOnFolderSettingsChange ( ) ; } private async ensurePropertiesFile ( ) { if ( this . propertiesFile && await util . checkFileExists ( this . propertiesFile . fsPath ) ) { return ; } else { try { if ( ! await util . checkDirectoryExists ( this . configFolder ) ) { fs . mkdirSync ( this . configFolder ) ; } const fullPathToFile = path . join ( this . configFolder , "c_cpp_properties.json" ) ; const settings = new CppSettings ( this . rootUri ) ; let providerId = settings . defaultConfigurationProvider ; if ( this . configurationJson ) { if ( ! providerId ) { providerId = this . configurationJson . configurations [ 0 ] . configurationProvider ; } this . resetToDefaultSettings ( true ) ; } this . applyDefaultIncludePathsAndFrameworks ( ) ; if ( providerId ) { if ( this . configurationJson ) { this . configurationJson . configurations [ 0 ] . configurationProvider = providerId ; } } await util . writeFileText ( fullPathToFile , jsonc . stringify ( this . configurationJson , null , 4 ) ) ; this . propertiesFile = vscode . Uri . file ( path . join ( this . configFolder , "c_cpp_properties.json" ) ) ; } catch ( errJS ) { const err = errJS as Error ; const failedToCreate = localize ( "failed.to.create.config.folder" , 'Failed to create "{0}"' , this . configFolder ) ; vscode . window . showErrorMessage ( `${ failedToCreate }: ${ err . message }` ) ; } } return ; } private parsePropertiesFile ( ) { if ( ! this . propertiesFile ) { return false ; } let success = true ; try { const readResults = fs . readFileSync ( this . propertiesFile . fsPath , 'utf8' ) ; if ( readResults === "" ) { return false ; } const newJson = jsonc . parse ( readResults ) ; if ( ! newJson || ! newJson . configurations || newJson . configurations . length === 0 ) { throw { message : localize ( "invalid.configuration.file" , "Invalid configuration file. There must be at least one configuration present in the array." ) } ; } if ( ! this . configurationIncomplete && this . configurationJson && this . configurationJson . configurations && this . CurrentConfigurationIndex >= 0 && this . CurrentConfigurationIndex < this . configurationJson . configurations . length ) { for ( let i = 0 ; i < newJson . configurations . length ; i ++ ) { if ( newJson . configurations [ i ] . name === this . configurationJson . configurations [ this . CurrentConfigurationIndex ] . name ) { if ( this . currentConfigurationIndex !== undefined ) { this . currentConfigurationIndex . Value = i ; } break ; } } } this . configurationJson = newJson ; if ( this . CurrentConfigurationIndex < 0 || this . CurrentConfigurationIndex >= newJson . configurations . length ) { const index = this . getConfigIndexForPlatform ( newJson ) ; if ( this . currentConfigurationIndex !== undefined ) { if ( index === undefined ) { this . currentConfigurationIndex . setDefault ( ) ; } else { this . currentConfigurationIndex . Value = index ; } } } let dirty = false ; for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { const newId = getCustomConfigProviders ( ) . checkId ( this . configurationJson . configurations [ i ] . configurationProvider ) ; if ( newId !== this . configurationJson . configurations [ i ] . configurationProvider ) { dirty = true ; this . configurationJson . configurations [ i ] . configurationProvider = newId ; } } if ( this . configurationJson . env ) { delete this . configurationJson . env [ 'workspaceRoot' ] ; delete this . configurationJson . env [ 'workspaceFolder' ] ; delete this . configurationJson . env [ 'workspaceFolderBasename' ] ; delete this . configurationJson . env [ 'execPath' ] ; delete this . configurationJson . env [ 'pathSeparator' ] ; delete this . configurationJson . env [ 'default' ] ; } this . configurationIncomplete = false ; if ( this . configurationJson . version !== configVersion ) { dirty = true ; if ( this . configurationJson . version === undefined ) { this . updateToVersion2 ( ) ; } if ( this . configurationJson . version === 2 ) { this . updateToVersion3 ( ) ; } if ( this . configurationJson . version === 3 ) { this . updateToVersion4 ( ) ; } else { this . configurationJson . version = configVersion ; vscode . window . showErrorMessage ( localize ( "unknown.properties.version" , 'Unknown version number found in c_cpp_properties.json. Some features may not work as expected.' ) ) ; } } this . configurationJson . configurations . forEach ( e => { if ( ( < any > e ) . knownCompilers !== undefined ) { delete ( < any > e ) . knownCompilers ; dirty = true ; } } ) ; for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { if ( ( this . configurationJson . configurations [ i ] . compilerPathIsExplicit !== undefined ) || ( this . configurationJson . configurations [ i ] . cStandardIsExplicit !== undefined ) || ( this . configurationJson . configurations [ i ] . cppStandardIsExplicit !== undefined ) || ( this . configurationJson . configurations [ i ] . intelliSenseModeIsExplicit !== undefined ) ) { dirty = true ; break ; } } if ( dirty ) { try { this . writeToJson ( ) ; } catch ( err ) { vscode . window . showWarningMessage ( localize ( 'update.properties.failed' , 'Attempt to update "{0}" failed (do you have write access?)' , this . propertiesFile . fsPath ) ) ; success = false ; } } this . configurationJson . configurations . forEach ( e => { e . compilerPathIsExplicit = e . compilerPath !== undefined ; e . cStandardIsExplicit = e . cStandard !== undefined ; e . cppStandardIsExplicit = e . cppStandard !== undefined ; e . intelliSenseModeIsExplicit = e . intelliSenseMode !== undefined ; } ) ; } catch ( errJS ) { const err = errJS as Error ; const failedToParse = localize ( "failed.to.parse.properties" , 'Failed to parse "{0}"' , this . propertiesFile . fsPath ) ; vscode . window . showErrorMessage ( `${ failedToParse }: ${ err . message }` ) ; success = false ; } if ( success ) { this . handleSquiggles ( ) ; } return success ; } private resolvePath ( path , isWindows ) { if ( ! path || path === "${default}" ) { return "" ; } let result = "" ; result = util . resolveVariables ( path , this . ExtendedEnvironment ) ; if ( this . rootUri ) { if ( result . includes ( "${workspaceFolder}" ) ) { result = result . replace ( "${workspaceFolder}" , this . rootUri . fsPath ) ; } if ( result . includes ( "${workspaceRoot}" ) ) { result = result . replace ( "${workspaceRoot}" , this . rootUri . fsPath ) ; } } if ( result . includes ( "${vcpkgRoot}" ) && util . getVcpkgRoot ( ) ) { result = result . replace ( "${vcpkgRoot}" , util . getVcpkgRoot ( ) ) ; } if ( result . includes ( "*" ) ) { result = result . replace ( /\*/g , "" ) ; } if ( isWindows && result . startsWith ( "/" ) ) { const mntStr = "/mnt/" ; if ( result . length > "/mnt/c/" . length && result . substr ( 0 , mntStr . length ) === mntStr ) { result = result . substr ( mntStr . length ) ; result = result . substr ( 0 , 1 ) + ":" + result . substr ( 1 ) ; } else if ( this . rootfs && this . rootfs . length > 0 ) { result = this . rootfs + result . substr ( 1 ) ; } } return result ; } private getErrorsForConfigUI ( configIndex ) { const errors = { } ; if ( ! this . configurationJson ) { return errors ; } const isWindows = os . platform ( ) === 'win32' ; const config = this . configurationJson . configurations [ configIndex ] ; errors . name = this . isConfigNameUnique ( config . name ) ; let resolvedCompilerPath = this . resolvePath ( config . compilerPath , isWindows ) ; const compilerPathAndArgs = util . extractCompilerPathAndArgs ( resolvedCompilerPath ) ; if ( resolvedCompilerPath && compilerPathAndArgs . compilerName . toLowerCase ( ) !== "cl.exe" && compilerPathAndArgs . compilerName . toLowerCase ( ) !== "cl" ) { resolvedCompilerPath = resolvedCompilerPath . trim ( ) ; const compilerPathNeedsQuotes = ( compilerPathAndArgs . additionalArgs && compilerPathAndArgs . additionalArgs . length > 0 ) && ! resolvedCompilerPath . startsWith ( '"' ) && compilerPathAndArgs . compilerPath !== undefined && compilerPathAndArgs . compilerPath . includes ( " " ) ; const compilerPathErrors = [ ] ; if ( compilerPathNeedsQuotes ) { compilerPathErrors . push ( localize ( "path.with.spaces" , 'Compiler path with spaces and arguments is missing double quotes " around the path.' ) ) ; } resolvedCompilerPath = compilerPathAndArgs . compilerPath ; if ( resolvedCompilerPath ) { let pathExists = true ; const existsWithExeAdded = ( path ) => isWindows && ! path . startsWith ( "/" ) && fs . existsSync ( path + ".exe" ) ; if ( ! fs . existsSync ( resolvedCompilerPath ) ) { if ( existsWithExeAdded ( resolvedCompilerPath ) ) { resolvedCompilerPath += ".exe" ; } else if ( ! this . rootUri ) { pathExists = false ; } else { const relativePath = this . rootUri . fsPath + path . sep + resolvedCompilerPath ; if ( ! fs . existsSync ( relativePath ) ) { if ( existsWithExeAdded ( resolvedCompilerPath ) ) { resolvedCompilerPath += ".exe" ; } else { pathExists = false ; } } else { resolvedCompilerPath = relativePath ; } } } if ( ! pathExists ) { const message = localize ( 'cannot.find' , "Cannot find: {0}" , resolvedCompilerPath ) ; compilerPathErrors . push ( message ) ; } else if ( compilerPathAndArgs . compilerPath === "" ) { const message = localize ( "cannot.resolve.compiler.path" , "Invalid input, cannot resolve compiler path" ) ; compilerPathErrors . push ( message ) ; } else if ( ! util . checkFileExistsSync ( resolvedCompilerPath ) ) { const message = localize ( "path.is.not.a.file" , "Path is not a file: {0}" , resolvedCompilerPath ) ; compilerPathErrors . push ( message ) ; } if ( compilerPathErrors . length > 0 ) { errors . compilerPath = compilerPathErrors . join ( '\n' ) ; } } } errors . includePath = this . validatePath ( config . includePath ) ; errors . macFrameworkPath = this . validatePath ( config . macFrameworkPath ) ; errors . browsePath = this . validatePath ( config . browse ? config . browse . path : undefined ) ; errors . forcedInclude = this . validatePath ( config . forcedInclude , false , true ) ; errors . compileCommands = this . validatePath ( config . compileCommands , false ) ; errors . databaseFilename = this . validatePath ( ( config . browse ? config . browse . databaseFilename : undefined ) , false ) ; if ( isWindows ) { const intelliSenesModeError = this . validateIntelliSenseMode ( config ) ; if ( intelliSenesModeError . length > 0 ) { errors . intelliSenseMode = intelliSenesModeError ; } } return errors ; } private validatePath ( input , isDirectory = true , skipRelativePaths = false ) { if ( ! input ) { return undefined ; } const isWindows = os . platform ( ) === 'win32' ; let errorMsg ; const errors = [ ] ; let paths = [ ] ; if ( util . isString ( input ) ) { paths . push ( input ) ; } else { paths = input ; } paths = this . resolveAndSplit ( paths , undefined , this . ExtendedEnvironment ) ; for ( const p of paths ) { let pathExists = true ; let resolvedPath = this . resolvePath ( p , isWindows ) ; if ( ! resolvedPath ) { continue ; } if ( ! fs . existsSync ( resolvedPath ) ) { if ( skipRelativePaths && ! path . isAbsolute ( resolvedPath ) ) { continue ; } else if ( ! this . rootUri ) { pathExists = false ; } else { const relativePath = this . rootUri . fsPath + path . sep + resolvedPath ; if ( ! fs . existsSync ( relativePath ) ) { pathExists = false ; } else { resolvedPath = relativePath ; } } } if ( ! pathExists ) { const message = localize ( 'cannot.find' , "Cannot find: {0}" , resolvedPath ) ; errors . push ( message ) ; continue ; } if ( isDirectory && ! util . checkDirectoryExistsSync ( resolvedPath ) ) { const message = localize ( "path.is.not.a.directory" , "Path is not a directory: {0}" , resolvedPath ) ; errors . push ( message ) ; } else if ( ! isDirectory && ! util . checkFileExistsSync ( resolvedPath ) ) { const message = localize ( "path.is.not.a.file" , "Path is not a file: {0}" , resolvedPath ) ; errors . push ( message ) ; } } if ( errors . length > 0 ) { errorMsg = errors . join ( '\n' ) ; } return errorMsg ; } private isConfigNameUnique ( configName ) { let errorMsg ; const occurrences = this . ConfigurationNames ?. filter ( function ( name ) : boolean { return name === configName ; } ) . length ; if ( occurrences && occurrences > 1 ) { errorMsg = localize ( 'duplicate.name' , "{0} is a duplicate. The configuration name should be unique." , configName ) ; } return errorMsg ; } private handleSquiggles ( ) { if ( ! this . propertiesFile ) { return ; } const settings = new CppSettings ( this . rootUri ) ; if ( ! this . configurationJson ) { return ; } if ( ( this . configurationJson . enableConfigurationSquiggles !== undefined && ! this . configurationJson . enableConfigurationSquiggles ) || ( this . configurationJson . enableConfigurationSquiggles === undefined && ! settings . defaultEnableConfigurationSquiggles ) ) { this . diagnosticCollection . clear ( ) ; return ; } vscode . workspace . openTextDocument ( this . propertiesFile ) . then ( ( document ) => { const diagnostics = new Array < vscode . Diagnostic > ( ) ; let curText = document . getText ( ) ; const configurationsText = util . escapeForSquiggles ( curText ) ; const configurations = jsonc . parse ( configurationsText ) ; const currentConfiguration = configurations . configurations [ this . CurrentConfigurationIndex ] ; let curTextStartOffset = 0 ; if ( ! currentConfiguration . name ) { return ; } let envText = "" ; const envStart = curText . search ( /\"env\"\s*:\s*\{/ ) ; const envEnd = envStart === - 1 ? - 1 : curText . indexOf ( "}," , envStart ) ; envText = curText . substr ( envStart , envEnd ) ; const envTextStartOffSet = envStart + 1 ; let allConfigText = curText ; let allConfigTextOffset = envTextStartOffSet ; const nameRegex = new RegExp ( `{\\s*"name"\\s*:\\s*".*"` ) ; let configStart = allConfigText . search ( new RegExp ( nameRegex ) ) ; let configNameStart ; let configNameEnd ; let configName ; const configNames = new Map < string , [ ] > ( ) ; let dupErrorMsg ; while ( configStart !== - 1 ) { allConfigText = allConfigText . substr ( configStart ) ; allConfigTextOffset += configStart ; configNameStart = allConfigText . indexOf ( '"' , allConfigText . indexOf ( ':' ) + 1 ) + 1 ; configNameEnd = allConfigText . indexOf ( '"' , configNameStart ) ; configName = allConfigText . substr ( configNameStart , configNameEnd - configNameStart ) ; const newRange = new vscode . Range ( 0 , allConfigTextOffset + configNameStart , 0 , allConfigTextOffset + configNameEnd ) ; const allRanges = configNames . get ( configName ) ; if ( allRanges ) { allRanges . push ( newRange ) ; configNames . set ( configName , allRanges ) ; } else { configNames . set ( configName , [ newRange ] ) ; } allConfigText = allConfigText . substr ( configNameEnd + 1 ) ; allConfigTextOffset += configNameEnd + 1 ; configStart = allConfigText . search ( new RegExp ( nameRegex ) ) ; } for ( const [ configName , allRanges ] of configNames ) { if ( allRanges && allRanges . length > 1 ) { dupErrorMsg = localize ( 'duplicate.name' , "{0} is a duplicate. The configuration name should be unique." , configName ) ; allRanges . forEach ( nameRange => { const diagnostic = new vscode . Diagnostic ( new vscode . Range ( document . positionAt ( nameRange . start . character ) , document . positionAt ( nameRange . end . character ) ) , dupErrorMsg , vscode . DiagnosticSeverity . Warning ) ; diagnostics . push ( diagnostic ) ; } ) ; } } configStart = curText . search ( new RegExp ( `{\\s*"name"\\s*:\\s*"${ escapeStringRegExp ( currentConfiguration . name ) }"` ) ) ; if ( configStart === - 1 ) { telemetry . logLanguageServerEvent ( "ConfigSquiggles" , { "error" : "config name not first" } ) ; return ; } curTextStartOffset = configStart + 1 ; curText = curText . substr ( curTextStartOffset ) ; const nameEnd = curText . indexOf ( ":" ) ; curTextStartOffset += nameEnd + 1 ; curText = curText . substr ( nameEnd + 1 ) ; const nextNameStart = curText . search ( new RegExp ( '"name"\\s*:\\s*"' ) ) ; if ( nextNameStart !== - 1 ) { curText = curText . substr ( 0 , nextNameStart + 6 ) ; const nextNameStart2 = curText . search ( new RegExp ( '\\s*}\\s*,\\s*{\\s*"name"' ) ) ; if ( nextNameStart2 === - 1 ) { telemetry . logLanguageServerEvent ( "ConfigSquiggles" , { "error" : "next config name not first" } ) ; return ; } curText = curText . substr ( 0 , nextNameStart2 ) ; } if ( this . prevSquiggleMetrics . get ( currentConfiguration . name ) === undefined ) { this . prevSquiggleMetrics . set ( currentConfiguration . name , { PathNonExistent : 0 , PathNotAFile : 0 , PathNotADirectory : 0 , CompilerPathMissingQuotes : 0 , CompilerModeMismatch : 0 } ) ; } const newSquiggleMetrics = { PathNonExistent : 0 , PathNotAFile : 0 , PathNotADirectory : 0 , CompilerPathMissingQuotes : 0 , CompilerModeMismatch : 0 } ; const isWindows = os . platform ( ) === 'win32' ; if ( isWindows ) { const intelliSenseModeStart = curText . search ( /\s*\"intelliSenseMode\"\s*:\s*\"/ ) ; if ( intelliSenseModeStart !== - 1 ) { const intelliSenseModeValueStart = curText . indexOf ( '"' , curText . indexOf ( ":" , intelliSenseModeStart ) ) ; const intelliSenseModeValueEnd = intelliSenseModeStart === - 1 ? - 1 : curText . indexOf ( '"' , intelliSenseModeValueStart + 1 ) + 1 ; const intelliSenseModeError = this . validateIntelliSenseMode ( currentConfiguration ) ; if ( intelliSenseModeError . length > 0 ) { const message = intelliSenseModeError ; const diagnostic = new vscode . Diagnostic ( new vscode . Range ( document . positionAt ( curTextStartOffset + intelliSenseModeValueStart ) , document . positionAt ( curTextStartOffset + intelliSenseModeValueEnd ) ) , message , vscode . DiagnosticSeverity . Warning ) ; diagnostics . push ( diagnostic ) ; newSquiggleMetrics . CompilerModeMismatch ++ ; } } } let paths = [ ] ; let compilerPath ; for ( const pathArray of [ ( currentConfiguration . browse ? currentConfiguration . browse . path : undefined ) , currentConfiguration . includePath , currentConfiguration . macFrameworkPath ] ) { if ( pathArray ) { for ( const curPath of pathArray ) { paths . push ( `${ curPath }` ) ; } } } if ( currentConfiguration . forcedInclude ) { for ( const file of currentConfiguration . forcedInclude ) { const resolvedFilePath = this . resolvePath ( file , isWindows ) ; if ( path . isAbsolute ( resolvedFilePath ) ) { paths . push ( `${ file }` ) ; } } } if ( currentConfiguration . compileCommands ) { paths . push ( `${ currentConfiguration . compileCommands }` ) ; } if ( currentConfiguration . compilerPath ) { compilerPath = currentConfiguration . compilerPath ; } paths = this . resolveAndSplit ( paths , undefined , this . ExtendedEnvironment ) ; compilerPath = util . resolveVariables ( compilerPath , this . ExtendedEnvironment ) . trim ( ) ; compilerPath = this . resolvePath ( compilerPath , isWindows ) ; const forcedIncludeStart = curText . search ( /\s*\"forcedInclude\"\s*:\s*\[/ ) ; const forcedeIncludeEnd = forcedIncludeStart === - 1 ? - 1 : curText . indexOf ( "]" , forcedIncludeStart ) ; const compileCommandsStart = curText . search ( /\s*\"compileCommands\"\s*:\s*\"/ ) ; const compileCommandsEnd = compileCommandsStart === - 1 ? - 1 : curText . indexOf ( '"' , curText . indexOf ( '"' , curText . indexOf ( ":" , compileCommandsStart ) ) + 1 ) ; const compilerPathStart = curText . search ( /\s*\"compilerPath\"\s*:\s*\"/ ) ; const compilerPathValueStart = curText . indexOf ( '"' , curText . indexOf ( ":" , compilerPathStart ) ) ; const compilerPathEnd = compilerPathStart === - 1 ? - 1 : curText . indexOf ( '"' , compilerPathValueStart + 1 ) + 1 ; const processedPaths = new Set < string > ( ) ; let compilerPathNeedsQuotes = false ; let compilerMessage ; const compilerPathAndArgs = util . extractCompilerPathAndArgs ( compilerPath ) ; const compilerLowerCase = compilerPathAndArgs . compilerName . toLowerCase ( ) ; const isClCompiler = compilerLowerCase === "cl" || compilerLowerCase === "cl.exe" ; if ( compilerPathAndArgs . compilerPath && ! isClCompiler ) { compilerPathNeedsQuotes = ( compilerPathAndArgs . additionalArgs && compilerPathAndArgs . additionalArgs . length > 0 ) && ! compilerPath . startsWith ( '"' ) && compilerPathAndArgs . compilerPath . includes ( " " ) ; compilerPath = compilerPathAndArgs . compilerPath ; if ( compilerPathNeedsQuotes || ( compilerPath && ! which . sync ( compilerPath , { nothrow : true } ) ) ) { if ( compilerPathNeedsQuotes ) { compilerMessage = localize ( "path.with.spaces" , 'Compiler path with spaces and arguments is missing double quotes " around the path.' ) ; newSquiggleMetrics . CompilerPathMissingQuotes ++ ; } else if ( ! util . checkFileExistsSync ( compilerPath ) ) { compilerMessage = localize ( "path.is.not.a.file" , "Path is not a file: {0}" , compilerPath ) ; newSquiggleMetrics . PathNotAFile ++ ; } } } const isWSL = isWindows && compilerPath . startsWith ( "/" ) ; let compilerPathExists = true ; if ( this . rootUri && ! isClCompiler ) { const checkPathExists = util . checkPathExistsSync ( compilerPath , this . rootUri . fsPath + path . sep , isWindows , isWSL , true ) ; compilerPathExists = checkPathExists . pathExists ; compilerPath = checkPathExists . path ; } if ( ! compilerPathExists ) { compilerMessage = localize ( 'cannot.find2' , "Cannot find \"{0}\"." , compilerPath ) ; newSquiggleMetrics . PathNonExistent ++ ; } if ( compilerMessage ) { const diagnostic = new vscode . Diagnostic ( new vscode . Range ( document . positionAt ( curTextStartOffset + compilerPathValueStart ) , document . positionAt ( curTextStartOffset + compilerPathEnd ) ) , compilerMessage , vscode . DiagnosticSeverity . Warning ) ; diagnostics . push ( diagnostic ) ; } for ( const curPath of paths ) { if ( processedPaths . has ( curPath ) ) { continue ; } processedPaths . add ( curPath ) ; if ( curPath === "${default}" ) { continue ; } let resolvedPath = this . resolvePath ( curPath , isWindows ) ; if ( ! resolvedPath ) { continue ; } let pathExists = true ; if ( this . rootUri ) { const checkPathExists = util . checkPathExistsSync ( resolvedPath , this . rootUri . fsPath + path . sep , isWindows , isWSL , false ) ; pathExists = checkPathExists . pathExists ; resolvedPath = checkPathExists . path ; } if ( path . sep === "/" ) { resolvedPath = resolvedPath . replace ( /\\/g , path . sep ) ; } else { resolvedPath = resolvedPath . replace ( /\//g , path . sep ) ; } let escapedPath = curPath . replace ( /\"/g , '\\\"' ) ; escapedPath = escapedPath . replace ( /[-\"\/\\^$*+?.()|[\]{}]/g , '\\$&' ) ; const pattern = new RegExp ( `"[^"]*?(?<="|;)${ escapedPath }(?="|;).*?"` , "g" ) ; const configMatches = curText . match ( pattern ) ; if ( configMatches ) { let curOffset = 0 ; let endOffset = 0 ; for ( const curMatch of configMatches ) { curOffset = curText . substr ( endOffset ) . search ( pattern ) + endOffset ; endOffset = curOffset + curMatch . length ; if ( curOffset >= compilerPathStart && curOffset <= compilerPathEnd ) { continue ; } let message ; if ( ! pathExists ) { if ( curOffset >= forcedIncludeStart && curOffset <= forcedeIncludeEnd && ! path . isAbsolute ( resolvedPath ) ) { continue ; } message = localize ( 'cannot.find2' , "Cannot find \"{0}\"." , resolvedPath ) ; newSquiggleMetrics . PathNonExistent ++ ; } else { if ( ( curOffset >= forcedIncludeStart && curOffset <= forcedeIncludeEnd ) || ( curOffset >= compileCommandsStart && curOffset <= compileCommandsEnd ) ) { if ( util . checkFileExistsSync ( resolvedPath ) ) { continue ; } message = localize ( "path.is.not.a.file" , "Path is not a file: {0}" , resolvedPath ) ; newSquiggleMetrics . PathNotAFile ++ ; } else { if ( util . checkDirectoryExistsSync ( resolvedPath ) ) { continue ; } message = localize ( "path.is.not.a.directory" , "Path is not a directory: {0}" , resolvedPath ) ; newSquiggleMetrics . PathNotADirectory ++ ; } } const diagnostic = new vscode . Diagnostic ( new vscode . Range ( document . positionAt ( curTextStartOffset + curOffset ) , document . positionAt ( curTextStartOffset + endOffset ) ) , message , vscode . DiagnosticSeverity . Warning ) ; diagnostics . push ( diagnostic ) ; } } else if ( envText ) { const envMatches = envText . match ( pattern ) ; if ( envMatches ) { let curOffset = 0 ; let endOffset = 0 ; for ( const curMatch of envMatches ) { curOffset = envText . substr ( endOffset ) . search ( pattern ) + endOffset ; endOffset = curOffset + curMatch . length ; let message ; if ( ! pathExists ) { message = localize ( 'cannot.find2' , "Cannot find \"{0}\"." , resolvedPath ) ; newSquiggleMetrics . PathNonExistent ++ ; const diagnostic = new vscode . Diagnostic ( new vscode . Range ( document . positionAt ( envTextStartOffSet + curOffset ) , document . positionAt ( envTextStartOffSet + endOffset ) ) , message , vscode . DiagnosticSeverity . Warning ) ; diagnostics . push ( diagnostic ) ; } } } } } if ( diagnostics . length !== 0 ) { this . diagnosticCollection . set ( document . uri , diagnostics ) ; } else { this . diagnosticCollection . clear ( ) ; } const changedSquiggleMetrics = { } ; if ( newSquiggleMetrics . PathNonExistent !== this . prevSquiggleMetrics . get ( currentConfiguration . name ) ?. PathNonExistent ) { changedSquiggleMetrics . PathNonExistent = newSquiggleMetrics . PathNonExistent ; } if ( newSquiggleMetrics . PathNotAFile !== this . prevSquiggleMetrics . get ( currentConfiguration . name ) ?. PathNotAFile ) { changedSquiggleMetrics . PathNotAFile = newSquiggleMetrics . PathNotAFile ; } if ( newSquiggleMetrics . PathNotADirectory !== this . prevSquiggleMetrics . get ( currentConfiguration . name ) ?. PathNotADirectory ) { changedSquiggleMetrics . PathNotADirectory = newSquiggleMetrics . PathNotADirectory ; } if ( newSquiggleMetrics . CompilerPathMissingQuotes !== this . prevSquiggleMetrics . get ( currentConfiguration . name ) ?. CompilerPathMissingQuotes ) { changedSquiggleMetrics . CompilerPathMissingQuotes = newSquiggleMetrics . CompilerPathMissingQuotes ; } if ( newSquiggleMetrics . CompilerModeMismatch !== this . prevSquiggleMetrics . get ( currentConfiguration . name ) ?. CompilerModeMismatch ) { changedSquiggleMetrics . CompilerModeMismatch = newSquiggleMetrics . CompilerModeMismatch ; } if ( Object . keys ( changedSquiggleMetrics ) . length > 0 ) { telemetry . logLanguageServerEvent ( "ConfigSquiggles" , undefined , changedSquiggleMetrics ) ; } this . prevSquiggleMetrics . set ( currentConfiguration . name , newSquiggleMetrics ) ; } ) ; } private updateToVersion2 ( ) { if ( this . configurationJson ) { this . configurationJson . version = 2 ; } } private updateToVersion3 ( ) { if ( this . configurationJson ) { this . configurationJson . version = 3 ; for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { const config = this . configurationJson . configurations [ i ] ; if ( config . name === "Mac" || ( process . platform === 'darwin' && config . name !== "Win32" && config . name !== "Linux" ) ) { if ( config . macFrameworkPath === undefined ) { config . macFrameworkPath = [ "/System/Library/Frameworks" , "/Library/Frameworks" ] ; } } } } } private updateToVersion4 ( ) { if ( this . configurationJson ) { this . configurationJson . version = 4 ; const settings = new CppSettings ( this . rootUri ) ; for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { const config = this . configurationJson . configurations [ i ] ; if ( config . intelliSenseMode === undefined && ! settings . defaultIntelliSenseMode ) { config . intelliSenseMode = this . getIntelliSenseModeForPlatform ( config . name ) ; } if ( config . compilerPath === undefined && this . defaultCompilerPath && ! config . compileCommands && ! settings . defaultCompilerPath ) { config . compilerPath = this . defaultCompilerPath ; } if ( ! config . cStandard && this . defaultCStandard && ! settings . defaultCStandard ) { config . cStandard = this . defaultCStandard ; } if ( ! config . cppStandard && this . defaultCppStandard && ! settings . defaultCppStandard ) { config . cppStandard = this . defaultCppStandard ; } } } } private writeToJson ( ) { const savedCompilerPathIsExplicit = [ ] ; const savedCStandardIsExplicit = [ ] ; const savedCppStandardIsExplicit = [ ] ; const savedIntelliSenseModeIsExplicit = [ ] ; if ( this . configurationJson ) { this . configurationJson . configurations . forEach ( e => { savedCompilerPathIsExplicit . push ( ! ! e . compilerPathIsExplicit ) ; if ( e . compilerPathIsExplicit !== undefined ) { delete e . compilerPathIsExplicit ; } savedCStandardIsExplicit . push ( ! ! e . cStandardIsExplicit ) ; if ( e . cStandardIsExplicit !== undefined ) { delete e . cStandardIsExplicit ; } savedCppStandardIsExplicit . push ( ! ! e . cppStandardIsExplicit ) ; if ( e . cppStandardIsExplicit !== undefined ) { delete e . cppStandardIsExplicit ; } savedIntelliSenseModeIsExplicit . push ( ! ! e . intelliSenseModeIsExplicit ) ; if ( e . intelliSenseModeIsExplicit !== undefined ) { delete e . intelliSenseModeIsExplicit ; } } ) ; } console . assert ( this . propertiesFile ) ; if ( this . propertiesFile ) { fs . writeFileSync ( this . propertiesFile . fsPath , jsonc . stringify ( this . configurationJson , null , 4 ) ) ; } if ( this . configurationJson ) { for ( let i = 0 ; i < this . configurationJson . configurations . length ; i ++ ) { this . configurationJson . configurations [ i ] . compilerPathIsExplicit = savedCompilerPathIsExplicit [ i ] ; this . configurationJson . configurations [ i ] . cStandardIsExplicit = savedCStandardIsExplicit [ i ] ; this . configurationJson . configurations [ i ] . cppStandardIsExplicit = savedCppStandardIsExplicit [ i ] ; this . configurationJson . configurations [ i ] . intelliSenseModeIsExplicit = savedIntelliSenseModeIsExplicit [ i ] ; } } } public checkCppProperties ( ) { const propertiesFile = path . join ( this . configFolder , "c_cpp_properties.json" ) ; fs . stat ( propertiesFile , ( err , stats ) => { if ( err ) { if ( err . code === "ENOENT" && this . propertiesFile ) { this . propertiesFile = null ; this . resetToDefaultSettings ( true ) ; this . handleConfigurationChange ( ) ; } } else if ( stats . mtime > this . configFileWatcherFallbackTime ) { if ( ! this . propertiesFile ) { this . propertiesFile = vscode . Uri . file ( propertiesFile ) ; } this . handleConfigurationChange ( ) ; } } ) ; } public checkCompileCommands ( ) { const compileCommands = this . CurrentConfiguration ?. compileCommands ; if ( ! compileCommands ) { return ; } const compileCommandsFile = this . resolvePath ( compileCommands , os . platform ( ) === "win32" ) ; fs . stat ( compileCommandsFile , ( err , stats ) => { if ( err ) { if ( err . code === "ENOENT" && this . compileCommandsFile ) { this . compileCommandsFileWatchers = [ ] ; this . onCompileCommandsChanged ( compileCommandsFile ) ; this . compileCommandsFile = null ; } } else if ( stats . mtime > this . compileCommandsFileWatcherFallbackTime ) { this . compileCommandsFileWatcherFallbackTime = new Date ( ) ; this . onCompileCommandsChanged ( compileCommandsFile ) ; this . compileCommandsFile = vscode . Uri . file ( compileCommandsFile ) ; } } ) ; } dispose ( ) { this . disposables . forEach ( ( d ) => d . dispose ( ) ) ; this . disposables = [ ] ; this . compileCommandsFileWatchers . forEach ( ( watcher ) => watcher . close ( ) ) ; this . compileCommandsFileWatchers = [ ] ; this . diagnosticCollection . dispose ( ) ; } }