280 changed files with 60510 additions and 76507 deletions
@ -1,7 +1,46 @@
@@ -1,7 +1,46 @@
|
||||
Copyright 2018-2018 by Qiming Zhao <chemzqm@gmail.com> |
||||
Copyright (c) <2022> <chemzqm@gmail.com> |
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
||||
"Anti 996" License Version 1.0 (Draft) |
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
||||
Permission is hereby granted to any individual or legal entity |
||||
obtaining a copy of this licensed work (including the source code, |
||||
documentation and/or related items, hereinafter collectively referred |
||||
to as the "licensed work"), free of charge, to deal with the licensed |
||||
work for any purpose, including without limitation, the rights to use, |
||||
reproduce, modify, prepare derivative works of, distribute, publish |
||||
and sublicense the licensed work, subject to the following conditions: |
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
1. The individual or the legal entity must conspicuously display, |
||||
without modification, this License and the notice on each redistributed |
||||
or derivative copy of the Licensed Work. |
||||
|
||||
2. The individual or the legal entity must strictly comply with all |
||||
applicable laws, regulations, rules and standards of the jurisdiction |
||||
relating to labor and employment where the individual is physically |
||||
located or where the individual was born or naturalized; or where the |
||||
legal entity is registered or is operating (whichever is stricter). In |
||||
case that the jurisdiction has no such laws, regulations, rules and |
||||
standards or its laws, regulations, rules and standards are |
||||
unenforceable, the individual or the legal entity are required to |
||||
comply with Core International Labor Standards. |
||||
|
||||
3. The individual or the legal entity shall not induce, suggest or force |
||||
its employee(s), whether full-time or part-time, or its independent |
||||
contractor(s), in any methods, to agree in oral or written form, to |
||||
directly or indirectly restrict, weaken or relinquish his or her |
||||
rights or remedies under such laws, regulations, rules and standards |
||||
relating to labor and employment as mentioned above, no matter whether |
||||
such written or oral agreements are enforceable under the laws of the |
||||
said jurisdiction, nor shall such individual or the legal entity |
||||
limit, in any methods, the rights of its employee(s) or independent |
||||
contractor(s) from reporting or complaining to the copyright holder or |
||||
relevant authorities monitoring the compliance of the license about |
||||
its violation(s) of the said license. |
||||
|
||||
THE LICENSED WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, |
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
||||
OTHERWISE, ARISING FROM, OUT OF OR IN ANY WAY CONNECTION WITH THE |
||||
LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE LICENSED WORK. |
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +0,0 @@
@@ -1,17 +0,0 @@
|
||||
" vim source for emails |
||||
function! coc#source#email#init() abort |
||||
return { |
||||
\ 'priority': 9, |
||||
\ 'shortcut': 'Email', |
||||
\ 'triggerCharacters': ['@'] |
||||
\} |
||||
endfunction |
||||
|
||||
function! coc#source#email#should_complete(opt) abort |
||||
return 1 |
||||
endfunction |
||||
|
||||
function! coc#source#email#complete(opt, cb) abort |
||||
let items = ['foo@gmail.com', 'bar@yahoo.com'] |
||||
call a:cb(items) |
||||
endfunction |
@ -1,62 +0,0 @@
@@ -1,62 +0,0 @@
|
||||
/* eslint-disable */ |
||||
import helper from '../helper' |
||||
// import * as assert from 'assert'
|
||||
import fs from 'fs' |
||||
import * as lsclient from '../../language-client' |
||||
import * as path from 'path' |
||||
import { URI } from 'vscode-uri' |
||||
// import which from 'which'
|
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Client integration', () => { |
||||
|
||||
it('should send file change notification', (done) => { |
||||
if (global.hasOwnProperty('__TEST__')) return done() |
||||
let serverModule = path.join(__dirname, './server/testFileWatcher.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
module: serverModule, |
||||
transport: lsclient.TransportKind.ipc |
||||
} |
||||
let clientOptions: lsclient.LanguageClientOptions = { |
||||
documentSelector: ['css'], |
||||
synchronize: {}, initializationOptions: {}, |
||||
middleware: { |
||||
} |
||||
} |
||||
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions) |
||||
let disposable = client.start() |
||||
|
||||
client.onReady().then(_ => { |
||||
setTimeout(async () => { |
||||
let file = path.join(__dirname, 'test.js') |
||||
fs.writeFileSync(file, '', 'utf8') |
||||
await helper.wait(300) |
||||
let res = await client.sendRequest('custom/received') |
||||
expect(res).toEqual({ |
||||
changes: [{ |
||||
uri: URI.file(file).toString(), |
||||
type: 1 |
||||
}] |
||||
}) |
||||
fs.unlinkSync(file) |
||||
disposable.dispose() |
||||
done() |
||||
}, 200) |
||||
}, e => { |
||||
disposable.dispose() |
||||
done(e) |
||||
}) |
||||
}) |
||||
|
||||
}) |
@ -1,140 +0,0 @@
@@ -1,140 +0,0 @@
|
||||
import { Duplex } from 'stream' |
||||
import { ProgressType } from 'vscode-jsonrpc' |
||||
import { createProtocolConnection, DocumentSymbolParams, DocumentSymbolRequest, InitializeParams, InitializeRequest, InitializeResult, ProtocolConnection, StreamMessageReader, StreamMessageWriter } from 'vscode-languageserver-protocol/node' |
||||
import { SymbolInformation, SymbolKind } from 'vscode-languageserver-types' |
||||
import { NullLogger } from '../../language-client/client' |
||||
|
||||
class TestStream extends Duplex { |
||||
public _write(chunk: string, _encoding: string, done: () => void): void { |
||||
this.emit('data', chunk) |
||||
done() |
||||
} |
||||
|
||||
public _read(_size: number): void { |
||||
} |
||||
} |
||||
|
||||
let serverConnection: ProtocolConnection |
||||
let clientConnection: ProtocolConnection |
||||
let progressType: ProgressType<any> = new ProgressType() |
||||
|
||||
beforeEach(() => { |
||||
const up = new TestStream() |
||||
const down = new TestStream() |
||||
const logger = new NullLogger() |
||||
serverConnection = createProtocolConnection(new StreamMessageReader(up), new StreamMessageWriter(down), logger) |
||||
clientConnection = createProtocolConnection(new StreamMessageReader(down), new StreamMessageWriter(up), logger) |
||||
serverConnection.listen() |
||||
clientConnection.listen() |
||||
}) |
||||
|
||||
afterEach(() => { |
||||
serverConnection.dispose() |
||||
clientConnection.dispose() |
||||
}) |
||||
|
||||
describe('Connection Tests', () => { |
||||
it('should ensure proper param passing', async () => { |
||||
let paramsCorrect = false |
||||
serverConnection.onRequest(InitializeRequest.type, params => { |
||||
paramsCorrect = !Array.isArray(params) |
||||
let result: InitializeResult = { |
||||
capabilities: { |
||||
} |
||||
} |
||||
return result |
||||
}) |
||||
|
||||
const init: InitializeParams = { |
||||
rootUri: 'file:///home/dirkb', |
||||
processId: 1, |
||||
capabilities: {}, |
||||
workspaceFolders: null, |
||||
} |
||||
await clientConnection.sendRequest(InitializeRequest.type, init) |
||||
expect(paramsCorrect).toBe(true) |
||||
}) |
||||
|
||||
it('should provid token', async () => { |
||||
serverConnection.onRequest(DocumentSymbolRequest.type, params => { |
||||
expect(params.partialResultToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2') |
||||
return [] |
||||
}) |
||||
|
||||
const params: DocumentSymbolParams = { |
||||
textDocument: { uri: 'file:///abc.txt' }, |
||||
partialResultToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2' |
||||
} |
||||
await clientConnection.sendRequest(DocumentSymbolRequest.type, params) |
||||
}) |
||||
|
||||
it('should report result', async () => { |
||||
let result: SymbolInformation = { |
||||
name: 'abc', |
||||
kind: SymbolKind.Class, |
||||
location: { |
||||
uri: 'file:///abc.txt', |
||||
range: { start: { line: 0, character: 1 }, end: { line: 2, character: 3 } } |
||||
} |
||||
} |
||||
serverConnection.onRequest(DocumentSymbolRequest.type, params => { |
||||
expect(params.partialResultToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2') |
||||
serverConnection.sendProgress(progressType, params.partialResultToken, [result]) |
||||
return [] |
||||
}) |
||||
|
||||
const params: DocumentSymbolParams = { |
||||
textDocument: { uri: 'file:///abc.txt' }, |
||||
partialResultToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2' |
||||
} |
||||
let progressOK = false |
||||
clientConnection.onProgress(progressType, '3b1db4c9-e011-489e-a9d1-0653e64707c2', values => { |
||||
progressOK = (values !== undefined && values.length === 1) |
||||
}) |
||||
await clientConnection.sendRequest(DocumentSymbolRequest.type, params) |
||||
expect(progressOK).toBeTruthy() |
||||
}) |
||||
|
||||
it('should provide workDoneToken', async () => { |
||||
serverConnection.onRequest(DocumentSymbolRequest.type, params => { |
||||
expect(params.workDoneToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2') |
||||
return [] |
||||
}) |
||||
|
||||
const params: DocumentSymbolParams = { |
||||
textDocument: { uri: 'file:///abc.txt' }, |
||||
workDoneToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2' |
||||
} |
||||
await clientConnection.sendRequest(DocumentSymbolRequest.type, params) |
||||
}) |
||||
|
||||
it('should report work done progress', async () => { |
||||
serverConnection.onRequest(DocumentSymbolRequest.type, params => { |
||||
expect(params.workDoneToken).toBe('3b1db4c9-e011-489e-a9d1-0653e64707c2') |
||||
serverConnection.sendProgress(progressType, params.workDoneToken, { |
||||
kind: 'begin', |
||||
title: 'progress' |
||||
}) |
||||
serverConnection.sendProgress(progressType, params.workDoneToken, { |
||||
kind: 'report', |
||||
message: 'message' |
||||
}) |
||||
serverConnection.sendProgress(progressType, params.workDoneToken, { |
||||
kind: 'end', |
||||
message: 'message' |
||||
}) |
||||
return [] |
||||
}) |
||||
|
||||
const params: DocumentSymbolParams = { |
||||
textDocument: { uri: 'file:///abc.txt' }, |
||||
workDoneToken: '3b1db4c9-e011-489e-a9d1-0653e64707c2' |
||||
} |
||||
let result = '' |
||||
clientConnection.onProgress(progressType, '3b1db4c9-e011-489e-a9d1-0653e64707c2', value => { |
||||
result += value.kind |
||||
}) |
||||
await clientConnection.sendRequest(DocumentSymbolRequest.type, params) |
||||
expect(result).toBe('beginreportend') |
||||
}) |
||||
}) |
@ -1,89 +0,0 @@
@@ -1,89 +0,0 @@
|
||||
import { CompletionTriggerKind, Position, TextDocumentItem, TextDocumentSaveReason } from 'vscode-languageserver-protocol' |
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import { URI } from 'vscode-uri' |
||||
import * as cv from '../../language-client/utils/converter' |
||||
|
||||
describe('converter', () => { |
||||
|
||||
function createDocument(): TextDocument { |
||||
return TextDocument.create('file:///1', 'css', 1, '') |
||||
} |
||||
|
||||
it('should asLanguageIds', () => { |
||||
let selector = ['css', { language: 'javascript' }] |
||||
expect(cv.asLanguageIds(selector)).toEqual(['css', 'javascript']) |
||||
}) |
||||
|
||||
it('should convertToTextDocumentItem', () => { |
||||
let doc = createDocument() |
||||
expect(cv.convertToTextDocumentItem(doc).uri).toBe(doc.uri) |
||||
expect(TextDocumentItem.is(cv.convertToTextDocumentItem(doc))).toBe(true) |
||||
}) |
||||
|
||||
it('should asCloseTextDocumentParams', () => { |
||||
let doc = createDocument() |
||||
expect(cv.asCloseTextDocumentParams(doc).textDocument.uri).toBe(doc.uri) |
||||
}) |
||||
|
||||
it('should asChangeTextDocumentParams', () => { |
||||
let doc = createDocument() |
||||
expect(cv.asChangeTextDocumentParams(doc).textDocument.uri).toBe(doc.uri) |
||||
}) |
||||
|
||||
it('should asWillSaveTextDocumentParams', () => { |
||||
let res = cv.asWillSaveTextDocumentParams({ document: createDocument(), reason: TextDocumentSaveReason.Manual, waitUntil: () => { } }) |
||||
expect(res.textDocument).toBeDefined() |
||||
expect(res.reason).toBeDefined() |
||||
}) |
||||
|
||||
it('should asVersionedTextDocumentIdentifier', () => { |
||||
let res = cv.asVersionedTextDocumentIdentifier(createDocument()) |
||||
expect(res.uri).toBeDefined() |
||||
expect(res.version).toBeDefined() |
||||
}) |
||||
|
||||
it('should asSaveTextDocumentParams', () => { |
||||
let res = cv.asSaveTextDocumentParams(createDocument(), true) |
||||
expect(res.textDocument.uri).toBeDefined() |
||||
expect(res.text).toBeDefined() |
||||
}) |
||||
|
||||
it('should asUri', () => { |
||||
let uri = URI.file('/tmp/a') |
||||
expect(cv.asUri(uri)).toBe(uri.toString()) |
||||
}) |
||||
|
||||
it('should asCompletionParams', () => { |
||||
let params = cv.asCompletionParams(createDocument(), Position.create(0, 0), { triggerKind: CompletionTriggerKind.Invoked }) |
||||
expect(params.textDocument).toBeDefined() |
||||
expect(params.position).toBeDefined() |
||||
expect(params.context).toBeDefined() |
||||
}) |
||||
|
||||
it('should asTextDocumentPositionParams', () => { |
||||
let params = cv.asTextDocumentPositionParams(createDocument(), Position.create(0, 0)) |
||||
expect(params.textDocument).toBeDefined() |
||||
expect(params.position).toBeDefined() |
||||
}) |
||||
|
||||
it('should asTextDocumentIdentifier', () => { |
||||
let doc = cv.asTextDocumentIdentifier(createDocument()) |
||||
expect(doc.uri).toBeDefined() |
||||
}) |
||||
|
||||
it('should asReferenceParams', () => { |
||||
let params = cv.asReferenceParams(createDocument(), Position.create(0, 0), { includeDeclaration: false }) |
||||
expect(params.textDocument.uri).toBeDefined() |
||||
expect(params.position).toBeDefined() |
||||
}) |
||||
|
||||
it('should asDocumentSymbolParams', () => { |
||||
let doc = cv.asDocumentSymbolParams(createDocument()) |
||||
expect(doc.textDocument.uri).toBeDefined() |
||||
}) |
||||
|
||||
it('should asCodeLensParams', () => { |
||||
let doc = cv.asCodeLensParams(createDocument()) |
||||
expect(doc.textDocument.uri).toBeDefined() |
||||
}) |
||||
}) |
@ -1,154 +0,0 @@
@@ -1,154 +0,0 @@
|
||||
/* eslint-disable */ |
||||
import assert from 'assert' |
||||
import { Delayer } from '../../language-client/utils/async' |
||||
|
||||
test('Delayer', () => { |
||||
let count = 0 |
||||
let factory = () => { |
||||
return Promise.resolve(++count) |
||||
} |
||||
|
||||
let delayer = new Delayer(0) |
||||
let promises: Thenable<any>[] = [] |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then((result) => { assert.equal(result, 1); assert(!delayer.isTriggered()) })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
return Promise.all(promises).then(() => { |
||||
assert(!delayer.isTriggered()) |
||||
}).finally(() => { |
||||
delayer.dispose() |
||||
}) |
||||
}) |
||||
|
||||
/* |
||||
test('Delayer - simple cancel', async () => { |
||||
let count = 0 |
||||
let factory = () => { |
||||
return Promise.resolve(++count) |
||||
} |
||||
|
||||
let delayer = new Delayer(10) |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
const p = delayer.trigger(factory).then(() => { |
||||
assert(false) |
||||
}, () => { |
||||
assert(true, 'yes, it was cancelled') |
||||
}) |
||||
assert(delayer.isTriggered()) |
||||
delayer.cancel() |
||||
assert(!delayer.isTriggered()) |
||||
await p |
||||
}) |
||||
|
||||
test('Delayer - cancel should cancel all calls to trigger', function() { |
||||
let count = 0 |
||||
let factory = () => { |
||||
return Promise.resolve(++count) |
||||
} |
||||
|
||||
let delayer = new Delayer(0) |
||||
let promises: Thenable<any>[] = [] |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
delayer.cancel() |
||||
|
||||
return Promise.all(promises).then(() => { |
||||
assert(!delayer.isTriggered()) |
||||
}) |
||||
}) |
||||
|
||||
test('Delayer - trigger, cancel, then trigger again', function() { |
||||
let count = 0 |
||||
let factory = () => { |
||||
return Promise.resolve(++count) |
||||
} |
||||
|
||||
let delayer = new Delayer(0) |
||||
let promises: Thenable<any>[] = [] |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
const p = delayer.trigger(factory).then((result) => { |
||||
assert.equal(result, 1) |
||||
assert(!delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(null, () => { assert(true, 'yes, it was cancelled') })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
delayer.cancel() |
||||
|
||||
const p = Promise.all(promises).then(() => { |
||||
promises = [] |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()) })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factory).then(() => { assert.equal(result, 1); assert(!delayer.isTriggered()) })) |
||||
assert(delayer.isTriggered()) |
||||
|
||||
const p = Promise.all(promises).then(() => { |
||||
assert(!delayer.isTriggered()) |
||||
}) |
||||
|
||||
assert(delayer.isTriggered()) |
||||
|
||||
return p |
||||
}) |
||||
|
||||
return p |
||||
}) |
||||
|
||||
assert(delayer.isTriggered()) |
||||
|
||||
return p |
||||
}) |
||||
*/ |
||||
|
||||
test('Delayer - last task should be the one getting called', function() { |
||||
let factoryFactory = (n: number) => () => { |
||||
return Promise.resolve(n) |
||||
} |
||||
|
||||
let delayer = new Delayer(0) |
||||
let promises: Thenable<any>[] = [] |
||||
|
||||
assert(!delayer.isTriggered()) |
||||
|
||||
promises.push(delayer.trigger(factoryFactory(1)).then((n) => { assert.equal(n, 3) })) |
||||
promises.push(delayer.trigger(factoryFactory(2)).then((n) => { assert.equal(n, 3) })) |
||||
promises.push(delayer.trigger(factoryFactory(3)).then((n) => { assert.equal(n, 3) })) |
||||
|
||||
const p = Promise.all(promises).then(() => { |
||||
assert(!delayer.isTriggered()) |
||||
}) |
||||
|
||||
assert(delayer.isTriggered()) |
||||
|
||||
return p |
||||
}) |
File diff suppressed because it is too large
Load Diff
@ -1,128 +0,0 @@
@@ -1,128 +0,0 @@
|
||||
/* eslint-disable */ |
||||
/*--------------------------------------------------------------------------------------------- |
||||
* Copyright (c) Microsoft Corporation. All rights reserved. |
||||
* Licensed under the MIT License. See License.txt in the project root for license information. |
||||
*--------------------------------------------------------------------------------------------*/ |
||||
'use strict' |
||||
|
||||
import helper from '../helper' |
||||
import * as assert from 'assert' |
||||
import * as lsclient from '../../language-client' |
||||
import path from 'path' |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
async function testLanguageServer(serverOptions: lsclient.ServerOptions): Promise<void> { |
||||
let clientOptions: lsclient.LanguageClientOptions = { |
||||
documentSelector: ['css'], |
||||
synchronize: {}, |
||||
initializationOptions: {} |
||||
} |
||||
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions) |
||||
client.start() |
||||
await client.onReady() |
||||
expect(client.initializeResult).toBeDefined() |
||||
} |
||||
|
||||
describe('Client integration', () => { |
||||
|
||||
it('should initialize use IPC channel', (done) => { |
||||
let serverModule = path.join(__dirname, './server/testInitializeResult.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
run: { module: serverModule, transport: lsclient.TransportKind.ipc }, |
||||
debug: { module: serverModule, transport: lsclient.TransportKind.ipc, options: { execArgv: ['--nolazy', '--inspect=6014'] } } |
||||
} |
||||
let clientOptions: lsclient.LanguageClientOptions = { |
||||
documentSelector: ['css'], |
||||
synchronize: {}, initializationOptions: {}, |
||||
middleware: { |
||||
handleDiagnostics: (uri, diagnostics, next) => { |
||||
assert.equal(uri, "uri:/test.ts") |
||||
assert.ok(Array.isArray(diagnostics)) |
||||
assert.equal(diagnostics.length, 0) |
||||
next(uri, diagnostics) |
||||
disposable.dispose() |
||||
done() |
||||
} |
||||
} |
||||
} |
||||
let client = new lsclient.LanguageClient('css', 'Test Language Server', serverOptions, clientOptions) |
||||
let disposable = client.start() |
||||
|
||||
assert.equal(client.initializeResult, undefined) |
||||
|
||||
client.onReady().then(_ => { |
||||
try { |
||||
let expected = { |
||||
capabilities: { |
||||
textDocumentSync: 1, |
||||
completionProvider: { resolveProvider: true, triggerCharacters: ['"', ':'] }, |
||||
hoverProvider: true, |
||||
renameProvider: { |
||||
prepareProvider: true |
||||
} |
||||
}, |
||||
customResults: { |
||||
"hello": "world" |
||||
} |
||||
} |
||||
assert.deepEqual(client.initializeResult, expected) |
||||
} catch (e) { |
||||
disposable.dispose() |
||||
done(e) |
||||
} |
||||
}, e => { |
||||
disposable.dispose() |
||||
done(e) |
||||
}) |
||||
}) |
||||
|
||||
it('should initialize use stdio', async () => { |
||||
let serverModule = path.join(__dirname, './server/testInitializeResult.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
module: serverModule, |
||||
transport: lsclient.TransportKind.stdio |
||||
} |
||||
await testLanguageServer(serverOptions) |
||||
}) |
||||
|
||||
it('should initialize use pipe', async () => { |
||||
let serverModule = path.join(__dirname, './server/testInitializeResult.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
module: serverModule, |
||||
transport: lsclient.TransportKind.pipe |
||||
} |
||||
await testLanguageServer(serverOptions) |
||||
}) |
||||
|
||||
it('should initialize use socket', async () => { |
||||
let serverModule = path.join(__dirname, './server/testInitializeResult.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
module: serverModule, |
||||
transport: { |
||||
kind: lsclient.TransportKind.socket, |
||||
port: 8088 |
||||
} |
||||
} |
||||
await testLanguageServer(serverOptions) |
||||
}) |
||||
|
||||
it('should initialize as command', async () => { |
||||
let serverModule = path.join(__dirname, './server/testInitializeResult.js') |
||||
let serverOptions: lsclient.ServerOptions = { |
||||
command: 'node', |
||||
args: [serverModule, '--stdio'] |
||||
} |
||||
await testLanguageServer(serverOptions) |
||||
}) |
||||
}) |
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
const languageserver = require('vscode-languageserver') |
||||
let connection = languageserver.createConnection() |
||||
let documents = new languageserver.TextDocuments() |
||||
documents.listen(connection) |
||||
|
||||
connection.onInitialize(() => { |
||||
let capabilities = { |
||||
textDocumentSync: documents.syncKind |
||||
} |
||||
return { capabilities } |
||||
}) |
||||
|
||||
connection.onInitialized(() => { |
||||
connection.sendRequest('client/registerCapability', { |
||||
registrations: [{ |
||||
id: 'didChangeWatchedFiles', |
||||
method: 'workspace/didChangeWatchedFiles', |
||||
registerOptions: { |
||||
watchers: [{ globPattern: "**" }] |
||||
} |
||||
}] |
||||
}) |
||||
}) |
||||
|
||||
let received |
||||
|
||||
connection.onNotification('workspace/didChangeWatchedFiles', params => { |
||||
received = params |
||||
}) |
||||
|
||||
connection.onRequest('custom/received', async () => { |
||||
return received |
||||
}) |
||||
|
||||
connection.listen() |
@ -1,40 +0,0 @@
@@ -1,40 +0,0 @@
|
||||
/*--------------------------------------------------------------------------------------------- |
||||
* Copyright (c) Microsoft Corporation. All rights reserved. |
||||
* Licensed under the MIT License. See License.txt in the project root for license information. |
||||
*--------------------------------------------------------------------------------------------*/ |
||||
'use strict' |
||||
Object.defineProperty(exports, "__esModule", {value: true}) |
||||
const tslib_1 = require("tslib") |
||||
const assert = tslib_1.__importStar(require("assert")) |
||||
const vscode_languageserver_1 = require("vscode-languageserver") |
||||
let connection = vscode_languageserver_1.createConnection() |
||||
|
||||
let documents = new vscode_languageserver_1.TextDocuments() |
||||
documents.listen(connection) |
||||
connection.onInitialize((params) => { |
||||
assert.equal(params.capabilities.workspace.applyEdit, true) |
||||
assert.equal(params.capabilities.workspace.workspaceEdit.documentChanges, true) |
||||
assert.deepEqual(params.capabilities.workspace.workspaceEdit.resourceOperations, [vscode_languageserver_1.ResourceOperationKind.Create, vscode_languageserver_1.ResourceOperationKind.Rename, vscode_languageserver_1.ResourceOperationKind.Delete]) |
||||
assert.equal(params.capabilities.workspace.workspaceEdit.failureHandling, vscode_languageserver_1.FailureHandlingKind.TextOnlyTransactional) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.deprecatedSupport, true) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.preselectSupport, true) |
||||
assert.equal(params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport, true) |
||||
assert.equal(params.capabilities.textDocument.rename.prepareSupport, true) |
||||
let valueSet = params.capabilities.textDocument.completion.completionItemKind.valueSet |
||||
assert.equal(valueSet[0], 1) |
||||
assert.equal(valueSet[valueSet.length - 1], vscode_languageserver_1.CompletionItemKind.TypeParameter) |
||||
let capabilities = { |
||||
textDocumentSync: documents.syncKind, |
||||
completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']}, |
||||
hoverProvider: true, |
||||
renameProvider: { |
||||
prepareProvider: true |
||||
} |
||||
} |
||||
return {capabilities, customResults: {"hello": "world"}} |
||||
}) |
||||
connection.onInitialized(() => { |
||||
connection.sendDiagnostics({uri: "uri:/test.ts", diagnostics: []}) |
||||
}) |
||||
// Listen on the connection
|
||||
connection.listen() |
@ -1,414 +0,0 @@
@@ -1,414 +0,0 @@
|
||||
/*--------------------------------------------------------------------------------------------- |
||||
* Copyright (c) Microsoft Corporation. All rights reserved. |
||||
* Licensed under the MIT License. See License.txt in the project root for license information. |
||||
*--------------------------------------------------------------------------------------------*/ |
||||
|
||||
const assert = require('assert') |
||||
const {URI} = require('vscode-uri') |
||||
const { |
||||
createConnection, CompletionItemKind, ResourceOperationKind, FailureHandlingKind, |
||||
DiagnosticTag, CompletionItemTag, TextDocumentSyncKind, MarkupKind, SignatureInformation, ParameterInformation, |
||||
Location, Range, DocumentHighlight, DocumentHighlightKind, CodeAction, Command, TextEdit, Position, DocumentLink, |
||||
ColorInformation, Color, ColorPresentation, FoldingRange, SelectionRange, SymbolKind, ProtocolRequestType, WorkDoneProgress, |
||||
WorkDoneProgressCreateRequest} = require('vscode-languageserver') |
||||
|
||||
const { |
||||
DidCreateFilesNotification, |
||||
DidRenameFilesNotification, |
||||
DidDeleteFilesNotification, |
||||
WillCreateFilesRequest, WillRenameFilesRequest, WillDeleteFilesRequest |
||||
} = require('vscode-languageserver-protocol') |
||||
|
||||
let connection = createConnection() |
||||
|
||||
console.log = connection.console.log.bind(connection.console) |
||||
console.error = connection.console.error.bind(connection.console) |
||||
|
||||
connection.onInitialize(params => { |
||||
assert.equal((params.capabilities.workspace).applyEdit, true) |
||||
assert.equal(params.capabilities.workspace.workspaceEdit.documentChanges, true) |
||||
assert.equal(params.capabilities.workspace.workspaceEdit.failureHandling, FailureHandlingKind.TextOnlyTransactional) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.deprecatedSupport, true) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.preselectSupport, true) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.tagSupport.valueSet.length, 1) |
||||
assert.equal(params.capabilities.textDocument.completion.completionItem.tagSupport.valueSet[0], CompletionItemTag.Deprecated) |
||||
assert.equal(params.capabilities.textDocument.signatureHelp.signatureInformation.parameterInformation.labelOffsetSupport, true) |
||||
// assert.equal(params.capabilities.textDocument.definition.linkSupport, true)
|
||||
// assert.equal(params.capabilities.textDocument.declaration.linkSupport, true)
|
||||
// assert.equal(params.capabilities.textDocument.implementation.linkSupport, true)
|
||||
// assert.equal(params.capabilities.textDocument.typeDefinition.linkSupport, true)
|
||||
assert.equal(params.capabilities.textDocument.rename.prepareSupport, true) |
||||
assert.equal(params.capabilities.textDocument.publishDiagnostics.relatedInformation, true) |
||||
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet.length, 2) |
||||
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet[0], DiagnosticTag.Unnecessary) |
||||
assert.equal(params.capabilities.textDocument.publishDiagnostics.tagSupport.valueSet[1], DiagnosticTag.Deprecated) |
||||
assert.equal(params.capabilities.textDocument.documentLink.tooltipSupport, true) |
||||
let valueSet = params.capabilities.textDocument.completion.completionItemKind.valueSet |
||||
assert.equal(valueSet[0], 1) |
||||
assert.equal(valueSet[valueSet.length - 1], CompletionItemKind.TypeParameter) |
||||
assert.deepEqual(params.capabilities.workspace.workspaceEdit.resourceOperations, [ResourceOperationKind.Create, ResourceOperationKind.Rename, ResourceOperationKind.Delete]) |
||||
assert.equal(params.capabilities.workspace.fileOperations.willCreate, true) |
||||
|
||||
let capabilities = { |
||||
textDocumentSync: TextDocumentSyncKind.Full, |
||||
definitionProvider: true, |
||||
hoverProvider: true, |
||||
completionProvider: {resolveProvider: true, triggerCharacters: ['"', ':']}, |
||||
signatureHelpProvider: { |
||||
triggerCharacters: [':'], |
||||
retriggerCharacters: [':'] |
||||
}, |
||||
referencesProvider: true, |
||||
documentHighlightProvider: true, |
||||
codeActionProvider: { |
||||
resolveProvider: true |
||||
}, |
||||
documentFormattingProvider: true, |
||||
documentRangeFormattingProvider: true, |
||||
documentOnTypeFormattingProvider: { |
||||
firstTriggerCharacter: ':' |
||||
}, |
||||
renameProvider: { |
||||
prepareProvider: true |
||||
}, |
||||
documentLinkProvider: { |
||||
resolveProvider: true |
||||
}, |
||||
colorProvider: true, |
||||
declarationProvider: true, |
||||
foldingRangeProvider: true, |
||||
implementationProvider: true, |
||||
selectionRangeProvider: true, |
||||
typeDefinitionProvider: true, |
||||
callHierarchyProvider: true, |
||||
semanticTokensProvider: { |
||||
legend: { |
||||
tokenTypes: [], |
||||
tokenModifiers: [] |
||||
}, |
||||
range: true, |
||||
full: { |
||||
delta: true |
||||
} |
||||
}, |
||||
workspace: { |
||||
fileOperations: { |
||||
// Static reg is folders + .txt files with operation kind in the path
|
||||
didCreate: { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/created-static/**{/,/*.txt}' }}] |
||||
}, |
||||
didRename: { |
||||
filters: [ |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-static/**/', matches: 'folder' } }, |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-static/**/*.txt', matches: 'file' } } |
||||
] |
||||
}, |
||||
didDelete: { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] |
||||
}, |
||||
willCreate: { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/created-static/**{/,/*.txt}' } }] |
||||
}, |
||||
willRename: { |
||||
filters: [ |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-static/**/', matches: 'folder' } }, |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-static/**/*.txt', matches: 'file' } } |
||||
] |
||||
}, |
||||
willDelete: { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/deleted-static/**{/,/*.txt}' } }] |
||||
}, |
||||
}, |
||||
}, |
||||
linkedEditingRangeProvider: true |
||||
} |
||||
return {capabilities, customResults: {hello: 'world'}} |
||||
}) |
||||
|
||||
connection.onInitialized(() => { |
||||
// Dynamic reg is folders + .js files with operation kind in the path
|
||||
connection.client.register(DidCreateFilesNotification.type, { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/created-dynamic/**{/,/*.js}' } }] |
||||
}); |
||||
connection.client.register(DidRenameFilesNotification.type, { |
||||
filters: [ |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/', matches: 'folder' } }, |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/*.js', matches: 'file' } } |
||||
] |
||||
}); |
||||
connection.client.register(DidDeleteFilesNotification.type, { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/deleted-dynamic/**{/,/*.js}' } }] |
||||
}); |
||||
connection.client.register(WillCreateFilesRequest.type, { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/created-dynamic/**{/,/*.js}' } }] |
||||
}); |
||||
connection.client.register(WillRenameFilesRequest.type, { |
||||
filters: [ |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/', matches: 'folder' } }, |
||||
{ scheme: 'file', pattern: { glob: '**/renamed-dynamic/**/*.js', matches: 'file' } } |
||||
] |
||||
}); |
||||
connection.client.register(WillDeleteFilesRequest.type, { |
||||
filters: [{ scheme: 'file', pattern: { glob: '**/deleted-dynamic/**{/,/*.js}' } }] |
||||
}); |
||||
}) |
||||
|
||||
connection.onDeclaration((params) => { |
||||
assert.equal(params.position.line, 1) |
||||
assert.equal(params.position.character, 1) |
||||
return {uri: params.textDocument.uri, range: {start: {line: 1, character: 1}, end: {line: 1, character: 2}}} |
||||
}) |
||||
|
||||
connection.onDefinition((params) => { |
||||
assert.equal(params.position.line, 1) |
||||
assert.equal(params.position.character, 1) |
||||
return {uri: params.textDocument.uri, range: {start: {line: 0, character: 0}, end: {line: 0, character: 1}}} |
||||
}) |
||||
|
||||
connection.onHover((_params) => { |
||||
return { |
||||
contents: { |
||||
kind: MarkupKind.PlainText, |
||||
value: 'foo' |
||||
} |
||||
} |
||||
}) |
||||
|
||||
connection.onCompletion((_params) => { |
||||
return [ |
||||
{label: 'item', insertText: 'text'} |
||||
] |
||||
}) |
||||
|
||||
connection.onCompletionResolve((item) => { |
||||
item.detail = 'detail' |
||||
return item |
||||
}) |
||||
|
||||
connection.onSignatureHelp((_params) => { |
||||
const result = { |
||||
signatures: [ |
||||
SignatureInformation.create('label', 'doc', ParameterInformation.create('label', 'doc')) |
||||
], |
||||
activeSignature: 1, |
||||
activeParameter: 1 |
||||
} |
||||
return result |
||||
}) |
||||
|
||||
connection.onReferences((params) => { |
||||
return [ |
||||
Location.create(params.textDocument.uri, Range.create(0, 0, 0, 0)), |
||||
Location.create(params.textDocument.uri, Range.create(1, 1, 1, 1)) |
||||
] |
||||
}) |
||||
|
||||
connection.onDocumentHighlight((_params) => { |
||||
return [ |
||||
DocumentHighlight.create(Range.create(2, 2, 2, 2), DocumentHighlightKind.Read) |
||||
] |
||||
}) |
||||
|
||||
connection.onCodeAction((_params) => { |
||||
return [ |
||||
CodeAction.create('title', Command.create('title', 'id')) |
||||
] |
||||
}) |
||||
|
||||
connection.onCodeActionResolve((codeAction) => { |
||||
codeAction.title = 'resolved' |
||||
return codeAction |
||||
}) |
||||
|
||||
connection.onDocumentFormatting((_params) => { |
||||
return [ |
||||
TextEdit.insert(Position.create(0, 0), 'insert') |
||||
] |
||||
}) |
||||
|
||||
connection.onDocumentRangeFormatting((_params) => { |
||||
return [ |
||||
TextEdit.del(Range.create(1, 1, 1, 2)) |
||||
] |
||||
}) |
||||
|
||||
connection.onDocumentOnTypeFormatting((_params) => { |
||||
return [ |
||||
TextEdit.replace(Range.create(2, 2, 2, 3), 'replace') |
||||
] |
||||
}) |
||||
|
||||
connection.onPrepareRename((_params) => { |
||||
return Range.create(1, 1, 1, 2) |
||||
}) |
||||
|
||||
connection.onRenameRequest((_params) => { |
||||
return {documentChanges: []} |
||||
}) |
||||
|
||||
connection.onDocumentLinks((_params) => { |
||||
return [ |
||||
DocumentLink.create(Range.create(1, 1, 1, 2)) |
||||
] |
||||
}) |
||||
|
||||
connection.onDocumentLinkResolve((link) => { |
||||
link.target = URI.file('/target.txt').toString() |
||||
return link |
||||
}) |
||||
|
||||
connection.onDocumentColor((_params) => { |
||||
return [ |
||||
ColorInformation.create(Range.create(1, 1, 1, 2), Color.create(1, 1, 1, 1)) |
||||
] |
||||
}) |
||||
|
||||
connection.onColorPresentation((_params) => { |
||||
return [ |
||||
ColorPresentation.create('label') |
||||
] |
||||
}) |
||||
|
||||
connection.onFoldingRanges((_params) => { |
||||
return [ |
||||
FoldingRange.create(1, 2) |
||||
] |
||||
}) |
||||
|
||||
connection.onImplementation((params) => { |
||||
assert.equal(params.position.line, 1) |
||||
assert.equal(params.position.character, 1) |
||||
return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}} |
||||
}) |
||||
|
||||
connection.onSelectionRanges((_params) => { |
||||
return [ |
||||
SelectionRange.create(Range.create(1, 2, 3, 4)) |
||||
] |
||||
}) |
||||
|
||||
let lastFileOperationRequest |
||||
connection.workspace.onDidCreateFiles((params) => {lastFileOperationRequest = {type: 'create', params}}) |
||||
connection.workspace.onDidRenameFiles((params) => {lastFileOperationRequest = {type: 'rename', params}}) |
||||
connection.workspace.onDidDeleteFiles((params) => {lastFileOperationRequest = {type: 'delete', params}}) |
||||
|
||||
connection.onRequest( |
||||
new ProtocolRequestType('testing/lastFileOperationRequest'), |
||||
() => { |
||||
return lastFileOperationRequest |
||||
}, |
||||
) |
||||
|
||||
connection.workspace.onWillCreateFiles((params) => { |
||||
const createdFilenames = params.files.map((f) => `${f.uri}`).join('\n') |
||||
return { |
||||
documentChanges: [{ |
||||
textDocument: {uri: '/dummy-edit', version: null}, |
||||
edits: [ |
||||
TextEdit.insert(Position.create(0, 0), `WILL CREATE:\n${createdFilenames}`), |
||||
] |
||||
}], |
||||
} |
||||
}) |
||||
|
||||
connection.workspace.onWillRenameFiles((params) => { |
||||
const renamedFilenames = params.files.map((f) => `${f.oldUri} -> ${f.newUri}`).join('\n') |
||||
return { |
||||
documentChanges: [{ |
||||
textDocument: {uri: '/dummy-edit', version: null}, |
||||
edits: [ |
||||
TextEdit.insert(Position.create(0, 0), `WILL RENAME:\n${renamedFilenames}`), |
||||
] |
||||
}], |
||||
} |
||||
}) |
||||
|
||||
connection.workspace.onWillDeleteFiles((params) => { |
||||
const deletedFilenames = params.files.map((f) => `${f.uri}`).join('\n') |
||||
return { |
||||
documentChanges: [{ |
||||
textDocument: {uri: '/dummy-edit', version: null}, |
||||
edits: [ |
||||
TextEdit.insert(Position.create(0, 0), `WILL DELETE:\n${deletedFilenames}`), |
||||
] |
||||
}], |
||||
} |
||||
}) |
||||
|
||||
connection.onTypeDefinition((params) => { |
||||
assert.equal(params.position.line, 1) |
||||
assert.equal(params.position.character, 1) |
||||
return {uri: params.textDocument.uri, range: {start: {line: 2, character: 2}, end: {line: 3, character: 3}}} |
||||
}) |
||||
|
||||
connection.languages.callHierarchy.onPrepare((params) => { |
||||
return [ |
||||
{ |
||||
kind: SymbolKind.Function, |
||||
name: 'name', |
||||
range: Range.create(1, 1, 1, 1), |
||||
selectionRange: Range.create(2, 2, 2, 2), |
||||
uri: params.textDocument.uri |
||||
} |
||||
] |
||||
}) |
||||
|
||||
connection.languages.callHierarchy.onIncomingCalls((params) => { |
||||
return [ |
||||
{ |
||||
from: params.item, |
||||
fromRanges: [Range.create(1, 1, 1, 1)] |
||||
} |
||||
] |
||||
}) |
||||
|
||||
connection.languages.callHierarchy.onOutgoingCalls((params) => { |
||||
return [ |
||||
{ |
||||
to: params.item, |
||||
fromRanges: [Range.create(1, 1, 1, 1)] |
||||
} |
||||
] |
||||
}) |
||||
|
||||
connection.languages.semanticTokens.onRange(() => { |
||||
return { |
||||
resultId: '1', |
||||
data: [] |
||||
} |
||||
}) |
||||
|
||||
connection.languages.semanticTokens.on(() => { |
||||
return { |
||||
resultId: '2', |
||||
data: [] |
||||
} |
||||
}) |
||||
|
||||
connection.languages.semanticTokens.onDelta(() => { |
||||
return { |
||||
resultId: '3', |
||||
data: [] |
||||
} |
||||
}) |
||||
|
||||
connection.languages.onLinkedEditingRange(() => { |
||||
return { |
||||
ranges: [Range.create(1, 1, 1, 1)], |
||||
wordPattern: '\\w' |
||||
} |
||||
}) |
||||
|
||||
connection.onRequest( |
||||
new ProtocolRequestType('testing/sendSampleProgress'), |
||||
async (_, __) => { |
||||
const progressToken = 'TEST-PROGRESS-TOKEN' |
||||
await connection.sendRequest(WorkDoneProgressCreateRequest.type, {token: progressToken}) |
||||
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'begin', title: 'Test Progress'}) |
||||
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'report', percentage: 50, message: 'Halfway!'}) |
||||
connection.sendProgress(WorkDoneProgress.type, progressToken, {kind: 'end', message: 'Completed!'}) |
||||
}, |
||||
) |
||||
|
||||
// Listen on the connection
|
||||
connection.listen() |
@ -1,5 +0,0 @@
@@ -1,5 +0,0 @@
|
||||
{ |
||||
"suggest.timeout": 5000, |
||||
"suggest.triggerCompletionWait": 10, |
||||
"tslint.enable": false |
||||
} |
@ -1,446 +0,0 @@
@@ -1,446 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { ISource, SourceType, CompleteResult, CompleteOption } from '../../types' |
||||
import helper from '../helper' |
||||
import sources from '../../sources' |
||||
import { CancellationToken } from 'vscode-jsonrpc' |
||||
|
||||
let nvim: Neovim |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('completion', () => { |
||||
|
||||
it('should not show word of word source on empty input', async () => { |
||||
await nvim.setLine('foo bar') |
||||
await helper.wait(200) |
||||
await nvim.input('of') |
||||
let res = await helper.visible('foo', 'around') |
||||
expect(res).toBe(true) |
||||
await nvim.input('<backspace>') |
||||
await helper.wait(200) |
||||
res = await helper.notVisible('foo') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should trigger on first letter insert', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo bar') |
||||
await helper.wait(30) |
||||
await nvim.input('of') |
||||
let res = await helper.visible('foo', 'around') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should trigger on force refresh', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo f') |
||||
await helper.wait(100) |
||||
await nvim.input('A') |
||||
await helper.wait(10) |
||||
await nvim.call('coc#start') |
||||
let res = await helper.visible('foo', 'around') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should filter and sort on increment search', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('forceDocumentSync format fallback') |
||||
await helper.wait(30) |
||||
await nvim.input('of') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
let l = items.length |
||||
await nvim.input('oa') |
||||
await helper.wait(100) |
||||
items = await helper.getItems() |
||||
expect(items.findIndex(o => o.word == 'fallback')).toBe(-1) |
||||
expect(items.length).toBeLessThan(l) |
||||
}) |
||||
|
||||
it('should filter on character remove by backspace', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('forceDocumentSync format fallback') |
||||
await helper.wait(30) |
||||
await nvim.input('ofa') |
||||
|
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
let words = items.map(o => o.word) |
||||
expect(words).toContain('fallback') |
||||
expect(words).toContain('format') |
||||
await nvim.input('<backspace>') |
||||
await helper.wait(100) |
||||
items = await helper.getItems() |
||||
words = items.map(o => o.word) |
||||
expect(words).toEqual([]) |
||||
}) |
||||
|
||||
it('should not trigger on insert enter', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo bar') |
||||
await helper.wait(30) |
||||
await nvim.input('o') |
||||
let visible = await nvim.call('pumvisible') |
||||
expect(visible).toBe(0) |
||||
}) |
||||
|
||||
it('should filter on fast input', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo bar') |
||||
await helper.wait(60) |
||||
await nvim.input('oba') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
let item = items.find(o => o.word == 'foo') |
||||
expect(item).toBeFalsy() |
||||
expect(items[0].word).toBe('bar') |
||||
}) |
||||
|
||||
it('should fix start column', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'test', |
||||
priority: 10, |
||||
enable: true, |
||||
firstMatch: false, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: [], |
||||
doComplete: async (): Promise<CompleteResult> => { |
||||
let result: CompleteResult = { |
||||
startcol: 0, |
||||
items: [{ word: 'foo.bar' }] |
||||
} |
||||
return Promise.resolve(result) |
||||
} |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.setLine('foo.') |
||||
await nvim.input('Ab') |
||||
await helper.waitPopup() |
||||
let val = await nvim.getVar('coc#_context') as any |
||||
expect(val.start).toBe(0) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should stop completion when type none trigger character', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'test', |
||||
priority: 10, |
||||
enable: true, |
||||
firstMatch: false, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: [], |
||||
doComplete: async (): Promise<CompleteResult> => { |
||||
let result: CompleteResult = { |
||||
items: [{ word: 'if(' }] |
||||
} |
||||
return Promise.resolve(result) |
||||
} |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.setLine('') |
||||
await nvim.input('iif') |
||||
await helper.waitPopup() |
||||
await nvim.input('(') |
||||
await helper.wait(300) |
||||
let res = await helper.pumvisible() |
||||
expect(res).toBe(true) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should trigger on triggerCharacters', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'trigger', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo' }] |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('.') |
||||
await helper.waitPopup() |
||||
sources.removeSource(source) |
||||
let res = await helper.visible('foo', 'trigger') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should should complete items without input', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
enable: true, |
||||
name: 'trigger', |
||||
priority: 10, |
||||
sourceType: SourceType.Native, |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo' }, { word: 'bar' }] |
||||
}) |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.command('inoremap <silent><expr> <c-space> coc#refresh()') |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('<c-space>') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBeGreaterThan(1) |
||||
disposable.dispose() |
||||
await helper.wait(300) |
||||
}) |
||||
|
||||
it('should show float window', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'float', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
doComplete: (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo', info: 'bar' }] |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('f') |
||||
await helper.waitPopup() |
||||
await nvim.eval('feedkeys("\\<down>","in")') |
||||
await helper.wait(800) |
||||
let hasFloat = await nvim.call('coc#float#has_float') |
||||
expect(hasFloat).toBe(1) |
||||
sources.removeSource(source) |
||||
let res = await helper.visible('foo', 'float') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should trigger on triggerPatterns', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'pattern', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerPatterns: [/\w+\.$/], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo' }] |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(10) |
||||
await nvim.input('.') |
||||
await helper.wait(30) |
||||
let pumvisible = await nvim.call('pumvisible') |
||||
expect(pumvisible).toBe(0) |
||||
await nvim.input('a') |
||||
await helper.wait(30) |
||||
await nvim.input('.') |
||||
await helper.waitPopup() |
||||
sources.removeSource(source) |
||||
let res = await helper.visible('foo', 'pattern') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should not trigger triggerOnly source', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo bar') |
||||
let source: ISource = { |
||||
name: 'pattern', |
||||
triggerOnly: true, |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerPatterns: [/^From:\s*/], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo' }] |
||||
}) |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.input('o') |
||||
await helper.wait(10) |
||||
await nvim.input('f') |
||||
await helper.wait(10) |
||||
let res = await helper.visible('foo', 'around') |
||||
expect(res).toBe(true) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(1) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should not trigger when cursor moved', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'trigger', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ word: 'foo' }] |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.setLine('.a') |
||||
await nvim.input('A') |
||||
await nvim.eval('feedkeys("\\<bs>")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("\\<left>")') |
||||
await helper.wait(200) |
||||
let visible = await nvim.call('pumvisible') |
||||
expect(visible).toBe(0) |
||||
sources.removeSource(source) |
||||
}) |
||||
|
||||
it('should trigger when completion is not completed', async () => { |
||||
await helper.edit() |
||||
let token: CancellationToken |
||||
let source: ISource = { |
||||
name: 'completion', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (opt, cancellationToken): Promise<CompleteResult> => { |
||||
if (opt.triggerCharacter != '.') { |
||||
token = cancellationToken |
||||
return new Promise<CompleteResult>((resolve, reject) => { |
||||
let timer = setTimeout(() => { |
||||
resolve({ items: [{ word: 'foo' }] }) |
||||
}, 200) |
||||
if (cancellationToken.isCancellationRequested) { |
||||
clearTimeout(timer) |
||||
reject(new Error('Cancelled')) |
||||
} |
||||
}) |
||||
} |
||||
return Promise.resolve({ |
||||
items: [{ word: 'bar' }] |
||||
}) |
||||
} |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.input('if') |
||||
await helper.wait(100) |
||||
await nvim.input('.') |
||||
await helper.visible('bar', 'completion') |
||||
expect(token.isCancellationRequested).toBe(true) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should limit results for low priority source', async () => { |
||||
helper.updateConfiguration('suggest.lowPrioritySourceLimit', 2) |
||||
await nvim.setLine('filename filepath find filter findIndex') |
||||
await helper.wait(200) |
||||
await nvim.input('of') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
items = items.filter(o => o.menu == '[A]') |
||||
expect(items.length).toBe(2) |
||||
}) |
||||
|
||||
it('should limit result for high priority source', async () => { |
||||
helper.updateConfiguration('suggest.highPrioritySourceLimit', 2) |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'high', |
||||
priority: 90, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: ['filename', 'filepath', 'filter', 'file'].map(key => ({ word: key })) |
||||
}) |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('.') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBeGreaterThan(1) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should truncate label of complete items', async () => { |
||||
helper.updateConfiguration('suggest.labelMaxLength', 10) |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'high', |
||||
priority: 90, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: ['a', 'b', 'c', 'd'].map(key => ({ word: key.repeat(20) })) |
||||
}) |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('.') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
for (let item of items) { |
||||
expect(item.abbr.length).toBeLessThanOrEqual(10) |
||||
} |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should delete previous items if complete item is null', async () => { |
||||
await helper.edit() |
||||
let source1: ISource = { |
||||
name: 'source1', |
||||
priority: 90, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [ {word: 'foo', dup: 1} ] |
||||
}) |
||||
} |
||||
let source2: ISource = { |
||||
name: 'source2', |
||||
priority: 90, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => { |
||||
let result: CompleteResult = opt.input == 'foo' ? null : { |
||||
items: [{ word: 'foo', dup: 1 }], isIncomplete: true |
||||
} |
||||
return Promise.resolve(result) |
||||
} |
||||
} |
||||
let disposable1 = sources.addSource(source1) |
||||
let disposable2 = sources.addSource(source2) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('.f') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toEqual(2) |
||||
await nvim.input('oo') |
||||
await helper.waitPopup() |
||||
items = await helper.getItems() |
||||
expect(items.length).toEqual(1) |
||||
expect(items[0].word).toBe('foo') |
||||
disposable1.dispose() |
||||
disposable2.dispose() |
||||
}) |
||||
}) |
@ -1,122 +0,0 @@
@@ -1,122 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import sources from '../../sources' |
||||
import { CompleteResult, ISource, SourceType } from '../../types' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let source: ISource |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
source = { |
||||
name: 'float', |
||||
priority: 10, |
||||
enable: true, |
||||
sourceType: SourceType.Native, |
||||
doComplete: (): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [{ |
||||
word: 'foo', |
||||
info: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' |
||||
}, { |
||||
word: 'foot', |
||||
info: 'foot' |
||||
}, { |
||||
word: 'football', |
||||
}] |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
sources.removeSource(source) |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('completion float', () => { |
||||
|
||||
it('should not show float window when disabled', async () => { |
||||
helper.updateConfiguration('suggest.floatEnable', false) |
||||
await helper.edit() |
||||
await nvim.input('if') |
||||
await helper.visible('foo', 'float') |
||||
helper.updateConfiguration('suggest.floatEnable', true) |
||||
let hasFloat = await nvim.call('coc#float#has_float') |
||||
expect(hasFloat).toBe(0) |
||||
}) |
||||
|
||||
it('should cancel float window', async () => { |
||||
await helper.edit() |
||||
await nvim.input('if') |
||||
await helper.visible('foo', 'float') |
||||
let items = await helper.getItems() |
||||
expect(items[0].word).toBe('foo') |
||||
expect(items[0].info.length > 0).toBeTruthy() |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(500) |
||||
await nvim.input('<esc>') |
||||
await helper.wait(100) |
||||
let hasFloat = await nvim.call('coc#float#has_float') |
||||
expect(hasFloat).toBe(0) |
||||
}) |
||||
|
||||
it('should adjust float window position', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine(' '.repeat(70)) |
||||
await nvim.input('Af') |
||||
await helper.visible('foo', 'float') |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(300) |
||||
let floatWin = await helper.getFloat() |
||||
let config = await floatWin.getConfig() |
||||
expect(config.col + config.width).toBeLessThan(180) |
||||
}) |
||||
|
||||
it('should redraw float window on item change', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine(' '.repeat(70)) |
||||
await nvim.input('Af') |
||||
await helper.visible('foo', 'float') |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(50) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(300) |
||||
let floatWin = await helper.getFloat() |
||||
let buf = await floatWin.buffer |
||||
let lines = await buf.lines |
||||
expect(lines.length).toBeGreaterThan(0) |
||||
expect(lines[0]).toMatch('foot') |
||||
}) |
||||
|
||||
it('should hide float window when item info is empty', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine(' '.repeat(70)) |
||||
await nvim.input('Af') |
||||
await helper.visible('foo', 'float') |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(10) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(10) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(100) |
||||
let hasFloat = await nvim.call('coc#float#has_float') |
||||
expect(hasFloat).toBe(0) |
||||
}) |
||||
|
||||
it('should hide float window after completion', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine(' '.repeat(70)) |
||||
await nvim.input('Af') |
||||
await helper.visible('foo', 'float') |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(100) |
||||
await nvim.input('<C-y>') |
||||
await helper.wait(30) |
||||
let hasFloat = await nvim.call('coc#float#has_float') |
||||
expect(hasFloat).toBe(0) |
||||
}) |
||||
}) |
@ -1,66 +0,0 @@
@@ -1,66 +0,0 @@
|
||||
import { matchScore } from '../../completion/match' |
||||
import { getCharCodes } from '../../util/fuzzy' |
||||
|
||||
function score(word: string, input: string): number { |
||||
return matchScore(word, getCharCodes(input)) |
||||
} |
||||
|
||||
describe('matchScore', () => { |
||||
|
||||
it('should match score for last letter', () => { |
||||
expect(score('#!3', '3')).toBe(1) |
||||
}) |
||||
|
||||
it('should match first letter', () => { |
||||
expect(score('abc', 'a')).toBe(5) |
||||
expect(score('Abc', 'a')).toBe(2.5) |
||||
expect(score('__abc', 'a')).toBe(2.5) |
||||
expect(score('$Abc', 'a')).toBe(2) |
||||
expect(score('$Abc', 'A')).toBe(2.5) |
||||
expect(score('$Abc', '$A')).toBe(6) |
||||
expect(score('$Abc', '$a')).toBe(5.5) |
||||
expect(score('foo_bar', 'b')).toBe(2.5) |
||||
expect(score('foo_Bar', 'b')).toBe(2) |
||||
expect(score('_foo_Bar', 'b')).toBe(0.5) |
||||
expect(score('_foo_Bar', 'f')).toBe(2.5) |
||||
expect(score('bar', 'a')).toBe(1) |
||||
expect(score('fooBar', 'B')).toBe(2.5) |
||||
expect(score('fooBar', 'b')).toBe(2) |
||||
}) |
||||
|
||||
it('should match follow letters', () => { |
||||
expect(score('abc', 'ab')).toBe(6) |
||||
expect(score('adB', 'ab')).toBe(5.75) |
||||
expect(score('adb', 'ab')).toBe(5.1) |
||||
expect(score('adCB', 'ab')).toBe(5.05) |
||||
expect(score('a_b_c', 'ab')).toBe(6) |
||||
expect(score('FooBar', 'fb')).toBe(3.25) |
||||
expect(score('FBar', 'fb')).toBe(3) |
||||
expect(score('FooBar', 'FB')).toBe(6) |
||||
expect(score('FBar', 'FB')).toBe(6) |
||||
expect(score('a__b', 'a__b')).toBe(8) |
||||
expect(score('aBc', 'ab')).toBe(5.5) |
||||
expect(score('a_B_c', 'ab')).toBe(5.75) |
||||
expect(score('abc', 'abc')).toBe(7) |
||||
expect(score('abc', 'aC')).toBe(0) |
||||
expect(score('abc', 'ac')).toBe(5.1) |
||||
expect(score('abC', 'ac')).toBe(5.75) |
||||
expect(score('abC', 'aC')).toBe(6) |
||||
}) |
||||
|
||||
it('should only allow search once', () => { |
||||
expect(score('foobar', 'fbr')).toBe(0) |
||||
expect(score('foobaRow', 'fbr')).toBe(5.85) |
||||
expect(score('foobaRow', 'fbR')).toBe(6.1) |
||||
expect(score('foobar', 'fa')).toBe(5.1) |
||||
}) |
||||
|
||||
it('should have higher score for strict match', () => { |
||||
expect(score('language-client-protocol', 'lct')).toBe(6.1) |
||||
expect(score('language-client-types', 'lct')).toBe(7) |
||||
}) |
||||
|
||||
it('should find highest score', () => { |
||||
expect(score('ArrayRotateTail', 'art')).toBe(4) |
||||
}) |
||||
}) |
@ -1,92 +0,0 @@
@@ -1,92 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import helper from '../helper' |
||||
import { ISource, SourceType, CompleteResult } from '../../types' |
||||
import sources from '../../sources' |
||||
|
||||
let nvim: Neovim |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('native sources', () => { |
||||
|
||||
it('should works for around source', async () => { |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo ') |
||||
await helper.wait(100) |
||||
let { mode } = await nvim.mode |
||||
expect(mode).toBe('n') |
||||
await nvim.input('Af') |
||||
let res = await helper.visible('foo', 'around') |
||||
expect(res).toBe(true) |
||||
await nvim.input('<esc>') |
||||
}) |
||||
|
||||
it('should works for buffer source', async () => { |
||||
await nvim.command('set hidden') |
||||
await helper.createDocument() |
||||
await helper.createDocument() |
||||
await nvim.setLine('other') |
||||
await nvim.command('bp') |
||||
await helper.wait(300) |
||||
let { mode } = await nvim.mode |
||||
expect(mode).toBe('n') |
||||
await nvim.input('io') |
||||
let res = await helper.visible('other', 'buffer') |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should works for file source', async () => { |
||||
await helper.edit() |
||||
await nvim.input('i/') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBeGreaterThan(0) |
||||
let res = await helper.visible(items[0].word, 'file') |
||||
expect(res).toBe(true) |
||||
await nvim.input('<esc>') |
||||
await nvim.input('o./') |
||||
await helper.waitPopup() |
||||
items = await helper.getItems() |
||||
let item = items.find(o => o.word == 'vimrc') |
||||
expect(item).toBeTruthy() |
||||
}) |
||||
|
||||
it('should works for file source with other source use same triggerCharacter', async () => { |
||||
await helper.edit() |
||||
let source: ISource = { |
||||
name: 'test', |
||||
priority: 50, |
||||
enable: true, |
||||
firstMatch: false, |
||||
sourceType: SourceType.Native, |
||||
triggerCharacters: ['.', '/'], |
||||
doComplete: async (): Promise<CompleteResult> => { |
||||
let result: CompleteResult = { |
||||
items: [{ word: 'foo' }] |
||||
} |
||||
return Promise.resolve(result) |
||||
} |
||||
} |
||||
let disposable = sources.addSource(source) |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBe(1) |
||||
await nvim.input('/') |
||||
await helper.waitPopup() |
||||
items = await helper.getItems() |
||||
expect(items.length).toBeGreaterThan(1) |
||||
expect(items[0].word).toBe('foo') |
||||
disposable.dispose() |
||||
}) |
||||
}) |
@ -1,32 +0,0 @@
@@ -1,32 +0,0 @@
|
||||
import { CompletionItemKind, TextEdit, Position } from 'vscode-languageserver-types' |
||||
import { getStartColumn, getKindString } from '../../sources/source-language' |
||||
|
||||
describe('getKindString()', () => { |
||||
it('should get kind text', async () => { |
||||
let map = new Map() |
||||
map.set(CompletionItemKind.Enum, 'E') |
||||
let res = getKindString(CompletionItemKind.Enum, map, '') |
||||
expect(res).toBe('E') |
||||
}) |
||||
|
||||
it('should get default value', async () => { |
||||
let map = new Map() |
||||
let res = getKindString(CompletionItemKind.Enum, map, 'D') |
||||
expect(res).toBe('D') |
||||
}) |
||||
}) |
||||
|
||||
describe('getStartColumn()', () => { |
||||
it('should get start col', async () => { |
||||
expect(getStartColumn('', [{ label: 'foo' }])).toBe(null) |
||||
expect(getStartColumn('', [ |
||||
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') }, |
||||
{ label: 'bar' }])).toBe(null) |
||||
expect(getStartColumn('foo', [ |
||||
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 0), 'a') }, |
||||
{ label: 'bar', textEdit: TextEdit.insert(Position.create(0, 1), 'b') }])).toBe(null) |
||||
expect(getStartColumn('foo', [ |
||||
{ label: 'foo', textEdit: TextEdit.insert(Position.create(0, 2), 'a') }, |
||||
{ label: 'bar', textEdit: TextEdit.insert(Position.create(0, 2), 'b') }])).toBe(2) |
||||
}) |
||||
}) |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
exports.activate = async context => { |
||||
return { |
||||
getContext: () => { |
||||
return context |
||||
} |
||||
} |
||||
} |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
{ |
||||
"name": "global", |
||||
"version": "1.0.0", |
||||
"engines": { |
||||
"coc": "^0.0.46" |
||||
} |
||||
} |
@ -1,6 +0,0 @@
@@ -1,6 +0,0 @@
|
||||
{ |
||||
"dependencies": { |
||||
"global": ">=1.0.0", |
||||
"test": ">=1.0.0" |
||||
} |
||||
} |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
exports.activate = context => { |
||||
return { |
||||
root: () => { |
||||
return context.extensionPath |
||||
} |
||||
} |
||||
} |
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
exports.activate = async context => { |
||||
return { |
||||
asAbsolutePath: p => { |
||||
return context.asAbsolutePath(p) |
||||
}, |
||||
getContext: () => { |
||||
return context |
||||
}, |
||||
echo: x => { |
||||
return x |
||||
} |
||||
} |
||||
} |
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
{ |
||||
"name": "test", |
||||
"version": "1.0.0", |
||||
"engines": { |
||||
"coc": "^0.0.46" |
||||
}, |
||||
"contributes": { |
||||
"rootPatterns": [ |
||||
{ |
||||
"filetype": "javascript", |
||||
"patterns": [ |
||||
"package.json", |
||||
"jsconfig.json" |
||||
] |
||||
} |
||||
], |
||||
"commands": [ |
||||
{ |
||||
"title": "Test", |
||||
"command": "test.run" |
||||
} |
||||
], |
||||
"configuration": { |
||||
"properties": { |
||||
"test.enable": { |
||||
"type": "boolean", |
||||
"default": true, |
||||
"description": "Enable test" |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
exports.activate = async context => { |
||||
return { |
||||
getContext: () => { |
||||
return context |
||||
} |
||||
} |
||||
} |
@ -1,7 +0,0 @@
@@ -1,7 +0,0 @@
|
||||
{ |
||||
"name": "local", |
||||
"version": "1.0.0", |
||||
"engines": { |
||||
"coc": "^0.0.46" |
||||
} |
||||
} |
@ -1,398 +0,0 @@
@@ -1,398 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, CallHierarchyItem, SymbolKind, Range, SymbolTag } from 'vscode-languageserver-protocol' |
||||
import CallHierarchyHandler from '../../handler/callHierarchy' |
||||
import languages from '../../languages' |
||||
import workspace from '../../workspace' |
||||
import { disposeAll } from '../../util' |
||||
import { URI } from 'vscode-uri' |
||||
import helper, { createTmpFile } from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let callHierarchy: CallHierarchyHandler |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
callHierarchy = helper.plugin.getHandler().callHierarchy |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
function createCallItem(name: string, kind: SymbolKind, uri: string, range: Range): CallHierarchyItem { |
||||
return { |
||||
name, |
||||
kind, |
||||
uri, |
||||
range, |
||||
selectionRange: range |
||||
} |
||||
} |
||||
|
||||
describe('CallHierarchy', () => { |
||||
it('should throw for when provider not exists', async () => { |
||||
let err |
||||
try { |
||||
await callHierarchy.getIncoming() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should get undefined when prepare failed', async () => { |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return undefined |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
return [] |
||||
} |
||||
})) |
||||
let res = await callHierarchy.getOutgoing() |
||||
expect(res).toBeUndefined() |
||||
}) |
||||
|
||||
it('should get incoming & outgoing callHierarchy items', async () => { |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, 'test:///foo', Range.create(0, 0, 0, 5)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [{ |
||||
from: createCallItem('bar', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)), |
||||
fromRanges: [Range.create(0, 0, 0, 5)] |
||||
}] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
return [{ |
||||
to: createCallItem('bar', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)), |
||||
fromRanges: [Range.create(1, 0, 1, 5)] |
||||
}] |
||||
} |
||||
})) |
||||
let res = await callHierarchy.getIncoming() |
||||
expect(res.length).toBe(1) |
||||
expect(res[0].from.name).toBe('bar') |
||||
let outgoing = await callHierarchy.getOutgoing() |
||||
expect(outgoing.length).toBe(1) |
||||
res = await callHierarchy.getIncoming(outgoing[0].to) |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
|
||||
it('should show message when provider not exists', async () => { |
||||
await callHierarchy.showCallHierarchyTree('incoming') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toMatch('callHierarchy provider not found') |
||||
await nvim.command('wincmd p') |
||||
}) |
||||
|
||||
it('should no results when no result returned.', async () => { |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
return [] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('incoming') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toBe('No results') |
||||
await nvim.command('wincmd p') |
||||
}) |
||||
|
||||
it('should render description and support default action', async () => { |
||||
let doc = await workspace.document |
||||
let bufnr = doc.bufnr |
||||
await doc.buffer.setLines(['foo'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(1, 0, 1, 3)) |
||||
item.detail = 'Detail' |
||||
item.tags = [SymbolTag.Deprecated] |
||||
return [{ |
||||
from: item, |
||||
fromRanges: [Range.create(2, 0, 2, 5)] |
||||
}] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
return [] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('incoming') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'INCOMING CALLS', |
||||
'- c foo', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('t') |
||||
await helper.wait(50) |
||||
let line = await nvim.line |
||||
expect(line).toEqual(' - c bar Detail') |
||||
await nvim.input('<cr>') |
||||
await helper.wait(50) |
||||
doc = await workspace.document |
||||
expect(doc.uri).toBe(uri) |
||||
let res = await nvim.call('coc#cursor#position') |
||||
expect(res).toEqual([1, 0]) |
||||
let matches = await nvim.call('getmatches') as any[] |
||||
expect(matches.length).toBe(2) |
||||
await nvim.command(`b ${bufnr}`) |
||||
await helper.wait(50) |
||||
matches = await nvim.call('getmatches') |
||||
expect(matches.length).toBe(0) |
||||
await nvim.command(`wincmd o`) |
||||
await helper.wait(50) |
||||
}) |
||||
|
||||
it('should invoke open in new tab action', async () => { |
||||
let doc = await workspace.document |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1)) |
||||
item.detail = 'Detail' |
||||
return [{ |
||||
to: item, |
||||
fromRanges: [Range.create(1, 0, 1, 3)] |
||||
}] |
||||
} |
||||
})) |
||||
let win = await nvim.window |
||||
let tab = await nvim.call('tabpagenr') |
||||
await callHierarchy.showCallHierarchyTree('outgoing') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('<cr>') |
||||
await helper.wait(200) |
||||
let newTab = await nvim.call('tabpagenr') |
||||
expect(newTab != tab).toBe(true) |
||||
doc = await workspace.document |
||||
expect(doc.uri).toBe(uri) |
||||
let res = await nvim.call('getmatches', [win.id]) |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
|
||||
it('should invoke show incoming calls action', async () => { |
||||
let doc = await workspace.document |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [{ |
||||
from: createCallItem('test', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)), |
||||
fromRanges: [Range.create(0, 0, 0, 5)] |
||||
}] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1)) |
||||
item.detail = 'Detail' |
||||
return [{ |
||||
to: item, |
||||
fromRanges: [Range.create(1, 0, 1, 3)] |
||||
}] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('outgoing') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('2') |
||||
await helper.wait(200) |
||||
lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'INCOMING CALLS', |
||||
'- c bar Detail', |
||||
' + c test' |
||||
]) |
||||
await nvim.command('bd!') |
||||
}) |
||||
|
||||
it('should invoke show outgoing calls action', async () => { |
||||
let doc = await workspace.document |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [{ |
||||
from: createCallItem('test', SymbolKind.Class, 'test:///bar', Range.create(1, 0, 1, 5)), |
||||
fromRanges: [Range.create(0, 0, 0, 5)] |
||||
}] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1)) |
||||
item.detail = 'Detail' |
||||
return [{ |
||||
to: item, |
||||
fromRanges: [Range.create(1, 0, 1, 3)] |
||||
}] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('incoming') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'INCOMING CALLS', |
||||
'- c foo', |
||||
' + c test' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('3') |
||||
await helper.wait(200) |
||||
lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c test', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('bd!') |
||||
}) |
||||
|
||||
it('should invoke dismiss action #1', async () => { |
||||
let doc = await workspace.document |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1)) |
||||
item.detail = 'Detail' |
||||
return [{ |
||||
to: item, |
||||
fromRanges: [Range.create(1, 0, 1, 3)] |
||||
}] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('outgoing') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('4') |
||||
await helper.wait(200) |
||||
lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo' |
||||
]) |
||||
await nvim.command('wincmd c') |
||||
}) |
||||
|
||||
it('should invoke dismiss action #2', async () => { |
||||
let doc = await workspace.document |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let fsPath = await createTmpFile('foo\nbar\ncontent\n') |
||||
let uri = URI.file(fsPath).toString() |
||||
disposables.push(languages.registerCallHierarchyProvider([{ language: '*' }], { |
||||
prepareCallHierarchy() { |
||||
return createCallItem('foo', SymbolKind.Class, doc.uri, Range.create(0, 0, 0, 3)) |
||||
}, |
||||
provideCallHierarchyIncomingCalls() { |
||||
return [] |
||||
}, |
||||
provideCallHierarchyOutgoingCalls() { |
||||
let item = createCallItem('bar', SymbolKind.Class, uri, Range.create(0, 0, 0, 1)) |
||||
item.detail = 'Detail' |
||||
return [{ |
||||
to: item, |
||||
fromRanges: [Range.create(1, 0, 1, 3)] |
||||
}] |
||||
} |
||||
})) |
||||
await callHierarchy.showCallHierarchyTree('outgoing') |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo', |
||||
' + c bar Detail' |
||||
]) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('t') |
||||
await helper.wait(50) |
||||
await nvim.command('exe 4') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('4') |
||||
await helper.wait(200) |
||||
lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTGOING CALLS', |
||||
'- c foo', |
||||
' - c bar Detail' |
||||
]) |
||||
await nvim.command('wincmd c') |
||||
}) |
||||
}) |
@ -1,386 +0,0 @@
@@ -1,386 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { CancellationToken, CodeAction, Command, CodeActionContext, CodeActionKind, TextEdit, Disposable, Range, Position } from 'vscode-languageserver-protocol' |
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import commands from '../../commands' |
||||
import ActionsHandler from '../../handler/codeActions' |
||||
import languages from '../../languages' |
||||
import { ProviderResult } from '../../provider' |
||||
import { disposeAll } from '../../util' |
||||
import { rangeInRange } from '../../util/position' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let codeActions: ActionsHandler |
||||
let currActions: CodeAction[] |
||||
let resolvedAction: CodeAction |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
codeActions = helper.plugin.getHandler().codeActions |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document: TextDocument, |
||||
_range: Range, |
||||
_context: CodeActionContext, |
||||
_token: CancellationToken |
||||
) => currActions, |
||||
resolveCodeAction: ( |
||||
_action: CodeAction, |
||||
_token: CancellationToken |
||||
): ProviderResult<CodeAction> => resolvedAction |
||||
}, undefined)) |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('handler codeActions', () => { |
||||
describe('organizeImport', () => { |
||||
it('should throw error when organize import action not found', async () => { |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
let err |
||||
try { |
||||
await codeActions.organizeImport() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should perform organize import action', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar')) |
||||
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports) |
||||
currActions = [action, CodeAction.create('another action')] |
||||
await codeActions.organizeImport() |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar', 'foo']) |
||||
}) |
||||
|
||||
it('should register editor.action.organizeImport command', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.setLines(['foo', 'bar'], { start: 0, end: -1, strictIndexing: false }) |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.replace(Range.create(0, 0, 0, 3), 'bar')) |
||||
edits.push(TextEdit.replace(Range.create(1, 0, 1, 3), 'foo')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('organize import', edit, CodeActionKind.SourceOrganizeImports) |
||||
currActions = [action, CodeAction.create('another action')] |
||||
await commands.executeCommand('editor.action.organizeImport') |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar', 'foo']) |
||||
}) |
||||
}) |
||||
|
||||
describe('codeActionRange', () => { |
||||
it('should show warning when no action available', async () => { |
||||
await helper.createDocument() |
||||
currActions = [] |
||||
await codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix) |
||||
let line = await helper.getCmdline() |
||||
expect(line).toMatch(/No quickfix code action/) |
||||
}) |
||||
|
||||
it('should apply choosen action', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix) |
||||
currActions = [action] |
||||
let p = codeActions.codeActionRange(1, 2, CodeActionKind.QuickFix) |
||||
await helper.wait(50) |
||||
await nvim.input('<CR>') |
||||
await p |
||||
let buf = nvim.createBuffer(doc.bufnr) |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toBe('bar') |
||||
}) |
||||
}) |
||||
|
||||
describe('getCodeActions', () => { |
||||
it('should get empty actions', async () => { |
||||
currActions = [] |
||||
let doc = await helper.createDocument() |
||||
let res = await codeActions.getCodeActions(doc) |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
|
||||
it('should filter disabled actions', async () => { |
||||
currActions = [] |
||||
let action = CodeAction.create('foo', CodeActionKind.QuickFix) |
||||
action.disabled = { reason: 'disabled' } |
||||
currActions.push(action) |
||||
action = CodeAction.create('foo', CodeActionKind.QuickFix) |
||||
action.disabled = { reason: 'disabled' } |
||||
currActions.push(action) |
||||
let doc = await helper.createDocument() |
||||
let res = await codeActions.getCodeActions(doc) |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
|
||||
it('should get all actions', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false }) |
||||
let action = CodeAction.create('curr action', CodeActionKind.Empty) |
||||
currActions = [action] |
||||
let range: Range |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document: TextDocument, |
||||
r: Range, |
||||
_context: CodeActionContext, _token: CancellationToken |
||||
) => { |
||||
range = r |
||||
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')] |
||||
}, |
||||
}, undefined)) |
||||
let res = await codeActions.getCodeActions(doc) |
||||
expect(range).toEqual(Range.create(0, 0, 3, 0)) |
||||
expect(res.length).toBe(4) |
||||
}) |
||||
|
||||
it('should filter actions by range', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.setLines(['', '', ''], { start: 0, end: -1, strictIndexing: false }) |
||||
currActions = [] |
||||
let range: Range |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document: TextDocument, |
||||
r: Range, |
||||
_context: CodeActionContext, _token: CancellationToken |
||||
) => { |
||||
range = r |
||||
if (rangeInRange(r, Range.create(0, 0, 1, 0))) return [CodeAction.create('a')] |
||||
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')] |
||||
}, |
||||
}, undefined)) |
||||
let res = await codeActions.getCodeActions(doc, Range.create(0, 0, 0, 0)) |
||||
expect(range).toEqual(Range.create(0, 0, 0, 0)) |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
|
||||
it('should filter actions by kind prefix', async () => { |
||||
let doc = await helper.createDocument() |
||||
let action = CodeAction.create('my action', CodeActionKind.SourceFixAll) |
||||
currActions = [action] |
||||
let res = await codeActions.getCodeActions(doc, undefined, [CodeActionKind.Source]) |
||||
expect(res.length).toBe(1) |
||||
expect(res[0].kind).toBe(CodeActionKind.SourceFixAll) |
||||
}) |
||||
}) |
||||
|
||||
describe('getCurrentCodeActions', () => { |
||||
let range: Range |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document: TextDocument, |
||||
r: Range, |
||||
_context: CodeActionContext, _token: CancellationToken |
||||
) => { |
||||
range = r |
||||
return [CodeAction.create('a'), CodeAction.create('b'), CodeAction.create('c')] |
||||
}, |
||||
}, undefined)) |
||||
}) |
||||
|
||||
it('should get codeActions by line', async () => { |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
let res = await codeActions.getCurrentCodeActions('line') |
||||
expect(range).toEqual(Range.create(0, 0, 1, 0)) |
||||
expect(res.length).toBe(3) |
||||
}) |
||||
|
||||
it('should get codeActions by cursor', async () => { |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
let res = await codeActions.getCurrentCodeActions('cursor') |
||||
expect(range).toEqual(Range.create(0, 0, 0, 0)) |
||||
expect(res.length).toBe(3) |
||||
}) |
||||
|
||||
it('should get codeActions by visual mode', async () => { |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! 0v$') |
||||
await nvim.input('<esc>') |
||||
let res = await codeActions.getCurrentCodeActions('v') |
||||
expect(range).toEqual(Range.create(0, 0, 0, 4)) |
||||
expect(res.length).toBe(3) |
||||
}) |
||||
}) |
||||
|
||||
describe('doCodeAction', () => { |
||||
it('should not throw when no action exists', async () => { |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
let err |
||||
try { |
||||
await codeActions.doCodeAction(undefined) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeUndefined() |
||||
}) |
||||
|
||||
it('should apply single code action when only is title', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix) |
||||
currActions = [action] |
||||
await codeActions.doCodeAction(undefined, 'code fix') |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
}) |
||||
|
||||
it('should apply single code action when only is codeAction array', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix) |
||||
currActions = [action] |
||||
await codeActions.doCodeAction(undefined, [CodeActionKind.QuickFix]) |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
}) |
||||
|
||||
it('should action dialog to choose action', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix) |
||||
currActions = [action, CodeAction.create('foo')] |
||||
let promise = codeActions.doCodeAction(null) |
||||
await helper.wait(50) |
||||
let ids = await nvim.call('coc#float#get_float_win_list') as number[] |
||||
expect(ids.length).toBeGreaterThan(0) |
||||
await nvim.input('<CR>') |
||||
await promise |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
}) |
||||
|
||||
it('should choose code actions by range', async () => { |
||||
let range: Range |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document: TextDocument, |
||||
r: Range, |
||||
_context: CodeActionContext, _token: CancellationToken |
||||
) => { |
||||
range = r |
||||
return [CodeAction.create('my title'), CodeAction.create('b'), CodeAction.create('c')] |
||||
}, |
||||
}, undefined)) |
||||
await helper.createDocument() |
||||
await nvim.setLine('abc') |
||||
await nvim.command('normal! 0v$') |
||||
await nvim.input('<esc>') |
||||
await codeActions.doCodeAction('v', 'my title') |
||||
expect(range).toEqual({ start: { line: 0, character: 0 }, end: { line: 0, character: 4 } }) |
||||
}) |
||||
}) |
||||
|
||||
describe('doQuickfix', () => { |
||||
it('should throw when quickfix action not exists', async () => { |
||||
let err |
||||
currActions = [] |
||||
await helper.createDocument() |
||||
try { |
||||
await codeActions.doQuickfix() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should do preferred quickfix action', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', edit, CodeActionKind.QuickFix) |
||||
action.isPreferred = true |
||||
currActions = [CodeAction.create('foo', CodeActionKind.QuickFix), action, CodeAction.create('bar')] |
||||
await codeActions.doQuickfix() |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
}) |
||||
}) |
||||
|
||||
describe('applyCodeAction', () => { |
||||
it('should resolve codeAction', async () => { |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', CodeActionKind.QuickFix) |
||||
action.isPreferred = true |
||||
currActions = [action] |
||||
resolvedAction = Object.assign({ edit }, action) |
||||
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix]) |
||||
await codeActions.applyCodeAction(arr[0]) |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
}) |
||||
|
||||
it('should throw for disabled action', async () => { |
||||
let action = CodeAction.create('my action', CodeActionKind.Empty) |
||||
action.disabled = { reason: 'disabled' } |
||||
let err |
||||
try { |
||||
await codeActions.applyCodeAction(action) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should invoke registered command after apply edit', async () => { |
||||
let called |
||||
disposables.push(commands.registerCommand('test.execute', async (s: string) => { |
||||
called = s |
||||
await nvim.command(s) |
||||
})) |
||||
let doc = await helper.createDocument() |
||||
let edits: TextEdit[] = [] |
||||
edits.push(TextEdit.insert(Position.create(0, 0), 'bar')) |
||||
let edit = { changes: { [doc.uri]: edits } } |
||||
let action = CodeAction.create('code fix', CodeActionKind.QuickFix) |
||||
action.isPreferred = true |
||||
currActions = [action] |
||||
resolvedAction = Object.assign({ |
||||
edit, |
||||
command: Command.create('run vim command', 'test.execute', 'normal! $') |
||||
}, action) |
||||
let arr = await codeActions.getCurrentCodeActions('line', [CodeActionKind.QuickFix]) |
||||
await codeActions.applyCodeAction(arr[0]) |
||||
let lines = await doc.buffer.lines |
||||
expect(lines).toEqual(['bar']) |
||||
expect(called).toBe('normal! $') |
||||
}) |
||||
}) |
||||
}) |
@ -1,269 +0,0 @@
@@ -1,269 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, Range, Command, TextEdit, Position } from 'vscode-languageserver-protocol' |
||||
import { disposeAll } from '../../util' |
||||
import languages from '../../languages' |
||||
import commands from '../../commands' |
||||
import CodeLens from '../../handler/codelens/index' |
||||
import helper, { createTmpFile } from '../helper' |
||||
import events from '../../events' |
||||
|
||||
let nvim: Neovim |
||||
let codeLens: CodeLens |
||||
let disposables: Disposable[] = [] |
||||
let srcId: number |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
srcId = await nvim.createNamespace('coc-codelens') |
||||
codeLens = helper.plugin.getHandler().codeLens |
||||
helper.updateConfiguration('codeLens.enable', true) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
helper.updateConfiguration('codeLens.enable', false) |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('codeLenes featrue', () => { |
||||
|
||||
it('should do codeLenes request and resolve codeLenes', async () => { |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}, { |
||||
range: Range.create(1, 0, 1, 1) |
||||
}] |
||||
}, |
||||
resolveCodeLens: codeLens => { |
||||
codeLens.command = Command.create('save', '__save') |
||||
return codeLens |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('example.js') |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await codeLens.checkProvider() |
||||
let buf = codeLens.buffers.getItem(doc.bufnr) |
||||
let codelens = buf.getCodelenses() |
||||
expect(codelens).toBeDefined() |
||||
expect(codelens[0].command).toBeDefined() |
||||
expect(codelens[1].command).toBeDefined() |
||||
let markers = await helper.getMarkers(doc.bufnr, srcId) |
||||
expect(markers.length).toBe(2) |
||||
}) |
||||
|
||||
it('should refresh codeLens on CursorHold', async () => { |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: document => { |
||||
let n = document.lineCount |
||||
let arr: any[] = [] |
||||
for (let i = 0; i <= n - 2; i++) { |
||||
arr.push({ |
||||
range: Range.create(i, 0, i, 1), |
||||
command: Command.create('save', '__save', i) |
||||
}) |
||||
} |
||||
return arr |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('example.js') |
||||
await helper.wait(100) |
||||
let markers = await helper.getMarkers(doc.bufnr, srcId) |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await doc.synchronize() |
||||
await events.fire('CursorHold', [doc.bufnr]) |
||||
await helper.wait(200) |
||||
markers = await helper.getMarkers(doc.bufnr, srcId) |
||||
expect(markers.length).toBe(3) |
||||
}) |
||||
|
||||
it('should cancel codeLenes request on document change', async () => { |
||||
let cancelled = false |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: (_, token) => { |
||||
return new Promise(resolve => { |
||||
token.onCancellationRequested(() => { |
||||
cancelled = true |
||||
clearTimeout(timer) |
||||
resolve(null) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
resolve([{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}, { |
||||
range: Range.create(1, 0, 1, 1) |
||||
}]) |
||||
}, 2000) |
||||
}) |
||||
}, |
||||
resolveCodeLens: codeLens => { |
||||
codeLens.command = Command.create('save', '__save') |
||||
return codeLens |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('codelens.js') |
||||
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'a\nb\nc')]) |
||||
let p = codeLens.checkProvider() |
||||
await doc.applyEdits([TextEdit.replace(Range.create(0, 0, 0, 1), 'foo')]) |
||||
await p |
||||
expect(cancelled).toBe(true) |
||||
let buf = codeLens.buffers.getItem(doc.bufnr) |
||||
let codelens = buf.getCodelenses() |
||||
expect(codelens).toBeUndefined() |
||||
}) |
||||
|
||||
it('should resolve on CursorMoved', async () => { |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(90, 0, 90, 1) |
||||
}, { |
||||
range: Range.create(91, 0, 91, 1) |
||||
}] |
||||
}, |
||||
resolveCodeLens: async codeLens => { |
||||
await helper.wait(50) |
||||
codeLens.command = Command.create('save', '__save') |
||||
return codeLens |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('example.js') |
||||
let arr = new Array(100) |
||||
arr.fill('') |
||||
await nvim.call('setline', [1, arr]) |
||||
await doc.synchronize() |
||||
await codeLens.checkProvider() |
||||
await nvim.command('normal! gg') |
||||
await helper.wait(300) |
||||
await nvim.command('normal! G') |
||||
await helper.wait(300) |
||||
let buf = codeLens.buffers.getItem(doc.bufnr) |
||||
let codelens = buf.getCodelenses() |
||||
expect(codelens).toBeDefined() |
||||
expect(codelens[0].command).toBeDefined() |
||||
expect(codelens[1].command).toBeDefined() |
||||
}) |
||||
|
||||
it('should invoke codeLenes action', async () => { |
||||
let fn = jest.fn() |
||||
disposables.push(commands.registerCommand('__save', (...args) => { |
||||
fn(...args) |
||||
})) |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}] |
||||
}, |
||||
resolveCodeLens: codeLens => { |
||||
codeLens.command = Command.create('save', '__save', 1, 2, 3) |
||||
return codeLens |
||||
} |
||||
})) |
||||
await helper.createDocument('example.js') |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await codeLens.checkProvider() |
||||
await helper.doAction('codeLensAction') |
||||
expect(fn).toBeCalledWith(1, 2, 3) |
||||
}) |
||||
|
||||
it('should use picker fo multiple codeLenses', async () => { |
||||
let fn = jest.fn() |
||||
disposables.push(commands.registerCommand('__save', (...args) => { |
||||
fn(...args) |
||||
})) |
||||
disposables.push(commands.registerCommand('__delete', (...args) => { |
||||
fn(...args) |
||||
})) |
||||
disposables.push(languages.registerCodeLensProvider([{ language: 'javascript' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1), |
||||
command: Command.create('save', '__save', 1, 2, 3) |
||||
}, { |
||||
range: Range.create(0, 1, 0, 2), |
||||
command: Command.create('save', '__delete', 4, 5, 6) |
||||
}] |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('example.js') |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await doc.synchronize() |
||||
await codeLens.checkProvider() |
||||
let p = helper.doAction('codeLensAction') |
||||
await helper.wait(30) |
||||
await nvim.input('<cr>') |
||||
await p |
||||
expect(fn).toBeCalledWith(1, 2, 3) |
||||
}) |
||||
|
||||
it('should refresh for failed codeLens request', async () => { |
||||
let called = 0 |
||||
let fn = jest.fn() |
||||
disposables.push(commands.registerCommand('__save', (...args) => { |
||||
fn(...args) |
||||
})) |
||||
disposables.push(commands.registerCommand('__foo', (...args) => { |
||||
fn(...args) |
||||
})) |
||||
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], { |
||||
provideCodeLenses: () => { |
||||
called++ |
||||
if (called == 1) { |
||||
return null |
||||
} |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1), |
||||
command: Command.create('foo', '__foo') |
||||
}] |
||||
} |
||||
})) |
||||
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1), |
||||
command: Command.create('save', '__save') |
||||
}] |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('example.js') |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await codeLens.checkProvider() |
||||
let markers = await helper.getMarkers(doc.buffer.id, srcId) |
||||
expect(markers.length).toBeGreaterThan(0) |
||||
let codeLensBuffer = codeLens.buffers.getItem(doc.buffer.id) |
||||
await codeLensBuffer.forceFetch() |
||||
let curr = codeLensBuffer.currentCodeLens() |
||||
expect(curr.length).toBeGreaterThan(1) |
||||
expect(called).toBe(2) |
||||
}) |
||||
|
||||
it('should refresh on configuration change', async () => { |
||||
disposables.push(languages.registerCodeLensProvider([{ language: '*' }], { |
||||
provideCodeLenses: () => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1), |
||||
command: Command.create('save', '__save') |
||||
}] |
||||
} |
||||
})) |
||||
let filepath = await createTmpFile('abc') |
||||
let buffer = await helper.edit(filepath) |
||||
await codeLens.checkProvider() |
||||
helper.updateConfiguration('codeLens.enable', false) |
||||
await helper.wait(10) |
||||
let markers = await helper.getMarkers(buffer.id, srcId) |
||||
expect(markers.length).toBe(0) |
||||
helper.updateConfiguration('codeLens.enable', true) |
||||
await helper.wait(300) |
||||
markers = await helper.getMarkers(buffer.id, srcId) |
||||
expect(markers.length).toBeGreaterThan(0) |
||||
}) |
||||
}) |
@ -1,254 +0,0 @@
@@ -1,254 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { CancellationToken, Color, ColorInformation, ColorPresentation, Disposable, Position, Range } from 'vscode-languageserver-protocol' |
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import commands from '../../commands' |
||||
import { toHexString } from '../../util/color' |
||||
import Colors from '../../handler/colors/index' |
||||
import languages from '../../languages' |
||||
import { ProviderResult } from '../../provider' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let state = 'normal' |
||||
let colors: Colors |
||||
let disposables: Disposable[] = [] |
||||
let colorPresentations: ColorPresentation[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
colors = helper.plugin.getHandler().colors |
||||
disposables.push(languages.registerDocumentColorProvider([{ language: '*' }], { |
||||
provideColorPresentations: ( |
||||
_color: Color, |
||||
_context: { document: TextDocument; range: Range }, |
||||
_token: CancellationToken |
||||
): ColorPresentation[] => colorPresentations, |
||||
provideDocumentColors: ( |
||||
document: TextDocument, |
||||
_token: CancellationToken |
||||
): ProviderResult<ColorInformation[]> => { |
||||
if (state == 'empty') return [] |
||||
if (state == 'error') return Promise.reject(new Error('no color')) |
||||
let matches = Array.from((document.getText() as any).matchAll(/#\w{6}/g)) as any |
||||
return matches.map(o => { |
||||
let start = document.positionAt(o.index) |
||||
let end = document.positionAt(o.index + o[0].length) |
||||
return { |
||||
range: Range.create(start, end), |
||||
color: getColor(255, 255, 255) |
||||
} |
||||
}) |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
disposeAll(disposables) |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
colorPresentations = [] |
||||
await helper.reset() |
||||
}) |
||||
|
||||
function getColor(r: number, g: number, b: number): Color { |
||||
return { red: r / 255, green: g / 255, blue: b / 255, alpha: 1 } |
||||
} |
||||
|
||||
describe('Colors', () => { |
||||
describe('utils', () => { |
||||
it('should get hex string', () => { |
||||
let color = getColor(255, 255, 255) |
||||
let hex = toHexString(color) |
||||
expect(hex).toBe('ffffff') |
||||
}) |
||||
}) |
||||
|
||||
describe('configuration', () => { |
||||
it('should toggle enable state on configuration change', async () => { |
||||
await helper.createDocument() |
||||
helper.updateConfiguration('coc.preferences.colorSupport', false) |
||||
expect(colors.enabled).toBe(false) |
||||
helper.updateConfiguration('coc.preferences.colorSupport', true) |
||||
expect(colors.enabled).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('commands', () => { |
||||
it('should register editor.action.pickColor command', async () => { |
||||
await helper.mockFunction('coc#util#pick_color', [0, 0, 0]) |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
await commands.executeCommand('editor.action.pickColor') |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('#000000') |
||||
}) |
||||
|
||||
it('should register editor.action.colorPresentation command', async () => { |
||||
colorPresentations = [ColorPresentation.create('red'), ColorPresentation.create('#ff0000')] |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
let p = commands.executeCommand('editor.action.colorPresentation') |
||||
await helper.wait(100) |
||||
await nvim.input('1<enter>') |
||||
await p |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('red') |
||||
}) |
||||
}) |
||||
|
||||
describe('doHighlight', () => { |
||||
it('should clearHighlight on empty result', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
state = 'empty' |
||||
await colors.doHighlight(doc.bufnr) |
||||
let res = colors.hasColor(doc.bufnr) |
||||
expect(res).toBe(false) |
||||
state = 'normal' |
||||
}) |
||||
|
||||
it('should not highlight on error result', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
state = 'error' |
||||
await colors.doHighlight(doc.bufnr) |
||||
let res = colors.hasColor(doc.bufnr) |
||||
expect(res).toBe(false) |
||||
state = 'normal' |
||||
}) |
||||
|
||||
it('should highlight after document changed', async () => { |
||||
let doc = await helper.createDocument() |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
expect(colors.hasColor(doc.bufnr)).toBe(false) |
||||
expect(colors.hasColorAtPosition(doc.bufnr, Position.create(0, 1))).toBe(false) |
||||
await nvim.setLine('#ffffff #ff0000') |
||||
doc.forceSync() |
||||
await helper.wait(300) |
||||
expect(colors.hasColorAtPosition(doc.bufnr, Position.create(0, 1))).toBe(true) |
||||
expect(colors.hasColor(doc.bufnr)).toBe(true) |
||||
}) |
||||
|
||||
it('should clearHighlight on clearHighlight', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff #ff0000') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
expect(colors.hasColor(doc.bufnr)).toBe(true) |
||||
colors.clearHighlight(doc.bufnr) |
||||
expect(colors.hasColor(doc.bufnr)).toBe(false) |
||||
}) |
||||
|
||||
it('should highlight colors', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
await colors.doHighlight(doc.bufnr) |
||||
let exists = await nvim.call('hlexists', 'BGffffff') |
||||
expect(exists).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('hasColor()', () => { |
||||
it('should return false when bufnr not exists', async () => { |
||||
let res = colors.hasColor(99) |
||||
colors.clearHighlight(99) |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('getColorInformation()', () => { |
||||
it('should return null when highlighter not exists', async () => { |
||||
let res = await colors.getColorInformation(99) |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return null when color not found', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff foo ') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
await nvim.call('cursor', [1, 12]) |
||||
let res = await colors.getColorInformation(doc.bufnr) |
||||
expect(res).toBe(null) |
||||
}) |
||||
}) |
||||
|
||||
describe('hasColorAtPosition()', () => { |
||||
it('should return false when bufnr not exists', async () => { |
||||
let res = colors.hasColorAtPosition(99, Position.create(0, 0)) |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('pickPresentation()', () => { |
||||
it('should show warning when color not exists', async () => { |
||||
await helper.createDocument() |
||||
await colors.pickPresentation() |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('Color not found') |
||||
}) |
||||
|
||||
it('should not throw when presentations not exists', async () => { |
||||
colorPresentations = [] |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(99) |
||||
await colors.doHighlight(doc.bufnr) |
||||
await helper.doAction('colorPresentation') |
||||
}) |
||||
|
||||
it('should pick presentations', async () => { |
||||
colorPresentations = [ColorPresentation.create('red'), ColorPresentation.create('#ff0000')] |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
let p = helper.doAction('colorPresentation') |
||||
await helper.wait(100) |
||||
await nvim.input('1<enter>') |
||||
await p |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('red') |
||||
}) |
||||
}) |
||||
|
||||
describe('pickColor()', () => { |
||||
it('should show warning when color not exists', async () => { |
||||
await helper.createDocument() |
||||
await colors.pickColor() |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('not found') |
||||
}) |
||||
|
||||
it('should pickColor', async () => { |
||||
await helper.mockFunction('coc#util#pick_color', [0, 0, 0]) |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
await helper.doAction('pickColor') |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('#000000') |
||||
}) |
||||
|
||||
it('should not throw when pick color return 0', async () => { |
||||
await helper.mockFunction('coc#util#pick_color', 0) |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('#ffffff') |
||||
doc.forceSync() |
||||
await colors.doHighlight(doc.bufnr) |
||||
await helper.doAction('pickColor') |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('#ffffff') |
||||
}) |
||||
}) |
||||
}) |
@ -1,82 +0,0 @@
@@ -1,82 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable } from 'vscode-languageserver-protocol' |
||||
import CommandsHandler from '../../handler/commands' |
||||
import commandManager from '../../commands' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let commands: CommandsHandler |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
commands = (helper.plugin as any).handler.commands |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Commands', () => { |
||||
describe('addVimCommand', () => { |
||||
it('should register global vim commands', async () => { |
||||
await commandManager.executeCommand('vim.config') |
||||
await helper.wait(50) |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toMatch('coc-settings.json') |
||||
let list = commands.getCommandList() |
||||
expect(list.includes('vim.config')).toBe(true) |
||||
}) |
||||
|
||||
it('should add vim command with title', async () => { |
||||
commands.addVimCommand({ id: 'list', cmd: 'CocList', title: 'list of coc.nvim' }) |
||||
let res = commandManager.titles.get('vim.list') |
||||
expect(res).toBe('list of coc.nvim') |
||||
commandManager.unregister('vim.list') |
||||
}) |
||||
}) |
||||
|
||||
describe('getCommands', () => { |
||||
it('should get command items', async () => { |
||||
let res = commands.getCommands() |
||||
let idx = res.findIndex(o => o.id == 'workspace.showOutput') |
||||
expect(idx != -1).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('repeat', () => { |
||||
it('should repeat command', async () => { |
||||
// let buf = await nvim.buffer
|
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
commands.addVimCommand({ id: 'remove', cmd: 'normal! dd' }) |
||||
await commands.runCommand('vim.remove') |
||||
await helper.wait(50) |
||||
let res = await nvim.call('getline', [1, '$']) |
||||
expect(res).toEqual(['b', 'c']) |
||||
await commands.repeat() |
||||
await helper.wait(50) |
||||
res = await nvim.call('getline', [1, '$']) |
||||
expect(res).toEqual(['c']) |
||||
}) |
||||
}) |
||||
|
||||
describe('runCommand', () => { |
||||
it('should open command list without id', async () => { |
||||
await commands.runCommand() |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toBe('list:///commands') |
||||
}) |
||||
}) |
||||
}) |
@ -1,71 +0,0 @@
@@ -1,71 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, FoldingRange } from 'vscode-languageserver-protocol' |
||||
import FoldHandler from '../../handler/fold' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let folds: FoldHandler |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
folds = (helper.plugin as any).handler.fold |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Folds', () => { |
||||
|
||||
it('should return false when no fold ranges found', async () => { |
||||
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], { |
||||
provideFoldingRanges(_doc) { |
||||
return [] |
||||
} |
||||
})) |
||||
let res = await folds.fold() |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should fold all fold ranges', async () => { |
||||
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], { |
||||
provideFoldingRanges(_doc) { |
||||
return [FoldingRange.create(1, 3), FoldingRange.create(4, 6, 0, 0, 'comment')] |
||||
} |
||||
})) |
||||
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']]) |
||||
let res = await folds.fold() |
||||
expect(res).toBe(true) |
||||
let closed = await nvim.call('foldclosed', [2]) |
||||
expect(closed).toBe(2) |
||||
closed = await nvim.call('foldclosed', [5]) |
||||
expect(closed).toBe(5) |
||||
}) |
||||
|
||||
it('should fold comment ranges', async () => { |
||||
disposables.push(languages.registerFoldingRangeProvider([{ language: '*' }], { |
||||
provideFoldingRanges(_doc) { |
||||
return [FoldingRange.create(1, 3), FoldingRange.create(4, 6, 0, 0, 'comment')] |
||||
} |
||||
})) |
||||
await nvim.call('setline', [1, ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']]) |
||||
let res = await folds.fold('comment') |
||||
expect(res).toBe(true) |
||||
let closed = await nvim.call('foldclosed', [2]) |
||||
expect(closed).toBe(-1) |
||||
closed = await nvim.call('foldclosed', [5]) |
||||
expect(closed).toBe(5) |
||||
}) |
||||
}) |
@ -1,253 +0,0 @@
@@ -1,253 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import window from '../../window' |
||||
import workspace from '../../workspace' |
||||
import Format from '../../handler/format' |
||||
import helper, { createTmpFile } from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let format: Format |
||||
|
||||
beforeAll(async () => { |
||||
let { configurations } = workspace |
||||
configurations.updateUserConfig({ 'coc.preferences.formatOnType': true }) |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
format = helper.plugin.getHandler().format |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', []) |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('format handler', () => { |
||||
describe('documentFormat', () => { |
||||
it('should throw when provider not found', async () => { |
||||
let doc = await helper.createDocument() |
||||
let err |
||||
try { |
||||
await format.documentFormat(doc) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should return false when get empty edits ', async () => { |
||||
disposables.push(languages.registerDocumentFormatProvider(['*'], { |
||||
provideDocumentFormattingEdits: () => { |
||||
return [] |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument() |
||||
let res = await format.documentFormat(doc) |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('formatOnSave', () => { |
||||
it('should not throw when provider not found', async () => { |
||||
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['javascript']) |
||||
let filepath = await createTmpFile('') |
||||
await helper.edit(filepath) |
||||
await nvim.command('setf javascript') |
||||
await nvim.setLine('foo') |
||||
await nvim.command('silent w') |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
it('should invoke format on save', async () => { |
||||
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['text']) |
||||
disposables.push(languages.registerDocumentFormatProvider(['text'], { |
||||
provideDocumentFormattingEdits: document => { |
||||
let lines = document.getText().replace(/\n$/, '').split(/\n/) |
||||
let edits: TextEdit[] = [] |
||||
for (let i = 0; i < lines.length; i++) { |
||||
let text = lines[i] |
||||
if (!text.startsWith(' ')) { |
||||
edits.push(TextEdit.insert(Position.create(i, 0), ' ')) |
||||
} |
||||
} |
||||
return edits |
||||
} |
||||
})) |
||||
let filepath = await createTmpFile('a\nb\nc\n') |
||||
let buf = await helper.edit(filepath) |
||||
await nvim.command('setf text') |
||||
await nvim.command('w') |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([' a', ' b', ' c']) |
||||
}) |
||||
|
||||
it('should cancel when timeout', async () => { |
||||
helper.updateConfiguration('coc.preferences.formatOnSaveFiletypes', ['*']) |
||||
disposables.push(languages.registerDocumentFormatProvider(['*'], { |
||||
provideDocumentFormattingEdits: () => { |
||||
return new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve(undefined) |
||||
}, 2000) |
||||
}) |
||||
} |
||||
})) |
||||
let filepath = await createTmpFile('a\nb\nc\n') |
||||
await helper.edit(filepath) |
||||
let n = Date.now() |
||||
await nvim.command('w') |
||||
expect(Date.now() - n).toBeLessThan(1000) |
||||
}) |
||||
}) |
||||
|
||||
describe('rangeFormat', () => { |
||||
it('should invoke range format', async () => { |
||||
disposables.push(languages.registerDocumentRangeFormatProvider(['text'], { |
||||
provideDocumentRangeFormattingEdits: (_document, range) => { |
||||
let lines: number[] = [] |
||||
for (let i = range.start.line; i <= range.end.line; i++) { |
||||
lines.push(i) |
||||
} |
||||
return lines.map(i => { |
||||
return TextEdit.insert(Position.create(i, 0), ' ') |
||||
}) |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await nvim.command('setf text') |
||||
await nvim.command('normal! ggvG') |
||||
await nvim.input('<esc>') |
||||
await helper.doAction('formatSelected', 'v') |
||||
let buf = nvim.createBuffer(doc.bufnr) |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([' a', ' b', ' c']) |
||||
}) |
||||
|
||||
it('should format range by formatexpr option', async () => { |
||||
let range: Range |
||||
disposables.push(languages.registerDocumentRangeFormatProvider(['text'], { |
||||
provideDocumentRangeFormattingEdits: (_document, r) => { |
||||
range = r |
||||
return [] |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await nvim.command('setf text') |
||||
await nvim.command(`setl formatexpr=CocAction('formatSelected')`) |
||||
await nvim.command('normal! ggvGgq') |
||||
expect(range).toEqual({ |
||||
start: { line: 0, character: 0 }, end: { line: 3, character: 0 } |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
describe('formatOnType', () => { |
||||
it('should invoke format', async () => { |
||||
disposables.push(languages.registerDocumentFormatProvider(['text'], { |
||||
provideDocumentFormattingEdits: () => { |
||||
return [TextEdit.insert(Position.create(0, 0), ' ')] |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
await nvim.command('setf text') |
||||
await helper.doAction('format') |
||||
let line = await nvim.line |
||||
expect(line).toEqual(' foo') |
||||
}) |
||||
|
||||
it('should does format on type', async () => { |
||||
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], { |
||||
provideOnTypeFormattingEdits: () => { |
||||
return [TextEdit.insert(Position.create(0, 0), ' ')] |
||||
} |
||||
}, ['|'])) |
||||
await helper.edit() |
||||
await nvim.command('setf text') |
||||
await nvim.input('i|') |
||||
await helper.wait(200) |
||||
let line = await nvim.line |
||||
expect(line).toBe(' |') |
||||
let cursor = await window.getCursorPosition() |
||||
expect(cursor).toEqual({ line: 0, character: 3 }) |
||||
}) |
||||
|
||||
it('should format on new line inserted', async () => { |
||||
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], { |
||||
provideOnTypeFormattingEdits: (doc, position) => { |
||||
let text = doc.getText() |
||||
if (text.startsWith(' ')) return [] |
||||
return [TextEdit.insert(Position.create(position.line, 0), ' ')] |
||||
} |
||||
}, ['\n'])) |
||||
let buf = await helper.edit() |
||||
await nvim.command('setf text') |
||||
await nvim.setLine('foo') |
||||
await nvim.input('o') |
||||
await helper.wait(100) |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([' foo', '']) |
||||
}) |
||||
|
||||
it('should adjust cursor after format on type', async () => { |
||||
disposables.push(languages.registerOnTypeFormattingEditProvider(['text'], { |
||||
provideOnTypeFormattingEdits: () => { |
||||
return [ |
||||
TextEdit.insert(Position.create(0, 0), ' '), |
||||
TextEdit.insert(Position.create(0, 2), 'end') |
||||
] |
||||
} |
||||
}, ['|'])) |
||||
await helper.edit() |
||||
await nvim.command('setf text') |
||||
await nvim.setLine('"') |
||||
await nvim.input('i|') |
||||
await helper.wait(100) |
||||
let line = await nvim.line |
||||
expect(line).toBe(' |"end') |
||||
let cursor = await window.getCursorPosition() |
||||
expect(cursor).toEqual({ line: 0, character: 3 }) |
||||
}) |
||||
}) |
||||
|
||||
describe('bracketEnterImprove', () => { |
||||
afterEach(() => { |
||||
nvim.command('iunmap <CR>', true) |
||||
}) |
||||
|
||||
it('should format vim file on enter', async () => { |
||||
let buf = await helper.edit('foo.vim') |
||||
await nvim.command(`inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\\<C-g>u\\<CR>\\<c-r>=coc#on_enter()\\<CR>"`) |
||||
await nvim.setLine('let foo={}') |
||||
await nvim.command(`normal! gg$`) |
||||
await nvim.input('i') |
||||
await nvim.eval(`feedkeys("\\<CR>", 'im')`) |
||||
await helper.wait(100) |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual(['let foo={', ' \\ ', ' \\ }']) |
||||
}) |
||||
|
||||
it('should add new line between bracket', async () => { |
||||
let buf = await helper.edit() |
||||
await nvim.command(`inoremap <silent><expr> <cr> pumvisible() ? coc#_select_confirm() : "\\<C-g>u\\<CR>\\<c-r>=coc#on_enter()\\<CR>"`) |
||||
await nvim.setLine(' {}') |
||||
await nvim.command(`normal! gg$`) |
||||
await nvim.input('i') |
||||
await nvim.eval(`feedkeys("\\<CR>", 'im')`) |
||||
await helper.wait(100) |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([' {', ' ', ' }']) |
||||
}) |
||||
}) |
||||
}) |
||||
|
@ -1,138 +0,0 @@
@@ -1,138 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, DocumentHighlightKind, Position, Range } from 'vscode-languageserver-protocol' |
||||
import Highlights from '../../handler/highlights' |
||||
import languages from '../../languages' |
||||
import workspace from '../../workspace' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let highlights: Highlights |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
highlights = helper.plugin.getHandler().documentHighlighter |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
function registProvider(): void { |
||||
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], { |
||||
provideDocumentHighlights: async document => { |
||||
let word = await nvim.eval('expand("<cword>")') |
||||
// let word = document.get
|
||||
let matches = Array.from((document.getText() as any).matchAll(/\w+/g)) as any[] |
||||
let filtered = matches.filter(o => o[0] == word) |
||||
return filtered.map((o, i) => { |
||||
let start = document.positionAt(o.index) |
||||
let end = document.positionAt(o.index + o[0].length) |
||||
return { |
||||
range: Range.create(start, end), |
||||
kind: i % 2 == 0 ? DocumentHighlightKind.Read : DocumentHighlightKind.Write |
||||
} |
||||
}) |
||||
} |
||||
})) |
||||
} |
||||
|
||||
describe('document highlights', () => { |
||||
|
||||
it('should return null when highlights provide not exists', async () => { |
||||
let doc = await helper.createDocument() |
||||
let res = await highlights.getHighlights(doc, Position.create(0, 0)) |
||||
expect(res).toBeNull() |
||||
}) |
||||
|
||||
it('should cancel request on CursorMoved', async () => { |
||||
let fn = jest.fn() |
||||
languages.registerDocumentHighlightProvider([{ language: '*' }], { |
||||
provideDocumentHighlights: (_document, _position, token) => { |
||||
return new Promise(resolve => { |
||||
token.onCancellationRequested(() => { |
||||
clearTimeout(timer) |
||||
fn() |
||||
resolve([]) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
resolve([{ range: Range.create(0, 0, 0, 3) }]) |
||||
}, 3000) |
||||
}) |
||||
} |
||||
}) |
||||
await helper.edit() |
||||
await nvim.setLine('foo') |
||||
let p = highlights.highlight() |
||||
await helper.wait(50) |
||||
await nvim.call('cursor', [1, 2]) |
||||
await p |
||||
expect(fn).toBeCalled() |
||||
}) |
||||
|
||||
it('should add highlights to symbols', async () => { |
||||
registProvider() |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo bar foo') |
||||
await helper.doAction('highlight') |
||||
let winid = await nvim.call('win_getid') as number |
||||
expect(highlights.hasHighlights(winid)).toBe(true) |
||||
}) |
||||
|
||||
it('should return highlight ranges', async () => { |
||||
registProvider() |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo bar foo') |
||||
let res = await helper.doAction('symbolRanges') |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should return null when cursor not in word range', async () => { |
||||
disposables.push(languages.registerDocumentHighlightProvider([{ language: '*' }], { |
||||
provideDocumentHighlights: () => { |
||||
return [{ range: Range.create(0, 0, 0, 3) }] |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine(' oo') |
||||
await nvim.call('cursor', [1, 2]) |
||||
let res = await highlights.getHighlights(doc, Position.create(0, 0)) |
||||
expect(res).toBeNull() |
||||
}) |
||||
|
||||
it('should not throw when document is command line', async () => { |
||||
await nvim.call('feedkeys', ['q:', 'in']) |
||||
let doc = await workspace.document |
||||
expect(doc.isCommandLine).toBe(true) |
||||
let err |
||||
try { |
||||
await highlights.highlight() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
await nvim.input('<C-c>') |
||||
expect(err).toBeUndefined() |
||||
}) |
||||
|
||||
it('should not throw when provider not found', async () => { |
||||
disposeAll(disposables) |
||||
await helper.createDocument() |
||||
await nvim.setLine(' oo') |
||||
await nvim.call('cursor', [1, 2]) |
||||
let err |
||||
try { |
||||
await highlights.highlight() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeUndefined() |
||||
}) |
||||
}) |
@ -1,178 +0,0 @@
@@ -1,178 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, MarkedString, Hover, Range, TextEdit, Position } from 'vscode-languageserver-protocol' |
||||
import HoverHandler from '../../handler/hover' |
||||
import { URI } from 'vscode-uri' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import helper, { createTmpFile } from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let hover: HoverHandler |
||||
let disposables: Disposable[] = [] |
||||
let hoverResult: Hover |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
hover = helper.plugin.getHandler().hover |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
disposables.push(languages.registerHoverProvider([{ language: '*' }], { |
||||
provideHover: (_doc, _pos, _token) => { |
||||
return hoverResult |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
async function getDocumentText(): Promise<string> { |
||||
let lines = await nvim.call('getbufline', ['coc://document', 1, '$']) as string[] |
||||
return lines.join('\n') |
||||
} |
||||
|
||||
describe('Hover', () => { |
||||
describe('onHover', () => { |
||||
it('should return false when hover not found', async () => { |
||||
hoverResult = null |
||||
let res = await hover.onHover('preview') |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should show MarkupContent hover', async () => { |
||||
hoverResult = { contents: { kind: 'plaintext', value: 'my hover' } } |
||||
await hover.onHover('preview') |
||||
let res = await getDocumentText() |
||||
expect(res).toMatch('my hover') |
||||
}) |
||||
|
||||
it('should show MarkedString hover', async () => { |
||||
hoverResult = { contents: 'string hover' } |
||||
disposables.push(languages.registerHoverProvider([{ language: '*' }], { |
||||
provideHover: (_doc, _pos, _token) => { |
||||
return { contents: { language: 'typescript', value: 'language hover' } } |
||||
} |
||||
})) |
||||
await hover.onHover('preview') |
||||
let res = await getDocumentText() |
||||
expect(res).toMatch('string hover') |
||||
expect(res).toMatch('language hover') |
||||
}) |
||||
|
||||
it('should show MarkedString hover array', async () => { |
||||
hoverResult = { contents: ['foo', { language: 'typescript', value: 'bar' }] } |
||||
await hover.onHover('preview') |
||||
let res = await getDocumentText() |
||||
expect(res).toMatch('foo') |
||||
expect(res).toMatch('bar') |
||||
}) |
||||
|
||||
it('should highlight hover range', async () => { |
||||
await nvim.setLine('var') |
||||
await nvim.command('normal! 0') |
||||
hoverResult = { contents: ['foo'], range: Range.create(0, 0, 0, 3) } |
||||
await hover.onHover('preview') |
||||
let res = await nvim.call('getmatches') as any[] |
||||
expect(res.length).toBe(1) |
||||
expect(res[0].group).toBe('CocHoverRange') |
||||
await helper.wait(600) |
||||
res = await nvim.call('getmatches') |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('previewHover', () => { |
||||
it('should echo hover message', async () => { |
||||
hoverResult = { contents: ['foo'] } |
||||
let res = await hover.onHover('echo') |
||||
expect(res).toBe(true) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('foo') |
||||
}) |
||||
|
||||
it('should show hover in float window', async () => { |
||||
hoverResult = { contents: { kind: 'markdown', value: '```typescript\nconst foo:number\n```' } } |
||||
await hover.onHover('float') |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await nvim.eval(`getbufline(winbufnr(${win.id}),1,'$')`) |
||||
expect(lines).toEqual(['const foo:number']) |
||||
}) |
||||
}) |
||||
|
||||
describe('getHover', () => { |
||||
it('should get hover from MarkedString array', async () => { |
||||
hoverResult = { contents: ['foo', { language: 'typescript', value: 'bar' }] } |
||||
disposables.push(languages.registerHoverProvider([{ language: '*' }], { |
||||
provideHover: (_doc, _pos, _token) => { |
||||
return { contents: { language: 'typescript', value: 'MarkupContent hover' } } |
||||
} |
||||
})) |
||||
disposables.push(languages.registerHoverProvider([{ language: '*' }], { |
||||
provideHover: (_doc, _pos, _token) => { |
||||
return { contents: MarkedString.fromPlainText('MarkedString hover') } |
||||
} |
||||
})) |
||||
let res = await hover.getHover() |
||||
expect(res.includes('foo')).toBe(true) |
||||
expect(res.includes('bar')).toBe(true) |
||||
expect(res.includes('MarkupContent hover')).toBe(true) |
||||
expect(res.includes('MarkedString hover')).toBe(true) |
||||
}) |
||||
|
||||
it('should filter empty hover message', async () => { |
||||
hoverResult = { contents: [''] } |
||||
let res = await hover.getHover() |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('definitionHover', () => { |
||||
it('should load definition from buffer', async () => { |
||||
hoverResult = { contents: 'string hover' } |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('cursor', [1, 1]) |
||||
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')]) |
||||
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], { |
||||
provideDefinition() { |
||||
return [{ |
||||
targetUri: doc.uri, |
||||
targetRange: Range.create(0, 0, 1, 3), |
||||
targetSelectionRange: Range.create(0, 0, 0, 3), |
||||
}] |
||||
} |
||||
})) |
||||
await hover.definitionHover('preview') |
||||
let res = await getDocumentText() |
||||
expect(res).toBe('string hover\n\nfoo\nbar') |
||||
}) |
||||
|
||||
it('should load definition link from file', async () => { |
||||
let fsPath = await createTmpFile('foo\nbar\n') |
||||
hoverResult = { contents: 'string hover' } |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('cursor', [1, 1]) |
||||
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar')]) |
||||
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], { |
||||
provideDefinition() { |
||||
return [{ |
||||
targetUri: URI.file(fsPath).toString(), |
||||
targetRange: Range.create(0, 0, 1, 3), |
||||
targetSelectionRange: Range.create(0, 0, 0, 3), |
||||
}] |
||||
} |
||||
})) |
||||
await hover.definitionHover('preview') |
||||
let res = await getDocumentText() |
||||
expect(res).toBe('string hover\n\nfoo\nbar') |
||||
}) |
||||
}) |
||||
}) |
@ -1,93 +0,0 @@
@@ -1,93 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable } from 'vscode-languageserver-protocol' |
||||
import Handler from '../../handler/index' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let handler: Handler |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
handler = (helper.plugin as any).handler |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Handler', () => { |
||||
describe('hasProvider', () => { |
||||
it('should check provider for document', async () => { |
||||
let res = await handler.hasProvider('definition') |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('checkProvier', () => { |
||||
it('should throw error when provider not found', async () => { |
||||
let doc = await helper.createDocument() |
||||
let err |
||||
try { |
||||
handler.checkProvier('definition', doc.textDocument) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('withRequestToken', () => { |
||||
it('should cancel previous request when called again', async () => { |
||||
let cancelled = false |
||||
let p = handler.withRequestToken('test', token => { |
||||
return new Promise(s => { |
||||
token.onCancellationRequested(() => { |
||||
cancelled = true |
||||
clearTimeout(timer) |
||||
s(undefined) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
s(undefined) |
||||
}, 3000) |
||||
}) |
||||
}, false) |
||||
setTimeout(async () => { |
||||
await handler.withRequestToken('test', () => { |
||||
return Promise.resolve(undefined) |
||||
}, false) |
||||
}, 50) |
||||
await p |
||||
expect(cancelled).toBe(true) |
||||
}) |
||||
|
||||
it('should cancel request on insert start', async () => { |
||||
let cancelled = false |
||||
let p = handler.withRequestToken('test', token => { |
||||
return new Promise(s => { |
||||
token.onCancellationRequested(() => { |
||||
cancelled = true |
||||
clearTimeout(timer) |
||||
s(undefined) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
s(undefined) |
||||
}, 3000) |
||||
}) |
||||
}, false) |
||||
await nvim.input('i') |
||||
await p |
||||
expect(cancelled).toBe(true) |
||||
}) |
||||
}) |
||||
}) |
@ -1,99 +0,0 @@
@@ -1,99 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, DocumentLink, Range } from 'vscode-languageserver-protocol' |
||||
import LinksHandler from '../../handler/links' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let links: LinksHandler |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
links = (helper.plugin as any).handler.links |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Links', () => { |
||||
it('should get document links', async () => { |
||||
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], { |
||||
provideDocumentLinks: (_doc, _token) => { |
||||
return [ |
||||
DocumentLink.create(Range.create(0, 0, 0, 5), 'test:///foo'), |
||||
DocumentLink.create(Range.create(1, 0, 1, 5), 'test:///bar') |
||||
] |
||||
} |
||||
})) |
||||
let res = await links.getLinks() |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should throw error when link target not resolved', async () => { |
||||
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], { |
||||
provideDocumentLinks(_doc, _token) { |
||||
return [ |
||||
DocumentLink.create(Range.create(0, 0, 0, 5)) |
||||
] |
||||
}, |
||||
resolveDocumentLink(link) { |
||||
return link |
||||
} |
||||
})) |
||||
let res = await links.getLinks() |
||||
let err |
||||
try { |
||||
await links.openLink(res[0]) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should open link at current position', async () => { |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! 0') |
||||
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], { |
||||
provideDocumentLinks(_doc, _token) { |
||||
return [ |
||||
DocumentLink.create(Range.create(0, 0, 0, 5)), |
||||
] |
||||
}, |
||||
resolveDocumentLink(link) { |
||||
link.target = 'test:///foo' |
||||
return link |
||||
} |
||||
})) |
||||
await links.openCurrentLink() |
||||
let bufname = await nvim.call('bufname', '%') |
||||
expect(bufname).toBe('test:///foo') |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await nvim.call('cursor', [3, 1]) |
||||
let res = await links.openCurrentLink() |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should return false when current links not found', async () => { |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! 0') |
||||
disposables.push(languages.registerDocumentLinkProvider([{ language: '*' }], { |
||||
provideDocumentLinks(_doc, _token) { |
||||
return [] |
||||
} |
||||
})) |
||||
let res = await links.openCurrentLink() |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
@ -1,306 +0,0 @@
@@ -1,306 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, LocationLink, Location, Range } from 'vscode-languageserver-protocol' |
||||
import LocationHandler from '../../handler/locations' |
||||
import languages from '../../languages' |
||||
import services from '../../services' |
||||
import workspace from '../../workspace' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
import { URI } from 'vscode-uri' |
||||
|
||||
let nvim: Neovim |
||||
let locations: LocationHandler |
||||
let disposables: Disposable[] = [] |
||||
let currLocations: Location[] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
Object.assign(workspace.env, { |
||||
locationlist: false |
||||
}) |
||||
locations = helper.plugin.getHandler().locations |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
await helper.createDocument() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
function createLocation(name: string, sl: number, sc: number, el: number, ec: number): Location { |
||||
return Location.create(`test://${name}`, Range.create(sl, sc, el, ec)) |
||||
} |
||||
|
||||
describe('locations', () => { |
||||
describe('reference', () => { |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerReferencesProvider([{ language: '*' }], { |
||||
provideReferences: () => { |
||||
return currLocations |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
it('should get references', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)] |
||||
let res = await locations.references() |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should jump to references', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0)] |
||||
let res = await locations.gotoReferences('edit', true) |
||||
expect(res).toBe(true) |
||||
let name = await nvim.call('bufname', ['%']) |
||||
expect(name).toBe('test://foo') |
||||
}) |
||||
|
||||
it('should return false when references not found', async () => { |
||||
currLocations = [] |
||||
let res = await locations.gotoReferences('edit', true) |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('definition', () => { |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], { |
||||
provideDefinition: () => { |
||||
return currLocations |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
it('should get definitions', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)] |
||||
let res = await locations.definitions() |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should jump to definitions', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0)] |
||||
let res = await locations.gotoDefinition('edit') |
||||
expect(res).toBe(true) |
||||
let name = await nvim.call('bufname', ['%']) |
||||
expect(name).toBe('test://foo') |
||||
}) |
||||
|
||||
it('should return false when definitions not found', async () => { |
||||
currLocations = [] |
||||
let res = await locations.gotoDefinition('edit') |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('declaration', () => { |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerDeclarationProvider([{ language: '*' }], { |
||||
provideDeclaration: () => { |
||||
return currLocations |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
it('should get declarations', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)] |
||||
let res = await locations.declarations() as Location[] |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should jump to declaration', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0)] |
||||
let res = await locations.gotoDeclaration('edit') |
||||
expect(res).toBe(true) |
||||
let name = await nvim.call('bufname', ['%']) |
||||
expect(name).toBe('test://foo') |
||||
}) |
||||
|
||||
it('should return false when declaration not found', async () => { |
||||
currLocations = [] |
||||
let res = await locations.gotoDeclaration('edit') |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('typeDefinition', () => { |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerTypeDefinitionProvider([{ language: '*' }], { |
||||
provideTypeDefinition: () => { |
||||
return currLocations |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
it('should get type definition', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)] |
||||
let res = await locations.typeDefinitions() as Location[] |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should jump to type definition', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0)] |
||||
let res = await locations.gotoTypeDefinition('edit') |
||||
expect(res).toBe(true) |
||||
let name = await nvim.call('bufname', ['%']) |
||||
expect(name).toBe('test://foo') |
||||
}) |
||||
|
||||
it('should return false when type definition not found', async () => { |
||||
currLocations = [] |
||||
let res = await locations.gotoTypeDefinition('edit') |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('implementation', () => { |
||||
beforeEach(() => { |
||||
disposables.push(languages.registerImplementationProvider([{ language: '*' }], { |
||||
provideImplementation: () => { |
||||
return currLocations |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
it('should get implementations', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0), createLocation('bar', 0, 0, 0, 0)] |
||||
let res = await locations.implementations() as Location[] |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should jump to implementation', async () => { |
||||
currLocations = [createLocation('foo', 0, 0, 0, 0)] |
||||
let res = await locations.gotoImplementation('edit') |
||||
expect(res).toBe(true) |
||||
let name = await nvim.call('bufname', ['%']) |
||||
expect(name).toBe('test://foo') |
||||
}) |
||||
|
||||
it('should return false when implementation not found', async () => { |
||||
currLocations = [] |
||||
let res = await locations.gotoImplementation('edit') |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('getTagList', () => { |
||||
it('should return null when cword not exists', async () => { |
||||
let res = await locations.getTagList() |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return null when provider not exists', async () => { |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! ^') |
||||
let res = await locations.getTagList() |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return null when result is empty', async () => { |
||||
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], { |
||||
provideDefinition: () => { |
||||
return [] |
||||
} |
||||
})) |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! ^') |
||||
let res = await locations.getTagList() |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return tag definitions', async () => { |
||||
disposables.push(languages.registerDefinitionProvider([{ language: '*' }], { |
||||
provideDefinition: () => { |
||||
return [createLocation('bar', 2, 0, 2, 5), Location.create(URI.file('/foo').toString(), Range.create(1, 0, 1, 5))] |
||||
} |
||||
})) |
||||
await nvim.setLine('foo') |
||||
await nvim.command('normal! ^') |
||||
let res = await locations.getTagList() |
||||
expect(res).toEqual([ |
||||
{ |
||||
name: 'foo', |
||||
cmd: 'keepjumps 3 | normal 1|', |
||||
filename: 'test://bar' |
||||
}, |
||||
{ name: 'foo', cmd: 'keepjumps 2 | normal 1|', filename: '/foo' } |
||||
]) |
||||
}) |
||||
}) |
||||
|
||||
describe('findLocations', () => { |
||||
// hook result
|
||||
let fn |
||||
let result: any |
||||
beforeAll(() => { |
||||
fn = services.sendRequest |
||||
services.sendRequest = () => { |
||||
return Promise.resolve(result) |
||||
} |
||||
}) |
||||
|
||||
afterAll(() => { |
||||
services.sendRequest = fn |
||||
}) |
||||
|
||||
it('should handle locations from language client', async () => { |
||||
result = [createLocation('bar', 2, 0, 2, 5)] |
||||
await locations.findLocations('foo', 'mylocation', {}, false) |
||||
let res = await nvim.getVar('coc_jump_locations') |
||||
expect(res).toEqual([{ |
||||
uri: 'test://bar', |
||||
lnum: 3, |
||||
col: 1, |
||||
filename: 'test://bar', |
||||
text: '', |
||||
range: Range.create(2, 0, 2, 5) |
||||
}]) |
||||
}) |
||||
|
||||
it('should handle nested locations', async () => { |
||||
let location: any = { |
||||
location: createLocation('file', 0, 0, 0, 0), |
||||
children: [{ |
||||
location: createLocation('foo', 3, 0, 3, 5), |
||||
children: [] |
||||
}, { |
||||
location: createLocation('bar', 4, 0, 4, 5), |
||||
children: [] |
||||
}] |
||||
} |
||||
result = location |
||||
await locations.findLocations('foo', 'mylocation', {}, false) |
||||
let res = await nvim.getVar('coc_jump_locations') as any[] |
||||
expect(res.length).toBe(3) |
||||
}) |
||||
}) |
||||
|
||||
describe('handleLocations', () => { |
||||
it('should not throw when location is undefined', async () => { |
||||
await locations.handleLocations(null) |
||||
}) |
||||
|
||||
it('should not throw when locations is empty array', async () => { |
||||
await locations.handleLocations([]) |
||||
}) |
||||
|
||||
it('should handle single location', async () => { |
||||
await locations.handleLocations(createLocation('single', 0, 0, 0, 0)) |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toBe('test://single') |
||||
}) |
||||
|
||||
it('should handle location link', async () => { |
||||
let link = LocationLink.create('test://link', Range.create(0, 0, 0, 3), Range.create(1, 0, 1, 3)) |
||||
await locations.handleLocations([link]) |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toBe('test://link') |
||||
}) |
||||
}) |
||||
}) |
@ -1,426 +0,0 @@
@@ -1,426 +0,0 @@
|
||||
import { Buffer, Neovim } from '@chemzqm/neovim' |
||||
import { CancellationToken, CodeAction, CodeActionContext, CodeActionKind, Disposable, Range, SymbolTag, TextEdit } from 'vscode-languageserver-protocol' |
||||
import events from '../../events' |
||||
import Symbols from '../../handler/symbols/index' |
||||
import languages from '../../languages' |
||||
import { ProviderResult } from '../../provider' |
||||
import { disposeAll } from '../../util' |
||||
import workspace from '../../workspace' |
||||
import helper from '../helper' |
||||
import Parser from './parser' |
||||
|
||||
let nvim: Neovim |
||||
let symbols: Symbols |
||||
let disposables: Disposable[] = [] |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
symbols = helper.plugin.getHandler().symbols |
||||
}) |
||||
|
||||
beforeEach(() => { |
||||
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'javascript' }], { |
||||
provideDocumentSymbols: document => { |
||||
let parser = new Parser(document.getText()) |
||||
let res = parser.parse() |
||||
if (res.length) { |
||||
res[0].tags = [SymbolTag.Deprecated] |
||||
} |
||||
return Promise.resolve(res) |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
async function getOutlineBuffer(): Promise<Buffer | undefined> { |
||||
let winid = await nvim.call('coc#window#find', ['cocViewId', 'OUTLINE']) |
||||
if (winid == -1) return undefined |
||||
let bufnr = await nvim.call('winbufnr', [winid]) |
||||
if (bufnr == -1) return undefined |
||||
return nvim.createBuffer(bufnr) |
||||
} |
||||
|
||||
describe('symbols outline', () => { |
||||
|
||||
let defaultCode = `class myClass {
|
||||
fun1() { } |
||||
fun2() {} |
||||
}` |
||||
|
||||
async function createBuffer(code = defaultCode): Promise<Buffer> { |
||||
await helper.edit() |
||||
let buf = await nvim.buffer |
||||
await nvim.command('setf javascript') |
||||
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false }) |
||||
let doc = await workspace.document |
||||
doc.forceSync() |
||||
return buf |
||||
} |
||||
|
||||
describe('configuration', () => { |
||||
afterEach(() => { |
||||
let { configurations } = workspace |
||||
configurations.updateUserConfig({ |
||||
'outline.splitCommand': 'botright 30vs', |
||||
'outline.followCursor': true, |
||||
'outline.keepWindow': false, |
||||
'outline.sortBy': 'category', |
||||
'outline.expandLevel': 1, |
||||
'outline.checkBufferSwitch': true |
||||
}) |
||||
}) |
||||
|
||||
it('should follow cursor', async () => { |
||||
await createBuffer() |
||||
let curr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await nvim.command('wincmd p') |
||||
await nvim.command('exe 3') |
||||
await events.fire('CursorHold', [curr]) |
||||
await helper.wait(50) |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let lines = await buf.getLines() |
||||
expect(lines).toEqual([ |
||||
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3' |
||||
]) |
||||
let signs = await buf.getSigns({ group: 'CocTree' }) |
||||
expect(signs.length).toBe(1) |
||||
expect(signs[0]).toEqual({ |
||||
lnum: 2, |
||||
id: 3001, |
||||
name: 'CocTreeSelected', |
||||
priority: 10, |
||||
group: 'CocTree' |
||||
}) |
||||
}) |
||||
|
||||
it('should not follow cursor', async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.followCursor': false, |
||||
}) |
||||
await createBuffer() |
||||
let curr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await nvim.command('wincmd p') |
||||
await nvim.command('exe 3') |
||||
await events.fire('CursorHold', [curr]) |
||||
await helper.wait(50) |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let signs = await buf.getSigns({ group: 'CocTree' }) |
||||
expect(signs.length).toBe(0) |
||||
}) |
||||
|
||||
it('should keep current window', async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.keepWindow': true, |
||||
}) |
||||
await createBuffer() |
||||
let curr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline() |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
expect(curr).toBe(bufnr) |
||||
}) |
||||
|
||||
it('should check on buffer switch', async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.checkBufferSwitch': true, |
||||
}) |
||||
await createBuffer() |
||||
await symbols.showOutline(1) |
||||
await helper.edit('unnamed') |
||||
await helper.wait(300) |
||||
let buf = await getOutlineBuffer() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual(['Document symbol provider not found']) |
||||
}) |
||||
|
||||
it('should not check on buffer switch', async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.checkBufferSwitch': false |
||||
}) |
||||
await helper.wait(30) |
||||
await createBuffer() |
||||
await symbols.showOutline(1) |
||||
await helper.edit('unnamed') |
||||
await helper.wait(100) |
||||
let buf = await getOutlineBuffer() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3' |
||||
]) |
||||
}) |
||||
|
||||
it('should not check on buffer reload', async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.checkBufferSwitch': false |
||||
}) |
||||
await symbols.showOutline(1) |
||||
await helper.wait(50) |
||||
await createBuffer() |
||||
await helper.wait(50) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeUndefined() |
||||
}) |
||||
|
||||
it('should sort by position', async () => { |
||||
let code = `class myClass {
|
||||
fun2() { } |
||||
fun1() {} |
||||
}` |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.sortBy': 'position', |
||||
}) |
||||
await createBuffer(code) |
||||
await symbols.showOutline(1) |
||||
let buf = await getOutlineBuffer() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTLINE', '- c myClass 1', ' m fun2 2', ' m fun1 3' |
||||
]) |
||||
}) |
||||
|
||||
it('should sort by name', async () => { |
||||
let code = `class myClass {
|
||||
fun2() {} |
||||
fun1() {} |
||||
}` |
||||
workspace.configurations.updateUserConfig({ |
||||
'outline.sortBy': 'name', |
||||
}) |
||||
await createBuffer(code) |
||||
await symbols.showOutline(1) |
||||
let buf = await getOutlineBuffer() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTLINE', '- c myClass 1', ' m fun1 3', ' m fun2 2' |
||||
]) |
||||
}) |
||||
}) |
||||
|
||||
describe('events', () => { |
||||
|
||||
it('should dispose on buffer unload', async () => { |
||||
await createBuffer() |
||||
let curr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
await nvim.command('tabe') |
||||
await nvim.command(`bd! ${curr}`) |
||||
await helper.wait(30) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeUndefined() |
||||
}) |
||||
|
||||
it('should check current window on BufEnter', async () => { |
||||
await createBuffer() |
||||
await symbols.showOutline(0) |
||||
let winid = await nvim.call('win_getid', []) |
||||
await nvim.command('enew') |
||||
await helper.wait(200) |
||||
let win = await nvim.window |
||||
expect(win.id).toBe(winid) |
||||
}) |
||||
|
||||
it('should recreated when original window exists', async () => { |
||||
await symbols.showOutline(1) |
||||
await helper.wait(50) |
||||
await createBuffer() |
||||
await helper.wait(50) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeDefined() |
||||
}) |
||||
|
||||
it('should keep old outline when new buffer not attached', async () => { |
||||
await createBuffer() |
||||
await symbols.showOutline(1) |
||||
await nvim.command(`vnew +setl\\ buftype=nofile`) |
||||
await helper.wait(50) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeDefined() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'OUTLINE', '- c myClass 1', ' m fun1 2', ' m fun2 3' |
||||
]) |
||||
}) |
||||
|
||||
it('should not reload when switch to original buffer', async () => { |
||||
await createBuffer() |
||||
await symbols.showOutline(0) |
||||
let buf = await getOutlineBuffer() |
||||
let name = await buf.name |
||||
await nvim.command('wincmd p') |
||||
await helper.wait(50) |
||||
buf = await getOutlineBuffer() |
||||
let curr = await buf.name |
||||
expect(curr).toBe(name) |
||||
}) |
||||
|
||||
it('should dispose provider on outline hide', async () => { |
||||
await createBuffer() |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
await nvim.command('q') |
||||
await helper.wait(30) |
||||
let exists = symbols.hasOutline(bufnr) |
||||
expect(exists).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('show()', () => { |
||||
it('should throw when document not attached', async () => { |
||||
await nvim.command(`edit +setl\\ buftype=nofile t`) |
||||
await helper.wait(50) |
||||
let err |
||||
try { |
||||
await symbols.showOutline(1) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should not throw when provider not exists', async () => { |
||||
await symbols.showOutline(1) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeDefined() |
||||
}) |
||||
|
||||
it('should not throw when symbols is empty', async () => { |
||||
await createBuffer('') |
||||
await symbols.showOutline(1) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeDefined() |
||||
}) |
||||
|
||||
it('should jump to selected symbol', async () => { |
||||
await createBuffer() |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
await helper.wait(50) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<cr>') |
||||
await helper.wait(50) |
||||
let curr = await nvim.call('bufnr', ['%']) |
||||
expect(curr).toBe(bufnr) |
||||
let cursor = await nvim.call('coc#cursor#position') |
||||
expect(cursor).toEqual([1, 2]) |
||||
}) |
||||
|
||||
it('should update symbols', async () => { |
||||
await createBuffer() |
||||
let doc = await workspace.document |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(1) |
||||
await helper.wait(10) |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let code = 'class foo{}' |
||||
await buf.setLines(code.split('\n'), { |
||||
start: 0, |
||||
end: -1, |
||||
strictIndexing: false |
||||
}) |
||||
await doc.synchronize() |
||||
await helper.wait(200) |
||||
buf = await getOutlineBuffer() |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual([ |
||||
'No results', |
||||
'', |
||||
'OUTLINE' |
||||
]) |
||||
}) |
||||
}) |
||||
|
||||
describe('actions', () => { |
||||
it('should invoke visual select', async () => { |
||||
await createBuffer() |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(0) |
||||
await helper.wait(50) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('<cr>') |
||||
await helper.wait(50) |
||||
let m = await nvim.mode |
||||
expect(m.mode).toBe('v') |
||||
let buf = await nvim.buffer |
||||
expect(buf.id).toBe(bufnr) |
||||
}) |
||||
|
||||
it('should invoke selected code action', async () => { |
||||
const codeAction = CodeAction.create('my action', CodeActionKind.Refactor) |
||||
let uri: string |
||||
disposables.push(languages.registerCodeActionProvider([{ language: '*' }], { |
||||
provideCodeActions: ( |
||||
_document, |
||||
_range: Range, |
||||
_context: CodeActionContext, |
||||
_token: CancellationToken |
||||
) => [codeAction], |
||||
resolveCodeAction: (action): ProviderResult<CodeAction> => { |
||||
action.edit = { |
||||
changes: { |
||||
[uri]: [TextEdit.del(Range.create(0, 0, 0, 5))] |
||||
} |
||||
} |
||||
return action |
||||
} |
||||
}, undefined)) |
||||
await createBuffer() |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
let doc = workspace.getDocument(bufnr) |
||||
uri = doc.uri |
||||
await symbols.showOutline(0) |
||||
await helper.wait(50) |
||||
await nvim.command('exe 3') |
||||
await nvim.input('<tab>') |
||||
await helper.wait(50) |
||||
await nvim.input('<cr>') |
||||
await helper.wait(200) |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toBe(' myClass {') |
||||
}) |
||||
}) |
||||
|
||||
describe('hide()', () => { |
||||
it('should hide outline', async () => { |
||||
await createBuffer('') |
||||
await symbols.showOutline(0) |
||||
await symbols.hideOutline() |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeUndefined() |
||||
}) |
||||
|
||||
it('should not throw when outline not exists', async () => { |
||||
await symbols.hideOutline() |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeUndefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('dispose', () => { |
||||
it('should dispose provider and views', async () => { |
||||
await createBuffer('') |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await symbols.showOutline(1) |
||||
symbols.dispose() |
||||
await helper.wait(50) |
||||
expect(symbols.hasOutline(bufnr)).toBe(false) |
||||
let buf = await getOutlineBuffer() |
||||
expect(buf).toBeUndefined() |
||||
}) |
||||
}) |
||||
}) |
@ -1,117 +0,0 @@
@@ -1,117 +0,0 @@
|
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import { DocumentSymbol, SymbolKind, Range } from 'vscode-languageserver-protocol' |
||||
|
||||
/** |
||||
* A syntax parser that parse `class` and `method` only. |
||||
*/ |
||||
export default class Parser { |
||||
private _curr = 0 |
||||
private _symbols: DocumentSymbol[] = [] |
||||
private currSymbol: DocumentSymbol | undefined |
||||
private len: number |
||||
private textDocument: TextDocument |
||||
constructor(private _content: string) { |
||||
this.len = _content.length |
||||
this.textDocument = TextDocument.create('test:///a', 'txt', 1, _content) |
||||
} |
||||
|
||||
public parse(): DocumentSymbol[] { |
||||
while (this._curr <= this.len - 1) { |
||||
this.parseToken() |
||||
} |
||||
return this._symbols |
||||
} |
||||
|
||||
/** |
||||
* Parse a symbol, reset currSymbol & _curr |
||||
*/ |
||||
private parseToken(): void { |
||||
this.skipSpaces() |
||||
if (this.currSymbol) { |
||||
let endOffset = this.textDocument.offsetAt(this.currSymbol.range.end) |
||||
if (this._curr > endOffset) { |
||||
this.currSymbol = undefined |
||||
} |
||||
} |
||||
let remain = this.getLineRemian() |
||||
let ms = remain.match(/^(class)\s(\w+)\s\{\s*/) |
||||
if (ms) { |
||||
// find class
|
||||
let start = this._curr + 6 |
||||
let end = start + ms[2].length |
||||
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end)) |
||||
let endPosition = this.findMatchedIndex(this._curr + ms[0].length) |
||||
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition)) |
||||
let symbolInfo: DocumentSymbol = { |
||||
range, |
||||
selectionRange, |
||||
kind: SymbolKind.Class, |
||||
name: ms[2], |
||||
children: [] |
||||
} |
||||
if (this.currSymbol && this.currSymbol.children) { |
||||
this.currSymbol.children.push(symbolInfo) |
||||
} else { |
||||
this._symbols.push(symbolInfo) |
||||
} |
||||
this.currSymbol = symbolInfo |
||||
} else if (this.currSymbol && this.currSymbol.kind == SymbolKind.Class) { |
||||
let ms = remain.match(/(\w+)\(.*\)\s*\{/) |
||||
if (ms) { |
||||
// find method
|
||||
let start = this._curr |
||||
let end = start + ms[1].length |
||||
let selectionRange = Range.create(this.textDocument.positionAt(start), this.textDocument.positionAt(end)) |
||||
let endPosition = this.findMatchedIndex(this._curr + ms[0].length) |
||||
let range = Range.create(this.textDocument.positionAt(this._curr), this.textDocument.positionAt(endPosition)) |
||||
let symbolInfo: DocumentSymbol = { |
||||
range, |
||||
selectionRange, |
||||
kind: SymbolKind.Method, |
||||
name: ms[1] |
||||
} |
||||
if (this.currSymbol && this.currSymbol.children) { |
||||
this.currSymbol.children.push(symbolInfo) |
||||
} else { |
||||
this._symbols.push(symbolInfo) |
||||
} |
||||
} |
||||
} |
||||
this._curr = this._curr + remain.length + 1 |
||||
} |
||||
|
||||
private findMatchedIndex(start: number): number { |
||||
let level = 0 |
||||
for (let i = start; i < this.len; i++) { |
||||
let ch = this._content[i] |
||||
if (ch == '{') { |
||||
level = level + 1 |
||||
} |
||||
if (ch == '}') { |
||||
if (level == 0) return i |
||||
level = level - 1 |
||||
} |
||||
} |
||||
throw new Error(`Can't find matched }`) |
||||
} |
||||
|
||||
private getLineRemian(): string { |
||||
let chars = '' |
||||
for (let i = this._curr; i < this.len; i++) { |
||||
let ch = this._content[i] |
||||
if (ch == '\n') break |
||||
chars = chars + ch |
||||
} |
||||
return chars |
||||
} |
||||
|
||||
private skipSpaces(): void { |
||||
for (let i = this._curr; i < this.len; i++) { |
||||
let ch = this._content[i] |
||||
if (!ch || /\S/.test(ch)) { |
||||
this._curr = i |
||||
break |
||||
} |
||||
} |
||||
} |
||||
} |
@ -1,347 +0,0 @@
@@ -1,347 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import fs from 'fs' |
||||
import { URI } from 'vscode-uri' |
||||
import Refactor, { FileItem } from '../../handler/refactor/index' |
||||
import helper, { createTmpFile } from '../helper' |
||||
import languages from '../../languages' |
||||
import { WorkspaceEdit, Range } from 'vscode-languageserver-types' |
||||
import { Disposable } from '@chemzqm/neovim/lib/api/Buffer' |
||||
|
||||
let nvim: Neovim |
||||
let refactor: Refactor |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
refactor = helper.plugin.getHandler().refactor |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
refactor.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('refactor', () => { |
||||
describe('create', () => { |
||||
it('should create from workspaceEdit', async () => { |
||||
let changes = { |
||||
[URI.file(__filename).toString()]: [{ |
||||
range: Range.create(0, 0, 0, 6), |
||||
newText: '' |
||||
}, { |
||||
range: Range.create(1, 0, 1, 6), |
||||
newText: '' |
||||
}] |
||||
} |
||||
let edit: WorkspaceEdit = { changes } |
||||
let buf = await refactor.fromWorkspaceEdit(edit) |
||||
let shown = await buf.valid |
||||
expect(shown).toBe(true) |
||||
let items = buf.fileItems |
||||
expect(items.length).toBe(1) |
||||
}) |
||||
|
||||
it('should create from locations', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
let locations = [{ |
||||
uri, |
||||
range: Range.create(0, 0, 0, 6), |
||||
}, { |
||||
uri, |
||||
range: Range.create(1, 0, 1, 6), |
||||
}] |
||||
let buf = await refactor.fromLocations(locations) |
||||
let shown = await buf.valid |
||||
expect(shown).toBe(true) |
||||
let items = buf.fileItems |
||||
expect(items.length).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('onChange', () => { |
||||
it('should ignore when change after range', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.append(['foo', 'bar']) |
||||
await refactor.fromLocations([{ uri: doc.uri, range: Range.create(0, 0, 0, 3) }]) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
await doc.buffer.append(['def']) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let newLines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(newLines) |
||||
}) |
||||
|
||||
it('should adjust when change before range', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.append(['', '', '', '', 'foo', 'bar']) |
||||
await helper.wait(50) |
||||
doc.forceSync() |
||||
let buf = await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }]) |
||||
await doc.buffer.setLines(['def'], { start: 0, end: 0, strictIndexing: false }) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let fileRange = buf.getFileRange(4) |
||||
expect(fileRange.start).toBe(2) |
||||
expect(fileRange.end).toBe(8) |
||||
}) |
||||
|
||||
it('should removed when lines empty', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.append(['', '', '', '', 'foo', 'bar']) |
||||
await helper.wait(50) |
||||
doc.forceSync() |
||||
let buf = await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }]) |
||||
await doc.buffer.setLines([], { start: 0, end: -1, strictIndexing: false }) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines.length).toBe(3) |
||||
let items = buf.fileItems |
||||
expect(items.length).toBe(0) |
||||
}) |
||||
|
||||
it('should change when liens changed', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.append(['', '', '', '', 'foo', 'bar']) |
||||
await helper.wait(50) |
||||
doc.forceSync() |
||||
await refactor.fromLocations([{ uri: doc.uri, range: Range.create(4, 0, 4, 3) }]) |
||||
await doc.buffer.setLines(['def'], { start: 5, end: 6, strictIndexing: false }) |
||||
doc.forceSync() |
||||
await helper.wait(30) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines[lines.length - 2]).toBe('def') |
||||
}) |
||||
}) |
||||
|
||||
describe('refactor#getFileChanges', () => { |
||||
it('should get changes #1', async () => { |
||||
await helper.createDocument() |
||||
let lines = ` |
||||
Save current buffer to make changes |
||||
\u3000 |
||||
\u3000 |
||||
\u3000/a.ts |
||||
}) |
||||
} ` |
||||
let buf = await refactor.fromLines(lines.split('\n')) |
||||
let changes = await buf.getFileChanges() |
||||
expect(changes).toEqual([{ lnum: 5, filepath: '/a.ts', lines: [' })', ' } '] }]) |
||||
}) |
||||
|
||||
it('should get changes #2', async () => { |
||||
let lines = ` |
||||
\u3000/a.ts |
||||
}) |
||||
} ` |
||||
let buf = await refactor.fromLines(lines.split('\n')) |
||||
let changes = await buf.getFileChanges() |
||||
expect(changes).toEqual([{ lnum: 2, filepath: '/a.ts', lines: [' })', ' } '] }]) |
||||
}) |
||||
|
||||
it('should get changes #3', async () => { |
||||
let lines = ` |
||||
\u3000/a.ts |
||||
}) |
||||
} |
||||
\u3000` |
||||
let buf = await refactor.fromLines(lines.split('\n')) |
||||
let changes = await buf.getFileChanges() |
||||
expect(changes).toEqual([{ lnum: 2, filepath: '/a.ts', lines: [' })', ' }'] }]) |
||||
}) |
||||
|
||||
it('should get changes #4', async () => { |
||||
let lines = ` |
||||
\u3000/a.ts |
||||
foo |
||||
\u3000/b.ts |
||||
bar |
||||
\u3000` |
||||
let buf = await refactor.fromLines(lines.split('\n')) |
||||
let changes = await buf.getFileChanges() |
||||
expect(changes).toEqual([ |
||||
{ filepath: '/a.ts', lnum: 2, lines: ['foo'] }, |
||||
{ filepath: '/b.ts', lnum: 4, lines: ['bar'] } |
||||
]) |
||||
}) |
||||
}) |
||||
|
||||
describe('Refactor#createRefactorBuffer', () => { |
||||
it('should create refactor buffer', async () => { |
||||
await helper.createDocument() |
||||
let winid = await nvim.call('win_getid') |
||||
let buf = await refactor.createRefactorBuffer() |
||||
let curr = await nvim.call('win_getid') |
||||
expect(curr).toBeGreaterThan(winid) |
||||
let valid = await buf.valid |
||||
expect(valid).toBe(true) |
||||
}) |
||||
|
||||
it('should jump to position by <CR>', async () => { |
||||
await helper.createDocument() |
||||
let buf = await refactor.createRefactorBuffer() |
||||
let fileItem: FileItem = { |
||||
filepath: __filename, |
||||
ranges: [{ start: 10, end: 11 }, { start: 15, end: 20 }] |
||||
} |
||||
await buf.addFileItems([fileItem]) |
||||
await nvim.call('cursor', [5, 1]) |
||||
await buf.splitOpen() |
||||
let line = await nvim.eval('line(".")') |
||||
let bufname = await nvim.eval('bufname("%")') |
||||
expect(bufname).toMatch('refactor.test.ts') |
||||
expect(line).toBe(11) |
||||
}) |
||||
}) |
||||
|
||||
describe('Refactor#saveRefactor', () => { |
||||
it('should adjust line ranges after change', async () => { |
||||
let filename = await createTmpFile('foo\n\nbar\n') |
||||
let fileItem: FileItem = { |
||||
filepath: filename, |
||||
ranges: [{ start: 0, end: 1 }, { start: 2, end: 3 }] |
||||
} |
||||
let buf = await refactor.createRefactorBuffer() |
||||
await buf.addFileItems([fileItem]) |
||||
nvim.pauseNotification() |
||||
nvim.call('setline', [5, ['xyz']], true) |
||||
nvim.command('undojoin', true) |
||||
nvim.call('append', [5, ['de']], true) |
||||
nvim.command('undojoin', true) |
||||
nvim.call('append', [8, ['bar']], true) |
||||
await nvim.resumeNotification() |
||||
await helper.wait(100) |
||||
let res = await refactor.save(buf.buffer.id) |
||||
expect(res).toBe(true) |
||||
let content = fs.readFileSync(filename, 'utf8') |
||||
expect(content).toBe('xyz\nde\n\nbar\nbar\n') |
||||
}) |
||||
|
||||
it('should not save when no change made', async () => { |
||||
let buf = await refactor.createRefactorBuffer() |
||||
let fileItem: FileItem = { |
||||
filepath: __filename, |
||||
ranges: [{ start: 10, end: 11 }, { start: 15, end: 20 }] |
||||
} |
||||
await buf.addFileItems([fileItem]) |
||||
let res = await buf.save() |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should sync buffer change to file', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.buffer.replace(['foo', 'bar', 'line'], 0) |
||||
await helper.wait(30) |
||||
let filename = URI.parse(doc.uri).fsPath |
||||
let fileItem: FileItem = { |
||||
filepath: filename, |
||||
ranges: [{ start: 0, end: 2 }] |
||||
} |
||||
let buf = await refactor.createRefactorBuffer() |
||||
await buf.addFileItems([fileItem]) |
||||
await nvim.call('setline', [5, 'changed']) |
||||
let res = await buf.save() |
||||
expect(res).toBe(true) |
||||
expect(fs.existsSync(filename)).toBe(true) |
||||
let content = fs.readFileSync(filename, 'utf8') |
||||
let lines = content.split('\n') |
||||
expect(lines).toEqual(['changed', 'bar', 'line', '']) |
||||
fs.unlinkSync(filename) |
||||
}) |
||||
}) |
||||
|
||||
describe('doRefactor', () => { |
||||
let disposable: Disposable |
||||
|
||||
afterEach(() => { |
||||
if (disposable) disposable.dispose() |
||||
disposable = null |
||||
}) |
||||
|
||||
it('should throw when rename provider not found', async () => { |
||||
await helper.createDocument() |
||||
let err |
||||
try { |
||||
await refactor.doRefactor() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should show message when prepare failed', async () => { |
||||
await helper.createDocument() |
||||
disposable = languages.registerRenameProvider(['*'], { |
||||
prepareRename: () => { |
||||
return undefined |
||||
}, |
||||
provideRenameEdits: () => { |
||||
return null |
||||
} |
||||
}) |
||||
await refactor.doRefactor() |
||||
let res = await helper.getCmdline() |
||||
expect(res).toMatch(/unable to rename/) |
||||
}) |
||||
|
||||
it('should show message when returned edits is null', async () => { |
||||
await helper.createDocument() |
||||
disposable = languages.registerRenameProvider(['*'], { |
||||
provideRenameEdits: () => { |
||||
return null |
||||
} |
||||
}) |
||||
await refactor.doRefactor() |
||||
let res = await helper.getCmdline() |
||||
expect(res).toMatch(/returns null/) |
||||
}) |
||||
|
||||
it('should open refactor window when edits is valid', async () => { |
||||
let filepath = __filename |
||||
disposable = languages.registerRenameProvider(['*'], { |
||||
provideRenameEdits: () => { |
||||
let changes = { |
||||
[URI.file(filepath).toString()]: [{ |
||||
range: Range.create(0, 0, 0, 6), |
||||
newText: '' |
||||
}, { |
||||
range: Range.create(1, 0, 1, 6), |
||||
newText: '' |
||||
}] |
||||
} |
||||
let edit: WorkspaceEdit = { changes } |
||||
return edit |
||||
} |
||||
}) |
||||
await helper.createDocument(filepath) |
||||
let winid = await nvim.call('win_getid') |
||||
await refactor.doRefactor() |
||||
let currWin = await nvim.call('win_getid') |
||||
expect(currWin - winid).toBeGreaterThan(0) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
let b = refactor.getBuffer(bufnr) |
||||
expect(b).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('search', () => { |
||||
it('should open refactor buffer from search result', async () => { |
||||
let escaped = await nvim.call('fnameescape', [__dirname]) |
||||
await nvim.command(`cd ${escaped}`) |
||||
await helper.createDocument() |
||||
await refactor.search(['registerRenameProvider']) |
||||
let buf = await nvim.buffer |
||||
let name = await buf.name |
||||
expect(name).toMatch(/__coc_refactor__/) |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toMatch(/Save current buffer/) |
||||
}) |
||||
}) |
||||
}) |
@ -1,263 +0,0 @@
@@ -1,263 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol' |
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import Rename from '../../handler/rename' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let rename: Rename |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
rename = helper.plugin.getHandler().rename |
||||
}) |
||||
|
||||
function getWordRangeAtPosition(doc: TextDocument, position: Position): Range | null { |
||||
let lines = doc.getText().split(/\r?\n/) |
||||
let line = lines[position.line] |
||||
if (line.length == 0 || position.character >= line.length) return null |
||||
if (!/\w/.test(line[position.character])) return null |
||||
let start = position.character |
||||
let end = position.character + 1 |
||||
if (!/\w/.test(line[start])) { |
||||
return Range.create(position, { line: position.line, character: position.character + 1 }) |
||||
} |
||||
while (start >= 0) { |
||||
let ch = line[start - 1] |
||||
if (!ch || !/\w/.test(ch)) break |
||||
start = start - 1 |
||||
} |
||||
while (end <= line.length) { |
||||
let ch = line[end] |
||||
if (!ch || !/\w/.test(ch)) break |
||||
end = end + 1 |
||||
} |
||||
return Range.create(position.line, start, position.line, end) |
||||
} |
||||
|
||||
function getSymbolRanges(textDocument: TextDocument, word: string): Range[] { |
||||
let res: Range[] = [] |
||||
let str = '' |
||||
let content = textDocument.getText() |
||||
for (let i = 0, l = content.length; i < l; i++) { |
||||
let ch = content[i] |
||||
if ('-' == ch && str.length == 0) { |
||||
continue |
||||
} |
||||
let isKeyword = /\w/.test(ch) |
||||
if (isKeyword) { |
||||
str = str + ch |
||||
} |
||||
if (str.length > 0 && !isKeyword && str == word) { |
||||
res.push(Range.create(textDocument.positionAt(i - str.length), textDocument.positionAt(i))) |
||||
} |
||||
if (!isKeyword) { |
||||
str = '' |
||||
} |
||||
} |
||||
return res |
||||
} |
||||
|
||||
beforeEach(() => { |
||||
disposables.push(languages.registerRenameProvider([{ language: 'javascript' }], { |
||||
provideRenameEdits: (doc, position: Position, newName: string) => { |
||||
let range = getWordRangeAtPosition(doc, position) |
||||
if (range) { |
||||
let word = doc.getText(range) |
||||
if (word) { |
||||
let ranges = getSymbolRanges(doc, word) |
||||
return { |
||||
changes: { |
||||
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return undefined |
||||
}, |
||||
prepareRename: (doc, position) => { |
||||
let range = getWordRangeAtPosition(doc, position) |
||||
return range ? { range, placeholder: doc.getText(range) } : null |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('rename handler', () => { |
||||
describe('getWordEdit', () => { |
||||
it('should not throw when provider not found', async () => { |
||||
await helper.edit() |
||||
let res = await rename.getWordEdit() |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return null when prepare failed', async () => { |
||||
let doc = await helper.createDocument('t.js') |
||||
await nvim.setLine('你') |
||||
await doc.synchronize() |
||||
let res = await rename.getWordEdit() |
||||
expect(res).toBe(null) |
||||
}) |
||||
|
||||
it('should return workspace edit', async () => { |
||||
let doc = await helper.createDocument('t.js') |
||||
await nvim.setLine('foo foo') |
||||
await doc.synchronize() |
||||
let res = await rename.getWordEdit() |
||||
expect(res).toBeDefined() |
||||
expect(res.changes[doc.uri].length).toBe(2) |
||||
}) |
||||
|
||||
it('should extract words from buffer', async () => { |
||||
let doc = await helper.createDocument('t') |
||||
await nvim.setLine('你 你 你') |
||||
await doc.synchronize() |
||||
let res = await rename.getWordEdit() |
||||
expect(res).toBeDefined() |
||||
expect(res.changes[doc.uri].length).toBe(3) |
||||
}) |
||||
}) |
||||
|
||||
describe('rename', () => { |
||||
it('should throw when provider not found', async () => { |
||||
await helper.edit() |
||||
let err |
||||
try { |
||||
await rename.rename('foo') |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should return false for invalid position', async () => { |
||||
await helper.createDocument('t.js') |
||||
let res = await rename.rename('foo') |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should use newName from placeholder', async () => { |
||||
await helper.createDocument('t.js') |
||||
await nvim.setLine('foo foo foo') |
||||
let p = rename.rename() |
||||
await helper.wait(50) |
||||
await nvim.input('<C-u>') |
||||
await helper.wait(10) |
||||
await nvim.input('bar') |
||||
await nvim.input('<cr>') |
||||
let res = await p |
||||
expect(res).toBe(true) |
||||
}) |
||||
|
||||
it('should return false for empty name', async () => { |
||||
await helper.createDocument('t.js') |
||||
await nvim.setLine('foo foo foo') |
||||
let p = rename.rename() |
||||
await helper.wait(50) |
||||
await nvim.input('<C-u>') |
||||
await helper.wait(20) |
||||
await nvim.input('<cr>') |
||||
let res = await p |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should use newName from range', async () => { |
||||
disposables.push(languages.registerRenameProvider([{ language: '*' }], { |
||||
provideRenameEdits: (doc, position: Position, newName: string) => { |
||||
let range = getWordRangeAtPosition(doc, position) |
||||
if (range) { |
||||
let word = doc.getText(range) |
||||
if (word) { |
||||
let ranges = getSymbolRanges(doc, word) |
||||
return { |
||||
changes: { |
||||
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return undefined |
||||
}, |
||||
prepareRename: (doc, position) => { |
||||
let range = getWordRangeAtPosition(doc, position) |
||||
return range ? range : null |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo foo foo') |
||||
let p = rename.rename() |
||||
await helper.wait(50) |
||||
await nvim.input('<C-u>') |
||||
await helper.wait(10) |
||||
await nvim.input('bar') |
||||
await nvim.input('<cr>') |
||||
let res = await p |
||||
expect(res).toBe(true) |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('bar bar bar') |
||||
}) |
||||
|
||||
it('should use newName from cword', async () => { |
||||
disposables.push(languages.registerRenameProvider([{ language: '*' }], { |
||||
provideRenameEdits: (doc, position: Position, newName: string) => { |
||||
let range = getWordRangeAtPosition(doc, position) |
||||
if (range) { |
||||
let word = doc.getText(range) |
||||
if (word) { |
||||
let ranges = getSymbolRanges(doc, word) |
||||
return { |
||||
changes: { |
||||
[doc.uri]: ranges.map(o => TextEdit.replace(o, newName)) |
||||
} |
||||
} |
||||
} |
||||
} |
||||
return undefined |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo foo foo') |
||||
let p = rename.rename() |
||||
await helper.wait(50) |
||||
await nvim.input('<C-u>') |
||||
await helper.wait(10) |
||||
await nvim.input('bar') |
||||
await nvim.input('<cr>') |
||||
let res = await p |
||||
expect(res).toBe(true) |
||||
let line = await nvim.getLine() |
||||
expect(line).toBe('bar bar bar') |
||||
}) |
||||
|
||||
it('should return false when result is empty', async () => { |
||||
disposables.push(languages.registerRenameProvider([{ language: '*' }], { |
||||
provideRenameEdits: () => { |
||||
return null |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo foo foo') |
||||
let p = rename.rename() |
||||
await helper.wait(50) |
||||
await nvim.input('<C-u>') |
||||
await helper.wait(10) |
||||
await nvim.input('bar') |
||||
await nvim.input('<cr>') |
||||
let res = await p |
||||
expect(res).toBe(false) |
||||
}) |
||||
}) |
||||
}) |
@ -1,101 +0,0 @@
@@ -1,101 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import Refactor from '../../handler/refactor' |
||||
import Search, { getPathFromArgs } from '../../handler/search' |
||||
import helper from '../helper' |
||||
import path from 'path' |
||||
|
||||
let nvim: Neovim |
||||
let refactor: Refactor |
||||
// use fake rg command
|
||||
let cmd = path.resolve(__dirname, '../rg') |
||||
let cwd = process.cwd() |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
refactor = helper.plugin.getHandler().refactor |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
refactor.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('getPathFromArgs', () => { |
||||
it('should get undefined path', async () => { |
||||
let res = getPathFromArgs(['a']) |
||||
expect(res).toBeUndefined() |
||||
res = getPathFromArgs(['a', 'b', '-c']) |
||||
expect(res).toBeUndefined() |
||||
res = getPathFromArgs(['a', '-b', 'c']) |
||||
expect(res).toBeUndefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('search', () => { |
||||
|
||||
it('should open refactor window', async () => { |
||||
let search = new Search(nvim, cmd) |
||||
let buf = await refactor.createRefactorBuffer() |
||||
await search.run([], cwd, buf) |
||||
await helper.wait(50) |
||||
let fileItems = buf.fileItems |
||||
expect(fileItems.length).toBe(2) |
||||
expect(fileItems[0].ranges.length).toBe(2) |
||||
}) |
||||
|
||||
it('should abort task', async () => { |
||||
let search = new Search(nvim, cmd) |
||||
let buf = await refactor.createRefactorBuffer() |
||||
let p = search.run(['--sleep', '1000'], cwd, buf) |
||||
search.abort() |
||||
await p |
||||
let fileItems = buf.fileItems |
||||
expect(fileItems.length).toBe(0) |
||||
}) |
||||
|
||||
it('should work with CocAction search', async () => { |
||||
await helper.doAction('search', ['CocAction']) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
let buf = refactor.getBuffer(bufnr) |
||||
expect(buf).toBeDefined() |
||||
}) |
||||
|
||||
it('should fail on invalid command', async () => { |
||||
let search = new Search(nvim, 'rrg') |
||||
let buf = await refactor.createRefactorBuffer() |
||||
let err |
||||
try { |
||||
await search.run([], cwd, buf) |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch(/Error on command "rrg"/) |
||||
}) |
||||
|
||||
it('should show empty result when no result found', async () => { |
||||
await helper.doAction('search', ['should found ' + ' no result']) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
let buf = refactor.getBuffer(bufnr) |
||||
expect(buf).toBeDefined() |
||||
let buffer = await nvim.buffer |
||||
let lines = await buffer.lines |
||||
expect(lines[1]).toMatch(/No match found/) |
||||
}) |
||||
|
||||
it('should use corrent search folder for rg', async () => { |
||||
let search = new Search(nvim, 'rg') |
||||
await helper.createDocument() |
||||
let buf = await refactor.createRefactorBuffer() |
||||
await search.run(['-w', 'createRefactorBuffer', 'src/__tests__'], cwd, buf) |
||||
let buffer = await nvim.buffer |
||||
let lines = await buffer.lines |
||||
expect(lines[1].startsWith('Files: ')).toBe(true) |
||||
}) |
||||
}) |
@ -1,143 +0,0 @@
@@ -1,143 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, Position, Range, TextEdit } from 'vscode-languageserver-protocol' |
||||
import SelectionRange from '../../handler/selectionRange' |
||||
import languages from '../../languages' |
||||
import workspace from '../../workspace' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let selection: SelectionRange |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
selection = helper.plugin.getHandler().selectionRange |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('selectionRange', () => { |
||||
describe('getSelectionRanges()', () => { |
||||
it('should throw error when selectionRange provider not exists', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.synchronize() |
||||
let err |
||||
try { |
||||
await selection.getSelectionRanges() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
|
||||
it('should return ranges', async () => { |
||||
await helper.createDocument() |
||||
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], { |
||||
provideSelectionRanges: _doc => { |
||||
return [{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}] |
||||
} |
||||
})) |
||||
let res = await selection.getSelectionRanges() |
||||
expect(res).toBeDefined() |
||||
expect(Array.isArray(res)).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('selectRange()', () => { |
||||
async function getSelectedRange(): Promise<Range> { |
||||
let m = await nvim.mode |
||||
expect(m.mode).toBe('v') |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
await nvim.input('<esc>') |
||||
let doc = workspace.getDocument(bufnr) |
||||
let res = await workspace.getSelectedRange('v', doc) |
||||
return res |
||||
} |
||||
|
||||
it('should select ranges forward', async () => { |
||||
let doc = await helper.createDocument() |
||||
let called = 0 |
||||
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar\ntest\n')]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], { |
||||
provideSelectionRanges: _doc => { |
||||
called += 1 |
||||
let arr = [{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}, { |
||||
range: Range.create(0, 0, 0, 3) |
||||
}, { |
||||
range: Range.create(0, 0, 1, 3) |
||||
}] |
||||
return arr |
||||
} |
||||
})) |
||||
await doc.synchronize() |
||||
await selection.selectRange('', false) |
||||
await selection.selectRange('', true) |
||||
expect(called).toBe(1) |
||||
let res = await getSelectedRange() |
||||
expect(res).toEqual(Range.create(0, 0, 0, 1)) |
||||
await selection.selectRange('v', true) |
||||
expect(called).toBe(2) |
||||
res = await getSelectedRange() |
||||
expect(res).toEqual(Range.create(0, 0, 0, 3)) |
||||
await selection.selectRange('v', true) |
||||
expect(called).toBe(3) |
||||
res = await getSelectedRange() |
||||
expect(res).toEqual(Range.create(0, 0, 1, 3)) |
||||
await selection.selectRange('v', true) |
||||
expect(called).toBe(4) |
||||
let m = await nvim.mode |
||||
expect(m.mode).toBe('n') |
||||
}) |
||||
|
||||
it('should select ranges backward', async () => { |
||||
let doc = await helper.createDocument() |
||||
await doc.applyEdits([TextEdit.insert(Position.create(0, 0), 'foo\nbar\ntest\n')]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
disposables.push(languages.registerSelectionRangeProvider([{ language: '*' }], { |
||||
provideSelectionRanges: _doc => { |
||||
let arr = [{ |
||||
range: Range.create(0, 0, 0, 1) |
||||
}, { |
||||
range: Range.create(0, 0, 0, 3) |
||||
}, { |
||||
range: Range.create(0, 0, 1, 3) |
||||
}] |
||||
return arr |
||||
} |
||||
})) |
||||
await doc.synchronize() |
||||
await selection.selectRange('', true) |
||||
let mode = await nvim.call('mode') |
||||
expect(mode).toBe('v') |
||||
await nvim.input('<esc>') |
||||
await workspace.selectRange(Range.create(0, 0, 1, 3)) |
||||
await nvim.input('<esc>') |
||||
await selection.selectRange('v', false) |
||||
let r = await getSelectedRange() |
||||
expect(r).toEqual(Range.create(0, 0, 0, 3)) |
||||
await nvim.input('<esc>') |
||||
await selection.selectRange('v', false) |
||||
r = await getSelectedRange() |
||||
expect(r).toEqual(Range.create(0, 0, 0, 1)) |
||||
await nvim.input('<esc>') |
||||
await selection.selectRange('v', false) |
||||
mode = await nvim.call('mode') |
||||
expect(mode).toBe('n') |
||||
}) |
||||
}) |
||||
}) |
@ -1,180 +0,0 @@
@@ -1,180 +0,0 @@
|
||||
import { Buffer, Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, SemanticTokensLegend } from 'vscode-languageserver-protocol' |
||||
import languages from '../../languages' |
||||
import SemanticTokensHighlights from '../../handler/semanticTokensHighlights/index' |
||||
import { disposeAll } from '../../util' |
||||
import workspace from '../../workspace' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let highlighter: SemanticTokensHighlights |
||||
let legend: SemanticTokensLegend = { |
||||
tokenTypes: [ |
||||
"comment", |
||||
"keyword", |
||||
"string", |
||||
"number", |
||||
"regexp", |
||||
"operator", |
||||
"namespace", |
||||
"type", |
||||
"struct", |
||||
"class", |
||||
"interface", |
||||
"enum", |
||||
"enumMember", |
||||
"typeParameter", |
||||
"function", |
||||
"method", |
||||
"property", |
||||
"macro", |
||||
"variable", |
||||
"parameter", |
||||
"angle", |
||||
"arithmetic", |
||||
"attribute", |
||||
"bitwise", |
||||
"boolean", |
||||
"brace", |
||||
"bracket", |
||||
"builtinType", |
||||
"character", |
||||
"colon", |
||||
"comma", |
||||
"comparison", |
||||
"constParameter", |
||||
"dot", |
||||
"escapeSequence", |
||||
"formatSpecifier", |
||||
"generic", |
||||
"label", |
||||
"lifetime", |
||||
"logical", |
||||
"operator", |
||||
"parenthesis", |
||||
"punctuation", |
||||
"selfKeyword", |
||||
"semicolon", |
||||
"typeAlias", |
||||
"union", |
||||
"unresolvedReference" |
||||
], |
||||
tokenModifiers: [ |
||||
"documentation", |
||||
"declaration", |
||||
"definition", |
||||
"static", |
||||
"abstract", |
||||
"deprecated", |
||||
"readonly", |
||||
"constant", |
||||
"controlFlow", |
||||
"injected", |
||||
"mutable", |
||||
"consuming", |
||||
"async", |
||||
"library", |
||||
"public", |
||||
"unsafe", |
||||
"attribute", |
||||
"trait", |
||||
"callable", |
||||
"intraDocLink" |
||||
] |
||||
} |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
highlighter = helper.plugin.getHandler().semanticHighlighter |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
async function createBuffer(code: string): Promise<Buffer> { |
||||
let buf = await nvim.buffer |
||||
await nvim.command('setf rust') |
||||
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false }) |
||||
let doc = await workspace.document |
||||
doc.forceSync() |
||||
return buf |
||||
} |
||||
|
||||
disposables.push(languages.registerDocumentSemanticTokensProvider([{ language: 'rust' }], { |
||||
provideDocumentSemanticTokens: () => { |
||||
return { |
||||
resultId: '1', |
||||
data: [ |
||||
0, 0, 2, 1, 0, |
||||
0, 3, 4, 14, 2, |
||||
0, 4, 1, 41, 0, |
||||
0, 1, 1, 41, 0, |
||||
0, 2, 1, 25, 0, |
||||
1, 4, 8, 17, 0, |
||||
0, 8, 1, 41, 0, |
||||
0, 1, 3, 2, 0, |
||||
0, 3, 1, 41, 0, |
||||
0, 1, 1, 44, 0, |
||||
1, 0, 1, 25, 0, |
||||
] |
||||
} |
||||
} |
||||
}, legend)) |
||||
await createBuffer(`fn main() {
|
||||
println!("H"); |
||||
}`)
|
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
workspace.configurations.updateUserConfig({ |
||||
'coc.preferences.semanticTokensHighlights': true |
||||
}) |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('semanticTokens', () => { |
||||
describe('triggerSemanticTokens', () => { |
||||
it('should be disabled', async () => { |
||||
await helper.createDocument() |
||||
workspace.configurations.updateUserConfig({ |
||||
'coc.preferences.semanticTokensHighlights': false |
||||
}) |
||||
const curr = await highlighter.getCurrentItem() |
||||
let err |
||||
try { |
||||
curr.checkState() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
expect(err.message).toMatch('disabled by configuration') |
||||
}) |
||||
|
||||
it('should get legend by API', async () => { |
||||
const doc = await workspace.document |
||||
const l = languages.getLegend(doc.textDocument) |
||||
expect(l).toEqual(legend) |
||||
}) |
||||
|
||||
it('should get semanticTokens by API', async () => { |
||||
// const doc = await workspace.document
|
||||
// const highlights = await highlighter.getHighlights(doc.bufnr)
|
||||
// expect(highlights.length).toBe(11)
|
||||
// expect(highlights[0].hlGroup).toBe('CocSem_keyword')
|
||||
}) |
||||
|
||||
it('should doHighlight', async () => { |
||||
const doc = await workspace.document |
||||
await nvim.call('CocAction', 'semanticHighlight') |
||||
const highlights = await nvim.call("coc#highlight#get_highlights", [doc.bufnr, 'semanticTokens']) |
||||
expect(highlights.length).toBe(11) |
||||
expect(highlights[0].hlGroup).toBe('CocSem_keyword') |
||||
}) |
||||
}) |
||||
}) |
@ -1,376 +0,0 @@
@@ -1,376 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, Position, ParameterInformation, SignatureInformation } from 'vscode-languageserver-protocol' |
||||
import languages from '../../languages' |
||||
import { disposeAll } from '../../util' |
||||
import Signature from '../../handler/signature' |
||||
import workspace from '../../workspace' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let signature: Signature |
||||
let disposables: Disposable[] = [] |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
signature = helper.plugin.getHandler().signature |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('signatureHelp', () => { |
||||
|
||||
describe('triggerSignatureHelp', () => { |
||||
it('should show signature by api', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, [])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo') |
||||
await signature.triggerSignatureHelp() |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines[2]).toMatch('my signature') |
||||
}) |
||||
|
||||
it('should trigger by space', async () => { |
||||
let called = false |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
called = true |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, [' '])) |
||||
await helper.createDocument() |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input(' ') |
||||
await helper.wait(50) |
||||
expect(called).toBe(true) |
||||
}) |
||||
|
||||
it('should show signature help with param label as string', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [ |
||||
SignatureInformation.create('foo()', 'my signature'), |
||||
SignatureInformation.create('foo(a, b)', 'my signature', ParameterInformation.create('a', 'description')), |
||||
], |
||||
activeParameter: 0, |
||||
activeSignature: 1 |
||||
} |
||||
} |
||||
}, [])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo') |
||||
await signature.triggerSignatureHelp() |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines.join('\n')).toMatch(/description/) |
||||
}) |
||||
|
||||
it('should consider coc_last_placeholder on select mode', async () => { |
||||
let pos: Position |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, position) => { |
||||
pos = position |
||||
return { |
||||
signatures: [ |
||||
SignatureInformation.create('foo(a, b)', 'my signature', ParameterInformation.create('a', 'description')), |
||||
], |
||||
activeParameter: 1, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, [])) |
||||
let doc = await helper.createDocument() |
||||
let line = await nvim.call('line', ['.']) |
||||
await nvim.setLine(' fn(abc, def)') |
||||
await nvim.command('normal! 0fave') |
||||
await nvim.input('<C-g>') |
||||
let placeholder = { |
||||
bufnr: doc.bufnr, |
||||
start: Position.create(line - 1, 5), |
||||
end: Position.create(line - 1, 8) |
||||
} |
||||
await nvim.setVar('coc_last_placeholder', placeholder) |
||||
let m = await nvim.mode |
||||
expect(m.mode).toBe('s') |
||||
await signature.triggerSignatureHelp() |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
expect(pos).toEqual(Position.create(0, 5)) |
||||
}) |
||||
}) |
||||
|
||||
describe('events', () => { |
||||
it('should trigger signature help', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo(x, y)', 'my signature')], |
||||
activeParameter: 0, |
||||
activeSignature: 0 |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo') |
||||
await nvim.input('(') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines[2]).toMatch('my signature') |
||||
}) |
||||
|
||||
it('should cancel trigger on InsertLeave', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: async (_doc, _position, token) => { |
||||
await helper.wait(1000) |
||||
if (token.isCancellationRequested) return undefined |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo') |
||||
let p = signature.triggerSignatureHelp() |
||||
await helper.wait(10) |
||||
await nvim.command('stopinsert') |
||||
await nvim.call('feedkeys', [String.fromCharCode(27), 'in']) |
||||
let res = await p |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should not close signature on type', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo(') |
||||
await helper.wait(100) |
||||
await nvim.input('bar') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines[2]).toMatch('my signature') |
||||
}) |
||||
|
||||
it('should close signature float when empty signatures returned', async () => { |
||||
let empty = false |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
if (empty) return undefined |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo(') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
empty = true |
||||
await signature.triggerSignatureHelp() |
||||
await helper.wait(50) |
||||
let res = await nvim.call('coc#float#valid', [win.id]) |
||||
expect(res).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('float window', () => { |
||||
it('should align signature window to top', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
let buf = await nvim.buffer |
||||
await buf.setLines(['', '', '', '', ''], { start: 0, end: -1, strictIndexing: true }) |
||||
await nvim.call('cursor', [5, 1]) |
||||
await nvim.input('foo(') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines[2]).toMatch('my signature') |
||||
let res = await nvim.call('coc#float#cursor_relative', [win.id]) as any |
||||
expect(res.row).toBeLessThan(0) |
||||
}) |
||||
|
||||
it('should show parameter docs', async () => { |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo(a, b)', 'my signature', |
||||
ParameterInformation.create('a', 'foo'), |
||||
ParameterInformation.create([7, 8], 'bar'))], |
||||
activeParameter: 1, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
let buf = await nvim.buffer |
||||
await buf.setLines(['', '', '', '', ''], { start: 0, end: -1, strictIndexing: true }) |
||||
await nvim.call('cursor', [5, 1]) |
||||
await nvim.input('foo(a,') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeDefined() |
||||
let lines = await helper.getWinLines(win.id) |
||||
expect(lines.join('\n')).toMatch('bar') |
||||
}) |
||||
}) |
||||
|
||||
describe('configurations', () => { |
||||
let { configurations } = workspace |
||||
afterEach(() => { |
||||
configurations.updateUserConfig({ |
||||
'signature.target': 'float', |
||||
'signature.hideOnTextChange': false, |
||||
'signature.enable': true, |
||||
'signature.triggerSignatureWait': 500 |
||||
}) |
||||
}) |
||||
|
||||
it('should cancel signature on timeout', async () => { |
||||
configurations.updateUserConfig({ 'signature.triggerSignatureWait': 50 }) |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position, token) => { |
||||
return new Promise(resolve => { |
||||
token.onCancellationRequested(() => { |
||||
clearTimeout(timer) |
||||
resolve(undefined) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
resolve({ |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
}) |
||||
}, 200) |
||||
}) |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await signature.triggerSignatureHelp() |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeUndefined() |
||||
configurations.updateUserConfig({ 'signature.triggerSignatureWait': 100 }) |
||||
}) |
||||
|
||||
it('should hide signature window on text change', async () => { |
||||
configurations.updateUserConfig({ 'signature.hideOnTextChange': true }) |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('ifoo(') |
||||
let winid = await helper.waitFloat() |
||||
await nvim.input('x') |
||||
await helper.wait(100) |
||||
let res = await nvim.call('coc#float#valid', [winid]) |
||||
expect(res).toBe(0) |
||||
configurations.updateUserConfig({ 'signature.hideOnTextChange': false }) |
||||
}) |
||||
|
||||
it('should disable signature help trigger', async () => { |
||||
configurations.updateUserConfig({ 'signature.enable': false }) |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo()', 'my signature')], |
||||
activeParameter: null, |
||||
activeSignature: null |
||||
} |
||||
} |
||||
}, ['(', ','])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo') |
||||
await nvim.input('(') |
||||
await helper.wait(100) |
||||
let win = await helper.getFloat() |
||||
expect(win).toBeUndefined() |
||||
}) |
||||
|
||||
it('should echo simple signature help', async () => { |
||||
let idx = 0 |
||||
let activeSignature = null |
||||
configurations.updateUserConfig({ 'signature.target': 'echo' }) |
||||
disposables.push(languages.registerSignatureHelpProvider([{ scheme: 'file' }], { |
||||
provideSignatureHelp: (_doc, _position) => { |
||||
return { |
||||
signatures: [SignatureInformation.create('foo(a, b)', 'my signature', |
||||
ParameterInformation.create('a', 'foo'), |
||||
ParameterInformation.create([7, 8], 'bar')), |
||||
SignatureInformation.create('a'.repeat(workspace.env.columns + 10)) |
||||
], |
||||
activeParameter: idx, |
||||
activeSignature |
||||
} |
||||
} |
||||
}, [])) |
||||
await helper.createDocument() |
||||
await nvim.input('foo(') |
||||
await signature.triggerSignatureHelp() |
||||
let line = await helper.getCmdline() |
||||
expect(line).toMatch('(a, b)') |
||||
await nvim.input('a,') |
||||
idx = 1 |
||||
await signature.triggerSignatureHelp() |
||||
line = await helper.getCmdline() |
||||
expect(line).toMatch('foo(a, b)') |
||||
activeSignature = 1 |
||||
await signature.triggerSignatureHelp() |
||||
line = await helper.getCmdline() |
||||
expect(line).toMatch('aaaaaa') |
||||
}) |
||||
}) |
||||
}) |
@ -1,284 +0,0 @@
@@ -1,284 +0,0 @@
|
||||
import { Buffer, Neovim } from '@chemzqm/neovim' |
||||
import { Disposable, SymbolInformation, SymbolKind, Range } from 'vscode-languageserver-protocol' |
||||
import Symbols from '../../handler/symbols/index' |
||||
import languages from '../../languages' |
||||
import workspace from '../../workspace' |
||||
import events from '../../events' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
import Parser from './parser' |
||||
|
||||
let nvim: Neovim |
||||
let symbols: Symbols |
||||
let disposables: Disposable[] = [] |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
symbols = helper.plugin.getHandler().symbols |
||||
}) |
||||
|
||||
beforeEach(() => { |
||||
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'javascript' }], { |
||||
provideDocumentSymbols: document => { |
||||
let parser = new Parser(document.getText()) |
||||
let res = parser.parse() |
||||
return Promise.resolve(res) |
||||
} |
||||
})) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
disposeAll(disposables) |
||||
disposables = [] |
||||
}) |
||||
|
||||
describe('Parser', () => { |
||||
it('should parse content', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { } |
||||
}` |
||||
let parser = new Parser(code) |
||||
let res = parser.parse() |
||||
expect(res.length).toBeGreaterThan(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('symbols handler', () => { |
||||
|
||||
async function createBuffer(code: string): Promise<Buffer> { |
||||
let buf = await nvim.buffer |
||||
await nvim.command('setf javascript') |
||||
await buf.setLines(code.split('\n'), { start: 0, end: -1, strictIndexing: false }) |
||||
let doc = await workspace.document |
||||
doc.forceSync() |
||||
return buf |
||||
} |
||||
|
||||
describe('configuration', () => { |
||||
afterEach(() => { |
||||
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', false) |
||||
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', false) |
||||
}) |
||||
|
||||
it('should get configuration', async () => { |
||||
let functionUpdate = symbols.functionUpdate |
||||
expect(functionUpdate).toBe(false) |
||||
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', true) |
||||
functionUpdate = symbols.functionUpdate |
||||
expect(functionUpdate).toBe(true) |
||||
}) |
||||
|
||||
it('should update symbols automatically', async () => { |
||||
helper.updateConfiguration('coc.preferences.currentFunctionSymbolAutoUpdate', true) |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
} |
||||
}` |
||||
let buf = await createBuffer(code) |
||||
await nvim.call('cursor', [2, 8]) |
||||
await events.fire('CursorHold', [buf.id]) |
||||
let val = await buf.getVar('coc_current_function') |
||||
expect(val).toBe('fun1') |
||||
await nvim.call('cursor', [1, 8]) |
||||
await events.fire('CursorHold', [buf.id]) |
||||
val = await buf.getVar('coc_current_function') |
||||
expect(val).toBe('myClass') |
||||
}) |
||||
}) |
||||
|
||||
describe('documentSymbols', () => { |
||||
it('should get symbols of current buffer', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { } |
||||
}` |
||||
await createBuffer(code) |
||||
let res = await helper.plugin.cocAction('documentSymbols') |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
|
||||
it('should get current function symbols', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
} |
||||
fun2() { |
||||
} |
||||
} |
||||
` |
||||
await createBuffer(code) |
||||
await nvim.call('cursor', [3, 0]) |
||||
let res = await helper.doAction('getCurrentFunctionSymbol') |
||||
expect(res).toBe('fun1') |
||||
await nvim.command('normal! G') |
||||
res = await helper.doAction('getCurrentFunctionSymbol') |
||||
expect(res).toBe('') |
||||
}) |
||||
|
||||
it('should reset coc_current_function when symbols not exists', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
} |
||||
}` |
||||
await createBuffer(code) |
||||
await nvim.call('cursor', [3, 0]) |
||||
let res = await helper.doAction('getCurrentFunctionSymbol') |
||||
expect(res).toBe('fun1') |
||||
await nvim.command('normal! ggdG') |
||||
res = await symbols.getCurrentFunctionSymbol() |
||||
expect(res).toBe('') |
||||
}) |
||||
|
||||
it('should support SymbolInformation', async () => { |
||||
disposables.push(languages.registerDocumentSymbolProvider(['*'], { |
||||
provideDocumentSymbols: () => { |
||||
return [ |
||||
SymbolInformation.create('root', SymbolKind.Function, Range.create(0, 0, 0, 10)), |
||||
SymbolInformation.create('child', SymbolKind.Function, Range.create(0, 0, 0, 10), '', 'root') |
||||
] |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument() |
||||
let res = await symbols.getDocumentSymbols(doc.bufnr) |
||||
expect(res.length).toBe(2) |
||||
expect(res[0].text).toBe('root') |
||||
expect(res[1].text).toBe('child') |
||||
}) |
||||
}) |
||||
|
||||
describe('selectSymbolRange', () => { |
||||
it('should show warning when no symbols exists', async () => { |
||||
disposables.push(languages.registerDocumentSymbolProvider(['*'], { |
||||
provideDocumentSymbols: () => { |
||||
return [] |
||||
} |
||||
})) |
||||
await helper.createDocument() |
||||
await nvim.call('cursor', [3, 0]) |
||||
await symbols.selectSymbolRange(false, '', ['Function']) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch(/No symbols found/) |
||||
}) |
||||
|
||||
it('should select symbol range at cursor position', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
} |
||||
}` |
||||
let buf = await createBuffer(code) |
||||
await nvim.call('cursor', [3, 0]) |
||||
await helper.doAction('selectSymbolRange', false, '', ['Function', 'Method']) |
||||
let mode = await nvim.mode |
||||
expect(mode.mode).toBe('v') |
||||
let doc = workspace.getDocument(buf.id) |
||||
await nvim.input('<esc>') |
||||
let res = await workspace.getSelectedRange('v', doc) |
||||
expect(res).toEqual({ start: { line: 1, character: 6 }, end: { line: 2, character: 6 } }) |
||||
}) |
||||
|
||||
it('should select inner range', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
let foo; |
||||
} |
||||
}` |
||||
let buf = await createBuffer(code) |
||||
await nvim.call('cursor', [3, 3]) |
||||
await symbols.selectSymbolRange(true, '', ['Method']) |
||||
let mode = await nvim.mode |
||||
expect(mode.mode).toBe('v') |
||||
let doc = workspace.getDocument(buf.id) |
||||
await nvim.input('<esc>') |
||||
let res = await workspace.getSelectedRange('v', doc) |
||||
expect(res).toEqual({ |
||||
start: { line: 2, character: 8 }, end: { line: 2, character: 16 } |
||||
}) |
||||
}) |
||||
|
||||
it('should reset visualmode when selection not found', async () => { |
||||
let code = `class myClass {}` |
||||
await createBuffer(code) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await nvim.command('normal! gg0v$') |
||||
let mode = await nvim.mode |
||||
expect(mode.mode).toBe('v') |
||||
await nvim.input('<esc>') |
||||
await symbols.selectSymbolRange(true, 'v', ['Method']) |
||||
mode = await nvim.mode |
||||
expect(mode.mode).toBe('v') |
||||
}) |
||||
|
||||
it('should select symbol range from select range', async () => { |
||||
let code = `class myClass {
|
||||
fun1() { |
||||
} |
||||
}` |
||||
let buf = await createBuffer(code) |
||||
await nvim.call('cursor', [2, 8]) |
||||
await nvim.command('normal! viw') |
||||
await nvim.input('<esc>') |
||||
await helper.doAction('selectSymbolRange', false, 'v', ['Class']) |
||||
let mode = await nvim.mode |
||||
expect(mode.mode).toBe('v') |
||||
let doc = workspace.getDocument(buf.id) |
||||
await nvim.input('<esc>') |
||||
let res = await workspace.getSelectedRange('v', doc) |
||||
expect(res).toEqual({ start: { line: 0, character: 0 }, end: { line: 3, character: 4 } }) |
||||
}) |
||||
}) |
||||
|
||||
describe('cancel', () => { |
||||
it('should cancel symbols request on insert', async () => { |
||||
let cancelled = false |
||||
disposables.push(languages.registerDocumentSymbolProvider([{ language: 'text' }], { |
||||
provideDocumentSymbols: (_doc, token) => { |
||||
return new Promise(s => { |
||||
token.onCancellationRequested(() => { |
||||
if (timer) clearTimeout(timer) |
||||
cancelled = true |
||||
s(undefined) |
||||
}) |
||||
let timer = setTimeout(() => { |
||||
s(undefined) |
||||
}, 3000) |
||||
}) |
||||
} |
||||
})) |
||||
let doc = await helper.createDocument('t.txt') |
||||
let p = symbols.getDocumentSymbols(doc.bufnr) |
||||
setTimeout(async () => { |
||||
await nvim.input('i') |
||||
}, 500) |
||||
await p |
||||
expect(cancelled).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('workspaceSymbols', () => { |
||||
it('should get workspace symbols', async () => { |
||||
disposables.push(languages.registerWorkspaceSymbolProvider({ |
||||
provideWorkspaceSymbols: (_query, _token) => { |
||||
return [SymbolInformation.create('far', SymbolKind.Class, Range.create(0, 0, 0, 0))] |
||||
}, |
||||
resolveWorkspaceSymbol: sym => { |
||||
let res = Object.assign({}, sym) |
||||
res.location.uri = 'test:///foo' |
||||
return res |
||||
} |
||||
})) |
||||
disposables.push(languages.registerWorkspaceSymbolProvider({ |
||||
provideWorkspaceSymbols: (_query, _token) => { |
||||
return [SymbolInformation.create('bar', SymbolKind.Function, Range.create(0, 0, 0, 0))] |
||||
} |
||||
})) |
||||
let res = await symbols.getWorkspaceSymbols('a') |
||||
expect(res.length).toBe(2) |
||||
let resolved = await symbols.resolveWorkspaceSymbol(res[0]) |
||||
expect(resolved?.location?.uri).toBe('test:///foo') |
||||
}) |
||||
}) |
||||
}) |
@ -1,22 +0,0 @@
@@ -1,22 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import Plugin from '../plugin' |
||||
import helper from './helper' |
||||
|
||||
let nvim: Neovim |
||||
let plugin: Plugin |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
plugin = helper.plugin |
||||
}) |
||||
|
||||
describe('Helper', () => { |
||||
it('should setup', () => { |
||||
expect(nvim).toBeTruthy() |
||||
expect(plugin.isReady).toBeTruthy() |
||||
}) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
@ -1,248 +0,0 @@
@@ -1,248 +0,0 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */ |
||||
/* eslint-disable @typescript-eslint/no-unsafe-call */ |
||||
import { Buffer, Neovim, Window } from '@chemzqm/neovim' |
||||
import * as cp from 'child_process' |
||||
import { EventEmitter } from 'events' |
||||
import fs from 'fs' |
||||
import os from 'os' |
||||
import path from 'path' |
||||
import util from 'util' |
||||
import attach from '../attach' |
||||
import Document from '../model/document' |
||||
import Plugin from '../plugin' |
||||
import workspace from '../workspace' |
||||
import { v4 as uuid } from 'uuid' |
||||
import { VimCompleteItem } from '../types' |
||||
|
||||
export interface CursorPosition { |
||||
bufnum: number |
||||
lnum: number |
||||
col: number |
||||
} |
||||
|
||||
process.on('uncaughtException', err => { |
||||
let msg = 'Uncaught exception: ' + err.stack |
||||
console.error(msg) |
||||
}) |
||||
export class Helper extends EventEmitter { |
||||
public nvim: Neovim |
||||
public proc: cp.ChildProcess |
||||
public plugin: Plugin |
||||
|
||||
constructor() { |
||||
super() |
||||
this.setMaxListeners(99) |
||||
} |
||||
|
||||
public setup(): Promise<void> { |
||||
const vimrc = path.resolve(__dirname, 'vimrc') |
||||
let proc = this.proc = cp.spawn('nvim', ['-u', vimrc, '-i', 'NONE', '--embed'], { |
||||
cwd: __dirname |
||||
}) |
||||
let plugin = this.plugin = attach({ proc }) |
||||
this.nvim = plugin.nvim |
||||
this.nvim.uiAttach(160, 80, {}).catch(_e => { |
||||
// noop
|
||||
}) |
||||
proc.on('exit', () => { |
||||
this.proc = null |
||||
}) |
||||
this.nvim.on('notification', (method, args) => { |
||||
if (method == 'redraw') { |
||||
for (let arg of args) { |
||||
let event = arg[0] |
||||
this.emit(event, arg.slice(1)) |
||||
} |
||||
} |
||||
}) |
||||
return new Promise(resolve => { |
||||
plugin.once('ready', resolve) |
||||
}) |
||||
} |
||||
|
||||
public async shutdown(): Promise<void> { |
||||
this.plugin.dispose() |
||||
await this.nvim.quit() |
||||
if (this.proc) { |
||||
this.proc.kill('SIGKILL') |
||||
} |
||||
await this.wait(60) |
||||
} |
||||
|
||||
public async waitPopup(): Promise<void> { |
||||
for (let i = 0; i < 40; i++) { |
||||
await this.wait(50) |
||||
let visible = await this.nvim.call('pumvisible') |
||||
if (visible) return |
||||
} |
||||
throw new Error('timeout after 2s') |
||||
} |
||||
|
||||
public async waitFloat(): Promise<number> { |
||||
for (let i = 0; i < 40; i++) { |
||||
await this.wait(50) |
||||
let winid = await this.nvim.call('coc#float#get_float_win') |
||||
if (winid) return winid |
||||
} |
||||
throw new Error('timeout after 2s') |
||||
} |
||||
|
||||
public async selectCompleteItem(idx: number): Promise<void> { |
||||
await this.nvim.call('nvim_select_popupmenu_item', [idx, true, true, {}]) |
||||
} |
||||
|
||||
public async doAction(method: string, ...args: any[]): Promise<any> { |
||||
return await this.plugin.cocAction(method, ...args) |
||||
} |
||||
|
||||
public async reset(): Promise<void> { |
||||
let mode = await this.nvim.mode |
||||
if (mode.mode != 'n' || mode.blocking) { |
||||
await this.nvim.command('stopinsert') |
||||
await this.nvim.call('feedkeys', [String.fromCharCode(27), 'in']) |
||||
} |
||||
await this.nvim.command('silent! %bwipeout!') |
||||
await this.wait(80) |
||||
} |
||||
|
||||
public async pumvisible(): Promise<boolean> { |
||||
let res = await this.nvim.call('pumvisible', []) as number |
||||
return res == 1 |
||||
} |
||||
|
||||
public wait(ms = 30): Promise<void> { |
||||
return new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve() |
||||
}, ms) |
||||
}) |
||||
} |
||||
|
||||
public async visible(word: string, source?: string): Promise<boolean> { |
||||
await this.waitPopup() |
||||
let context = await this.nvim.getVar('coc#_context') as any |
||||
let items = context.candidates |
||||
if (!items) return false |
||||
let item = items.find(o => o.word == word) |
||||
if (!item || !item.user_data) return false |
||||
try { |
||||
let o = JSON.parse(item.user_data) |
||||
if (source && o.source !== source) { |
||||
return false |
||||
} |
||||
} catch (e) { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
public async notVisible(word: string): Promise<boolean> { |
||||
let items = await this.getItems() |
||||
return items.findIndex(o => o.word == word) == -1 |
||||
} |
||||
|
||||
public async getItems(): Promise<VimCompleteItem[]> { |
||||
let visible = await this.pumvisible() |
||||
if (!visible) return [] |
||||
let context = await this.nvim.getVar('coc#_context') as any |
||||
let items = context.candidates |
||||
return items || [] |
||||
} |
||||
|
||||
public async edit(file?: string): Promise<Buffer> { |
||||
if (!file || !path.isAbsolute(file)) { |
||||
file = path.join(__dirname, file ? file : `${uuid()}`) |
||||
} |
||||
let escaped = await this.nvim.call('fnameescape', file) as string |
||||
await this.nvim.command(`edit ${escaped}`) |
||||
await this.wait(60) |
||||
let bufnr = await this.nvim.call('bufnr', ['%']) as number |
||||
return this.nvim.createBuffer(bufnr) |
||||
} |
||||
|
||||
public async createDocument(name?: string): Promise<Document> { |
||||
let buf = await this.edit(name) |
||||
let doc = workspace.getDocument(buf.id) |
||||
if (!doc) return await workspace.document |
||||
return doc |
||||
} |
||||
|
||||
public async getMarkers(bufnr: number, ns: number): Promise<[number, number, number][]> { |
||||
return await this.nvim.call('nvim_buf_get_extmarks', [bufnr, ns, 0, -1, {}]) as [number, number, number][] |
||||
} |
||||
|
||||
public async getCmdline(): Promise<string> { |
||||
let str = '' |
||||
for (let i = 1, l = 70; i < l; i++) { |
||||
let ch = await this.nvim.call('screenchar', [79, i]) |
||||
if (ch == -1) break |
||||
str += String.fromCharCode(ch) |
||||
} |
||||
return str.trim() |
||||
} |
||||
|
||||
public updateConfiguration(key: string, value: any): () => void { |
||||
let { configurations } = workspace as any |
||||
let curr = workspace.getConfiguration(key) |
||||
configurations.updateUserConfig({ [key]: value }) |
||||
return () => { |
||||
configurations.updateUserConfig({ [key]: curr }) |
||||
} |
||||
} |
||||
|
||||
public async mockFunction(name: string, result: string | number | any): Promise<void> { |
||||
let content = ` |
||||
function! ${name}(...) |
||||
return ${typeof result == 'number' ? result : JSON.stringify(result)} |
||||
endfunction |
||||
` |
||||
let file = await createTmpFile(content) |
||||
await this.nvim.command(`source ${file}`) |
||||
} |
||||
|
||||
public async items(): Promise<VimCompleteItem[]> { |
||||
let context = await this.nvim.getVar('coc#_context') |
||||
return context['candidates'] || [] |
||||
} |
||||
|
||||
public async screenLine(line: number): Promise<string> { |
||||
let res = '' |
||||
for (let i = 1; i <= 80; i++) { |
||||
let ch = await this.nvim.call('screenchar', [line, i]) |
||||
res = res + String.fromCharCode(ch) |
||||
} |
||||
return res |
||||
} |
||||
|
||||
public async getWinLines(winid: number): Promise<string[]> { |
||||
return await this.nvim.eval(`getbufline(winbufnr(${winid}), 1, '$')`) as string[] |
||||
} |
||||
|
||||
public async getFloat(): Promise<Window> { |
||||
let wins = await this.nvim.windows |
||||
let floatWin: Window |
||||
for (let win of wins) { |
||||
let f = await win.getVar('float') |
||||
if (f) floatWin = win |
||||
} |
||||
return floatWin |
||||
} |
||||
|
||||
public async getFloats(): Promise<Window[]> { |
||||
let ids = await this.nvim.call('coc#float#get_float_win_list', []) |
||||
if (!ids) return [] |
||||
return ids.map(id => this.nvim.createWindow(id)) |
||||
} |
||||
} |
||||
|
||||
export async function createTmpFile(content: string): Promise<string> { |
||||
let tmpFolder = path.join(os.tmpdir(), `coc-${process.pid}`) |
||||
if (!fs.existsSync(tmpFolder)) { |
||||
fs.mkdirSync(tmpFolder) |
||||
} |
||||
let filename = path.join(tmpFolder, uuid()) |
||||
await util.promisify(fs.writeFile)(filename, content, 'utf8') |
||||
return filename |
||||
} |
||||
|
||||
export default new Helper() |
@ -1,174 +0,0 @@
@@ -1,174 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { URI } from 'vscode-uri' |
||||
import { Disposable, Range, Location } from 'vscode-languageserver-protocol' |
||||
import BasicList, { getFiletype, PreviewOptions } from '../../list/basic' |
||||
import manager from '../../list/manager' |
||||
import { ListItem } from '../../types' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
let previewOptions: PreviewOptions |
||||
|
||||
let list: SimpleList |
||||
class SimpleList extends BasicList { |
||||
public name = 'simple' |
||||
public defaultAction: 'preview' |
||||
constructor(nvim: Neovim) { |
||||
super(nvim) |
||||
this.addAction('preview', async (_item, context) => { |
||||
await this.preview(previewOptions, context) |
||||
}) |
||||
} |
||||
public loadItems(): Promise<ListItem[]> { |
||||
return Promise.resolve(['a', 'b', 'c'].map((s, idx) => { |
||||
return { label: s, location: Location.create('test:///a', Range.create(idx, 0, idx + 1, 0)) } as ListItem |
||||
})) |
||||
} |
||||
} |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
beforeEach(() => { |
||||
list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('getFiletype()', () => { |
||||
it('should get filetype', async () => { |
||||
expect(getFiletype('javascriptreact')).toBe('javascript') |
||||
expect(getFiletype('typescriptreact')).toBe('typescript') |
||||
expect(getFiletype('foo.bar')).toBe('foo') |
||||
expect(getFiletype('foo')).toBe('foo') |
||||
}) |
||||
}) |
||||
|
||||
describe('BasicList', () => { |
||||
async function doPreview(opts: PreviewOptions): Promise<number> { |
||||
previewOptions = opts |
||||
await manager.start(['--normal', 'simple']) |
||||
await manager.session.ui.ready |
||||
await manager.doAction('preview') |
||||
let res = await nvim.call('coc#list#has_preview') as number |
||||
expect(res).toBeGreaterThan(0) |
||||
let winid = await nvim.call('win_getid', [res]) |
||||
return winid |
||||
} |
||||
|
||||
describe('preview()', () => { |
||||
it('should preview lines', async () => { |
||||
await doPreview({ filetype: '', lines: ['foo', 'bar'] }) |
||||
}) |
||||
|
||||
it('should preview with bufname', async () => { |
||||
await doPreview({ |
||||
bufname: 't.js', |
||||
filetype: 'typescript', |
||||
lines: ['foo', 'bar'] |
||||
}) |
||||
}) |
||||
|
||||
it('should preview with range highlight', async () => { |
||||
let winid = await doPreview({ |
||||
bufname: 't.js', |
||||
filetype: 'typescript', |
||||
lines: ['foo', 'bar'], |
||||
range: Range.create(0, 0, 0, 3) |
||||
}) |
||||
let res = await nvim.call('getmatches', [winid]) |
||||
expect(res.length).toBeGreaterThan(0) |
||||
}) |
||||
|
||||
}) |
||||
|
||||
describe('createAction()', () => { |
||||
it('should overwrite action', async () => { |
||||
let idx: number |
||||
list.createAction({ |
||||
name: 'foo', |
||||
execute: () => { idx = 0 } |
||||
}) |
||||
list.createAction({ |
||||
name: 'foo', |
||||
execute: () => { idx = 1 } |
||||
}) |
||||
await manager.start(['--normal', 'simple']) |
||||
await manager.session.ui.ready |
||||
await manager.doAction('foo') |
||||
expect(idx).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('jumpTo()', () => { |
||||
it('should jump to uri', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
await list.jumpTo(uri, 'edit') |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toMatch('basicList.test.ts') |
||||
}) |
||||
|
||||
it('should jump to location', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
let loc = Location.create(uri, Range.create(0, 0, 1, 0)) |
||||
await list.jumpTo(loc, 'edit') |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toMatch('basicList.test.ts') |
||||
}) |
||||
|
||||
it('should jump to location with empty range', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
let loc = Location.create(uri, Range.create(0, 0, 0, 0)) |
||||
await list.jumpTo(loc, 'edit') |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toMatch('basicList.test.ts') |
||||
}) |
||||
}) |
||||
|
||||
describe('convertLocation()', () => { |
||||
it('should convert uri', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
let res = await list.convertLocation(uri) |
||||
expect(res.uri).toBe(uri) |
||||
}) |
||||
|
||||
it('should convert location with line', async () => { |
||||
let uri = URI.file(__filename).toString() |
||||
let res = await list.convertLocation({ uri, line: 'convertLocation()', text: 'convertLocation' }) |
||||
expect(res.uri).toBe(uri) |
||||
res = await list.convertLocation({ uri, line: 'convertLocation()' }) |
||||
expect(res.uri).toBe(uri) |
||||
}) |
||||
|
||||
it('should convert location with custom schema', async () => { |
||||
let uri = 'test:///foo' |
||||
let res = await list.convertLocation({ uri, line: 'convertLocation()'}) |
||||
expect(res.uri).toBe(uri) |
||||
}) |
||||
}) |
||||
|
||||
describe('quickfix action', () => { |
||||
it('should invoke quickfix action', async () => { |
||||
list.addLocationActions() |
||||
await manager.start(['--normal', 'simple', '-arg']) |
||||
await manager.session.ui.ready |
||||
await manager.session.ui.selectAll() |
||||
await manager.doAction('quickfix') |
||||
let res = await nvim.call('getqflist') |
||||
expect(res.length).toBeGreaterThan(1) |
||||
}) |
||||
}) |
||||
}) |
@ -1,134 +0,0 @@
@@ -1,134 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import path from 'path' |
||||
import { ListContext, ListTask } from '../../types' |
||||
import manager from '../../list/manager' |
||||
import helper, { createTmpFile } from '../helper' |
||||
import BasicList from '../../list/basic' |
||||
import { Disposable } from 'vscode-languageserver-protocol' |
||||
import { disposeAll } from '../../util' |
||||
|
||||
class DataList extends BasicList { |
||||
public name = 'data' |
||||
public async loadItems(_context: ListContext): Promise<ListTask> { |
||||
let fsPath = await createTmpFile(`console.log('foo');console.log('');console.log('bar');`) |
||||
return this.createCommandTask({ |
||||
cmd: 'node', |
||||
args: [fsPath], |
||||
cwd: path.dirname(fsPath), |
||||
onLine: line => { |
||||
if (!line) return undefined |
||||
return { |
||||
label: line |
||||
} |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
class SleepList extends BasicList { |
||||
public name = 'sleep' |
||||
public loadItems(_context: ListContext): Promise<ListTask> { |
||||
return Promise.resolve(this.createCommandTask({ |
||||
cmd: 'sleep', |
||||
args: ['10'], |
||||
cwd: __dirname, |
||||
onLine: line => { |
||||
return { |
||||
label: line |
||||
} |
||||
} |
||||
})) |
||||
} |
||||
} |
||||
|
||||
class StderrList extends BasicList { |
||||
public name = 'stderr' |
||||
public async loadItems(_context: ListContext): Promise<ListTask> { |
||||
let fsPath = await createTmpFile(`console.error('stderr');console.log('stdout')`) |
||||
return Promise.resolve(this.createCommandTask({ |
||||
cmd: 'node', |
||||
args: [fsPath], |
||||
cwd: path.dirname(fsPath), |
||||
onLine: line => { |
||||
return { |
||||
label: line |
||||
} |
||||
} |
||||
})) |
||||
} |
||||
} |
||||
|
||||
class ErrorTask extends BasicList { |
||||
public name = 'error' |
||||
public async loadItems(_context: ListContext): Promise<ListTask> { |
||||
return Promise.resolve(this.createCommandTask({ |
||||
cmd: 'NOT_EXISTS', |
||||
args: [], |
||||
cwd: __dirname, |
||||
onLine: line => { |
||||
return { |
||||
label: line |
||||
} |
||||
} |
||||
})) |
||||
} |
||||
} |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Command task', () => { |
||||
it('should not show stderr', async () => { |
||||
disposables.push(manager.registerList(new StderrList(nvim))) |
||||
await manager.start(['stderr']) |
||||
await manager.session.ui.ready |
||||
let lines = await nvim.call('getline', [1, '$']) as string[] |
||||
expect(lines).toEqual(['stdout']) |
||||
}) |
||||
|
||||
it('should not show error', async () => { |
||||
disposables.push(manager.registerList(new ErrorTask(nvim))) |
||||
await manager.start(['error']) |
||||
await helper.wait(100) |
||||
let res = await helper.getCmdline() |
||||
expect(res).toMatch('NOT_EXISTS ENOENT') |
||||
}) |
||||
|
||||
it('should create command task', async () => { |
||||
let list = new DataList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['data']) |
||||
await helper.wait(500) |
||||
let lines = await nvim.call('getline', [1, '$']) as string[] |
||||
expect(lines).toEqual(['foo', 'bar']) |
||||
}) |
||||
|
||||
it('should stop command task', async () => { |
||||
let list = new SleepList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['sleep']) |
||||
manager.session.stop() |
||||
}) |
||||
|
||||
it('should show error for bad key', async () => { |
||||
let list = new DataList(nvim) |
||||
list.config.fixKey('<X-a>') |
||||
await helper.wait(500) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('not supported') |
||||
}) |
||||
}) |
@ -1,32 +0,0 @@
@@ -1,32 +0,0 @@
|
||||
import { formatPath, UnformattedListItem, formatListItems } from '../../list/formatting' |
||||
|
||||
describe('formatPath()', () => { |
||||
it('should format path', async () => { |
||||
expect(formatPath('hidden', 'path')).toBe('') |
||||
expect(formatPath('full', __filename)).toMatch('formatting.test.ts') |
||||
expect(formatPath('short', __filename)).toMatch('formatting.test.ts') |
||||
expect(formatPath('filename', __filename)).toMatch('formatting.test.ts') |
||||
}) |
||||
}) |
||||
|
||||
describe('formatListItems', () => { |
||||
it('should format list items', async () => { |
||||
expect(formatListItems(false, [])).toEqual([]) |
||||
let items: UnformattedListItem[] = [{ |
||||
label: ['a', 'b', 'c'] |
||||
}] |
||||
expect(formatListItems(false, items)).toEqual([{ |
||||
label: 'a\tb\tc' |
||||
}]) |
||||
items = [{ |
||||
label: ['a', 'b', 'c'] |
||||
}, { |
||||
label: ['foo', 'bar', 'go'] |
||||
}] |
||||
expect(formatListItems(true, items)).toEqual([{ |
||||
label: 'a \tb \tc ' |
||||
}, { |
||||
label: 'foo\tbar\tgo' |
||||
}]) |
||||
}) |
||||
}) |
@ -1,79 +0,0 @@
@@ -1,79 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import manager from '../../list/manager' |
||||
import { Range } from 'vscode-languageserver-protocol' |
||||
import events from '../../events' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
const locations: any[] = [{ |
||||
filename: __filename, |
||||
range: Range.create(0, 0, 0, 6), |
||||
text: 'foo' |
||||
}, { |
||||
filename: __filename, |
||||
range: Range.create(2, 0, 2, 6), |
||||
text: 'Bar' |
||||
}, { |
||||
filename: __filename, |
||||
range: Range.create(3, 0, 4, 6), |
||||
text: 'multiple' |
||||
}] |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
await nvim.setVar('coc_jump_locations', locations) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
manager.reset() |
||||
await helper.reset() |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
describe('list commands', () => { |
||||
it('should highlight ranges', async () => { |
||||
await manager.start(['--normal', '--auto-preview', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(200) |
||||
manager.prompt.cancel() |
||||
await nvim.command('wincmd k') |
||||
let name = await nvim.eval('bufname("%")') |
||||
expect(name).toMatch('location.test.ts') |
||||
let res = await nvim.call('getmatches') |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
|
||||
it('should change highlight on cursor move', async () => { |
||||
await manager.start(['--normal', '--auto-preview', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(200) |
||||
await nvim.command('exe 2') |
||||
let bufnr = await nvim.eval('bufnr("%")') |
||||
await events.fire('CursorMoved', [bufnr, [2, 1]]) |
||||
await helper.wait(300) |
||||
await nvim.command('wincmd k') |
||||
let res = await nvim.call('getmatches') |
||||
expect(res.length).toBe(1) |
||||
expect(res[0]['pos1']).toEqual([3, 1, 6]) |
||||
}) |
||||
|
||||
it('should highlight multiple line range', async () => { |
||||
await manager.start(['--normal', '--auto-preview', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(200) |
||||
await nvim.command('exe 3') |
||||
let bufnr = await nvim.eval('bufnr("%")') |
||||
await events.fire('CursorMoved', [bufnr, [2, 1]]) |
||||
await helper.wait(300) |
||||
await nvim.command('wincmd k') |
||||
let res = await nvim.call('getmatches') |
||||
expect(res.length).toBe(1) |
||||
expect(res[0]['pos1']).toBeDefined() |
||||
expect(res[0]['pos2']).toBeDefined() |
||||
}) |
||||
}) |
@ -1,509 +0,0 @@
@@ -1,509 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import path from 'path' |
||||
import manager from '../../list/manager' |
||||
import events from '../../events' |
||||
import { QuickfixItem, IList, ListItem } from '../../types' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
const locations: ReadonlyArray<QuickfixItem> = [{ |
||||
filename: __filename, |
||||
col: 2, |
||||
lnum: 1, |
||||
text: 'foo' |
||||
}, { |
||||
filename: __filename, |
||||
col: 1, |
||||
lnum: 2, |
||||
text: 'Bar' |
||||
}, { |
||||
filename: __filename, |
||||
col: 1, |
||||
lnum: 3, |
||||
text: 'option' |
||||
}] |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
await nvim.setVar('coc_jump_locations', locations) |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
describe('list', () => { |
||||
describe('events', () => { |
||||
it('should cancel and enable prompt', async () => { |
||||
let winid = await nvim.call('win_getid') |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.call('win_gotoid', [winid]) |
||||
await helper.wait(50) |
||||
let res = await nvim.call('coc#prompt#activated') |
||||
expect(res).toBe(0) |
||||
await nvim.command('wincmd p') |
||||
await helper.wait(50) |
||||
res = await nvim.call('coc#prompt#activated') |
||||
expect(res).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('list commands', () => { |
||||
it('should be activated', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
expect(manager.isActivated).toBe(true) |
||||
let line = await nvim.getLine() |
||||
expect(line).toMatch(/manager.test.ts/) |
||||
}) |
||||
|
||||
it('should get list names', () => { |
||||
let names = manager.names |
||||
expect(names.length > 0).toBe(true) |
||||
}) |
||||
|
||||
it('should resume list', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("j", "in")') |
||||
await helper.wait(30) |
||||
let line = await nvim.call('line', '.') |
||||
expect(line).toBe(2) |
||||
await manager.cancel() |
||||
await helper.wait(30) |
||||
await manager.resume() |
||||
await helper.wait(30) |
||||
line = await nvim.call('line', '.') |
||||
expect(line).toBe(2) |
||||
}) |
||||
|
||||
it('should not quit list with --no-quit', async () => { |
||||
await manager.start(['--normal', '--no-quit', 'location']) |
||||
await manager.session.ui.ready |
||||
let winnr = await nvim.eval('win_getid()') as number |
||||
await manager.doAction() |
||||
await helper.wait(100) |
||||
let wins = await nvim.windows |
||||
let ids = wins.map(o => o.id) |
||||
expect(ids).toContain(winnr) |
||||
}) |
||||
|
||||
it('should do default action for first item', async () => { |
||||
await manager.start(['--normal', '--first', 'location']) |
||||
await helper.wait(300) |
||||
let name = await nvim.eval('bufname("%")') as string |
||||
let filename = path.basename(__filename) |
||||
expect(name.includes(filename)).toBe(true) |
||||
let pos = await nvim.eval('getcurpos()') |
||||
expect(pos[1]).toBe(1) |
||||
expect(pos[2]).toBe(2) |
||||
}) |
||||
|
||||
it('should goto next & previous', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(60) |
||||
await manager.doAction() |
||||
await manager.cancel() |
||||
let bufname = await nvim.eval('expand("%:p")') |
||||
expect(bufname).toMatch('manager.test.ts') |
||||
await manager.next() |
||||
let line = await nvim.call('line', '.') |
||||
expect(line).toBe(2) |
||||
await helper.wait(60) |
||||
await manager.previous() |
||||
line = await nvim.call('line', '.') |
||||
expect(line).toBe(1) |
||||
}) |
||||
|
||||
it('should parse arguments', async () => { |
||||
await manager.start(['--input=test', '--normal', '--no-sort', '--ignore-case', '--top', '--number-select', '--auto-preview', '--strict', 'location']) |
||||
await helper.wait(30) |
||||
let opts = manager.session?.listOptions |
||||
expect(opts).toEqual({ |
||||
numberSelect: true, |
||||
autoPreview: true, |
||||
first: false, |
||||
input: 'test', |
||||
interactive: false, |
||||
matcher: 'strict', |
||||
ignorecase: true, |
||||
position: 'top', |
||||
mode: 'normal', |
||||
noQuit: false, |
||||
sort: false |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
describe('list options', () => { |
||||
it('should respect input option', async () => { |
||||
await manager.start(['--input=foo', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(30) |
||||
let line = await helper.getCmdline() |
||||
expect(line).toMatch('foo') |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should respect regex filter', async () => { |
||||
await manager.start(['--input=f.o', '--regex', 'location']) |
||||
await helper.wait(200) |
||||
let item = await manager.session?.ui.item |
||||
expect(item.label).toMatch('foo') |
||||
}) |
||||
|
||||
it('should respect normal option', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
let line = await helper.getCmdline() |
||||
expect(line).toBe('') |
||||
}) |
||||
|
||||
it('should respect nosort option', async () => { |
||||
await manager.start(['--ignore-case', '--no-sort', 'location']) |
||||
await manager.session.ui.ready |
||||
expect(manager.isActivated).toBe(true) |
||||
await nvim.input('oo') |
||||
await helper.wait(100) |
||||
let line = await nvim.call('getline', ['.']) |
||||
expect(line).toMatch('foo') |
||||
}) |
||||
|
||||
it('should respect ignorecase option', async () => { |
||||
await manager.start(['--ignore-case', '--strict', 'location']) |
||||
await manager.session.ui.ready |
||||
expect(manager.isActivated).toBe(true) |
||||
await nvim.input('bar') |
||||
await helper.wait(100) |
||||
let n = manager.session?.ui.length |
||||
expect(n).toBe(1) |
||||
let line = await nvim.line |
||||
expect(line).toMatch('Bar') |
||||
}) |
||||
|
||||
it('should respect top option', async () => { |
||||
await manager.start(['--top', 'location']) |
||||
await manager.session.ui.ready |
||||
let nr = await nvim.call('winnr') |
||||
expect(nr).toBe(1) |
||||
}) |
||||
|
||||
it('should respect number select option', async () => { |
||||
await manager.start(['--number-select', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("2", "in")') |
||||
await helper.wait(100) |
||||
let lnum = locations[1].lnum |
||||
let curr = await nvim.call('line', '.') |
||||
expect(lnum).toBe(curr) |
||||
}) |
||||
|
||||
it('should respect auto preview option', async () => { |
||||
await manager.start(['--auto-preview', 'location']) |
||||
await helper.wait(300) |
||||
let previewWinnr = await nvim.call('coc#list#has_preview') |
||||
expect(previewWinnr).toBe(2) |
||||
let bufnr = await nvim.call('winbufnr', previewWinnr) |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let name = await buf.name |
||||
expect(name).toMatch('manager.test.ts') |
||||
await nvim.eval('feedkeys("j", "in")') |
||||
await helper.wait(100) |
||||
let winnr = await nvim.call('coc#list#has_preview') |
||||
expect(winnr).toBe(previewWinnr) |
||||
}) |
||||
|
||||
it('should respect tab option', async () => { |
||||
await manager.start(['--tab', '--auto-preview', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(200) |
||||
await nvim.command('wincmd l') |
||||
let previewwindow = await nvim.eval('w:previewwindow') |
||||
expect(previewwindow).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('list configuration', () => { |
||||
it('should change indicator', async () => { |
||||
helper.updateConfiguration('list.indicator', '>>') |
||||
await manager.start(['location']) |
||||
await helper.wait(200) |
||||
let line = await helper.getCmdline() |
||||
expect(line).toMatch('>>') |
||||
}) |
||||
|
||||
it('should split right for preview window', async () => { |
||||
helper.updateConfiguration('list.previewSplitRight', true) |
||||
let win = await nvim.window |
||||
await manager.start(['location']) |
||||
await helper.wait(100) |
||||
await manager.doAction('preview') |
||||
await helper.wait(100) |
||||
manager.prompt.cancel() |
||||
await helper.wait(10) |
||||
await nvim.call('win_gotoid', [win.id]) |
||||
await nvim.command('wincmd l') |
||||
let curr = await nvim.window |
||||
let isPreview = await curr.getVar('previewwindow') |
||||
expect(isPreview).toBe(1) |
||||
helper.updateConfiguration('list.previewSplitRight', false) |
||||
}) |
||||
|
||||
it('should toggle selection mode', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session?.ui.ready |
||||
await nvim.input('V') |
||||
await helper.wait(30) |
||||
await nvim.input('1') |
||||
await helper.wait(30) |
||||
await nvim.input('j') |
||||
await helper.wait(100) |
||||
await manager.session?.ui.toggleSelection() |
||||
let items = await manager.session?.ui.getItems() |
||||
expect(items.length).toBe(2) |
||||
}) |
||||
|
||||
it('should change next/previous keymap', async () => { |
||||
helper.updateConfiguration('list.nextKeymap', '<tab>') |
||||
helper.updateConfiguration('list.previousKeymap', '<s-tab>') |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("\\<tab>", "in")') |
||||
await helper.wait(100) |
||||
let line = await nvim.line |
||||
expect(line).toMatch('Bar') |
||||
await nvim.eval('feedkeys("\\<s-tab>", "in")') |
||||
await helper.wait(100) |
||||
line = await nvim.line |
||||
expect(line).toMatch('foo') |
||||
}) |
||||
|
||||
it('should respect mouse events', async () => { |
||||
async function setMouseEvent(line: number): Promise<void> { |
||||
let winid = manager.session?.ui.winid |
||||
await nvim.command(`let v:mouse_winid = ${winid}`) |
||||
await nvim.command(`let v:mouse_lnum = ${line}`) |
||||
await nvim.command(`let v:mouse_col = 1`) |
||||
} |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
await setMouseEvent(1) |
||||
await manager.onNormalInput('<LeftMouse>') |
||||
await setMouseEvent(2) |
||||
await manager.onNormalInput('<LeftDrag>') |
||||
await setMouseEvent(3) |
||||
await manager.onNormalInput('<LeftRelease>') |
||||
await helper.wait(100) |
||||
let items = await manager.session?.ui.getItems() |
||||
expect(items.length).toBe(3) |
||||
}) |
||||
|
||||
it('should toggle preview', async () => { |
||||
await manager.start(['--normal', '--auto-preview', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
await manager.togglePreview() |
||||
await helper.wait(100) |
||||
await manager.togglePreview() |
||||
await helper.wait(100) |
||||
let has = await nvim.call('coc#list#has_preview') |
||||
expect(has).toBeGreaterThan(0) |
||||
}) |
||||
|
||||
it('should show help of current list', async () => { |
||||
await manager.start(['--normal', '--auto-preview', 'location']) |
||||
await helper.wait(200) |
||||
await manager.session?.showHelp() |
||||
let bufname = await nvim.call('bufname', '%') |
||||
expect(bufname).toBe('[LIST HELP]') |
||||
}) |
||||
|
||||
it('should resolve list item', async () => { |
||||
let list: IList = { |
||||
name: 'test', |
||||
actions: [{ |
||||
name: 'open', execute: _item => { |
||||
// noop
|
||||
} |
||||
}], |
||||
defaultAction: 'open', |
||||
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }]), |
||||
resolveItem: item => { |
||||
item.label = item.label.slice(0, 1) |
||||
return Promise.resolve(item) |
||||
} |
||||
} |
||||
let disposable = manager.registerList(list) |
||||
await manager.start(['--normal', 'test']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
let line = await nvim.line |
||||
expect(line).toBe('f') |
||||
disposable.dispose() |
||||
}) |
||||
}) |
||||
|
||||
describe('descriptions', () => { |
||||
it('should get descriptions', async () => { |
||||
let res = manager.descriptions |
||||
expect(res).toBeDefined() |
||||
expect(res.location).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('loadItems()', () => { |
||||
it('should load items for list', async () => { |
||||
let res = await manager.loadItems('location') |
||||
expect(res.length).toBeGreaterThan(0) |
||||
; (manager as any).lastSession = undefined |
||||
manager.toggleMode() |
||||
manager.stop() |
||||
}) |
||||
}) |
||||
|
||||
describe('onInsertInput()', () => { |
||||
it('should handle insert input', async () => { |
||||
await manager.onInsertInput('k') |
||||
await manager.onInsertInput('<LeftMouse>') |
||||
await manager.start(['--number-select', 'location']) |
||||
await manager.session.ui.ready |
||||
await manager.onInsertInput('1') |
||||
await helper.wait(300) |
||||
let bufname = await nvim.call('expand', ['%:p']) |
||||
expect(bufname).toMatch('manager.test.ts') |
||||
}) |
||||
|
||||
it('should ignore invalid input', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await manager.onInsertInput('<X-y>') |
||||
await manager.onInsertInput(String.fromCharCode(65533)) |
||||
await manager.onInsertInput(String.fromCharCode(30)) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should ignore <plug> insert', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<plug>x", "in")') |
||||
await helper.wait(50) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('parseArgs()', () => { |
||||
it('should show error for bad option', async () => { |
||||
manager.parseArgs(['$x', 'location']) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('Invalid list option') |
||||
}) |
||||
|
||||
it('should show error for option not exists', async () => { |
||||
manager.parseArgs(['-xyz', 'location']) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('Invalid option') |
||||
}) |
||||
|
||||
it('should show error for interactive with list not support interactive', async () => { |
||||
manager.parseArgs(['--interactive', 'location']) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('not supported') |
||||
}) |
||||
}) |
||||
|
||||
describe('resume()', () => { |
||||
it('should resume by name', async () => { |
||||
await events.fire('FocusGained', []) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
await manager.session.hide() |
||||
await helper.wait(100) |
||||
await manager.resume('location') |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('start()', () => { |
||||
it('should show error when loadItems throws', async () => { |
||||
let list: IList = { |
||||
name: 'test', |
||||
actions: [{ |
||||
name: 'open', |
||||
execute: (_item: ListItem) => { |
||||
} |
||||
}], |
||||
defaultAction: 'open', |
||||
loadItems: () => { |
||||
throw new Error('test error') |
||||
} |
||||
} |
||||
manager.registerList(list) |
||||
await manager.start(['test']) |
||||
await helper.wait(50) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('test error') |
||||
}) |
||||
}) |
||||
|
||||
describe('first(), last()', () => { |
||||
it('should get session by name', async () => { |
||||
let last: string |
||||
let list: IList = { |
||||
name: 'test', |
||||
actions: [{ |
||||
name: 'open', |
||||
execute: (item: ListItem) => { |
||||
last = item.label |
||||
} |
||||
}], |
||||
defaultAction: 'open', |
||||
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }]) |
||||
} |
||||
manager.registerList(list) |
||||
await manager.start(['test']) |
||||
await manager.session.ui.ready |
||||
await manager.first('a') |
||||
await manager.last('a') |
||||
await manager.first('test') |
||||
expect(last).toBe('foo') |
||||
await manager.last('test') |
||||
expect(last).toBe('bar') |
||||
}) |
||||
}) |
||||
|
||||
describe('registerList()', () => { |
||||
it('should recreat list', async () => { |
||||
let list: IList = { |
||||
name: 'test', |
||||
actions: [{ |
||||
name: 'open', execute: _item => { |
||||
// noop
|
||||
} |
||||
}], |
||||
defaultAction: 'open', |
||||
loadItems: () => Promise.resolve([{ label: 'foo' }, { label: 'bar' }]) |
||||
} |
||||
manager.registerList(list) |
||||
helper.updateConfiguration('list.source.test.defaultAction', 'open') |
||||
let disposable = manager.registerList(list) |
||||
disposable.dispose() |
||||
await helper.wait(30) |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('recreated') |
||||
}) |
||||
}) |
||||
}) |
@ -1,903 +0,0 @@
@@ -1,903 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { CancellationToken } from 'vscode-jsonrpc' |
||||
import BasicList from '../../list/basic' |
||||
import manager from '../../list/manager' |
||||
import window from '../../window' |
||||
import { ListContext, IList, ListItem, QuickfixItem } from '../../types' |
||||
import helper from '../helper' |
||||
|
||||
class TestList extends BasicList { |
||||
public name = 'test' |
||||
public timeout = 3000 |
||||
public text = 'test' |
||||
public detail = 'detail' |
||||
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListItem[]> { |
||||
return new Promise(resolve => { |
||||
let timer = setTimeout(() => { |
||||
resolve([{ label: this.text }]) |
||||
}, this.timeout) |
||||
token.onCancellationRequested(() => { |
||||
if (timer) { |
||||
clearTimeout(timer) |
||||
resolve([]) |
||||
} |
||||
}) |
||||
}) |
||||
} |
||||
} |
||||
|
||||
let nvim: Neovim |
||||
const locations: ReadonlyArray<QuickfixItem> = [{ |
||||
filename: __filename, |
||||
col: 2, |
||||
lnum: 1, |
||||
text: 'foo' |
||||
}, { |
||||
filename: __filename, |
||||
col: 1, |
||||
lnum: 2, |
||||
text: 'Bar' |
||||
}, { |
||||
filename: __filename, |
||||
col: 1, |
||||
lnum: 3, |
||||
text: 'option' |
||||
}] |
||||
|
||||
const lineList: IList = { |
||||
name: 'lines', |
||||
actions: [{ |
||||
name: 'open', |
||||
execute: async item => { |
||||
await window.moveTo({ |
||||
line: (item as ListItem).data.line, |
||||
character: 0 |
||||
}) |
||||
// noop
|
||||
} |
||||
}], |
||||
defaultAction: 'open', |
||||
async loadItems(_context, _token): Promise<ListItem[]> { |
||||
let lines = [] |
||||
for (let i = 0; i < 100; i++) { |
||||
lines.push(i.toString()) |
||||
} |
||||
return lines.map((line, idx) => ({ |
||||
label: line, |
||||
data: { line: idx } |
||||
})) |
||||
} |
||||
} |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
await nvim.setVar('coc_jump_locations', locations) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('list normal mappings', () => { |
||||
it('should tabopen by t', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(100) |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(2) |
||||
}) |
||||
|
||||
it('should open by <cr>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<cr>", "in")') |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('expand', ['%:p']) |
||||
expect(bufname).toMatch('mappings.test.ts') |
||||
}) |
||||
|
||||
it('should stop by <C-c>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-c>", "in")') |
||||
await helper.wait(50) |
||||
let loading = manager.session?.worker.isLoading |
||||
expect(loading).toBe(false) |
||||
}) |
||||
|
||||
it('should jump back by <C-o>', async () => { |
||||
let doc = await helper.createDocument() |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-o>", "in")') |
||||
await helper.wait(50) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
expect(bufnr).toBe(doc.bufnr) |
||||
}) |
||||
|
||||
it('should scroll preview window by <C-e>, <C-y>', async () => { |
||||
await helper.createDocument() |
||||
await manager.start(['--auto-preview', '--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
let winnr = await nvim.call('coc#list#has_preview') as number |
||||
expect(winnr).toBeGreaterThan(0) |
||||
let winid = await nvim.call('win_getid', [winnr]) |
||||
await nvim.eval('feedkeys("\\<C-e>", "in")') |
||||
await helper.wait(50) |
||||
let res = await nvim.call('getwininfo', [winid]) |
||||
expect(res[0].topline).toBeGreaterThan(1) |
||||
await nvim.eval('feedkeys("\\<C-y>", "in")') |
||||
await helper.wait(50) |
||||
res = await nvim.call('getwininfo', [winid]) |
||||
expect(res[0].topline).toBeLessThan(7) |
||||
}) |
||||
|
||||
it('should insert command by :', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys(":", "in")') |
||||
await helper.wait(50) |
||||
await nvim.eval('feedkeys("let g:x = 1\\<cr>", "in")') |
||||
await helper.wait(50) |
||||
let res = await nvim.getVar('x') |
||||
expect(res).toBe(1) |
||||
}) |
||||
|
||||
it('should select action by <tab>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<tab>", "in")') |
||||
await helper.wait(100) |
||||
await nvim.input('t') |
||||
await helper.wait(300) |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(2) |
||||
}) |
||||
|
||||
it('should preview by p', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
await nvim.eval('feedkeys("p", "in")') |
||||
await helper.wait(200) |
||||
let winnr = await nvim.call('coc#list#has_preview') |
||||
expect(winnr).toBe(2) |
||||
}) |
||||
|
||||
it('should stop task by <C-c>', async () => { |
||||
let disposable = manager.registerList(new TestList(nvim)) |
||||
let p = manager.start(['--normal', 'test']) |
||||
await helper.wait(200) |
||||
await nvim.input('<C-c>') |
||||
await helper.wait(200) |
||||
await p |
||||
let len = manager.session?.ui.length |
||||
expect(len).toBe(0) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should cancel list by <esc>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<esc>", "in")') |
||||
await helper.wait(200) |
||||
expect(manager.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should reload list by <C-l>', async () => { |
||||
let list = new TestList(nvim) |
||||
list.timeout = 0 |
||||
let disposable = manager.registerList(list) |
||||
await manager.start(['--normal', 'test']) |
||||
await manager.session.ui.ready |
||||
list.text = 'new' |
||||
await nvim.input('<C-l>') |
||||
await helper.wait(30) |
||||
let line = await nvim.line |
||||
expect(line).toMatch('new') |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should select all items by <C-a>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.input('<C-a>') |
||||
await helper.wait(30) |
||||
let selected = manager.session?.ui.selectedItems |
||||
expect(selected.length).toBe(locations.length) |
||||
}) |
||||
|
||||
it('should toggle selection <space>', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<space>", "in")') |
||||
await helper.wait(100) |
||||
let selected = manager.session?.ui.selectedItems |
||||
expect(selected.length).toBe(1) |
||||
await nvim.eval('feedkeys("k", "in")') |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("\\<space>", "in")') |
||||
await helper.wait(100) |
||||
selected = manager.session?.ui.selectedItems |
||||
expect(selected.length).toBe(0) |
||||
}) |
||||
|
||||
it('should change to insert mode by i, o, a', async () => { |
||||
let keys = ['i', 'I', 'o', 'O', 'a', 'A'] |
||||
for (let key of keys) { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
await nvim.eval(`feedkeys("${key}", "in")`) |
||||
await helper.wait(100) |
||||
let mode = manager.prompt.mode |
||||
expect(mode).toBe('insert') |
||||
} |
||||
}) |
||||
|
||||
it('should show help by ?', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("?", "in")') |
||||
await helper.wait(30) |
||||
await nvim.input('<CR>') |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('bufname', '%') |
||||
expect(bufname).toBe('[LIST HELP]') |
||||
}) |
||||
|
||||
it('should drop by d', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("d", "in")') |
||||
await helper.wait(100) |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(1) |
||||
}) |
||||
|
||||
it('should split by s', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("s", "in")') |
||||
await helper.wait(100) |
||||
let nr = await nvim.call('winnr') |
||||
expect(nr).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('list insert mappings', () => { |
||||
it('should open by <cr>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<cr>", "in")') |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('expand', ['%:p']) |
||||
expect(bufname).toMatch('mappings.test.ts') |
||||
}) |
||||
|
||||
it('should paste input by <C-v>', async () => { |
||||
await nvim.call('setreg', ['*', 'foo']) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-v>", "in")') |
||||
await helper.wait(100) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('foo') |
||||
}) |
||||
|
||||
it('should insert register content by <C-r>', async () => { |
||||
await nvim.call('setreg', ['*', 'foo']) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-r>", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("*", "in")') |
||||
await helper.wait(100) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('foo') |
||||
await nvim.eval('feedkeys("\\<C-r>", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("<", "in")') |
||||
await helper.wait(100) |
||||
input = manager.prompt.input |
||||
expect(input).toBe('foo') |
||||
manager.prompt.reset() |
||||
}) |
||||
|
||||
it('should cancel by <esc>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<esc>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should select action by <tab>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
nvim.call('eval', 'feedkeys("\\<tab>", "in")', true) |
||||
await helper.wait(100) |
||||
await nvim.input('t') |
||||
await helper.wait(500) |
||||
let pages = await nvim.tabpages |
||||
expect(pages.length).toBe(2) |
||||
}) |
||||
|
||||
it('should select action for visual selected items', async () => { |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(100) |
||||
await nvim.input('V') |
||||
await helper.wait(30) |
||||
await nvim.input('2') |
||||
await helper.wait(30) |
||||
await nvim.input('j') |
||||
await helper.wait(30) |
||||
await manager.doAction('tabe') |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBeGreaterThan(3) |
||||
}) |
||||
|
||||
it('should stop loading by <C-c>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-c>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should reload by <C-l>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-l>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should change to normal mode by <C-o>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-o>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
let line = await helper.getCmdline() |
||||
expect(line).toBe('') |
||||
}) |
||||
|
||||
it('should select line by <down> and <up>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<down>", "in")') |
||||
await helper.wait(50) |
||||
await nvim.eval('feedkeys("\\<up>", "in")') |
||||
await helper.wait(50) |
||||
expect(manager.isActivated).toBe(true) |
||||
let line = await nvim.line |
||||
expect(line).toMatch('foo') |
||||
}) |
||||
|
||||
it('should move cursor by <left> and <right>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("\\<left>", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("\\<left>", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("a", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("\\<right>", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("\\<right>", "in")') |
||||
await helper.wait(10) |
||||
await nvim.eval('feedkeys("c", "in")') |
||||
await helper.wait(10) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('afc') |
||||
}) |
||||
|
||||
it('should move cursor by <end> and <home>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<home>", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<end>a", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('a') |
||||
}) |
||||
|
||||
it('should move cursor by <PageUp> <PageDown> <C-d>', async () => { |
||||
let disposable = manager.registerList(lineList) |
||||
await manager.start(['lines']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<PageDown>", "in")') |
||||
await helper.wait(100) |
||||
let line = await nvim.eval('line(".")') |
||||
expect(line).toBeGreaterThan(1) |
||||
await nvim.eval('feedkeys("\\<PageUp>", "in")') |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("\\<C-d>", "in")') |
||||
await helper.wait(100) |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should scroll window by <C-f> and <C-b>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<C-f>", "in")') |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("\\<C-b>", "in")') |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
it('should change input by <Backspace>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<Backspace>", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('') |
||||
}) |
||||
|
||||
it('should change input by <C-x>', async () => { |
||||
let revert = helper.updateConfiguration('list.insertMappings', { |
||||
'<C-b>': 'prompt:removetail', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("foo", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<C-a>", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<C-b>", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
revert() |
||||
expect(input).toBe('') |
||||
}) |
||||
|
||||
it('should change input by <C-h>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<C-h>", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('') |
||||
}) |
||||
|
||||
it('should change input by <C-w>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-w>", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("a", "in")') |
||||
await helper.wait(30) |
||||
await nvim.eval('feedkeys("\\<C-w>", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('') |
||||
}) |
||||
|
||||
it('should change input by <C-u>', async () => { |
||||
await manager.start(['--input=a', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-u>", "in")') |
||||
await helper.wait(30) |
||||
let input = manager.prompt.input |
||||
expect(input).toBe('') |
||||
}) |
||||
|
||||
it('should change input by <C-n> and <C-p>', async () => { |
||||
async function session(input: string): Promise<void> { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("${input}", "in")`) |
||||
await helper.wait(100) |
||||
await manager.cancel() |
||||
} |
||||
await session('foo') |
||||
await session('bar') |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await helper.wait(50) |
||||
await nvim.eval('feedkeys("\\<C-n>", "in")') |
||||
await helper.wait(100) |
||||
let input = manager.prompt.input |
||||
expect(input.length).toBeGreaterThan(0) |
||||
await nvim.eval('feedkeys("\\<C-p>", "in")') |
||||
await helper.wait(100) |
||||
input = manager.prompt.input |
||||
expect(input.length).toBeGreaterThan(0) |
||||
}) |
||||
|
||||
it('should change matcher by <C-s>', async () => { |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-s>", "in")') |
||||
await helper.wait(10) |
||||
let matcher = manager.session?.listOptions.matcher |
||||
expect(matcher).toBe('strict') |
||||
await nvim.eval('feedkeys("\\<C-s>", "in")') |
||||
await helper.wait(10) |
||||
matcher = manager.session?.listOptions.matcher |
||||
expect(matcher).toBe('regex') |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(30) |
||||
let len = manager.session?.ui.length |
||||
expect(len).toBeGreaterThan(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('evalExpression', () => { |
||||
it('should throw for bad expression', async () => { |
||||
let revert = helper.updateConfiguration('list.normalMappings', { |
||||
t: 'expr', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(30) |
||||
revert() |
||||
let msg = await helper.getCmdline() |
||||
expect(msg).toMatch('Invalid list mapping expression') |
||||
}) |
||||
|
||||
it('should show help', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'do:help', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let bufname = await nvim.call('bufname', ['%']) |
||||
expect(bufname).toMatch('[LIST HELP]') |
||||
}) |
||||
|
||||
it('should exit list', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'do:exit', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
expect(manager.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should cancel prompt', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'do:cancel', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let res = await nvim.call('coc#prompt#activated') |
||||
expect(res).toBe(0) |
||||
}) |
||||
|
||||
it('should jump back', async () => { |
||||
let doc = await helper.createDocument() |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'do:jumpback', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let bufnr = await nvim.call('bufnr', ['%']) |
||||
expect(bufnr).toBe(doc.bufnr) |
||||
}) |
||||
|
||||
it('should invoke normal command', async () => { |
||||
let revert = helper.updateConfiguration('list.normalMappings', { |
||||
x: 'normal!:G' |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("x", "in")`) |
||||
await helper.wait(50) |
||||
revert() |
||||
let lnum = await nvim.call('line', ['.']) |
||||
expect(lnum).toBeGreaterThan(1) |
||||
}) |
||||
|
||||
it('should toggle, scroll preview', async () => { |
||||
let revert = helper.updateConfiguration('list.normalMappings', { |
||||
'<space>': 'do:toggle', |
||||
a: 'do:toggle', |
||||
b: 'do:previewtoggle', |
||||
c: 'do:previewup', |
||||
d: 'do:previewdown', |
||||
e: 'prompt:insertregister', |
||||
f: 'do:stop', |
||||
g: 'do:togglemode', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys(" ", "in")`) |
||||
await helper.wait(50) |
||||
for (let key of ['a', 'b', 'c', 'd', 'e', 'f', 'g']) { |
||||
await nvim.eval(`feedkeys("${key}", "in")`) |
||||
await helper.wait(50) |
||||
} |
||||
revert() |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should show error when action not exists', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'do:invalid', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let cmd = await helper.getCmdline() |
||||
expect(cmd).toMatch('not supported') |
||||
}) |
||||
|
||||
it('should show error when prompt action not exists', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'prompt:invalid', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let cmd = await helper.getCmdline() |
||||
expect(cmd).toMatch('not supported') |
||||
}) |
||||
|
||||
it('should show error for invalid expression ', async () => { |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'x:y', |
||||
}) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("t", "in")') |
||||
await helper.wait(50) |
||||
let cmd = await helper.getCmdline() |
||||
expect(cmd).toMatch('Invalid expression') |
||||
}) |
||||
}) |
||||
|
||||
describe('User mappings', () => { |
||||
it('should show warning for invalid key', async () => { |
||||
let revert = helper.updateConfiguration('list.insertMappings', { |
||||
xy: 'action:tabe', |
||||
}) |
||||
await helper.wait(30) |
||||
let msg = await helper.getCmdline() |
||||
revert() |
||||
await nvim.command('echo ""') |
||||
expect(msg).toMatch('Invalid list mappings key') |
||||
revert = helper.updateConfiguration('list.insertMappings', { |
||||
'<M-x>': 'action:tabe', |
||||
}) |
||||
await helper.wait(30) |
||||
msg = await helper.getCmdline() |
||||
revert() |
||||
expect(msg).toMatch('Invalid list mappings key') |
||||
}) |
||||
|
||||
it('should execute action keymap', async () => { |
||||
await helper.wait(200) |
||||
let revert = helper.updateConfiguration('list.insertMappings', { |
||||
'<C-d>': 'action:tabe', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-d>", "in")`) |
||||
await helper.wait(200) |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(2) |
||||
revert() |
||||
}) |
||||
|
||||
it('should execute expr keymap', async () => { |
||||
await helper.mockFunction('TabOpen', 'tabe') |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-t>': 'expr:TabOpen', |
||||
}) |
||||
helper.updateConfiguration('list.normalMappings', { |
||||
t: 'expr:TabOpen', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-t>", "in")`) |
||||
await helper.wait(100) |
||||
let nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(2) |
||||
await manager.start(['--normal', 'location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("t", "in")`) |
||||
await helper.wait(100) |
||||
nr = await nvim.call('tabpagenr') |
||||
expect(nr).toBe(3) |
||||
}) |
||||
|
||||
it('should execute do mappings', async () => { |
||||
helper.updateConfiguration('list.previousKeymap', '<c-j>') |
||||
helper.updateConfiguration('list.nextKeymap', '<c-k>') |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-r>': 'do:refresh', |
||||
'<C-a>': 'do:selectall', |
||||
'<C-s>': 'do:switch', |
||||
'<C-l>': 'do:cancel', |
||||
'<C-t>': 'do:toggle', |
||||
'<C-n>': 'do:next', |
||||
'<C-p>': 'do:previous', |
||||
'<C-x>': 'do:defaultaction', |
||||
'<C-h>': 'do:help', |
||||
'<C-d>': 'do:exit', |
||||
'<C-b>': 'do:toggleMode' |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-r>", "in")') |
||||
await helper.wait(30) |
||||
expect(manager.isActivated).toBe(true) |
||||
await nvim.eval('feedkeys("\\<C-a>", "in")') |
||||
await helper.wait(30) |
||||
expect(manager.session?.ui.selectedItems.length).toBe(locations.length) |
||||
await nvim.eval('feedkeys("\\<C-s>", "in")') |
||||
await helper.wait(30) |
||||
expect(manager.session?.listOptions.matcher).toBe('strict') |
||||
await nvim.eval('feedkeys("\\<C-n>", "in")') |
||||
await helper.wait(30) |
||||
let item = await manager.session?.ui.item |
||||
expect(item.label).toMatch(locations[1].text) |
||||
await nvim.eval('feedkeys("\\<C-p>", "in")') |
||||
await helper.wait(30) |
||||
item = await manager.session?.ui.item |
||||
expect(item.label).toMatch(locations[0].text) |
||||
await nvim.eval('feedkeys("\\<C-x>", "in")') |
||||
await helper.wait(30) |
||||
expect(manager.isActivated).toBe(false) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-l>", "in")') |
||||
await helper.wait(50) |
||||
let res = await nvim.call('coc#prompt#activated') |
||||
expect(res).toBe(0) |
||||
await manager.session.hide() |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("?", "in")') |
||||
await helper.wait(30) |
||||
await nvim.input('<CR>') |
||||
await manager.cancel() |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-d>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(false) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval('feedkeys("\\<C-b>", "in")') |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
await nvim.call('coc#prompt#stop_prompt', ['list']) |
||||
}, 20000) |
||||
|
||||
it('should execute prompt mappings', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-p>': 'prompt:previous', |
||||
'<C-n>': 'prompt:next', |
||||
'<C-a>': 'prompt:start', |
||||
'<C-e>': 'prompt:end', |
||||
'<Left>': 'prompt:left', |
||||
'<Right>': 'prompt:right', |
||||
'<backspace>': 'prompt:deleteforward', |
||||
'<C-x>': 'prompt:deletebackward', |
||||
'<C-k>': 'prompt:removetail', |
||||
'<C-u>': 'prompt:removeahead', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
for (let key of ['<C-p>', '<C-n>', '<C-a>', '<C-e>', '<Left>', '<Right>', '<backspace>', '<C-x>', '<C-k>', '<C-u>']) { |
||||
await nvim.input(key) |
||||
await helper.wait(30) |
||||
} |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should execute feedkeys keymap', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-f>': 'feedkeys:\\<C-f>', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-f>", "in")`) |
||||
await helper.wait(30) |
||||
let line = await nvim.call('line', '.') |
||||
expect(line).toBe(locations.length) |
||||
}) |
||||
|
||||
it('should execute normal keymap', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-g>': 'normal:G', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-g>", "in")`) |
||||
await helper.wait(30) |
||||
let line = await nvim.call('line', '.') |
||||
expect(line).toBe(locations.length) |
||||
}) |
||||
|
||||
it('should execute command keymap', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-w>': 'command:wincmd p', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-w>", "in")`) |
||||
await helper.wait(30) |
||||
expect(manager.isActivated).toBe(true) |
||||
let winnr = await nvim.call('winnr') |
||||
expect(winnr).toBe(1) |
||||
}) |
||||
|
||||
it('should execute call keymap', async () => { |
||||
await helper.mockFunction('Test', 1) |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-t>': 'call:Test', |
||||
}) |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-t>", "in")`) |
||||
await helper.wait(30) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should insert clipboard register to prompt', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-r>': 'prompt:paste', |
||||
}) |
||||
await nvim.command('let @* = "foobar"') |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-r>", "in")`) |
||||
await helper.wait(100) |
||||
let { input } = manager.prompt |
||||
expect(input).toMatch('foobar') |
||||
await nvim.command('let @* = ""') |
||||
await nvim.eval(`feedkeys("\\<C-r>", "in")`) |
||||
await helper.wait(100) |
||||
expect(manager.prompt.input).toMatch('foobar') |
||||
}) |
||||
|
||||
it('should insert text from default register to prompt', async () => { |
||||
helper.updateConfiguration('list.insertMappings', { |
||||
'<C-v>': 'eval:@@', |
||||
}) |
||||
await nvim.command('let @@ = "bar"') |
||||
await manager.start(['location']) |
||||
await manager.session.ui.ready |
||||
await nvim.eval(`feedkeys("\\<C-v>", "in")`) |
||||
await helper.wait(200) |
||||
let { input } = manager.prompt |
||||
expect(input).toMatch('bar') |
||||
}) |
||||
}) |
@ -1,246 +0,0 @@
@@ -1,246 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable } from 'vscode-languageserver-protocol' |
||||
import BasicList from '../../list/basic' |
||||
import manager from '../../list/manager' |
||||
import ListSession from '../../list/session' |
||||
import { ListItem } from '../../types' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let labels: string[] = [] |
||||
let lastItem: string |
||||
let lastItems: ListItem[] |
||||
|
||||
class SimpleList extends BasicList { |
||||
public name = 'simple' |
||||
public detail = 'detail' |
||||
public options = [{ |
||||
name: 'foo', |
||||
description: 'foo' |
||||
}] |
||||
constructor(nvim: Neovim) { |
||||
super(nvim) |
||||
this.addAction('open', item => { |
||||
lastItem = item.label |
||||
}) |
||||
this.addMultipleAction('multiple', items => { |
||||
lastItems = items |
||||
}) |
||||
this.addAction('parallel', async () => { |
||||
await helper.wait(100) |
||||
}, { parallel: true }) |
||||
this.addAction('reload', item => { |
||||
lastItem = item.label |
||||
}, { persist: true, reload: true }) |
||||
} |
||||
public loadItems(): Promise<ListItem[]> { |
||||
return Promise.resolve(labels.map(s => { |
||||
return { label: s } as ListItem |
||||
})) |
||||
} |
||||
} |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('list session', () => { |
||||
describe('doDefaultAction()', () => { |
||||
it('should throw error when default action not exists', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
list.defaultAction = 'foo' |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
let err |
||||
try { |
||||
await manager.session.first() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
err = null |
||||
try { |
||||
await manager.session.last() |
||||
} catch (e) { |
||||
err = e |
||||
} |
||||
expect(err).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('doItemAction()', () => { |
||||
it('should invoke multiple action', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await ui.selectAll() |
||||
await manager.doAction('multiple') |
||||
expect(lastItems.length).toBe(3) |
||||
lastItems = undefined |
||||
}) |
||||
|
||||
it('should invoke parallel action', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await ui.selectAll() |
||||
let d = Date.now() |
||||
await manager.doAction('parallel') |
||||
expect(Date.now() - d).toBeLessThan(300) |
||||
}) |
||||
|
||||
it('should invoke reload action', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
labels = ['d', 'e'] |
||||
await manager.doAction('reload') |
||||
await helper.wait(50) |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual(['d', 'e']) |
||||
}) |
||||
}) |
||||
|
||||
describe('resume()', () => { |
||||
it('should do preview on resume', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let lastItem |
||||
let list = new SimpleList(nvim) |
||||
list.actions.push({ |
||||
name: 'preview', |
||||
execute: item => { |
||||
lastItem = item |
||||
} |
||||
}) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', '--auto-preview', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await ui.selectLines(1, 2) |
||||
await helper.wait(50) |
||||
await nvim.call('coc#window#close', [ui.winid]) |
||||
await helper.wait(100) |
||||
await manager.session.resume() |
||||
await helper.wait(100) |
||||
expect(lastItem).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('jumpBack()', () => { |
||||
it('should jump back', async () => { |
||||
let win = await nvim.window |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
manager.session.jumpBack() |
||||
await helper.wait(50) |
||||
let winid = await nvim.call('win_getid') |
||||
expect(winid).toBe(win.id) |
||||
}) |
||||
}) |
||||
|
||||
describe('doNumberSelect()', () => { |
||||
async function create(len: number): Promise<ListSession> { |
||||
labels = [] |
||||
for (let i = 0; i < len; i++) { |
||||
let code = 'a'.charCodeAt(0) + i |
||||
labels.push(String.fromCharCode(code)) |
||||
} |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', '--number-select', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
return manager.session |
||||
} |
||||
|
||||
it('should return false for invalid number', async () => { |
||||
let session = await create(5) |
||||
let res = await session.doNumberSelect('a') |
||||
expect(res).toBe(false) |
||||
res = await session.doNumberSelect('8') |
||||
expect(res).toBe(false) |
||||
}) |
||||
|
||||
it('should consider 0 as 10', async () => { |
||||
let session = await create(15) |
||||
let res = await session.doNumberSelect('0') |
||||
expect(res).toBe(true) |
||||
expect(lastItem).toBe('j') |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
describe('showHelp()', () => { |
||||
it('should show description and options in help', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await manager.session.showHelp() |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines.indexOf('DESCRIPTION')).toBeGreaterThan(0) |
||||
expect(lines.indexOf('ARGUMENTS')).toBeGreaterThan(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('chooseAction()', () => { |
||||
it('should filter actions not have shortcuts', async () => { |
||||
labels = ['a', 'b', 'c'] |
||||
let list = new SimpleList(nvim) |
||||
list.actions.push({ |
||||
name: 'a', |
||||
execute: () => { |
||||
} |
||||
}) |
||||
list.actions.push({ |
||||
name: 'b', |
||||
execute: () => { |
||||
} |
||||
}) |
||||
list.actions.push({ |
||||
name: 'ab', |
||||
execute: () => { |
||||
} |
||||
}) |
||||
disposables.push(manager.registerList(list)) |
||||
await manager.start(['--normal', 'simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
let p = manager.session.chooseAction() |
||||
await helper.wait(100) |
||||
await nvim.input('a') |
||||
await p |
||||
}) |
||||
}) |
@ -1,203 +0,0 @@
@@ -1,203 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { ListContext, ListItem, ListArgument } from '../../types' |
||||
import manager from '../../list/manager' |
||||
import languages from '../../languages' |
||||
import helper from '../helper' |
||||
import workspace from '../../workspace' |
||||
import { CancellationToken } from 'vscode-jsonrpc' |
||||
import { Location, Range } from 'vscode-languageserver-types' |
||||
import BasicList from '../../list/basic' |
||||
|
||||
let listItems: ListItem[] = [] |
||||
class OptionList extends BasicList { |
||||
public name = 'option' |
||||
public options: ListArgument[] = [{ |
||||
name: '-w, -word', |
||||
description: 'word' |
||||
}, { |
||||
name: '-i, -input INPUT', |
||||
hasValue: true, |
||||
description: 'input' |
||||
}] |
||||
constructor(nvim) { |
||||
super(nvim) |
||||
this.addLocationActions() |
||||
} |
||||
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListItem[]> { |
||||
return Promise.resolve(listItems) |
||||
} |
||||
} |
||||
jest.setTimeout(3000) |
||||
|
||||
let nvim: Neovim |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
manager.dispose() |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
manager.reset() |
||||
await helper.reset() |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
describe('BasicList', () => { |
||||
describe('parse arguments', () => { |
||||
it('should parse args #1', () => { |
||||
let list = new OptionList(nvim) |
||||
let res = list.parseArguments(['-w']) |
||||
expect(res).toEqual({ word: true }) |
||||
}) |
||||
|
||||
it('should parse args #2', () => { |
||||
let list = new OptionList(nvim) |
||||
let res = list.parseArguments(['-word']) |
||||
expect(res).toEqual({ word: true }) |
||||
}) |
||||
|
||||
it('should parse args #3', () => { |
||||
let list = new OptionList(nvim) |
||||
let res = list.parseArguments(['-input', 'foo']) |
||||
expect(res).toEqual({ input: 'foo' }) |
||||
}) |
||||
}) |
||||
|
||||
describe('preview()', () => { |
||||
it('should preview sketch buffer', async () => { |
||||
await nvim.command('new') |
||||
await nvim.setLine('foo') |
||||
let buffer = await nvim.buffer |
||||
await helper.wait(30) |
||||
let doc = workspace.getDocument(buffer.id) |
||||
expect(doc.uri).toMatch('untitled') |
||||
let list = new OptionList(nvim) |
||||
listItems.push({ |
||||
label: 'foo', |
||||
location: Location.create(doc.uri, Range.create(0, 0, 0, 0)) |
||||
}) |
||||
let disposable = manager.registerList(list) |
||||
await manager.start(['option']) |
||||
await helper.wait(100) |
||||
await manager.doAction('preview') |
||||
await helper.wait(100) |
||||
await nvim.command('wincmd p') |
||||
let win = await nvim.window |
||||
let isPreview = await win.getVar('previewwindow') |
||||
expect(isPreview).toBe(1) |
||||
let line = await nvim.line |
||||
expect(line).toBe('foo') |
||||
disposable.dispose() |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
describe('list sources', () => { |
||||
|
||||
describe('commands', () => { |
||||
it('should load commands source', async () => { |
||||
await manager.start(['commands']) |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should do run action', async () => { |
||||
await manager.start(['commands']) |
||||
await helper.wait(100) |
||||
await manager.doAction() |
||||
}) |
||||
}) |
||||
|
||||
describe('diagnostics', () => { |
||||
it('should load diagnostics source', async () => { |
||||
await manager.start(['diagnostics']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('extensions', () => { |
||||
it('should load extensions source', async () => { |
||||
await manager.start(['extensions']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('folders', () => { |
||||
it('should load folders source', async () => { |
||||
await manager.start(['folders']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('lists', () => { |
||||
it('should load lists source', async () => { |
||||
await manager.start(['lists']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('outline', () => { |
||||
it('should load outline source', async () => { |
||||
await manager.start(['outline']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('services', () => { |
||||
it('should load services source', async () => { |
||||
await manager.start(['services']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('sources', () => { |
||||
it('should load sources source', async () => { |
||||
await manager.start(['sources']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('symbols', () => { |
||||
it('should load symbols source', async () => { |
||||
let disposable = languages.registerWorkspaceSymbolProvider({ |
||||
provideWorkspaceSymbols: () => [] |
||||
}) |
||||
await manager.start(['symbols']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
disposable.dispose() |
||||
}) |
||||
}) |
||||
|
||||
describe('links', () => { |
||||
it('should load links source', async () => { |
||||
let disposable = languages.registerDocumentLinkProvider([{ scheme: 'file' }, { scheme: 'untitled' }], { |
||||
provideDocumentLinks: () => [] |
||||
}) |
||||
await manager.start(['links']) |
||||
await manager.session?.ui.ready |
||||
await helper.wait(100) |
||||
expect(manager.isActivated).toBe(true) |
||||
disposable.dispose() |
||||
}) |
||||
}) |
||||
}) |
@ -1,140 +0,0 @@
@@ -1,140 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable } from 'vscode-languageserver-protocol' |
||||
import BasicList from '../../list/basic' |
||||
import events from '../../events' |
||||
import manager from '../../list/manager' |
||||
import { ListItem } from '../../types' |
||||
import { disposeAll } from '../../util' |
||||
import helper from '../helper' |
||||
|
||||
let labels: string[] = [] |
||||
let lastItem: string |
||||
|
||||
class SimpleList extends BasicList { |
||||
public name = 'simple' |
||||
constructor(nvim: Neovim) { |
||||
super(nvim) |
||||
this.addAction('open', item => { |
||||
lastItem = item.label |
||||
}) |
||||
} |
||||
public loadItems(): Promise<ListItem[]> { |
||||
return Promise.resolve(labels.map(s => { |
||||
return { label: s, ansiHighlights: [{ span: [0, 1], hlGroup: 'Search' }] } as ListItem |
||||
})) |
||||
} |
||||
} |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('list ui', () => { |
||||
describe('selectLines()', () => { |
||||
it('should select lines', async () => { |
||||
labels = ['foo', 'bar'] |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await ui.selectLines(3, 1) |
||||
let buf = await nvim.buffer |
||||
let res = await buf.getSigns({ group: 'coc-list' }) |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('resume()', () => { |
||||
it('should resume with selected lines', async () => { |
||||
labels = ['foo', 'bar'] |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await ui.selectLines(1, 2) |
||||
await nvim.call('coc#window#close', [ui.winid]) |
||||
await helper.wait(100) |
||||
await manager.session.resume() |
||||
await helper.wait(100) |
||||
let buf = await nvim.buffer |
||||
let res = await buf.getSigns({ group: 'coc-list' }) |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('events', () => { |
||||
async function mockMouse(winid: number, lnum: number): Promise<void> { |
||||
await nvim.command(`let v:mouse_winid = ${winid}`) |
||||
await nvim.command(`let v:mouse_lnum = ${lnum}`) |
||||
await nvim.command('let v:mouse_col = 1') |
||||
} |
||||
|
||||
it('should fire action on double click', async () => { |
||||
labels = ['foo', 'bar'] |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await mockMouse(ui.winid, 1) |
||||
await manager.session.onMouseEvent('<2-LeftMouse>') |
||||
await helper.wait(100) |
||||
expect(lastItem).toBe('foo') |
||||
}) |
||||
|
||||
it('should select clicked line', async () => { |
||||
labels = ['foo', 'bar'] |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await mockMouse(ui.winid, 2) |
||||
await ui.onMouse('mouseDown') |
||||
await helper.wait(50) |
||||
await mockMouse(ui.winid, 2) |
||||
await ui.onMouse('mouseUp') |
||||
await helper.wait(50) |
||||
let item = await ui.item |
||||
expect(item.label).toBe('bar') |
||||
}) |
||||
|
||||
it('should jump to original window on click', async () => { |
||||
labels = ['foo', 'bar'] |
||||
let win = await nvim.window |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await mockMouse(win.id, 1) |
||||
await ui.onMouse('mouseUp') |
||||
await helper.wait(50) |
||||
let curr = await nvim.window |
||||
expect(curr.id).toBe(win.id) |
||||
}) |
||||
|
||||
it('should highlights items on CursorMoved', async () => { |
||||
labels = (new Array(400)).fill('a') |
||||
disposables.push(manager.registerList(new SimpleList(nvim))) |
||||
await manager.start(['simple', '--normal']) |
||||
let ui = manager.session.ui |
||||
await ui.ready |
||||
await nvim.call('cursor', [350, 1]) |
||||
await events.fire('CursorMoved', [ui.bufnr, [350, 1]]) |
||||
await helper.wait(300) |
||||
let res = await nvim.call('getmatches') |
||||
expect(res.length).toBeGreaterThan(300) |
||||
}) |
||||
}) |
||||
}) |
@ -1,204 +0,0 @@
@@ -1,204 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import manager from '../../list/manager' |
||||
import { parseInput } from '../../list/worker' |
||||
import helper from '../helper' |
||||
import { ListContext, ListTask, ListItem } from '../../types' |
||||
import { CancellationToken, Disposable } from 'vscode-languageserver-protocol' |
||||
import { EventEmitter } from 'events' |
||||
import colors from 'colors/safe' |
||||
import BasicList from '../../list/basic' |
||||
import { disposeAll } from '../../util' |
||||
|
||||
let items: ListItem[] = [] |
||||
|
||||
class DataList extends BasicList { |
||||
public name = 'data' |
||||
public loadItems(): Promise<ListItem[]> { |
||||
return Promise.resolve(items) |
||||
} |
||||
} |
||||
|
||||
class IntervalTaskList extends BasicList { |
||||
public name = 'task' |
||||
public timeout = 3000 |
||||
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListTask> { |
||||
let emitter: any = new EventEmitter() |
||||
let i = 0 |
||||
let interval = setInterval(() => { |
||||
emitter.emit('data', { label: i.toFixed() }) |
||||
i++ |
||||
}, 50) |
||||
emitter.dispose = () => { |
||||
clearInterval(interval) |
||||
emitter.emit('end') |
||||
} |
||||
token.onCancellationRequested(() => { |
||||
emitter.dispose() |
||||
}) |
||||
return emitter |
||||
} |
||||
} |
||||
|
||||
class DelayTask extends BasicList { |
||||
public name = 'delay' |
||||
public interactive = true |
||||
public loadItems(_context: ListContext, token: CancellationToken): Promise<ListTask> { |
||||
let emitter: any = new EventEmitter() |
||||
let disposed = false |
||||
setTimeout(() => { |
||||
if (disposed) return |
||||
emitter.emit('data', { label: 'ahead' }) |
||||
}, 100) |
||||
setTimeout(() => { |
||||
if (disposed) return |
||||
emitter.emit('data', { label: 'abort' }) |
||||
}, 200) |
||||
emitter.dispose = () => { |
||||
disposed = true |
||||
emitter.emit('end') |
||||
} |
||||
token.onCancellationRequested(() => { |
||||
emitter.dispose() |
||||
}) |
||||
return emitter |
||||
} |
||||
} |
||||
|
||||
class InteractiveList extends BasicList { |
||||
public name = 'test' |
||||
public interactive = true |
||||
public loadItems(context: ListContext, _token: CancellationToken): Promise<ListItem[]> { |
||||
return Promise.resolve([{ |
||||
label: colors.magenta(context.input || '') |
||||
}]) |
||||
} |
||||
} |
||||
|
||||
class ErrorList extends BasicList { |
||||
public name = 'error' |
||||
public interactive = true |
||||
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListItem[]> { |
||||
return Promise.reject(new Error('test error')) |
||||
} |
||||
} |
||||
|
||||
class ErrorTaskList extends BasicList { |
||||
public name = 'task' |
||||
public loadItems(_context: ListContext, _token: CancellationToken): Promise<ListTask> { |
||||
let emitter: any = new EventEmitter() |
||||
let timeout = setTimeout(() => { |
||||
emitter.emit('error', new Error('task error')) |
||||
}, 100) |
||||
emitter.dispose = () => { |
||||
clearTimeout(timeout) |
||||
} |
||||
return emitter |
||||
} |
||||
} |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('parseInput', () => { |
||||
it('should parse input with space', async () => { |
||||
let res = parseInput('a b') |
||||
expect(res).toEqual(['a', 'b']) |
||||
}) |
||||
|
||||
it('should parse input with escaped space', async () => { |
||||
let res = parseInput('a\\ b') |
||||
expect(res).toEqual(['a b']) |
||||
}) |
||||
}) |
||||
|
||||
describe('list worker', () => { |
||||
|
||||
it('should work with long running task', async () => { |
||||
disposables.push(manager.registerList(new IntervalTaskList(nvim))) |
||||
await manager.start(['task']) |
||||
await helper.wait(300) |
||||
let len = manager.session?.length |
||||
expect(len > 2).toBe(true) |
||||
await manager.cancel() |
||||
}) |
||||
|
||||
it('should sort by sortText', async () => { |
||||
items = [{ |
||||
label: 'abc', |
||||
sortText: 'b' |
||||
}, { |
||||
label: 'ade', |
||||
sortText: 'a' |
||||
}] |
||||
disposables.push(manager.registerList(new DataList(nvim))) |
||||
await manager.start(['data']) |
||||
await helper.wait(100) |
||||
await nvim.input('a') |
||||
await helper.wait(100) |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual(['ade', 'abc']) |
||||
await manager.cancel() |
||||
}) |
||||
|
||||
it('should cancel task by use CancellationToken', async () => { |
||||
disposables.push(manager.registerList(new IntervalTaskList(nvim))) |
||||
await manager.start(['task']) |
||||
expect(manager.session?.worker.isLoading).toBe(true) |
||||
await helper.wait(100) |
||||
manager.session?.stop() |
||||
expect(manager.session?.worker.isLoading).toBe(false) |
||||
}) |
||||
|
||||
it('should render slow interactive list', async () => { |
||||
disposables.push(manager.registerList(new DelayTask(nvim))) |
||||
await manager.start(['delay']) |
||||
await nvim.input('a') |
||||
await helper.wait(600) |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines).toEqual(['ahead', 'abort']) |
||||
}) |
||||
|
||||
it('should work with interactive list', async () => { |
||||
disposables.push(manager.registerList(new InteractiveList(nvim))) |
||||
await manager.start(['-I', 'test']) |
||||
await manager.session?.ui.ready |
||||
expect(manager.isActivated).toBe(true) |
||||
await nvim.eval('feedkeys("f", "in")') |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("a", "in")') |
||||
await helper.wait(100) |
||||
await nvim.eval('feedkeys("x", "in")') |
||||
await helper.wait(300) |
||||
let item = await manager.session?.ui.item |
||||
expect(item.label).toBe('fax') |
||||
}) |
||||
|
||||
it('should not activate on load error', async () => { |
||||
disposables.push(manager.registerList(new ErrorList(nvim))) |
||||
await manager.start(['test']) |
||||
expect(manager.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should deactivate on task error', async () => { |
||||
disposables.push(manager.registerList(new ErrorTaskList(nvim))) |
||||
await manager.start(['task']) |
||||
await helper.wait(500) |
||||
expect(manager.isActivated).toBe(false) |
||||
}) |
||||
}) |
@ -1,151 +0,0 @@
@@ -1,151 +0,0 @@
|
||||
import { getHighlightItems, parseMarkdown, parseDocuments } from '../../markdown/index' |
||||
|
||||
describe('getHighlightItems', () => { |
||||
it('should get highlights in single line', async () => { |
||||
let res = getHighlightItems('this line has highlights', 0, [10, 15]) |
||||
expect(res).toEqual([{ |
||||
colStart: 10, |
||||
colEnd: 15, |
||||
lnum: 0, |
||||
hlGroup: 'CocUnderline' |
||||
}]) |
||||
}) |
||||
|
||||
it('should get highlights when active end extended', async () => { |
||||
let res = getHighlightItems('this line', 0, [5, 30]) |
||||
expect(res).toEqual([{ |
||||
colStart: 5, |
||||
colEnd: 9, |
||||
lnum: 0, |
||||
hlGroup: 'CocUnderline' |
||||
}]) |
||||
}) |
||||
|
||||
it('should get highlights across line', async () => { |
||||
let res = getHighlightItems('this line\nhas highlights', 0, [5, 15]) |
||||
expect(res).toEqual([{ |
||||
colStart: 5, colEnd: 9, lnum: 0, hlGroup: 'CocUnderline' |
||||
}, { |
||||
colStart: 0, colEnd: 5, lnum: 1, hlGroup: 'CocUnderline' |
||||
}]) |
||||
}) |
||||
}) |
||||
|
||||
describe('parseMarkdown', () => { |
||||
it('should parse code blocks', async () => { |
||||
let content = ` |
||||
\`\`\`js
|
||||
var global = globalThis |
||||
\`\`\` |
||||
\`\`\`ts
|
||||
let str:string |
||||
\`\`\` |
||||
` |
||||
let res = parseMarkdown(content, {}) |
||||
expect(res.lines).toEqual([ |
||||
'var global = globalThis', |
||||
'', |
||||
'let str:string' |
||||
]) |
||||
expect(res.codes).toEqual([ |
||||
{ filetype: 'javascript', startLine: 0, endLine: 1 }, |
||||
{ filetype: 'typescript', startLine: 2, endLine: 3 } |
||||
]) |
||||
}) |
||||
|
||||
it('should parse html code block', async () => { |
||||
let content = ` |
||||
example: |
||||
\`\`\`html
|
||||
<div>code</div> |
||||
\`\`\` |
||||
` |
||||
let res = parseMarkdown(content, {}) |
||||
expect(res.lines).toEqual(['example:', '', '<div>code</div>']) |
||||
expect(res.codes).toEqual([{ filetype: 'html', startLine: 2, endLine: 3 }]) |
||||
}) |
||||
|
||||
it('should compose empty lines', async () => { |
||||
let content = 'foo\n\n\nbar\n\n\n' |
||||
let res = parseMarkdown(content, {}) |
||||
expect(res.lines).toEqual(['foo', '', 'bar']) |
||||
}) |
||||
|
||||
it('should parse ansi highlights', async () => { |
||||
let content = '__foo__\n[link](link)' |
||||
let res = parseMarkdown(content, {}) |
||||
expect(res.lines).toEqual(['foo', 'link']) |
||||
expect(res.highlights).toEqual([ |
||||
{ hlGroup: 'CocBold', lnum: 0, colStart: 0, colEnd: 3 }, |
||||
{ hlGroup: 'CocUnderline', lnum: 1, colStart: 0, colEnd: 4 } |
||||
]) |
||||
}) |
||||
|
||||
it('should exclude images by option', async () => { |
||||
let content = 'head\n\ncontent  ' |
||||
let res = parseMarkdown(content, { excludeImages: true }) |
||||
expect(res.lines).toEqual(['head', '', 'content']) |
||||
}) |
||||
}) |
||||
|
||||
describe('parseDocuments', () => { |
||||
it('should parse documents with diagnostic filetypes', async () => { |
||||
let docs = [{ |
||||
filetype: 'Error', |
||||
content: 'Error text' |
||||
}, { |
||||
filetype: 'Warning', |
||||
content: 'Warning text' |
||||
}] |
||||
let res = parseDocuments(docs) |
||||
expect(res.lines).toEqual([ |
||||
'Error text', |
||||
'─', |
||||
'Warning text' |
||||
]) |
||||
expect(res.codes).toEqual([ |
||||
{ hlGroup: 'CocErrorFloat', startLine: 0, endLine: 1 }, |
||||
{ hlGroup: 'CocWarningFloat', startLine: 2, endLine: 3 } |
||||
]) |
||||
}) |
||||
|
||||
it('should parse markdown document with filetype document', async () => { |
||||
let docs = [{ |
||||
filetype: 'typescript', |
||||
content: 'const workspace' |
||||
}, { |
||||
filetype: 'markdown', |
||||
content: '**header**' |
||||
}] |
||||
let res = parseDocuments(docs) |
||||
expect(res.lines).toEqual([ |
||||
'const workspace', |
||||
'─', |
||||
'header' |
||||
]) |
||||
expect(res.highlights).toEqual([{ |
||||
hlGroup: 'CocBold', |
||||
lnum: 2, |
||||
colStart: 0, |
||||
colEnd: 6 |
||||
}]) |
||||
expect(res.codes).toEqual([ |
||||
{ filetype: 'typescript', startLine: 0, endLine: 1 } |
||||
]) |
||||
}) |
||||
|
||||
it('should parse documents with active highlights', async () => { |
||||
let docs = [{ |
||||
filetype: 'javascript', |
||||
content: 'func(foo, bar)', |
||||
active: [5, 8] |
||||
}, { |
||||
filetype: 'javascript', |
||||
content: 'func()', |
||||
active: [15, 20] |
||||
}] |
||||
let res = parseDocuments(docs as any) |
||||
expect(res.highlights).toEqual([{ colStart: 5, colEnd: 8, lnum: 0, hlGroup: 'CocUnderline' } |
||||
]) |
||||
}) |
||||
}) |
@ -1,119 +0,0 @@
@@ -1,119 +0,0 @@
|
||||
import marked from 'marked' |
||||
import Renderer from '../../markdown/renderer' |
||||
import * as styles from '../../markdown/styles' |
||||
import { parseAnsiHighlights, AnsiResult } from '../../util/ansiparse' |
||||
|
||||
marked.setOptions({ |
||||
renderer: new Renderer() |
||||
}) |
||||
|
||||
function parse(text: string): AnsiResult { |
||||
let m = marked(text) |
||||
let res = parseAnsiHighlights(m.split(/\n/)[0], true) |
||||
return res |
||||
} |
||||
|
||||
describe('styles', () => { |
||||
it('should add styles', async () => { |
||||
let keys = ['gray', 'magenta', 'bold', 'underline', 'italic', 'strikethrough', 'yellow', 'green', 'blue'] |
||||
for (let key of keys) { |
||||
let res = styles[key]('text') |
||||
expect(res).toContain('text') |
||||
} |
||||
}) |
||||
}) |
||||
|
||||
describe('Renderer of marked', () => { |
||||
it('should create bold highlights', async () => { |
||||
let res = parse('**note**.') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 4], |
||||
hlGroup: 'CocBold' |
||||
}) |
||||
}) |
||||
|
||||
it('should create italic highlights', async () => { |
||||
let res = parse('_note_.') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 4], |
||||
hlGroup: 'CocItalic' |
||||
}) |
||||
}) |
||||
|
||||
it('should create underline highlights for link', async () => { |
||||
let res = parse('[baidu](https://baidu.com)') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 5], |
||||
hlGroup: 'CocMarkdownLink' |
||||
}) |
||||
res = parse('https://baidu.com') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 17], |
||||
hlGroup: 'CocUnderline' |
||||
}) |
||||
}) |
||||
|
||||
it('should parse link', async () => { |
||||
// let res = parse('https://doc.rust-lang.org/nightly/core/iter/traits/iterator/Iterator.t.html#map.v')
|
||||
// console.log(JSON.stringify(res, null, 2))
|
||||
let link = 'https://doc.rust-lang.org/nightly/core/iter/traits/iterator/Iterator.t.html#map.v' |
||||
let parsed = marked(link) |
||||
let res = parseAnsiHighlights(parsed.split(/\n/)[0], true) |
||||
expect(res.line).toEqual(link) |
||||
expect(res.highlights.length).toBeGreaterThan(0) |
||||
expect(res.highlights[0].hlGroup).toBe('CocUnderline') |
||||
}) |
||||
|
||||
it('should create highlight for code span', async () => { |
||||
let res = parse('`let foo = "bar"`') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 15], |
||||
hlGroup: 'CocMarkdownCode' |
||||
}) |
||||
}) |
||||
|
||||
it('should create header highlights', async () => { |
||||
let res = parse('# header') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 8], |
||||
hlGroup: 'CocMarkdownHeader' |
||||
}) |
||||
res = parse('## header') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 9], |
||||
hlGroup: 'CocMarkdownHeader' |
||||
}) |
||||
res = parse('### header') |
||||
expect(res.highlights[0]).toEqual({ |
||||
span: [0, 10], |
||||
hlGroup: 'CocMarkdownHeader' |
||||
}) |
||||
}) |
||||
|
||||
it('should indent blockquote', async () => { |
||||
let res = parse('> header') |
||||
expect(res.line).toBe(' header') |
||||
}) |
||||
|
||||
it('should preserve code block', async () => { |
||||
let text = '``` js\nconsole.log("foo")\n```' |
||||
let m = marked(text) |
||||
expect(m.split('\n')).toEqual([ |
||||
'``` js', |
||||
'console.log("foo")', |
||||
'```', |
||||
'' |
||||
]) |
||||
}) |
||||
|
||||
it('should renderer table', async () => { |
||||
let text = ` |
||||
| Syntax | Description | |
||||
| ----------- | ----------- | |
||||
| Header | Title | |
||||
| Paragraph | Text | |
||||
` |
||||
let res = marked(text) |
||||
expect(res).toContain('Syntax') |
||||
}) |
||||
}) |
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
{} |
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
import * as assert from 'assert' |
||||
import * as arrays from '../../util/array' |
||||
|
||||
describe('Arrays', () => { |
||||
|
||||
test('distinct', () => { |
||||
function compare(a: string): string { |
||||
return a |
||||
} |
||||
|
||||
assert.deepStrictEqual(arrays.distinct(['32', '4', '5'], compare), ['32', '4', '5']) |
||||
assert.deepStrictEqual(arrays.distinct(['32', '4', '5', '4'], compare), ['32', '4', '5']) |
||||
assert.deepStrictEqual(arrays.distinct(['32', 'constructor', '5', '1'], compare), ['32', 'constructor', '5', '1']) |
||||
assert.deepStrictEqual(arrays.distinct(['32', 'constructor', 'proto', 'proto', 'constructor'], compare), ['32', 'constructor', 'proto']) |
||||
assert.deepStrictEqual(arrays.distinct(['32', '4', '5', '32', '4', '5', '32', '4', '5', '5'], compare), ['32', '4', '5']) |
||||
}) |
||||
|
||||
test('tail', () => { |
||||
assert.strictEqual(arrays.tail([1, 2, 3]), 3) |
||||
}) |
||||
|
||||
test('lastIndex', () => { |
||||
let res = arrays.lastIndex([1, 2, 3], x => x < 3) |
||||
assert.strictEqual(res, 1) |
||||
}) |
||||
|
||||
test('flatMap', () => { |
||||
let objs: { [key: string]: number[] }[] = [{ x: [1, 2] }, { y: [3, 4] }, { z: [5, 6] }] |
||||
function values(item: { [key: string]: number[] }): number[] { |
||||
return Object.keys(item).reduce((p, c) => p.concat(item[c]), []) |
||||
} |
||||
let res = arrays.flatMap(objs, values) |
||||
assert.deepStrictEqual(res, [1, 2, 3, 4, 5, 6]) |
||||
}) |
||||
}) |
@ -1,53 +0,0 @@
@@ -1,53 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import events from '../../events' |
||||
import helper from '../helper' |
||||
|
||||
function wait(ms: number): Promise<void> { |
||||
return new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve() |
||||
}, ms) |
||||
}) |
||||
} |
||||
let nvim: Neovim |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('attach', () => { |
||||
|
||||
it('should listen CocInstalled', async () => { |
||||
nvim.emit('notification', 'VimEnter') |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
it('should not throw on event handler error', async () => { |
||||
events.on('CursorHold', async () => { |
||||
throw new Error('error') |
||||
}) |
||||
let fn = jest.fn() |
||||
nvim.emit('request', 'CocAutocmd', ['CursorHold'], { |
||||
send: fn |
||||
}) |
||||
await wait(100) |
||||
expect(fn).toBeCalled() |
||||
}) |
||||
|
||||
it('should not throw when plugin method not found', async () => { |
||||
let fn = jest.fn() |
||||
nvim.emit('request', 'NotExists', [], { |
||||
send: fn |
||||
}) |
||||
await wait(100) |
||||
expect(fn).toBeCalled() |
||||
}) |
||||
}) |
@ -1,78 +0,0 @@
@@ -1,78 +0,0 @@
|
||||
import { Chars } from '../../model/chars' |
||||
|
||||
describe('chars keyword option', () => { |
||||
it('should match @', () => { |
||||
let chars = new Chars('@') |
||||
expect(chars.isKeywordChar('a')).toBe(true) |
||||
expect(chars.isKeywordChar('z')).toBe(true) |
||||
expect(chars.isKeywordChar('A')).toBe(true) |
||||
expect(chars.isKeywordChar('Z')).toBe(true) |
||||
}) |
||||
|
||||
it('should match letter range', () => { |
||||
let chars = new Chars('a-z') |
||||
expect(chars.isKeywordChar('a')).toBe(true) |
||||
expect(chars.isKeywordChar('A')).toBe(false) |
||||
}) |
||||
|
||||
it('should match code range', () => { |
||||
let chars = new Chars('48-57') |
||||
expect(chars.isKeywordChar('a')).toBe(false) |
||||
expect(chars.isKeywordChar('0')).toBe(true) |
||||
expect(chars.isKeywordChar('9')).toBe(true) |
||||
}) |
||||
|
||||
it('should match @-@', () => { |
||||
let chars = new Chars('@-@') |
||||
expect(chars.isKeywordChar('@')).toBe(true) |
||||
}) |
||||
|
||||
it('should match single code', () => { |
||||
let chars = new Chars('58') |
||||
expect(chars.isKeywordChar(':')).toBe(true) |
||||
}) |
||||
|
||||
it('should match single character', () => { |
||||
let chars = new Chars('_') |
||||
expect(chars.isKeywordChar('_')).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('chars addKeyword', () => { |
||||
it('should add keyword', () => { |
||||
let chars = new Chars('_') |
||||
chars.addKeyword(':') |
||||
expect(chars.isKeywordChar(':')).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('chars change keyword', () => { |
||||
it('should change keyword', () => { |
||||
let chars = new Chars('_') |
||||
chars.setKeywordOption(':') |
||||
expect(chars.isKeywordChar(':')).toBe(true) |
||||
expect(chars.isKeywordChar('_')).toBe(false) |
||||
}) |
||||
}) |
||||
|
||||
describe('chars match keywords', () => { |
||||
it('should match keywords', () => { |
||||
let chars = new Chars('@') |
||||
let res = chars.matchKeywords('foo bar') |
||||
expect(res).toEqual(['foo', 'bar']) |
||||
}) |
||||
|
||||
it('should consider unicode character as word', () => { |
||||
let chars = new Chars('@') |
||||
let res = chars.matchKeywords('blackкофе') |
||||
expect(res).toEqual(['blackкофе']) |
||||
}) |
||||
}) |
||||
|
||||
describe('chars isKeyword', () => { |
||||
it('should check isKeyword', () => { |
||||
let chars = new Chars('@') |
||||
expect(chars.isKeyword('foo')).toBe(true) |
||||
expect(chars.isKeyword('f@')).toBe(false) |
||||
}) |
||||
}) |
@ -1,35 +0,0 @@
@@ -1,35 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('node client pauseNotification', () => { |
||||
it('should work with notify & request', async () => { |
||||
nvim.pauseNotification() |
||||
nvim.call('setline', [1, 'foo'], true) |
||||
nvim.call('append', [1, ['bar']], true) |
||||
await nvim.resumeNotification(false, true) |
||||
await helper.wait(500) |
||||
let buffer = await nvim.buffer |
||||
let lines = await buffer.lines |
||||
expect(lines).toEqual(['foo', 'bar']) |
||||
nvim.pauseNotification() |
||||
nvim.call('eval', ['&buftype'], true) |
||||
nvim.call('bufnr', ['%'], true) |
||||
let res = await nvim.resumeNotification() |
||||
expect(res).toEqual([['', buffer.id], null]) |
||||
}) |
||||
}) |
@ -1,728 +0,0 @@
@@ -1,728 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Disposable } from 'vscode-jsonrpc' |
||||
import { CompletionItem, InsertTextFormat, Position, Range, TextEdit, CompletionList } from 'vscode-languageserver-types' |
||||
import completion from '../../completion' |
||||
import languages from '../../languages' |
||||
import { CompletionItemProvider } from '../../provider' |
||||
import snippetManager from '../../snippets/manager' |
||||
import sources from '../../sources' |
||||
import { CompleteOption, CompleteResult, ISource, SourceType } from '../../types' |
||||
import { disposeAll } from '../../util' |
||||
import workspace from '../../workspace' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let disposables: Disposable[] = [] |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
beforeEach(async () => { |
||||
disposables = [] |
||||
await helper.createDocument() |
||||
await nvim.call('feedkeys', [String.fromCharCode(27), 'in']) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
disposeAll(disposables) |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('completion events', () => { |
||||
|
||||
it('should load preferences', () => { |
||||
let minTriggerInputLength = completion.config.minTriggerInputLength |
||||
expect(minTriggerInputLength).toBe(1) |
||||
}) |
||||
|
||||
it('should reload preferences onChange', () => { |
||||
let { configurations } = workspace |
||||
configurations.updateUserConfig({ 'suggest.maxCompleteItemCount': 30 }) |
||||
let snippetIndicator = completion.config.maxItemCount |
||||
expect(snippetIndicator).toBe(30) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion start', () => { |
||||
|
||||
it('should deactivate on doComplete error', async () => { |
||||
let fn = (completion as any)._doComplete |
||||
; (completion as any)._doComplete = async () => { |
||||
throw new Error('fake') |
||||
} |
||||
let option: CompleteOption = await nvim.call('coc#util#get_complete_option') |
||||
await completion.startCompletion(option) |
||||
; (completion as any)._doComplete = fn |
||||
expect(completion.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should start completion', async () => { |
||||
await nvim.setLine('foo football') |
||||
await nvim.input('a') |
||||
await nvim.call('cursor', [1, 2]) |
||||
let option: CompleteOption = await nvim.call('coc#util#get_complete_option') |
||||
await completion.startCompletion(option) |
||||
expect(completion.isActivated).toBe(true) |
||||
}) |
||||
|
||||
it('should show slow source', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'slow', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] }) |
||||
}, 600) |
||||
}) |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion resumeCompletion', () => { |
||||
|
||||
it('should stop if no filtered items', async () => { |
||||
await nvim.setLine('foo ') |
||||
await helper.wait(50) |
||||
await nvim.input('Af') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
await nvim.input('d') |
||||
await helper.wait(60) |
||||
expect(completion.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should deactivate without filtered items', async () => { |
||||
await nvim.setLine('foo fbi ') |
||||
await nvim.input('Af') |
||||
await helper.waitPopup() |
||||
await nvim.input('c') |
||||
await helper.wait(100) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(0) |
||||
expect(completion.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should deactivate when insert space', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'empty', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => new Promise(resolve => { |
||||
resolve({ items: [{ word: 'foo bar' }] }) |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
sources.removeSource(source) |
||||
let items = await helper.items() |
||||
expect(items[0].word).toBe('foo bar') |
||||
await nvim.input(' ') |
||||
await helper.wait(60) |
||||
expect(completion.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should use resume input to filter', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'source', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (): Promise<CompleteResult> => new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] }) |
||||
}, 60) |
||||
}) |
||||
} |
||||
sources.addSource(source) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.wait(20) |
||||
await nvim.input('f') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(1) |
||||
expect(items[0].word).toBe('foo') |
||||
sources.removeSource(source) |
||||
}) |
||||
|
||||
it('should filter slow source', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'slow', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (): Promise<CompleteResult> => new Promise(resolve => { |
||||
setTimeout(() => { |
||||
resolve({ items: [{ word: 'foo' }, { word: 'bar' }] }) |
||||
}, 600) |
||||
}) |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.wait(60) |
||||
await nvim.input('f') |
||||
await helper.waitPopup() |
||||
await nvim.input('o') |
||||
await helper.wait(100) |
||||
expect(completion.isActivated).toBe(true) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(1) |
||||
expect(items[0].word).toBe('foo') |
||||
}) |
||||
|
||||
it('should complete inComplete source', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'inComplete', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => { |
||||
await helper.wait(30) |
||||
if (opt.input.length <= 1) { |
||||
return { isIncomplete: true, items: [{ word: 'foo' }, { word: opt.input }] } |
||||
} |
||||
return { isIncomplete: false, items: [{ word: 'foo' }, { word: opt.input }] } |
||||
} |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
await nvim.input('a') |
||||
await helper.wait(30) |
||||
await nvim.input('b') |
||||
await helper.wait(100) |
||||
}) |
||||
|
||||
it('should not complete inComplete source when isIncomplete is false', async () => { |
||||
await helper.createDocument() |
||||
let lastOption: CompleteOption |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'inComplete', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: async (opt: CompleteOption): Promise<CompleteResult> => { |
||||
lastOption = opt |
||||
await helper.wait(30) |
||||
if (opt.input.length <= 1) { |
||||
return { isIncomplete: true, items: [{ word: 'foobar' }] } |
||||
} |
||||
return { isIncomplete: false, items: [{ word: 'foobar' }] } |
||||
} |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await helper.edit() |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
await nvim.input('fo') |
||||
await helper.wait(100) |
||||
await nvim.input('b') |
||||
await helper.wait(200) |
||||
expect(completion.isActivated).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion InsertEnter', () => { |
||||
it('should trigger completion if triggerAfterInsertEnter is true', async () => { |
||||
await helper.createDocument() |
||||
await nvim.setLine('foo fo') |
||||
let config = workspace.getConfiguration('suggest') |
||||
config.update('triggerAfterInsertEnter', true) |
||||
await helper.wait(100) |
||||
let triggerAfterInsertEnter = completion.config.triggerAfterInsertEnter |
||||
expect(triggerAfterInsertEnter).toBe(true) |
||||
await nvim.input('A') |
||||
await helper.waitPopup() |
||||
expect(completion.isActivated).toBe(true) |
||||
config.update('triggerAfterInsertEnter', undefined) |
||||
}) |
||||
|
||||
it('should not trigger when input length too small', async () => { |
||||
let config = workspace.getConfiguration('suggest') |
||||
config.update('triggerAfterInsertEnter', true) |
||||
await helper.wait(100) |
||||
let triggerAfterInsertEnter = completion.config.triggerAfterInsertEnter |
||||
expect(triggerAfterInsertEnter).toBe(true) |
||||
await nvim.setLine('foo ') |
||||
await nvim.input('A') |
||||
await helper.wait(100) |
||||
expect(completion.isActivated).toBe(false) |
||||
config.update('triggerAfterInsertEnter', undefined) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion TextChangedP', () => { |
||||
it('should stop when input length below option input length', async () => { |
||||
await helper.edit() |
||||
await nvim.setLine('foo fbi ') |
||||
await nvim.input('Af') |
||||
await helper.waitPopup() |
||||
await nvim.input('<backspace>') |
||||
await helper.wait(100) |
||||
expect(completion.isActivated).toBe(false) |
||||
}) |
||||
|
||||
it('should fix cursor position with plain text on additionalTextEdits', async () => { |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (): Promise<CompletionItem[]> => [{ |
||||
label: 'foo', |
||||
filterText: 'foo', |
||||
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'a\nbar')] |
||||
}] |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider)) |
||||
await nvim.input('if') |
||||
await helper.waitPopup() |
||||
await helper.selectCompleteItem(0) |
||||
await helper.wait(200) |
||||
let line = await nvim.line |
||||
expect(line).toBe('barfoo') |
||||
let [, lnum, col] = await nvim.call('getcurpos') |
||||
expect(lnum).toBe(2) |
||||
expect(col).toBe(7) |
||||
}) |
||||
|
||||
it('should filter in complete request', async () => { |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (doc, pos, token, context): Promise<CompletionList> => { |
||||
let option = (context as any).option |
||||
if (context.triggerCharacter == '.') { |
||||
return { |
||||
isIncomplete: true, |
||||
items: [ |
||||
{ |
||||
label: 'foo' |
||||
}, { |
||||
label: 'bar' |
||||
} |
||||
] |
||||
} |
||||
} |
||||
if (option.input == 'f') { |
||||
await helper.wait(100) |
||||
if (token.isCancellationRequested) return |
||||
return { |
||||
isIncomplete: true, |
||||
items: [ |
||||
{ |
||||
label: 'foo' |
||||
} |
||||
] |
||||
} |
||||
} |
||||
if (option.input == 'fo') { |
||||
await helper.wait(100) |
||||
if (token.isCancellationRequested) return |
||||
return { |
||||
isIncomplete: false, |
||||
items: [ |
||||
{ |
||||
label: 'foo' |
||||
} |
||||
] |
||||
} |
||||
} |
||||
} |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider, ['.'])) |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
await nvim.input('f') |
||||
await helper.wait(60) |
||||
await nvim.input('o') |
||||
await helper.wait(300) |
||||
let res = await helper.getItems() |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
|
||||
it('should provide word when textEdit after startcol', async () => { |
||||
// some LS would send textEdit after first character,
|
||||
// need fix the word from newText
|
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (_, position): Promise<CompletionItem[]> => { |
||||
if (position.line != 0) return null |
||||
return [{ |
||||
label: 'bar', |
||||
filterText: 'ar', |
||||
textEdit: { |
||||
range: Range.create(0, 1, 0, 1), |
||||
newText: 'ar' |
||||
} |
||||
}] |
||||
} |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider)) |
||||
await nvim.input('ib') |
||||
await helper.waitPopup() |
||||
let context = await nvim.getVar('coc#_context') as any |
||||
expect(context.start).toBe(1) |
||||
expect(context.candidates[0].word).toBe('ar') |
||||
}) |
||||
|
||||
it('should adjust completion position by textEdit start position', async () => { |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (_document, _position, _token, context): Promise<CompletionItem[]> => { |
||||
if (!context.triggerCharacter) return |
||||
return [{ |
||||
label: 'foo', |
||||
textEdit: { |
||||
range: Range.create(0, 0, 0, 1), |
||||
newText: '?foo' |
||||
} |
||||
}] |
||||
} |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('fix', 'f', null, provider, ['?'])) |
||||
await nvim.input('i?') |
||||
await helper.waitPopup() |
||||
await nvim.eval('feedkeys("\\<C-n>", "in")') |
||||
await helper.wait(200) |
||||
let line = await nvim.line |
||||
expect(line).toBe('?foo') |
||||
}) |
||||
|
||||
it('should fix cursor position with snippet on additionalTextEdits', async () => { |
||||
await helper.createDocument() |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (): Promise<CompletionItem[]> => [{ |
||||
label: 'if', |
||||
insertTextFormat: InsertTextFormat.Snippet, |
||||
textEdit: { range: Range.create(0, 0, 0, 2), newText: 'if($1)' }, |
||||
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')], |
||||
preselect: true |
||||
}] |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider)) |
||||
await nvim.input('ii') |
||||
await helper.waitPopup() |
||||
let res = await helper.getItems() |
||||
let idx = res.findIndex(o => o.menu == '[edit]') |
||||
await helper.selectCompleteItem(idx) |
||||
await helper.wait(800) |
||||
let line = await nvim.line |
||||
expect(line).toBe('bar if()') |
||||
let [, lnum, col] = await nvim.call('getcurpos') |
||||
expect(lnum).toBe(1) |
||||
expect(col).toBe(8) |
||||
}) |
||||
|
||||
it('should fix cursor position with plain text snippet on additionalTextEdits', async () => { |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (): Promise<CompletionItem[]> => [{ |
||||
label: 'if', |
||||
insertTextFormat: InsertTextFormat.Snippet, |
||||
textEdit: { range: Range.create(0, 0, 0, 2), newText: 'do$0' }, |
||||
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')], |
||||
preselect: true |
||||
}] |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider)) |
||||
await nvim.input('iif') |
||||
await helper.waitPopup() |
||||
await helper.selectCompleteItem(0) |
||||
await helper.wait(200) |
||||
let line = await nvim.line |
||||
let [, lnum, col] = await nvim.call('getcurpos') |
||||
expect(line).toBe('bar do') |
||||
expect(lnum).toBe(1) |
||||
expect(col).toBe(7) |
||||
}) |
||||
|
||||
it('should fix cursor position with nested snippet on additionalTextEdits', async () => { |
||||
await helper.createDocument() |
||||
let res = await snippetManager.insertSnippet('func($1)$0') |
||||
expect(res).toBe(true) |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (): Promise<CompletionItem[]> => [{ |
||||
label: 'if', |
||||
insertTextFormat: InsertTextFormat.Snippet, |
||||
insertText: 'do$0', |
||||
additionalTextEdits: [TextEdit.insert(Position.create(0, 0), 'bar ')], |
||||
preselect: true |
||||
}] |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('edits', 'edit', null, provider)) |
||||
await nvim.input('if') |
||||
await helper.waitPopup() |
||||
await helper.selectCompleteItem(0) |
||||
await helper.wait(200) |
||||
let line = await nvim.line |
||||
let [, lnum, col] = await nvim.call('getcurpos') |
||||
expect(line).toBe('bar func(do)') |
||||
expect(lnum).toBe(1) |
||||
expect(col).toBe(12) |
||||
}) |
||||
|
||||
it('should fix input for snippet item', async () => { |
||||
let provider: CompletionItemProvider = { |
||||
provideCompletionItems: async (): Promise<CompletionItem[]> => [{ |
||||
label: 'foo', |
||||
filterText: 'foo', |
||||
insertText: '${1:foo}($2)', |
||||
insertTextFormat: InsertTextFormat.Snippet, |
||||
}] |
||||
} |
||||
disposables.push(languages.registerCompletionItemProvider('snippets-test', 'st', null, provider)) |
||||
await nvim.input('if') |
||||
await helper.waitPopup() |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(100) |
||||
let line = await nvim.line |
||||
expect(line).toBe('foo') |
||||
}) |
||||
|
||||
it('should filter on none keyword input', async () => { |
||||
let source: ISource = { |
||||
priority: 99, |
||||
enable: true, |
||||
name: 'temp', |
||||
sourceType: SourceType.Service, |
||||
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({ items: [{ word: 'foo#abc' }] }), |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await nvim.input('if') |
||||
await helper.waitPopup() |
||||
await nvim.input('#') |
||||
await helper.wait(100) |
||||
let items = await helper.getItems() |
||||
expect(items[0].word).toBe('foo#abc') |
||||
}) |
||||
|
||||
it('should use source-provided score', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'source', |
||||
sourceType: SourceType.Service, |
||||
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({ |
||||
items: [ |
||||
{ word: 'candidate_a', sourceScore: 0.1 }, |
||||
{ word: 'candidate_b', sourceScore: 10 }, |
||||
{ word: 'candidate_c' }, |
||||
] |
||||
}), |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await nvim.input('ocand') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items[0].word).toBe('candidate_b') |
||||
expect(items[1].word).toBe('candidate_c') |
||||
expect(items[2].word).toBe('candidate_a') |
||||
}) |
||||
|
||||
it('should do resolve for complete item', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'resolve', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (_opt: CompleteOption): Promise<CompleteResult> => Promise.resolve({ items: [{ word: 'foo' }] }), |
||||
onCompleteResolve: item => { |
||||
item.info = 'detail' |
||||
} |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('i.') |
||||
await helper.waitPopup() |
||||
await helper.wait(100) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(100) |
||||
// let items = completion.completeItems
|
||||
// expect(items[0].info).toBe('detail')
|
||||
sources.removeSource(source) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion done', () => { |
||||
it('should fix word on CompleteDone', async () => { |
||||
await nvim.setLine('fball football') |
||||
await nvim.input('i') |
||||
await nvim.call('cursor', [1, 2]) |
||||
let option: CompleteOption = await nvim.call('coc#util#get_complete_option') |
||||
await completion.startCompletion(option) |
||||
let items = await helper.items() |
||||
expect(items.length).toBe(1) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(30) |
||||
await nvim.call('coc#_select') |
||||
await helper.wait(100) |
||||
let line = await nvim.line |
||||
expect(line).toBe('football football') |
||||
}) |
||||
}) |
||||
|
||||
describe('completion option', () => { |
||||
it('should hide kind and menu when configured', async () => { |
||||
helper.updateConfiguration('suggest.disableKind', true) |
||||
helper.updateConfiguration('suggest.disableMenu', true) |
||||
await nvim.setLine('fball football') |
||||
await nvim.input('of') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items[0].kind).toBeUndefined() |
||||
expect(items[0].menu).toBeUndefined() |
||||
helper.updateConfiguration('suggest.disableKind', false) |
||||
helper.updateConfiguration('suggest.disableMenu', false) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion trigger', () => { |
||||
it('should trigger completion on type trigger character', async () => { |
||||
let source: ISource = { |
||||
priority: 1, |
||||
enable: true, |
||||
name: 'trigger', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (opt: CompleteOption): Promise<CompleteResult> => { |
||||
if (opt.triggerCharacter == '.') { |
||||
return Promise.resolve({ items: [{ word: 'bar' }] }) |
||||
} |
||||
return Promise.resolve({ items: [{ word: 'foo#bar' }] }) |
||||
} |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('i') |
||||
await helper.wait(30) |
||||
await nvim.input('.') |
||||
await helper.waitPopup() |
||||
let items = await helper.items() |
||||
expect(items.length).toBeGreaterThan(0) |
||||
sources.removeSource(source) |
||||
}) |
||||
|
||||
it('should not trigger if autoTrigger is none', async () => { |
||||
let config = workspace.getConfiguration('suggest') |
||||
config.update('autoTrigger', 'none') |
||||
let autoTrigger = completion.config.autoTrigger |
||||
expect(autoTrigger).toBe('none') |
||||
await nvim.setLine('foo fo') |
||||
await nvim.input('A') |
||||
await helper.wait(100) |
||||
expect(completion.isActivated).toBe(false) |
||||
config.update('autoTrigger', 'always') |
||||
}) |
||||
|
||||
it('should trigger complete on trigger patterns match', async () => { |
||||
let source: ISource = { |
||||
priority: 99, |
||||
enable: true, |
||||
name: 'temp', |
||||
triggerPatterns: [/EM/], |
||||
sourceType: SourceType.Service, |
||||
doComplete: (opt: CompleteOption): Promise<CompleteResult> => { |
||||
if (!opt.input.startsWith('EM')) return null |
||||
return Promise.resolve({ |
||||
items: [ |
||||
{ word: 'foo', filterText: 'EMfoo' }, |
||||
{ word: 'bar', filterText: 'EMbar' } |
||||
] |
||||
}) |
||||
}, |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await nvim.input('i') |
||||
await nvim.input('EM') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBe(2) |
||||
}) |
||||
|
||||
it('should trigger complete when pumvisible and triggerPatterns match', async () => { |
||||
await nvim.setLine('EnumMember') |
||||
let source: ISource = { |
||||
priority: 99, |
||||
enable: true, |
||||
name: 'temp', |
||||
triggerPatterns: [/EM/], |
||||
sourceType: SourceType.Service, |
||||
doComplete: (opt: CompleteOption): Promise<CompleteResult> => { |
||||
if (!opt.input.startsWith('EM')) return null |
||||
return Promise.resolve({ |
||||
items: [ |
||||
{ word: 'a', filterText: 'EMa' }, |
||||
{ word: 'b', filterText: 'EMb' } |
||||
] |
||||
}) |
||||
}, |
||||
} |
||||
disposables.push(sources.addSource(source)) |
||||
await nvim.input('o') |
||||
await helper.wait(10) |
||||
await nvim.input('E') |
||||
await helper.wait(30) |
||||
await nvim.input('M') |
||||
await helper.waitPopup() |
||||
let items = await helper.getItems() |
||||
expect(items.length).toBeGreaterThan(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('completion TextChangedI', () => { |
||||
it('should respect commitCharacter on TextChangedI', async () => { |
||||
let source: ISource = { |
||||
priority: 0, |
||||
enable: true, |
||||
name: 'slow', |
||||
sourceType: SourceType.Service, |
||||
triggerCharacters: ['.'], |
||||
doComplete: (opt: CompleteOption): Promise<CompleteResult> => { |
||||
if (opt.triggerCharacter == '.') { |
||||
return Promise.resolve({ items: [{ word: 'bar' }] }) |
||||
} |
||||
return Promise.resolve({ items: [{ word: 'foo' }] }) |
||||
}, |
||||
shouldCommit: (_item, character) => character == '.' |
||||
} |
||||
sources.addSource(source) |
||||
await nvim.input('if') |
||||
await helper.pumvisible() |
||||
await helper.wait(100) |
||||
await nvim.input('.') |
||||
await helper.wait(100) |
||||
sources.removeSource(source) |
||||
}) |
||||
|
||||
it('should cancel completion with same pretext', async () => { |
||||
await nvim.setLine('foo') |
||||
await nvim.input('of') |
||||
await helper.pumvisible() |
||||
await helper.wait(30) |
||||
await nvim.call('coc#_cancel', []) |
||||
let line = await nvim.line |
||||
let visible = await nvim.call('pumvisible') |
||||
expect(line).toBe('f') |
||||
expect(visible).toBe(0) |
||||
}) |
||||
}) |
@ -1,247 +0,0 @@
@@ -1,247 +0,0 @@
|
||||
import fs from 'fs' |
||||
import os from 'os' |
||||
import { ParseError } from 'jsonc-parser' |
||||
import path from 'path' |
||||
import Configurations from '../../configuration' |
||||
import { convertErrors, getChangedKeys, getConfigurationValue, getKeys, parseConfiguration } from '../../configuration/util' |
||||
import { IConfigurationModel } from '../../types' |
||||
import { URI } from 'vscode-uri' |
||||
import { v1 as uuidv1 } from 'uuid' |
||||
import { CONFIG_FILE_NAME } from '../../util' |
||||
|
||||
const config = fs.readFileSync(path.join(__dirname, './settings.json'), 'utf8') |
||||
const workspaceConfigFile = path.resolve(__dirname, `../sample/.vim/${CONFIG_FILE_NAME}`) |
||||
|
||||
function getConfigurationModel(): IConfigurationModel { |
||||
let [, contents] = parseConfiguration(config) |
||||
return { contents } |
||||
} |
||||
|
||||
function createConfigurations(): Configurations { |
||||
let userConfigFile = path.join(__dirname, './settings.json') |
||||
return new Configurations(userConfigFile) |
||||
} |
||||
|
||||
describe('Configurations', () => { |
||||
it('should convert errors', () => { |
||||
let errors: ParseError[] = [] |
||||
for (let i = 0; i < 17; i++) { |
||||
errors.push({ |
||||
error: i, |
||||
offset: 0, |
||||
length: 10 |
||||
}) |
||||
} |
||||
let res = convertErrors('file:///1', 'abc', errors) |
||||
expect(res.length).toBe(17) |
||||
}) |
||||
|
||||
it('should get all keys', () => { |
||||
let res = getKeys({ |
||||
foo: { |
||||
bar: 1, |
||||
from: { |
||||
to: 2 |
||||
} |
||||
}, |
||||
bar: [1, 2] |
||||
}) |
||||
expect(res).toEqual(['foo', 'foo.bar', 'foo.from', 'foo.from.to', 'bar']) |
||||
}) |
||||
|
||||
it('should get configuration value', () => { |
||||
let root = { |
||||
foo: { |
||||
bar: 1, |
||||
from: { |
||||
to: 2 |
||||
} |
||||
}, |
||||
bar: [1, 2] |
||||
} |
||||
let res = getConfigurationValue(root, 'foo.from.to', 1) |
||||
expect(res).toBe(2) |
||||
res = getConfigurationValue(root, 'foo.from', 1) |
||||
expect(res).toEqual({ to: 2 }) |
||||
}) |
||||
|
||||
it('should add folder as workspace configuration', () => { |
||||
let configurations = createConfigurations() |
||||
configurations.onDidChange(e => { |
||||
let affects = e.affectsConfiguration('coc') |
||||
expect(affects).toBe(true) |
||||
}) |
||||
configurations.addFolderFile(workspaceConfigFile) |
||||
let o = configurations.configuration.workspace.contents |
||||
expect(o.coc.preferences.rootPath).toBe('./src') |
||||
configurations.dispose() |
||||
}) |
||||
|
||||
it('should get changed keys #1', () => { |
||||
let res = getChangedKeys({ y: 2 }, { x: 1 }) |
||||
expect(res).toEqual(['x', 'y']) |
||||
}) |
||||
|
||||
it('should get changed keys #2', () => { |
||||
let res = getChangedKeys({ x: 1, c: { d: 4 } }, { x: 1, b: { x: 5 } }) |
||||
expect(res).toEqual(['b', 'b.x', 'c', 'c.d']) |
||||
}) |
||||
|
||||
it('should load default configurations', () => { |
||||
let conf = new Configurations() |
||||
expect(conf.defaults.contents.coc).toBeDefined() |
||||
let c = conf.getConfiguration('languageserver') |
||||
expect(c).toEqual({}) |
||||
conf.dispose() |
||||
}) |
||||
|
||||
it('should parse configurations', () => { |
||||
let { contents } = getConfigurationModel() |
||||
expect(contents.foo.bar).toBe(1) |
||||
expect(contents.bar.foo).toBe(2) |
||||
expect(contents.schema).toEqual({ 'https://example.com': '*.yaml' }) |
||||
}) |
||||
|
||||
it('should update user config #1', () => { |
||||
let conf = new Configurations() |
||||
let fn = jest.fn() |
||||
conf.onDidChange(e => { |
||||
expect(e.affectsConfiguration('x')).toBe(true) |
||||
fn() |
||||
}) |
||||
conf.updateUserConfig({ x: 1 }) |
||||
let config = conf.configuration.user |
||||
expect(config.contents).toEqual({ x: 1 }) |
||||
expect(fn).toBeCalled() |
||||
}) |
||||
|
||||
it('should update user config #2', () => { |
||||
let conf = new Configurations() |
||||
conf.updateUserConfig({ x: 1 }) |
||||
conf.updateUserConfig({ x: undefined }) |
||||
let config = conf.configuration.user |
||||
expect(config.contents).toEqual({}) |
||||
}) |
||||
|
||||
it('should update workspace config', () => { |
||||
let conf = new Configurations() |
||||
conf.updateUserConfig({ foo: { bar: 1 } }) |
||||
let curr = conf.getConfiguration('foo') |
||||
curr.update('bar', 2, false) |
||||
curr = conf.getConfiguration('foo') |
||||
let n = curr.get<number>('bar') |
||||
expect(n).toBe(2) |
||||
}) |
||||
|
||||
it('should handle errors', () => { |
||||
let tmpFile = path.join(os.tmpdir(), uuidv1()) |
||||
fs.writeFileSync(tmpFile, '{"x":', 'utf8') |
||||
let conf = new Configurations(tmpFile) |
||||
let errors = conf.errorItems |
||||
expect(errors.length > 1).toBe(true) |
||||
conf.dispose() |
||||
}) |
||||
|
||||
it('should change to new folder configuration', () => { |
||||
let conf = new Configurations() |
||||
conf.addFolderFile(workspaceConfigFile) |
||||
let configFile = path.join(__dirname, './settings.json') |
||||
conf.addFolderFile(configFile) |
||||
let file = path.resolve(__dirname, '../sample/tmp.js') |
||||
let fn = jest.fn() |
||||
conf.onDidChange(fn) |
||||
conf.setFolderConfiguration(URI.file(file).toString()) |
||||
let { contents } = conf.workspace |
||||
expect(contents.foo).toBeUndefined() |
||||
expect(fn).toBeCalled() |
||||
conf.dispose() |
||||
}) |
||||
|
||||
it('should get nested property', () => { |
||||
let config = createConfigurations() |
||||
let conf = config.getConfiguration('servers.c') |
||||
let res = conf.get<string>('trace.server', '') |
||||
expect(res).toBe('verbose') |
||||
config.dispose() |
||||
}) |
||||
|
||||
it('should get user and workspace configuration', () => { |
||||
let userConfigFile = path.join(__dirname, './settings.json') |
||||
let configurations = new Configurations(userConfigFile) |
||||
let data = configurations.configuration.toData() |
||||
expect(data.user).toBeDefined() |
||||
expect(data.workspace).toBeDefined() |
||||
expect(data.defaults).toBeDefined() |
||||
let value = configurations.configuration.getValue() |
||||
expect(value.foo).toBeDefined() |
||||
expect(value.foo.bar).toBe(1) |
||||
configurations.dispose() |
||||
}) |
||||
|
||||
it('should override with new value', () => { |
||||
let configurations = createConfigurations() |
||||
configurations.configuration.defaults.setValue('foo', 1) |
||||
let { contents } = configurations.defaults |
||||
expect(contents.foo).toBe(1) |
||||
configurations.dispose() |
||||
}) |
||||
|
||||
it('should extends defaults', () => { |
||||
let configurations = createConfigurations() |
||||
configurations.extendsDefaults({ 'a.b': 1 }) |
||||
configurations.extendsDefaults({ 'a.b': 2 }) |
||||
let o = configurations.defaults.contents |
||||
expect(o.a.b).toBe(2) |
||||
configurations.dispose() |
||||
}) |
||||
|
||||
it('should update configuration', async () => { |
||||
let configurations = createConfigurations() |
||||
configurations.addFolderFile(workspaceConfigFile) |
||||
let fn = jest.fn() |
||||
configurations.onDidChange(e => { |
||||
expect(e.affectsConfiguration('foo')).toBe(true) |
||||
expect(e.affectsConfiguration('foo.bar')).toBe(true) |
||||
expect(e.affectsConfiguration('foo.bar', 'file://tmp/foo.js')).toBe(false) |
||||
fn() |
||||
}) |
||||
let config = configurations.getConfiguration('foo') |
||||
let o = config.get<number>('bar') |
||||
expect(o).toBe(1) |
||||
config.update('bar', 6) |
||||
config = configurations.getConfiguration('foo') |
||||
expect(config.get<number>('bar')).toBe(6) |
||||
expect(fn).toBeCalledTimes(1) |
||||
configurations.dispose() |
||||
}) |
||||
|
||||
it('should remove configuration', async () => { |
||||
let configurations = createConfigurations() |
||||
configurations.addFolderFile(workspaceConfigFile) |
||||
let fn = jest.fn() |
||||
configurations.onDidChange(e => { |
||||
expect(e.affectsConfiguration('foo')).toBe(true) |
||||
expect(e.affectsConfiguration('foo.bar')).toBe(true) |
||||
fn() |
||||
}) |
||||
let config = configurations.getConfiguration('foo') |
||||
let o = config.get<number>('bar') |
||||
expect(o).toBe(1) |
||||
config.update('bar', null, true) |
||||
config = configurations.getConfiguration('foo') |
||||
expect(config.get<any>('bar')).toBeUndefined() |
||||
expect(fn).toBeCalledTimes(1) |
||||
configurations.dispose() |
||||
}) |
||||
}) |
||||
|
||||
describe('parse configuration', () => { |
||||
it('should only split top level dot keys', () => { |
||||
let o = { 'x.y': 'foo' } |
||||
let [, contents] = parseConfiguration(JSON.stringify(o)) |
||||
expect(contents).toEqual({ x: { y: 'foo' } }) |
||||
let schema = { 'my.schema': { 'foo.bar': 1 } } |
||||
let [, obj] = parseConfiguration(JSON.stringify(schema)) |
||||
expect(obj).toEqual({ my: { schema: { 'foo.bar': 1 } } }) |
||||
}) |
||||
}) |
@ -1,421 +0,0 @@
@@ -1,421 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { Range } from 'vscode-languageserver-types' |
||||
import Cursors from '../../cursors' |
||||
import Document from '../../model/document' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
let cursors: Cursors |
||||
let ns: number |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
ns = await nvim.createNamespace('coc-cursors') |
||||
cursors = new Cursors(nvim) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
nvim.pauseNotification() |
||||
cursors.reset() |
||||
await nvim.resumeNotification() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
async function rangeCount(): Promise<number> { |
||||
let buf = await nvim.buffer |
||||
let markers = await helper.getMarkers(buf.id, ns) |
||||
return markers.length |
||||
} |
||||
|
||||
describe('cursors#select', () => { |
||||
|
||||
it('should select by position', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['a', 'b']]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await helper.wait(100) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
await helper.wait(30) |
||||
let n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
await nvim.setOption('virtualedit', 'onemore') |
||||
await nvim.call('cursor', [2, 2]) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
n = await rangeCount() |
||||
expect(n).toBe(2) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
}) |
||||
|
||||
it('should select by word', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['foo', 'bar']]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await cursors.select(doc.bufnr, 'word', 'n') |
||||
let n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
await nvim.call('cursor', [2, 2]) |
||||
await cursors.select(doc.bufnr, 'word', 'n') |
||||
n = await rangeCount() |
||||
expect(n).toBe(2) |
||||
await cursors.select(doc.bufnr, 'word', 'n') |
||||
n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
}) |
||||
|
||||
it('should select last character', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setOption('virtualedit', 'onemore') |
||||
await nvim.call('setline', [1, ['}', '{']]) |
||||
await nvim.call('cursor', [1, 2]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await cursors.select(doc.bufnr, 'word', 'n') |
||||
let n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await cursors.select(doc.bufnr, 'word', 'n') |
||||
n = await rangeCount() |
||||
expect(n).toBe(2) |
||||
}) |
||||
|
||||
it('should select by visual range', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['"foo"', '"bar"']]) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await nvim.command('normal! vE') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await cursors.select(doc.bufnr, 'range', 'v') |
||||
let n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await nvim.command('normal! vE') |
||||
await cursors.select(doc.bufnr, 'range', 'v') |
||||
n = await rangeCount() |
||||
expect(n).toBe(2) |
||||
await cursors.select(doc.bufnr, 'range', 'v') |
||||
n = await rangeCount() |
||||
expect(n).toBe(1) |
||||
}) |
||||
|
||||
it('should select by operator', async () => { |
||||
await nvim.command('nmap x <Plug>(coc-cursors-operator)') |
||||
await helper.createDocument() |
||||
await nvim.call('setline', [1, ['"short"', '"long"']]) |
||||
await nvim.call('cursor', [1, 2]) |
||||
await nvim.input('xa"') |
||||
await helper.wait(30) |
||||
await nvim.call('cursor', [2, 2]) |
||||
await nvim.input('xa"') |
||||
await helper.wait(30) |
||||
await nvim.command('nunmap x') |
||||
}) |
||||
}) |
||||
|
||||
describe('cursors#addRanges', () => { |
||||
it('should add ranges', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['foo foo foo', 'bar bar']]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
let ranges = [ |
||||
Range.create(0, 0, 0, 3), |
||||
Range.create(0, 4, 0, 7), |
||||
Range.create(0, 8, 0, 11), |
||||
Range.create(1, 0, 1, 3), |
||||
Range.create(1, 4, 1, 7) |
||||
] |
||||
await cursors.addRanges(ranges) |
||||
let n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
}) |
||||
}) |
||||
|
||||
describe('cursors#onchange', () => { |
||||
|
||||
async function setup(): Promise<Document> { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['foo foo foo', 'bar bar']]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
let ranges = [ |
||||
Range.create(0, 0, 0, 3), |
||||
Range.create(0, 4, 0, 7), |
||||
Range.create(0, 8, 0, 11), |
||||
Range.create(1, 0, 1, 3), |
||||
Range.create(1, 4, 1, 7) |
||||
] |
||||
await cursors.addRanges(ranges) |
||||
await nvim.call('cursor', [1, 1]) |
||||
return doc |
||||
} |
||||
|
||||
it('should ignore change after last range', async () => { |
||||
let doc = await setup() |
||||
await doc.buffer.append(['append']) |
||||
doc.forceSync() |
||||
await helper.wait(50) |
||||
let n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
}) |
||||
|
||||
it('should adjust ranges on change before first line', async () => { |
||||
let doc = await setup() |
||||
await doc.buffer.setLines(['prepend'], { start: 0, end: 0, strictIndexing: false }) |
||||
doc.forceSync() |
||||
await helper.wait(200) |
||||
let n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await nvim.input('ia') |
||||
await helper.wait(100) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['prepend', 'afoo afoo afoo', 'abar abar']) |
||||
}) |
||||
|
||||
it('should work when change made to unrelated line', async () => { |
||||
let doc = await setup() |
||||
await doc.buffer.setLines(['prepend'], { start: 0, end: 0, strictIndexing: false }) |
||||
doc.forceSync() |
||||
await helper.wait(200) |
||||
let n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await nvim.input('ia') |
||||
await helper.wait(200) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await nvim.input('a') |
||||
await helper.wait(100) |
||||
await doc.synchronize() |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['aprepend', 'afoo afoo afoo', 'abar abar']) |
||||
}) |
||||
|
||||
it('should add text before', async () => { |
||||
let doc = await setup() |
||||
await nvim.input('iabc') |
||||
await helper.wait(30) |
||||
await doc.synchronize() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['abcfoo abcfoo abcfoo', 'abcbar abcbar']) |
||||
}) |
||||
|
||||
it('should add text after', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 4]) |
||||
await nvim.input('iabc') |
||||
await helper.wait(30) |
||||
await doc.synchronize() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['fooabc fooabc fooabc', 'barabc barabc']) |
||||
}) |
||||
|
||||
it('should add text around', async () => { |
||||
let doc = await setup() |
||||
await nvim.setLine('"foo" foo foo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['"foo" "foo" "foo"', '"bar" "bar"']) |
||||
}) |
||||
|
||||
it('should remove text before', async () => { |
||||
let doc = await setup() |
||||
await nvim.command('normal! x') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['oo oo oo', 'ar ar']) |
||||
}) |
||||
|
||||
it('should remove text middle', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [2, 2]) |
||||
await nvim.command('normal! x') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['fo fo fo', 'br br']) |
||||
}) |
||||
|
||||
it('should remove text after', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 3]) |
||||
await nvim.command('normal! x') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['fo fo fo', 'ba ba']) |
||||
}) |
||||
|
||||
it('should remove text around', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['"foo" "bar"']]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
let ranges = [ |
||||
Range.create(0, 0, 0, 5), |
||||
Range.create(0, 6, 0, 11) |
||||
] |
||||
await cursors.addRanges(ranges) |
||||
await nvim.call('cursor', [1, 2]) |
||||
await nvim.setLine('foo "bar"') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['foo bar']) |
||||
}) |
||||
|
||||
it('should replace text before', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 1]) |
||||
await nvim.command('normal! ra') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['aoo aoo aoo', 'aar aar']) |
||||
}) |
||||
|
||||
it('should replace text after', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 3]) |
||||
await nvim.command('normal! ra') |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['foa foa foa', 'baa baa']) |
||||
}) |
||||
|
||||
it('should replace text middle', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 2]) |
||||
await nvim.input('sab') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await helper.wait(100) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['fabo fabo fabo', 'babr babr']) |
||||
}) |
||||
|
||||
it('should adjust undo & redo on add & remove', async () => { |
||||
let doc = await setup() |
||||
await nvim.call('cursor', [1, 4]) |
||||
await nvim.input('iabc') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
let n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
await helper.wait(30) |
||||
await nvim.command('undo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
n = await rangeCount() |
||||
expect(n).toBe(5) |
||||
await helper.wait(30) |
||||
await nvim.command('redo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
expect(await rangeCount()).toBe(5) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['fooabc fooabc fooabc', 'barabc barabc']) |
||||
}) |
||||
|
||||
it('should adjust undo & redo on change around', async () => { |
||||
let doc = await setup() |
||||
await nvim.setLine('"foo" foo foo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
expect(await rangeCount()).toBe(5) |
||||
await helper.wait(30) |
||||
await nvim.command('undo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
expect(await rangeCount()).toBe(5) |
||||
await helper.wait(30) |
||||
await nvim.command('redo') |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
expect(await rangeCount()).toBe(5) |
||||
let lines = await nvim.call('getline', [1, '$']) |
||||
expect(lines).toEqual(['"foo" "foo" "foo"', '"bar" "bar"']) |
||||
}) |
||||
}) |
||||
|
||||
describe('cursors#keymaps', () => { |
||||
async function setup(): Promise<void> { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
await helper.wait(30) |
||||
doc.forceSync() |
||||
await nvim.call('cursor', [1, 1]) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
await helper.wait(30) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
await helper.wait(30) |
||||
await nvim.call('cursor', [3, 1]) |
||||
await cursors.select(doc.bufnr, 'position', 'n') |
||||
} |
||||
|
||||
async function hasKeymap(key): Promise<boolean> { |
||||
let buf = await nvim.buffer |
||||
let keymaps = await buf.getKeymap('n') as any |
||||
return keymaps.find(o => o.lhs == key) != null |
||||
} |
||||
|
||||
it('should setup cancel keymap', async () => { |
||||
await setup() |
||||
let count = await rangeCount() |
||||
expect(count).toBe(3) |
||||
await nvim.input('<esc>') |
||||
await helper.wait(100) |
||||
count = await rangeCount() |
||||
expect(count).toBe(0) |
||||
let has = await hasKeymap('<Esc>') |
||||
expect(has).toBe(false) |
||||
}) |
||||
|
||||
it('should setup next key', async () => { |
||||
await setup() |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(50) |
||||
let cursor = await nvim.call('coc#cursor#position') |
||||
expect(cursor).toEqual([0, 0]) |
||||
await nvim.input('<C-n>') |
||||
await helper.wait(50) |
||||
cursor = await nvim.call('coc#cursor#position') |
||||
expect(cursor).toEqual([1, 0]) |
||||
}) |
||||
|
||||
it('should setup previous key', async () => { |
||||
await setup() |
||||
await nvim.input('<C-p>') |
||||
await helper.wait(50) |
||||
let cursor = await nvim.call('coc#cursor#position') |
||||
expect(cursor).toEqual([1, 0]) |
||||
await nvim.input('<C-p>') |
||||
await helper.wait(50) |
||||
cursor = await nvim.call('coc#cursor#position') |
||||
expect(cursor).toEqual([0, 0]) |
||||
}) |
||||
}) |
@ -1,60 +0,0 @@
@@ -1,60 +0,0 @@
|
||||
import DB from '../../model/db' |
||||
import path from 'path' |
||||
|
||||
let db: DB |
||||
beforeAll(async () => { |
||||
db = new DB(path.join(__dirname, 'db.json')) |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
db.destroy() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
db.clear() |
||||
}) |
||||
|
||||
describe('DB', () => { |
||||
|
||||
test('db.exists()', async () => { |
||||
let exists = db.exists('a.b') |
||||
expect(exists).toBe(false) |
||||
db.push('a.b', { foo: 1 }) |
||||
exists = db.exists('a.b.foo') |
||||
expect(exists).toBe(true) |
||||
}) |
||||
|
||||
test('db.fetch()', async () => { |
||||
let res = await db.fetch('x') |
||||
expect(res).toBeUndefined() |
||||
db.push('x', 1) |
||||
res = await db.fetch('x') |
||||
expect(res).toBe(1) |
||||
db.push('x', { foo: 1 }) |
||||
res = await db.fetch('x') |
||||
expect(res).toEqual({ foo: 1 }) |
||||
}) |
||||
|
||||
test('db.delete()', async () => { |
||||
db.push('foo.bar', 1) |
||||
db.delete('foo.bar') |
||||
let exists = db.exists('foo.bar') |
||||
expect(exists).toBe(false) |
||||
}) |
||||
|
||||
test('db.push()', async () => { |
||||
db.push('foo.x', 1) |
||||
db.push('foo.y', '2') |
||||
db.push('foo.z', true) |
||||
db.push('foo.n', null) |
||||
db.push('foo.o', { x: 1 }) |
||||
let res = db.fetch('foo') |
||||
expect(res).toEqual({ |
||||
x: 1, |
||||
y: '2', |
||||
z: true, |
||||
n: null, |
||||
o: { x: 1 } |
||||
}) |
||||
}) |
||||
}) |
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
import * as decorator from '../../util/decorator' |
||||
|
||||
class CallTest { |
||||
public count = 0 |
||||
|
||||
@decorator.memorize |
||||
public async memorized(): Promise<number> { return ++this.count } |
||||
} |
||||
|
||||
describe('memorize', () => { |
||||
test('overlapping', async () => { |
||||
const c = new CallTest() |
||||
|
||||
const first = c.memorized() |
||||
const second = c.memorized() |
||||
expect(await first).toBe(1) |
||||
expect(await second).toBe(2) |
||||
}) |
||||
test('nonoverlapping', async () => { |
||||
const c = new CallTest() |
||||
|
||||
const first = c.memorized() |
||||
expect(await first).toBe(1) |
||||
const second = c.memorized() |
||||
expect(await second).toBe(1) |
||||
}) |
||||
}) |
@ -1,250 +0,0 @@
@@ -1,250 +0,0 @@
|
||||
import helper from '../helper' |
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import { DiagnosticBuffer } from '../../diagnostic/buffer' |
||||
import { Range, DiagnosticSeverity, Diagnostic, DiagnosticTag, Position } from 'vscode-languageserver-types' |
||||
import workspace from '../../workspace' |
||||
|
||||
let nvim: Neovim |
||||
const config: any = { |
||||
autoRefresh: true, |
||||
checkCurrentLine: false, |
||||
locationlistUpdate: true, |
||||
enableSign: true, |
||||
enableHighlightLineNumber: true, |
||||
enableMessage: 'always', |
||||
messageTarget: 'echo', |
||||
messageDelay: 250, |
||||
refreshOnInsertMode: false, |
||||
virtualTextSrcId: 99, |
||||
virtualText: false, |
||||
virtualTextCurrentLineOnly: true, |
||||
virtualTextPrefix: " ", |
||||
virtualTextLines: 3, |
||||
virtualTextLineSeparator: " \\ ", |
||||
displayByAle: false, |
||||
level: DiagnosticSeverity.Hint, |
||||
signPriority: 11, |
||||
errorSign: '>>', |
||||
warningSign: '>>', |
||||
infoSign: '>>', |
||||
hintSign: '>>', |
||||
filetypeMap: { |
||||
default: '' |
||||
}, |
||||
} |
||||
|
||||
async function createDiagnosticBuffer(): Promise<DiagnosticBuffer> { |
||||
let doc = await helper.createDocument() |
||||
return new DiagnosticBuffer(nvim, doc.bufnr, doc.uri, config, () => { |
||||
// noop
|
||||
}) |
||||
} |
||||
|
||||
function createDiagnostic(msg: string, range?: Range, severity?: DiagnosticSeverity, tags?: DiagnosticTag[]): Diagnostic & { collection: string } { |
||||
range = range ? range : Range.create(0, 0, 0, 1) |
||||
return Object.assign(Diagnostic.create(range, msg, severity || DiagnosticSeverity.Error, 999, 'test'), { collection: 'test', tags }) |
||||
} |
||||
|
||||
let ns: number |
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
ns = await nvim.createNamespace('coc-diagnostic') |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('diagnostic buffer', () => { |
||||
describe('refresh()', () => { |
||||
it('should add signs', async () => { |
||||
let diagnostics = [createDiagnostic('foo'), createDiagnostic('bar')] |
||||
let buf = await createDiagnosticBuffer() |
||||
buf.addSigns('a', diagnostics) |
||||
await helper.wait(30) |
||||
let res = await nvim.call('sign_getplaced', [buf.bufnr, { group: 'CocDiagnostica' }]) |
||||
let signs = res[0].signs |
||||
expect(signs).toBeDefined() |
||||
expect(signs[0].name).toBe('CocError') |
||||
}) |
||||
|
||||
it('should set diagnostic info', async () => { |
||||
let r = Range.create(0, 1, 0, 2) |
||||
let diagnostics = [ |
||||
createDiagnostic('foo', r, DiagnosticSeverity.Error), |
||||
createDiagnostic('bar', r, DiagnosticSeverity.Warning), |
||||
createDiagnostic('foo', r, DiagnosticSeverity.Hint), |
||||
createDiagnostic('bar', r, DiagnosticSeverity.Information) |
||||
] |
||||
let buf = await createDiagnosticBuffer() |
||||
await buf.refresh({ '': diagnostics }) |
||||
let buffer = await nvim.buffer |
||||
let res = await buffer.getVar('coc_diagnostic_info') |
||||
expect(res).toEqual({ |
||||
lnums: [1, 1, 1, 1], |
||||
information: 1, |
||||
hint: 1, |
||||
warning: 1, |
||||
error: 1 |
||||
}) |
||||
}) |
||||
|
||||
it('should add highlight', async () => { |
||||
let buf = await createDiagnosticBuffer() |
||||
let doc = workspace.getDocument(buf.bufnr) |
||||
await nvim.setLine('abc') |
||||
await doc.patchChange(true) |
||||
nvim.pauseNotification() |
||||
buf.updateHighlights('', [ |
||||
createDiagnostic('foo', Range.create(0, 0, 0, 1), DiagnosticSeverity.Error), |
||||
createDiagnostic('bar', Range.create(0, 0, 0, 1), DiagnosticSeverity.Warning) |
||||
]) |
||||
await nvim.resumeNotification() |
||||
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any |
||||
expect(res).toEqual([ |
||||
[ |
||||
1, |
||||
0, |
||||
0, |
||||
{ |
||||
hl_group: 'CocWarningHighlight', |
||||
priority: 4096, |
||||
end_col: 1, |
||||
end_row: 0 |
||||
} |
||||
], |
||||
[ |
||||
2, |
||||
0, |
||||
0, |
||||
{ |
||||
hl_group: 'CocErrorHighlight', |
||||
priority: 4096, |
||||
end_col: 1, |
||||
end_row: 0 |
||||
} |
||||
] |
||||
]) |
||||
nvim.pauseNotification() |
||||
buf.updateHighlights('', []) |
||||
await nvim.resumeNotification() |
||||
res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any[] |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
|
||||
it('should add deprecated highlight', async () => { |
||||
let diagnostic = createDiagnostic('foo', Range.create(0, 0, 0, 1), DiagnosticSeverity.Information, [DiagnosticTag.Deprecated]) |
||||
let buf = await createDiagnosticBuffer() |
||||
let doc = workspace.getDocument(buf.bufnr) |
||||
await nvim.setLine('foo') |
||||
await doc.patchChange(true) |
||||
nvim.pauseNotification() |
||||
buf.updateHighlights('', [diagnostic]) |
||||
await nvim.resumeNotification() |
||||
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, {}]) as [number, number, number][] |
||||
expect(res.length).toBe(1) |
||||
}) |
||||
}) |
||||
|
||||
describe('showVirtualText()', () => { |
||||
beforeEach(async () => { |
||||
config.virtualText = true |
||||
config.virtualTextSrcId = await nvim.createNamespace('diagnostics-virtualText') |
||||
}) |
||||
afterEach(() => { |
||||
config.virtualText = false |
||||
config.virtualTextCurrentLineOnly = true |
||||
}) |
||||
|
||||
it('should show virtual text on current line', async () => { |
||||
let diagnostic = createDiagnostic('foo') |
||||
let buf = await createDiagnosticBuffer() |
||||
let diagnostics = [diagnostic] |
||||
await buf.refresh({ '': diagnostics }) |
||||
let ns = config.virtualTextSrcId |
||||
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any |
||||
expect(res.length).toBe(1) |
||||
let texts = res[0][3].virt_text |
||||
expect(texts[0]).toEqual([' foo', 'CocErrorVirtualText']) |
||||
}) |
||||
|
||||
it('should virtual text on all lines', async () => { |
||||
config.virtualTextCurrentLineOnly = false |
||||
let buf = await createDiagnosticBuffer() |
||||
let diagnostics = [ |
||||
createDiagnostic('foo', Range.create(0, 0, 0, 1)), |
||||
createDiagnostic('bar', Range.create(1, 0, 1, 1)), |
||||
] |
||||
await buf.refresh({ '': diagnostics }) |
||||
let ns = config.virtualTextSrcId |
||||
let res = await nvim.call('nvim_buf_get_extmarks', [buf.bufnr, ns, 0, -1, { details: true }]) as any |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('updateLocationList()', () => { |
||||
beforeEach(async () => { |
||||
config.locationlistUpdate = true |
||||
}) |
||||
afterEach(() => { |
||||
config.locationlistUpdate = false |
||||
}) |
||||
|
||||
it('should update location list', async () => { |
||||
let buf = await createDiagnosticBuffer() |
||||
await nvim.call('setloclist', [0, [], 'r', { title: 'Diagnostics of coc', items: [] }]) |
||||
await buf.refresh({ |
||||
a: [createDiagnostic('foo')] |
||||
}) |
||||
let res = await nvim.eval(`getloclist(bufwinid(${buf.bufnr}))`) as any[] |
||||
expect(res.length).toBe(1) |
||||
expect(res[0].text).toBe('[test 999] foo [E]') |
||||
}) |
||||
}) |
||||
|
||||
describe('clear()', () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
beforeEach(() => { |
||||
config.update('virtualText', true) |
||||
}) |
||||
afterEach(() => { |
||||
config.update('virtualText', false) |
||||
}) |
||||
|
||||
it('should clear all diagnostics', async () => { |
||||
let diagnostic = createDiagnostic('foo') |
||||
let buf = await createDiagnosticBuffer() |
||||
let diagnostics = [diagnostic] |
||||
await buf.refresh({ '': diagnostics }) |
||||
buf.clear() |
||||
await helper.wait(50) |
||||
let buffer = await nvim.buffer |
||||
let res = await buffer.getVar("coc_diagnostic_info") |
||||
expect(res == null).toBe(true) |
||||
}) |
||||
}) |
||||
|
||||
describe('getDiagnostics()', () => { |
||||
it('should get sorted diagnostics', async () => { |
||||
let buf = await createDiagnosticBuffer() |
||||
let diagnostics = [ |
||||
createDiagnostic('three', Range.create(0, 1, 0, 2), DiagnosticSeverity.Error), |
||||
createDiagnostic('one', Range.create(0, 0, 0, 2), DiagnosticSeverity.Warning), |
||||
createDiagnostic('two', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error), |
||||
] |
||||
diagnostics[0].tags = [DiagnosticTag.Unnecessary] |
||||
await buf.refresh({ |
||||
x: diagnostics, |
||||
y: [createDiagnostic('four', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error)] |
||||
}) |
||||
let res = buf.getDiagnosticsAt(Position.create(0, 1), false) |
||||
let arr = res.map(o => o.message) |
||||
expect(arr).toEqual(['four', 'two', 'three', 'one']) |
||||
}) |
||||
}) |
||||
}) |
@ -1,98 +0,0 @@
@@ -1,98 +0,0 @@
|
||||
import DiagnosticCollection from '../../diagnostic/collection' |
||||
import { Diagnostic, Range } from 'vscode-languageserver-types' |
||||
|
||||
function createDiagnostic(msg: string, range?: Range): Diagnostic { |
||||
range = range ? range : Range.create(0, 0, 0, 1) |
||||
return Diagnostic.create(range, msg) |
||||
} |
||||
|
||||
describe('diagnostic collection', () => { |
||||
|
||||
it('should create collection', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
expect(collection.name).toBe('test') |
||||
}) |
||||
|
||||
it('should set diagnostic with uri', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
collection.set(uri, [diagnostic]) |
||||
expect(collection.get(uri).length).toBe(1) |
||||
collection.set(uri, []) |
||||
expect(collection.get(uri).length).toBe(0) |
||||
}) |
||||
|
||||
it('should clear diagnostics with null as diagnostics', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
collection.set(uri, [diagnostic]) |
||||
expect(collection.get(uri).length).toBe(1) |
||||
collection.set(uri, null) |
||||
expect(collection.get(uri).length).toBe(0) |
||||
}) |
||||
|
||||
it('should clear diagnostics with undefined as diagnostics in entries', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let entries: [string, Diagnostic[] | null][] = [ |
||||
['file:1', [diagnostic]], |
||||
['file:1', undefined] |
||||
] |
||||
let uri = 'file:///1' |
||||
collection.set(entries) |
||||
expect(collection.get(uri).length).toBe(0) |
||||
}) |
||||
|
||||
it('should set diagnostics with entries', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
let other = 'file:///2' |
||||
let entries: [string, Diagnostic[]][] = [ |
||||
[uri, [diagnostic]], |
||||
[other, [diagnostic]], |
||||
[uri, [createDiagnostic('other')]] |
||||
] |
||||
collection.set(entries) |
||||
expect(collection.get(uri).length).toBe(2) |
||||
expect(collection.get(other).length).toBe(1) |
||||
}) |
||||
|
||||
it('should delete diagnostics for uri', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
collection.set(uri, [diagnostic]) |
||||
collection.delete(uri) |
||||
expect(collection.get(uri).length).toBe(0) |
||||
}) |
||||
|
||||
it('should clear all diagnostics', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
collection.set(uri, [diagnostic]) |
||||
collection.clear() |
||||
expect(collection.get(uri).length).toBe(0) |
||||
}) |
||||
|
||||
it('should call for every uri with diagnostics', () => { |
||||
let collection = new DiagnosticCollection('test') |
||||
let diagnostic = createDiagnostic('error') |
||||
let uri = 'file:///1' |
||||
let other = 'file:///2' |
||||
let entries: [string, Diagnostic[]][] = [ |
||||
[uri, [diagnostic]], |
||||
[other, [diagnostic]], |
||||
[uri, [createDiagnostic('other')]] |
||||
] |
||||
collection.set(entries) |
||||
let arr: string[] = [] |
||||
collection.forEach(uri => { |
||||
arr.push(uri) |
||||
}) |
||||
expect(arr).toEqual([uri, other]) |
||||
}) |
||||
}) |
@ -1,556 +0,0 @@
@@ -1,556 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import path from 'path' |
||||
import { severityLevel, getNameFromSeverity } from '../../diagnostic/util' |
||||
import { Range, DiagnosticSeverity, Diagnostic, Location, DiagnosticTag } from 'vscode-languageserver-types' |
||||
import { URI } from 'vscode-uri' |
||||
import Document from '../../model/document' |
||||
import workspace from '../../workspace' |
||||
import window from '../../window' |
||||
import manager from '../../diagnostic/manager' |
||||
import helper, { createTmpFile } from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
function createDiagnostic(msg: string, range?: Range, severity?: DiagnosticSeverity): Diagnostic { |
||||
range = range ? range : Range.create(0, 0, 0, 1) |
||||
return Diagnostic.create(range, msg, severity || DiagnosticSeverity.Error) |
||||
} |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
manager.reset() |
||||
await helper.reset() |
||||
}) |
||||
|
||||
async function createDocument(name?: string): Promise<Document> { |
||||
let doc = await helper.createDocument(name) |
||||
let collection = manager.create('test') |
||||
let diagnostics: Diagnostic[] = [] |
||||
await doc.buffer.setLines(['foo bar foo bar', 'foo bar', 'foo', 'bar'], { |
||||
start: 0, |
||||
end: -1, |
||||
strictIndexing: false |
||||
}) |
||||
diagnostics.push(createDiagnostic('error', Range.create(0, 2, 0, 4), DiagnosticSeverity.Error)) |
||||
diagnostics.push(createDiagnostic('warning', Range.create(0, 5, 0, 6), DiagnosticSeverity.Warning)) |
||||
diagnostics.push(createDiagnostic('information', Range.create(1, 0, 1, 1), DiagnosticSeverity.Information)) |
||||
diagnostics.push(createDiagnostic('hint', Range.create(1, 2, 1, 3), DiagnosticSeverity.Hint)) |
||||
diagnostics.push(createDiagnostic('error', Range.create(2, 0, 2, 2), DiagnosticSeverity.Error)) |
||||
collection.set(doc.uri, diagnostics) |
||||
doc.forceSync() |
||||
return doc |
||||
} |
||||
|
||||
describe('diagnostic manager', () => { |
||||
describe('refresh()', () => { |
||||
it('should refresh on buffer create', async () => { |
||||
let uri = URI.file(path.join(path.dirname(__dirname), 'doc')).toString() |
||||
let fn = jest.fn() |
||||
let disposable = manager.onDidRefresh(() => { |
||||
fn() |
||||
}) |
||||
let collection = manager.create('tmp') |
||||
let diagnostic = createDiagnostic('My Error') |
||||
collection.set(uri, [diagnostic]) |
||||
let doc = await helper.createDocument('doc') |
||||
let val = await doc.buffer.getVar('coc_diagnostic_info') as any |
||||
expect(fn).toBeCalled() |
||||
expect(val).toBeDefined() |
||||
expect(val.error).toBe(1) |
||||
collection.dispose() |
||||
disposable.dispose() |
||||
}) |
||||
|
||||
it('should delay refresh on InsertLeave', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.input('i') |
||||
let collection = manager.create('test') |
||||
let diagnostics: Diagnostic[] = [] |
||||
await doc.buffer.setLines(['foo bar foo bar', 'foo bar', 'foo', 'bar'], { |
||||
start: 0, |
||||
end: -1, |
||||
strictIndexing: false |
||||
}) |
||||
diagnostics.push(createDiagnostic('error', Range.create(0, 2, 0, 4), DiagnosticSeverity.Error)) |
||||
collection.set(doc.uri, diagnostics) |
||||
await helper.wait(10) |
||||
await nvim.input('<esc>') |
||||
await helper.wait(100) |
||||
let val = await doc.buffer.getVar('coc_diagnostic_info') as any |
||||
expect(val).toBe(null) |
||||
await helper.wait(600) |
||||
val = await doc.buffer.getVar('coc_diagnostic_info') as any |
||||
expect(val).toBeDefined() |
||||
}) |
||||
}) |
||||
|
||||
describe('toggleDiagnostic()', () => { |
||||
it('should toggle diagnostics', async () => { |
||||
let doc = await createDocument() |
||||
await helper.wait(50) |
||||
manager.toggleDiagnostic() |
||||
await helper.wait(50) |
||||
let val = await doc.buffer.getVar('coc_diagnostic_info') as any |
||||
expect(val).toBe(null) |
||||
manager.toggleDiagnostic() |
||||
await helper.wait(50) |
||||
val = await doc.buffer.getVar('coc_diagnostic_info') as any |
||||
expect(val).toBeDefined() |
||||
expect(val.error).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('getDiagnosticList()', () => { |
||||
it('should get all diagnostics', async () => { |
||||
await createDocument() |
||||
let list = manager.getDiagnosticList() |
||||
expect(list).toBeDefined() |
||||
expect(list.length).toBeGreaterThanOrEqual(5) |
||||
expect(list[0].severity).toBe('Error') |
||||
expect(list[1].severity).toBe('Error') |
||||
expect(list[2].severity).toBe('Warning') |
||||
expect(list[3].severity).toBe('Information') |
||||
expect(list[4].severity).toBe('Hint') |
||||
}) |
||||
|
||||
it('should filter diagnostics by configuration', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('level', 'warning') |
||||
config.update('showUnused', false) |
||||
config.update('showDeprecated', false) |
||||
let doc = await createDocument() |
||||
let diagnostics = manager.getDiagnostics(doc.uri)['test'] |
||||
diagnostics[0].tags = [DiagnosticTag.Unnecessary] |
||||
diagnostics[2].tags = [DiagnosticTag.Deprecated] |
||||
let collection = manager.getCollectionByName('test') |
||||
collection.set(doc.uri, diagnostics) |
||||
let list = manager.getDiagnosticList() |
||||
expect(list.length).toBe(1) |
||||
expect(list[0].severity).toBe('Warning') |
||||
let res = manager.getDiagnostics(doc.uri)['test'] |
||||
expect(res.length).toBe(1) |
||||
helper.updateConfiguration('diagnostic.level', 'hint') |
||||
helper.updateConfiguration('diagnostic.showUnused', true) |
||||
helper.updateConfiguration('diagnostic.showDeprecated', true) |
||||
let ranges = manager.getSortedRanges(doc.uri) |
||||
expect(ranges.length).toBe(3) |
||||
}) |
||||
}) |
||||
|
||||
describe('preview()', () => { |
||||
it('should not throw with empty diagnostics', async () => { |
||||
await helper.createDocument() |
||||
await manager.preview() |
||||
let tabpage = await nvim.tabpage |
||||
let wins = await tabpage.windows |
||||
expect(wins.length).toBe(1) |
||||
}) |
||||
|
||||
it('should open preview window', async () => { |
||||
await createDocument() |
||||
await nvim.call('cursor', [1, 3]) |
||||
await manager.preview() |
||||
let res = await nvim.call('coc#window#find', ['&previewwindow', 1]) |
||||
expect(res).toBeDefined() |
||||
await nvim.call('win_gotoid', [res]) |
||||
let buf = await nvim.buffer |
||||
let lines = await buf.lines |
||||
expect(lines[0]).toEqual('[test] [E]') |
||||
}) |
||||
}) |
||||
|
||||
describe('setLocationlist()', () => { |
||||
it('should set location list', async () => { |
||||
let doc = await createDocument() |
||||
await manager.setLocationlist(doc.bufnr) |
||||
await nvim.command('lopen') |
||||
let buftype = await nvim.eval('&buftype') as string |
||||
expect(buftype).toBe('quickfix') |
||||
}) |
||||
}) |
||||
|
||||
describe('setConfigurationErrors()', () => { |
||||
it('should set configuration errors', async () => { |
||||
let doc = await helper.createDocument() |
||||
let errors = [{ |
||||
location: Location.create(doc.uri, Range.create(0, 0, 1, 0)), |
||||
message: 'foo', |
||||
}, { |
||||
location: Location.create(doc.uri, Range.create(1, 0, 2, 0)), |
||||
message: 'bar', |
||||
}] |
||||
manager.setConfigurationErrors(errors) |
||||
await helper.wait(50) |
||||
let res = manager.getDiagnostics(doc.uri, 'config')['config'] |
||||
expect(res.length).toBe(2) |
||||
manager.setConfigurationErrors() |
||||
await helper.wait(50) |
||||
res = manager.getDiagnostics(doc.uri, 'config')['config'] |
||||
expect(res.length).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('create()', () => { |
||||
it('should create diagnostic collection', async () => { |
||||
let doc = await helper.createDocument() |
||||
let collection = manager.create('test') |
||||
collection.set(doc.uri, [createDiagnostic('foo')]) |
||||
await helper.wait(50) |
||||
let info = await doc.buffer.getVar('coc_diagnostic_info') |
||||
expect(info).toBeDefined() |
||||
await nvim.command('bd!') |
||||
await helper.wait(50) |
||||
}) |
||||
}) |
||||
|
||||
describe('getSortedRanges()', () => { |
||||
it('should get sorted ranges of document', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.call('setline', [1, ['a', 'b', 'c']]) |
||||
let collection = manager.create('test') |
||||
let diagnostics: Diagnostic[] = [] |
||||
diagnostics.push(createDiagnostic('x', Range.create(0, 0, 0, 1))) |
||||
diagnostics.push(createDiagnostic('y', Range.create(0, 1, 0, 2))) |
||||
diagnostics.push(createDiagnostic('z', Range.create(1, 0, 1, 2))) |
||||
collection.set(doc.uri, diagnostics) |
||||
let ranges = manager.getSortedRanges(doc.uri) |
||||
expect(ranges[0]).toEqual(Range.create(0, 0, 0, 1)) |
||||
expect(ranges[1]).toEqual(Range.create(0, 1, 0, 2)) |
||||
expect(ranges[2]).toEqual(Range.create(1, 0, 1, 2)) |
||||
ranges = manager.getSortedRanges(doc.uri, 'error') |
||||
expect(ranges.length).toBe(3) |
||||
expect(manager.getSortedRanges(doc.uri, 'warning').length).toBe(0) |
||||
}) |
||||
}) |
||||
|
||||
describe('getDiagnosticsInRange', () => { |
||||
it('should get diagnostics in range', async () => { |
||||
let doc = await helper.createDocument() |
||||
let collection = manager.create('test') |
||||
let diagnostics: Diagnostic[] = [] |
||||
await doc.buffer.setLines(['foo bar foo bar', 'foo bar'], { |
||||
start: 0, |
||||
end: -1, |
||||
strictIndexing: false |
||||
}) |
||||
await helper.wait(300) |
||||
diagnostics.push(createDiagnostic('a', Range.create(0, 0, 0, 1))) |
||||
diagnostics.push(createDiagnostic('b', Range.create(0, 2, 0, 3))) |
||||
diagnostics.push(createDiagnostic('c', Range.create(1, 0, 1, 2))) |
||||
collection.set(doc.uri, diagnostics) |
||||
let res = manager.getDiagnosticsInRange(doc.textDocument, Range.create(0, 0, 0, 3)) |
||||
expect(res.length).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('getCurrentDiagnostics', () => { |
||||
it('should get diagnostics under corsor', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
await createDocument() |
||||
let diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBe(0) |
||||
await nvim.call('cursor', [1, 4]) |
||||
diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBe(1) |
||||
config.update('checkCurrentLine', true) |
||||
await nvim.call('cursor', [1, 2]) |
||||
diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBe(2) |
||||
config.update('checkCurrentLine', false) |
||||
}) |
||||
|
||||
it('should get empty diagnostic at end of line', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
doc.forceSync() |
||||
await nvim.command('normal! $') |
||||
let diagnostic = Diagnostic.create(Range.create(0, 3, 1, 0), 'error', DiagnosticSeverity.Error) |
||||
let collection = manager.create('empty') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.bufnr, true) |
||||
let diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBeGreaterThanOrEqual(1) |
||||
expect(diagnostics[0].message).toBe('error') |
||||
collection.dispose() |
||||
}) |
||||
|
||||
it('should get diagnostic next to end of line', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
doc.forceSync() |
||||
await nvim.command('normal! $') |
||||
let diagnostic = Diagnostic.create(Range.create(0, 3, 0, 4), 'error', DiagnosticSeverity.Error) |
||||
let collection = manager.create('empty') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.bufnr, true) |
||||
let diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBeGreaterThanOrEqual(1) |
||||
expect(diagnostics[0].message).toBe('error') |
||||
collection.dispose() |
||||
}) |
||||
|
||||
it('should get diagnostic with empty range at end of line', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
doc.forceSync() |
||||
await nvim.command('normal! $') |
||||
let diagnostic = Diagnostic.create(Range.create(0, 3, 1, 0), 'error', DiagnosticSeverity.Error) |
||||
let collection = manager.create('empty') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.bufnr, true) |
||||
let diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBeGreaterThanOrEqual(1) |
||||
expect(diagnostics[0].message).toBe('error') |
||||
collection.dispose() |
||||
}) |
||||
|
||||
it('should get diagnostic pass end of the buffer lines', async () => { |
||||
let doc = await helper.createDocument() |
||||
await nvim.setLine('foo') |
||||
doc.forceSync() |
||||
await nvim.command('normal! ^') |
||||
let diagnostic = Diagnostic.create(Range.create(1, 0, 1, 0), 'error', DiagnosticSeverity.Error) |
||||
let collection = manager.create('empty') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.bufnr, true) |
||||
let diagnostics = await manager.getCurrentDiagnostics() |
||||
expect(diagnostics.length).toBeGreaterThanOrEqual(1) |
||||
expect(diagnostics[0].message).toBe('error') |
||||
collection.dispose() |
||||
}) |
||||
|
||||
}) |
||||
|
||||
describe('jumpRelated', () => { |
||||
it('should jump to related position', async () => { |
||||
let doc = await helper.createDocument() |
||||
let range = Range.create(0, 0, 0, 10) |
||||
let location = Location.create(URI.file(__filename).toString(), range) |
||||
let diagnostic = Diagnostic.create(range, 'msg', DiagnosticSeverity.Error, 1000, 'test', |
||||
[{ location, message: 'test' }]) |
||||
let collection = manager.create('positions') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.uri, true) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await manager.jumpRelated() |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('bufname', '%') |
||||
expect(bufname).toMatch('diagnosticManager') |
||||
}) |
||||
|
||||
it('should open location list', async () => { |
||||
let doc = await helper.createDocument() |
||||
let range = Range.create(0, 0, 0, 10) |
||||
let diagnostic = Diagnostic.create(range, 'msg', DiagnosticSeverity.Error, 1000, 'test', |
||||
[{ |
||||
location: Location.create(URI.file(__filename).toString(), Range.create(1, 0, 1, 10)), |
||||
message: 'foo' |
||||
}, { |
||||
location: Location.create(URI.file(__filename).toString(), Range.create(2, 0, 2, 10)), |
||||
message: 'bar' |
||||
}]) |
||||
let collection = manager.create('positions') |
||||
collection.set(doc.uri, [diagnostic]) |
||||
await manager.refreshBuffer(doc.uri, true) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await manager.jumpRelated() |
||||
await helper.wait(100) |
||||
let bufname = await nvim.call('bufname', '%') |
||||
expect(bufname).toBe('list:///location') |
||||
}) |
||||
}) |
||||
|
||||
describe('jumpPrevious & jumpNext', () => { |
||||
it('should jump to previous', async () => { |
||||
let doc = await createDocument() |
||||
await nvim.command('normal! G$') |
||||
let ranges = manager.getSortedRanges(doc.uri) |
||||
ranges.reverse() |
||||
for (let i = 0; i < ranges.length; i++) { |
||||
await manager.jumpPrevious() |
||||
let pos = await window.getCursorPosition() |
||||
expect(pos).toEqual(ranges[i].start) |
||||
} |
||||
await manager.jumpPrevious() |
||||
}) |
||||
|
||||
it('should jump to next', async () => { |
||||
let doc = await createDocument() |
||||
await nvim.call('cursor', [0, 0]) |
||||
let ranges = manager.getSortedRanges(doc.uri) |
||||
for (let i = 0; i < ranges.length; i++) { |
||||
await manager.jumpNext() |
||||
let pos = await window.getCursorPosition() |
||||
expect(pos).toEqual(ranges[i].start) |
||||
} |
||||
await manager.jumpNext() |
||||
}) |
||||
}) |
||||
|
||||
describe('diagnostic configuration', () => { |
||||
it('should use filetype map from config', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('filetypeMap', { default: 'bufferType' }) |
||||
let doc = await createDocument('foo.js') |
||||
let collection = manager.getCollectionByName('test') |
||||
let diagnostics = [createDiagnostic('99', Range.create(0, 0, 0, 2), DiagnosticSeverity.Error)] |
||||
collection.set(doc.uri, diagnostics) |
||||
await nvim.call('cursor', [1, 1]) |
||||
await nvim.command('doautocmd CursorHold') |
||||
let winid = await helper.waitFloat() |
||||
await nvim.call('win_gotoid', [winid]) |
||||
await nvim.command('normal! $') |
||||
let res = await nvim.eval('synIDattr(synID(line("."),col("."),1),"name")') |
||||
expect(res).toMatch(/javascript/i) |
||||
config.update('filetypeMap', {}) |
||||
}) |
||||
|
||||
it('should show floating window on cursor hold', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('messageTarget', 'float') |
||||
await createDocument() |
||||
await nvim.call('cursor', [1, 3]) |
||||
await nvim.command('doautocmd CursorHold') |
||||
let winid = await helper.waitFloat() |
||||
let bufnr = await nvim.call('nvim_win_get_buf', winid) as number |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let lines = await buf.lines |
||||
expect(lines.join('\n')).toMatch('error') |
||||
}) |
||||
|
||||
it('should echo messages on cursor hold', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('messageTarget', 'echo') |
||||
await createDocument() |
||||
await nvim.call('cursor', [1, 3]) |
||||
await helper.wait(600) |
||||
let line = await helper.getCmdline() |
||||
expect(line).toMatch('error') |
||||
config.update('messageTarget', 'float') |
||||
}) |
||||
|
||||
it('should show diagnostics of current line', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('checkCurrentLine', true) |
||||
await createDocument() |
||||
await nvim.call('cursor', [1, 1]) |
||||
let winid = await helper.waitFloat() |
||||
let bufnr = await nvim.call('nvim_win_get_buf', winid) as number |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let lines = await buf.lines |
||||
expect(lines.length).toBe(3) |
||||
config.update('checkCurrentLine', false) |
||||
}) |
||||
|
||||
it('should filter diagnostics by level', async () => { |
||||
helper.updateConfiguration('diagnostic.level', 'warning') |
||||
let doc = await createDocument() |
||||
let diagnosticsMap = manager.getDiagnostics(doc.uri) |
||||
for (let diagnostics of Object.values(diagnosticsMap)) { |
||||
for (let diagnostic of diagnostics) { |
||||
expect(diagnostic.severity != DiagnosticSeverity.Hint).toBe(true) |
||||
expect(diagnostic.severity != DiagnosticSeverity.Information).toBe(true) |
||||
} |
||||
} |
||||
helper.updateConfiguration('diagnostic.level', 'hint') |
||||
}) |
||||
|
||||
it('should send ale diagnostic items', async () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
config.update('displayByAle', true) |
||||
let content = ` |
||||
function! MockAleResults(bufnr, collection, items) |
||||
let g:collection = a:collection |
||||
let g:items = a:items |
||||
endfunction |
||||
` |
||||
let file = await createTmpFile(content) |
||||
await nvim.command(`source ${file}`) |
||||
await createDocument() |
||||
await helper.wait(50) |
||||
let items = await nvim.getVar('items') as any[] |
||||
expect(Array.isArray(items)).toBe(true) |
||||
expect(items.length).toBeGreaterThan(0) |
||||
await nvim.command('bd!') |
||||
await helper.wait(50) |
||||
items = await nvim.getVar('items') as any[] |
||||
expect(items).toEqual([]) |
||||
config.update('displayByAle', false) |
||||
}) |
||||
}) |
||||
|
||||
describe('severityLevel & getNameFromSeverity', () => { |
||||
it('should get severity level', () => { |
||||
expect(severityLevel('hint')).toBe(DiagnosticSeverity.Hint) |
||||
expect(severityLevel('error')).toBe(DiagnosticSeverity.Error) |
||||
expect(severityLevel('warning')).toBe(DiagnosticSeverity.Warning) |
||||
expect(severityLevel('information')).toBe(DiagnosticSeverity.Information) |
||||
expect(severityLevel('')).toBe(DiagnosticSeverity.Hint) |
||||
}) |
||||
|
||||
it('should get severity name', () => { |
||||
expect(getNameFromSeverity(null as any)).toBe('CocError') |
||||
}) |
||||
}) |
||||
|
||||
describe('toggleDiagnosticBuffer', () => { |
||||
it('should toggle diagnostics for buffer', async () => { |
||||
let doc = await createDocument() |
||||
// required to wait refresh finish
|
||||
await helper.wait(50) |
||||
await manager.toggleDiagnosticBuffer(doc.bufnr) |
||||
await helper.wait(50) |
||||
let buf = nvim.createBuffer(doc.bufnr) |
||||
let res = await buf.getVar('coc_diagnostic_info') as any |
||||
expect(res == null).toBe(true) |
||||
await manager.toggleDiagnosticBuffer(doc.bufnr) |
||||
await helper.wait(50) |
||||
res = await buf.getVar('coc_diagnostic_info') as any |
||||
expect(res.error).toBe(2) |
||||
}) |
||||
}) |
||||
|
||||
describe('refresh', () => { |
||||
let config = workspace.getConfiguration('diagnostic') |
||||
beforeEach(() => { |
||||
config.update('autoRefresh', false) |
||||
}) |
||||
afterEach(() => { |
||||
config.update('autoRefresh', true) |
||||
}) |
||||
|
||||
it('should refresh by bufnr', async () => { |
||||
let doc = await createDocument() |
||||
let buf = nvim.createBuffer(doc.bufnr) |
||||
let res = await buf.getVar('coc_diagnostic_info') as any |
||||
// should not refresh
|
||||
expect(res == null).toBe(true) |
||||
manager.refresh(doc.bufnr) |
||||
await helper.wait(100) |
||||
res = await buf.getVar('coc_diagnostic_info') as any |
||||
expect(res?.error).toBe(2) |
||||
}) |
||||
|
||||
it('should refresh all buffers', async () => { |
||||
let one = await helper.createDocument('one') |
||||
let two = await helper.createDocument('two') |
||||
let collection = manager.create('tmp') |
||||
collection.set([[one.uri, [createDiagnostic('Error one')]], [two.uri, [createDiagnostic('Error two')]]]) |
||||
manager.refresh() |
||||
await helper.wait(50) |
||||
for (let bufnr of [one.bufnr, two.bufnr]) { |
||||
let buf = nvim.createBuffer(bufnr) |
||||
let res = await buf.getVar('coc_diagnostic_info') as any |
||||
expect(res?.error).toBe(1) |
||||
} |
||||
collection.dispose() |
||||
}) |
||||
}) |
||||
}) |
@ -1,60 +0,0 @@
@@ -1,60 +0,0 @@
|
||||
import { Neovim } from '@chemzqm/neovim' |
||||
import Dialog, { DialogButton } from '../../model/dialog' |
||||
import helper from '../helper' |
||||
|
||||
let nvim: Neovim |
||||
|
||||
beforeAll(async () => { |
||||
await helper.setup() |
||||
nvim = helper.nvim |
||||
}) |
||||
|
||||
afterAll(async () => { |
||||
await helper.shutdown() |
||||
}) |
||||
|
||||
afterEach(async () => { |
||||
await helper.reset() |
||||
}) |
||||
|
||||
describe('Dialog module', () => { |
||||
it('should show dialog', async () => { |
||||
let dialog = new Dialog(nvim, { content: '你好' }) |
||||
await dialog.show({}) |
||||
let winid = await dialog.winid |
||||
let win = nvim.createWindow(winid) |
||||
let width = await win.width |
||||
expect(width).toBe(4) |
||||
await nvim.call('coc#float#close', [winid]) |
||||
}) |
||||
|
||||
it('should invoke callback with index -1', async () => { |
||||
let callback = jest.fn() |
||||
let dialog = new Dialog(nvim, { content: '你好', callback }) |
||||
await dialog.show({}) |
||||
let winid = await dialog.winid |
||||
await nvim.call('coc#float#close', [winid]) |
||||
await helper.wait(50) |
||||
expect(callback).toHaveBeenCalledWith(-1) |
||||
}) |
||||
|
||||
it('should invoke callback on click', async () => { |
||||
let callback = jest.fn() |
||||
let buttons: DialogButton[] = [{ |
||||
index: 0, |
||||
text: 'yes' |
||||
}, { |
||||
index: 1, |
||||
text: 'no' |
||||
}] |
||||
let dialog = new Dialog(nvim, { content: '你好', buttons, callback }) |
||||
await dialog.show({}) |
||||
let winid = await dialog.winid |
||||
let btnwin = await nvim.call('coc#float#get_related', [winid, 'buttons']) |
||||
await nvim.call('win_gotoid', [btnwin]) |
||||
await nvim.call('cursor', [2, 1]) |
||||
await nvim.call('coc#float#nvim_float_click', []) |
||||
await helper.wait(50) |
||||
expect(callback).toHaveBeenCalledWith(0) |
||||
}) |
||||
}) |
@ -1,178 +0,0 @@
@@ -1,178 +0,0 @@
|
||||
import { TextEdit } from 'vscode-languageserver-types' |
||||
import { TextDocument } from 'vscode-languageserver-textdocument' |
||||
import { diffLines, getChange, patchLine, ChangedLines } from '../../util/diff' |
||||
|
||||
describe('diff lines', () => { |
||||
function diff(oldStr: string, newStr: string): ChangedLines { |
||||
let oldLines = oldStr.split('\n') |
||||
return diffLines(oldLines, newStr.split('\n'), oldLines.length - 2) |
||||
} |
||||
|
||||
it('should diff changed lines', () => { |
||||
let res = diff('a\n', 'b\n') |
||||
expect(res).toEqual({ start: 0, end: 1, replacement: ['b'] }) |
||||
}) |
||||
|
||||
it('should diff added lines', () => { |
||||
let res = diff('a\n', 'a\nb\n') |
||||
expect(res).toEqual({ |
||||
start: 1, |
||||
end: 1, |
||||
replacement: ['b'] |
||||
}) |
||||
}) |
||||
|
||||
it('should diff remove lines', () => { |
||||
let res = diff('a\n\n', 'a\n') |
||||
expect(res).toEqual({ |
||||
start: 1, |
||||
end: 2, |
||||
replacement: [] |
||||
}) |
||||
}) |
||||
|
||||
it('should diff remove multiple lines', () => { |
||||
let res = diff('a\n\n\n', 'a\n') |
||||
expect(res).toEqual({ |
||||
start: 1, |
||||
end: 3, |
||||
replacement: [] |
||||
}) |
||||
}) |
||||
|
||||
it('should diff removed line', () => { |
||||
let res = diff('a\n\n\nb', 'a\n\nb') |
||||
expect(res).toEqual({ |
||||
start: 2, |
||||
end: 3, |
||||
replacement: [] |
||||
}) |
||||
}) |
||||
|
||||
it('should reduce changed lines', async () => { |
||||
let res = diffLines(['a', 'b', 'c'], ['a', 'b', 'c', 'd'], 0) |
||||
expect(res).toEqual({ |
||||
start: 3, |
||||
end: 3, |
||||
replacement: ['d'] |
||||
}) |
||||
}) |
||||
}) |
||||
|
||||
describe('patch line', () => { |
||||
it('should patch line', () => { |
||||
let res = patchLine('foo', 'bar foo bar') |
||||
expect(res.length).toBe(7) |
||||
expect(res).toBe(' foo') |
||||
}) |
||||
}) |
||||
|
||||
describe('should get text edits', () => { |
||||
|
||||
function applyEdits(oldStr: string, newStr: string): void { |
||||
let doc = TextDocument.create('untitled://1', 'markdown', 0, oldStr) |
||||
let change = getChange(doc.getText(), newStr) |
||||
let start = doc.positionAt(change.start) |
||||
let end = doc.positionAt(change.end) |
||||
let edit: TextEdit = { |
||||
range: { start, end }, |
||||
newText: change.newText |
||||
} |
||||
let res = TextDocument.applyEdits(doc, [edit]) |
||||
expect(res).toBe(newStr) |
||||
} |
||||
|
||||
it('should get diff for comments ', async () => { |
||||
let oldStr = '/*\n *\n * \n' |
||||
let newStr = '/*\n *\n *\n * \n' |
||||
let doc = TextDocument.create('untitled://1', 'markdown', 0, oldStr) |
||||
let change = getChange(doc.getText(), newStr, 1) |
||||
let start = doc.positionAt(change.start) |
||||
let end = doc.positionAt(change.end) |
||||
let edit: TextEdit = { |
||||
range: { start, end }, |
||||
newText: change.newText |
||||
} |
||||
let res = TextDocument.applyEdits(doc, [edit]) |
||||
expect(res).toBe(newStr) |
||||
}) |
||||
|
||||
it('should return null for same content', () => { |
||||
let change = getChange('', '') |
||||
expect(change).toBeNull() |
||||
change = getChange('abc', 'abc') |
||||
expect(change).toBeNull() |
||||
}) |
||||
|
||||
it('should get diff for added', () => { |
||||
applyEdits('1\n2', '1\n2\n3\n4') |
||||
}) |
||||
|
||||
it('should get diff for added #0', () => { |
||||
applyEdits('\n\n', '\n\n\n') |
||||
}) |
||||
|
||||
it('should get diff for added #1', () => { |
||||
applyEdits('1\n2\n3', '5\n1\n2\n3') |
||||
}) |
||||
|
||||
it('should get diff for added #2', () => { |
||||
applyEdits('1\n2\n3', '1\n2\n4\n3') |
||||
}) |
||||
|
||||
it('should get diff for added #3', () => { |
||||
applyEdits('1\n2\n3', '4\n1\n2\n3\n5') |
||||
}) |
||||
|
||||
it('should get diff for added #4', () => { |
||||
applyEdits(' ', ' ') |
||||
}) |
||||
|
||||
it('should get diff for replace', () => { |
||||
applyEdits('1\n2\n3\n4\n5', '1\n5\n3\n6\n7') |
||||
}) |
||||
|
||||
it('should get diff for replace #1', () => { |
||||
applyEdits('1\n2\n3\n4\n5', '1\n5\n3\n6\n7') |
||||
}) |
||||
|
||||
it('should get diff for remove #0', () => { |
||||
applyEdits('1\n2\n3\n4', '1\n4') |
||||
}) |
||||
|
||||
it('should get diff for remove #1', () => { |
||||
applyEdits('1\n2\n3\n4', '1') |
||||
}) |
||||
|
||||
it('should get diff for remove #2', () => { |
||||
applyEdits(' ', ' ') |
||||
}) |
||||
|
||||
it('should prefer cursor position for change', async () => { |
||||
let res = getChange(' int n', ' n', 0) |
||||
expect(res).toEqual({ start: 1, end: 5, newText: '' }) |
||||
res = getChange(' int n', ' n') |
||||
expect(res).toEqual({ start: 0, end: 4, newText: '' }) |
||||
}) |
||||
|
||||
it('should prefer next line for change', async () => { |
||||
let res = getChange('a\nb', 'a\nc\nb') |
||||
expect(res).toEqual({ start: 2, end: 2, newText: 'c\n' }) |
||||
applyEdits('a\nb', 'a\nc\nb') |
||||
}) |
||||
|
||||
it('should prefer previous line for change', async () => { |
||||
let res = getChange('\n\na', '\na') |
||||
expect(res).toEqual({ start: 0, end: 1, newText: '' }) |
||||
}) |
||||
|
||||
it('should consider cursor', () => { |
||||
let res = getChange('\n\n\n', '\n\n\n\n', 1) |
||||
expect(res).toEqual({ start: 2, end: 2, newText: '\n' }) |
||||
}) |
||||
|
||||
it('should get minimal diff', () => { |
||||
let res = getChange('foo\nbar', 'fab\nbar', 2) |
||||
expect(res).toEqual({ start: 1, end: 3, newText: 'ab' }) |
||||
}) |
||||
}) |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue