"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const typescript_1 = __importDefault(require("typescript"));
const assert_1 = __importDefault(require("assert"));
const fs_1 = require("fs");
const documents_1 = require("../../../../src/lib/documents");
const utils_1 = require("../../../../src/utils");
const vscode_languageserver_1 = require("vscode-languageserver");
const CompletionProvider_1 = require("../../../../src/plugins/typescript/features/CompletionProvider");
const LSAndTSDocResolver_1 = require("../../../../src/plugins/typescript/LSAndTSDocResolver");
const lodash_1 = require("lodash");
const ls_config_1 = require("../../../../src/ls-config");
const service_1 = require("../../../../src/plugins/typescript/service");
const test_utils_1 = require("../test-utils");
const compiler_1 = require("svelte/compiler");
const testDir = (0, path_1.join)(__dirname, '..');
const testFilesDir = (0, path_1.join)(testDir, 'testfiles', 'completions');
const newLine = typescript_1.default.sys.newLine;
const indent = ' '.repeat(4);
const isSvelte5Plus = +compiler_1.VERSION.split('.')[0] >= 5;
const fileNameToAbsoluteUri = (file) => {
    return (0, utils_1.pathToUrl)((0, path_1.join)(testFilesDir, file));
};
function harmonizeNewLines(input) {
    return input?.replace(/\r\n/g, '~:~').replace(/\n/g, '~:~').replace(/~:~/g, typescript_1.default.sys.newLine);
}
// describe('CompletionProviderImpl (old transformation)', test(false));
describe('CompletionProviderImpl', function () {
    (0, test_utils_1.serviceWarmup)(this, testFilesDir, (0, utils_1.pathToUrl)(testDir));
    function setup(filename) {
        const docManager = new documents_1.DocumentManager((textDocument) => documents_1.Document.createForTest(textDocument.uri, textDocument.text));
        const lsConfigManager = new ls_config_1.LSConfigManager();
        const lsAndTsDocResolver = new LSAndTSDocResolver_1.LSAndTSDocResolver(docManager, [(0, utils_1.pathToUrl)(testDir)], lsConfigManager);
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        const filePath = (0, path_1.join)(testFilesDir, filename);
        const document = docManager.openClientDocument({
            uri: (0, utils_1.pathToUrl)(filePath),
            text: typescript_1.default.sys.readFile(filePath) || ''
        });
        return { completionProvider, document, docManager, lsConfigManager };
    }
    it('provides completions', async () => {
        const { completionProvider, document } = setup('completions.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 49), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '.'
        });
        assert_1.default.ok(Array.isArray(completions && completions.items), 'Expected completion items to be an array');
        assert_1.default.ok(completions.items.length > 0, 'Expected completions to have length');
        const first = completions.items[0];
        delete first.data;
        assert_1.default.deepStrictEqual(first, {
            label: 'b',
            insertText: undefined,
            insertTextFormat: undefined,
            kind: vscode_languageserver_1.CompletionItemKind.Method,
            sortText: '11',
            commitCharacters: ['.', ',', ';', '('],
            preselect: undefined,
            textEdit: undefined,
            labelDetails: undefined
        });
    });
    it('provides completions on simple property access in mustache', async () => {
        const { completionProvider, document } = setup('mustache.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 3), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '.'
        });
        const first = completions.items[0];
        delete first.data;
        assert_1.default.deepStrictEqual(first, {
            label: 'b',
            insertText: undefined,
            insertTextFormat: undefined,
            kind: vscode_languageserver_1.CompletionItemKind.Field,
            sortText: '11',
            commitCharacters: ['.', ',', ';', '('],
            preselect: undefined,
            textEdit: undefined,
            labelDetails: undefined
        });
    });
    const editingTestPositionsNewOnly = [
        [21, 22, '@const'],
        [24, 19, 'action directive'],
        [26, 24, 'transition directive'],
        [38, 24, 'animate']
    ];
    const editingTestPositions = [
        [4, 3, 'mustache'],
        [6, 10, '#await'],
        [10, 8, '#key'],
        [14, 9, '@html'],
        [16, 7, '#if'],
        [28, 26, 'element event handler'],
        [30, 21, 'binding'],
        [32, 16, 'element props'],
        [34, 21, 'class directive'],
        [36, 23, 'style directive'],
        [40, 17, 'component props'],
        [42, 22, 'component binding'],
        [44, 29, 'component event handler'],
        ...editingTestPositionsNewOnly
    ];
    async function testEditingCompletion(position) {
        const { completionProvider, document } = setup('editingCompletion.svelte');
        const completions = await completionProvider.getCompletions(document, position, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '.'
        });
        assert_1.default.ok(completions?.items?.find((item) => item.label === 'c' && item.kind === vscode_languageserver_1.CompletionItemKind.Field));
    }
    for (const [line, character, type] of editingTestPositions) {
        it(`provides completions on simple property access in ${type}`, async () => {
            await testEditingCompletion({ line, character });
        });
    }
    it('provide completion with items default when supported', async () => {
        const { completionProvider, document, lsConfigManager } = setup('completions.svelte');
        lsConfigManager.updateClientCapabilities({
            textDocument: {
                completion: {
                    completionList: {
                        itemDefaults: ['commitCharacters']
                    }
                }
            }
        });
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 49), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '.'
        });
        assert_1.default.deepStrictEqual(completions?.itemDefaults?.commitCharacters, ['.', ',', ';', '(']);
        const first = completions.items[0];
        assert_1.default.strictEqual(first.commitCharacters, undefined);
    });
    it('provides event completions', async () => {
        const { completionProvider, document } = setup('component-events-completion.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 5), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        assert_1.default.ok(Array.isArray(completions && completions.items), 'Expected completion items to be an array');
        assert_1.default.ok(completions.items.length > 0, 'Expected completions to have length');
        const eventCompletions = completions.items.filter((item) => item.label.startsWith('on:'));
        assert_1.default.deepStrictEqual(eventCompletions, [
            {
                commitCharacters: [],
                detail: 'aa: CustomEvent<boolean>',
                documentation: '',
                label: 'on:aa',
                sortText: '-1',
                kind: undefined,
                textEdit: undefined
            },
            {
                commitCharacters: [],
                detail: 'ab: MouseEvent',
                documentation: {
                    kind: 'markdown',
                    value: 'TEST'
                },
                label: 'on:ab',
                sortText: '-1',
                kind: undefined,
                textEdit: undefined
            },
            {
                commitCharacters: [],
                detail: 'ac: any',
                documentation: '',
                label: 'on:ac',
                sortText: '-1',
                kind: undefined,
                textEdit: undefined
            }
        ]);
    });
    it('provide event completions for element from type definition', async () => {
        const { completionProvider, document } = setup('element-events-completion.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 14), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions.items.find((item) => item.label.startsWith('on:touchend'));
        delete item.data;
        assert_1.default.deepStrictEqual(item, {
            commitCharacters: ['.', ',', ';', '('],
            label: 'on:touchend',
            labelDetails: undefined,
            sortText: '11',
            kind: vscode_languageserver_1.CompletionItemKind.Field,
            textEdit: {
                range: { start: { line: 0, character: 8 }, end: { line: 0, character: 14 } },
                newText: 'on:touchend'
            },
            preselect: undefined,
            insertText: undefined,
            insertTextFormat: undefined
        });
    });
    it('provide tag name completion from type definition', async () => {
        const { completionProvider, document } = setup('custom-element-tag.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 2), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions.items.find((item) => item.label === 'custom-element');
        assert_1.default.ok(item);
        await completionProvider.resolveCompletion(document, item);
        delete item.data;
        assert_1.default.deepStrictEqual(item, {
            label: 'custom-element',
            documentation: {
                value: 'Custom doc for custom element',
                kind: 'markdown'
            },
            kind: vscode_languageserver_1.CompletionItemKind.Property,
            commitCharacters: [],
            textEdit: {
                range: { start: { line: 0, character: 1 }, end: { line: 0, character: 2 } },
                newText: 'custom-element'
            }
        });
    });
    it("doesn't provide event completions inside attribute value", async () => {
        const { completionProvider, document } = setup('component-events-completion.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 17), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        assert_1.default.deepStrictEqual(completions, null);
    });
    it('provides event completions with correct text replacement span', async () => {
        const { completionProvider, document } = setup('component-events-completion.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 11), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        assert_1.default.ok(Array.isArray(completions && completions.items), 'Expected completion items to be an array');
        assert_1.default.ok(completions.items.length > 0, 'Expected completions to have length');
        const eventCompletions = completions.items.filter((item) => item.label.startsWith('on:'));
        assert_1.default.deepStrictEqual(eventCompletions, [
            {
                commitCharacters: [],
                detail: 'aa: CustomEvent<boolean>',
                documentation: '',
                label: 'on:aa',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'on:aa',
                    range: {
                        start: {
                            line: 5,
                            character: 7
                        },
                        end: {
                            line: 5,
                            character: 11
                        }
                    }
                }
            },
            {
                commitCharacters: [],
                detail: 'ab: MouseEvent',
                documentation: {
                    kind: 'markdown',
                    value: 'TEST'
                },
                label: 'on:ab',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'on:ab',
                    range: {
                        start: {
                            line: 5,
                            character: 7
                        },
                        end: {
                            line: 5,
                            character: 11
                        }
                    }
                }
            },
            {
                commitCharacters: [],
                detail: 'ac: any',
                documentation: '',
                label: 'on:ac',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'on:ac',
                    range: {
                        start: {
                            line: 5,
                            character: 7
                        },
                        end: {
                            line: 5,
                            character: 11
                        }
                    }
                }
            }
        ]);
    });
    it('provides event completions from createEventDispatcher', async () => {
        const { completionProvider, document } = setup('component-events-completion.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(6, 5), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const eventCompletions = completions.items.filter((item) => item.label.startsWith('on:'));
        assert_1.default.deepStrictEqual(eventCompletions, [
            {
                commitCharacters: [],
                detail: 'c: CustomEvent<boolean>',
                documentation: {
                    kind: 'markdown',
                    value: 'abc'
                },
                label: 'on:c',
                sortText: '-1',
                kind: undefined,
                textEdit: undefined
            }
        ]);
    });
    it('provides event completion for components with type definition', async () => {
        const { completionProvider, document } = setup('component-events-completion-ts-def.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(4, 16), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const eventCompletions = completions.items.filter((item) => item.label.startsWith('on:'));
        assert_1.default.deepStrictEqual(eventCompletions, [
            {
                commitCharacters: [],
                detail: 'event1: CustomEvent<null>',
                documentation: '',
                label: 'on:event1',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'on:event1',
                    range: {
                        end: {
                            character: 16,
                            line: 4
                        },
                        start: {
                            character: 14,
                            line: 4
                        }
                    }
                }
            },
            {
                commitCharacters: [],
                detail: 'event2: CustomEvent<string>',
                documentation: {
                    kind: 'markdown',
                    value: 'documentation for event2'
                },
                label: 'on:event2',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'on:event2',
                    range: {
                        end: {
                            character: 16,
                            line: 4
                        },
                        start: {
                            character: 14,
                            line: 4
                        }
                    }
                }
            }
        ]);
    });
    it('provides event completion for components with type definition having multiple declarations of the same event', async () => {
        const { completionProvider, document } = setup('component-events-completion-ts-def.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(6, 16), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const eventCompletions = completions.items.filter((item) => item.label.startsWith('on:'));
        assert_1.default.deepStrictEqual(eventCompletions, [
            {
                commitCharacters: [],
                detail: 'event1: CustomEvent<string> | CustomEvent<number>',
                label: 'on:event1',
                sortText: '-1',
                documentation: '',
                kind: undefined,
                textEdit: {
                    newText: 'on:event1',
                    range: {
                        end: {
                            character: 17,
                            line: 6
                        },
                        start: {
                            character: 15,
                            line: 6
                        }
                    }
                }
            }
        ]);
    });
    it('does not provide completions inside style tag', async () => {
        const { completionProvider, document } = setup('completionsstyle.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(4, 1), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked,
            triggerCharacter: 'a'
        });
        assert_1.default.ok(completions === null, 'Expected completion to be null');
    });
    it('provides completion resolve info', async () => {
        const filename = 'completions.svelte';
        const { completionProvider, document } = setup(filename);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 49), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '.'
        });
        const { data } = completions.items[0];
        assert_1.default.deepStrictEqual(data, {
            data: undefined,
            name: 'b',
            position: {
                character: 49,
                line: 0
            },
            source: undefined,
            uri: fileNameToAbsoluteUri(filename)
        });
    });
    it('resolve completion and provide documentation', async () => {
        const { completionProvider, document } = setup('../documentation.svelte');
        const { documentation, detail } = await completionProvider.resolveCompletion(document, {
            label: 'foo',
            kind: 6,
            commitCharacters: ['.', ',', ';', '('],
            data: {
                name: 'foo',
                uri: '',
                position: vscode_languageserver_1.Position.create(3, 7)
            }
        });
        assert_1.default.deepStrictEqual(detail, '(alias) function foo(): boolean\nimport foo');
        assert_1.default.deepStrictEqual(documentation, {
            value: 'bars\n\n*@author* — John',
            kind: vscode_languageserver_1.MarkupKind.Markdown
        });
    });
    it('provides import completions for directory', async () => {
        const { completionProvider, document } = setup('importcompletions.svelte');
        const mockDirName = 'foo';
        const mockDirPath = (0, path_1.join)(testFilesDir, mockDirName);
        (0, fs_1.mkdirSync)(mockDirPath);
        try {
            const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 27), {
                triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
                triggerCharacter: '/'
            });
            const mockedDirImportCompletion = completions?.items.find((item) => item.label === mockDirName);
            assert_1.default.notEqual(mockedDirImportCompletion, undefined, "can't provide completions on directory");
            assert_1.default.equal(mockedDirImportCompletion?.kind, vscode_languageserver_1.CompletionItemKind.Folder);
        }
        finally {
            (0, fs_1.rmdirSync)(mockDirPath);
        }
    });
    it('provides import completions in file with uppercase directory', async () => {
        const { completionProvider, document } = setup('UpperCase/dirCasing.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 22), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '/'
        });
        assert_1.default.equal(completions?.items[0].label, 'toImport.ts');
    });
    it('provides import completions for supported files', async () => {
        const sourceFile = 'importcompletions.svelte';
        const { completionProvider, document } = setup(sourceFile);
        const supportedExtensions = [
            typescript_1.default.Extension.Js,
            typescript_1.default.Extension.Ts,
            typescript_1.default.Extension.Dts,
            typescript_1.default.Extension.Jsx,
            typescript_1.default.Extension.Tsx,
            typescript_1.default.Extension.Json,
            '.svelte'
        ];
        const ignores = ['tsconfig.json', sourceFile];
        const testfiles = (0, fs_1.readdirSync)(testFilesDir, { withFileTypes: true })
            .filter((f) => f.isDirectory() ||
            (supportedExtensions.includes((0, path_1.extname)(f.name)) && !ignores.includes(f.name)))
            .map((f) => f.name);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 27), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '/'
        });
        assert_1.default.deepStrictEqual((0, lodash_1.sortBy)(completions?.items.map((item) => item.label), (x) => x), (0, lodash_1.sortBy)(testfiles, (x) => x));
    });
    it('resolve auto import completion (is first import in file)', async () => {
        const { completionProvider, document } = setup('importcompletions1.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 3));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'blubb');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "../definitions"\n\nfunction blubb(): boolean');
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), 
        // " instead of ' because VSCode uses " by default when there are no other imports indicating otherwise
        `${newLine}${indent}import { blubb } from "../definitions";${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 8), vscode_languageserver_1.Position.create(0, 8)));
    });
    it('resolve auto import completion (is second import in file)', async () => {
        const { completionProvider, document } = setup('importcompletions2.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(2, 3));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'blubb');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "../definitions"\n\nfunction blubb(): boolean');
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), `${indent}import { blubb } from '../definitions';${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(2, 0), vscode_languageserver_1.Position.create(2, 0)));
    });
    it('resolve auto import completion (importing in same line as first import)', async () => {
        const { completionProvider, document } = setup('importcompletions3.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 42));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'blubb');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "../definitions"\n\nfunction blubb(): boolean');
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), `${newLine}${indent}import { blubb } from '../definitions';${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 8), vscode_languageserver_1.Position.create(0, 8)));
    });
    it('resolve auto import completion (is second import, module-script present)', async () => {
        const { completionProvider, document } = setup('importcompletions7.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(7, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'ComponentDef');
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "./ComponentDef"\n\nclass ComponentDef');
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), `${newLine}${indent}import { ComponentDef } from "./ComponentDef";${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(4, 8), vscode_languageserver_1.Position.create(4, 8)));
    });
    it('resolve auto import completion in instance script (instance and module script present)', async () => {
        const { completionProvider, document } = setup('importcompletions9.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'onMount');
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), 
        // " instead of ' because VSCode uses " by default when there are no other imports indicating otherwise
        `${newLine}${indent}import { onMount } from "svelte";${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(4, 8), vscode_languageserver_1.Position.create(4, 8)));
    });
    it('resolve auto import completion in module script (instance and module script present)', async () => {
        const { completionProvider, document } = setup('importcompletions9.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'onMount');
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), 
        // " instead of ' because VSCode uses " by default when there are no other imports indicating otherwise
        `${newLine}${indent}import { onMount } from "svelte";${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 25), vscode_languageserver_1.Position.create(0, 25)));
    });
    async function openFileToBeImported(docManager, completionProvider, name = '../imported-file.svelte') {
        const filePath = (0, path_1.join)(testFilesDir, name);
        const hoverinfoDoc = docManager.openClientDocument({
            uri: (0, utils_1.pathToUrl)(filePath),
            text: typescript_1.default.sys.readFile(filePath) || ''
        });
        await completionProvider.getCompletions(hoverinfoDoc, vscode_languageserver_1.Position.create(1, 1));
    }
    it('resolve auto import completion (importing a svelte component)', async () => {
        const { completionProvider, document, docManager } = setup('importcompletions4.svelte');
        // make sure that the ts language service does know about the imported-file file
        await openFileToBeImported(docManager, completionProvider);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(2, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'ImportedFile');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, `Add import from "../imported-file.svelte"${isSvelte5Plus ? '' : '\n\nclass ImportedFile'}`);
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), 
        // " instead of ' because VSCode uses " by default when there are no other imports indicating otherwise
        `${newLine}${indent}import ImportedFile from "../imported-file.svelte";${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 8), vscode_languageserver_1.Position.create(0, 8)));
    });
    it('resolve auto import completion (importing a svelte component, no script tag yet)', async () => {
        const { completionProvider, document, docManager } = setup('importcompletions5.svelte');
        // make sure that the ts language service does know about the imported-file file
        await openFileToBeImported(docManager, completionProvider);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'ImportedFile');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits, detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, `Add import from "../imported-file.svelte"${isSvelte5Plus ? '' : '\n\nclass ImportedFile'}`);
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), 
        // " instead of ' because VSCode uses " by default when there are no other imports indicating otherwise
        `<script>${newLine}${indent}import ImportedFile from "../imported-file.svelte";` +
            `${newLine}${newLine}</script>${newLine}`);
        assert_1.default.deepEqual(additionalTextEdits[0]?.range, vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), vscode_languageserver_1.Position.create(0, 0)));
    });
    it('resolve auto completion without auto import (a svelte component which was already imported)', async () => {
        const { completionProvider, document, docManager } = setup('importcompletions6.svelte');
        // make sure that the ts language service does know about the imported-file file
        await openFileToBeImported(docManager, completionProvider);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(3, 7));
        document.version++;
        const item = completions?.items.find((item) => item.label === 'ImportedFile');
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(additionalTextEdits, undefined);
    });
    it('doesnt suggest svelte auto import when already other import with same name present', async () => {
        const { completionProvider, document, docManager } = setup('importcompletions-2nd-import.svelte');
        // make sure that the ts language service does know about the imported-file file
        await openFileToBeImported(docManager, completionProvider, 'ScndImport.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(2, 13));
        document.version++;
        const items = completions?.items.filter((item) => item.label === 'ScndImport');
        assert_1.default.equal(items?.length, 1);
        const item = items?.[0];
        assert_1.default.equal(item?.additionalTextEdits, undefined);
        assert_1.default.equal(item?.detail, undefined);
        assert_1.default.equal(item?.kind, vscode_languageserver_1.CompletionItemKind.Variable);
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(additionalTextEdits, undefined);
    });
    it('resolve auto completion in correct place when already imported in module script', async () => {
        const { completionProvider, document } = setup('importcompletions8.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 8));
        const item = completions?.items.find((item) => item.label === 'blubb');
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.deepStrictEqual(additionalTextEdits, [
            {
                newText: '{ blubb }',
                range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(1, 11), vscode_languageserver_1.Position.create(1, 14))
            }
        ]);
    });
    it('indent according to prettier config', async () => {
        const { completionProvider, document } = setup('useTabs/importcompletions1.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 3));
        const item = completions?.items.find((item) => item.label === 'blubb');
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(harmonizeNewLines(additionalTextEdits[0]?.newText), `${newLine}\timport { blubb } from "../../definitions";${newLine}`);
    });
    it('can be canceled before promise resolved', async () => {
        const { completionProvider, document } = setup('importcompletions1.svelte');
        const cancellationTokenSource = new vscode_languageserver_1.CancellationTokenSource();
        const completionsPromise = completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 3), undefined, cancellationTokenSource.token);
        cancellationTokenSource.cancel();
        assert_1.default.deepStrictEqual(await completionsPromise, null);
    });
    it('can cancel completion resolving before promise resolved', async () => {
        const { completionProvider, document } = setup('importcompletions1.svelte');
        const cancellationTokenSource = new vscode_languageserver_1.CancellationTokenSource();
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 3));
        const item = completions?.items.find((item) => item.label === 'blubb');
        const completionResolvingPromise = completionProvider.resolveCompletion(document, item, cancellationTokenSource.token);
        cancellationTokenSource.cancel();
        assert_1.default.deepStrictEqual((await completionResolvingPromise).additionalTextEdits, undefined);
    });
    const testForJsDocTemplateCompletion = async (position, newText) => {
        const { completionProvider, document } = setup('jsdoc-completions.svelte');
        const completions = await completionProvider.getCompletions(document, position, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.TriggerCharacter,
            triggerCharacter: '*'
        });
        const item = completions?.items?.[0];
        const { line, character } = position;
        const start = vscode_languageserver_1.Position.create(line, character - '/**'.length);
        const end = vscode_languageserver_1.Position.create(line, character + '*/'.length);
        assert_1.default.strictEqual(harmonizeNewLines(item?.textEdit?.newText), newText);
        assert_1.default.deepStrictEqual(item?.textEdit?.range, vscode_languageserver_1.Range.create(start, end));
    };
    it('show jsDoc template completion', async () => {
        await testForJsDocTemplateCompletion(vscode_languageserver_1.Position.create(1, 7), `/**${newLine} * $0${newLine} */`);
    });
    it('show jsDoc template completion on function', async () => {
        await testForJsDocTemplateCompletion(vscode_languageserver_1.Position.create(4, 7), `/**${newLine} * $0${newLine} * @param parameter1${newLine} */`);
    });
    it('shows completions in reactive statement', async () => {
        const { completionProvider, document } = setup('completions-in-reactive-statement.svelte');
        await checkCompletion(vscode_languageserver_1.Position.create(9, 13));
        await checkCompletion(vscode_languageserver_1.Position.create(10, 16));
        await checkCompletion(vscode_languageserver_1.Position.create(11, 14));
        await checkCompletion(vscode_languageserver_1.Position.create(13, 17));
        await checkCompletion(vscode_languageserver_1.Position.create(14, 20));
        await checkCompletion(vscode_languageserver_1.Position.create(15, 18));
        async function checkCompletion(position) {
            const completions = await completionProvider.getCompletions(document, position, {
                triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
            });
            assert_1.default.strictEqual(completions?.items.length, 1);
            const item = completions?.items?.[0];
            assert_1.default.strictEqual(item?.label, 'abc');
        }
    }).timeout(this.timeout() * 2);
    it('provides default slot-let completion for components with type definition', async () => {
        const { completionProvider, document } = setup('component-events-completion-ts-def.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(5, 17), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const slotLetCompletions = completions.items.filter((item) => item.label.startsWith('let:'));
        assert_1.default.deepStrictEqual(slotLetCompletions, [
            {
                commitCharacters: [],
                detail: 'let1: boolean',
                documentation: '',
                label: 'let:let1',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'let:let1',
                    range: {
                        end: {
                            character: 17,
                            line: 5
                        },
                        start: {
                            character: 14,
                            line: 5
                        }
                    }
                }
            },
            {
                commitCharacters: [],
                detail: 'let2: string',
                documentation: {
                    kind: 'markdown',
                    value: 'documentation for let2'
                },
                label: 'let:let2',
                sortText: '-1',
                kind: undefined,
                textEdit: {
                    newText: 'let:let2',
                    range: {
                        end: {
                            character: 17,
                            line: 5
                        },
                        start: {
                            character: 14,
                            line: 5
                        }
                    }
                }
            }
        ]);
    });
    it('provides import statement completion', async () => {
        const { completionProvider, document } = setup('importstatementcompletions.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 1,
            character: 14
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'blubb');
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            additionalTextEdits: [
                {
                    newText: 'import ',
                    range: {
                        end: {
                            character: 11,
                            line: 1
                        },
                        start: {
                            character: 4,
                            line: 1
                        }
                    }
                }
            ],
            label: 'blubb',
            insertText: 'import { blubb$1 } from "../definitions";',
            insertTextFormat: 2,
            kind: vscode_languageserver_1.CompletionItemKind.Function,
            sortText: '11',
            commitCharacters: undefined,
            preselect: undefined,
            labelDetails: {
                description: '../definitions'
            },
            textEdit: {
                newText: '{ blubb$1 } from "../definitions";',
                range: {
                    end: {
                        character: 15,
                        line: 1
                    },
                    start: {
                        character: 11,
                        line: 1
                    }
                }
            }
        });
    });
    it('provides import statement completion with brackets', async () => {
        const { completionProvider, document } = setup('importstatementcompletions2.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 1,
            character: 15
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'blubb');
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            additionalTextEdits: [
                {
                    newText: 'import ',
                    range: {
                        end: {
                            character: 11,
                            line: 1
                        },
                        start: {
                            character: 4,
                            line: 1
                        }
                    }
                }
            ],
            label: 'blubb',
            insertText: 'import { blubb$1 } from "../definitions";',
            insertTextFormat: 2,
            kind: vscode_languageserver_1.CompletionItemKind.Function,
            sortText: '11',
            commitCharacters: undefined,
            preselect: undefined,
            labelDetails: {
                description: '../definitions'
            },
            textEdit: {
                newText: '{ blubb$1 } from "../definitions";',
                range: {
                    end: {
                        character: 16,
                        line: 1
                    },
                    start: {
                        character: 11,
                        line: 1
                    }
                }
            }
        });
    });
    it('provides optional chaining completion', async () => {
        const { completionProvider, document } = setup('completions-auto-optional-chain.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 3,
            character: 6
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'toString');
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            additionalTextEdits: [
                {
                    newText: '?',
                    range: {
                        end: {
                            character: 6,
                            line: 3
                        },
                        start: {
                            character: 5,
                            line: 3
                        }
                    }
                }
            ],
            label: 'toString',
            insertText: '?.toString',
            insertTextFormat: undefined,
            kind: vscode_languageserver_1.CompletionItemKind.Method,
            sortText: '11',
            commitCharacters: ['.', ',', ';', '('],
            preselect: undefined,
            labelDetails: undefined,
            textEdit: {
                newText: '.toString',
                range: {
                    end: {
                        character: 6,
                        line: 3
                    },
                    start: {
                        character: 6,
                        line: 3
                    }
                }
            }
        });
    });
    it('provide replacement for string completions', async () => {
        const { completionProvider, document } = setup('string-completion.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 1,
            character: 10
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === '@hi');
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            label: '@hi',
            kind: vscode_languageserver_1.CompletionItemKind.Constant,
            sortText: '11',
            preselect: undefined,
            insertText: undefined,
            insertTextFormat: undefined,
            labelDetails: undefined,
            commitCharacters: [],
            textEdit: {
                newText: '@hi',
                range: {
                    end: {
                        character: 10,
                        line: 1
                    },
                    start: {
                        character: 9,
                        line: 1
                    }
                }
            }
        });
    });
    it("auto import with system new line if can't detect file new line", async () => {
        const { completionProvider, document, docManager } = setup('importcompletions-new-line.svelte');
        if (newLine != '\n') {
            docManager.updateDocument({
                uri: document.uri,
                version: document.version + 1
            }, [
                {
                    range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, 0), document.positionAt(document.getTextLength())),
                    text: document.getText().replace('\n', '\r\n')
                }
            ]);
        }
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 7));
        const items = completions?.items.filter((item) => item.label === 'ScndImport');
        const item = items?.[0];
        const { additionalTextEdits } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(additionalTextEdits?.[0].newText, `${newLine}${indent}import { ScndImport } from "./to-import";${newLine}`);
    });
    it('shouldnt do completions in text', async () => {
        const { completionProvider, document } = setup('importcompletions-text.svelte');
        await expectNoCompletions(4, 1);
        await expectNoCompletions(5, 5);
        await expectNoCompletions(5, 6);
        await expectNoCompletions(6, 0);
        await expectNoCompletions(6, 1);
        await expectNoCompletions(7, 5);
        await expectNoCompletions(8, 7);
        await expectNoCompletions(8, 8);
        await expectNoCompletions(9, 0);
        await expectNoCompletions(9, 1);
        await expectNoCompletions(10, 6);
        async function expectNoCompletions(line, char) {
            const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(line, char));
            assert_1.default.strictEqual(completions, null, `expected no completions for ${line},${char}`);
        }
    });
    it('handles completion in empty text attribute', async () => {
        const { completionProvider, document } = setup('emptytext-importer.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(4, 14));
        assert_1.default.deepStrictEqual(completions?.items.map((item) => item.label), ['s', 'm', 'l']);
    });
    it('can auto import in workspace without tsconfig/jsconfig', async () => {
        const virtualTestDir = (0, test_utils_1.getRandomVirtualDirPath)(testFilesDir);
        const { docManager, document, lsAndTsDocResolver, lsConfigManager, virtualSystem } = (0, test_utils_1.setupVirtualEnvironment)({
            filename: 'index.svelte',
            fileContent: '<script>f</script>',
            testDir: virtualTestDir
        });
        const mockPackageDir = (0, path_1.join)(virtualTestDir, 'node_modules', '@types/random-package');
        // the main problem is how ts resolve reference type directive
        // it would start with a relative url and fail to auto import
        virtualSystem.writeFile((0, path_1.join)(mockPackageDir, 'index.d.ts'), '/// <reference types="random-package2" />' + '\nexport function bar(): string');
        virtualSystem.writeFile((0, path_1.join)(virtualTestDir, 'node_modules', '@types', 'random-package2', 'index.d.ts'), 'declare function foo(): string\n' + 'export = foo');
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        // let the language service aware of random-package and random-package2
        docManager.openClientDocument({
            text: '<script>import {} from "random-package";</script>',
            uri: (0, utils_1.pathToUrl)((0, path_1.join)(virtualTestDir, 'test.svelte'))
        });
        const completions = await completionProvider.getCompletions(document, {
            line: 0,
            character: 9
        });
        const item = completions?.items.find((item) => item.label === 'foo');
        const { detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "random-package2"\n\nfunction foo(): string');
    });
    it('can auto import package not in the program', async () => {
        const virtualTestDir = (0, test_utils_1.getRandomVirtualDirPath)(testFilesDir);
        const { document, lsAndTsDocResolver, lsConfigManager, virtualSystem } = (0, test_utils_1.setupVirtualEnvironment)({
            filename: 'index.svelte',
            fileContent: '<script>b</script>',
            testDir: virtualTestDir
        });
        const mockPackageDir = (0, path_1.join)(virtualTestDir, 'node_modules', 'random-package');
        virtualSystem.writeFile((0, path_1.join)(virtualTestDir, 'package.json'), JSON.stringify({
            dependencies: {
                'random-package': '*'
            }
        }));
        virtualSystem.writeFile((0, path_1.join)(mockPackageDir, 'package.json'), JSON.stringify({
            name: 'random-package',
            version: '1.0.0'
        }));
        virtualSystem.writeFile((0, path_1.join)(mockPackageDir, 'index.d.ts'), 'export function bar(): string');
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        const completions = await completionProvider.getCompletions(document, {
            line: 0,
            character: 9
        });
        const item = completions?.items.find((item) => item.label === 'bar');
        const { detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.strictEqual(detail, 'Add import from "random-package"\n\nfunction bar(): string');
    });
    it('can auto import new file', async () => {
        const virtualTestDir = (0, test_utils_1.getRandomVirtualDirPath)(testFilesDir);
        const { document, lsAndTsDocResolver, lsConfigManager, docManager } = (0, test_utils_1.setupVirtualEnvironment)({
            filename: 'index.svelte',
            fileContent: '<script>b</script>',
            testDir: virtualTestDir
        });
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        const completions = await completionProvider.getCompletions(document, {
            line: 0,
            character: 9
        });
        const item = completions?.items.find((item) => item.label === 'Bar');
        assert_1.default.equal(item, undefined);
        docManager.openClientDocument({
            text: '',
            uri: (0, utils_1.pathToUrl)((0, path_1.join)(virtualTestDir, 'Bar.svelte'))
        });
        const completions2 = await completionProvider.getCompletions(document, {
            line: 0,
            character: 9
        });
        const item2 = completions2?.items.find((item) => item.label === 'Bar');
        const { detail } = await completionProvider.resolveCompletion(document, item2);
        assert_1.default.strictEqual(detail, `Add import from "./Bar.svelte"${isSvelte5Plus ? '' : '\n\nclass Bar'}`);
    });
    it("doesn't use empty cache", async () => {
        const virtualTestDir = (0, test_utils_1.getRandomVirtualDirPath)(testFilesDir);
        const { document, lsAndTsDocResolver, lsConfigManager, docManager } = (0, test_utils_1.setupVirtualEnvironment)({
            filename: 'index.svelte',
            fileContent: '<script>b</script>',
            testDir: virtualTestDir
        });
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        await lsAndTsDocResolver.getLSAndTSDoc(document);
        docManager.updateDocument(document, [
            {
                range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, document.content.length), vscode_languageserver_1.Position.create(0, document.content.length)),
                text: ' '
            }
        ]);
        docManager.openClientDocument({
            text: '',
            uri: (0, utils_1.pathToUrl)((0, path_1.join)(virtualTestDir, 'Bar.svelte'))
        });
        docManager.updateDocument(document, [
            {
                range: vscode_languageserver_1.Range.create(vscode_languageserver_1.Position.create(0, document.content.length), vscode_languageserver_1.Position.create(0, document.content.length)),
                text: ' '
            }
        ]);
        const completions = await completionProvider.getCompletions(document, {
            line: 0,
            character: 9
        });
        const item2 = completions?.items.find((item) => item.label === 'Bar');
        const { detail } = await completionProvider.resolveCompletion(document, item2);
        assert_1.default.strictEqual(detail, `Add import from "./Bar.svelte"${isSvelte5Plus ? '' : '\n\nclass Bar'}`);
    });
    it('can auto import new export', async () => {
        const virtualTestDir = (0, test_utils_1.getRandomVirtualDirPath)(testFilesDir);
        const { document, lsAndTsDocResolver, lsConfigManager, virtualSystem } = (0, test_utils_1.setupVirtualEnvironment)({
            filename: 'index.svelte',
            fileContent: '<script>import {} from "./foo";f</script>',
            testDir: virtualTestDir
        });
        const completionProvider = new CompletionProvider_1.CompletionsProviderImpl(lsAndTsDocResolver, lsConfigManager);
        const tsFile = (0, path_1.join)(virtualTestDir, 'foo.ts');
        virtualSystem.writeFile(tsFile, 'export {}');
        const completions = await completionProvider.getCompletions(document, {
            line: 0,
            character: 31
        });
        const item = completions?.items.find((item) => item.label === 'foo');
        assert_1.default.equal(item, undefined);
        virtualSystem.writeFile(tsFile, 'export function foo() {}');
        lsAndTsDocResolver.updateExistingTsOrJsFile(tsFile);
        const completions2 = await completionProvider.getCompletions(document, {
            line: 0,
            character: 31
        });
        const item2 = completions2?.items.find((item) => item.label === 'foo');
        const { detail } = await completionProvider.resolveCompletion(document, item2);
        assert_1.default.strictEqual(detail, 'Update import from "./foo"\n\nfunction foo(): void');
    });
    it('provides completions for object literal member', async () => {
        const { completionProvider, document } = setup('object-member.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 6,
            character: 9
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'hi' && item.labelDetails?.detail === '(name)');
        item.insertText = harmonizeNewLines(item.insertText);
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            label: 'hi',
            labelDetails: {
                detail: '(name)'
            },
            kind: vscode_languageserver_1.CompletionItemKind.Method,
            sortText: '11\u0000hi\u00001',
            preselect: undefined,
            insertText: `hi(name) {${newLine}${indent}$0${newLine}},`,
            insertTextFormat: 2,
            commitCharacters: ['.', ',', ';', '('],
            textEdit: undefined
        });
    });
    it('provides completions for class member', async () => {
        const { completionProvider, document } = setup('object-member.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 10,
            character: 9
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'hi');
        item.insertText = harmonizeNewLines(item.insertText);
        delete item?.data;
        assert_1.default.deepStrictEqual(item, {
            label: 'hi',
            kind: vscode_languageserver_1.CompletionItemKind.Method,
            sortText: '11',
            preselect: undefined,
            insertText: `hi(name: string): string {${newLine}${indent}$0${newLine}}`,
            insertTextFormat: 2,
            commitCharacters: undefined,
            textEdit: undefined,
            labelDetails: undefined
        });
    });
    it('marks optional object literal members with a trailing ? in the label', async () => {
        const { completionProvider, document } = setup('object-member.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 14,
            character: 30
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'baz?');
        assert_1.default.ok(item, 'Expected optional property completion to be labelled "baz?"');
        // The `?` is display-only — the inserted text must remain the bare name.
        assert_1.default.strictEqual(item?.insertText ?? item?.label, 'baz');
        assert_1.default.strictEqual(item?.data?.name, 'baz');
    });
    it('provides import completions store that isnt imported yet', async () => {
        const { completionProvider, document } = setup('importcompletions-store.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 1,
            character: 10
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === '$store');
        assert_1.default.ok(item);
        assert_1.default.equal(item?.data?.source?.endsWith('/to-import'), true);
        const { data, ...itemWithoutData } = item;
        assert_1.default.deepStrictEqual(itemWithoutData, {
            label: '$store',
            kind: vscode_languageserver_1.CompletionItemKind.Constant,
            sortText: '16',
            preselect: undefined,
            insertText: undefined,
            insertTextFormat: undefined,
            commitCharacters: ['.', ',', ';', '('],
            textEdit: undefined,
            labelDetails: {
                description: './to-import'
            }
        });
        const { detail } = await completionProvider.resolveCompletion(document, item);
        assert_1.default.deepStrictEqual(detail, 'Add import from "./to-import"\n\nconst store: Writable<number>');
    });
    it(`provide props completions for namespaced component`, async () => {
        const namespacedComponentTestList = [
            [vscode_languageserver_1.Position.create(9, 26), 'namespace import after tag name'],
            [vscode_languageserver_1.Position.create(9, 35), 'namespace import before tag end'],
            [vscode_languageserver_1.Position.create(10, 27), 'object namespace after tag name'],
            [vscode_languageserver_1.Position.create(10, 36), 'object namespace before tag end'],
            [vscode_languageserver_1.Position.create(11, 27), 'object namespace + reexport after tag name'],
            [vscode_languageserver_1.Position.create(11, 36), 'object namespace + reexport before tag end'],
            [vscode_languageserver_1.Position.create(12, 37), 'constructor signature after tag name'],
            [vscode_languageserver_1.Position.create(12, 46), 'constructor signature before tag end'],
            [vscode_languageserver_1.Position.create(12, 37), 'overloaded constructor signature after tag name'],
            [vscode_languageserver_1.Position.create(12, 46), 'overloaded constructor signature before tag end']
        ];
        for (const [position, name] of namespacedComponentTestList) {
            const { completionProvider, document } = setup('namespaced.svelte');
            const completions = await completionProvider.getCompletions(document, position, {
                triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
            });
            const item = completions?.items.find((item) => item.label === 'hi2');
            assert_1.default.ok(item, `expected to have completion for ${name}`);
        }
    });
    // Hacky, but it works. Needed due to testing both new and old transformation
    after(() => {
        (0, service_1.__resetCache)();
    });
    // -------------------- put tests that only run in Svelte 5 below this line and everything else above --------------------
    if (!isSvelte5Plus)
        return;
    it(`provide props completions for rune-mode component`, async () => {
        const { completionProvider, document } = setup('component-props-completion-rune.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 5,
            character: 20
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'a');
        assert_1.default.ok(item);
    });
    it(`provide props completions for v5+ Component type`, async () => {
        const { completionProvider, document } = setup('component-props-completion-rune.svelte');
        const completions = await completionProvider.getCompletions(document, {
            line: 6,
            character: 15
        }, {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'hi');
        assert_1.default.ok(item);
    });
    it('provides namespace component completions for Svelte 5+', async () => {
        const { completionProvider, document } = setup('namespaced-v5.svelte');
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(4, 12), {
            triggerKind: vscode_languageserver_1.CompletionTriggerKind.Invoked
        });
        const item = completions?.items.find((item) => item.label === 'ComponentDef');
        assert_1.default.ok(item);
    });
    it('use correct position for component tag auto-import', async () => {
        const { completionProvider, document } = setup('importcompletions_unclose-tag.svelte');
        const completionsAssumption = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(0, 4));
        const itemAssumption = completionsAssumption?.items.find((item) => item.label === 'Completions');
        assert_1.default.ok(itemAssumption, `expected this to exist. Otherwise, the "Completions" assertion is wrong`);
        const completions = await completionProvider.getCompletions(document, vscode_languageserver_1.Position.create(1, 2));
        const item = completions?.items.find((item) => item.label === 'EmptytextImported');
        assert_1.default.ok(item);
        const shouldNotExistItem = completions?.items.find((item) => item.label === 'Completions');
        assert_1.default.ok(!shouldNotExistItem, 'expected auto-import be filtered out by current identifier');
    });
});
//# sourceMappingURL=CompletionProvider.test.js.map