volar-service-markdown 0.0.45 → 0.0.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +3 -2
  2. package/index.js +183 -145
  3. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- import type { DocumentSelector, ProviderResult, ServiceContext, LanguageServicePlugin } from '@volar/language-service';
1
+ import { type DocumentSelector, type LanguageServicePlugin, type ProviderResult, type ServiceContext } from '@volar/language-service';
2
2
  import type { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  import type { DiagnosticOptions, IMdLanguageService } from 'vscode-markdown-languageservice';
4
4
  export interface Provide {
5
5
  'markdown/languageService': () => IMdLanguageService;
6
6
  }
7
- export declare function create({ documentSelector, getDiagnosticOptions, }?: {
7
+ export declare function create({ documentSelector, fileExtensions, getDiagnosticOptions, }?: {
8
8
  documentSelector?: DocumentSelector;
9
+ fileExtensions?: string[];
9
10
  getDiagnosticOptions?(document: TextDocument, context: ServiceContext): ProviderResult<DiagnosticOptions | undefined>;
10
11
  }): LanguageServicePlugin;
11
12
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -7,22 +7,23 @@ const vscode_markdown_languageservice_1 = require("vscode-markdown-languageservi
7
7
  const vscode_uri_1 = require("vscode-uri");
8
8
  const MarkdownIt = require("markdown-it");
9
9
  const md = new MarkdownIt();
10
- function assert(condition, message) {
11
- if (!condition) {
12
- throw new Error(message);
13
- }
14
- }
15
- function create({ documentSelector = ['markdown'], getDiagnosticOptions = async (_document, context) => {
10
+ function create({ documentSelector = ['markdown'], fileExtensions = [
11
+ 'md',
12
+ 'mkd',
13
+ 'mdwn',
14
+ 'mdown',
15
+ 'markdown',
16
+ 'markdn',
17
+ 'mdtxt',
18
+ 'mdtext',
19
+ 'workbook',
20
+ ], getDiagnosticOptions = async (_document, context) => {
16
21
  return await context.env.getConfiguration?.('markdown.validate');
17
22
  }, } = {}) {
18
23
  return {
19
24
  name: 'markdown',
20
25
  triggerCharacters: ['.', '/', '#'],
21
26
  create(context) {
22
- let lastProjectVersion;
23
- const { fs, onDidChangeWatchedFiles } = context.env;
24
- assert(fs, 'context.env.fs must be defined');
25
- assert(onDidChangeWatchedFiles, 'context.env.fs.onDidChangeWatchedFiles must be defined');
26
27
  const logger = {
27
28
  level: vscode_markdown_languageservice_1.LogLevel.Off,
28
29
  log(_logLevel, message) {
@@ -35,136 +36,24 @@ function create({ documentSelector = ['markdown'], getDiagnosticOptions = async
35
36
  return md.parse(document.getText(), {});
36
37
  }
37
38
  };
38
- const onDidChangeMarkdownDocument = new vscode_jsonrpc_1.Emitter();
39
- const onDidCreateMarkdownDocument = new vscode_jsonrpc_1.Emitter();
40
- const onDidDeleteMarkdownDocument = new vscode_jsonrpc_1.Emitter();
41
- const fileWatcher = onDidChangeWatchedFiles(event => {
42
- for (const change of event.changes) {
43
- switch (change.type) {
44
- case 2: {
45
- const document = getTextDocument(change.uri, false);
46
- if (document) {
47
- onDidChangeMarkdownDocument.fire(document);
48
- }
49
- break;
50
- }
51
- case 1: {
52
- const document = getTextDocument(change.uri, false);
53
- if (document) {
54
- onDidCreateMarkdownDocument.fire(document);
55
- }
56
- break;
57
- }
58
- case 3: {
59
- onDidDeleteMarkdownDocument.fire(vscode_uri_1.URI.parse(change.uri));
60
- break;
61
- }
62
- }
63
- }
64
- });
65
- const workspace = {
66
- async getAllMarkdownDocuments() {
67
- sync();
68
- return syncedVersions.values();
69
- },
70
- getContainingDocument() {
71
- return undefined;
72
- },
73
- hasMarkdownDocument(resource) {
74
- const document = getTextDocument(resource.toString(), true);
75
- return Boolean(document && matchDocument(documentSelector, document));
76
- },
77
- onDidChangeMarkdownDocument: onDidChangeMarkdownDocument.event,
78
- onDidCreateMarkdownDocument: onDidCreateMarkdownDocument.event,
79
- onDidDeleteMarkdownDocument: onDidDeleteMarkdownDocument.event,
80
- async openMarkdownDocument(resource) {
81
- return getTextDocument(resource.toString(), true);
82
- },
83
- async readDirectory(resource) {
84
- const directory = await fs.readDirectory(resource.toString());
85
- return directory.map(([fileName, fileType]) => [
86
- fileName,
87
- { isDirectory: fileType === 2 }
88
- ]);
89
- },
90
- async stat(resource) {
91
- const stat = await fs.stat(resource.toString());
92
- if (stat) {
93
- return { isDirectory: stat.type === 2 };
94
- }
95
- },
96
- workspaceFolders: [vscode_uri_1.URI.parse(context.env.workspaceFolder)],
97
- };
39
+ const workspace = getMarkdownWorkspace();
98
40
  const ls = (0, vscode_markdown_languageservice_1.createLanguageService)({
99
41
  logger,
100
42
  parser,
101
- workspace
43
+ workspace: workspace.workspace,
102
44
  });
103
- const syncedVersions = new Map();
104
- const sync = () => {
105
- if (!context.language.typescript) {
106
- return;
107
- }
108
- const { languageServiceHost } = context.language.typescript;
109
- const newProjectVersion = languageServiceHost.getProjectVersion?.();
110
- const shouldUpdate = newProjectVersion === undefined || newProjectVersion !== lastProjectVersion;
111
- if (!shouldUpdate) {
112
- return;
113
- }
114
- lastProjectVersion = newProjectVersion;
115
- const oldVersions = new Set(syncedVersions.keys());
116
- const newVersions = new Map();
117
- for (const fileName of languageServiceHost.getScriptFileNames()) {
118
- const uri = context.env.typescript.fileNameToUri(fileName);
119
- const decoded = context.decodeEmbeddedDocumentUri(uri);
120
- const sourceScript = decoded && context.language.scripts.get(decoded[0]);
121
- if (sourceScript?.generated) {
122
- for (const virtualCode of (0, language_service_1.forEachEmbeddedCode)(sourceScript.generated.root)) {
123
- if (matchDocument(documentSelector, virtualCode)) {
124
- const uri = context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id);
125
- const document = context.documents.get(uri, virtualCode.languageId, virtualCode.snapshot);
126
- newVersions.set(document.uri, document);
127
- }
128
- }
129
- }
130
- else if (sourceScript) {
131
- const document = context.documents.get(fileName, sourceScript.languageId, sourceScript.snapshot);
132
- if (document && matchDocument(documentSelector, document)) {
133
- newVersions.set(document.uri, document);
134
- }
135
- }
136
- }
137
- for (const [uri, document] of Array.from(newVersions)) {
138
- const old = syncedVersions.get(uri);
139
- syncedVersions.set(uri, document);
140
- if (old) {
141
- onDidChangeMarkdownDocument.fire(document);
142
- }
143
- else {
144
- onDidCreateMarkdownDocument.fire(document);
145
- }
146
- }
147
- for (const uri of Array.from(oldVersions)) {
148
- if (!newVersions.has(uri)) {
149
- syncedVersions.delete(uri);
150
- onDidDeleteMarkdownDocument.fire(vscode_uri_1.URI.parse(uri));
151
- }
152
- }
153
- };
154
- const prepare = (document) => {
155
- if (!matchDocument(documentSelector, document)) {
156
- return false;
45
+ const firedDocumentChanges = new Map();
46
+ const fsSourceScripts = new Map();
47
+ const fileWatcher = context.env.onDidChangeWatchedFiles?.(event => {
48
+ for (const change of event.changes) {
49
+ fsSourceScripts.delete(change.uri);
157
50
  }
158
- sync();
159
- return true;
160
- };
51
+ });
161
52
  return {
162
53
  dispose() {
163
54
  ls.dispose();
164
- fileWatcher.dispose();
165
- onDidDeleteMarkdownDocument.dispose();
166
- onDidCreateMarkdownDocument.dispose();
167
- onDidChangeMarkdownDocument.dispose();
55
+ workspace.dispose();
56
+ fileWatcher?.dispose();
168
57
  },
169
58
  provide: {
170
59
  'markdown/languageService': () => ls
@@ -212,9 +101,9 @@ function create({ documentSelector = ['markdown'], getDiagnosticOptions = async
212
101
  return ls.getDocumentHighlights(document, position, token);
213
102
  }
214
103
  },
215
- provideDocumentLinks(document, token) {
104
+ async provideDocumentLinks(document, token) {
216
105
  if (prepare(document)) {
217
- return ls.getDocumentLinks(document, token);
106
+ return await ls.getDocumentLinks(document, token);
218
107
  }
219
108
  },
220
109
  provideDocumentSymbols(document, token) {
@@ -258,26 +147,175 @@ function create({ documentSelector = ['markdown'], getDiagnosticOptions = async
258
147
  }
259
148
  },
260
149
  provideWorkspaceSymbols(query, token) {
261
- sync();
262
150
  return ls.getWorkspaceSymbols(query, token);
263
151
  },
264
152
  async resolveDocumentLink(link, token) {
265
- const result = await ls.resolveDocumentLink(link, token);
266
- return result || link;
153
+ return await ls.resolveDocumentLink(link, token) ?? link;
267
154
  }
268
155
  };
269
- function getTextDocument(uri, includeVirtualFile) {
270
- if (includeVirtualFile) {
271
- const decoded = context.decodeEmbeddedDocumentUri(uri);
272
- const sourceScript = decoded && context.language.scripts.get(decoded[0]);
273
- const virtualCode = decoded && sourceScript?.generated?.embeddedCodes.get(decoded[1]);
156
+ function prepare(document) {
157
+ if (matchDocument(documentSelector, document)) {
158
+ if (firedDocumentChanges.get(document.uri) !== document.version) {
159
+ firedDocumentChanges.set(document.uri, document.version);
160
+ workspace.onDidChangeMarkdownDocument.fire(document);
161
+ }
162
+ return true;
163
+ }
164
+ return false;
165
+ }
166
+ function getMarkdownWorkspace() {
167
+ const onDidChangeMarkdownDocument = new vscode_jsonrpc_1.Emitter();
168
+ const onDidCreateMarkdownDocument = new vscode_jsonrpc_1.Emitter();
169
+ const onDidDeleteMarkdownDocument = new vscode_jsonrpc_1.Emitter();
170
+ const { fs, onDidChangeWatchedFiles } = context.env;
171
+ const fileWatcher = onDidChangeWatchedFiles?.(event => {
172
+ for (const change of event.changes) {
173
+ switch (change.type) {
174
+ case 2: {
175
+ const document = getTextDocument(change.uri);
176
+ if (document) {
177
+ onDidChangeMarkdownDocument.fire(document);
178
+ }
179
+ break;
180
+ }
181
+ case 1: {
182
+ const document = getTextDocument(change.uri);
183
+ if (document) {
184
+ onDidCreateMarkdownDocument.fire(document);
185
+ }
186
+ break;
187
+ }
188
+ case 3: {
189
+ onDidDeleteMarkdownDocument.fire(vscode_uri_1.URI.parse(change.uri));
190
+ break;
191
+ }
192
+ }
193
+ }
194
+ });
195
+ const workspace = {
196
+ async getAllMarkdownDocuments() {
197
+ // TODO: Add opened files (such as untitled files)
198
+ // const openTextDocumentResults = this.documents.all()
199
+ // .filter(doc => this.isRelevantMarkdownDocument(doc));
200
+ return await findMarkdownFilesInWorkspace(vscode_uri_1.URI.parse(context.env.workspaceFolder));
201
+ },
202
+ getContainingDocument(resource) {
203
+ const decoded = context.decodeEmbeddedDocumentUri(resource.toString());
204
+ if (decoded) {
205
+ return {
206
+ uri: vscode_uri_1.URI.parse(decoded[0]),
207
+ children: [],
208
+ };
209
+ }
210
+ },
211
+ hasMarkdownDocument(resource) {
212
+ const document = getTextDocument(resource.toString());
213
+ return Boolean(document && matchDocument(documentSelector, document));
214
+ },
215
+ onDidChangeMarkdownDocument: onDidChangeMarkdownDocument.event,
216
+ onDidCreateMarkdownDocument: onDidCreateMarkdownDocument.event,
217
+ onDidDeleteMarkdownDocument: onDidDeleteMarkdownDocument.event,
218
+ async openMarkdownDocument(resource) {
219
+ return getTextDocument(resource.toString());
220
+ },
221
+ async readDirectory(resource) {
222
+ const directory = await fs?.readDirectory(resource.toString()) ?? [];
223
+ return directory
224
+ .filter(file => file[1] !== 0)
225
+ .map(([fileName, fileType]) => [
226
+ fileName,
227
+ { isDirectory: fileType === 2 }
228
+ ]);
229
+ },
230
+ async stat(resource) {
231
+ const stat = await fs?.stat(resource.toString());
232
+ if (stat?.type === 0) {
233
+ return;
234
+ }
235
+ return { isDirectory: stat?.type === 2 };
236
+ },
237
+ workspaceFolders: [vscode_uri_1.URI.parse(context.env.workspaceFolder)],
238
+ };
239
+ return {
240
+ workspace,
241
+ onDidChangeMarkdownDocument,
242
+ onDidCreateMarkdownDocument,
243
+ onDidDeleteMarkdownDocument,
244
+ dispose() {
245
+ fileWatcher?.dispose();
246
+ onDidDeleteMarkdownDocument.dispose();
247
+ onDidCreateMarkdownDocument.dispose();
248
+ onDidChangeMarkdownDocument.dispose();
249
+ },
250
+ };
251
+ }
252
+ async function findMarkdownFilesInWorkspace(folder) {
253
+ const { fs } = context.env;
254
+ const files = await fs?.readDirectory(folder.toString()) ?? [];
255
+ const docs = [];
256
+ await Promise.all(files.map(async ([fileName, fileType]) => {
257
+ if (fileType === 2 && fileName !== 'node_modules') {
258
+ for (const doc of await findMarkdownFilesInWorkspace(vscode_uri_1.Utils.joinPath(folder, fileName))) {
259
+ docs.push(doc);
260
+ }
261
+ }
262
+ else if (fileExtensions.some(ext => fileName.endsWith('.' + ext))) {
263
+ const fileUri = vscode_uri_1.Utils.joinPath(folder, fileName);
264
+ let sourceScript = context.language.scripts.get(fileUri.toString());
265
+ if (!sourceScript) {
266
+ if (!fsSourceScripts.has(fileUri.toString())) {
267
+ fsSourceScripts.set(fileUri.toString(), undefined);
268
+ const fileContent = await fs?.readFile(fileUri.toString());
269
+ if (fileContent !== undefined) {
270
+ fsSourceScripts.set(fileUri.toString(), context.language.scripts.set(fileUri.toString(), {
271
+ getText(start, end) {
272
+ return fileContent.substring(start, end);
273
+ },
274
+ getLength() {
275
+ return fileContent.length;
276
+ },
277
+ getChangeRange() {
278
+ return undefined;
279
+ },
280
+ }));
281
+ context.language.scripts.delete(fileUri.toString());
282
+ }
283
+ }
284
+ sourceScript = fsSourceScripts.get(fileUri.toString());
285
+ }
286
+ if (sourceScript?.generated) {
287
+ for (const virtualCode of (0, language_service_1.forEachEmbeddedCode)(sourceScript.generated.root)) {
288
+ if (matchDocument(documentSelector, virtualCode)) {
289
+ const uri = context.encodeEmbeddedDocumentUri(sourceScript.id, virtualCode.id);
290
+ const doc = context.documents.get(uri, virtualCode.languageId, virtualCode.snapshot);
291
+ docs.push(doc);
292
+ }
293
+ }
294
+ }
295
+ else if (sourceScript) {
296
+ const doc = context.documents.get(fileName, sourceScript.languageId, sourceScript.snapshot);
297
+ if (doc && matchDocument(documentSelector, doc)) {
298
+ docs.push(doc);
299
+ }
300
+ }
301
+ }
302
+ }));
303
+ return docs;
304
+ }
305
+ function getTextDocument(uri) {
306
+ const decoded = context.decodeEmbeddedDocumentUri(uri);
307
+ if (decoded) {
308
+ const sourceScript = context.language.scripts.get(decoded[0]);
309
+ const virtualCode = sourceScript?.generated?.embeddedCodes.get(decoded[1]);
274
310
  if (virtualCode) {
275
311
  return context.documents.get(uri, virtualCode.languageId, virtualCode.snapshot);
276
312
  }
277
313
  }
278
- const sourceScript = context.language.scripts.get(uri);
279
- if (sourceScript) {
280
- return context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
314
+ else {
315
+ const sourceScript = context.language.scripts.get(uri);
316
+ if (sourceScript) {
317
+ return context.documents.get(uri, sourceScript.languageId, sourceScript.snapshot);
318
+ }
281
319
  }
282
320
  }
283
321
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volar-service-markdown",
3
- "version": "0.0.45",
3
+ "version": "0.0.47",
4
4
  "description": "Integrate vscode-markdown-languageservice into Volar",
5
5
  "homepage": "https://github.com/volarjs/services/tree/master/packages/markdown",
6
6
  "bugs": "https://github.com/volarjs/services/issues",
@@ -41,5 +41,5 @@
41
41
  "optional": true
42
42
  }
43
43
  },
44
- "gitHead": "f5f0ef73116e7fb8a080a245d7fddcbbe4837817"
44
+ "gitHead": "d53f4dfa7007e77409af3ef28cc165e9940e7313"
45
45
  }