"use strict";
/*
 * Copyright (C) 2017, 2018 TypeFox and others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
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());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
const chai = __importStar(require("chai"));
const lsp = __importStar(require("vscode-languageserver/node"));
const lspcalls = __importStar(require("./lsp-protocol.calls.proposed"));
const test_utils_1 = require("./test-utils");
const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const types_1 = require("./utils/types");
const assert = chai.assert;
const diagnostics = new Map();
let server;
before(() => __awaiter(void 0, void 0, void 0, function* () {
    server = yield (0, test_utils_1.createServer)({
        rootUri: null,
        publishDiagnostics: args => diagnostics.set(args.uri, args)
    });
    server.didChangeConfiguration({
        settings: {
            completions: {
                completeFunctionCalls: true
            }
        }
    });
}));
beforeEach(() => {
    server.closeAll();
    // "closeAll" triggers final publishDiagnostics with an empty list so clear last.
    diagnostics.clear();
});
after(() => {
    server.closeAll();
});
describe('completion', () => {
    it('simple test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(): void {
          console.log('test')
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const pos = (0, test_utils_1.position)(doc, 'console');
        const proposals = yield server.completion({ textDocument: doc, position: pos });
        assert.isNotNull(proposals);
        assert.isAtLeast(proposals.items.length, 800);
        const item = proposals.items.find(i => i.label === 'addEventListener');
        assert.isDefined(item);
        const resolvedItem = yield server.completionResolve(item);
        assert.isNotTrue(resolvedItem.deprecated, 'resolved item is not deprecated');
        assert.isDefined(resolvedItem.detail);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('simple JS test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.js'),
            languageId: 'javascript',
            version: 1,
            text: `
        export function foo() {
          console.log('test')
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const pos = (0, test_utils_1.position)(doc, 'console');
        const proposals = yield server.completion({ textDocument: doc, position: pos });
        assert.isNotNull(proposals);
        assert.isAtLeast(proposals.items.length, 800);
        const item = proposals.items.find(i => i.label === 'addEventListener');
        assert.isDefined(item);
        const resolvedItem = yield server.completionResolve(item);
        assert.isDefined(resolvedItem.detail);
        const containsInvalidCompletions = proposals.items.reduce((accumulator, current) => {
            if (accumulator) {
                return accumulator;
            }
            // console.log as a warning is erroneously mapped to a non-function type
            return current.label === 'log' &&
                (current.kind !== lsp.CompletionItemKind.Function && current.kind !== lsp.CompletionItemKind.Method);
        }, false);
        assert.isFalse(containsInvalidCompletions);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('deprecated by JSDoc', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
            /**
             * documentation
             * @deprecated for a reason
             */
            export function foo() {
                console.log('test')
            }

            foo(); // call me
            `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const pos = (0, test_utils_1.position)(doc, 'foo(); // call me');
        const proposals = yield server.completion({ textDocument: doc, position: pos });
        assert.isNotNull(proposals);
        const item = proposals.items.find(i => i.label === 'foo');
        assert.isDefined(item);
        const resolvedItem = yield server.completionResolve(item);
        assert.isDefined(resolvedItem.detail);
        assert.isArray(resolvedItem.tags);
        assert.include(resolvedItem.tags, lsp.CompletionItemTag.Deprecated, 'resolved item is deprecated');
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('incorrect source location', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(): void {
          console.log('test')
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const pos = (0, test_utils_1.position)(doc, 'foo');
        const proposals = yield server.completion({ textDocument: doc, position: pos });
        assert.isNull(proposals);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('includes completions from global modules', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'pathex'
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.position)(doc, 'ex') });
        assert.isNotNull(proposals);
        const pathExistsCompletion = proposals.items.find(completion => completion.label === 'pathExists');
        assert.isDefined(pathExistsCompletion);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('includes completions with invalid identifier names', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                interface Foo {
                    'invalid-identifier-name': string
                }

                const foo: Foo
                foo.i
            `
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.positionAfter)(doc, '.i') });
        assert.isNotNull(proposals);
        const completion = proposals.items.find(completion => completion.label === 'invalid-identifier-name');
        assert.isDefined(completion);
        assert.isDefined(completion.textEdit);
        assert.equal(completion.textEdit.newText, '["invalid-identifier-name"]');
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('includes detail field with package name for auto-imports', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'readFile'
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.positionAfter)(doc, 'readFile') });
        assert.isNotNull(proposals);
        const completion = proposals.items.find(completion => completion.label === 'readFile');
        assert.isDefined(completion);
        assert.strictEqual(completion.detail, 'fs');
        assert.strictEqual(completion.insertTextFormat, /* snippet */ 2);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('resolves text edit for auto-import completion', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'readFile'
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.positionAfter)(doc, 'readFile') });
        assert.isNotNull(proposals);
        const completion = proposals.items.find(completion => completion.label === 'readFile');
        assert.isDefined(completion);
        const resolvedItem = yield server.completionResolve(completion);
        assert.deepEqual(resolvedItem.additionalTextEdits, [
            {
                newText: 'import { readFile } from "fs";\n\n',
                range: {
                    end: {
                        character: 0,
                        line: 0
                    },
                    start: {
                        character: 0,
                        line: 0
                    }
                }
            }
        ]);
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
    it('resolves text edit for auto-import completion in right format', () => __awaiter(void 0, void 0, void 0, function* () {
        server.didChangeConfiguration({
            settings: {
                typescript: {
                    format: {
                        semicolons: 'remove',
                        insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: false
                    }
                }
            }
        });
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'readFile'
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.positionAfter)(doc, 'readFile') });
        assert.isNotNull(proposals);
        const completion = proposals.items.find(completion => completion.label === 'readFile');
        assert.isDefined(completion);
        const resolvedItem = yield server.completionResolve(completion);
        assert.deepEqual(resolvedItem.additionalTextEdits, [
            {
                newText: 'import {readFile} from "fs"\n\n',
                range: {
                    end: {
                        character: 0,
                        line: 0
                    },
                    start: {
                        character: 0,
                        line: 0
                    }
                }
            }
        ]);
        server.didCloseTextDocument({ textDocument: doc });
        server.didChangeConfiguration({
            settings: {
                completions: {
                    completeFunctionCalls: true
                },
                typescript: {
                    format: {
                        semicolons: 'ignore',
                        insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true
                    }
                }
            }
        });
    })).timeout(10000);
    it('resolves a snippet for method completion', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                import fs from 'fs'
                fs.readFile
            `
        };
        server.didOpenTextDocument({ textDocument: doc });
        const proposals = yield server.completion({ textDocument: doc, position: (0, test_utils_1.positionAfter)(doc, 'readFile') });
        assert.isNotNull(proposals);
        const completion = proposals.items.find(completion => completion.label === 'readFile');
        assert.strictEqual(completion.insertTextFormat, lsp.InsertTextFormat.Snippet);
        assert.strictEqual(completion.label, 'readFile');
        const resolvedItem = yield server.completionResolve(completion);
        assert.strictEqual(resolvedItem.insertTextFormat, lsp.InsertTextFormat.Snippet);
        // eslint-disable-next-line no-template-curly-in-string
        assert.strictEqual(resolvedItem.insertText, 'readFile(${1:path}, ${2:options}, ${3:callback})$0');
        server.didCloseTextDocument({ textDocument: doc });
    })).timeout(10000);
});
describe('diagnostics', () => {
    it('simple test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('diagnosticsBar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(): void {
          missing('test')
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const resultsForFile = diagnostics.get(doc.uri);
        assert.isDefined(resultsForFile);
        const fileDiagnostics = resultsForFile.diagnostics;
        assert.equal(fileDiagnostics.length, 1);
        assert.equal("Cannot find name 'missing'.", fileDiagnostics[0].message);
    })).timeout(10000);
    it('supports diagnostic tags', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('diagnosticsBar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        import { join } from 'path';

        /** @deprecated */
        function foo(): void {}
        foo();
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const resultsForFile = diagnostics.get(doc.uri);
        assert.isDefined(resultsForFile);
        const fileDiagnostics = resultsForFile.diagnostics;
        assert.equal(fileDiagnostics.length, 2);
        const unusedDiagnostic = fileDiagnostics.find(d => d.code === 6133);
        assert.isDefined(unusedDiagnostic);
        assert.deepEqual(unusedDiagnostic.tags, [lsp.DiagnosticTag.Unnecessary]);
        const deprecatedDiagnostic = fileDiagnostics.find(d => d.code === 6387);
        assert.isDefined(deprecatedDiagnostic);
        assert.deepEqual(deprecatedDiagnostic.tags, [lsp.DiagnosticTag.Deprecated]);
    })).timeout(10000);
    it('multiple files test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('multipleFileDiagnosticsBar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
    export function bar(): void {
        missing('test')
    }
`
        };
        const doc2 = {
            uri: (0, test_utils_1.uri)('multipleFileDiagnosticsFoo.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
    export function foo(): void {
        missing('test')
    }
`
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        server.didOpenTextDocument({
            textDocument: doc2
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        assert.equal(diagnostics.size, 2);
        const diagnosticsForDoc = diagnostics.get(doc.uri);
        const diagnosticsForDoc2 = diagnostics.get(doc2.uri);
        assert.isDefined(diagnosticsForDoc);
        assert.isDefined(diagnosticsForDoc2);
        assert.equal(diagnosticsForDoc.diagnostics.length, 1, JSON.stringify(diagnostics));
        assert.equal(diagnosticsForDoc2.diagnostics.length, 1, JSON.stringify(diagnostics));
    })).timeout(10000);
    it('code 6133 (ununsed variable) is ignored', () => __awaiter(void 0, void 0, void 0, function* () {
        server.didChangeConfiguration({
            settings: {
                diagnostics: {
                    ignoredCodes: [6133]
                }
            }
        });
        const doc = {
            uri: (0, test_utils_1.uri)('diagnosticsBar2.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                export function foo() {
                    const x = 42;
                    return 1;
                }
          `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const diagnosticsForThisFile = diagnostics.get(doc.uri);
        assert.isDefined(diagnosticsForThisFile);
        const fileDiagnostics = diagnosticsForThisFile.diagnostics;
        assert.equal(fileDiagnostics.length, 0, JSON.stringify(fileDiagnostics));
    })).timeout(10000);
});
describe('document symbol', () => {
    it('simple test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export class Foo {
          protected foo: string;
          public myFunction(arg: string) {
          }
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const symbols = yield server.documentSymbol({
            textDocument: doc,
            position: lsp.Position.create(1, 1)
        });
        assert.equal(`
Foo
  foo
  myFunction
`, symbolsAsString(symbols) + '\n');
    })).timeout(10000);
    it('merges interfaces correctly', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
interface Box {
    height: number;
    width: number;
}

interface Box {
    scale: number;
}`
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const symbols = yield server.documentSymbol({
            textDocument: doc,
            position: lsp.Position.create(1, 1)
        });
        assert.equal(`
Box
  height
  width
Box
  scale
`, symbolsAsString(symbols) + '\n');
    })).timeout(10000);
    it('duplication test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export class Foo {
          protected foo: string;
          public myFunction(arg: string) {
          }
        }
        export class Foo {
          protected foo: string;
          public myFunction(arg: string) {
          }
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const symbols = yield server.documentSymbol({
            textDocument: doc,
            position: lsp.Position.create(1, 1)
        });
        const expectation = `
Foo
  foo
  myFunction
Foo
  foo
  myFunction
`;
        assert.equal(symbolsAsString(symbols) + '\n', expectation);
        assert.deepEqual(symbols[0].selectionRange, { start: { line: 1, character: 21 }, end: { line: 1, character: 24 } });
        assert.deepEqual(symbols[0].range, { start: { line: 1, character: 8 }, end: { line: 5, character: 9 } });
        assert.deepEqual(symbols[1].selectionRange, symbols[1].range);
        assert.deepEqual(symbols[1].range, { start: { line: 6, character: 8 }, end: { line: 10, character: 9 } });
    })).timeout(10000);
});
function symbolsAsString(symbols, indentation = '') {
    return symbols.map(symbol => {
        let result = '\n' + indentation + symbol.name;
        if (lsp.DocumentSymbol.is(symbol)) {
            if (symbol.children) {
                result = result + symbolsAsString(symbol.children, indentation + '  ');
            }
        }
        else {
            if (symbol.containerName) {
                result = result + ` in ${symbol.containerName}`;
            }
        }
        return result;
    }).join('');
}
describe('editing', () => {
    it('open and change', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('openAndChangeBar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(): void {
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        server.didChangeTextDocument({
            textDocument: doc,
            contentChanges: [
                {
                    text: `
          export function foo(): void {
            missing('test');
          }
          `
                }
            ]
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const resultsForFile = diagnostics.get(doc.uri);
        assert.isDefined(resultsForFile);
        const fileDiagnostics = resultsForFile.diagnostics;
        assert.isTrue(fileDiagnostics.length >= 1, fileDiagnostics.map(d => d.message).join(','));
        assert.equal("Cannot find name 'missing'.", fileDiagnostics[0].message);
    })).timeout(10000);
});
describe('references', () => {
    it('respects "includeDeclaration" in the request', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('foo.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                export let foo = 1;
                foo++;
                foo = 1;
            `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        // Without declaration/definition.
        let references = yield server.references({
            context: { includeDeclaration: false },
            textDocument: doc,
            position: (0, test_utils_1.lastPosition)(doc, 'foo')
        });
        assert.strictEqual(references.length, 2);
        assert.strictEqual(references[0].range.start.line, 2);
        assert.strictEqual(references[1].range.start.line, 3);
        // With declaration/definition.
        references = yield server.references({
            context: { includeDeclaration: true },
            textDocument: doc,
            position: (0, test_utils_1.lastPosition)(doc, 'foo')
        });
        assert.strictEqual(references.length, 3);
    })).timeout(10000);
});
describe('workspace configuration', () => {
    it('receives workspace configuration notification', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                export function foo(): void {
                  console.log('test')
                }
            `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        server.didChangeConfiguration({
            settings: {
                typescript: {
                    format: {
                        insertSpaceAfterCommaDelimiter: true
                    }
                },
                javascript: {
                    format: {
                        insertSpaceAfterCommaDelimiter: false
                    }
                }
            }
        });
        const file = (0, test_utils_1.filePath)('bar.ts');
        const settings = server.getWorkspacePreferencesForDocument(file);
        assert.deepEqual(settings, { format: { insertSpaceAfterCommaDelimiter: true } });
    }));
});
describe('formatting', () => {
    const uriString = (0, test_utils_1.uri)('bar.ts');
    const languageId = 'typescript';
    const version = 1;
    it('full document formatting', () => __awaiter(void 0, void 0, void 0, function* () {
        const text = 'export  function foo (     )   :  void   {   }';
        const textDocument = {
            uri: uriString, languageId, version, text
        };
        server.didOpenTextDocument({ textDocument });
        const edits = yield server.documentFormatting({
            textDocument,
            options: {
                tabSize: 4,
                insertSpaces: true
            }
        });
        const result = vscode_languageserver_textdocument_1.TextDocument.applyEdits(vscode_languageserver_textdocument_1.TextDocument.create(uriString, languageId, version, text), edits);
        assert.equal('export function foo(): void { }', result);
    })).timeout(10000);
    it('indent settings (3 spaces)', () => __awaiter(void 0, void 0, void 0, function* () {
        const text = 'function foo() {\n// some code\n}';
        const textDocument = {
            uri: uriString, languageId, version, text
        };
        server.didOpenTextDocument({ textDocument });
        const edits = yield server.documentFormatting({
            textDocument,
            options: {
                tabSize: 3,
                insertSpaces: true
            }
        });
        const result = vscode_languageserver_textdocument_1.TextDocument.applyEdits(vscode_languageserver_textdocument_1.TextDocument.create(uriString, languageId, version, text), edits);
        assert.equal('function foo() {\n   // some code\n}', result);
    })).timeout(10000);
    it('indent settings (tabs)', () => __awaiter(void 0, void 0, void 0, function* () {
        const text = 'function foo() {\n// some code\n}';
        const textDocument = {
            uri: uriString, languageId, version, text
        };
        server.didOpenTextDocument({ textDocument });
        const edits = yield server.documentFormatting({
            textDocument,
            options: {
                tabSize: 4,
                insertSpaces: false
            }
        });
        const result = vscode_languageserver_textdocument_1.TextDocument.applyEdits(vscode_languageserver_textdocument_1.TextDocument.create(uriString, languageId, version, text), edits);
        assert.equal('function foo() {\n\t// some code\n}', result);
    })).timeout(10000);
    it('formatting setting set through workspace configuration', () => __awaiter(void 0, void 0, void 0, function* () {
        const text = 'function foo() {\n// some code\n}';
        const textDocument = {
            uri: uriString, languageId, version, text
        };
        server.didOpenTextDocument({ textDocument });
        server.didChangeConfiguration({
            settings: {
                typescript: {
                    format: {
                        newLineCharacter: '\n',
                        placeOpenBraceOnNewLineForFunctions: true
                    }
                }
            }
        });
        const edits = yield server.documentFormatting({
            textDocument,
            options: {
                tabSize: 4,
                insertSpaces: false
            }
        });
        const result = vscode_languageserver_textdocument_1.TextDocument.applyEdits(vscode_languageserver_textdocument_1.TextDocument.create(uriString, languageId, version, text), edits);
        assert.equal('function foo()\n{\n\t// some code\n}', result);
    })).timeout(10000);
    it('selected range', () => __awaiter(void 0, void 0, void 0, function* () {
        const text = 'function foo() {\nconst first = 1;\nconst second = 2;\nconst val = foo( "something" );\n//const fourth = 4;\n}';
        const textDocument = {
            uri: uriString, languageId, version, text
        };
        server.didOpenTextDocument({ textDocument });
        const edits = yield server.documentRangeFormatting({
            textDocument,
            range: {
                start: {
                    line: 2,
                    character: 0
                },
                end: {
                    line: 3,
                    character: 30
                }
            },
            options: {
                tabSize: 4,
                insertSpaces: true
            }
        });
        const result = vscode_languageserver_textdocument_1.TextDocument.applyEdits(vscode_languageserver_textdocument_1.TextDocument.create(uriString, languageId, version, text), edits);
        assert.equal('function foo() {\nconst first = 1;\n    const second = 2;\n    const val = foo("something");\n//const fourth = 4;\n}', result);
    })).timeout(10000);
});
describe('signatureHelp', () => {
    it('simple test', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(bar: string, baz?:boolean): void {}
        foo(param1, param2)
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        let result = (yield server.signatureHelp({
            textDocument: doc,
            position: (0, test_utils_1.position)(doc, 'param1')
        }));
        assert.equal('bar: string', result.signatures[result.activeSignature].parameters[result.activeParameter].label);
        result = (yield server.signatureHelp({
            textDocument: doc,
            position: (0, test_utils_1.position)(doc, 'param2')
        }));
        assert.equal('baz?: boolean', result.signatures[result.activeSignature].parameters[result.activeParameter].label);
    })).timeout(10000);
});
describe('code actions', () => {
    const doc = {
        uri: (0, test_utils_1.uri)('bar.ts'),
        languageId: 'typescript',
        version: 1,
        text: `import { something } from "something";
    export function foo(bar: string, baz?:boolean): void {}
    foo(param1, param2)
    `
    };
    it('can provide quickfix code actions', () => __awaiter(void 0, void 0, void 0, function* () {
        server.didOpenTextDocument({
            textDocument: doc
        });
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 1, character: 25 },
                end: { line: 1, character: 49 }
            },
            context: {
                diagnostics: [{
                        range: {
                            start: { line: 1, character: 25 },
                            end: { line: 1, character: 49 }
                        },
                        code: 6133,
                        message: 'unused arg'
                    }]
            }
        }));
        assert.strictEqual(result.length, 2);
        const quickFixDiagnostic = result.find(diagnostic => diagnostic.kind === 'quickfix');
        assert.isDefined(quickFixDiagnostic);
        assert.deepEqual(quickFixDiagnostic, {
            title: "Prefix 'bar' with an underscore",
            command: {
                title: "Prefix 'bar' with an underscore",
                command: '_typescript.applyWorkspaceEdit',
                arguments: [
                    {
                        documentChanges: [
                            {
                                textDocument: {
                                    uri: (0, test_utils_1.uri)('bar.ts'),
                                    version: 1
                                },
                                edits: [
                                    {
                                        range: {
                                            start: {
                                                line: 1,
                                                character: 24
                                            },
                                            end: {
                                                line: 1,
                                                character: 27
                                            }
                                        },
                                        newText: '_bar'
                                    }
                                ]
                            }
                        ]
                    }
                ]
            },
            kind: 'quickfix'
        });
        const refactorDiagnostic = result.find(diagnostic => diagnostic.kind === 'refactor');
        assert.isDefined(refactorDiagnostic);
        assert.deepEqual(refactorDiagnostic, {
            title: 'Convert parameters to destructured object',
            command: {
                title: 'Convert parameters to destructured object',
                command: '_typescript.applyRefactoring',
                arguments: [
                    {
                        file: (0, test_utils_1.filePath)('bar.ts'),
                        startLine: 2,
                        startOffset: 26,
                        endLine: 2,
                        endOffset: 50,
                        refactor: 'Convert parameters to destructured object',
                        action: 'Convert parameters to destructured object'
                    }
                ]
            },
            kind: 'refactor'
        });
    })).timeout(10000);
    it('can filter quickfix code actions filtered by only', () => __awaiter(void 0, void 0, void 0, function* () {
        server.didOpenTextDocument({
            textDocument: doc
        });
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 1, character: 25 },
                end: { line: 1, character: 49 }
            },
            context: {
                diagnostics: [{
                        range: {
                            start: { line: 1, character: 25 },
                            end: { line: 1, character: 49 }
                        },
                        code: 6133,
                        message: 'unused arg'
                    }],
                only: ['refactor', 'invalid-action']
            }
        }));
        assert.deepEqual(result, [
            {
                command: {
                    arguments: [
                        {
                            action: 'Convert parameters to destructured object',
                            endLine: 2,
                            endOffset: 50,
                            file: (0, test_utils_1.filePath)('bar.ts'),
                            refactor: 'Convert parameters to destructured object',
                            startLine: 2,
                            startOffset: 26
                        }
                    ],
                    command: '_typescript.applyRefactoring',
                    title: 'Convert parameters to destructured object'
                },
                kind: 'refactor',
                title: 'Convert parameters to destructured object'
            }
        ]);
    })).timeout(10000);
    it('does not provide organize imports when there are errors', () => __awaiter(void 0, void 0, void 0, function* () {
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 1, character: 29 },
                end: { line: 1, character: 53 }
            },
            context: {
                diagnostics: [{
                        range: {
                            start: { line: 1, character: 25 },
                            end: { line: 1, character: 49 }
                        },
                        code: 6133,
                        message: 'unused arg'
                    }],
                only: [types_1.CodeActionKind.SourceOrganizeImportsTs.value]
            }
        }));
        assert.deepEqual(result, []);
    })).timeout(10000);
    it('provides "add missing imports" when explicitly requested in only', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'existsSync(\'t\');'
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 1, character: 29 },
                end: { line: 1, character: 53 }
            },
            context: {
                diagnostics: [],
                only: [types_1.CodeActionKind.SourceAddMissingImportsTs.value]
            }
        }));
        assert.deepEqual(result, [
            {
                kind: types_1.CodeActionKind.SourceAddMissingImportsTs.value,
                title: 'Add all missing imports',
                edit: {
                    documentChanges: [
                        {
                            edits: [
                                {
                                    // Prefers import that is declared in package.json.
                                    newText: 'import { existsSync } from "fs-extra";\n\n',
                                    range: {
                                        end: {
                                            character: 0,
                                            line: 0
                                        },
                                        start: {
                                            character: 0,
                                            line: 0
                                        }
                                    }
                                }
                            ],
                            textDocument: {
                                uri: (0, test_utils_1.uri)('bar.ts'),
                                version: 1
                            }
                        }
                    ]
                }
            }
        ]);
    })).timeout(10000);
    it('provides "fix all" when explicitly requested in only', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `function foo() {
  return
  setTimeout(() => {})
}`
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 0, character: 0 },
                end: { line: 4, character: 0 }
            },
            context: {
                diagnostics: [],
                only: [types_1.CodeActionKind.SourceFixAllTs.value]
            }
        }));
        assert.deepEqual(result, [
            {
                kind: types_1.CodeActionKind.SourceFixAllTs.value,
                title: 'Fix all',
                edit: {
                    documentChanges: [
                        {
                            edits: [
                                {
                                    newText: '',
                                    range: {
                                        end: {
                                            character: 0,
                                            line: 3
                                        },
                                        start: {
                                            character: 0,
                                            line: 2
                                        }
                                    }
                                }
                            ],
                            textDocument: {
                                uri: (0, test_utils_1.uri)('bar.ts'),
                                version: 1
                            }
                        }
                    ]
                }
            }
        ]);
    })).timeout(10000);
    it('provides organize imports when explicitly requested in only', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `import { existsSync } from 'fs';
import { accessSync } from 'fs';
existsSync('t');`
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 0, character: 0 },
                end: { line: 3, character: 0 }
            },
            context: {
                diagnostics: [{
                        range: {
                            start: { line: 1, character: 25 },
                            end: { line: 1, character: 49 }
                        },
                        code: 6133,
                        message: 'unused arg'
                    }],
                only: [types_1.CodeActionKind.SourceOrganizeImportsTs.value]
            }
        }));
        assert.deepEqual(result, [
            {
                kind: types_1.CodeActionKind.SourceOrganizeImportsTs.value,
                title: 'Organize imports',
                edit: {
                    documentChanges: [
                        {
                            edits: [
                                {
                                    newText: "import { accessSync, existsSync } from 'fs';\n",
                                    range: {
                                        end: {
                                            character: 0,
                                            line: 1
                                        },
                                        start: {
                                            character: 0,
                                            line: 0
                                        }
                                    }
                                },
                                {
                                    newText: '',
                                    range: {
                                        end: {
                                            character: 0,
                                            line: 2
                                        },
                                        start: {
                                            character: 0,
                                            line: 1
                                        }
                                    }
                                }
                            ],
                            textDocument: {
                                uri: (0, test_utils_1.uri)('bar.ts'),
                                version: 1
                            }
                        }
                    ]
                }
            }
        ]);
    })).timeout(10000);
    it('provides "remove unused" when explicitly requested in only', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: 'import { existsSync } from \'fs\';'
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: (0, test_utils_1.position)(doc, 'existsSync'),
                end: (0, test_utils_1.positionAfter)(doc, 'existsSync')
            },
            context: {
                diagnostics: [],
                only: [types_1.CodeActionKind.SourceRemoveUnusedTs.value]
            }
        }));
        assert.deepEqual(result, [
            {
                kind: types_1.CodeActionKind.SourceRemoveUnusedTs.value,
                title: 'Remove all unused code',
                edit: {
                    documentChanges: [
                        {
                            edits: [
                                {
                                    newText: '',
                                    range: {
                                        end: {
                                            character: 32,
                                            line: 0
                                        },
                                        start: {
                                            character: 0,
                                            line: 0
                                        }
                                    }
                                }
                            ],
                            textDocument: {
                                uri: (0, test_utils_1.uri)('bar.ts'),
                                version: 1
                            }
                        }
                    ]
                }
            }
        ]);
    })).timeout(10000);
    it('only provides the "source.fixAll" kind if requested in only', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
                existsSync('x');
                export function foo() {
                    return
                    setTimeout(() => {})
                }
            `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const result = (yield server.codeAction({
            textDocument: doc,
            range: {
                start: { line: 0, character: 0 },
                end: (0, test_utils_1.lastPosition)(doc, '}')
            },
            context: {
                diagnostics: [],
                only: [types_1.CodeActionKind.SourceFixAllTs.value]
            }
        }));
        assert.strictEqual(result.length, 1, JSON.stringify(result, null, 2));
        assert.deepEqual(result, [
            {
                kind: types_1.CodeActionKind.SourceFixAllTs.value,
                title: 'Fix all',
                edit: {
                    documentChanges: [
                        {
                            edits: [
                                {
                                    newText: '',
                                    range: {
                                        start: {
                                            line: 4,
                                            character: 0
                                        },
                                        end: {
                                            line: 5,
                                            character: 0
                                        }
                                    }
                                }
                            ],
                            textDocument: {
                                uri: (0, test_utils_1.uri)('bar.ts'),
                                version: 1
                            }
                        }
                    ]
                }
            }
        ]);
    })).timeout(10000);
});
describe('documentHighlight', () => {
    it('simple test', () => __awaiter(void 0, void 0, void 0, function* () {
        const barDoc = {
            uri: (0, test_utils_1.uri)('bar.d.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export declare const Bar: unique symbol;
        export interface Bar {
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: barDoc
        });
        const fooDoc = {
            uri: (0, test_utils_1.uri)('bar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        import { Bar } from './bar';
        export class Foo implements Bar {
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: fooDoc
        });
        const result = yield server.documentHighlight({
            textDocument: fooDoc,
            position: (0, test_utils_1.lastPosition)(fooDoc, 'Bar')
        });
        assert.equal(2, result.length, JSON.stringify(result, undefined, 2));
    })).timeout(10000);
});
describe('calls', () => {
    function resultToString(callsResult, direction) {
        if (!callsResult.symbol) {
            if (callsResult.calls.length > 0) {
                return '<unexpected calls>';
            }
            return '<symbol not found>';
        }
        const arrow = lspcalls.CallDirection.Outgoing === direction ? '↖' : '↘';
        const symbolToString = (symbol) => `${symbol.name} (${symbol.location.uri.split('/').pop()}#${symbol.selectionRange.start.line})`;
        const out = [];
        out.push(`${arrow} ${symbolToString(callsResult.symbol)}`);
        for (const call of callsResult.calls) {
            out.push(`  ${arrow} ${symbolToString(call.symbol)} - ${call.location.uri.split('/').pop()}#${call.location.range.start.line}`);
        }
        return out.join('\n');
    }
    const doDoc = {
        uri: (0, test_utils_1.uri)('do.ts'),
        languageId: 'typescript',
        version: 1,
        text: `
export function doStuff(): boolean {
    return two() !== undefined;
}
export function two() {
    three();
    const ttt = three;
    return ttt();
}
export function three() {
    return "".toString();
}
`
    };
    const fooDoc = {
        uri: (0, test_utils_1.uri)('foo.ts'),
        languageId: 'typescript',
        version: 1,
        text: `import { doStuff } from './do';
class MyClass {
    doSomething() {
        doStuff();
        const x = doStuff();
        function f() {};
    }
}
export function factory() {
    new MyClass().doSomething();
}
`
    };
    function openDocuments() {
        server.didOpenTextDocument({
            textDocument: doDoc
        });
        server.didOpenTextDocument({
            textDocument: fooDoc
        });
    }
    it('callers: first step', () => __awaiter(void 0, void 0, void 0, function* () {
        openDocuments();
        const callsResult = yield server.calls({
            textDocument: fooDoc,
            position: lsp.Position.create(3, 9)
        });
        assert.equal(resultToString(callsResult, lspcalls.CallDirection.Incoming), `
↘ doStuff (do.ts#1)
  ↘ doStuff (foo.ts#0) - foo.ts#0
  ↘ doSomething (foo.ts#2) - foo.ts#3
  ↘ x (foo.ts#4) - foo.ts#4
            `.trim());
    })).timeout(10000);
    it('callers: second step', () => __awaiter(void 0, void 0, void 0, function* () {
        openDocuments();
        const callsResult = yield server.calls({
            textDocument: fooDoc,
            position: lsp.Position.create(2, 5)
        });
        assert.equal(resultToString(callsResult, lspcalls.CallDirection.Incoming), `
↘ doSomething (foo.ts#2)
  ↘ factory (foo.ts#8) - foo.ts#9
            `.trim());
    })).timeout(10000);
    it('callees: first step', () => __awaiter(void 0, void 0, void 0, function* () {
        openDocuments();
        const direction = lspcalls.CallDirection.Outgoing;
        const callsResult = yield server.calls({
            direction,
            textDocument: fooDoc,
            position: lsp.Position.create(3, 9)
        });
        assert.equal(resultToString(callsResult, direction), `
↖ doStuff (do.ts#1)
  ↖ two (do.ts#4) - do.ts#2
            `.trim());
    })).timeout(10000);
    it('callees: second step', () => __awaiter(void 0, void 0, void 0, function* () {
        openDocuments();
        const direction = lspcalls.CallDirection.Outgoing;
        const callsResult = yield server.calls({
            direction,
            textDocument: doDoc,
            position: lsp.Position.create(4, 17)
        });
        assert.equal(resultToString(callsResult, direction), `
↖ two (do.ts#4)
  ↖ three (do.ts#9) - do.ts#5
  ↖ ttt (do.ts#6) - do.ts#7
            `.trim());
    })).timeout(10000);
});
describe('diagnostics (no client support)', () => {
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        var _a;
        // Remove the "textDocument.publishDiagnostics" client capability.
        const clientCapabilitiesOverride = (0, test_utils_1.getDefaultClientCapabilities)();
        (_a = clientCapabilitiesOverride.textDocument) === null || _a === void 0 ? true : delete _a.publishDiagnostics;
        server = yield (0, test_utils_1.createServer)({
            rootUri: null,
            publishDiagnostics: args => diagnostics.set(args.uri, args),
            clientCapabilitiesOverride
        });
    }));
    it('diagnostics are published', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('diagnosticsBar.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo(): void {
          missing('test')
        }
      `
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        yield server.requestDiagnostics();
        yield new Promise(resolve => setTimeout(resolve, 200));
        const resultsForFile = diagnostics.get(doc.uri);
        assert.isDefined(resultsForFile);
        assert.strictEqual(resultsForFile === null || resultsForFile === void 0 ? void 0 : resultsForFile.diagnostics.length, 1);
    })).timeout(10000);
});
describe('jsx/tsx project', () => {
    before(() => __awaiter(void 0, void 0, void 0, function* () {
        server = yield (0, test_utils_1.createServer)({
            rootUri: (0, test_utils_1.uri)('jsx'),
            publishDiagnostics: args => diagnostics.set(args.uri, args)
        });
    }));
    it('includes snippet completion for element prop', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('jsx', 'app.tsx'),
            languageId: 'typescriptreact',
            version: 1,
            text: (0, test_utils_1.readContents)((0, test_utils_1.filePath)('jsx', 'app.tsx'))
        };
        server.didOpenTextDocument({
            textDocument: doc
        });
        const completion = yield server.completion({ textDocument: doc, position: (0, test_utils_1.position)(doc, 'title') });
        assert.isNotNull(completion);
        const item = completion.items.find(i => i.label === 'title');
        assert.isDefined(item);
        assert.strictEqual(item === null || item === void 0 ? void 0 : item.insertTextFormat, 2);
    })).timeout(10000);
});
describe('inlayHints', () => {
    it('inlayHints', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('module.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo() {
          return 3
        }
      `
        };
        yield server.initialize({
            initializationOptions: {
                preferences: {
                    includeInlayFunctionLikeReturnTypeHints: true
                }
            },
            processId: null,
            capabilities: (0, test_utils_1.getDefaultClientCapabilities)(),
            workspaceFolders: [],
            rootUri: ''
        });
        server.didOpenTextDocument({
            textDocument: doc
        });
        const { inlayHints } = yield server.inlayHints({
            textDocument: doc
        });
        assert.isDefined(inlayHints);
        assert.strictEqual(inlayHints.length, 1);
        assert.strictEqual(inlayHints[0].text, ': number');
        assert.strictEqual(inlayHints[0].kind, 'Type');
        assert.deepStrictEqual(inlayHints[0].position, { line: 1, character: 29 });
    })).timeout(10000);
    it('inlayHints options set through workspace configuration ', () => __awaiter(void 0, void 0, void 0, function* () {
        const doc = {
            uri: (0, test_utils_1.uri)('module.ts'),
            languageId: 'typescript',
            version: 1,
            text: `
        export function foo() {
          return 3
        }
      `
        };
        yield server.initialize({
            processId: null,
            capabilities: (0, test_utils_1.getDefaultClientCapabilities)(),
            workspaceFolders: [],
            rootUri: ''
        });
        server.didChangeConfiguration({
            settings: {
                typescript: {
                    inlayHints: {
                        includeInlayFunctionLikeReturnTypeHints: true
                    }
                }
            }
        });
        server.didOpenTextDocument({
            textDocument: doc
        });
        const { inlayHints } = yield server.inlayHints({
            textDocument: doc
        });
        assert.isDefined(inlayHints);
        assert.strictEqual(inlayHints.length, 1);
        assert.strictEqual(inlayHints[0].text, ': number');
        assert.strictEqual(inlayHints[0].kind, 'Type');
        assert.deepStrictEqual(inlayHints[0].position, { line: 1, character: 29 });
    })).timeout(10000);
});
//# sourceMappingURL=lsp-server.spec.js.map