Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Bierner <matb@microsoft.com>2022-07-12 17:04:25 +0300
committerGitHub <noreply@github.com>2022-07-12 17:04:25 +0300
commiteeb8d49317454c4337cde792bbac9e5d3851cf88 (patch)
tree52b8998d788f88746878f249357ae1a9f0bfef98 /extensions
parentcb67591f254d0700991a49d4fb13aa4edea6e640 (diff)
Move md workspace symbol search to language service (#154874)
* Move md workspace symbol search to language service Also implements more of IWorkspace for the server * Revert extra change
Diffstat (limited to 'extensions')
-rw-r--r--extensions/markdown-language-features/server/.vscode/launch.json21
-rw-r--r--extensions/markdown-language-features/server/.vscode/tasks.json30
-rw-r--r--extensions/markdown-language-features/server/package.json2
-rw-r--r--extensions/markdown-language-features/server/src/protocol.ts15
-rw-r--r--extensions/markdown-language-features/server/src/server.ts79
-rw-r--r--extensions/markdown-language-features/server/src/util/arrays.ts11
-rw-r--r--extensions/markdown-language-features/server/src/util/file.ts27
-rw-r--r--extensions/markdown-language-features/server/src/util/limiter.ts67
-rw-r--r--extensions/markdown-language-features/server/src/util/resourceMap.ts69
-rw-r--r--extensions/markdown-language-features/server/src/workspace.ts155
-rw-r--r--extensions/markdown-language-features/server/yarn.lock6
-rw-r--r--extensions/markdown-language-features/src/client.ts18
-rw-r--r--extensions/markdown-language-features/src/extension.shared.ts4
-rw-r--r--extensions/markdown-language-features/src/languageFeatures/documentSymbols.ts77
-rw-r--r--extensions/markdown-language-features/src/languageFeatures/workspaceSymbols.ts36
-rw-r--r--extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts105
-rw-r--r--extensions/markdown-language-features/src/util/file.ts22
17 files changed, 421 insertions, 323 deletions
diff --git a/extensions/markdown-language-features/server/.vscode/launch.json b/extensions/markdown-language-features/server/.vscode/launch.json
index 5753befac8b..1ea07e048c8 100644
--- a/extensions/markdown-language-features/server/.vscode/launch.json
+++ b/extensions/markdown-language-features/server/.vscode/launch.json
@@ -6,26 +6,7 @@
"name": "Attach",
"type": "node",
"request": "attach",
- "port": 6044,
- "protocol": "inspector",
- "sourceMaps": true,
- "outFiles": ["${workspaceFolder}/out/**/*.js"]
- },
- {
- "name": "Unit Tests",
- "type": "node",
- "request": "launch",
- "program": "${workspaceFolder}/../../../node_modules/mocha/bin/_mocha",
- "stopOnEntry": false,
- "args": [
- "--timeout",
- "999999",
- "--colors"
- ],
- "cwd": "${workspaceFolder}",
- "runtimeExecutable": null,
- "runtimeArgs": [],
- "env": {},
+ "port": 7675,
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/out/**/*.js"]
}
diff --git a/extensions/markdown-language-features/server/.vscode/tasks.json b/extensions/markdown-language-features/server/.vscode/tasks.json
index 6a159d6a5fa..ecc951a7baf 100644
--- a/extensions/markdown-language-features/server/.vscode/tasks.json
+++ b/extensions/markdown-language-features/server/.vscode/tasks.json
@@ -1,9 +1,27 @@
{
- "version": "0.1.0",
+ "version": "2.0.0",
"command": "npm",
- "isShellCommand": true,
- "showOutput": "silent",
- "args": ["run", "watch"],
- "isWatching": true,
- "problemMatcher": "$tsc-watch"
+ "args": [
+ "run",
+ "watch"
+ ],
+ "isBackground": true,
+ "problemMatcher": "$tsc-watch",
+ "tasks": [
+ {
+ "label": "npm",
+ "type": "shell",
+ "command": "npm",
+ "args": [
+ "run",
+ "watch"
+ ],
+ "isBackground": true,
+ "problemMatcher": "$tsc-watch",
+ "group": {
+ "_id": "build",
+ "isDefault": false
+ }
+ }
+ ]
} \ No newline at end of file
diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json
index 2ca66e39347..f3cfb2292a1 100644
--- a/extensions/markdown-language-features/server/package.json
+++ b/extensions/markdown-language-features/server/package.json
@@ -14,7 +14,7 @@
"vscode-uri": "^3.0.3",
"vscode-languageserver-textdocument": "^1.0.5",
"vscode-languageserver-types": "^3.17.1",
- "vscode-markdown-languageservice": "mjbvz/vscode-markdown-languageservice"
+ "vscode-markdown-languageservice": "microsoft/vscode-markdown-languageservice"
},
"devDependencies": {
"@types/node": "16.x"
diff --git a/extensions/markdown-language-features/server/src/protocol.ts b/extensions/markdown-language-features/server/src/protocol.ts
new file mode 100644
index 00000000000..9f49c277ae2
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/protocol.ts
@@ -0,0 +1,15 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+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 findFilesRequestTypes: RequestType<{}, string[], any> = new RequestType('markdown/findFiles');
diff --git a/extensions/markdown-language-features/server/src/server.ts b/extensions/markdown-language-features/server/src/server.ts
index 8bc1d4b9271..ad2491d9688 100644
--- a/extensions/markdown-language-features/server/src/server.ts
+++ b/extensions/markdown-language-features/server/src/server.ts
@@ -3,37 +3,15 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
-import { Connection, Emitter, Event, InitializeParams, InitializeResult, RequestType, TextDocuments } from 'vscode-languageserver';
+import { Connection, InitializeParams, InitializeResult, 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';
-
-const parseRequestType: RequestType<{ uri: string }, md.Token[], any> = new RequestType('markdown/parse');
-
-class TextDocumentToITextDocumentAdapter implements md.ITextDocument {
- public readonly uri: md.IUri;
-
- public get version(): number { return this._doc.version; }
-
- public get lineCount(): number { return this._doc.lineCount; }
-
- constructor(
- private readonly _doc: TextDocument,
- ) {
- this.uri = URI.parse(this._doc.uri);
- }
-
- getText(range?: md.IRange | undefined): string {
- return this._doc.getText(range);
- }
-
- positionAt(offset: number): md.IPosition {
- return this._doc.positionAt(offset);
- }
-}
+declare const TextDecoder: any;
export function startServer(connection: Connection) {
const documents = new TextDocuments(TextDocument);
@@ -45,11 +23,11 @@ export function startServer(connection: Connection) {
documentSymbolProvider: true,
foldingRangeProvider: true,
selectionRangeProvider: true,
+ workspaceSymbolProvider: true,
}
};
});
-
const parser = new class implements md.IMdParser {
slugifier = md.githubSlugifier;
@@ -58,42 +36,15 @@ export function startServer(connection: Connection) {
}
};
- const workspace = new class implements md.IMdWorkspace {
-
- private readonly _onDidChangeMarkdownDocument = new Emitter<md.ITextDocument>();
- onDidChangeMarkdownDocument: Event<md.ITextDocument> = this._onDidChangeMarkdownDocument.event;
-
- private readonly _onDidCreateMarkdownDocument = new Emitter<md.ITextDocument>();
- onDidCreateMarkdownDocument: Event<md.ITextDocument> = this._onDidCreateMarkdownDocument.event;
-
- private readonly _onDidDeleteMarkdownDocument = new Emitter<md.IUri>();
- onDidDeleteMarkdownDocument: Event<md.IUri> = this._onDidDeleteMarkdownDocument.event;
-
- async getAllMarkdownDocuments(): Promise<Iterable<md.ITextDocument>> {
- return documents.all().map(doc => new TextDocumentToITextDocumentAdapter(doc));
- }
- hasMarkdownDocument(resource: md.IUri): boolean {
- return !!documents.get(resource.toString());
- }
- async getOrLoadMarkdownDocument(_resource: md.IUri): Promise<md.ITextDocument | undefined> {
- return undefined;
- }
- async pathExists(_resource: md.IUri): Promise<boolean> {
- return false;
- }
- async readDirectory(_resource: md.IUri): Promise<[string, { isDir: boolean }][]> {
- return [];
- }
- };
-
+ const workspace = new VsCodeClientWorkspace(connection, documents);
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console));
- const provider = md.createLanguageService(workspace, parser, logger);
+ const provider = md.createLanguageService({ workspace, parser, logger });
connection.onDocumentSymbol(async (params, token): Promise<lsp.DocumentSymbol[]> => {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
- return await provider.provideDocumentSymbols(new TextDocumentToITextDocumentAdapter(document), token);
+ return await provider.provideDocumentSymbols(document, token);
}
} catch (e) {
console.error(e.stack);
@@ -105,7 +56,7 @@ export function startServer(connection: Connection) {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
- return await provider.provideFoldingRanges(new TextDocumentToITextDocumentAdapter(document), token);
+ return await provider.provideFoldingRanges(document, token);
}
} catch (e) {
console.error(e.stack);
@@ -117,7 +68,7 @@ export function startServer(connection: Connection) {
try {
const document = documents.get(params.textDocument.uri);
if (document) {
- return await provider.provideSelectionRanges(new TextDocumentToITextDocumentAdapter(document), params.positions, token);
+ return await provider.provideSelectionRanges(document, params.positions, token);
}
} catch (e) {
console.error(e.stack);
@@ -125,5 +76,15 @@ export function startServer(connection: Connection) {
return [];
});
+ connection.onWorkspaceSymbol(async (params, token): Promise<lsp.WorkspaceSymbol[]> => {
+ try {
+ return await provider.provideWorkspaceSymbols(params.query, token);
+ } catch (e) {
+ console.error(e.stack);
+ }
+ return [];
+ });
+
connection.listen();
}
+
diff --git a/extensions/markdown-language-features/server/src/util/arrays.ts b/extensions/markdown-language-features/server/src/util/arrays.ts
new file mode 100644
index 00000000000..3ed55d8f077
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/util/arrays.ts
@@ -0,0 +1,11 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+/**
+ * @returns New array with all falsy values removed. The original array IS NOT modified.
+ */
+export function coalesce<T>(array: ReadonlyArray<T | undefined | null>): T[] {
+ return <T[]>array.filter(e => !!e);
+}
diff --git a/extensions/markdown-language-features/server/src/util/file.ts b/extensions/markdown-language-features/server/src/util/file.ts
new file mode 100644
index 00000000000..45b072a82dc
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/util/file.ts
@@ -0,0 +1,27 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { TextDocument } from 'vscode-languageserver-textdocument';
+import * as URI from 'vscode-uri';
+
+const markdownFileExtensions = Object.freeze<string[]>([
+ '.md',
+ '.mkd',
+ '.mdwn',
+ '.mdown',
+ '.markdown',
+ '.markdn',
+ '.mdtxt',
+ '.mdtext',
+ '.workbook',
+]);
+
+export function looksLikeMarkdownPath(resolvedHrefPath: URI.URI) {
+ return markdownFileExtensions.includes(URI.Utils.extname(URI.URI.from(resolvedHrefPath)).toLowerCase());
+}
+
+export function isMarkdownDocument(document: TextDocument): boolean {
+ return document.languageId === 'markdown';
+}
diff --git a/extensions/markdown-language-features/server/src/util/limiter.ts b/extensions/markdown-language-features/server/src/util/limiter.ts
new file mode 100644
index 00000000000..bd4153cd08b
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/util/limiter.ts
@@ -0,0 +1,67 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+interface ILimitedTaskFactory<T> {
+ factory: ITask<Promise<T>>;
+ c: (value: T | Promise<T>) => void;
+ e: (error?: unknown) => void;
+}
+
+interface ITask<T> {
+ (): T;
+}
+
+/**
+ * A helper to queue N promises and run them all with a max degree of parallelism. The helper
+ * ensures that at any time no more than M promises are running at the same time.
+ *
+ * Taken from 'src/vs/base/common/async.ts'
+ */
+export class Limiter<T> {
+
+ private _size = 0;
+ private runningPromises: number;
+ private readonly maxDegreeOfParalellism: number;
+ private readonly outstandingPromises: ILimitedTaskFactory<T>[];
+
+ constructor(maxDegreeOfParalellism: number) {
+ this.maxDegreeOfParalellism = maxDegreeOfParalellism;
+ this.outstandingPromises = [];
+ this.runningPromises = 0;
+ }
+
+ get size(): number {
+ return this._size;
+ }
+
+ queue(factory: ITask<Promise<T>>): Promise<T> {
+ this._size++;
+
+ return new Promise<T>((c, e) => {
+ this.outstandingPromises.push({ factory, c, e });
+ this.consume();
+ });
+ }
+
+ private consume(): void {
+ while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) {
+ const iLimitedTask = this.outstandingPromises.shift()!;
+ this.runningPromises++;
+
+ const promise = iLimitedTask.factory();
+ promise.then(iLimitedTask.c, iLimitedTask.e);
+ promise.then(() => this.consumed(), () => this.consumed());
+ }
+ }
+
+ private consumed(): void {
+ this._size--;
+ this.runningPromises--;
+
+ if (this.outstandingPromises.length > 0) {
+ this.consume();
+ }
+ }
+}
diff --git a/extensions/markdown-language-features/server/src/util/resourceMap.ts b/extensions/markdown-language-features/server/src/util/resourceMap.ts
new file mode 100644
index 00000000000..7cec9d661d3
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/util/resourceMap.ts
@@ -0,0 +1,69 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { URI } from 'vscode-uri';
+
+
+type ResourceToKey = (uri: URI) => string;
+
+const defaultResourceToKey = (resource: URI): string => resource.toString();
+
+export class ResourceMap<T> {
+
+ private readonly map = new Map<string, { readonly uri: URI; readonly value: T }>();
+
+ private readonly toKey: ResourceToKey;
+
+ constructor(toKey: ResourceToKey = defaultResourceToKey) {
+ this.toKey = toKey;
+ }
+
+ public set(uri: URI, value: T): this {
+ this.map.set(this.toKey(uri), { uri, value });
+ return this;
+ }
+
+ public get(resource: URI): T | undefined {
+ return this.map.get(this.toKey(resource))?.value;
+ }
+
+ public has(resource: URI): boolean {
+ return this.map.has(this.toKey(resource));
+ }
+
+ public get size(): number {
+ return this.map.size;
+ }
+
+ public clear(): void {
+ this.map.clear();
+ }
+
+ public delete(resource: URI): boolean {
+ return this.map.delete(this.toKey(resource));
+ }
+
+ public *values(): IterableIterator<T> {
+ for (const entry of this.map.values()) {
+ yield entry.value;
+ }
+ }
+
+ public *keys(): IterableIterator<URI> {
+ for (const entry of this.map.values()) {
+ yield entry.uri;
+ }
+ }
+
+ public *entries(): IterableIterator<[URI, T]> {
+ for (const entry of this.map.values()) {
+ yield [entry.uri, entry.value];
+ }
+ }
+
+ public [Symbol.iterator](): IterableIterator<[URI, T]> {
+ return this.entries();
+ }
+}
diff --git a/extensions/markdown-language-features/server/src/workspace.ts b/extensions/markdown-language-features/server/src/workspace.ts
new file mode 100644
index 00000000000..964ff369d50
--- /dev/null
+++ b/extensions/markdown-language-features/server/src/workspace.ts
@@ -0,0 +1,155 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Connection, Emitter, FileChangeType, TextDocuments } from 'vscode-languageserver';
+import { TextDocument } from 'vscode-languageserver-textdocument';
+import * as md from 'vscode-markdown-languageservice';
+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';
+
+declare const TextDecoder: any;
+
+export class VsCodeClientWorkspace implements md.IWorkspace {
+
+ private readonly _onDidCreateMarkdownDocument = new Emitter<md.ITextDocument>();
+ public readonly onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocument.event;
+
+ private readonly _onDidChangeMarkdownDocument = new Emitter<md.ITextDocument>();
+ public readonly onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocument.event;
+
+ private readonly _onDidDeleteMarkdownDocument = new Emitter<URI>();
+ public readonly onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocument.event;
+
+ private readonly _documentCache = new ResourceMap<md.ITextDocument>();
+
+ private readonly _utf8Decoder = new TextDecoder('utf-8');
+
+ constructor(
+ private readonly connection: Connection,
+ private readonly documents: TextDocuments<TextDocument>,
+ ) {
+ documents.onDidOpen(e => {
+ this._documentCache.delete(URI.parse(e.document.uri));
+ if (this.isRelevantMarkdownDocument(e.document)) {
+ this._onDidCreateMarkdownDocument.fire(e.document);
+ }
+ });
+
+ documents.onDidChangeContent(e => {
+ if (this.isRelevantMarkdownDocument(e.document)) {
+ this._onDidChangeMarkdownDocument.fire(e.document);
+ }
+ });
+
+ documents.onDidClose(e => {
+ this._documentCache.delete(URI.parse(e.document.uri));
+ });
+
+ connection.onDidChangeWatchedFiles(async ({ changes }) => {
+ for (const change of changes) {
+ const resource = URI.parse(change.uri);
+ switch (change.type) {
+ case FileChangeType.Changed: {
+ this._documentCache.delete(resource);
+ const document = await this.getOrLoadMarkdownDocument(resource);
+ if (document) {
+ this._onDidChangeMarkdownDocument.fire(document);
+ }
+ break;
+ }
+ case FileChangeType.Created: {
+ const document = await this.getOrLoadMarkdownDocument(resource);
+ if (document) {
+ this._onDidCreateMarkdownDocument.fire(document);
+ }
+ break;
+ }
+ case FileChangeType.Deleted: {
+ this._documentCache.delete(resource);
+ this._onDidDeleteMarkdownDocument.fire(resource);
+ break;
+ }
+ }
+ }
+ });
+ }
+
+ async getAllMarkdownDocuments(): Promise<Iterable<md.ITextDocument>> {
+ const maxConcurrent = 20;
+
+ const foundFiles = new ResourceMap<void>();
+ const limiter = new Limiter<md.ITextDocument | undefined>(maxConcurrent);
+
+ // Add files on disk
+ const resources = await this.connection.sendRequest(protocol.findFilesRequestTypes, {});
+ const onDiskResults = await Promise.all(resources.map(strResource => {
+ return limiter.queue(async () => {
+ const resource = URI.parse(strResource);
+ const doc = await this.getOrLoadMarkdownDocument(resource);
+ if (doc) {
+ foundFiles.set(resource);
+ }
+ return doc;
+ });
+ }));
+
+ // Add opened files (such as untitled files)
+ const openTextDocumentResults = await Promise.all(this.documents.all()
+ .filter(doc => !foundFiles.has(URI.parse(doc.uri)) && this.isRelevantMarkdownDocument(doc)));
+
+ return coalesce([...onDiskResults, ...openTextDocumentResults]);
+ }
+
+ hasMarkdownDocument(resource: URI): boolean {
+ return !!this.documents.get(resource.toString());
+ }
+
+ async getOrLoadMarkdownDocument(resource: URI): Promise<md.ITextDocument | undefined> {
+ const existing = this._documentCache.get(resource);
+ if (existing) {
+ return existing;
+ }
+
+ const matchingDocument = this.documents.get(resource.toString());
+ if (matchingDocument) {
+ this._documentCache.set(resource, matchingDocument);
+ return matchingDocument;
+ }
+
+ if (!looksLikeMarkdownPath(resource)) {
+ return undefined;
+ }
+
+ try {
+ const response = await this.connection.sendRequest(protocol.readFileRequestType, { uri: resource.toString() });
+ // TODO: LSP doesn't seem to handle Array buffers well
+ const bytes = new Uint8Array(response);
+
+ // We assume that markdown is in UTF-8
+ const text = this._utf8Decoder.decode(bytes);
+ const doc = new md.InMemoryDocument(resource, text, 0);
+ this._documentCache.set(resource, doc);
+ return doc;
+ } catch (e) {
+ return undefined;
+ }
+ }
+
+ async pathExists(_resource: URI): Promise<boolean> {
+ return false;
+ }
+
+ async readDirectory(_resource: URI): Promise<[string, { isDir: boolean }][]> {
+ return [];
+ }
+
+ private isRelevantMarkdownDocument(doc: TextDocument) {
+ return isMarkdownDocument(doc) && URI.parse(doc.uri).scheme !== 'vscode-bulkeditpreview';
+ }
+}
diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock
index d0bce38f189..e46f1b1b8db 100644
--- a/extensions/markdown-language-features/server/yarn.lock
+++ b/extensions/markdown-language-features/server/yarn.lock
@@ -42,9 +42,9 @@ vscode-languageserver@^8.0.2-next.4:
dependencies:
vscode-languageserver-protocol "3.17.2-next.6"
-vscode-markdown-languageservice@mjbvz/vscode-markdown-languageservice:
- version "0.0.0-alpha.1"
- resolved "https://codeload.github.com/mjbvz/vscode-markdown-languageservice/tar.gz/e1a0e00bf6a99cc543da64964cc0995537647d15"
+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"
dependencies:
vscode-languageserver-types "^3.17.1"
vscode-uri "^3.0.3"
diff --git a/extensions/markdown-language-features/src/client.ts b/extensions/markdown-language-features/src/client.ts
index 0bf5588ec38..aabd09f4633 100644
--- a/extensions/markdown-language-features/src/client.ts
+++ b/extensions/markdown-language-features/src/client.ts
@@ -8,23 +8,30 @@ import * as vscode from 'vscode';
import { BaseLanguageClient, LanguageClientOptions, RequestType } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { IMdParser } from './markdownEngine';
+import { markdownFileExtensions } from './util/file';
import { IMdWorkspace } from './workspace';
const localize = nls.loadMessageBundle();
const parseRequestType: RequestType<{ uri: string }, Token[], any> = new RequestType('markdown/parse');
+const readFileRequestType: RequestType<{ uri: string }, number[], any> = new RequestType('markdown/readFile');
+
+const findFilesRequestTypes: RequestType<{}, string[], any> = new RequestType('markdown/findFiles');
+
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
export async function startClient(factory: LanguageClientConstructor, workspace: IMdWorkspace, parser: IMdParser): Promise<BaseLanguageClient> {
const documentSelector = ['markdown'];
+ const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`;
const clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {
- configurationSection: ['markdown']
+ configurationSection: ['markdown'],
+ fileEvents: vscode.workspace.createFileSystemWatcher(mdFileGlob),
},
initializationOptions: {}
};
@@ -43,6 +50,15 @@ export async function startClient(factory: LanguageClientConstructor, workspace:
}
});
+ client.onRequest(readFileRequestType, async (e): Promise<number[]> => {
+ const uri = vscode.Uri.parse(e.uri);
+ return Array.from(await vscode.workspace.fs.readFile(uri));
+ });
+
+ client.onRequest(findFilesRequestTypes, async (): Promise<string[]> => {
+ return (await vscode.workspace.findFiles(mdFileGlob, '**/node_modules/**')).map(x => x.toString());
+ });
+
await client.start();
return client;
diff --git a/extensions/markdown-language-features/src/extension.shared.ts b/extensions/markdown-language-features/src/extension.shared.ts
index 2a850191004..c5ebe5650c0 100644
--- a/extensions/markdown-language-features/src/extension.shared.ts
+++ b/extensions/markdown-language-features/src/extension.shared.ts
@@ -10,13 +10,11 @@ import { registerPasteSupport } from './languageFeatures/copyPaste';
import { registerDefinitionSupport } from './languageFeatures/definitions';
import { registerDiagnosticSupport } from './languageFeatures/diagnostics';
import { MdLinkProvider, registerDocumentLinkSupport } from './languageFeatures/documentLinks';
-import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbols';
import { registerDropIntoEditorSupport } from './languageFeatures/dropIntoEditor';
import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences';
import { registerPathCompletionSupport } from './languageFeatures/pathCompletions';
import { MdReferencesProvider, registerReferencesSupport } from './languageFeatures/references';
import { registerRenameSupport } from './languageFeatures/rename';
-import { registerWorkspaceSymbolSupport } from './languageFeatures/workspaceSymbols';
import { ILogger } from './logging';
import { IMdParser, MarkdownItEngine, MdParsingProvider } from './markdownEngine';
import { MarkdownContributionProvider } from './markdownExtensions';
@@ -67,7 +65,6 @@ function registerMarkdownLanguageFeatures(
const linkProvider = new MdLinkProvider(parser, workspace, logger);
const referencesProvider = new MdReferencesProvider(parser, workspace, tocProvider, logger);
- const symbolProvider = new MdDocumentSymbolProvider(tocProvider, logger);
return vscode.Disposable.from(
linkProvider,
@@ -83,7 +80,6 @@ function registerMarkdownLanguageFeatures(
registerPathCompletionSupport(selector, workspace, parser, linkProvider),
registerReferencesSupport(selector, referencesProvider),
registerRenameSupport(selector, workspace, referencesProvider, parser.slugifier),
- registerWorkspaceSymbolSupport(workspace, symbolProvider),
);
}
diff --git a/extensions/markdown-language-features/src/languageFeatures/documentSymbols.ts b/extensions/markdown-language-features/src/languageFeatures/documentSymbols.ts
deleted file mode 100644
index 2152e7bd46c..00000000000
--- a/extensions/markdown-language-features/src/languageFeatures/documentSymbols.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as vscode from 'vscode';
-import { ILogger } from '../logging';
-import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents';
-import { ITextDocument } from '../types/textDocument';
-
-interface MarkdownSymbol {
- readonly level: number;
- readonly parent: MarkdownSymbol | undefined;
- readonly children: vscode.DocumentSymbol[];
-}
-
-export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
-
- constructor(
- private readonly tocProvider: MdTableOfContentsProvider,
- private readonly logger: ILogger,
- ) { }
-
- public async provideDocumentSymbolInformation(document: ITextDocument): Promise<vscode.SymbolInformation[]> {
- this.logger.verbose('DocumentSymbolProvider', `provideDocumentSymbolInformation - ${document.uri}`);
- const toc = await this.tocProvider.getForDocument(document);
- return toc.entries.map(entry => this.toSymbolInformation(entry));
- }
-
- public async provideDocumentSymbols(document: ITextDocument): Promise<vscode.DocumentSymbol[]> {
- const toc = await this.tocProvider.getForDocument(document);
- const root: MarkdownSymbol = {
- level: -Infinity,
- children: [],
- parent: undefined
- };
- this.buildTree(root, toc.entries);
- return root.children;
- }
-
- private buildTree(parent: MarkdownSymbol, entries: readonly TocEntry[]) {
- if (!entries.length) {
- return;
- }
-
- const entry = entries[0];
- const symbol = this.toDocumentSymbol(entry);
- symbol.children = [];
-
- while (entry.level <= parent.level) {
- parent = parent.parent!;
- }
- parent.children.push(symbol);
- this.buildTree({ level: entry.level, children: symbol.children, parent }, entries.slice(1));
- }
-
- private toSymbolInformation(entry: TocEntry): vscode.SymbolInformation {
- return new vscode.SymbolInformation(
- this.getSymbolName(entry),
- vscode.SymbolKind.String,
- '',
- entry.sectionLocation);
- }
-
- private toDocumentSymbol(entry: TocEntry) {
- return new vscode.DocumentSymbol(
- this.getSymbolName(entry),
- '',
- vscode.SymbolKind.String,
- entry.sectionLocation.range,
- entry.sectionLocation.range);
- }
-
- private getSymbolName(entry: TocEntry): string {
- return '#'.repeat(entry.level) + ' ' + entry.text;
- }
-}
diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbols.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbols.ts
deleted file mode 100644
index 1bbef509791..00000000000
--- a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbols.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as vscode from 'vscode';
-import { Disposable } from '../util/dispose';
-import { MdWorkspaceInfoCache } from '../util/workspaceCache';
-import { IMdWorkspace } from '../workspace';
-import { MdDocumentSymbolProvider } from './documentSymbols';
-
-export class MdWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider {
-
- private readonly _cache: MdWorkspaceInfoCache<vscode.SymbolInformation[]>;
-
- public constructor(
- symbolProvider: MdDocumentSymbolProvider,
- workspace: IMdWorkspace,
- ) {
- super();
-
- this._cache = this._register(new MdWorkspaceInfoCache(workspace, doc => symbolProvider.provideDocumentSymbolInformation(doc)));
- }
-
- public async provideWorkspaceSymbols(query: string): Promise<vscode.SymbolInformation[]> {
- const allSymbols = (await this._cache.values()).flat();
- return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1);
- }
-}
-
-export function registerWorkspaceSymbolSupport(
- workspace: IMdWorkspace,
- symbolProvider: MdDocumentSymbolProvider,
-): vscode.Disposable {
- return vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspace));
-}
diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts
deleted file mode 100644
index 3762a5a4ba1..00000000000
--- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-/*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
-
-import * as assert from 'assert';
-import 'mocha';
-import * as vscode from 'vscode';
-import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbols';
-import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbols';
-import { MdTableOfContentsProvider } from '../tableOfContents';
-import { ITextDocument } from '../types/textDocument';
-import { DisposableStore } from '../util/dispose';
-import { InMemoryDocument } from '../util/inMemoryDocument';
-import { IMdWorkspace } from '../workspace';
-import { createNewMarkdownEngine } from './engine';
-import { InMemoryMdWorkspace } from './inMemoryWorkspace';
-import { nulLogger } from './nulLogging';
-import { withStore, workspacePath } from './util';
-
-function getWorkspaceSymbols(store: DisposableStore, workspace: IMdWorkspace, query = ''): Promise<vscode.SymbolInformation[]> {
- const engine = createNewMarkdownEngine();
- const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
- const symbolProvider = new MdDocumentSymbolProvider(tocProvider, nulLogger);
- const workspaceSymbolProvider = store.add(new MdWorkspaceSymbolProvider(symbolProvider, workspace));
- return workspaceSymbolProvider.provideWorkspaceSymbols(query);
-}
-
-suite('markdown.WorkspaceSymbolProvider', () => {
- test('Should not return anything for empty workspace', withStore(async (store) => {
- const workspace = store.add(new InMemoryMdWorkspace([]));
- assert.deepStrictEqual(await getWorkspaceSymbols(store, workspace, ''), []);
- }));
-
- test('Should return symbols from workspace with one markdown file', withStore(async (store) => {
- const workspace = store.add(new InMemoryMdWorkspace([
- new InMemoryDocument(workspacePath('test.md'), `# header1\nabc\n## header2`)
- ]));
-
- const symbols = await getWorkspaceSymbols(store, workspace, '');
- assert.strictEqual(symbols.length, 2);
- assert.strictEqual(symbols[0].name, '# header1');
- assert.strictEqual(symbols[1].name, '## header2');
- }));
-
- test('Should return all content basic workspace', withStore(async (store) => {
- const fileNameCount = 10;
- const files: ITextDocument[] = [];
- for (let i = 0; i < fileNameCount; ++i) {
- const testFileName = workspacePath(`test${i}.md`);
- files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`));
- }
-
- const workspace = store.add(new InMemoryMdWorkspace(files));
-
- const symbols = await getWorkspaceSymbols(store, workspace, '');
- assert.strictEqual(symbols.length, fileNameCount * 2);
- }));
-
- test('Should update results when markdown file changes symbols', withStore(async (store) => {
- const testFileName = workspacePath('test.md');
- const workspace = store.add(new InMemoryMdWorkspace([
- new InMemoryDocument(testFileName, `# header1`, 1 /* version */)
- ]));
-
- assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
-
- // Update file
- workspace.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */));
- const newSymbols = await getWorkspaceSymbols(store, workspace, '');
- assert.strictEqual(newSymbols.length, 2);
- assert.strictEqual(newSymbols[0].name, '# new header');
- assert.strictEqual(newSymbols[1].name, '## header2');
- }));
-
- test('Should remove results when file is deleted', withStore(async (store) => {
- const testFileName = workspacePath('test.md');
-
- const workspace = store.add(new InMemoryMdWorkspace([
- new InMemoryDocument(testFileName, `# header1`)
- ]));
-
- assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
-
- // delete file
- workspace.deleteDocument(testFileName);
- const newSymbols = await getWorkspaceSymbols(store, workspace, '');
- assert.strictEqual(newSymbols.length, 0);
- }));
-
- test('Should update results when markdown file is created', withStore(async (store) => {
- const testFileName = workspacePath('test.md');
-
- const workspace = store.add(new InMemoryMdWorkspace([
- new InMemoryDocument(testFileName, `# header1`)
- ]));
-
- assert.strictEqual((await getWorkspaceSymbols(store, workspace, '')).length, 1);
-
- // Create file
- workspace.createDocument(new InMemoryDocument(workspacePath('test2.md'), `# new header\nabc\n## header2`));
- const newSymbols = await getWorkspaceSymbols(store, workspace, '');
- assert.strictEqual(newSymbols.length, 3);
- }));
-});
diff --git a/extensions/markdown-language-features/src/util/file.ts b/extensions/markdown-language-features/src/util/file.ts
index 6d5f22e95e0..e97ab743929 100644
--- a/extensions/markdown-language-features/src/util/file.ts
+++ b/extensions/markdown-language-features/src/util/file.ts
@@ -6,16 +6,16 @@
import * as vscode from 'vscode';
import * as URI from 'vscode-uri';
-const markdownFileExtensions = Object.freeze<string[]>([
- '.md',
- '.mkd',
- '.mdwn',
- '.mdown',
- '.markdown',
- '.markdn',
- '.mdtxt',
- '.mdtext',
- '.workbook',
+export const markdownFileExtensions = Object.freeze<string[]>([
+ 'md',
+ 'mkd',
+ 'mdwn',
+ 'mdown',
+ 'markdown',
+ 'markdn',
+ 'mdtxt',
+ 'mdtext',
+ 'workbook',
]);
export function isMarkdownFile(document: vscode.TextDocument) {
@@ -23,5 +23,5 @@ export function isMarkdownFile(document: vscode.TextDocument) {
}
export function looksLikeMarkdownPath(resolvedHrefPath: vscode.Uri) {
- return markdownFileExtensions.includes(URI.Utils.extname(resolvedHrefPath).toLowerCase());
+ return markdownFileExtensions.includes(URI.Utils.extname(resolvedHrefPath).toLowerCase().replace('.', ''));
}