| | |
| | |
| | |
| | |
| |
|
| | import { |
| | createConnection, |
| | TextDocuments, |
| | Diagnostic, |
| | DiagnosticSeverity, |
| | ProposedFeatures, |
| | InitializeParams, |
| | DidChangeConfigurationNotification, |
| | CompletionItem, |
| | CompletionItemKind, |
| | TextDocumentPositionParams, |
| | TextDocumentSyncKind |
| | } from 'vscode-languageserver/node'; |
| |
|
| | import { |
| | TextDocument |
| | } from 'vscode-languageserver-textdocument'; |
| |
|
| | |
| | |
| | const connection = createConnection(ProposedFeatures.all); |
| |
|
| | const documents = new TextDocuments(TextDocument); |
| |
|
| | let hasConfigurationCapability = false; |
| | let hasWorkspaceFolderCapability = false; |
| | let hasDiagnosticRelatedInformationCapability = false; |
| |
|
| | connection.onInitialize((params: InitializeParams) => { |
| | const capabilities = params.capabilities; |
| |
|
| | |
| | |
| | hasConfigurationCapability = |
| | !!capabilities.workspace && !!capabilities.workspace.configuration; |
| | hasWorkspaceFolderCapability = |
| | !!capabilities.workspace && !!capabilities.workspace.workspaceFolders; |
| | hasDiagnosticRelatedInformationCapability = !!( |
| | capabilities.textDocument && |
| | capabilities.textDocument.publishDiagnostics && |
| | capabilities.textDocument.publishDiagnostics.relatedInformation); |
| |
|
| | return { |
| | capabilities: { |
| | textDocumentSync: TextDocumentSyncKind.Incremental, |
| | |
| | completionProvider: { |
| | resolveProvider: true |
| | } |
| | } |
| | }; |
| | }); |
| |
|
| | connection.onInitialized(() => { |
| | if (hasConfigurationCapability) { |
| | |
| | connection.client.register( |
| | DidChangeConfigurationNotification.type, |
| | undefined |
| | ); |
| | } |
| | if (hasWorkspaceFolderCapability) { |
| | connection.workspace.onDidChangeWorkspaceFolders(_event => { |
| | connection.console.log('Workspace folder change event received.'); |
| | }); |
| | } |
| | }); |
| |
|
| | |
| | interface ExampleSettings { |
| | maxNumberOfProblems: number; |
| | } |
| |
|
| | |
| | |
| | |
| | const defaultSettings: ExampleSettings = { maxNumberOfProblems: 1000 }; |
| | let globalSettings: ExampleSettings = defaultSettings; |
| |
|
| | |
| | const documentSettings = new Map<string, Thenable<ExampleSettings>>(); |
| |
|
| | connection.onDidChangeConfiguration(change => { |
| | if (hasConfigurationCapability) { |
| | |
| | documentSettings.clear(); |
| | } else { |
| | globalSettings = ( |
| | (change.settings.languageServerExample || defaultSettings) |
| | ); |
| | } |
| |
|
| | |
| | documents.all().forEach(validateTextDocument); |
| | }); |
| |
|
| | function getDocumentSettings(resource: string): Thenable<ExampleSettings> { |
| | if (!hasConfigurationCapability) { |
| | return Promise.resolve(globalSettings); |
| | } |
| | let result = documentSettings.get(resource); |
| | if (!result) { |
| | result = connection.workspace.getConfiguration({ |
| | scopeUri: resource, |
| | section: 'languageServerExample' |
| | }); |
| | documentSettings.set(resource, result); |
| | } |
| | return result; |
| | } |
| |
|
| | |
| | documents.onDidClose(e => { |
| | documentSettings.delete(e.document.uri); |
| | }); |
| |
|
| | |
| | |
| | documents.onDidChangeContent(change => { |
| | validateTextDocument(change.document); |
| | }); |
| |
|
| | async function validateTextDocument(textDocument: TextDocument): Promise<void> { |
| | |
| | const settings = await getDocumentSettings(textDocument.uri); |
| |
|
| | |
| | const text = textDocument.getText(); |
| | const pattern = /\b[A-Z]{2,}\b/g; |
| | let m: RegExpExecArray | null; |
| |
|
| | let problems = 0; |
| | const diagnostics: Diagnostic[] = []; |
| | while ((m = pattern.exec(text)) && problems < settings.maxNumberOfProblems) { |
| | problems++; |
| | const diagnostic: Diagnostic = { |
| | severity: DiagnosticSeverity.Warning, |
| | range: { |
| | start: textDocument.positionAt(m.index), |
| | end: textDocument.positionAt(m.index + m[0].length) |
| | }, |
| | message: `${m[0]} is all uppercase.`, |
| | source: 'ex' |
| | }; |
| | if (hasDiagnosticRelatedInformationCapability) { |
| | diagnostic.relatedInformation = [ |
| | { |
| | location: { |
| | uri: textDocument.uri, |
| | range: Object.assign({}, diagnostic.range) |
| | }, |
| | message: 'Spelling matters' |
| | }, |
| | { |
| | location: { |
| | uri: textDocument.uri, |
| | range: Object.assign({}, diagnostic.range) |
| | }, |
| | message: 'Particularly for names' |
| | } |
| | ]; |
| | } |
| | diagnostics.push(diagnostic); |
| | } |
| |
|
| | |
| | connection.sendDiagnostics({ uri: textDocument.uri, diagnostics }); |
| | } |
| |
|
| | connection.onDidChangeWatchedFiles(_change => { |
| | |
| | connection.console.log('We received a file change event'); |
| | }); |
| |
|
| | |
| | connection.onCompletion( |
| | (_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => { |
| | |
| | |
| | |
| | return [ |
| | { |
| | label: 'TypeScript', |
| | kind: CompletionItemKind.Text, |
| | data: 1 |
| | }, |
| | { |
| | label: 'JavaScript', |
| | kind: CompletionItemKind.Text, |
| | data: 2 |
| | } |
| | ]; |
| | } |
| | ); |
| |
|
| | |
| | |
| | connection.onCompletionResolve( |
| | (item: CompletionItem): CompletionItem => { |
| | if (item.data === 1) { |
| | item.detail = 'TypeScript details'; |
| | item.documentation = 'TypeScript documentation'; |
| | } else if (item.data === 2) { |
| | item.detail = 'JavaScript details'; |
| | item.documentation = 'JavaScript documentation'; |
| | } |
| | return item; |
| | } |
| | ); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | documents.listen(connection); |
| |
|
| | |
| | connection.listen(); |
| |
|