Move md path completions and document links to language server (#155100)

This commit is contained in:
Matt Bierner
2022-07-13 12:49:37 -07:00
committed by GitHub
parent 06443bcc10
commit bec36ce756
14 changed files with 153 additions and 1345 deletions

View File

@@ -10,17 +10,16 @@
"main": "./out/node/main",
"browser": "./dist/browser/main",
"dependencies": {
"vscode-languageserver": "^8.0.2-next.4",
"vscode-uri": "^3.0.3",
"vscode-languageserver": "^8.0.2-next.5`",
"vscode-languageserver-textdocument": "^1.0.5",
"vscode-languageserver-types": "^3.17.1",
"vscode-markdown-languageservice": "microsoft/vscode-markdown-languageservice"
"vscode-markdown-languageservice": "^0.0.0-alpha.5",
"vscode-uri": "^3.0.3"
},
"devDependencies": {
"@types/node": "16.x"
},
"scripts": {
"postinstall": "cd node_modules/vscode-markdown-languageservice && yarn run compile-ext",
"compile": "gulp compile-extension:markdown-language-features-server",
"watch": "gulp watch-extension:markdown-language-features-server"
}

View File

@@ -6,10 +6,12 @@
import { RequestType } from 'vscode-languageserver';
import * as md from 'vscode-markdown-languageservice';
declare const TextDecoder: any;
export const parseRequestType: RequestType<{ uri: string }, md.Token[], any> = new RequestType('markdown/parse');
export const readFileRequestType: RequestType<{ uri: string }, number[], any> = new RequestType('markdown/readFile');
export const statFileRequestType: RequestType<{ uri: string }, md.FileStat | undefined, any> = new RequestType('markdown/statFile');
export const readDirectoryRequestType: RequestType<{ uri: string }, [string, md.FileStat][], any> = new RequestType('markdown/readDirectory');
export const findFilesRequestTypes: RequestType<{}, string[], any> = new RequestType('markdown/findFiles');

View File

@@ -3,27 +3,35 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Connection, InitializeParams, InitializeResult, TextDocuments } from 'vscode-languageserver';
import { Connection, InitializeParams, InitializeResult, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import * as lsp from 'vscode-languageserver-types';
import * as md from 'vscode-markdown-languageservice';
import { URI } from 'vscode-uri';
import { LogFunctionLogger } from './logging';
import { parseRequestType } from './protocol';
import { VsCodeClientWorkspace } from './workspace';
declare const TextDecoder: any;
export function startServer(connection: Connection) {
export async function startServer(connection: Connection) {
const documents = new TextDocuments(TextDocument);
documents.listen(connection);
const notebooks = new NotebookDocuments(documents);
connection.onInitialize((_params: InitializeParams): InitializeResult => {
connection.onInitialize((params: InitializeParams): InitializeResult => {
workspace.workspaceFolders = (params.workspaceFolders ?? []).map(x => URI.parse(x.uri));
return {
capabilities: {
documentLinkProvider: { resolveProvider: true },
documentSymbolProvider: true,
completionProvider: { triggerCharacters: ['.', '/', '#'] },
foldingRangeProvider: true,
selectionRangeProvider: true,
workspaceSymbolProvider: true,
workspace: {
workspaceFolders: {
supported: true,
changeNotifications: true,
},
}
}
};
});
@@ -36,15 +44,36 @@ export function startServer(connection: Connection) {
}
};
const workspace = new VsCodeClientWorkspace(connection, documents);
const workspace = new VsCodeClientWorkspace(connection, documents, notebooks);
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console));
const provider = md.createLanguageService({ workspace, parser, logger });
connection.onDocumentLinks(async (params, token): Promise<lsp.DocumentLink[]> => {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
return await provider.getDocumentLinks(document, token);
}
} catch (e) {
console.error(e.stack);
}
return [];
});
connection.onDocumentLinkResolve(async (link, token): Promise<lsp.DocumentLink | undefined> => {
try {
return await provider.resolveDocumentLink(link, token);
} catch (e) {
console.error(e.stack);
}
return undefined;
});
connection.onDocumentSymbol(async (params, token): Promise<lsp.DocumentSymbol[]> => {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
return await provider.provideDocumentSymbols(document, token);
return await provider.getDocumentSymbols(document, token);
}
} catch (e) {
console.error(e.stack);
@@ -56,7 +85,7 @@ export function startServer(connection: Connection) {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
return await provider.provideFoldingRanges(document, token);
return await provider.getFoldingRanges(document, token);
}
} catch (e) {
console.error(e.stack);
@@ -68,7 +97,7 @@ export function startServer(connection: Connection) {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
return await provider.provideSelectionRanges(document, params.positions, token);
return await provider.getSelectionRanges(document, params.positions, token);
}
} catch (e) {
console.error(e.stack);
@@ -78,13 +107,26 @@ export function startServer(connection: Connection) {
connection.onWorkspaceSymbol(async (params, token): Promise<lsp.WorkspaceSymbol[]> => {
try {
return await provider.provideWorkspaceSymbols(params.query, token);
return await provider.getWorkspaceSymbols(params.query, token);
} catch (e) {
console.error(e.stack);
}
return [];
});
connection.onCompletion(async (params, token): Promise<lsp.CompletionItem[]> => {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
return await provider.getCompletionItems(document, params.position, params.context!, token);
}
} catch (e) {
console.error(e.stack);
}
return [];
});
documents.listen(connection);
notebooks.listen(connection);
connection.listen();
}

View File

