DockerDefinition.prototype.computeDefinition = function (textDocument, content, position) { var dockerfile = dockerfile_ast_1.DockerfileParser.parse(content); var range = this.computeBuildStageDefinition(dockerfile, position); if (range !== null) { return vscode_languageserver_types_1.Location.create(textDocument.uri, range); } range = this.computeVariableDefinition(dockerfile, position); if (range !== null) { return vscode_languageserver_types_1.Location.create(textDocument.uri, range); } return null; };
parseSymbolInformation(document, textDocumentURI) { let dockerfile = dockerfile_ast_1.DockerfileParser.parse(document.getText()); let directive = dockerfile.getDirective(); let symbols = []; if (directive !== null) { symbols.push(this.createSymbolInformation(directive.getName(), textDocumentURI, directive.getNameRange(), vscode_languageserver_1.SymbolKind.Property)); } for (let instruction of dockerfile.getInstructions()) { symbols.push(this.createSymbolInformation(instruction.getInstruction(), textDocumentURI, instruction.getInstructionRange(), vscode_languageserver_1.SymbolKind.Function)); } return symbols; }
/** * Formats the specified lines of the given document based on the * provided formatting options. * * @param document the text document to format * @param lines the lines to format * @param options the formatting options to use to perform the format * @return the text edits to apply to format the lines of the document */ format(document, lines, options) { let content = document.getText(); let dockerfile = dockerfile_ast_1.DockerfileParser.parse(content); const indentedLines = []; for (let i = 0; i < document.lineCount; i++) { indentedLines[i] = false; } for (let instruction of dockerfile.getInstructions()) { let range = instruction.getRange(); indentedLines[range.start.line] = false; for (let i = range.start.line + 1; i <= range.end.line; i++) { indentedLines[i] = true; } } return this.formatLines(document, content, lines, indentedLines, options); }
DockerFormatter.prototype.format = function (content, lines, options) { var document = vscode_languageserver_types_1.TextDocument.create("", "", 0, content); var dockerfile = dockerfile_ast_1.DockerfileParser.parse(content); var indentedLines = []; for (var i = 0; i < document.lineCount; i++) { indentedLines[i] = false; } for (var _i = 0, _a = dockerfile.getInstructions(); _i < _a.length; _i++) { var instruction = _a[_i]; var range = instruction.getRange(); indentedLines[range.start.line] = false; for (var i = range.start.line + 1; i <= range.end.line; i++) { indentedLines[i] = true; } } return this.formatLines(document, content, lines, indentedLines, options); };
DockerFormatter.prototype.formatOnType = function (content, position, ch, options) { var dockerfile = dockerfile_ast_1.DockerfileParser.parse(content); // check that the inserted character is the escape character if (dockerfile.getEscapeCharacter() === ch) { for (var _i = 0, _a = dockerfile.getComments(); _i < _a.length; _i++) { var comment = _a[_i]; // ignore if we're in a comment if (comment.getRange().start.line === position.line) { return []; } } var directive = dockerfile.getDirective(); // ignore if we're in the parser directive if (directive && position.line === 0) { return []; } var document = vscode_languageserver_types_1.TextDocument.create("", "", 0, content); validityCheck: for (var i = document.offsetAt(position); i < content.length; i++) { switch (content.charAt(i)) { case ' ': case '\t': break; case '\r': case '\n': break validityCheck; default: // not escaping a newline, no need to format the next line return []; } } var lines = [position.line + 1]; var indentedLines = []; indentedLines[lines[0]] = true; return this.formatLines(document, document.getText(), lines, indentedLines, options); } return []; };
formatOnType(document, position, ch, options) { const dockerfile = dockerfile_ast_1.DockerfileParser.parse(document.getText()); // check that the inserted character is the escape character if (dockerfile.getEscapeCharacter() === ch) { for (let comment of dockerfile.getComments()) { // ignore if we're in a comment if (comment.getRange().start.line === position.line) { return []; } } const directive = dockerfile.getDirective(); // ignore if we're in the parser directive if (directive && position.line === 0) { return []; } const content = document.getText(); validityCheck: for (let i = document.offsetAt(position); i < content.length; i++) { switch (content.charAt(i)) { case ' ': case '\t': break; case '\r': case '\n': break validityCheck; default: // not escaping a newline, no need to format the next line return []; } } const lines = [position.line + 1]; const indentedLines = []; indentedLines[lines[0]] = true; return this.formatLines(document, document.getText(), lines, indentedLines, options); } return []; }
computeProposals(document, position) { let buffer = document.getText(); let offset = document.offsetAt(position); let dockerfile = dockerfile_ast_1.DockerfileParser.parse(buffer); let escapeCharacter = dockerfile.getEscapeCharacter(); let directive = dockerfile.getDirective(); if (directive !== null && position.line === 0) { let range = directive.getNameRange(); if (position.character <= range.start.character) { // in whitespace before the directive's name return [this.createEscape(0, offset, dockerfile_ast_1.Directive.escape)]; } else if (position.character <= range.end.character) { // in the name return [this.createEscape(position.character - range.start.character, offset, dockerfile_ast_1.Directive.escape)]; } return []; } // directive only possible on the first line let comments = dockerfile.getComments(); if (comments.length !== 0) { if (position.line === 0) { let commentRange = comments[0].getRange(); // check if the first comment is on the first line if (commentRange.start.line === 0) { // is the user inside the comment if (commentRange.start.character < position.character) { let range = comments[0].getContentRange(); if (range === null || position.character <= range.start.character) { // in whitespace return [this.createEscape(0, offset, dockerfile_ast_1.Directive.escape)]; } let comment = comments[0].getContent(); if (position.character <= range.end.character) { // within the content let prefix = comment.substring(0, position.character - range.start.character); // substring check if (dockerfile_ast_1.Directive.escape.indexOf(prefix.toLowerCase()) === 0) { return [this.createEscape(prefix.length, offset, dockerfile_ast_1.Directive.escape)]; } } return []; } } } else { for (let comment of comments) { let range = comment.getRange(); if (range.start.line === position.line) { if (range.start.character < position.character && position.character <= range.end.character) { // inside a comment return []; } } } } } let prefix = DockerAssist.calculateTruePrefix(buffer, offset, escapeCharacter); if (prefix !== "") { let index = prefix.lastIndexOf('$'); // $ exists so we're at a variable if (index !== -1) { // check that the variable $ wasn't escaped if (prefix.charAt(index - 1) !== '\\') { // get the variable's prefix thus far var variablePrefix = prefix.substring(index + 1).toLowerCase(); let prefixLength = variablePrefix.length + 1; const items = []; if (variablePrefix === "") { // empty prefix, return all variables for (let variable of dockerfile.getAvailableVariables(position.line)) { let doc = dockerfile.resolveVariable(variable, position.line); items.push(this.createVariableCompletionItem(variable, prefixLength, offset, true, doc)); } for (let variable of dockerfile_ast_1.DefaultVariables) { let doc = dockerfile.resolveVariable(variable, position.line); items.push(this.createVariableCompletionItem(variable, prefixLength, offset, true, doc)); } } else { let brace = false; if (variablePrefix.charAt(0) === '{') { brace = true; variablePrefix = variablePrefix.substring(1); } for (let variable of dockerfile.getAvailableVariables(position.line)) { if (variable.toLowerCase().indexOf(variablePrefix) === 0) { let doc = dockerfile.resolveVariable(variable, position.line); items.push(this.createVariableCompletionItem(variable, prefixLength, offset, brace, doc)); } } for (let variable of dockerfile_ast_1.DefaultVariables) { if (variable.toLowerCase().indexOf(variablePrefix) === 0) { let doc = dockerfile.resolveVariable(variable, position.line); items.push(this.createVariableCompletionItem(variable, prefixLength, offset, brace, doc)); } } } items.sort((a, b) => { if (a.label.toLowerCase() === b.label.toLowerCase()) { // put uppercase variables first return a.label.localeCompare(b.label) * -1; } return a.label.localeCompare(b.label); }); return items; } } } let previousWord = ""; instructionsCheck: for (let instruction of dockerfile.getInstructions()) { if (docker_1.Util.isInsideRange(position, instruction.getInstructionRange())) { break; } else if (docker_1.Util.isInsideRange(position, instruction.getRange())) { switch (instruction.getKeyword()) { case "ADD": return this.createAddProposals(instruction, position, offset, prefix); case "COPY": return this.createCopyProposals(dockerfile, instruction, position, offset, prefix); case "FROM": return this.createFromProposals(instruction, position, prefix); case "HEALTHCHECK": let subcommand = instruction.getSubcommand(); if (subcommand && subcommand.isBefore(position)) { return []; } return this.createHealthcheckProposals(offset, prefix); case "ONBUILD": let onbuildArgs = instruction.getArguments(); if (onbuildArgs.length === 0 || docker_1.Util.isInsideRange(position, onbuildArgs[0].getRange())) { // no trigger instructions or the cursor is in the trigger instruction previousWord = "ONBUILD"; break instructionsCheck; } else { let trigger = instruction.getTriggerInstruction(); switch (trigger.getKeyword()) { case "ADD": return this.createAddProposals(trigger, position, offset, prefix); case "COPY": return this.createCopyProposals(dockerfile, trigger, position, offset, prefix); case "HEALTHCHECK": let subcommand = trigger.getSubcommand(); if (subcommand && subcommand.isBefore(position)) { return []; } return this.createHealthcheckProposals(offset, prefix); } } return []; default: return []; } } } if (prefix === "") { if (dockerfile.getInstructions().length === 0) { // if we don't have any instructions, only suggest FROM return [this.createFROM(0, offset, "FROM")]; } // no prefix, return all the proposals return this.createProposals(docker_1.KEYWORDS, previousWord, 0, offset); } const suggestions = []; var uppercasePrefix = prefix.toUpperCase(); for (let i = 0; i < docker_1.KEYWORDS.length; i++) { if (docker_1.KEYWORDS[i] === uppercasePrefix) { // prefix is a keyword already, nothing to suggest return []; } else if (docker_1.KEYWORDS[i].indexOf(uppercasePrefix) === 0) { suggestions.push(docker_1.KEYWORDS[i]); } } if (suggestions.length === 0) { // prefix doesn't match any keywords, nothing to suggest return []; } return this.createProposals(suggestions, previousWord, prefix.length, offset); }
computeHighlightRanges(document, position) { let dockerfile = dockerfile_ast_1.DockerfileParser.parse(document.getText()); let provider = new dockerDefinition_1.DockerDefinition(); let location = provider.computeDefinition(document, position); let image = location === null ? dockerfile.getContainingImage(position) : dockerfile.getContainingImage(location.range.start); const highlights = []; if (location === null) { for (let instruction of dockerfile.getCOPYs()) { let flag = instruction.getFromFlag(); if (flag) { let range = flag.getValueRange(); if (range && range.start.line === position.line && range.start.character <= position.character && position.character <= range.end.character) { let stage = flag.getValue(); for (let other of dockerfile.getCOPYs()) { let otherFlag = other.getFromFlag(); if (otherFlag && otherFlag.getValue() === stage) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(otherFlag.getValueRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } return highlights; } } } for (const from of dockerfile.getFROMs()) { for (const variable of from.getVariables()) { if (docker_1.Util.isInsideRange(position, variable.getNameRange())) { const name = variable.getName(); for (const loopFrom of dockerfile.getFROMs()) { for (const fromVariable of loopFrom.getVariables()) { if (fromVariable.getName() === name) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(fromVariable.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } } return highlights; } } } for (let instruction of image.getInstructions()) { for (let variable of instruction.getVariables()) { if (docker_1.Util.isInsideRange(position, variable.getNameRange())) { let name = variable.getName(); for (let instruction of image.getInstructions()) { if (!(instruction instanceof dockerfile_ast_1.From)) { for (let variable of instruction.getVariables()) { if (variable.getName() === name) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(variable.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } } } return highlights; } } } } else { let definition = document.getText().substring(document.offsetAt(location.range.start), document.offsetAt(location.range.end)); for (let from of dockerfile.getFROMs()) { let range = from.getBuildStageRange(); if (range && range.start.line === location.range.start.line) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(location.range, vscode_languageserver_1.DocumentHighlightKind.Write)); for (let instruction of dockerfile.getCOPYs()) { let flag = instruction.getFromFlag(); if (flag) { if (flag.getValue() === definition) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(flag.getValueRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } } return highlights; } } for (let arg of image.getARGs()) { let property = arg.getProperty(); // property may be null if it's an ARG with no arguments if (property && property.getName() === definition) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(property.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Write)); } } for (let env of image.getENVs()) { for (let property of env.getProperties()) { if (property.getName() === definition) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(property.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Write)); } } } for (let instruction of image.getInstructions()) { // only highlight variables in non-FROM instructions if (!(instruction instanceof dockerfile_ast_1.From)) { for (const variable of instruction.getVariables()) { if (variable.getName() === definition) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(variable.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } } } for (const arg of dockerfile.getInitialARGs()) { const property = arg.getProperty(); if (property && docker_1.Util.rangeEquals(property.getNameRange(), location.range)) { for (const from of dockerfile.getFROMs()) { for (const variable of from.getVariables()) { if (variable.getName() === definition) { highlights.push(vscode_languageserver_1.DocumentHighlight.create(variable.getNameRange(), vscode_languageserver_1.DocumentHighlightKind.Read)); } } } } } } return highlights; }
DockerDefinition.prototype.computeDefinitionRange = function (content, position) { var dockerfile = dockerfile_ast_1.DockerfileParser.parse(content); var range = this.computeBuildStageDefinition(dockerfile, position); return range ? range : this.computeVariableDefinition(dockerfile, position); };