Skip to content

Commit 9ee8961

Browse files
authored
Move MD references, rename, and definition support to md LS (microsoft#155127)
1 parent f992a90 commit 9ee8961

23 files changed

+194
-2029
lines changed

extensions/markdown-language-features/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,8 @@
562562
"@types/picomatch": "^2.3.0",
563563
"@types/vscode-notebook-renderer": "^1.60.0",
564564
"@types/vscode-webview": "^1.57.0",
565-
"lodash.throttle": "^4.1.1"
565+
"lodash.throttle": "^4.1.1",
566+
"vscode-languageserver-types": "^3.17.2"
566567
},
567568
"repository": {
568569
"type": "git",

extensions/markdown-language-features/server/.vscode/launch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"name": "Attach",
77
"type": "node",
88
"request": "attach",
9-
"port": 7675,
9+
"port": 7692,
1010
"sourceMaps": true,
1111
"outFiles": ["${workspaceFolder}/out/**/*.js"]
1212
}

extensions/markdown-language-features/server/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"vscode-languageserver": "^8.0.2-next.5`",
1414
"vscode-languageserver-textdocument": "^1.0.5",
1515
"vscode-languageserver-types": "^3.17.1",
16-
"vscode-markdown-languageservice": "^0.0.0-alpha.5",
16+
"vscode-markdown-languageservice": "^0.0.0-alpha.8",
1717
"vscode-uri": "^3.0.3"
1818
},
1919
"devDependencies": {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
export interface LsConfiguration {
7+
/**
8+
* List of file extensions should be considered as markdown.
9+
*
10+
* These should not include the leading `.`.
11+
*/
12+
readonly markdownFileExtensions: readonly string[];
13+
}
14+
15+
const defaultConfig: LsConfiguration = {
16+
markdownFileExtensions: ['md'],
17+
};
18+
19+
export function getLsConfiguration(overrides: Partial<LsConfiguration>): LsConfiguration {
20+
return {
21+
...defaultConfig,
22+
...overrides,
23+
};
24+
}

extensions/markdown-language-features/server/src/protocol.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55

66
import { RequestType } from 'vscode-languageserver';
77
import * as md from 'vscode-markdown-languageservice';
8+
import * as lsp from 'vscode-languageserver-types';
89

10+
// From server
911
export const parseRequestType: RequestType<{ uri: string }, md.Token[], any> = new RequestType('markdown/parse');
10-
1112
export const readFileRequestType: RequestType<{ uri: string }, number[], any> = new RequestType('markdown/readFile');
12-
1313
export const statFileRequestType: RequestType<{ uri: string }, md.FileStat | undefined, any> = new RequestType('markdown/statFile');
14-
1514
export const readDirectoryRequestType: RequestType<{ uri: string }, [string, md.FileStat][], any> = new RequestType('markdown/readDirectory');
16-
1715
export const findFilesRequestTypes: RequestType<{}, string[], any> = new RequestType('markdown/findFiles');
16+
17+
// To server
18+
export const getReferencesToFileInWorkspace = new RequestType<{ uri: string }, lsp.Location[], any>('markdown/getReferencesToFileInWorkspace');

extensions/markdown-language-features/server/src/server.ts

Lines changed: 94 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,51 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Connection, InitializeParams, InitializeResult, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
6+
import { CancellationToken, Connection, InitializeParams, InitializeResult, NotebookDocuments, TextDocuments } from 'vscode-languageserver';
77
import { TextDocument } from 'vscode-languageserver-textdocument';
88
import * as lsp from 'vscode-languageserver-types';
99
import * as md from 'vscode-markdown-languageservice';
1010
import { URI } from 'vscode-uri';
11+
import { getLsConfiguration } from './config';
1112
import { LogFunctionLogger } from './logging';
12-
import { parseRequestType } from './protocol';
13+
import * as protocol from './protocol';
1314
import { VsCodeClientWorkspace } from './workspace';
1415

1516
export async function startServer(connection: Connection) {
1617
const documents = new TextDocuments(TextDocument);
1718
const notebooks = new NotebookDocuments(documents);
1819

1920
connection.onInitialize((params: InitializeParams): InitializeResult => {
21+
const parser = new class implements md.IMdParser {
22+
slugifier = md.githubSlugifier;
23+
24+
async tokenize(document: md.ITextDocument): Promise<md.Token[]> {
25+
return await connection.sendRequest(protocol.parseRequestType, { uri: document.uri.toString() });
26+
}
27+
};
28+
29+
const config = getLsConfiguration({
30+
markdownFileExtensions: params.initializationOptions.markdownFileExtensions,
31+
});
32+
33+
const workspace = new VsCodeClientWorkspace(connection, config, documents, notebooks);
34+
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console));
35+
provider = md.createLanguageService({
36+
workspace,
37+
parser,
38+
logger,
39+
markdownFileExtensions: config.markdownFileExtensions,
40+
});
41+
2042
workspace.workspaceFolders = (params.workspaceFolders ?? []).map(x => URI.parse(x.uri));
2143
return {
2244
capabilities: {
45+
completionProvider: { triggerCharacters: ['.', '/', '#'] },
46+
definitionProvider: true,
2347
documentLinkProvider: { resolveProvider: true },
2448
documentSymbolProvider: true,
25-
completionProvider: { triggerCharacters: ['.', '/', '#'] },
2649
foldingRangeProvider: true,
50+
renameProvider: { prepareProvider: true, },
2751
selectionRangeProvider: true,
2852
workspaceSymbolProvider: true,
2953
workspace: {
@@ -36,23 +60,14 @@ export async function startServer(connection: Connection) {
3660
};
3761
});
3862

39-
const parser = new class implements md.IMdParser {
40-
slugifier = md.githubSlugifier;
41-
42-
async tokenize(document: md.ITextDocument): Promise<md.Token[]> {
43-
return await connection.sendRequest(parseRequestType, { uri: document.uri.toString() });
44-
}
45-
};
4663

47-
const workspace = new VsCodeClientWorkspace(connection, documents, notebooks);
48-
const logger = new LogFunctionLogger(connection.console.log.bind(connection.console));
49-
const provider = md.createLanguageService({ workspace, parser, logger });
64+
let provider: md.IMdLanguageService | undefined;
5065

5166
connection.onDocumentLinks(async (params, token): Promise<lsp.DocumentLink[]> => {
5267
try {
5368
const document = documents.get(params.textDocument.uri);
5469
if (document) {
55-
return await provider.getDocumentLinks(document, token);
70+
return await provider!.getDocumentLinks(document, token);
5671
}
5772
} catch (e) {
5873
console.error(e.stack);
@@ -62,7 +77,7 @@ export async function startServer(connection: Connection) {
6277

6378
connection.onDocumentLinkResolve(async (link, token): Promise<lsp.DocumentLink | undefined> => {
6479
try {
65-
return await provider.resolveDocumentLink(link, token);
80+
return await provider!.resolveDocumentLink(link, token);
6681
} catch (e) {
6782
console.error(e.stack);
6883
}
@@ -73,7 +88,7 @@ export async function startServer(connection: Connection) {
7388
try {
7489
const document = documents.get(params.textDocument.uri);
7590
if (document) {
76-
return await provider.getDocumentSymbols(document, token);
91+
return await provider!.getDocumentSymbols(document, token);
7792
}
7893
} catch (e) {
7994
console.error(e.stack);
@@ -85,7 +100,7 @@ export async function startServer(connection: Connection) {
85100
try {
86101
const document = documents.get(params.textDocument.uri);
87102
if (document) {
88-
return await provider.getFoldingRanges(document, token);
103+
return await provider!.getFoldingRanges(document, token);
89104
}
90105
} catch (e) {
91106
console.error(e.stack);
@@ -97,7 +112,7 @@ export async function startServer(connection: Connection) {
97112
try {
98113
const document = documents.get(params.textDocument.uri);
99114
if (document) {
100-
return await provider.getSelectionRanges(document, params.positions, token);
115+
return await provider!.getSelectionRanges(document, params.positions, token);
101116
}
102117
} catch (e) {
103118
console.error(e.stack);
@@ -107,7 +122,7 @@ export async function startServer(connection: Connection) {
107122

108123
connection.onWorkspaceSymbol(async (params, token): Promise<lsp.WorkspaceSymbol[]> => {
109124
try {
110-
return await provider.getWorkspaceSymbols(params.query, token);
125+
return await provider!.getWorkspaceSymbols(params.query, token);
111126
} catch (e) {
112127
console.error(e.stack);
113128
}
@@ -118,14 +133,73 @@ export async function startServer(connection: Connection) {
118133
try {
119134
const document = documents.get(params.textDocument.uri);
120135
if (document) {
121-
return await provider.getCompletionItems(document, params.position, params.context!, token);
136+
return await provider!.getCompletionItems(document, params.position, params.context!, token);
137+
}
138+
} catch (e) {
139+
console.error(e.stack);
140+
}
141+
return [];
142+
});
143+
144+
connection.onReferences(async (params, token): Promise<lsp.Location[]> => {
145+
try {
146+
const document = documents.get(params.textDocument.uri);
147+
if (document) {
148+
return await provider!.getReferences(document, params.position, params.context, token);
122149
}
123150
} catch (e) {
124151
console.error(e.stack);
125152
}
126153
return [];
127154
});
128155

156+
connection.onDefinition(async (params, token): Promise<lsp.Definition | undefined> => {
157+
try {
158+
const document = documents.get(params.textDocument.uri);
159+
if (document) {
160+
return await provider!.getDefinition(document, params.position, token);
161+
}
162+
} catch (e) {
163+
console.error(e.stack);
164+
}
165+
return undefined;
166+
});
167+
168+
connection.onPrepareRename(async (params, token) => {
169+
try {
170+
const document = documents.get(params.textDocument.uri);
171+
if (document) {
172+
return await provider!.prepareRename(document, params.position, token);
173+
}
174+
} catch (e) {
175+
console.error(e.stack);
176+
}
177+
return undefined;
178+
});
179+
180+
connection.onRenameRequest(async (params, token) => {
181+
try {
182+
const document = documents.get(params.textDocument.uri);
183+
if (document) {
184+
const edit = await provider!.getRenameEdit(document, params.position, params.newName, token);
185+
console.log(JSON.stringify(edit));
186+
return edit;
187+
}
188+
} catch (e) {
189+
console.error(e.stack);
190+
}
191+
return undefined;
192+
});
193+
194+
connection.onRequest(protocol.getReferencesToFileInWorkspace, (async (params: { uri: string }, token: CancellationToken) => {
195+
try {
196+
return await provider!.getFileReferences(URI.parse(params.uri), token);
197+
} catch (e) {
198+
console.error(e.stack);
199+
}
200+
return undefined;
201+
}));
202+
129203
documents.listen(connection);
130204
notebooks.listen(connection);
131205
connection.listen();

extensions/markdown-language-features/server/src/util/file.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,13 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { TextDocument } from 'vscode-languageserver-textdocument';
7-
import * as URI from 'vscode-uri';
7+
import { URI, Utils } from 'vscode-uri';
8+
import { LsConfiguration } from '../config';
89

9-
const markdownFileExtensions = Object.freeze<string[]>([
10-
'.md',
11-
'.mkd',
12-
'.mdwn',
13-
'.mdown',
14-
'.markdown',
15-
'.markdn',
16-
'.mdtxt',
17-
'.mdtext',
18-
'.workbook',
19-
]);
20-
21-
export function looksLikeMarkdownPath(resolvedHrefPath: URI.URI) {
22-
return markdownFileExtensions.includes(URI.Utils.extname(URI.URI.from(resolvedHrefPath)).toLowerCase());
10+
export function looksLikeMarkdownPath(config: LsConfiguration, resolvedHrefPath: URI) {
11+
return config.markdownFileExtensions.includes(Utils.extname(URI.from(resolvedHrefPath)).toLowerCase().replace('.', ''));
2312
}
2413

25-
export function isMarkdownDocument(document: TextDocument): boolean {
14+
export function isMarkdownFile(document: TextDocument) {
2615
return document.languageId === 'markdown';
2716
}

extensions/markdown-language-features/server/src/workspace.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ import { TextDocument } from 'vscode-languageserver-textdocument';
88
import * as md from 'vscode-markdown-languageservice';
99
import { ContainingDocumentContext } from 'vscode-markdown-languageservice/out/workspace';
1010
import { URI } from 'vscode-uri';
11+
import { LsConfiguration } from './config';
1112
import * as protocol from './protocol';
1213
import { coalesce } from './util/arrays';
13-
import { isMarkdownDocument, looksLikeMarkdownPath } from './util/file';
14+
import { isMarkdownFile, looksLikeMarkdownPath } from './util/file';
1415
import { Limiter } from './util/limiter';
1516
import { ResourceMap } from './util/resourceMap';
1617
import { Schemes } from './util/schemes';
@@ -34,6 +35,7 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
3435

3536
constructor(
3637
private readonly connection: Connection,
38+
private readonly config: LsConfiguration,
3739
private readonly documents: TextDocuments<TextDocument>,
3840
private readonly notebooks: NotebookDocuments<TextDocument>,
3941
) {
@@ -141,7 +143,7 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
141143
return matchingDocument;
142144
}
143145

144-
if (!looksLikeMarkdownPath(resource)) {
146+
if (!looksLikeMarkdownPath(this.config, resource)) {
145147
return undefined;
146148
}
147149

@@ -182,6 +184,6 @@ export class VsCodeClientWorkspace implements md.IWorkspace {
182184
}
183185

184186
private isRelevantMarkdownDocument(doc: TextDocument) {
185-
return isMarkdownDocument(doc) && URI.parse(doc.uri).scheme !== 'vscode-bulkeditpreview';
187+
return isMarkdownFile(doc) && URI.parse(doc.uri).scheme !== 'vscode-bulkeditpreview';
186188
}
187189
}

extensions/markdown-language-features/server/yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ vscode-languageserver@^8.0.2-next.5`:
4242
dependencies:
4343
vscode-languageserver-protocol "3.17.2-next.6"
4444

45-
vscode-markdown-languageservice@^0.0.0-alpha.5:
46-
version "0.0.0-alpha.5"
47-
resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.0.0-alpha.5.tgz#fb3042f3ee79589606154c19b15565541337bceb"
48-
integrity sha512-vy8UVa1jtm3CwkifRn3fEWM710JC4AYEECNd5KQthSCoFSfT5pOshJNFWs5yzBeVrohiy4deOdhSrfbDMg/Hyg==
45+
vscode-markdown-languageservice@^0.0.0-alpha.8:
46+
version "0.0.0-alpha.8"
47+
resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.0.0-alpha.8.tgz#05d4f86cf0514fd71479847eef742fcc8cdbe87f"
48+
integrity sha512-si8weZsY4LtyonyZwxpFYk8WucRFiKJisErNTt1HDjUCglSDIZqsMNuMIcz3t0nVNfG0LrpdMFVLGhmET5D71Q==
4949
dependencies:
5050
vscode-languageserver-textdocument "^1.0.5"
5151
vscode-languageserver-types "^3.17.1"

extensions/markdown-language-features/src/client.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ export type LanguageClientConstructor = (name: string, description: string, clie
2424

2525
export async function startClient(factory: LanguageClientConstructor, workspace: IMdWorkspace, parser: IMdParser): Promise<BaseLanguageClient> {
2626

27-
const documentSelector = ['markdown'];
2827
const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`;
2928

3029
const clientOptions: LanguageClientOptions = {
31-
documentSelector,
30+
documentSelector: [{ language: 'markdown' }],
3231
synchronize: {
3332
configurationSection: ['markdown'],
3433
fileEvents: vscode.workspace.createFileSystemWatcher(mdFileGlob),
3534
},
35+
initializationOptions: {
36+
markdownFileExtensions,
37+
}
3638
};
3739

3840
const client = factory('markdown', localize('markdownServer.name', 'Markdown Language Server'), clientOptions);

0 commit comments

Comments
 (0)