import * as path from 'path' ; import * as vscode from "vscode" ; import { AzureUserInput , callWithTelemetryAndErrorHandling , callWithTelemetryAndErrorHandlingSync , createAzExtOutputChannel , IActionContext , ITelemetryContext , parseError , registerCommand , registerUIExtensionVariables , TelemetryProperties } from "vscode-azureextensionui" ; import { armTemplateLanguageId , configKeys , configPrefix , documentSchemes , expressionsDiagnosticsCompletionMessage , expressionsDiagnosticsSource , globalStateKeys , outputChannelName } from "../common" ; import { delay } from "../test/support/delay" ; import { writeToLog } from '../test/support/testLog' ; import { DeploymentDocument , ResolvableCodeLens } from "./documents/DeploymentDocument" ; import { DeploymentFileMapping } from "./documents/parameters/DeploymentFileMapping" ; import { DeploymentParametersDoc } from "./documents/parameters/DeploymentParametersDoc" ; import { IParameterDefinitionsSource } from "./documents/parameters/IParameterDefinitionsSource" ; import { IParameterValuesSource } from "./documents/parameters/IParameterValuesSource" ; import { IParameterValuesSourceProvider } from "./documents/parameters/IParameterValuesSourceProvider" ; import { defaultTabSize } from './documents/parameters/parameterFileGeneration' ; import { considerQueryingForParameterFileInBackground , getFriendlyPathToFile , openParameterFile , openTemplateFile , selectParameterFile } from "./documents/parameters/parameterFiles" ; import { addMissingParameters } from "./documents/parameters/ParameterValues" ; import { IReferenceSite , PositionContext } from "./documents/positionContexts/PositionContext" ; import { TemplatePositionContext } from "./documents/positionContexts/TemplatePositionContext" ; import { DeploymentTemplateDoc } from "./documents/templates/DeploymentTemplateDoc" ; import { ExtractItem } from './documents/templates/ExtractItem' ; import { getNormalizedDocumentKey } from './documents/templates/getNormalizedDocumentKey' ; import { gotoResources } from './documents/templates/gotoResources' ; import { IJsonDocument } from './documents/templates/IJsonDocument' ; import { getItemTypeQuickPicks , InsertItem } from "./documents/templates/insertItem" ; import { assignTemplateGraphToDeploymentTemplate , INotifyTemplateGraphArgs , openLinkedTemplateFileCommand , tryLoadNonLocalLinkedFile } from './documents/templates/linkedTemplates/linkedTemplates' ; import { allSchemas , getPreferredSchema } from './documents/templates/schemas' ; import { getQuickPickItems , sortTemplate } from "./documents/templates/sortTemplate" ; import { mightBeDeploymentParameters , mightBeDeploymentTemplate , setLangIdToArm , templateDocumentSelector , templateOrParameterDocumentSelector } from "./documents/templates/supported" ; import { TemplateSectionType } from "./documents/templates/TemplateSectionType" ; import { UnsupportedJsonDocument } from './documents/UnsupportedJsonDocument' ; import { ext } from "./extensionVariables" ; import { assert } from './fixed_assert' ; import { IProvideOpenedDocuments } from './IProvideOpenedDocuments' ; import * as TLE from "./language/expressions/TLE" ; import { Issue , IssueSeverity } from "./language/Issue" ; import * as Json from "./language/json/JSON" ; import { ReferenceList } from "./language/ReferenceList" ; import { Span } from "./language/Span" ; import { getAvailableResourceTypesAndVersionsNoThrow } from './languageclient/getAvailableResourceTypesAndVersionsNoThrow' ; import { showAvailableResourceTypesAndVersions } from './languageclient/showAvailableResourceTypesAndVersions' ; import { LanguageServerState , notifyTemplateGraphAvailable , startArmLanguageServerInBackground , waitForLanguageServerAvailable } from "./languageclient/startArmLanguageServer" ; import { InsertionContext } from './snippets/InsertionContext' ; import { KnownContexts } from './snippets/KnownContexts' ; import { showInsertionContext } from "./snippets/showInsertionContext" ; import { SnippetManager } from "./snippets/SnippetManager" ; import { survey } from "./survey" ; import { TimedMessage } from './TimedMessage' ; import { assertNever } from './util/assertNever' ; import { CachedPromise } from "./util/CachedPromise" ; import { escapeNonPaths } from "./util/escapeNonPaths" ; import { expectTemplateDocument } from "./util/expectDocument" ; import { getRenameError } from "./util/getRenameError" ; import { Histogram } from "./util/Histogram" ; import { prependLinkedTemplateScheme } from './util/linkedTemplateScheme' ; import { pathExistsNoThrow } from "./util/pathExistsNoThrow" ; import { readUtf8FileWithBom } from "./util/readUtf8FileWithBom" ; import { Stopwatch } from "./util/Stopwatch" ; import { Cancellation } from "./util/throwOnCancel" ; import { parseUri } from './util/uri' ; import { IncorrectArgumentsCountIssue } from "./visitors/IncorrectArgumentsCountIssue" ; import { UnrecognizedBuiltinFunctionIssue } from "./visitors/UnrecognizedFunctionIssues" ; import { IAddMissingParametersArgs , IGotoParameterValueArgs , IGotoResourcesArgs } from "./vscodeIntegration/commandArguments" ; import { Item } from './vscodeIntegration/Completion' ; import { ConsoleOutputChannelWrapper } from "./vscodeIntegration/ConsoleOutputChannelWrapper" ; import { IHoverInfo } from './vscodeIntegration/IHoverInfo' ; import { RenameCodeActionProvider } from "./vscodeIntegration/RenameCodeActionProvider" ; import { resetGlobalState } from "./vscodeIntegration/resetGlobalState" ; import { setContext } from "./vscodeIntegration/setContext" ; import { getFunctionParamUsage } from "./vscodeIntegration/signatureFormatting" ; import { onCompletionActivated , toVsCodeCompletionItem } from "./vscodeIntegration/toVsCodeCompletionItem" ; import { toVSCodeDiagnosticFromIssue } from './vscodeIntegration/toVSCodeDiagnosticFromIssue' ; import { JsonOutlineProvider } from "./vscodeIntegration/Treeview" ; import { getVSCodeRangeFromSpan } from "./vscodeIntegration/vscodePosition" ; interface IErrorsAndWarnings { errors : Issue [ ] ; warnings : Issue [ ] ; } const invalidRenameError = "Only parameters, variables, user namespaces and user functions can be renamed." ; const echoOutputChannelToConsole = /^(true|1)$/i . test ( process . env . ECHO_OUTPUT_CHANNEL_TO_CONSOLE ?? '' ) ; export async function activateInternal ( context , perfStats ) { writeToLog ( ">>> activateInternal" , true ) ; try { ext . extensionStartupComplete = false ; ext . context = context ; ext . ui = new AzureUserInput ( context . globalState ) ; let outputChannel = createAzExtOutputChannel ( outputChannelName , configPrefix ) ; if ( echoOutputChannelToConsole ) { outputChannel = new ConsoleOutputChannelWrapper ( outputChannel ) ; } ext . outputChannel = outputChannel ; writeToLog ( ">>> registerUIExtensionVariables" , true ) ; registerUIExtensionVariables ( ext ) ; context . subscriptions . push ( ext . completionItemsSpy ) ; ext . deploymentFileMapping . value = new DeploymentFileMapping ( ext . configuration ) ; if ( ! ext . snippetManager . hasValue ) { ext . snippetManager . value = SnippetManager . createDefault ( ) ; } await callWithTelemetryAndErrorHandling ( 'activate' , async ( actionContext ) : Promise < void > => { actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . telemetry . measurements . mainFileLoad = ( perfStats . loadEndTime - perfStats . loadStartTime ) / 1000 ; recordConfigValuesToTelemetry ( actionContext ) ; context . subscriptions . push ( new AzureRMTools ( context ) ) ; } ) ; } catch ( err ) { const msg = parseError ( err ) . message ; ext . extensionStartupError = msg ; console . error ( msg ) ; throw err ; } if ( ! ext . extensionStartupError ) { ext . extensionStartupComplete = true ; } writeToLog ( ">>> activateInternal end" , true ) ; } function recordConfigValuesToTelemetry ( actionContext ) { const config = ext . configuration ; actionContext . telemetry . properties . autoDetectJsonTemplates = String ( config . get < boolean > ( configKeys . autoDetectJsonTemplates ) ) ; const paramFiles = config . get < unknown [ ] > ( configKeys . parameterFiles ) ; actionContext . telemetry . properties [ `${ configKeys . parameterFiles }.length` ] = String ( paramFiles ? Object . keys ( paramFiles ) . length : 0 ) ; recordConfigValue < boolean > ( configKeys . checkForLatestSchema ) ; recordConfigValue < boolean > ( configKeys . checkForMatchingParameterFiles ) ; recordConfigValue < boolean > ( configKeys . enableCodeLens ) ; recordConfigValue < boolean > ( configKeys . codeLensForParameters ) ; function recordConfigValue < T > ( key ) { actionContext . telemetry . properties [ key ] = String ( config . get < T > ( key ) ) ; } } export function deactivateInternal ( ) { } export class AzureRMTools implements IProvideOpenedDocuments { private readonly _diagnosticsCollection : vscode . DiagnosticCollection ; private readonly _deploymentDocuments : Map < string , DeploymentDocument > = new Map < string , DeploymentDocument > ( ) ; private readonly _cachedTemplateGraphs : Map < string , INotifyTemplateGraphArgs > = new Map < string , INotifyTemplateGraphArgs > ( ) ; private readonly _filesAskedToUpdateSchemaThisSession : Set < string > = new Set < string > ( ) ; private readonly _paramsStatusBarItem : vscode . StatusBarItem ; private _areDeploymentTemplateEventsHookedUp : boolean = false ; private _diagnosticsVersion : number = 0 ; private _mapping : DeploymentFileMapping = ext . deploymentFileMapping . value ; private _codeLensChangedEmitter : vscode . EventEmitter < void > = new vscode . EventEmitter < void > ( ) ; private _linkedTemplateDocProviderChangedEmitter : vscode . EventEmitter < vscode . Uri > = new vscode . EventEmitter < vscode . Uri > ( ) ; private _bicepMessage : TimedMessage = new TimedMessage ( globalStateKeys . messages . bicepMessagePostponedUntilTime , "debugBicepMessage" , "Try Azure Bicep, the next generation of ARM templates, in VS Code" , parseUri ( "https://aka.ms/bicep-install" ) ) ; private readonly _braceHighlightDecorationType : vscode . TextEditorDecorationType = vscode . window . createTextEditorDecorationType ( { borderWidth : "1px" , borderStyle : "solid" , light : { borderColor : "rgba(0, 0, 0, 0.2)" , backgroundColor : "rgba(0, 0, 0, 0.05)" } , dark : { borderColor : "rgba(128, 128, 128, 0.5)" , backgroundColor : "rgba(128, 128, 128, 0.1)" } } ) ; constructor ( context ) { ext . provideOpenedDocuments = this ; const jsonOutline = new JsonOutlineProvider ( context ) ; ext . jsonOutlineProvider = jsonOutline ; context . subscriptions . push ( vscode . window . registerTreeDataProvider ( "azurerm-vscode-tools.template-outline" , jsonOutline ) ) ; context . subscriptions . push ( this . getRegisteredRenameCodeActionProvider ( ) ) ; registerCommand ( "azurerm-vscode-tools.completion-activated" , ( actionContext , args ) => { onCompletionActivated ( actionContext , < { [ key ] : string ; } > args ) ; } ) ; registerCommand ( "azurerm-vscode-tools.treeview.goto" , ( _actionContext , range ) => { jsonOutline . revealRangeInEditor ( range ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortTemplate" , async ( _context , uri ? , editor ? ) => { editor = editor || vscode . window . activeTextEditor ; uri = uri || vscode . window . activeTextEditor ?. document . uri ; if ( typeof uri === "string" ) { uri = vscode . window . activeTextEditor ?. document . uri ; } if ( uri && editor ) { const sectionType = await ext . ui . showQuickPick ( getQuickPickItems ( ) , { placeHolder : 'What do you want to sort?' } ) ; await this . sortTemplate ( sectionType . value , uri , editor ) ; } } ) ; registerCommand ( "azurerm-vscode-tools.sortFunctions" , async ( ) => { await this . sortTemplate ( TemplateSectionType . Functions ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortOutputs" , async ( ) => { await this . sortTemplate ( TemplateSectionType . Outputs ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortParameters" , async ( ) => { await this . sortTemplate ( TemplateSectionType . Parameters ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortResources" , async ( ) => { await this . sortTemplate ( TemplateSectionType . Resources ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortVariables" , async ( ) => { await this . sortTemplate ( TemplateSectionType . Variables ) ; } ) ; registerCommand ( "azurerm-vscode-tools.sortTopLevel" , async ( ) => { await this . sortTemplate ( TemplateSectionType . TopLevel ) ; } ) ; registerCommand ( "azurerm-vscode-tools.selectParameterFile" , async ( actionContext , sourceTemplateUri ? ) => { await selectParameterFile ( actionContext , this . _mapping , sourceTemplateUri ) ; } ) ; registerCommand ( "azurerm-vscode-tools.openParameterFile" , async ( _actionContext , sourceTemplateUri ? ) => { sourceTemplateUri = sourceTemplateUri ?? vscode . window . activeTextEditor ?. document . uri ; await openParameterFile ( this . _mapping , sourceTemplateUri , undefined ) ; } ) ; registerCommand ( "azurerm-vscode-tools.openTemplateFile" , async ( _actionContext , sourceParamUri ? ) => { sourceParamUri = sourceParamUri ?? vscode . window . activeTextEditor ?. document . uri ; await openTemplateFile ( this . _mapping , sourceParamUri , undefined ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeLens.openLinkedTemplateFile" , async ( actionContext , linkedTemplateUri ) => { await openLinkedTemplateFileCommand ( linkedTemplateUri , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeLens.reloadLinkedTemplateFile" , async ( actionContext , linkedTemplateUri ) => { this . closeDeploymentFile ( linkedTemplateUri ) ; this . _linkedTemplateDocProviderChangedEmitter . fire ( prependLinkedTemplateScheme ( linkedTemplateUri ) ) ; } ) ; registerCommand ( "azurerm-vscode-tools.insertItem" , async ( actionContext , uri ? , editor ? ) => { editor = editor || vscode . window . activeTextEditor ; uri = uri || vscode . window . activeTextEditor ?. document . uri ; if ( typeof uri === "string" ) { uri = vscode . window . activeTextEditor ?. document . uri ; } if ( uri && editor ) { const sectionType = await ext . ui . showQuickPick ( getItemTypeQuickPicks ( ) , { placeHolder : 'What do you want to insert?' } ) ; await this . insertItem ( sectionType . value , actionContext , uri , editor ) ; } } ) ; registerCommand ( "azurerm-vscode-tools.insertParameter" , async ( actionContext ) => { await this . insertItem ( TemplateSectionType . Parameters , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.insertVariable" , async ( actionContext ) => { await this . insertItem ( TemplateSectionType . Variables , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.insertOutput" , async ( actionContext ) => { await this . insertItem ( TemplateSectionType . Outputs , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.insertFunction" , async ( actionContext ) => { await this . insertItem ( TemplateSectionType . Functions , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.insertResource" , async ( actionContext ) => { await this . insertItem ( TemplateSectionType . Resources , actionContext ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeAction.addAllMissingParameters" , async ( actionContext , source ? , args ? ) => { await this . addMissingParameters ( actionContext , source , args , false ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeAction.addMissingRequiredParameters" , async ( actionContext , source ? , args ? ) => { await this . addMissingParameters ( actionContext , source , args , true ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeAction.extractParameter" , async ( _context , uri ? , editor ? ) => { editor = editor || vscode . window . activeTextEditor ; uri = uri || vscode . window . activeTextEditor ?. document . uri ; if ( editor ) { let deploymentTemplate = this . getOpenedDeploymentTemplate ( editor . document ) ; if ( ! deploymentTemplate ) { return ; } await new ExtractItem ( ext . ui ) . extractParameter ( editor , deploymentTemplate , _context ) ; } } ) ; registerCommand ( "azurerm-vscode-tools.codeAction.extractVariable" , async ( _context , uri ? , editor ? ) => { editor = editor || vscode . window . activeTextEditor ; uri = uri || vscode . window . activeTextEditor ?. document . uri ; if ( editor ) { let deploymentTemplate = this . getOpenedDeploymentTemplate ( editor . document ) ; if ( ! deploymentTemplate ) { return ; } await new ExtractItem ( ext . ui ) . extractVariable ( editor , deploymentTemplate , _context ) ; } } ) ; registerCommand ( "azurerm-vscode-tools.codeLens.gotoParameterValue" , async ( actionContext , args ) => { await this . onGotoParameterValue ( actionContext , args ) ; } ) ; registerCommand ( "azurerm-vscode-tools.codeLens.gotoResources" , async ( actionContext , args ) => { await gotoResources ( actionContext , args ) ; } ) ; registerCommand ( "azurerm-vscode-tools.developer.resetGlobalState" , resetGlobalState ) ; registerCommand ( "azurerm-vscode-tools.developer.showAvailableResourceTypesAndVersions" , async ( _context , uri ? , editor ? ) => { editor = editor || vscode . window . activeTextEditor ; uri = uri || vscode . window . activeTextEditor ?. document . uri ; if ( editor ) { let deploymentTemplate = this . getOpenedDeploymentTemplate ( editor . document ) ; if ( ! deploymentTemplate ) { return ; } const schema = deploymentTemplate . schemaUri ; if ( schema ) { ext . outputChannel . appendLine ( `Retrieving available resource types and apiVersions for ${ schema }...` ) ; await showAvailableResourceTypesAndVersions ( schema ) ; } } } ) ; registerCommand ( "azurerm-vscode-tools.developer.showInsertionContext" , async ( actionContext ) => { let editor = vscode . window . activeTextEditor ; if ( editor ) { let position = editor . selection . anchor ; let pc = await this . getPositionContext ( editor . document , position , Cancellation . cantCancel ) ; if ( pc ) { showInsertionContext ( pc ) ; } } } ) ; this . _paramsStatusBarItem = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left ) ; ext . context . subscriptions . push ( this . _paramsStatusBarItem ) ; vscode . window . onDidChangeActiveTextEditor ( this . onActiveTextEditorChanged , this , context . subscriptions ) ; vscode . workspace . onDidOpenTextDocument ( this . onDocumentOpened , this , context . subscriptions ) ; vscode . workspace . onDidChangeTextDocument ( this . onDocumentChanged , this , context . subscriptions ) ; vscode . workspace . onDidCloseTextDocument ( this . onDocumentClosed , this , ext . context . subscriptions ) ; vscode . workspace . onDidChangeConfiguration ( async ( ) => { this . _mapping . resetCache ( ) ; this . updateEditorStateInBackground ( ) ; this . _codeLensChangedEmitter . fire ( ) ; } , this , context . subscriptions ) ; this . _diagnosticsCollection = vscode . languages . createDiagnosticCollection ( "azurerm-tools-expressions" ) ; context . subscriptions . push ( this . _diagnosticsCollection ) ; const completionProvider = { provideCompletionItems : async ( document , position , token , ctx ) : Promise < vscode . CompletionList | undefined > => { return await this . onProvideCompletions ( document , position , token , ctx ) ; } , resolveCompletionItem : ( item , token ) : vscode . CompletionItem => { return this . onResolveCompletionItem ( item , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerCompletionItemProvider ( templateOrParameterDocumentSelector , completionProvider , "'" , "[" , "." , '"' , '(' , ',' , ' ' , '{' ) ) ; const activeEditor = vscode . window . activeTextEditor ; if ( activeEditor ) { const activeDocument = activeEditor . document ; this . updateOpenedDocument ( activeDocument ) ; } if ( vscode . extensions . getExtension ( 'ms-azuretools.vscode-bicep' ) ) { this . _bicepMessage . neverShowAgain ( ) . catch ( err => { assert . fail ( "neverShowAgain failed" ) ; } ) ; } } public setStaticDocument ( documentOrUri , content ) { throw new Error ( "Method not implemented." ) ; } private getRegisteredRenameCodeActionProvider ( ) { const metaData = { providedCodeActionKinds : [ vscode . CodeActionKind . RefactorRewrite ] } ; const renameCodeActionProvider = new RenameCodeActionProvider ( async ( document , position ) : Promise < PositionContext | undefined > => await this . getPositionContext ( document , position , Cancellation . cantCancel ) ) ; return vscode . languages . registerCodeActionsProvider ( templateOrParameterDocumentSelector , renameCodeActionProvider , metaData ) ; } private async addMissingParameters ( actionContext , source , args , onlyRequiredParameters ) { source = source || vscode . window . activeTextEditor ?. document . uri ; const editor = vscode . window . activeTextEditor ; const uri = source || editor ?. document . uri ; if ( editor && uri && editor . document . uri . fsPath === uri . fsPath ) { let parameterValues ; let parameterDefinitions ; let parentParameterDefinitions ; if ( ! args ) { let { doc , associatedDoc : template } = await this . getDeploymentDocAndAssociatedDoc ( editor . document , Cancellation . cantCancel ) ; if ( doc instanceof DeploymentParametersDoc ) { parameterValues = doc . topLevelParameterValuesSource ; parameterDefinitions = expectTemplateDocument ( template ) . topLevelScope . parameterDefinitionsSource ; } } else { parameterValues = args . parameterValuesSource ; parameterDefinitions = args . parameterDefinitionsSource ; parentParameterDefinitions = args . parentParameterDefinitionsSource ; } if ( parameterValues && parameterDefinitions ) { await addMissingParameters ( parameterDefinitions , parameterValues , editor , parentParameterDefinitions , onlyRequiredParameters ) ; } } } private async sortTemplate ( sectionType , documentUri ? , editor ? ) { editor = editor || vscode . window . activeTextEditor ; documentUri = documentUri || editor ?. document . uri ; if ( editor && documentUri && editor . document . uri . fsPath === documentUri . fsPath ) { let deploymentTemplate = this . getOpenedDeploymentTemplate ( editor . document ) ; await sortTemplate ( deploymentTemplate , sectionType , editor ) ; } } private async insertItem ( sectionType , context , documentUri ? , editor ? ) { editor = editor || vscode . window . activeTextEditor ; documentUri = documentUri || editor ?. document . uri ; if ( editor && documentUri && editor . document . uri . fsPath === documentUri . fsPath ) { let deploymentTemplate = this . getOpenedDeploymentTemplate ( editor . document ) ; await new InsertItem ( ext . ui ) . insertItem ( deploymentTemplate , sectionType , editor , context ) ; } } public dispose ( ) { callWithTelemetryAndErrorHandlingSync ( 'dispose' , ( actionContext ) : void => { actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . errorHandling . suppressDisplay = true ; } ) ; } public setOpenedDeploymentDocument ( documentUri , deploymentDocument ) { assert ( documentUri ) ; const documentPathKey = getNormalizedDocumentKey ( documentUri ) ; if ( deploymentDocument ) { this . _deploymentDocuments . set ( documentPathKey , deploymentDocument ) ; if ( deploymentDocument instanceof DeploymentTemplateDoc ) { const templateGraph = this . _cachedTemplateGraphs . get ( documentPathKey ) ; if ( templateGraph ) { assignTemplateGraphToDeploymentTemplate ( templateGraph , deploymentDocument , this ) ; this . updateEditorStateInBackground ( ) ; } } } else { this . _deploymentDocuments . delete ( documentPathKey ) ; } this . _codeLensChangedEmitter . fire ( ) ; } private getOpenedDeploymentDocument ( documentOrUri ) { assert ( documentOrUri ) ; const uri = documentOrUri instanceof vscode . Uri ? documentOrUri : documentOrUri . uri ; const documentPathKey = getNormalizedDocumentKey ( uri ) ; return this . _deploymentDocuments . get ( documentPathKey ) ; } public getOpenedDeploymentTemplate ( documentOrUri ) { const file = this . getOpenedDeploymentDocument ( documentOrUri ) ; return file instanceof DeploymentTemplateDoc ? file : undefined ; } private getOpenedDeploymentParameters ( documentOrUri ) { const file = this . getOpenedDeploymentDocument ( documentOrUri ) ; return file instanceof DeploymentParametersDoc ? file : undefined ; } /**
     * Analyzes a text document that has been opened, and handles it appropriately if
     * it's a deployment template or parameter file
     *
     * NOTE: This method is called for *every* file opened in vscode, so we
     * take extra care to avoid slowing down performance, especially if it's not
     * an ARM template or parameter file.
     */ private updateOpenedDocument ( textDocument ) { callWithTelemetryAndErrorHandlingSync ( 'updateDeploymentDocument' , ( actionContext ) : void => { actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . telemetry . properties . fileExt = path . extname ( textDocument . fileName ) ; assert ( textDocument ) ; const editor = vscode . window . activeTextEditor ; const stopwatch = new Stopwatch ( ) ; stopwatch . start ( ) ; let treatAsDeploymentTemplate = false ; let treatAsDeploymentParameters = false ; const documentUri = textDocument . uri ; if ( textDocument . languageId === armTemplateLanguageId ) { treatAsDeploymentTemplate = true ; } const isNewlyOpened = ! this . getOpenedDeploymentDocument ( documentUri ) ; let shouldParseFile = treatAsDeploymentTemplate || mightBeDeploymentTemplate ( textDocument ) ; if ( shouldParseFile ) { let deploymentTemplate = new DeploymentTemplateDoc ( textDocument . getText ( ) , documentUri , textDocument . version ) ; if ( deploymentTemplate . hasArmSchemaUri ( ) ) { treatAsDeploymentTemplate = true ; } actionContext . telemetry . measurements . parseDurationInMilliseconds = stopwatch . duration . totalMilliseconds ; if ( treatAsDeploymentTemplate ) { this . ensureDeploymentDocumentEventsHookedUp ( ) ; this . setOpenedDeploymentDocument ( documentUri , deploymentTemplate ) ; this . registerActiveUse ( ) ; if ( isNewlyOpened ) { if ( textDocument . languageId !== armTemplateLanguageId ) { setLangIdToArm ( textDocument , actionContext ) ; return ; } } let errorsWarnings = this . reportDeploymentTemplateErrorsNoThrow ( textDocument , deploymentTemplate ) ; if ( isNewlyOpened ) { if ( errorsWarnings ) { this . reportAllTemplateOpenedTelemetryNoThrow ( textDocument , deploymentTemplate , stopwatch , errorsWarnings ) ; } if ( editor && editor . document === textDocument ) { this . considerQueryingForNewerSchema ( editor , deploymentTemplate ) ; considerQueryingForParameterFileInBackground ( this . _mapping , textDocument ) ; } } } } if ( ! treatAsDeploymentTemplate ) { let shouldParseParameterFile = treatAsDeploymentTemplate || mightBeDeploymentParameters ( textDocument ) ; if ( shouldParseParameterFile ) { let deploymentParameters = new DeploymentParametersDoc ( textDocument . getText ( ) , textDocument . uri , textDocument . version ) ; if ( deploymentParameters . hasParametersSchema ( ) ) { treatAsDeploymentParameters = true ; } actionContext . telemetry . measurements . parseDurationInMilliseconds = stopwatch . duration . totalMilliseconds ; if ( treatAsDeploymentParameters ) { this . ensureDeploymentDocumentEventsHookedUp ( ) ; this . setOpenedDeploymentDocument ( documentUri , deploymentParameters ) ; this . registerActiveUse ( ) ; this . reportDeploymentParametersErrorsNoThrow ( textDocument , deploymentParameters ) . then ( async ( errorsWarnings ) => { if ( isNewlyOpened && errorsWarnings ) { this . reportParameterFileOpenedTelemetry ( textDocument , deploymentParameters , stopwatch , errorsWarnings ) ; } } ) . catch ( err => { assert . fail ( "reportDeploymentParametersErrorsNoThrow failed" ) ; } ) ; } } } if ( ! treatAsDeploymentTemplate && ! treatAsDeploymentParameters ) { this . closeDeploymentFile ( textDocument ) ; } this . updateEditorStateInBackground ( ) ; } ) ; } private reportAllTemplateOpenedTelemetryNoThrow ( document , deploymentTemplate , stopwatch , errorsWarnings ) { try { let backgroundTask = async ( ) : Promise < void > => { await this . reportTemplateOpenedTelemetry ( deploymentTemplate , errorsWarnings , document , stopwatch ) ; await this . logFunctionCounts ( deploymentTemplate ) ; await this . logResourceUsage ( deploymentTemplate ) ; } ; backgroundTask ( ) . catch ( err => { assert . fail ( "background task in reportAllTemplateOpenedTelemetryNoThrow failed" ) ; } ) ; } catch ( err ) { assert . fail ( "reportAllTemplateOpenedTelemetryNoThrow shouldn't throw" ) ; } } private async reportTemplateOpenedTelemetry ( deploymentTemplate , errorsWarnings , document , stopwatch ) { await callWithTelemetryAndErrorHandling ( "Deployment Template Opened" , async ( actionContext ) => { actionContext . errorHandling . suppressDisplay = true ; const functionsInEachNamespace = deploymentTemplate . topLevelScope . namespaceDefinitions . map ( ns => ns . members . length ) ; const totalUserFunctionsCount = functionsInEachNamespace . reduce ( ( sum , count ) => sum + count , 0 ) ; const issuesHistograph = new Histogram ( ) ; for ( const error of errorsWarnings . errors ) { issuesHistograph . add ( `extErr:${ error . kind }` ) ; } for ( const warning of errorsWarnings . warnings ) { issuesHistograph . add ( `extWarn:${ warning . kind }` ) ; } const props = actionContext . telemetry . properties ; props . docLangId = document . languageId ; props . docExtension = path . extname ( document . fileName ) ; const schemaUri = deploymentTemplate . schemaUri ; const schemaVersionAndScope = ( schemaUri ?. match ( /([-0-9a-zA-Z]+\/[a-zA-Z]+\.json)#?$/ ) ?? [ ] ) [ 1 ] ; props . schema = escapeNonPaths ( restrictToAllowedListLC < allSchemas > ( schemaVersionAndScope , [ '2019-08-01/tenantDeploymentTemplate.json' , '2018-05-01/subscriptionDeploymentTemplate.json' , '2014-04-01-preview/deploymentTemplate.json' , '2015-01-01/deploymentTemplate.json' , '2019-04-01/deploymentTemplate.json' , '2019-08-01/managementGroupDeploymentTemplate.json' , '2019-08-01/tenantDeploymentTemplate.json' ] ) ) ; props . schemaScheme = restrictToAllowedListLC ( ( schemaUri ?. match ( /^https?/ ) ?? [ ] ) [ 0 ] , [ 'http' , 'https' ] ) ; props . apiProfile = deploymentTemplate . apiProfile ?? "" ; props . issues = this . histogramToTelemetryString ( issuesHistograph ) ; const measurements = actionContext . telemetry . measurements ; measurements . documentSizeInCharacters = document . getText ( ) . length ; measurements . parseDurationInMilliseconds = stopwatch . duration . totalMilliseconds ; measurements . lineCount = deploymentTemplate . lineCount ; measurements . maxLineLength = deploymentTemplate . getMaxLineLength ( ) ; measurements . paramsCount = deploymentTemplate . topLevelScope . parameterDefinitionsSource ?. parameterDefinitions . length ; measurements . paramsWithDefaultCount = deploymentTemplate . topLevelScope . parameterDefinitionsSource ?. parameterDefinitions . filter ( pd => pd . defaultValue ) . length ; measurements . varsCount = deploymentTemplate . topLevelScope . variableDefinitions . length ; measurements . namespacesCount = deploymentTemplate . topLevelScope . namespaceDefinitions . length ; measurements . userFunctionsCount = totalUserFunctionsCount ; measurements . multilineStringCount = deploymentTemplate . getMultilineStringCount ( ) ; measurements . commentCount = deploymentTemplate . getCommentCount ( ) ; measurements . extErrorsCount = errorsWarnings . errors . length ; measurements . extWarnCount = errorsWarnings . warnings . length ; measurements . parameterFiles = this . _mapping . getParameterFile ( document . uri ) ? 1 : 0 ; const getChildTemplatesInfo = deploymentTemplate . getChildTemplatesInfo ( ) ; measurements . linkedTemplatesCount = getChildTemplatesInfo . linkedTemplatesCount ; measurements . linkedTemplatesUriCount = getChildTemplatesInfo . linkedTemplatesUriCount ; measurements . linkedTemplatesRelativePathCount = getChildTemplatesInfo . linkedTemplatesRelativePathCount ; measurements . nestedInnerCount = getChildTemplatesInfo . nestedInnerCount ; measurements . nestedOuterCount = getChildTemplatesInfo . nestedOuterCount ; } ) ; function restrictToAllowedListLC < T extends string > ( s , allowedList ) { allowedList = allowedList . map ( s2 => < T > s2 . toLowerCase ( ) ) ; if ( s && allowedList . includes ( < T > s . toLowerCase ( ) ) ) { return s ; } return ! ! s ? "other" : '' ; } } private reportParameterFileOpenedTelemetry ( document , parameters , stopwatch , errorsWarnings ) { const issuesHistograph = new Histogram ( ) ; for ( const error of errorsWarnings . errors ) { issuesHistograph . add ( `extErr:${ error . kind }` ) ; } for ( const warning of errorsWarnings . warnings ) { issuesHistograph . add ( `extWarn:${ warning . kind }` ) ; } callWithTelemetryAndErrorHandlingSync ( 'Parameter File Opened' , ( actionContext ) => { actionContext . errorHandling . suppressDisplay = true ; const props = actionContext . telemetry . properties ; props . docLangId = document . languageId ; props . docExtension = path . extname ( document . fileName ) ; props . schema = parameters . schemaUri ?? "" ; const measurements = actionContext . telemetry . measurements ; measurements . documentSizeInCharacters = document . getText ( ) . length ; measurements . parseDurationInMilliseconds = stopwatch . duration . totalMilliseconds ; measurements . lineCount = parameters . lineCount ; measurements . maxLineLength = parameters . getMaxLineLength ( ) ; measurements . paramsCount = parameters . parameterValueDefinitions . length ; measurements . commentCount = parameters . getCommentCount ( ) ; measurements . templateFiles = this . _mapping . getTemplateFile ( document . uri ) ? 1 : 0 ; measurements . extErrorsCount = errorsWarnings . errors . length ; measurements . extWarnCount = errorsWarnings . warnings . length ; } ) ; } private reportDeploymentDocumentErrors ( textDocument , deploymentDocument , associatedDocument ) { ++ this . _diagnosticsVersion ; let errors = deploymentDocument . getErrors ( associatedDocument ) ; const diagnostics = [ ] ; for ( const error of errors ) { assert ( error . severity === IssueSeverity . Error , "Errors should have severity of Error" ) ; diagnostics . push ( toVSCodeDiagnosticFromIssue ( deploymentDocument , error , vscode . DiagnosticSeverity . Error ) ) ; } const warnings = deploymentDocument . getWarnings ( ) ; for ( const warning of warnings ) { assert ( warning . severity === IssueSeverity . Warning || warning . severity === IssueSeverity . Information , "Warnings should have severity of Warning or Information" ) ; const severity = warning . severity === IssueSeverity . Information ? vscode . DiagnosticSeverity . Information : vscode . DiagnosticSeverity . Warning ; diagnostics . push ( toVSCodeDiagnosticFromIssue ( deploymentDocument , warning , severity ) ) ; } let completionDiagnostic = this . getCompletedDiagnostic ( ) ; if ( completionDiagnostic ) { diagnostics . push ( completionDiagnostic ) ; } this . _diagnosticsCollection . set ( textDocument . uri , diagnostics ) ; return { errors , warnings } ; } private reportDeploymentTemplateErrorsNoThrow ( textDocument , deploymentTemplate ) { try { return callWithTelemetryAndErrorHandlingSync ( 'reportDeploymentTemplateErrors' , ( actionContext ) : IErrorsAndWarnings => { actionContext . telemetry . suppressIfSuccessful = true ; const associatedParameters = undefined ; return this . reportDeploymentDocumentErrors ( textDocument , deploymentTemplate , associatedParameters ) ; } ) ; } catch ( err ) { assert . fail ( "callWithTelemetryAndErrorHandling in reportDeploymentTemplateErrorsNoThrow shouldn't throw" ) ; } } private async reportDeploymentParametersErrorsNoThrow ( textDocument , deploymentParameters ) { try { return await callWithTelemetryAndErrorHandling ( 'reportDeploymentParametersErrors' , async ( actionContext ) : Promise < IErrorsAndWarnings > => { actionContext . telemetry . suppressIfSuccessful = true ; const templateUri = this . _mapping . getTemplateFile ( deploymentParameters . documentUri ) ; let template ; if ( templateUri ) { const templateFileExists = await pathExistsNoThrow ( templateUri ) ; actionContext . telemetry . properties . templateFileExists = String ( templateFileExists ) ; if ( templateFileExists ) { template = await this . getOrReadDeploymentTemplate ( templateUri ) ; } } return this . reportDeploymentDocumentErrors ( textDocument , deploymentParameters , template ) ; } ) ; } catch ( err ) { assert . fail ( "callWithTelemetryAndErrorHandling in reportDeploymentParametersErrorsNoThrow shouldn't throw" ) ; } } private considerQueryingForNewerSchema ( editor , deploymentTemplate ) { if ( editor . document . uri . scheme !== documentSchemes . file ) { return ; } const document = editor . document ; const documentPath = document . uri . fsPath ; let queriedToUpdateSchema = this . _filesAskedToUpdateSchemaThisSession . has ( documentPath ) ; if ( queriedToUpdateSchema ) { return ; } this . _filesAskedToUpdateSchemaThisSession . add ( documentPath ) ; const schemaValue = deploymentTemplate . schemaValue ; const schemaUri = deploymentTemplate . schemaUri || undefined ; const preferredSchemaUri = schemaUri && getPreferredSchema ( schemaUri ) ; const checkForLatestSchema = ! ! ext . configuration . get < boolean > ( configKeys . checkForLatestSchema ) ; if ( preferredSchemaUri && schemaValue ) { callWithTelemetryAndErrorHandling ( 'queryUpdateSchema' , async ( actionContext ) : Promise < void > => { actionContext . telemetry . properties . currentSchema = schemaUri ; actionContext . telemetry . properties . preferredSchema = preferredSchemaUri ; actionContext . telemetry . properties . checkForLatestSchema = String ( checkForLatestSchema ) ; if ( ! checkForLatestSchema ) { return ; } const dontAskFiles = ext . context . globalState . get < string [ ] > ( globalStateKeys . dontAskAboutSchemaFiles ) || [ ] ; if ( dontAskFiles . includes ( documentPath ) ) { actionContext . telemetry . properties . isInDontAskList = 'true' ; return ; } const yes = { title : "Use latest" } ; const notNow = { title : "Not now" } ; const neverForThisFile = { title : "Never for this file" } ; const response = await ext . ui . showWarningMessage ( `Warning: You are using a deprecated schema version that is no longer maintained.  Would you like us to update "${ path . basename ( document . uri . path ) }" to use the newest schema?` , { learnMoreLink : "https://aka.ms/vscode-azurearmtools-updateschema" } , yes , notNow , neverForThisFile ) ; actionContext . telemetry . properties . response = response . title ; switch ( response . title ) { case yes . title : await this . replaceSchema ( document . uri , deploymentTemplate , schemaValue . unquotedValue , preferredSchemaUri ) ; actionContext . telemetry . properties . replacedSchema = "true" ; return ; case notNow . title : return ; case neverForThisFile . title : dontAskFiles . push ( documentPath ) ; await ext . context . globalState . update ( globalStateKeys . dontAskAboutSchemaFiles , dontAskFiles ) ; break ; default : assert ( "queryUseNewerSchema: Unexpected response" ) ; break ; } } ) . catch ( err => { assert . fail ( "callWithTelemetryAndErrorHandling in queryUpdateSchema shouldn't throw" ) ; } ) ; } } private async replaceSchema ( uri , deploymentTemplate , previousSchema , newSchema ) { const editor = await vscode . window . showTextDocument ( uri ) ; const currentTemplate = new DeploymentTemplateDoc ( editor . document . getText ( ) , editor . document . uri , editor . document . version ) ; const currentSchemaValue = currentTemplate . schemaValue ; if ( currentSchemaValue && currentSchemaValue . unquotedValue === previousSchema ) { const range = getVSCodeRangeFromSpan ( currentTemplate , currentSchemaValue . unquotedSpan ) ; await editor . edit ( edit => { edit . replace ( range , newSchema ) ; } ) ; editor . selection = new vscode . Selection ( range . start , range . end ) ; editor . revealRange ( range , vscode . TextEditorRevealType . Default ) ; } else { throw new Error ( "The document has changed, the $schema was not replaced." ) ; } } private getCompletedDiagnostic ( ) { if ( ext . addCompletedDiagnostic ) { return { severity : vscode . DiagnosticSeverity . Information , message : `${ expressionsDiagnosticsCompletionMessage }, version ${ this . _diagnosticsVersion }` , source : expressionsDiagnosticsSource , code : "" , range : new vscode . Range ( 0 , 0 , 0 , 0 ) } ; } else { return undefined ; } } /**
     * Hook up events related to template files (as opposed to plain JSON files). This is only called when
     * actual template files are open, to avoid slowing performance when simple JSON files are opened.
     */ private ensureDeploymentDocumentEventsHookedUp ( ) { if ( this . _areDeploymentTemplateEventsHookedUp ) { return ; } this . _areDeploymentTemplateEventsHookedUp = true ; callWithTelemetryAndErrorHandlingSync ( "ensureDeploymentTemplateEventsHookedUp" , ( actionContext ) => { actionContext . telemetry . suppressIfSuccessful = true ; vscode . window . onDidChangeTextEditorSelection ( this . onTextSelectionChanged , this , ext . context . subscriptions ) ; const hoverProvider = { provideHover : async ( document , position , token ) : Promise < vscode . Hover | undefined > => { return await this . onProvideHover ( document , position , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerHoverProvider ( templateDocumentSelector , hoverProvider ) ) ; const codeLensProvider = { onDidChangeCodeLenses : this . _codeLensChangedEmitter . event , provideCodeLenses : ( document , token ) : vscode . CodeLens [ ] | undefined => { return this . onProvideCodeLenses ( document , token ) ; } , resolveCodeLens : async ( codeLens , token ) : Promise < vscode . CodeLens | undefined > => { return await this . onResolveCodeLens ( codeLens , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerCodeLensProvider ( templateDocumentSelector , codeLensProvider ) ) ; const codeActionProvider = { provideCodeActions : async ( textDocument , range , context , token ) : Promise < ( vscode . Command | vscode . CodeAction ) [ ] | undefined > => { return await this . onProvideCodeActions ( textDocument , range , context , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerCodeActionsProvider ( templateOrParameterDocumentSelector , codeActionProvider , { providedCodeActionKinds : [ vscode . CodeActionKind . QuickFix , vscode . CodeActionKind . RefactorExtract ] } ) ) ; const definitionProvider = { provideDefinition : async ( document , position , token ) : Promise < vscode . Definition | undefined > => { return await this . onProvideDefinition ( document , position , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerDefinitionProvider ( templateOrParameterDocumentSelector , definitionProvider ) ) ; const referenceProvider = { provideReferences : async ( document , position , context , token ) : Promise < vscode . Location [ ] | undefined > => { return this . onProvideReferences ( document , position , context , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerReferenceProvider ( templateOrParameterDocumentSelector , referenceProvider ) ) ; const signatureHelpProvider = { provideSignatureHelp : async ( document , position , token ) : Promise < vscode . SignatureHelp | undefined > => { return await this . onProvideSignatureHelp ( document , position , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerSignatureHelpProvider ( templateDocumentSelector , signatureHelpProvider , "," , "(" , "\n" ) ) ; const renameProvider = { provideRenameEdits : async ( document , position , newName , token ) : Promise < vscode . WorkspaceEdit | undefined > => { return await this . onProvideRename ( document , position , newName , token ) ; } , prepareRename : async ( document , position , token ) : Promise < vscode . Range | { range : vscode . Range ; placeholder : string ; } | undefined > => { return await this . prepareRename ( document , position , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerRenameProvider ( templateOrParameterDocumentSelector , renameProvider ) ) ; const documentLinkProvider = { provideDocumentLinks : async ( document , token ) : Promise < vscode . DocumentLink [ ] | undefined > => { return await this . provideDocumentLinks ( document , token ) ; } } ; ext . context . subscriptions . push ( vscode . languages . registerDocumentLinkProvider ( templateDocumentSelector , documentLinkProvider ) ) ; const linkedTemplateDocumentProvider = { onDidChange : this . _linkedTemplateDocProviderChangedEmitter . event , provideTextDocumentContent : async ( uri , _token ) : Promise < string | undefined > => { return await this . provideContentForNonlocalUri ( uri ) ; } } ; ext . context . subscriptions . push ( vscode . workspace . registerTextDocumentContentProvider ( documentSchemes . linkedTemplate , linkedTemplateDocumentProvider ) ) ; ext . context . subscriptions . push ( notifyTemplateGraphAvailable ( this . onTemplateGraphAvailable , this ) ) ; ext . context . subscriptions . push ( ext . languageServerStateChanged ( this . onLanguageServerStateChanged , this ) ) ; startArmLanguageServerInBackground ( ) ; } ) ; } param uri private async provideContentForNonlocalUri ( uri ) { return callWithTelemetryAndErrorHandling ( 'provideContentForNonlocalUris' , async ( context ) => { context . errorHandling . rethrow = true ; context . errorHandling . suppressDisplay = true ; let dt = this . getOpenedDeploymentDocument ( uri ) ; if ( ! dt ) { await tryLoadNonLocalLinkedFile ( uri , context , false ) ; dt = this . getOpenedDeploymentDocument ( uri ) ; } return dt ?. documentText ; } ) ; } private updateEditorStateInBackground ( ) { callWithTelemetryAndErrorHandling ( "updateEditorState" , async ( actionContext ) => { actionContext . telemetry . suppressIfSuccessful = true ; let isWarning = false ; let statusBarText ; let isTemplateFile = false ; let templateFileHasParamFile = false ; let isParamFile = false ; let paramFileHasTemplateFile = false ; try { const activeDocument = vscode . window . activeTextEditor ?. document ; if ( activeDocument ) { const state = ext . languageServerState ; switch ( state ) { case LanguageServerState . Failed : case LanguageServerState . Stopped : statusBarText = state === LanguageServerState . Failed ? "$(error) ARM language server failed to start." : "$(error) ARM language server stopped." ; this . _paramsStatusBarItem . text = statusBarText ; this . _paramsStatusBarItem . command = undefined ; this . _paramsStatusBarItem . color = undefined ; this . _paramsStatusBarItem . show ( ) ; return ; case LanguageServerState . LoadingSchemas : case LanguageServerState . NotStarted : case LanguageServerState . Running : case LanguageServerState . Starting : break ; default : assertNever ( state ) ; } const deploymentTemplate = this . getOpenedDeploymentDocument ( activeDocument ) ; if ( deploymentTemplate instanceof DeploymentTemplateDoc ) { isTemplateFile = true ; const paramFileUri = this . _mapping . getParameterFile ( activeDocument . uri ) ; if ( paramFileUri ) { templateFileHasParamFile = true ; const doesParamFileExist = await pathExistsNoThrow ( paramFileUri ) ; statusBarText = `Parameter file: ${ getFriendlyPathToFile ( paramFileUri ) }` ; if ( ! doesParamFileExist ) { statusBarText += " $(error) Not found" ; } } else { statusBarText = "Select/Create parameter file..." ; } const fullValidationOn = deploymentTemplate . templateGraph ?. fullValidationStatus . fullValidationEnabled ?? templateFileHasParamFile ; isWarning = ! fullValidationOn ; statusBarText = isWarning ? `$(warning) WARNING: Full template validation off. Add param file or top-level param defaults to enable.` : statusBarText ; this . _paramsStatusBarItem . command = "azurerm-vscode-tools.selectParameterFile" ; this . _paramsStatusBarItem . text = statusBarText ; } else if ( deploymentTemplate instanceof DeploymentParametersDoc ) { isParamFile = true ; const templateFileUri = this . _mapping . getTemplateFile ( activeDocument . uri ) ; if ( templateFileUri ) { paramFileHasTemplateFile = true ; const doesTemplateFileExist = await pathExistsNoThrow ( templateFileUri ) ; statusBarText = `Template file: ${ getFriendlyPathToFile ( templateFileUri ) }` ; if ( ! doesTemplateFileExist ) { statusBarText += " $(error) Not found" ; } } else { statusBarText = "No template file selected" ; } this . _paramsStatusBarItem . command = "azurerm-vscode-tools.openTemplateFile" ; this . _paramsStatusBarItem . text = statusBarText ; } this . _paramsStatusBarItem . color = isWarning ? new vscode . ThemeColor ( 'problemsWarningIcon.foreground' ) : undefined ; } } finally { if ( statusBarText ) { this . _paramsStatusBarItem . show ( ) ; } else { this . _paramsStatusBarItem . hide ( ) ; } setContext ( { isTemplateFile , hasParamFile : templateFileHasParamFile , isParamFile : isParamFile , hasTemplateFile : paramFileHasTemplateFile } ) ; } } ) . catch ( err => { assert . fail ( "updateEditorStateInBackground shouldn't throw" ) ; } ) ; } /**
     * Logs telemetry with information about the functions used in a template. Only meaningful if called
     * in a relatively stable state, such as after first opening
     */ private async logFunctionCounts ( deploymentTemplate ) { await callWithTelemetryAndErrorHandling ( "tle.stats" , async ( actionContext ) : Promise < void > => { actionContext . errorHandling . suppressDisplay = true ; let properties = actionContext . telemetry . properties ; let issues = deploymentTemplate . getErrors ( undefined ) ; const functionCounts = deploymentTemplate . getFunctionCounts ( ) ; const functionsData = { } ; for ( const functionName of functionCounts . keys ) { functionsData [ < string > functionName ] = functionCounts . getCount ( functionName ) ; } properties . functionCounts = JSON . stringify ( functionsData ) ; let unrecognized = new Set < string > ( ) ; let incorrectArgCounts = new Set < string > ( ) ; for ( const issue of issues ) { if ( issue instanceof UnrecognizedBuiltinFunctionIssue ) { unrecognized . add ( issue . functionName ) ; } else if ( issue instanceof IncorrectArgumentsCountIssue ) { let encodedName = `${ issue . functionName }(${ issue . actual })[${ issue . minExpected }..${ issue . maxExpected }]` ; incorrectArgCounts . add ( encodedName ) ; } } properties . unrecognized = AzureRMTools . convertSetToJson ( unrecognized ) ; properties . incorrectArgs = AzureRMTools . convertSetToJson ( incorrectArgCounts ) ; } ) ; } /**
     * Log information about which resource types and apiVersions are being used
     */ private async logResourceUsage ( deploymentTemplate ) { await callWithTelemetryAndErrorHandling ( "schema.stats" , async ( actionContext ) : Promise < void > => { actionContext . errorHandling . suppressDisplay = true ; let properties = actionContext . telemetry . properties ; await waitForLanguageServerAvailable ( ) ; const availableResourceTypesAndVersions = await getAvailableResourceTypesAndVersionsNoThrow ( deploymentTemplate . schemaUri ?? '' ) ; const [ resourceCounts , invalidResourceCounts , invalidVersionCounts ] = deploymentTemplate . getResourceUsage ( availableResourceTypesAndVersions ) ; properties . resourceCounts = escapeNonPaths ( this . histogramToTelemetryString ( resourceCounts ) ) ; properties . invalidResources = escapeNonPaths ( this . histogramToTelemetryString ( invalidResourceCounts ) ) ; properties . invalidVersions = escapeNonPaths ( this . histogramToTelemetryString ( invalidVersionCounts ) ) ; } ) ; } private histogramToTelemetryString ( histogram ) { const data = { } ; for ( const key of histogram . keys ) { data [ < string > key ] = histogram . getCount ( key ) ; } return JSON . stringify ( data ) ; } private static convertSetToJson ( s ) { if ( ! s . size ) { return "" ; } let array = [ ] ; for ( let item of s ) { array . push ( item ) ; } return JSON . stringify ( array ) ; } private closeDeploymentFile ( documentOrUri ) { assert ( documentOrUri ) ; const uri = documentOrUri instanceof vscode . Uri ? documentOrUri : documentOrUri . uri ; this . _diagnosticsCollection . delete ( uri ) ; this . setOpenedDeploymentDocument ( uri , undefined ) ; } private onProvideCodeLenses ( textDocument , token ) { if ( ! ext . configuration . get < boolean > ( configKeys . enableCodeLens ) ) { return undefined ; } return callWithTelemetryAndErrorHandlingSync ( 'ProvideCodeLenses' , ( actionContext ) : vscode . CodeLens [ ] | undefined => { actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; const doc = this . getOpenedDeploymentDocument ( textDocument . uri ) ; if ( doc instanceof DeploymentTemplateDoc ) { let topLevelParametersProvider = this . getTopLevelParameterValuesSourceProvider ( doc ) ; return doc . getCodeLenses ( topLevelParametersProvider ) ; } } ) ; } /**
     * Retrieves parameter values for the parameters defined at the top level of the template (i.e. those
     * in the parameter value, if any)
     */ private getTopLevelParameterValuesSourceProvider ( doc ) { /**
         * A parameter value source provider using a parameter file. This is here just for convenient access to the
         * getOrReadParametersFiles function.
         */ class ParameterValuesSourceProviderFromParameterFile implements IParameterValuesSourceProvider { private _parameterValuesSource : CachedPromise < IParameterValuesSource > = new CachedPromise < IParameterValuesSource > ( ) ; public constructor ( private readonly parent , public readonly parameterFileUri ) { } public async getValuesSource ( ) { return this . _parameterValuesSource . getOrCachePromise ( async ( ) => { const dp = await this . parent . getOrReadParametersFile ( this . parameterFileUri ) ; return dp ?. topLevelParameterValuesSource ; } ) ; } } const dpUri = this . _mapping . getParameterFile ( doc . documentUri ) ; let topLevelParametersProvider ; if ( dpUri ) { topLevelParametersProvider = new ParameterValuesSourceProviderFromParameterFile ( this , dpUri ) ; } return topLevelParametersProvider ; } private async onResolveCodeLens ( codeLens , token ) { return await callWithTelemetryAndErrorHandling ( 'ResolveCodeLens' , async ( actionContext ) : Promise < vscode . CodeLens | undefined > => { actionContext . telemetry . suppressIfSuccessful = true ; if ( codeLens instanceof ResolvableCodeLens ) { if ( await codeLens . resolve ( ) ) { assert ( codeLens . command ?. title , "CodeLens wasn't resolved" ) ; return codeLens ; } } else { assert . fail ( 'Expected ResolvableCodeLens instance' ) ; } codeLens . command = { title : '' , command : '' } ; return codeLens ; } ) ; } private async onProvideHover ( document , position , token ) { return await callWithTelemetryAndErrorHandling ( 'Hover' , async ( actionContext ) : Promise < vscode . Hover | undefined > => { actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; const properties = < TelemetryProperties & { hoverType ? : string ; tleFunctionName : string ; } > actionContext . telemetry . properties ; const cancel = new Cancellation ( token , actionContext ) ; const { doc , associatedDoc } = await this . getDeploymentDocAndAssociatedDoc ( document , cancel ) ; if ( doc ) { const context = doc . getContextFromDocumentLineAndColumnIndexes ( position . line , position . character , associatedDoc ) ; const hoverInfos = context . getHoverInfo ( ) ; if ( hoverInfos . length > 0 ) { properties . hoverTypes = hoverInfos . map ( i => i . hoverType ) . join ( ',' ) ; const markdownStrings = hoverInfos . map ( hi => hi . getHoverText ( ) ) ; return new vscode . Hover ( markdownStrings ) ; } } return undefined ; } ) ; } private async onProvideCompletions ( document , position , token , context ) { return await callWithTelemetryAndErrorHandling ( 'provideCompletionItems' , async ( actionContext ) : Promise < vscode . CompletionList | undefined > => { actionContext . telemetry . suppressIfSuccessful = true ; actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . properties . langId = document . languageId ; const cancel = new Cancellation ( token , actionContext ) ; const pc = await this . getPositionContext ( document , position , cancel ) ; let jsonDocument ; let items ; if ( pc ) { jsonDocument = pc . document ; const triggerCharacter = context . triggerKind === vscode . CompletionTriggerKind . TriggerCharacter ? context . triggerCharacter : undefined ; let triggerSuggest ; ( { items , triggerSuggest } = await pc . getCompletionItems ( triggerCharacter , defaultTabSize ) ) ; if ( triggerSuggest ) { delay ( 1 ) . then ( async ( ) => { await vscode . commands . executeCommand ( 'type' , { text : '\n' } ) ; vscode . commands . executeCommand ( 'editor.action.triggerSuggest' ) ; } ) . catch ( err => { assert . fail ( "editor.action.triggerSuggest shouldn't throw" ) ; } ) ; return undefined ; } } else { const result = await this . getJsonScaffoldingSnippets ( actionContext , document , position ) ; jsonDocument = result . jsonDocument ; items = result . items ; } if ( jsonDocument && items ) { const vsCodeItems = items . map ( c => toVsCodeCompletionItem ( jsonDocument ! , c , position ) ) ; ext . completionItemsSpy . postCompletionItemsResult ( jsonDocument , items , vsCodeItems ) ; return new vscode . CompletionList ( vsCodeItems , true ) ; } } ) ; } /**
     * Retrieve snippets for a JSON file that is not a deployment or parameters file
     */ private async getJsonScaffoldingSnippets ( actionContext , document , position ) { const fullText = document . getText ( ) ; let text = fullText ; if ( text . length > 100 ) { return { } ; } text = text . trim ( ) ; if ( ! ( text === "" || text === "a" || text === "ar" || text . startsWith ( "arm" ) ) ) { return { } ; } actionContext . telemetry . properties . isEmptyDoc = 'true' ; const insertionContext = { context : KnownContexts . emptyDocument , parents : [ ] } ; const jsonDocument = new UnsupportedJsonDocument ( document . getText ( ) , document . uri ) ; const index = fullText . length - fullText . trimLeft ( ) . length ; const span = new Span ( index , fullText . length - index ) ; const items = await ext . snippetManager . value . getSnippetsAsCompletionItems ( insertionContext , span ) ; return { jsonDocument , items } ; } private onResolveCompletionItem ( item , _token ) { ext . completionItemsSpy . postCompletionItemResolution ( item ) ; return item ; } private async onGotoParameterValue ( actionContext , args ) { const uri = args . inParameterFile ?. parameterFileUri ?? args . inTemplateFile ?. documentUri ; assert ( uri ) ; let textDocument = await vscode . workspace . openTextDocument ( uri ) ; const editor = await vscode . window . showTextDocument ( textDocument ) ; const doc = this . getOpenedDeploymentDocument ( uri ) ; let range ; if ( args . inParameterFile && doc instanceof DeploymentParametersDoc ) { const parameterValues = doc . topLevelParameterValuesSource ; const parameterName = args . inParameterFile . parameterName ; const span = ( parameterName ? parameterValues . getParameterValue ( parameterName ) ?. value ?. span : undefined ) ?? parameterValues . parameterValuesProperty ?. nameValue . span ?? new Span ( 0 , 0 ) ; range = getVSCodeRangeFromSpan ( doc , span ) ; } else if ( args . inTemplateFile && doc instanceof DeploymentTemplateDoc ) { range = args . inTemplateFile . range ; } if ( range ) { editor . selection = new vscode . Selection ( range . start , range . end ) ; editor . revealRange ( range ) ; } } /**
     * Given a document, get a DeploymentTemplate or DeploymentParameters instance from it, and then
     * find the appropriate associated document for it
     */ private async getDeploymentDocAndAssociatedDoc ( documentOrUri , cancel ) { cancel . throwIfCancelled ( ) ; const docUri = documentOrUri instanceof vscode . Uri ? documentOrUri : documentOrUri . uri ; const doc = this . getOpenedDeploymentDocument ( docUri ) ; if ( ! doc ) { return { } ; } if ( doc instanceof DeploymentTemplateDoc ) { const template = doc ; let params ; const paramsUri = this . _mapping . getParameterFile ( docUri ) ; if ( paramsUri ) { params = await this . getOrReadParametersFile ( paramsUri ) ; cancel . throwIfCancelled ( ) ; } assert ( doc instanceof DeploymentTemplateDoc ) ; assert ( ! params || params instanceof DeploymentParametersDoc ) ; return { doc : template , associatedDoc : params } ; } else if ( doc instanceof DeploymentParametersDoc ) { const params = doc ; let template ; const templateUri = this . _mapping . getTemplateFile ( docUri ) ; if ( templateUri ) { template = await this . getOrReadDeploymentTemplate ( templateUri ) ; cancel . throwIfCancelled ( ) ; } return { doc : params , associatedDoc : template } ; } else { assert . fail ( "Unexpected doc type" ) ; } } /**
     * Given a document, get a DeploymentTemplate or DeploymentParameters instance from it, and then
     * create the appropriate context for it from the given position
     */ private async getPositionContext ( textDocument , position , cancel ) { cancel . throwIfCancelled ( ) ; const { doc , associatedDoc } = await this . getDeploymentDocAndAssociatedDoc ( textDocument , cancel ) ; if ( ! doc ) { return undefined ; } cancel . throwIfCancelled ( ) ; return doc . getContextFromDocumentLineAndColumnIndexes ( position . line , position . character , associatedDoc ) ; } /**
     * Given a deployment template URI, return the corresponding opened DeploymentTemplate for it.
     * If none, create a new one by reading the location from disk
     */ private async getOrReadDeploymentTemplate ( uri ) { const doc = this . getOpenedDeploymentTemplate ( uri ) ; if ( doc ) { assert ( doc instanceof DeploymentTemplateDoc , "Expected DeploymentTemplateDoc" ) ; return doc ; } const contents = await readUtf8FileWithBom ( uri . fsPath ) ; return new DeploymentTemplateDoc ( contents , uri , 0 ) ; } /**
     * Given a parameter file URI, return the corresponding opened DeploymentParameters for it.
     * If none, create a new one by reading the location from disk
     */ private async getOrReadParametersFile ( uri ) { const doc = this . getOpenedDeploymentParameters ( uri ) ; if ( doc ) { assert ( doc instanceof DeploymentParametersDoc , "Expected DeploymentParametersDoc" ) ; return doc ; } const contents = await readUtf8FileWithBom ( uri . fsPath ) ; return new DeploymentParametersDoc ( contents , uri , 0 ) ; } private getDocTypeForTelemetry ( doc ) { if ( doc instanceof DeploymentTemplateDoc ) { return "template" ; } else if ( doc instanceof DeploymentParametersDoc ) { return "parameters" ; } else { assert . fail ( "Unexpected doc type" ) ; } } private async onProvideDefinition ( document , position , token ) { return await callWithTelemetryAndErrorHandling ( 'Go To Definition' , async ( actionContext ) : Promise < vscode . Location | undefined > => { const cancel = new Cancellation ( token , actionContext ) ; const pc = await this . getPositionContext ( document , position , cancel ) ; if ( pc ) { let properties = < TelemetryProperties & { definitionType ? : string ; docType ? : string ; } > actionContext . telemetry . properties ; actionContext . errorHandling . suppressDisplay = true ; properties . docType = this . getDocTypeForTelemetry ( pc . document ) ; const refInfo = pc . getReferenceSiteInfo ( true ) ; if ( refInfo && refInfo . definition . nameValue ) { properties . definitionType = refInfo . definition . definitionKind ; return new vscode . Location ( refInfo . definitionDocument . documentUri , getVSCodeRangeFromSpan ( refInfo . definitionDocument , refInfo . definition . nameValue . span ) ) ; } return undefined ; } } ) ; } private async onProvideReferences ( textDocument , position , context , token ) { return await callWithTelemetryAndErrorHandling ( 'Find References' , async ( actionContext ) : Promise < vscode . Location [ ] > => { const cancel = new Cancellation ( token , actionContext ) ; const results = [ ] ; const pc = await this . getPositionContext ( textDocument , position , cancel ) ; if ( pc ) { const references = pc . getReferences ( ) ; if ( references && references . length > 0 ) { actionContext . telemetry . properties . referenceType = references . kind ; for ( const ref of references . references ) { const locationUri = ref . document . documentUri ; const referenceRange = getVSCodeRangeFromSpan ( ref . document , ref . span ) ; results . push ( new vscode . Location ( locationUri , referenceRange ) ) ; } } } return results ; } ) ; } param textDocument param range param context param token return private async onProvideCodeActions ( textDocument , range , context , token ) { return await callWithTelemetryAndErrorHandling ( 'Provide code actions' , async ( actionContext ) : Promise < ( vscode . Command | vscode . CodeAction ) [ ] > => { actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; const cancel = new Cancellation ( token , actionContext ) ; const { doc , associatedDoc } = await this . getDeploymentDocAndAssociatedDoc ( textDocument , cancel ) ; if ( doc ) { return doc . getCodeActions ( associatedDoc , range , context ) ; } return [ ] ; } ) ; } private async onProvideSignatureHelp ( textDocument , position , token ) { return await callWithTelemetryAndErrorHandling ( 'provideSignatureHelp' , async ( actionContext ) : Promise < vscode . SignatureHelp | undefined > => { actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; const cancel = new Cancellation ( token , actionContext ) ; const pc = await this . getPositionContext ( textDocument , position , cancel ) ; if ( pc ) { let functionSignatureHelp = pc . getSignatureHelp ( ) ; let signatureHelp ; if ( functionSignatureHelp ) { const signatureInformation = new vscode . SignatureInformation ( functionSignatureHelp . functionMetadata . usage , functionSignatureHelp . functionMetadata . description ) ; signatureInformation . parameters = [ ] ; for ( const param of functionSignatureHelp . functionMetadata . parameters ) { const paramUsage = getFunctionParamUsage ( param . name , param . type ) ; const paramDocumentation = "" ; signatureInformation . parameters . push ( new vscode . ParameterInformation ( paramUsage , paramDocumentation ) ) ; } signatureHelp = new vscode . SignatureHelp ( ) ; signatureHelp . activeParameter = functionSignatureHelp . activeParameterIndex ; signatureHelp . activeSignature = 0 ; signatureHelp . signatures = [ signatureInformation ] ; } return signatureHelp ; } return undefined ; } ) ; } param textDocument param position param token return private async prepareRename ( textDocument , position , token ) { return await callWithTelemetryAndErrorHandling ( 'PrepareRename' , async ( actionContext ) => { actionContext . errorHandling . rethrow = true ; const cancel = new Cancellation ( token , actionContext ) ; const pc = await this . getPositionContext ( textDocument , position , cancel ) ; if ( ! token . isCancellationRequested && pc ) { const referenceSiteInfo = pc . getReferenceSiteInfo ( true ) ; let renameError = referenceSiteInfo && getRenameError ( referenceSiteInfo ) ; if ( renameError ) { actionContext . errorHandling . suppressDisplay = true ; throw new Error ( renameError ) ; } if ( referenceSiteInfo ) { return getVSCodeRangeFromSpan ( pc . document , referenceSiteInfo . unquotedReferenceSpan ) ; } actionContext . errorHandling . suppressDisplay = true ; throw new Error ( invalidRenameError ) ; } return undefined ; } ) ; } private async onProvideRename ( textDocument , position , newName , token ) { return await callWithTelemetryAndErrorHandling ( 'Rename' , async ( actionContext ) => { actionContext . errorHandling . rethrow = true ; const cancel = new Cancellation ( token , actionContext ) ; const pc = await this . getPositionContext ( textDocument , position , cancel ) ; if ( ! token . isCancellationRequested && pc ) { const result = new vscode . WorkspaceEdit ( ) ; const referenceSiteInfo = pc . getReferenceSiteInfo ( true ) ; let renameError = referenceSiteInfo && getRenameError ( referenceSiteInfo ) ; if ( renameError ) { throw new Error ( renameError ) ; } const referenceList = pc . getReferences ( ) ; if ( referenceList ) { const firstSingleQuoteIndex = newName . indexOf ( "'" ) ; if ( firstSingleQuoteIndex >= 0 ) { const secondSingleQuoteIndex = newName . indexOf ( "'" , firstSingleQuoteIndex + 1 ) ; if ( secondSingleQuoteIndex >= 0 ) { newName = newName . substring ( firstSingleQuoteIndex + 1 , secondSingleQuoteIndex ) ; } else { newName = newName . substring ( firstSingleQuoteIndex + 1 ) ; } } newName = newName . replace ( /^"(.*)"$/ , '$1' ) ; for ( const ref of referenceList . references ) { const referenceRange = getVSCodeRangeFromSpan ( ref . document , ref . span ) ; result . replace ( ref . document . documentUri , referenceRange , newName ) ; } } else { throw new Error ( invalidRenameError ) ; } return result ; } } ) ; } private async provideDocumentLinks ( textDocument , token ) { return await callWithTelemetryAndErrorHandling ( 'provideDocumentLinks' , async ( actionContext ) => { actionContext . errorHandling . rethrow = true ; actionContext . telemetry . suppressIfSuccessful = true ; const dt = this . getOpenedDeploymentTemplate ( textDocument ) ; if ( dt ) { return dt . getDocumentLinks ( actionContext ) ; } return undefined ; } ) ; } private onActiveTextEditorChanged ( editor ) { callWithTelemetryAndErrorHandlingSync ( 'onActiveTextEditorChanged' , ( actionContext ) : void => { actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; let activeDocument = editor ?. document ; if ( activeDocument ) { if ( ! this . getOpenedDeploymentDocument ( activeDocument ) ) { this . updateOpenedDocument ( activeDocument ) ; } } this . updateEditorStateInBackground ( ) ; } ) ; } private async onTextSelectionChanged ( ) { await callWithTelemetryAndErrorHandling ( 'onTextSelectionChanged' , async ( actionContext ) : Promise < void > => { actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . errorHandling . suppressDisplay = true ; actionContext . telemetry . suppressIfSuccessful = true ; let editor = vscode . window . activeTextEditor ; if ( editor ) { let position = editor . selection . anchor ; let pc = await this . getPositionContext ( editor . document , position , Cancellation . cantCancel ) ; if ( pc && pc instanceof TemplatePositionContext ) { let tleBraceHighlightIndexes = TLE . BraceHighlighter . getHighlightCharacterIndexes ( pc ) ; let braceHighlightRanges = [ ] ; for ( let tleHighlightIndex of tleBraceHighlightIndexes ) { const highlightSpan = new Span ( tleHighlightIndex + pc . jsonTokenStartIndex , 1 ) ; braceHighlightRanges . push ( getVSCodeRangeFromSpan ( pc . document , highlightSpan ) ) ; } editor . setDecorations ( this . _braceHighlightDecorationType , braceHighlightRanges ) ; } } } ) ; } private onDocumentChanged ( event ) { this . updateOpenedDocument ( event . document ) ; } private onDocumentOpened ( openedDocument ) { this . updateOpenedDocument ( openedDocument ) ; } private onDocumentClosed ( closedDocument ) { callWithTelemetryAndErrorHandlingSync ( 'onDocumentClosed' , ( actionContext ) : void => { actionContext . telemetry . properties . isActivationEvent = 'true' ; actionContext . telemetry . suppressIfSuccessful = true ; actionContext . errorHandling . suppressDisplay = true ; this . closeDeploymentFile ( closedDocument ) ; } ) ; } /**
     * This event is fired when validation on a template finishes, and informs us of the the
     * linked templates referenced by that template
     */ private onTemplateGraphAvailable ( e ) { callWithTelemetryAndErrorHandlingSync ( "onTemplateGraphAvailable" , ( actionContext ) => { actionContext . telemetry . suppressIfSuccessful = true ; assert ( e . fullValidationStatus , "onTemplateGraphAvailable: fullValidationStatus should never be undefined" ) ; const rootTemplateUri = parseUri ( e . rootTemplateUri ) ; const rootTemplate = this . getOpenedDeploymentTemplate ( rootTemplateUri ) ; const rootTemplateKey = getNormalizedDocumentKey ( parseUri ( e . rootTemplateUri ) ) ; this . _cachedTemplateGraphs . set ( rootTemplateKey , e ) ; if ( rootTemplate ) { try { for ( const doc of vscode . workspace . textDocuments ) { if ( getNormalizedDocumentKey ( doc . uri ) === rootTemplateKey ) { assignTemplateGraphToDeploymentTemplate ( e , rootTemplate , this ) ; this . updateEditorStateInBackground ( ) ; this . reportDeploymentTemplateErrorsNoThrow ( doc , rootTemplate ) ; } } } finally { this . _codeLensChangedEmitter . fire ( ) ; } } } ) ; } private onLanguageServerStateChanged ( ) { this . _codeLensChangedEmitter . fire ( ) ; this . updateEditorStateInBackground ( ) ; } private registerActiveUse ( ) { survey . registerActiveUseNoThrow ( ) ; this . _bicepMessage . registerActiveUseNoThrow ( ) ; } }