"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCompletions = getCompletions;
const os_1 = require("os");
const vscode_languageserver_1 = require("vscode-languageserver");
const SvelteTags_1 = require("./SvelteTags");
const parseHtml_1 = require("../../../lib/documents/parseHtml");
const getModifierData_1 = require("./getModifierData");
const utils_1 = require("./utils");
const HTML_COMMENT_START = '<!--';
const componentDocumentationCompletion = {
    label: '@component',
    insertText: `component${os_1.EOL}$1${os_1.EOL}`,
    documentation: 'Documentation for this component. ' +
        'It will show up on hover. You can use markdown and code blocks here',
    insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
    kind: vscode_languageserver_1.CompletionItemKind.Snippet,
    sortText: '-1',
    filterText: 'component',
    preselect: true
};
function getCompletions(document, svelteDoc, position) {
    if ((0, utils_1.inStyleOrScript)(svelteDoc, position)) {
        return null;
    }
    const offset = svelteDoc.offsetAt(position);
    const lastCharactersBeforePosition = svelteDoc
        .getText()
        // use last 10 characters, should cover 99% of all cases
        .substr(Math.max(offset - 10, 0), Math.min(offset, 10));
    const precededByOpeningBracket = /[\s\S]*{\s*[#:/@]\w*$/.test(lastCharactersBeforePosition);
    if (precededByOpeningBracket) {
        return getTagCompletionsWithinMoustache();
    }
    const attributeContext = (0, parseHtml_1.getAttributeContextAtPosition)(document, position);
    if (attributeContext) {
        return getEventModifierCompletion(attributeContext);
    }
    return getComponentDocumentationCompletions();
    /**
     * Get completions for special svelte tags within moustache tags.
     */
    function getTagCompletionsWithinMoustache() {
        const triggerCharacter = getTriggerCharacter(lastCharactersBeforePosition);
        // return all, filtering with regards to user input will be done client side
        return getCompletionsWithRegardToTriggerCharacter(triggerCharacter, svelteDoc, offset);
    }
    function getComponentDocumentationCompletions() {
        if (!lastCharactersBeforePosition.includes(HTML_COMMENT_START)) {
            return null;
        }
        const commentStartIndex = lastCharactersBeforePosition.lastIndexOf(HTML_COMMENT_START);
        const text = lastCharactersBeforePosition
            .substring(commentStartIndex + HTML_COMMENT_START.length)
            .trimLeft();
        if (componentDocumentationCompletion.label.includes(text)) {
            return vscode_languageserver_1.CompletionList.create([componentDocumentationCompletion], false);
        }
        return null;
    }
}
function getEventModifierCompletion(attributeContext) {
    const modifiers = (0, getModifierData_1.getModifierData)();
    if (!attributeContext || !(0, utils_1.attributeCanHaveEventModifier)(attributeContext)) {
        return null;
    }
    const items = modifiers
        .filter((modifier) => !attributeContext.name.includes('|' + modifier.modifier) &&
        !modifier.modifiersInvalidWith?.some((invalidWith) => attributeContext.name.includes(invalidWith)))
        .map((m) => ({
        label: m.modifier,
        documentation: m.documentation,
        kind: vscode_languageserver_1.CompletionItemKind.Event
    }));
    return vscode_languageserver_1.CompletionList.create(items);
}
const atCompletionItems = createCompletionItems([
    { tag: 'html', label: 'html' },
    { tag: 'debug', label: 'debug' },
    { tag: 'const', label: 'const' },
    { tag: 'render', label: 'render' },
    { tag: 'attach', label: 'attach' }
]);
const hashtagCompletionItems = createCompletionItems([
    { tag: 'if', label: 'if', insertText: 'if $1}\n\t$2\n{/if' },
    { tag: 'each', label: 'each', insertText: 'each $1 as $2}\n\t$3\n{/each' },
    {
        tag: 'await',
        label: 'await :then',
        insertText: 'await $1}\n\t$2\n{:then $3} \n\t$4\n{/await'
    },
    {
        tag: 'await',
        label: 'await then',
        insertText: 'await $1 then $2}\n\t$3\n{/await'
    },
    { tag: 'key', label: 'key', insertText: 'key $1}\n\t$2\n{/key' },
    { tag: 'snippet', label: 'snippet', insertText: 'snippet $1($2)}\n\t$3\n{/snippet' }
]);
const beginningAwaitOpenCompletionItems = createCompletionItems([
    { tag: 'await', label: 'then' },
    { tag: 'await', label: 'catch' }
]);
const beginningEachOpenCompletionItems = createCompletionItems([{ tag: 'each', label: 'else' }]);
const beginningIfOpenCompletionItems = createCompletionItems([
    { tag: 'if', label: 'else' },
    { tag: 'if', label: 'else if' }
]);
const endAwaitOpenCompletionItems = createCompletionItems([{ tag: 'await', label: 'await' }]);
const endEachOpenCompletionItems = createCompletionItems([{ tag: 'each', label: 'each' }]);
const endIfOpenCompletionItems = createCompletionItems([{ tag: 'if', label: 'if' }]);
const endKeyOpenCompletionItems = createCompletionItems([{ tag: 'key', label: 'key' }]);
/**
 * Get completions with regard to trigger character.
 */
function getCompletionsWithRegardToTriggerCharacter(triggerCharacter, svelteDoc, offset) {
    if (triggerCharacter === '@') {
        return atCompletionItems;
    }
    if (triggerCharacter === '#') {
        return hashtagCompletionItems;
    }
    if (triggerCharacter === ':') {
        return showCompletionWithRegardsToOpenedTags({
            awaitOpen: beginningAwaitOpenCompletionItems,
            eachOpen: beginningEachOpenCompletionItems,
            ifOpen: beginningIfOpenCompletionItems
        }, svelteDoc, offset);
    }
    if (triggerCharacter === '/') {
        return showCompletionWithRegardsToOpenedTags({
            awaitOpen: endAwaitOpenCompletionItems,
            eachOpen: endEachOpenCompletionItems,
            ifOpen: endIfOpenCompletionItems,
            keyOpen: endKeyOpenCompletionItems
        }, svelteDoc, offset);
    }
    return null;
}
/**
 * Get trigger character in front of current position.
 */
function getTriggerCharacter(content) {
    const chars = [
        getLastIndexOf('#'),
        getLastIndexOf('/'),
        getLastIndexOf(':'),
        getLastIndexOf('@')
    ];
    return chars.sort((c1, c2) => c2.idx - c1.idx)[0].char;
    function getLastIndexOf(char) {
        return { char, idx: content.lastIndexOf(char) };
    }
}
/**
 * Return completions with regards to last opened tag.
 */
function showCompletionWithRegardsToOpenedTags(on, svelteDoc, offset) {
    switch ((0, SvelteTags_1.getLatestOpeningTag)(svelteDoc, offset)) {
        case 'each':
            return on.eachOpen;
        case 'if':
            return on.ifOpen;
        case 'await':
            return on.awaitOpen;
        case 'key':
            return on?.keyOpen ?? null;
        case 'snippet':
            return on.snippetOpen ?? null;
        default:
            return null;
    }
}
/**
 * Create the completion items for given labels and tags.
 */
function createCompletionItems(items) {
    return vscode_languageserver_1.CompletionList.create(
    // add sortText/preselect so it is ranked higher than other completions and selected first
    items.map((item) => ({
        insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
        insertText: item.insertText,
        label: item.label,
        sortText: '-1',
        kind: vscode_languageserver_1.CompletionItemKind.Keyword,
        preselect: true,
        documentation: {
            kind: vscode_languageserver_1.MarkupKind.Markdown,
            value: SvelteTags_1.documentation[item.tag]
        }
    })));
}
//# sourceMappingURL=getCompletions.js.map