@@ -0,0 +1,8 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export const Schemes = Object.freeze({
notebookCell: 'vscode-notebook-cell',
});

View File

@@ -3,15 +3,17 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Connection, Emitter, FileChangeType, TextDocuments } from 'vscode-languageserver';
import { Connection, Emitter, FileChangeType, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import * as md from 'vscode-markdown-languageservice';
import { ContainingDocumentContext } from 'vscode-markdown-languageservice/out/workspace';
import { URI } from 'vscode-uri';
import * as protocol from './protocol';
import { coalesce } from './util/arrays';
import { isMarkdownDocument, looksLikeMarkdownPath } from './util/file';
import { Limiter } from './util/limiter';
import { ResourceMap } from './util/resourceMap';
import { Schemes } from './util/schemes';
declare const TextDecoder: any;
@@ -33,6 +35,7 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
constructor(
private readonly connection: Connection,
private readonly documents: TextDocuments<TextDocument>,
private readonly notebooks: NotebookDocuments<TextDocument>,
) {
documents.onDidOpen(e => {
this._documentCache.delete(URI.parse(e.document.uri));
@@ -57,14 +60,14 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
switch (change.type) {
case FileChangeType.Changed: {
this._documentCache.delete(resource);
const document = await this.getOrLoadMarkdownDocument(resource);
const document = await this.openMarkdownDocument(resource);
if (document) {
this._onDidChangeMarkdownDocument.fire(document);
}
break;
}
case FileChangeType.Created: {
const document = await this.getOrLoadMarkdownDocument(resource);
const document = await this.openMarkdownDocument(resource);
if (document) {
this._onDidCreateMarkdownDocument.fire(document);
}
@@ -80,6 +83,22 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
});
}
public listen() {
this.connection.workspace.onDidChangeWorkspaceFolders(async () => {
this.workspaceFolders = (await this.connection.workspace.getWorkspaceFolders() ?? []).map(x => URI.parse(x.uri));
});
}
private _workspaceFolders: readonly URI[] = [];
get workspaceFolders(): readonly URI[] {
return this._workspaceFolders;
}
set workspaceFolders(value: readonly URI[]) {
this._workspaceFolders = value;
}
async getAllMarkdownDocuments(): Promise<Iterable<md.ITextDocument>> {
const maxConcurrent = 20;
@@ -91,7 +110,7 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
const onDiskResults = await Promise.all(resources.map(strResource => {
return limiter.queue(async () => {
const resource = URI.parse(strResource);
const doc = await this.getOrLoadMarkdownDocument(resource);
const doc = await this.openMarkdownDocument(resource);
if (doc) {
foundFiles.set(resource);
}
@@ -110,7 +129,7 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
return !!this.documents.get(resource.toString());
}
async getOrLoadMarkdownDocument(resource: URI): Promise<md.ITextDocument | undefined> {
async openMarkdownDocument(resource: URI): Promise<md.ITextDocument | undefined> {
const existing = this._documentCache.get(resource);
if (existing) {
return existing;
@@ -141,12 +160,25 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
}
}
async pathExists(_resource: URI): Promise<boolean> {
return false;
stat(resource: URI): Promise<md.FileStat | undefined> {
return this.connection.sendRequest(protocol.statFileRequestType, { uri: resource.toString() });
}
async readDirectory(_resource: URI): Promise<[string, { isDir: boolean }][]> {
return [];
async readDirectory(resource: URI): Promise<[string, md.FileStat][]> {
return this.connection.sendRequest(protocol.readDirectoryRequestType, { uri: resource.toString() });
}
getContainingDocument(resource: URI): ContainingDocumentContext | undefined {
if (resource.scheme === Schemes.notebookCell) {
const nb = this.notebooks.findNotebookDocumentForCell(resource.toString());
if (nb) {
return {
uri: URI.parse(nb.uri),
children: nb.cells.map(cell => ({ uri: URI.parse(cell.document) })),
};
}
}
return undefined;
}
private isRelevantMarkdownDocument(doc: TextDocument) {

View File

@@ -35,17 +35,19 @@ vscode-languageserver-types@^3.17.1:
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
vscode-languageserver@^8.0.2-next.4:
vscode-languageserver@^8.0.2-next.5`:
version "8.0.2-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.2-next.5.tgz#39a2dd4c504fb88042375e7ac706a714bdaab4e5"
integrity sha512-2ZDb7O/4atS9mJKufPPz637z+51kCyZfgnobFW5eSrUdS3c0UB/nMS4Ng1EavYTX84GVaVMKCrmP0f2ceLmR0A==
dependencies:
vscode-languageserver-protocol "3.17.2-next.6"
vscode-markdown-languageservice@microsoft/vscode-markdown-languageservice:
version "0.0.0-alpha.2"
resolved "https://codeload.github.com/microsoft/vscode-markdown-languageservice/tar.gz/db497ada376aae9a335519dbfb406c6a1f873446"
vscode-markdown-languageservice@^0.0.0-alpha.5:
version "0.0.0-alpha.5"
resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.0.0-alpha.5.tgz#fb3042f3ee79589606154c19b15565541337bceb"
integrity sha512-vy8UVa1jtm3CwkifRn3fEWM710JC4AYEECNd5KQthSCoFSfT5pOshJNFWs5yzBeVrohiy4deOdhSrfbDMg/Hyg==
dependencies:
vscode-languageserver-textdocument "^1.0.5"
vscode-languageserver-types "^3.17.1"
vscode-uri "^3.0.3"