/* --------------------------------------------------------------------------------------------
* Copyright (c) Remy Suen. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __generator = (this && this.__generator) || function (thisArg, body) {
    var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
    return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
    function verb(n) { return function (v) { return step([n, v]); }; }
    function step(op) {
        if (f) throw new TypeError("Generator is already executing.");
        while (g && (g = 0, op[0] && (_ = 0)), _) try {
            if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
            if (y = 0, t) op = [op[0] & 2, t.value];
            switch (op[0]) {
                case 0: case 1: t = op; break;
                case 4: _.label++; return { value: op[1], done: false };
                case 5: _.label++; y = op[1]; op = [0]; continue;
                case 7: op = _.ops.pop(); _.trys.pop(); continue;
                default:
                    if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
                    if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
                    if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
                    if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
                    if (t[2]) _.ops.pop();
                    _.trys.pop(); continue;
            }
            op = body.call(thisArg, _);
        } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
        if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
    }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DockerAssist = void 0;
var vscode_languageserver_types_1 = require("vscode-languageserver-types");
var docker_1 = require("./docker");
var dockerfile_ast_1 = require("dockerfile-ast");
var DockerAssist = /** @class */ (function () {
    /**
     * Creates a content assist processor for suggesting completion items related to a Dockerfile.
     *
     * @param document the text document to provide suggestions for
     * @param dockerRegistryClient the client for communicating with a Docker registry
     * @param completionItemCapabilities the capabilities that should be set for the returned items
     */
    function DockerAssist(document, dockerRegistryClient, completionItemCapabilities) {
        this.document = document;
        this.dockerRegistryClient = dockerRegistryClient;
        this.deprecatedSupport = completionItemCapabilities && completionItemCapabilities.deprecatedSupport;
        this.snippetSupport = completionItemCapabilities && completionItemCapabilities.snippetSupport;
        this.supportedTags = completionItemCapabilities && completionItemCapabilities.tagSupport && completionItemCapabilities.tagSupport.valueSet;
    }
    DockerAssist.prototype.computeProposals = function (position) {
        var buffer = this.document.getText();
        var offset = this.document.offsetAt(position);
        var dockerfile = dockerfile_ast_1.DockerfileParser.parse(buffer);
        var escapeCharacter = dockerfile.getEscapeCharacter();
        var lastDirectiveLine = -1;
        for (var _i = 0, _a = dockerfile.getDirectives(); _i < _a.length; _i++) {
            var directive = _a[_i];
            var range = directive.getNameRange();
            lastDirectiveLine = range.start.line;
            if (position.line === lastDirectiveLine) {
                if (position.character <= range.start.character) {
                    // in whitespace before the directive's name
                    return [
                        this.createEscape(0, offset, dockerfile_ast_1.Directive.escape),
                        this.createSyntax(0, offset, dockerfile_ast_1.Directive.syntax)
                    ];
                }
                else if (position.character <= range.end.character) {
                    // in the name
                    var prefix_1 = directive.getName().substring(0, position.character - range.start.character);
                    prefix_1 = prefix_1.toLowerCase();
                    if (dockerfile_ast_1.Directive.escape.indexOf(prefix_1) === 0) {
                        return [this.createEscape(prefix_1.length, offset, dockerfile_ast_1.Directive.escape)];
                    }
                    else if (dockerfile_ast_1.Directive.syntax.indexOf(prefix_1) === 0) {
                        return [this.createSyntax(prefix_1.length, offset, dockerfile_ast_1.Directive.syntax)];
                    }
                }
                return [];
            }
        }
        // directive only possible on the first line
        var comments = dockerfile.getComments();
        if (comments.length !== 0) {
            if (position.line === lastDirectiveLine + 1) {
                var commentRange = comments[0].getRange();
                // check if the first comment is on the first line
                if (commentRange.start.line === position.line) {
                    // is the user inside the comment
                    if (commentRange.start.character < position.character) {
                        var range = comments[0].getContentRange();
                        if (range === null || position.character <= range.start.character) {
                            // in whitespace
                            return [
                                this.createEscape(0, offset, dockerfile_ast_1.Directive.escape),
                                this.createSyntax(0, offset, dockerfile_ast_1.Directive.syntax),
                            ];
                        }
                        var comment = comments[0].getContent();
                        if (position.character <= range.end.character) {
                            // within the content
                            var prefix_2 = comment.substring(0, position.character - range.start.character);
                            // substring check
                            if (dockerfile_ast_1.Directive.escape.indexOf(prefix_2.toLowerCase()) === 0) {
                                return [this.createEscape(prefix_2.length, offset, dockerfile_ast_1.Directive.escape)];
                            }
                            else if (dockerfile_ast_1.Directive.syntax.indexOf(prefix_2.toLowerCase()) === 0) {
                                return [this.createSyntax(prefix_2.length, offset, dockerfile_ast_1.Directive.syntax)];
                            }
                        }
                        return [];
                    }
                }
            }
            else {
                for (var _b = 0, comments_1 = comments; _b < comments_1.length; _b++) {
                    var comment = comments_1[_b];
                    var range = comment.getRange();
                    if (range.start.line === position.line) {
                        if (range.start.character < position.character && position.character <= range.end.character) {
                            // inside a comment
                            return [];
                        }
                    }
                }
            }
        }
        var prefix = this.calculateTruePrefix(dockerfile, buffer, offset, escapeCharacter);
        if (prefix.content !== "") {
            var index = prefix.content.lastIndexOf('$');
            // $ exists so we're at a variable
            if (index !== -1) {
                // check that the variable $ wasn't escaped
                if (prefix.content.charAt(index - 1) !== '\\') {
                    // get the variable's prefix thus far
                    var variablePrefix = prefix.content.substring(index + 1).toLowerCase();
                    var prefixLength = variablePrefix.length + 1;
                    var items = [];
                    if (variablePrefix === "") {
                        // empty prefix, return all variables
                        for (var _c = 0, _d = dockerfile.getAvailableVariables(position.line); _c < _d.length; _c++) {
                            var variable = _d[_c];
                            var doc = dockerfile.resolveVariable(variable, position.line);
                            items.push(this.createVariableCompletionItem(variable, prefixLength, offset, true, doc));
                        }
                        for (var _e = 0, DefaultVariables_1 = dockerfile_ast_1.DefaultVariables; _e < DefaultVariables_1.length; _e++) {
                            var variable = DefaultVariables_1[_e];
                            var doc = dockerfile.resolveVariable(variable, position.line);
                            items.push(this.createVariableCompletionItem(variable, prefixLength, offset, true, doc));
                        }
                    }
                    else {
                        var brace = false;
                        if (variablePrefix.charAt(0) === '{') {
                            brace = true;
                            variablePrefix = variablePrefix.substring(1);
                        }
                        // merge list of available variables with the defaults
                        var variables = dockerfile.getAvailableVariables(position.line);
                        for (var _f = 0, DefaultVariables_2 = dockerfile_ast_1.DefaultVariables; _f < DefaultVariables_2.length; _f++) {
                            var variable = DefaultVariables_2[_f];
                            if (variables.indexOf(variable) === -1) {
                                variables.push(variable);
                            }
                        }
                        for (var _g = 0, variables_1 = variables; _g < variables_1.length; _g++) {
                            var variable = variables_1[_g];
                            if (variable.toLowerCase().indexOf(variablePrefix) === 0) {
                                var doc = dockerfile.resolveVariable(variable, position.line);
                                items.push(this.createVariableCompletionItem(variable, prefixLength, offset, brace, doc));
                            }
                        }
                    }
                    items.sort(function (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;
                }
            }
        }
        var previousWord = "";
        instructionsCheck: for (var _h = 0, _j = dockerfile.getInstructions(); _h < _j.length; _h++) {
            var instruction = _j[_h];
            if (docker_1.Util.isInsideRange(position, instruction.getInstructionRange()) || prefix.content === instruction.getKeyword()) {
                break;
            }
            else if (docker_1.Util.isInsideRange(position, instruction.getRange())) {
                switch (instruction.getKeyword()) {
                    case "ADD":
                        return this.createAddProposals(dockerfile, instruction, position, offset, prefix.content);
                    case "COPY":
                        return this.createCopyProposals(dockerfile, instruction, position, offset, prefix.content);
                    case "FROM":
                        return this.createFromProposals(instruction, position, offset, prefix.content);
                    case "HEALTHCHECK":
                        var subcommand = instruction.getSubcommand();
                        if (subcommand && subcommand.isBefore(position)) {
                            return [];
                        }
                        return this.createHealthcheckProposals(offset, prefix.content);
                    case "ONBUILD":
                        var 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 {
                            var trigger = instruction.getTriggerInstruction();
                            switch (trigger.getKeyword()) {
                                case "ADD":
                                    return this.createAddProposals(dockerfile, trigger, position, offset, prefix.content);
                                case "COPY":
                                    return this.createCopyProposals(dockerfile, trigger, position, offset, prefix.content);
                                case "HEALTHCHECK":
                                    var subcommand_1 = trigger.getSubcommand();
                                    if (subcommand_1 && subcommand_1.isBefore(position)) {
                                        return [];
                                    }
                                    return this.createHealthcheckProposals(offset, prefix.content);
                            }
                        }
                        return [];
                    default:
                        return [];
                }
            }
        }
        if (prefix.content === "") {
            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);
        }
        var suggestions = [];
        var uppercasePrefix = prefix.content.toUpperCase();
        for (var 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, offset - prefix.offset, offset);
    };
    DockerAssist.prototype.createProposals = function (keywords, previousWord, prefixLength, offset) {
        var proposals = [];
        for (var i = 0; i < keywords.length; i++) {
            switch (keywords[i]) {
                case "ARG":
                    if (this.snippetSupport) {
                        proposals.push(this.createARG_NameOnly(prefixLength, offset));
                        proposals.push(this.createARG_DefaultValue(prefixLength, offset));
                    }
                    else {
                        proposals.push(this.createARG(prefixLength, offset));
                    }
                    break;
                case "HEALTHCHECK":
                    proposals.push(this.createHEALTHCHECK_CMD(prefixLength, offset));
                    proposals.push(this.createHEALTHCHECK_NONE(prefixLength, offset));
                    break;
                case "FROM":
                case "MAINTAINER":
                case "ONBUILD":
                    // can't have FROM, MAINTAINER, or ONBUILD follow an ONBUILD
                    if (previousWord) {
                        break;
                    }
                default:
                    proposals.push(this.createSingleProposals(keywords[i], prefixLength, offset));
                    break;
            }
        }
        return proposals;
    };
    DockerAssist.prototype.createAddProposals = function (dockerfile, add, position, offset, prefix) {
        var fnMap = new Map();
        fnMap.set("chown", this.createADD_FlagChown.bind(this));
        var args = add.getArguments();
        var flagProposals = this.createFlagProposals(add.getFlags(), args, position, offset, prefix, fnMap);
        if (flagProposals !== null) {
            return flagProposals;
        }
        return this.createTargetFolderProposals(dockerfile, args, position, offset, prefix);
    };
    DockerAssist.prototype.createOtherFlagProposals = function (flagProposalsMap, allProposals, skipList, prefixLength, offset) {
        if (skipList.length === 0) {
            // have nothing, return everything
            return allProposals;
        }
        if (skipList.length === flagProposalsMap.size) {
            // have everything, return nothing
            return [];
        }
        var flagProposals = [];
        flagProposalsMap.forEach(function (_, key) {
            if (skipList.indexOf(key) === -1) {
                flagProposals.push(key);
            }
        });
        return flagProposals.map(function (flag) {
            var func = flagProposalsMap.get(flag);
            return func(prefixLength, offset);
        });
    };
    DockerAssist.prototype.createFlagProposals = function (flags, args, position, offset, prefix, flagProposalsMap) {
        var allProposals = [];
        flagProposalsMap.forEach(function (createFlagProposalItem) {
            allProposals.push(createFlagProposalItem(prefix.length, offset));
        });
        var promptFlags = true;
        if (args.length > 0 && docker_1.Util.positionBefore(args[0].getRange().start, this.document.positionAt(offset))) {
            // current position is not before the first argument, don't prompt flags
            promptFlags = false;
        }
        var skipList = [];
        var insideFlag = false;
        for (var _i = 0, flags_1 = flags; _i < flags_1.length; _i++) {
            var flag = flags_1[_i];
            var name = flag.getName();
            if (flagProposalsMap.has(name)) {
                skipList.push(name);
            }
            if (docker_1.Util.isInsideRange(position, flag.getRange())) {
                insideFlag = true;
            }
        }
        if (insideFlag) {
            if (prefix === "--") {
                return this.createOtherFlagProposals(flagProposalsMap, allProposals, skipList, prefix.length, offset);
            }
            var item_1 = null;
            flagProposalsMap.forEach(function (fn, flagName) {
                if ("--".concat(flagName, "=").indexOf(prefix) === 0) {
                    item_1 = fn(prefix.length, offset);
                }
            });
            if (item_1 !== null) {
                return [item_1];
            }
        }
        if (promptFlags || (args.length > 0 && docker_1.Util.isInsideRange(position, args[0].getRange()) && prefix === "-")) {
            return this.createOtherFlagProposals(flagProposalsMap, allProposals, skipList, prefix.length, offset);
        }
        return null;
    };
    DockerAssist.prototype.createCopyProposals = function (dockerfile, copy, position, offset, prefix) {
        var flag = copy.getFromFlag();
        // is the user in the --from= area
        if (flag && docker_1.Util.isInsideRange(position, flag.getValueRange())) {
            var names = {};
            var items = [];
            var stageIndex = 0;
            // determines if the last build stage was named or not
            var lastNumber = false;
            // get the prefix
            var stagePrefix = this.document.getText().substring(this.document.offsetAt(flag.getValueRange().start), offset).toLowerCase();
            for (var _i = 0, _a = dockerfile.getFROMs(); _i < _a.length; _i++) {
                var from = _a[_i];
                if (copy.isAfter(from)) {
                    var image = from.getImage();
                    var stage = from.getBuildStage();
                    if (stage) {
                        var lowercase = stage.toLowerCase();
                        if (lowercase.indexOf(stagePrefix) === 0 && !names[lowercase]) {
                            names[lowercase] = true;
                            items.push(this.createSourceImageCompletionItem(stage, image, stageIndex, stagePrefix.length, offset));
                        }
                        lastNumber = false;
                    }
                    else if (!names[stageIndex]) {
                        names[stageIndex] = true;
                        items.push(this.createSourceImageCompletionItem(stageIndex.toString(), image, stageIndex, stagePrefix.length, offset));
                        lastNumber = true;
                    }
                    stageIndex++;
                }
                else {
                    break;
                }
            }
            // last build stage was not named, don't suggest it as it is recursive
            if (lastNumber && items.length > 0) {
                items.pop();
            }
            return items;
        }
        var fnMap = new Map();
        fnMap.set("chown", this.createCOPY_FlagChown.bind(this));
        fnMap.set("from", this.createCOPY_FlagFrom.bind(this));
        var args = copy.getArguments();
        var flagProposals = this.createFlagProposals(copy.getFlags(), args, position, offset, prefix, fnMap);
        if (flagProposals !== null) {
            return flagProposals;
        }
        return this.createTargetFolderProposals(dockerfile, args, position, offset, prefix);
    };
    DockerAssist.prototype.createTargetFolderProposals = function (dockerfile, args, position, offset, prefix) {
        if (args.length === 1) {
            // after the one and only argument, suggest folder names
            if (docker_1.Util.positionBefore(args[0].getRange().end, position)) {
                return this.createWorkdirPathProposals(dockerfile, position, offset, prefix);
            }
            return [];
        }
        var lastRange = args[args.length - 1].getRange();
        if (docker_1.Util.isInsideRange(position, lastRange) || docker_1.Util.positionBefore(lastRange.end, position)) {
            // in the last argument or after the last argument
            return this.createWorkdirPathProposals(dockerfile, position, offset, prefix);
        }
        return [];
    };
    DockerAssist.prototype.createWorkdirPathProposals = function (dockerfile, position, offset, prefix) {
        var items = [];
        for (var _i = 0, _a = dockerfile.getAvailableWorkingDirectories(position.line); _i < _a.length; _i++) {
            var directory = _a[_i];
            if (directory.startsWith(prefix)) {
                items.push(this.createPlainTextCompletionItem(directory, prefix.length, offset, directory, vscode_languageserver_types_1.CompletionItemKind.Folder));
            }
        }
        return items;
    };
    DockerAssist.prototype.createFromProposals = function (from, position, offset, prefix) {
        var _this = this;
        // checks if the cursor is in the image's tag area
        if (docker_1.Util.isInsideRange(position, from.getImageTagRange())) {
            var index = prefix.indexOf(':');
            var lastIndex = prefix.indexOf(':');
            if (index === lastIndex) {
                prefix = prefix.substring(index + 1);
            }
            else {
                prefix = prefix.substring(index + 1, lastIndex);
            }
            var client_1 = this.dockerRegistryClient;
            return new Promise(function (resolve) { return __awaiter(_this, void 0, void 0, function () {
                var items, tags, _i, tags_1, tag;
                return __generator(this, function (_a) {
                    switch (_a.label) {
                        case 0:
                            items = [];
                            return [4 /*yield*/, client_1.getTags(from.getImageName())];
                        case 1:
                            tags = _a.sent();
                            for (_i = 0, tags_1 = tags; _i < tags_1.length; _i++) {
                                tag = tags_1[_i];
                                if (tag.indexOf(prefix) === 0) {
                                    items.push({
                                        textEdit: this.createTextEdit(prefix.length, this.document.offsetAt(position), tag),
                                        label: tag,
                                        kind: vscode_languageserver_types_1.CompletionItemKind.Property,
                                        insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
                                    });
                                }
                            }
                            resolve(items);
                            return [2 /*return*/];
                    }
                });
            }); });
        }
        var args = from.getArguments();
        if (args.length > 0 && args[0].isBefore(position)) {
            return [];
        }
        if ("--platform".indexOf(prefix) === 0) {
            return [this.createFROM_FlagPlatform(prefix.length, offset)];
        }
        return [];
    };
    DockerAssist.prototype.createHealthcheckProposals = function (offset, prefix) {
        var items = [];
        if (prefix.length < 3 && "CMD".indexOf(prefix.toUpperCase()) === 0) {
            items.push(this.createHEALTHCHECK_CMD_Subcommand(prefix.length, offset));
        }
        if (prefix.length < 4 && "NONE".indexOf(prefix.toUpperCase()) === 0) {
            items.push(this.createHEALTHCHECK_NONE_Subcommand(prefix.length, offset));
        }
        if ("--interval".indexOf(prefix) === 0) {
            items.push(this.createHEALTHCHECK_FlagInterval(prefix.length, offset));
        }
        if ("--retries".indexOf(prefix) === 0) {
            items.push(this.createHEALTHCHECK_FlagRetries(prefix.length, offset));
        }
        if ("--start-interval".indexOf(prefix) === 0) {
            items.push(this.createHEALTHCHECK_FlagStartInterval(prefix.length, offset));
        }
        if ("--start-period".indexOf(prefix) === 0) {
            items.push(this.createHEALTHCHECK_FlagStartPeriod(prefix.length, offset));
        }
        if ("--timeout".indexOf(prefix) === 0) {
            items.push(this.createHEALTHCHECK_FlagTimeout(prefix.length, offset));
        }
        for (var i = 0; i < items.length; i++) {
            items[i].sortText = i.toString();
        }
        return items;
    };
    /**
    * Walks back in the text buffer to calculate the true prefix of the
    * current text caret offset. This function will handle the
    * Dockerfile escape characters to skip escaped newline characters
    * where applicable.
    *
    * @param buffer the content of the opened file
    * @param offset the current text caret's offset
    * @param escapeCharacter the escape character defined in this Dockerfile
    */
    DockerAssist.prototype.calculateTruePrefix = function (dockerfile, buffer, offset, escapeCharacter) {
        var char = buffer.charAt(offset - 1);
        var checkEscape = true;
        switch (char) {
            case '\n':
                var escapedPrefix = "";
                escapeCheck: for (var i = offset - 2; i >= 0; i--) {
                    switch (buffer.charAt(i)) {
                        case ' ':
                        case '\t':
                            if (!checkEscape) {
                                break escapeCheck;
                            }
                            break;
                        case '\r':
                        case '\n':
                            checkEscape = true;
                            break;
                        case escapeCharacter:
                            if (checkEscape) {
                                checkEscape = false;
                                continue;
                            }
                            break;
                        default:
                            if (checkEscape) {
                                break escapeCheck;
                            }
                            offset = i;
                            escapedPrefix = buffer.charAt(i).toUpperCase() + escapedPrefix;
                            break;
                    }
                }
                if (escapedPrefix !== "") {
                    return { content: escapedPrefix, offset: offset };
                }
                break;
            case '\r':
            case ' ':
            case '\t':
                break;
            default:
                checkEscape = false;
                var truePrefix = char;
                for (var i_1 = offset - 2; i_1 >= 0; i_1--) {
                    char = buffer.charAt(i_1);
                    if (docker_1.Util.isNewline(char)) {
                        checkEscape = true;
                    }
                    else if (docker_1.Util.isWhitespace(char)) {
                        if (checkEscape) {
                            continue;
                        }
                        break;
                    }
                    else {
                        if (char === escapeCharacter) {
                            if (checkEscape) {
                                checkEscape = false;
                                continue;
                            }
                        }
                        else if (checkEscape) {
                            break;
                        }
                        truePrefix = char + truePrefix;
                    }
                }
                for (var _i = 0, _a = dockerfile.getInstructions(); _i < _a.length; _i++) {
                    var instruction = _a[_i];
                    var instructionRange = instruction.getInstructionRange();
                    var startOffset = this.document.offsetAt(instructionRange.start);
                    if (startOffset <= offset && offset <= this.document.offsetAt(instructionRange.end)) {
                        return { content: truePrefix, offset: startOffset };
                    }
                    for (var _b = 0, _c = instruction.getArguments(); _b < _c.length; _b++) {
                        var arg = _c[_b];
                        var argRange = arg.getRange();
                        var startOffset_1 = this.document.offsetAt(argRange.start);
                        if (startOffset_1 <= offset && offset <= this.document.offsetAt(argRange.end)) {
                            return { content: truePrefix, offset: startOffset_1 };
                        }
                    }
                    if (instruction instanceof dockerfile_ast_1.ModifiableInstruction) {
                        for (var _d = 0, _e = instruction.getFlags(); _d < _e.length; _d++) {
                            var flag = _e[_d];
                            var flagRange = flag.getRange();
                            var startOffset_2 = this.document.offsetAt(flagRange.start);
                            if (startOffset_2 <= offset && offset <= this.document.offsetAt(flagRange.end)) {
                                return { content: truePrefix, offset: startOffset_2 };
                            }
                        }
                    }
                }
        }
        return { content: "", offset: offset };
    };
    DockerAssist.prototype.createSingleProposals = function (keyword, prefixLength, offset) {
        switch (keyword) {
            case "ADD":
                return this.createADD(prefixLength, offset, keyword);
            case "CMD":
                return this.createCMD(prefixLength, offset, keyword);
            case "COPY":
                return this.createCOPY(prefixLength, offset, keyword);
            case "ENTRYPOINT":
                return this.createENTRYPOINT(prefixLength, offset, keyword);
            case "ENV":
                return this.createENV(prefixLength, offset, keyword);
            case "EXPOSE":
                return this.createEXPOSE(prefixLength, offset, keyword);
            case "FROM":
                return this.createFROM(prefixLength, offset, keyword);
            case "LABEL":
                return this.createLABEL(prefixLength, offset, keyword);
            case "MAINTAINER":
                return this.createMAINTAINER(prefixLength, offset, keyword);
            case "ONBUILD":
                return this.createONBUILD(prefixLength, offset, keyword);
            case "RUN":
                return this.createRUN(prefixLength, offset, keyword);
            case "SHELL":
                return this.createSHELL(prefixLength, offset, keyword);
            case "STOPSIGNAL":
                return this.createSTOPSIGNAL(prefixLength, offset, keyword);
            case "WORKDIR":
                return this.createWORKDIR(prefixLength, offset, keyword);
            case "VOLUME":
                return this.createVOLUME(prefixLength, offset, keyword);
            case "USER":
                return this.createUSER(prefixLength, offset, keyword);
        }
        throw new Error("Unknown keyword found: " + keyword);
    };
    DockerAssist.prototype.createADD = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("ADD", "ADD source dest", prefixLength, offset, "ADD ${1:source} ${2:dest}", markdown);
    };
    DockerAssist.prototype.createARG = function (prefixLength, offset) {
        return this.createKeywordCompletionItem("ARG", "ARG", prefixLength, offset, "ARG", "ARG");
    };
    DockerAssist.prototype.createARG_NameOnly = function (prefixLength, offset) {
        return this.createKeywordCompletionItem("ARG", "ARG name", prefixLength, offset, "ARG ${1:name}", "ARG_NameOnly");
    };
    DockerAssist.prototype.createARG_DefaultValue = function (prefixLength, offset) {
        return this.createKeywordCompletionItem("ARG", "ARG name=defaultValue", prefixLength, offset, "ARG ${1:name}=${2:defaultValue}", "ARG_DefaultValue");
    };
    DockerAssist.prototype.createCMD = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("CMD", "CMD [ \"executable\" ]", prefixLength, offset, "CMD [ \"${1:executable}\" ]", markdown);
    };
    DockerAssist.prototype.createCOPY = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("COPY", "COPY source dest", prefixLength, offset, "COPY ${1:source} ${2:dest}", markdown);
    };
    DockerAssist.prototype.createENTRYPOINT = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("ENTRYPOINT", "ENTRYPOINT [ \"executable\" ]", prefixLength, offset, "ENTRYPOINT [ \"${1:executable}\" ]", markdown);
    };
    DockerAssist.prototype.createENV = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("ENV", "ENV key=value", prefixLength, offset, "ENV ${1:key}=${2:value}", markdown);
    };
    DockerAssist.prototype.createEXPOSE = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("EXPOSE", "EXPOSE port", prefixLength, offset, "EXPOSE ${1:port}", markdown);
    };
    DockerAssist.prototype.createFROM = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("FROM", "FROM baseImage", prefixLength, offset, "FROM ${1:baseImage}", markdown);
    };
    DockerAssist.prototype.createHEALTHCHECK_CMD = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createKeywordCompletionItem("HEALTHCHECK", "HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD [ \"executable\" ]", prefixLength, offset, "HEALTHCHECK --interval=${1:30s} --timeout=${2:30s} --start-period=${3:5s} --retries=${4:3} CMD [ \"${5:executable}\" ]", "HEALTHCHECK_CMD");
        }
        var textEdit = this.createTextEdit(prefixLength, offset, "HEALTHCHECK CMD");
        return {
            data: "HEALTHCHECK_CMD",
            textEdit: textEdit,
            label: "HEALTHCHECK CMD",
            kind: vscode_languageserver_types_1.CompletionItemKind.Keyword,
            insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
        };
    };
    DockerAssist.prototype.createHEALTHCHECK_CMD_Subcommand = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createKeywordCompletionItem("CMD", "CMD [ \"executable\" ]", prefixLength, offset, "CMD [ \"${1:executable}\" ]", "HEALTHCHECK_CMD");
        }
        var textEdit = this.createTextEdit(prefixLength, offset, "CMD");
        return {
            data: "HEALTHCHECK_CMD",
            textEdit: textEdit,
            label: "CMD",
            kind: vscode_languageserver_types_1.CompletionItemKind.Keyword,
            insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
        };
    };
    DockerAssist.prototype.createADD_FlagChown = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--chown=user:group", prefixLength, offset, "--chown=${1:user\:group}", "ADD_FlagChown");
        }
        return this.createFlagCompletionItem("--chown=", prefixLength, offset, "--chown=", "ADD_FlagChown");
    };
    DockerAssist.prototype.createCOPY_FlagChown = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--chown=user:group", prefixLength, offset, "--chown=${1:user\:group}", "COPY_FlagChown");
        }
        return this.createFlagCompletionItem("--chown=", prefixLength, offset, "--chown=", "COPY_FlagChown");
    };
    DockerAssist.prototype.createCOPY_FlagFrom = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--from=stage", prefixLength, offset, "--from=${1:stage}", "COPY_FlagFrom");
        }
        return this.createFlagCompletionItem("--from=", prefixLength, offset, "--from=", "COPY_FlagFrom");
    };
    DockerAssist.prototype.createFROM_FlagPlatform = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--platform=arm64", prefixLength, offset, "--platform=${1:arm64}", "FROM_FlagPlatform");
        }
        return this.createFlagCompletionItem("--platform=", prefixLength, offset, "--platform=", "FROM_FlagPlatform");
    };
    DockerAssist.prototype.createHEALTHCHECK_FlagInterval = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--interval=30s", prefixLength, offset, "--interval=${1:30s}", "HEALTHCHECK_FlagInterval");
        }
        return this.createFlagCompletionItem("--interval=", prefixLength, offset, "--interval=", "HEALTHCHECK_FlagInterval");
    };
    DockerAssist.prototype.createHEALTHCHECK_FlagRetries = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--retries=3", prefixLength, offset, "--retries=${1:3}", "HEALTHCHECK_FlagRetries");
        }
        return this.createFlagCompletionItem("--retries=", prefixLength, offset, "--retries=", "HEALTHCHECK_FlagRetries");
    };
    DockerAssist.prototype.createHEALTHCHECK_FlagStartInterval = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--start-interval=5s", prefixLength, offset, "--start-interval=${1:5s}", "HEALTHCHECK_FlagStartInterval");
        }
        return this.createFlagCompletionItem("--start-interval=", prefixLength, offset, "--start-interval=", "HEALTHCHECK_FlagStartInterval");
    };
    DockerAssist.prototype.createHEALTHCHECK_FlagStartPeriod = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--start-period=5s", prefixLength, offset, "--start-period=${1:5s}", "HEALTHCHECK_FlagStartPeriod");
        }
        return this.createFlagCompletionItem("--start-period=", prefixLength, offset, "--start-period=", "HEALTHCHECK_FlagStartPeriod");
    };
    DockerAssist.prototype.createHEALTHCHECK_FlagTimeout = function (prefixLength, offset) {
        if (this.snippetSupport) {
            return this.createFlagCompletionItem("--timeout=30s", prefixLength, offset, "--timeout=${1:30s}", "HEALTHCHECK_FlagTimeout");
        }
        return this.createFlagCompletionItem("--timeout=", prefixLength, offset, "--timeout=", "HEALTHCHECK_FlagTimeout");
    };
    DockerAssist.prototype.createHEALTHCHECK_NONE = function (prefixLength, offset) {
        return this.createPlainTextCompletionItem("HEALTHCHECK NONE", prefixLength, offset, "HEALTHCHECK NONE", vscode_languageserver_types_1.CompletionItemKind.Keyword, "HEALTHCHECK_NONE");
    };
    DockerAssist.prototype.createHEALTHCHECK_NONE_Subcommand = function (prefixLength, offset) {
        return this.createPlainTextCompletionItem("NONE", prefixLength, offset, "NONE", vscode_languageserver_types_1.CompletionItemKind.Keyword, "HEALTHCHECK_NONE");
    };
    DockerAssist.prototype.createLABEL = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("LABEL", "LABEL key=\"value\"", prefixLength, offset, "LABEL ${1:key}=\"${2:value}\"", markdown);
    };
    DockerAssist.prototype.createMAINTAINER = function (prefixLength, offset, markdown) {
        var item = this.createKeywordCompletionItem("MAINTAINER", "MAINTAINER name", prefixLength, offset, "MAINTAINER ${1:name}", markdown);
        if (this.supportedTags !== undefined && this.supportedTags.length > 0 && this.supportedTags.indexOf(vscode_languageserver_types_1.CompletionItemTag.Deprecated) >= 0) {
            item.tags = [vscode_languageserver_types_1.CompletionItemTag.Deprecated];
        }
        if (this.deprecatedSupport) {
            item.deprecated = true;
        }
        return item;
    };
    DockerAssist.prototype.createONBUILD = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("ONBUILD", "ONBUILD INSTRUCTION", prefixLength, offset, "ONBUILD ${1:INSTRUCTION}", markdown);
    };
    DockerAssist.prototype.createRUN = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("RUN", "RUN command", prefixLength, offset, "RUN ${1:command}", markdown);
    };
    DockerAssist.prototype.createSHELL = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("SHELL", "SHELL [ \"executable\" ]", prefixLength, offset, "SHELL [ \"${1:executable}\" ]", markdown);
    };
    DockerAssist.prototype.createSTOPSIGNAL = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("STOPSIGNAL", "STOPSIGNAL signal", prefixLength, offset, "STOPSIGNAL ${1:signal}", markdown);
    };
    DockerAssist.prototype.createUSER = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("USER", "USER daemon", prefixLength, offset, "USER ${1:daemon}", markdown);
    };
    DockerAssist.prototype.createVOLUME = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("VOLUME", "VOLUME [ \"/data\" ]", prefixLength, offset, "VOLUME [ \"${1:/data}\" ]", markdown);
    };
    DockerAssist.prototype.createWORKDIR = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem("WORKDIR", "WORKDIR /the/workdir/path", prefixLength, offset, "WORKDIR ${1:/the/workdir/path}", markdown);
    };
    DockerAssist.prototype.createEscape = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem(dockerfile_ast_1.Directive.escape, "escape=`", prefixLength, offset, "escape=${1:`}", markdown);
    };
    DockerAssist.prototype.createSyntax = function (prefixLength, offset, markdown) {
        return this.createKeywordCompletionItem(dockerfile_ast_1.Directive.syntax, "syntax=docker/dockerfile:experimental", prefixLength, offset, "syntax=${1:docker/dockerfile:experimental}", markdown);
    };
    DockerAssist.prototype.createKeywordCompletionItem = function (keyword, label, prefixLength, offset, insertText, markdown) {
        if (!this.snippetSupport) {
            // only inserting the keyword so set the label to the keyword
            label = keyword;
            // just insert the keyword if snippets are not supported by the client
            insertText = keyword;
        }
        var textEdit = this.createTextEdit(prefixLength, offset, insertText);
        return {
            data: markdown,
            textEdit: textEdit,
            label: label,
            kind: vscode_languageserver_types_1.CompletionItemKind.Keyword,
            insertTextFormat: this.snippetSupport ? vscode_languageserver_types_1.InsertTextFormat.Snippet : vscode_languageserver_types_1.InsertTextFormat.PlainText,
        };
    };
    DockerAssist.prototype.createPlainTextCompletionItem = function (label, prefixLength, offset, insertText, kind, markdown) {
        var textEdit = this.createTextEdit(prefixLength, offset, insertText);
        return {
            data: markdown,
            textEdit: textEdit,
            label: label,
            kind: kind,
            insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
        };
    };
    DockerAssist.prototype.createFlagCompletionItem = function (label, prefixLength, offset, insertText, markdown) {
        var textEdit = this.createTextEdit(prefixLength, offset, insertText);
        return {
            data: markdown,
            textEdit: textEdit,
            label: label,
            kind: vscode_languageserver_types_1.CompletionItemKind.Field,
            insertTextFormat: this.snippetSupport ? vscode_languageserver_types_1.InsertTextFormat.Snippet : vscode_languageserver_types_1.InsertTextFormat.PlainText,
        };
    };
    DockerAssist.prototype.createSourceImageCompletionItem = function (label, documentation, buildIndex, prefixLength, offset) {
        return {
            textEdit: this.createTextEdit(prefixLength, offset, label),
            label: label,
            documentation: documentation,
            kind: vscode_languageserver_types_1.CompletionItemKind.Reference,
            insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
            sortText: buildIndex.toString()
        };
    };
    DockerAssist.prototype.createVariableCompletionItem = function (text, prefixLength, offset, brace, documentation) {
        text = brace ? "${" + text + '}' : '$' + text;
        return {
            textEdit: this.createTextEdit(prefixLength, offset, text),
            label: text,
            kind: vscode_languageserver_types_1.CompletionItemKind.Variable,
            insertTextFormat: vscode_languageserver_types_1.InsertTextFormat.PlainText,
            documentation: documentation
        };
    };
    DockerAssist.prototype.createTextEdit = function (prefixLength, offset, content) {
        if (prefixLength === 0) {
            return vscode_languageserver_types_1.TextEdit.insert(this.document.positionAt(offset), content);
        }
        return vscode_languageserver_types_1.TextEdit.replace(vscode_languageserver_types_1.Range.create(this.document.positionAt(offset - prefixLength), this.document.positionAt(offset)), content);
    };
    return DockerAssist;
}());
exports.DockerAssist = DockerAssist;
