'use strict' ; import * as vscode from 'vscode' ; import * as util from '../common' ; /**
 * track settings changes for telemetry
 */ type FilterFunction = ( key : string , val : string , settings : vscode . WorkspaceConfiguration ) => boolean ; type KeyValuePair = { key : string ; value : string ; } ; const maxSettingLengthForTelemetry = 50 ; let cache ; export class SettingsTracker { private previousCppSettings : { [ key ] : any ; } = { } ; private resource : vscode . Uri | undefined ; constructor ( resource ) { this . resource = resource ; this . collectSettings ( ( ) => true ) ; } public getUserModifiedSettings ( ) { const filter = ( key , val , settings ) => ! this . areEqual ( val , settings . inspect ( key ) ?. defaultValue ) ; return this . collectSettings ( filter ) ; } public getChangedSettings ( ) { const filter = ( key , val ) => ! ( key in this . previousCppSettings ) || ! this . areEqual ( val , this . previousCppSettings [ key ] ) ; return this . collectSettings ( filter ) ; } private collectSettings ( filter ) { const settingsResourceScope = vscode . workspace . getConfiguration ( "C_Cpp" , this . resource ) ; const settingsNonScoped = vscode . workspace . getConfiguration ( "C_Cpp" ) ; const selectCorrectlyScopedSettings = ( rawSetting ) : vscode . WorkspaceConfiguration => ( ! rawSetting || rawSetting . scope === "resource" || rawSetting . scope === "machine-overridable" ) ? settingsResourceScope : settingsNonScoped ; const result = { } ; for ( const key in settingsResourceScope ) { const rawSetting = util . packageJson . contributes . configuration . properties [ "C_Cpp." + key ] ; const correctlyScopedSettings = selectCorrectlyScopedSettings ( rawSetting ) ; const val = this . getSetting ( correctlyScopedSettings , key ) ; if ( val === undefined ) { continue ; } const collectSettingsRecursive = ( key , val , depth ) => { if ( depth > 4 ) { return ; } for ( const subKey in val ) { const newKey = key + "." + subKey ; const newRawSetting = util . packageJson . contributes . configuration . properties [ "C_Cpp." + newKey ] ; const correctlyScopedSubSettings = selectCorrectlyScopedSettings ( newRawSetting ) ; const subVal = this . getSetting ( correctlyScopedSubSettings , newKey ) ; if ( subVal === undefined ) { continue ; } if ( subVal instanceof Object && ! ( subVal instanceof Array ) ) { collectSettingsRecursive ( newKey , subVal , depth + 1 ) ; } else { const entry = this . filterAndSanitize ( newKey , subVal , correctlyScopedSubSettings , filter ) ; if ( entry && entry . key && entry . value ) { result [ entry . key ] = entry . value ; } } } } ; if ( val instanceof Object && ! ( val instanceof Array ) ) { collectSettingsRecursive ( key , val , 1 ) ; continue ; } const entry = this . filterAndSanitize ( key , val , correctlyScopedSettings , filter ) ; if ( entry && entry . key && entry . value ) { result [ entry . key ] = entry . value ; } } return result ; } private getSetting ( settings , key ) { if ( settings . inspect ( key ) ?. defaultValue !== undefined ) { const val = settings . get ( key ) ; if ( val instanceof Object ) { return val ; } const curSetting = util . packageJson . contributes . configuration . properties [ "C_Cpp." + key ] ; if ( curSetting ) { const type = this . typeMatch ( val , curSetting [ "type" ] ) ; if ( type ) { if ( type !== "string" ) { return val ; } const curEnum = curSetting [ "enum" ] ; if ( curEnum && curEnum . indexOf ( val ) === - 1 ) { return "<invalid>" ; } return val ; } } } return undefined ; } private typeMatch ( value , type ? ) { if ( type ) { if ( type instanceof Array ) { for ( let i = 0 ; i < type . length ; i ++ ) { const t = type [ i ] ; if ( t ) { if ( typeof value === t ) { return t ; } if ( t === "array" && value instanceof Array ) { return t ; } if ( t === "null" && value === null ) { return t ; } } } } else if ( typeof type === "string" && typeof value === type ) { return type ; } } return undefined ; } private filterAndSanitize ( key , val , settings , filter ) { if ( filter ( key , val , settings ) ) { let value ; this . previousCppSettings [ key ] = val ; switch ( key ) { case "clang_format_style" : case "clang_format_fallbackStyle" : { const newKey = key + "2" ; if ( val ) { switch ( String ( val ) . toLowerCase ( ) ) { case "emulated visual studio" : case "visual studio" : case "llvm" : case "google" : case "chromium" : case "mozilla" : case "webkit" : case "file" : case "none" : { value = String ( this . previousCppSettings [ key ] ) ; break ; } default : { value = "..." ; break ; } } } else { value = "null" ; } key = newKey ; break ; } case "commentContinuationPatterns" : { key = "commentContinuationPatterns2" ; value = this . areEqual ( val , settings . inspect ( key ) ?. defaultValue ) ? "<default>" : "..." ; break ; } default : { if ( key === "clang_format_path" || key === "intelliSenseCachePath" || key . startsWith ( "default." ) || key === "codeAnalysis.clangTidy.path" || key === "codeAnalysis.clangTidy.headerFilter" || key === "codeAnalysis.clangTidy.args" || key === "codeAnalysis.clangTidy.config" || key === "codeAnalysis.clangTidy.fallbackConfig" || key === "files.exclude" || key === "codeAnalysis.exclude" ) { value = this . areEqual ( val , settings . inspect ( key ) ?. defaultValue ) ? "<default>" : "..." ; } else { value = String ( this . previousCppSettings [ key ] ) ; } } } if ( value && value . length > maxSettingLengthForTelemetry ) { value = value . substr ( 0 , maxSettingLengthForTelemetry ) + "..." ; } return { key : key , value : value } ; } return undefined ; } private areEqual ( value1 , value2 ) { if ( value1 instanceof Object && value2 instanceof Object ) { return JSON . stringify ( value1 ) === JSON . stringify ( value2 ) ; } return value1 === value2 ; } } export function getTracker ( resource ) { if ( ! cache ) { cache = new SettingsTracker ( resource ) ; } return cache ; }