410 lines
13 KiB
JavaScript
410 lines
13 KiB
JavaScript
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.Undo)
|
|
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()
|