Path: blob/main/extensions/markdown-language-features/src/client/client.ts
3292 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as vscode from 'vscode';6import { BaseLanguageClient, LanguageClientOptions, NotebookDocumentSyncRegistrationType, Range, TextEdit } from 'vscode-languageclient';7import { IMdParser } from '../markdownEngine';8import { IDisposable } from '../util/dispose';9import { looksLikeMarkdownPath, markdownFileExtensions, markdownLanguageIds } from '../util/file';10import { FileWatcherManager } from './fileWatchingManager';11import { InMemoryDocument } from './inMemoryDocument';12import * as proto from './protocol';13import { VsCodeMdWorkspace } from './workspace';1415export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;1617export class MdLanguageClient implements IDisposable {1819constructor(20private readonly _client: BaseLanguageClient,21private readonly _workspace: VsCodeMdWorkspace,22) { }2324dispose(): void {25this._client.stop();26this._workspace.dispose();27}2829resolveLinkTarget(linkText: string, uri: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> {30return this._client.sendRequest(proto.resolveLinkTarget, { linkText, uri: uri.toString() });31}3233getEditForFileRenames(files: ReadonlyArray<{ oldUri: string; newUri: string }>, token: vscode.CancellationToken) {34return this._client.sendRequest(proto.getEditForFileRenames, files, token);35}3637getReferencesToFileInWorkspace(resource: vscode.Uri, token: vscode.CancellationToken) {38return this._client.sendRequest(proto.getReferencesToFileInWorkspace, { uri: resource.toString() }, token);39}4041prepareUpdatePastedLinks(doc: vscode.Uri, ranges: readonly vscode.Range[], token: vscode.CancellationToken) {42return this._client.sendRequest(proto.prepareUpdatePastedLinks, {43uri: doc.toString(),44ranges: ranges.map(range => Range.create(range.start.line, range.start.character, range.end.line, range.end.character)),45}, token);46}4748getUpdatePastedLinksEdit(pastingIntoDoc: vscode.Uri, edits: readonly vscode.TextEdit[], metadata: string, token: vscode.CancellationToken) {49return this._client.sendRequest(proto.getUpdatePastedLinksEdit, {50metadata,51pasteIntoDoc: pastingIntoDoc.toString(),52edits: edits.map(edit => TextEdit.replace(edit.range, edit.newText)),53}, token);54}55}5657export async function startClient(factory: LanguageClientConstructor, parser: IMdParser): Promise<MdLanguageClient> {5859const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`;6061const clientOptions: LanguageClientOptions = {62documentSelector: markdownLanguageIds,63synchronize: {64configurationSection: ['markdown'],65fileEvents: vscode.workspace.createFileSystemWatcher(mdFileGlob),66},67initializationOptions: {68markdownFileExtensions,69i10lLocation: vscode.l10n.uri?.toJSON(),70},71diagnosticPullOptions: {72onChange: true,73onTabs: true,74match(_documentSelector, resource) {75return looksLikeMarkdownPath(resource);76},77},78markdown: {79supportHtml: true,80}81};8283const client = factory('markdown', vscode.l10n.t("Markdown Language Server"), clientOptions);8485client.registerProposedFeatures();8687const notebookFeature = client.getFeature(NotebookDocumentSyncRegistrationType.method);88if (notebookFeature !== undefined) {89notebookFeature.register({90id: String(Date.now()),91registerOptions: {92notebookSelector: [{93notebook: '*',94cells: [{ language: 'markdown' }]95}]96}97});98}99100const workspace = new VsCodeMdWorkspace();101102client.onRequest(proto.parse, async (e) => {103const uri = vscode.Uri.parse(e.uri);104if (typeof e.text === 'string') {105return parser.tokenize(new InMemoryDocument(uri, e.text, -1));106} else {107const doc = await workspace.getOrLoadMarkdownDocument(uri);108if (doc) {109return parser.tokenize(doc);110} else {111return [];112}113}114});115116client.onRequest(proto.fs_readFile, async (e): Promise<number[]> => {117const uri = vscode.Uri.parse(e.uri);118return Array.from(await vscode.workspace.fs.readFile(uri));119});120121client.onRequest(proto.fs_stat, async (e): Promise<{ isDirectory: boolean } | undefined> => {122const uri = vscode.Uri.parse(e.uri);123try {124const stat = await vscode.workspace.fs.stat(uri);125return { isDirectory: stat.type === vscode.FileType.Directory };126} catch {127return undefined;128}129});130131client.onRequest(proto.fs_readDirectory, async (e): Promise<[string, { isDirectory: boolean }][]> => {132const uri = vscode.Uri.parse(e.uri);133const result = await vscode.workspace.fs.readDirectory(uri);134return result.map(([name, type]) => [name, { isDirectory: type === vscode.FileType.Directory }]);135});136137client.onRequest(proto.findMarkdownFilesInWorkspace, async (): Promise<string[]> => {138return (await vscode.workspace.findFiles(mdFileGlob, '**/node_modules/**')).map(x => x.toString());139});140141const watchers = new FileWatcherManager();142143client.onRequest(proto.fs_watcher_create, async (params): Promise<void> => {144const id = params.id;145const uri = vscode.Uri.parse(params.uri);146147const sendWatcherChange = (kind: 'create' | 'change' | 'delete') => {148client.sendRequest(proto.fs_watcher_onChange, { id, uri: params.uri, kind });149};150151watchers.create(id, uri, params.watchParentDirs, {152create: params.options.ignoreCreate ? undefined : () => sendWatcherChange('create'),153change: params.options.ignoreChange ? undefined : () => sendWatcherChange('change'),154delete: params.options.ignoreDelete ? undefined : () => sendWatcherChange('delete'),155});156});157158client.onRequest(proto.fs_watcher_delete, async (params): Promise<void> => {159watchers.delete(params.id);160});161162vscode.commands.registerCommand('vscodeMarkdownLanguageservice.open', (uri, args) => {163return vscode.commands.executeCommand('vscode.open', uri, args);164});165166vscode.commands.registerCommand('vscodeMarkdownLanguageservice.rename', (uri, pos) => {167return vscode.commands.executeCommand('editor.action.rename', [vscode.Uri.from(uri), new vscode.Position(pos.line, pos.character)]);168});169170await client.start();171172return new MdLanguageClient(client, workspace);173}174175176