import { MarkdownString } from "vscode" ; import { templateKeys } from "../../../common" ; import { assert } from "../../fixed_assert" ; import { getFriendlyExpressionFromTleExpression } from "../../language/expressions/friendlyExpressions" ; import { isTleExpression } from "../../language/expressions/isTleExpression" ; import * as Json from "../../language/json/JSON" ; import { ContainsBehavior , Span } from "../../language/Span" ; import * as Completion from "../../vscodeIntegration/Completion" ; import { TemplatePositionContext } from "../positionContexts/TemplatePositionContext" ; import { getResourcesInfo , IJsonResourceInfo , IResourceInfo , jsonStringToTleExpression } from "./getResourcesInfo" ; export function getDependsOnCompletions ( pc ) { const completions = [ ] ; let span ; if ( pc . jsonToken ?. type === Json . TokenType . QuotedString ) { span = pc . jsonToken . span ; } else { span = pc . emptySpanAtDocumentCharacterIndex ; } if ( pc . tleInfo ?. tleParseResult . leftSquareBracketToken ) { return [ ] ; } const scope = pc . getScope ( ) ; const infos = getResourcesInfo ( { scope , recognizeDecoupledChildren : true } ) ; if ( infos . length === 0 ) { return completions ; } let currentResource = findClosestEnclosingResource ( pc . documentCharacterIndex , infos ) ; const descendentsIncludingSelf = findDescendentsIncludingSelf ( currentResource ) ; for ( const resource of infos ) { if ( descendentsIncludingSelf . has ( resource ) ) { continue ; } const items = getDependsOnCompletionsForResource ( resource , span , currentResource ?. parent === resource ) ; completions . push ( ... items ) ; } return completions ; } /**
 * Get possible completions for entries inside a resource's "dependsOn" array
 */ function getDependsOnCompletionsForResource ( resource , span , isParent ) { const completions = [ ] ; const resourceIdExpression = resource . getResourceIdExpression ( ) ; if ( resourceIdExpression ) { const friendlyNameExpression = resource . getFriendlyNameExpression ( { fullName : false } ) ; let friendlyTypeExpression = resource . getFriendlyTypeExpression ( { fullType : false } ) ; const label = friendlyNameExpression ; const insertText = `"[${ resourceIdExpression }]"` ; const detail = friendlyTypeExpression ; const documentation = `Inserts this resourceId reference:\n\`\`\`arm-template\n"[${ resourceIdExpression }]"\n\`\`\`\n<br/>` ; const item = new Completion . Item ( { label : isParent ? `Parent (${ label })` : label , insertText : insertText , detail , documentation : new MarkdownString ( documentation ) , span , kind : Completion . CompletionKind . dependsOnResourceId , filterText : `${ insertText }${ isParent ? " parent" : "" }` , priority : isParent ? Completion . CompletionPriority . high : Completion . CompletionPriority . normal } ) ; completions . push ( item ) ; const copyName = resource . copyBlockElement ?. getPropertyValue ( templateKeys . copyName ) ?. asStringValue ?. unquotedValue ; if ( copyName ) { const copyNameExpression = jsonStringToTleExpression ( copyName ) ; const copyLabel = `Loop ${ getFriendlyExpressionFromTleExpression ( copyNameExpression ) }` ; const copyInsertText = isTleExpression ( copyName ) ? `"[${ copyNameExpression }]"` : `"${ copyName }"` ; const copyDetail = detail ; const copyDocumentation = `Inserts this COPY element reference:
\`\`\`arm-template
${ copyInsertText }
\`\`\`
from resource \`${ friendlyNameExpression }\` of type \`${ friendlyTypeExpression }\`` ; const copyItem = new Completion . Item ( { label : copyLabel , insertText : copyInsertText , detail : copyDetail , documentation : new MarkdownString ( copyDocumentation ) , span , kind : Completion . CompletionKind . dependsOnResourceCopyLoop , filterText : copyInsertText } ) ; completions . push ( copyItem ) ; } } return completions ; } function findClosestEnclosingResource ( documentIndex , infos ) { const containsBehavior = ContainsBehavior . enclosed ; const firstMatch = infos . find ( info => info . resourceObject . span . contains ( documentIndex , containsBehavior ) ) ; if ( firstMatch ) { let deepestMatch = firstMatch ; while ( true ) { const childrenContainingResource = deepestMatch . children . filter ( child => ( < IJsonResourceInfo > child ) . resourceObject . span . contains ( documentIndex , containsBehavior ) ) ; assert ( childrenContainingResource . length <= 1 , "Shouldn't find multiple children containing the document position" ) ; if ( childrenContainingResource . length > 0 ) { deepestMatch = < IJsonResourceInfo > childrenContainingResource [ 0 ] ; } else { return deepestMatch ; } } } return undefined ; } function findDescendentsIncludingSelf ( resource ) { const descendentsIncludingSelf = new Set < IResourceInfo > ( ) ; visit ( resource ) ; return descendentsIncludingSelf ; function visit ( res ) { if ( res ) { descendentsIncludingSelf . add ( res ) ; for ( const child of res . children ) { visit ( child ) ; } } } }