import * as vscode from "vscode" ; import { IActionContext , IAzureUserInput , UserCancelledError } from "vscode-azureextensionui" ; import { isTleExpression } from "../../language/expressions/isTleExpression" ; import { ObjectValue } from "../../language/json/JSON" ; import { DeploymentTemplateDoc } from "./DeploymentTemplateDoc" ; import { InsertItem } from "./insertItem" ; export class ExtractItem { constructor ( private ui ) { } public async extractParameter ( editor , template , context ) { let selection = this . expandSelection ( editor . selection , editor . document , template , editor ) ; editor . selection = selection ; let selectedText = editor . document . getText ( selection ) ; let name = await this . ui . showInputBox ( { prompt : "Enter the new parameter name" } ) ; if ( ! name ) { context . telemetry . properties . cancelStep = 'name' ; throw new UserCancelledError ( ) ; } const leaveEmpty = "Press 'Enter' if you do not want to add a description." ; let description = await this . ui . showInputBox ( { prompt : "Description?" , placeHolder : leaveEmpty } ) ; const insertText = `[parameters('${ name }')]` ; const texts = this . fixExtractTexts ( selectedText , insertText , selection , template , editor ) ; let owningRootObject = this . getVarsParamsOwningRootObject ( template , selection ) ; await new InsertItem ( this . ui ) . insertParameterWithDefaultValue ( owningRootObject , editor , context , name , texts . selectedText , description , { undoStopBefore : true , undoStopAfter : false } ) ; await editor . edit ( builder => builder . replace ( editor . selection , texts . insertText ) , { undoStopBefore : false , undoStopAfter : true } ) ; editor . revealRange ( new vscode . Range ( editor . selection . start , editor . selection . end ) , vscode . TextEditorRevealType . Default ) ; } private getVarsParamsOwningRootObject ( template , selection ) { let scope = template . getContextFromDocumentLineAndColumnIndexes ( selection . start . line , selection . start . character , undefined ) . getScope ( ) ; return scope . memberOwningRootObject ; } public async extractVariable ( editor , template , context ) { let selection = this . expandSelection ( editor . selection , editor . document , template , editor ) ; editor . selection = selection ; let selectedText = editor . document . getText ( selection ) ; let name = await this . ui . showInputBox ( { prompt : "Enter the new variable name" } ) ; if ( ! name ) { context . telemetry . properties . cancelStep = 'name' ; throw new UserCancelledError ( ) ; } let insertText = `[variables('${ name }')]` ; const texts = this . fixExtractTexts ( selectedText , insertText , selection , template , editor ) ; let topLevel = this . getVarsParamsOwningRootObject ( template , selection ) ; await new InsertItem ( this . ui ) . insertVariableWithValue ( topLevel , editor , context , name , texts . selectedText , { undoStopBefore : true , undoStopAfter : false } ) ; await editor . edit ( builder => builder . replace ( editor . selection , texts . insertText ) , { undoStopBefore : false , undoStopAfter : true } ) ; editor . revealRange ( new vscode . Range ( editor . selection . start , editor . selection . end ) , vscode . TextEditorRevealType . Default ) ; } private fixExtractTexts ( selectedText , insertText , selection , template , editor ) { if ( this . isInsideExpression ( selection , template , editor ) ) { if ( this . isText ( selectedText ) ) { insertText = this . removeStartAndEnd ( insertText ) ; selectedText = this . removeStartAndEnd ( selectedText . trim ( ) ) ; } else if ( this . isNumber ( selectedText ) ) { insertText = this . removeStartAndEnd ( insertText ) ; selectedText = selectedText . trim ( ) ; } else { insertText = this . removeStartAndEnd ( insertText ) ; selectedText = this . addSquareBrackets ( selectedText ) ; } } return { selectedText : selectedText , insertText : insertText } ; } private expandSelection ( selection , document , template , editor ) { if ( selection . start . character === 0 ) { return selection ; } if ( selection . start . character === selection . end . character ) { let pc = template . getContextFromDocumentLineAndColumnIndexes ( selection . start . line , selection . start . character , undefined ) ; if ( pc . jsonValue ) { const span = pc . jsonValue . span ; return new vscode . Selection ( editor . document . positionAt ( span . startIndex + 1 ) , editor . document . positionAt ( span . endIndex ) ) ; } } const startPos = new vscode . Position ( selection . anchor . line , selection . anchor . character - 1 ) ; const endPos = new vscode . Position ( selection . end . line , selection . end . character + 1 ) ; const textBefore = document . getText ( new vscode . Selection ( startPos , selection . anchor ) ) ; const textAfter = document . getText ( new vscode . Selection ( selection . end , endPos ) ) ; if ( textBefore === "[" && textAfter === "]" || textBefore === '\'' && textAfter === '\'' ) { return new vscode . Selection ( startPos , endPos ) ; } return selection ; } private removeStartAndEnd ( text ) { return text . substr ( 1 , text . length - 2 ) ; } private addSquareBrackets ( text ) { return `[${ text }]` ; } private isText ( text ) { const regEx = /^\s*'.+'\s*$/gi ; return regEx . test ( text ) ; } private isNumber ( text ) { const regEx = /^\s*\d+\s*$/gi ; return regEx . test ( text ) ; } private isInsideExpression ( selection , template , editor ) { let pc = template . getContextFromDocumentLineAndColumnIndexes ( selection . start . line , selection . start . character , undefined ) ; if ( pc . jsonValue && pc . jsonValue . asStringValue ) { let selectedText = editor . document . getText ( selection ) ; if ( selectedText === pc . jsonValue . asStringValue . unquotedValue ) { return false ; } return isTleExpression ( pc . jsonValue . asStringValue ?. unquotedValue ) ; } return false ; } }