mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-23 19:59:37 +00:00
Clean up structure of markdown extension (#161148)
- Move things related to the client under `client` - Remove extra abstractions that are no longer used - Add MdLanguageClient type
This commit is contained in:
@@ -604,7 +604,6 @@
|
|||||||
"morphdom": "^2.6.1",
|
"morphdom": "^2.6.1",
|
||||||
"picomatch": "^2.3.1",
|
"picomatch": "^2.3.1",
|
||||||
"vscode-languageclient": "^8.0.2",
|
"vscode-languageclient": "^8.0.2",
|
||||||
"vscode-languageserver-textdocument": "^1.0.4",
|
|
||||||
"vscode-nls": "^5.1.0",
|
"vscode-nls": "^5.1.0",
|
||||||
"vscode-uri": "^3.0.3"
|
"vscode-uri": "^3.0.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,22 +5,44 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient, LanguageClientOptions, NotebookDocumentSyncRegistrationType } from 'vscode-languageclient';
|
import { BaseLanguageClient, LanguageClientOptions, NotebookDocumentSyncRegistrationType } from 'vscode-languageclient';
|
||||||
import { disposeAll, IDisposable } from 'vscode-markdown-languageservice/out/util/dispose';
|
|
||||||
import { ResourceMap } from 'vscode-markdown-languageservice/out/util/resourceMap';
|
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { Utils } from 'vscode-uri';
|
import { IMdParser } from '../markdownEngine';
|
||||||
import { IMdParser } from './markdownEngine';
|
|
||||||
import * as proto from './protocol';
|
import * as proto from './protocol';
|
||||||
import { looksLikeMarkdownPath, markdownFileExtensions } from './util/file';
|
import { looksLikeMarkdownPath, markdownFileExtensions } from '../util/file';
|
||||||
import { Schemes } from './util/schemes';
|
import { VsCodeMdWorkspace } from './workspace';
|
||||||
import { IMdWorkspace } from './workspace';
|
import { FileWatcherManager } from './fileWatchingManager';
|
||||||
|
import { IDisposable } from '../util/dispose';
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
|
export type LanguageClientConstructor = (name: string, description: string, clientOptions: LanguageClientOptions) => BaseLanguageClient;
|
||||||
|
|
||||||
|
export class MdLanguageClient implements IDisposable {
|
||||||
|
|
||||||
export async function startClient(factory: LanguageClientConstructor, workspace: IMdWorkspace, parser: IMdParser): Promise<BaseLanguageClient> {
|
constructor(
|
||||||
|
private readonly _client: BaseLanguageClient,
|
||||||
|
private readonly _workspace: VsCodeMdWorkspace,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
dispose(): void {
|
||||||
|
this._client.stop();
|
||||||
|
this._workspace.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveLinkTarget(linkText: string, uri: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> {
|
||||||
|
return this._client.sendRequest(proto.resolveLinkTarget, { linkText, uri: uri.toString() });
|
||||||
|
}
|
||||||
|
|
||||||
|
getEditForFileRenames(files: ReadonlyArray<{ oldUri: string; newUri: string }>, token: vscode.CancellationToken) {
|
||||||
|
return this._client.sendRequest(proto.getEditForFileRenames, files, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
getReferencesToFileInWorkspace(resource: vscode.Uri, token: vscode.CancellationToken) {
|
||||||
|
return this._client.sendRequest(proto.getReferencesToFileInWorkspace, { uri: resource.toString() }, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function startClient(factory: LanguageClientConstructor, parser: IMdParser): Promise<MdLanguageClient> {
|
||||||
|
|
||||||
const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`;
|
const mdFileGlob = `**/*.{${markdownFileExtensions.join(',')}}`;
|
||||||
|
|
||||||
@@ -59,6 +81,8 @@ export async function startClient(factory: LanguageClientConstructor, workspace:
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const workspace = new VsCodeMdWorkspace();
|
||||||
|
|
||||||
client.onRequest(proto.parse, async (e) => {
|
client.onRequest(proto.parse, async (e) => {
|
||||||
const uri = vscode.Uri.parse(e.uri);
|
const uri = vscode.Uri.parse(e.uri);
|
||||||
const doc = await workspace.getOrLoadMarkdownDocument(uri);
|
const doc = await workspace.getOrLoadMarkdownDocument(uri);
|
||||||
@@ -125,93 +149,5 @@ export async function startClient(factory: LanguageClientConstructor, workspace:
|
|||||||
|
|
||||||
await client.start();
|
await client.start();
|
||||||
|
|
||||||
return client;
|
return new MdLanguageClient(client, workspace);
|
||||||
}
|
|
||||||
|
|
||||||
type DirWatcherEntry = {
|
|
||||||
readonly uri: vscode.Uri;
|
|
||||||
readonly listeners: IDisposable[];
|
|
||||||
};
|
|
||||||
|
|
||||||
class FileWatcherManager {
|
|
||||||
|
|
||||||
private readonly fileWatchers = new Map<number, {
|
|
||||||
readonly watcher: vscode.FileSystemWatcher;
|
|
||||||
readonly dirWatchers: DirWatcherEntry[];
|
|
||||||
}>();
|
|
||||||
|
|
||||||
private readonly dirWatchers = new ResourceMap<{
|
|
||||||
readonly watcher: vscode.FileSystemWatcher;
|
|
||||||
refCount: number;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void {
|
|
||||||
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete);
|
|
||||||
const parentDirWatchers: DirWatcherEntry[] = [];
|
|
||||||
this.fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers });
|
|
||||||
|
|
||||||
if (listeners.create) { watcher.onDidCreate(listeners.create); }
|
|
||||||
if (listeners.change) { watcher.onDidChange(listeners.change); }
|
|
||||||
if (listeners.delete) { watcher.onDidDelete(listeners.delete); }
|
|
||||||
|
|
||||||
if (watchParentDirs && uri.scheme !== Schemes.untitled) {
|
|
||||||
// We need to watch the parent directories too for when these are deleted / created
|
|
||||||
for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) {
|
|
||||||
const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] };
|
|
||||||
|
|
||||||
let parentDirWatcher = this.dirWatchers.get(dirUri);
|
|
||||||
if (!parentDirWatcher) {
|
|
||||||
const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri));
|
|
||||||
const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete);
|
|
||||||
parentDirWatcher = { refCount: 0, watcher: parentWatcher };
|
|
||||||
this.dirWatchers.set(dirUri, parentDirWatcher);
|
|
||||||
}
|
|
||||||
parentDirWatcher.refCount++;
|
|
||||||
|
|
||||||
if (listeners.create) {
|
|
||||||
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidCreate(async () => {
|
|
||||||
// Just because the parent dir was created doesn't mean our file was created
|
|
||||||
try {
|
|
||||||
const stat = await vscode.workspace.fs.stat(uri);
|
|
||||||
if (stat.type === vscode.FileType.File) {
|
|
||||||
listeners.create!();
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Noop
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listeners.delete) {
|
|
||||||
// When the parent dir is deleted, consider our file deleted too
|
|
||||||
|
|
||||||
// TODO: this fires if the file previously did not exist and then the parent is deleted
|
|
||||||
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidDelete(listeners.delete));
|
|
||||||
}
|
|
||||||
|
|
||||||
parentDirWatchers.push(dirWatcher);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(id: number): void {
|
|
||||||
const entry = this.fileWatchers.get(id);
|
|
||||||
if (entry) {
|
|
||||||
for (const dirWatcher of entry.dirWatchers) {
|
|
||||||
disposeAll(dirWatcher.listeners);
|
|
||||||
|
|
||||||
const dirWatcherEntry = this.dirWatchers.get(dirWatcher.uri);
|
|
||||||
if (dirWatcherEntry) {
|
|
||||||
if (--dirWatcherEntry.refCount <= 0) {
|
|
||||||
dirWatcherEntry.watcher.dispose();
|
|
||||||
this.dirWatchers.delete(dirWatcher.uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.watcher.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.fileWatchers.delete(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { disposeAll } from 'vscode-markdown-languageservice/out/util/dispose';
|
||||||
|
import { ResourceMap } from 'vscode-markdown-languageservice/out/util/resourceMap';
|
||||||
|
import { Utils } from 'vscode-uri';
|
||||||
|
import { IDisposable } from '../util/dispose';
|
||||||
|
import { Schemes } from '../util/schemes';
|
||||||
|
|
||||||
|
type DirWatcherEntry = {
|
||||||
|
readonly uri: vscode.Uri;
|
||||||
|
readonly listeners: IDisposable[];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export class FileWatcherManager {
|
||||||
|
|
||||||
|
private readonly fileWatchers = new Map<number, {
|
||||||
|
readonly watcher: vscode.FileSystemWatcher;
|
||||||
|
readonly dirWatchers: DirWatcherEntry[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
private readonly dirWatchers = new ResourceMap<{
|
||||||
|
readonly watcher: vscode.FileSystemWatcher;
|
||||||
|
refCount: number;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void {
|
||||||
|
const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete);
|
||||||
|
const parentDirWatchers: DirWatcherEntry[] = [];
|
||||||
|
this.fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers });
|
||||||
|
|
||||||
|
if (listeners.create) { watcher.onDidCreate(listeners.create); }
|
||||||
|
if (listeners.change) { watcher.onDidChange(listeners.change); }
|
||||||
|
if (listeners.delete) { watcher.onDidDelete(listeners.delete); }
|
||||||
|
|
||||||
|
if (watchParentDirs && uri.scheme !== Schemes.untitled) {
|
||||||
|
// We need to watch the parent directories too for when these are deleted / created
|
||||||
|
for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) {
|
||||||
|
const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] };
|
||||||
|
|
||||||
|
let parentDirWatcher = this.dirWatchers.get(dirUri);
|
||||||
|
if (!parentDirWatcher) {
|
||||||
|
const glob = new vscode.RelativePattern(Utils.dirname(dirUri), Utils.basename(dirUri));
|
||||||
|
const parentWatcher = vscode.workspace.createFileSystemWatcher(glob, !listeners.create, true, !listeners.delete);
|
||||||
|
parentDirWatcher = { refCount: 0, watcher: parentWatcher };
|
||||||
|
this.dirWatchers.set(dirUri, parentDirWatcher);
|
||||||
|
}
|
||||||
|
parentDirWatcher.refCount++;
|
||||||
|
|
||||||
|
if (listeners.create) {
|
||||||
|
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidCreate(async () => {
|
||||||
|
// Just because the parent dir was created doesn't mean our file was created
|
||||||
|
try {
|
||||||
|
const stat = await vscode.workspace.fs.stat(uri);
|
||||||
|
if (stat.type === vscode.FileType.File) {
|
||||||
|
listeners.create!();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Noop
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listeners.delete) {
|
||||||
|
// When the parent dir is deleted, consider our file deleted too
|
||||||
|
// TODO: this fires if the file previously did not exist and then the parent is deleted
|
||||||
|
dirWatcher.listeners.push(parentDirWatcher.watcher.onDidDelete(listeners.delete));
|
||||||
|
}
|
||||||
|
|
||||||
|
parentDirWatchers.push(dirWatcher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(id: number): void {
|
||||||
|
const entry = this.fileWatchers.get(id);
|
||||||
|
if (entry) {
|
||||||
|
for (const dirWatcher of entry.dirWatchers) {
|
||||||
|
disposeAll(dirWatcher.listeners);
|
||||||
|
|
||||||
|
const dirWatcherEntry = this.dirWatchers.get(dirWatcher.uri);
|
||||||
|
if (dirWatcherEntry) {
|
||||||
|
if (--dirWatcherEntry.refCount <= 0) {
|
||||||
|
dirWatcherEntry.watcher.dispose();
|
||||||
|
this.dirWatchers.delete(dirWatcher.uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.watcher.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.fileWatchers.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,31 +4,17 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
||||||
import { ITextDocument } from '../types/textDocument';
|
import { ITextDocument } from '../types/textDocument';
|
||||||
|
|
||||||
export class InMemoryDocument implements ITextDocument {
|
export class InMemoryDocument implements ITextDocument {
|
||||||
|
|
||||||
private readonly _doc: TextDocument;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public readonly uri: vscode.Uri, contents: string,
|
public readonly uri: vscode.Uri,
|
||||||
|
private readonly contents: string,
|
||||||
public readonly version = 0,
|
public readonly version = 0,
|
||||||
) {
|
) { }
|
||||||
|
|
||||||
this._doc = TextDocument.create(uri.toString(), 'markdown', version, contents);
|
getText(): string {
|
||||||
}
|
return this.contents;
|
||||||
|
|
||||||
get lineCount(): number {
|
|
||||||
return this._doc.lineCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
positionAt(offset: number): vscode.Position {
|
|
||||||
const pos = this._doc.positionAt(offset);
|
|
||||||
return new vscode.Position(pos.line, pos.character);
|
|
||||||
}
|
|
||||||
|
|
||||||
getText(range?: vscode.Range): string {
|
|
||||||
return this._doc.getText(range);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,25 +4,18 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { ITextDocument } from './types/textDocument';
|
import { ITextDocument } from '../types/textDocument';
|
||||||
import { Disposable } from './util/dispose';
|
import { Disposable } from '../util/dispose';
|
||||||
import { isMarkdownFile, looksLikeMarkdownPath } from './util/file';
|
import { isMarkdownFile, looksLikeMarkdownPath } from '../util/file';
|
||||||
import { InMemoryDocument } from './util/inMemoryDocument';
|
import { InMemoryDocument } from './inMemoryDocument';
|
||||||
import { ResourceMap } from './util/resourceMap';
|
import { ResourceMap } from '../util/resourceMap';
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides set of markdown files in the current workspace.
|
|
||||||
*/
|
|
||||||
export interface IMdWorkspace {
|
|
||||||
getOrLoadMarkdownDocument(resource: vscode.Uri): Promise<ITextDocument | undefined>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides set of markdown files known to VS Code.
|
* Provides set of markdown files known to VS Code.
|
||||||
*
|
*
|
||||||
* This includes both opened text documents and markdown files in the workspace.
|
* This includes both opened text documents and markdown files in the workspace.
|
||||||
*/
|
*/
|
||||||
export class VsCodeMdWorkspace extends Disposable implements IMdWorkspace {
|
export class VsCodeMdWorkspace extends Disposable {
|
||||||
|
|
||||||
private _watcher: vscode.FileSystemWatcher | undefined;
|
private _watcher: vscode.FileSystemWatcher | undefined;
|
||||||
|
|
||||||
@@ -4,14 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient, LanguageClient, LanguageClientOptions } from 'vscode-languageclient/browser';
|
import { LanguageClient, LanguageClientOptions } from 'vscode-languageclient/browser';
|
||||||
import { startClient } from './client';
|
import { MdLanguageClient, startClient } from './client/client';
|
||||||
import { activateShared } from './extension.shared';
|
import { activateShared } from './extension.shared';
|
||||||
import { VsCodeOutputLogger } from './logging';
|
import { VsCodeOutputLogger } from './logging';
|
||||||
import { IMdParser, MarkdownItEngine } from './markdownEngine';
|
import { IMdParser, MarkdownItEngine } from './markdownEngine';
|
||||||
import { getMarkdownExtensionContributions } from './markdownExtensions';
|
import { getMarkdownExtensionContributions } from './markdownExtensions';
|
||||||
import { githubSlugifier } from './slugify';
|
import { githubSlugifier } from './slugify';
|
||||||
import { IMdWorkspace, VsCodeMdWorkspace } from './workspace';
|
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
const contributions = getMarkdownExtensionContributions(context);
|
const contributions = getMarkdownExtensionContributions(context);
|
||||||
@@ -22,21 +21,16 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
|
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
|
||||||
|
|
||||||
const workspace = new VsCodeMdWorkspace();
|
const client = await startServer(context, engine);
|
||||||
context.subscriptions.push(workspace);
|
context.subscriptions.push(client);
|
||||||
|
|
||||||
const client = await startServer(context, workspace, engine);
|
|
||||||
context.subscriptions.push({
|
|
||||||
dispose: () => client.stop()
|
|
||||||
});
|
|
||||||
activateShared(context, client, engine, logger, contributions);
|
activateShared(context, client, engine, logger, contributions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise<BaseLanguageClient> {
|
function startServer(context: vscode.ExtensionContext, parser: IMdParser): Promise<MdLanguageClient> {
|
||||||
const serverMain = vscode.Uri.joinPath(context.extensionUri, 'server/dist/browser/main.js');
|
const serverMain = vscode.Uri.joinPath(context.extensionUri, 'server/dist/browser/main.js');
|
||||||
const worker = new Worker(serverMain.toString());
|
const worker = new Worker(serverMain.toString());
|
||||||
|
|
||||||
return startClient((id: string, name: string, clientOptions: LanguageClientOptions) => {
|
return startClient((id: string, name: string, clientOptions: LanguageClientOptions) => {
|
||||||
return new LanguageClient(id, name, clientOptions, worker);
|
return new LanguageClient(id, name, clientOptions, worker);
|
||||||
}, workspace, parser);
|
}, parser);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient } from 'vscode-languageclient';
|
import { MdLanguageClient } from './client/client';
|
||||||
import { CommandManager } from './commandManager';
|
import { CommandManager } from './commandManager';
|
||||||
import * as commands from './commands/index';
|
import * as commands from './commands/index';
|
||||||
import { registerPasteSupport } from './languageFeatures/copyPaste';
|
import { registerPasteSupport } from './languageFeatures/copyPaste';
|
||||||
@@ -23,7 +23,7 @@ import { MdLinkOpener } from './util/openDocumentLink';
|
|||||||
|
|
||||||
export function activateShared(
|
export function activateShared(
|
||||||
context: vscode.ExtensionContext,
|
context: vscode.ExtensionContext,
|
||||||
client: BaseLanguageClient,
|
client: MdLanguageClient,
|
||||||
engine: MarkdownItEngine,
|
engine: MarkdownItEngine,
|
||||||
logger: ILogger,
|
logger: ILogger,
|
||||||
contributions: MarkdownContributionProvider,
|
contributions: MarkdownContributionProvider,
|
||||||
@@ -49,7 +49,7 @@ export function activateShared(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerMarkdownLanguageFeatures(
|
function registerMarkdownLanguageFeatures(
|
||||||
client: BaseLanguageClient,
|
client: MdLanguageClient,
|
||||||
commandManager: CommandManager,
|
commandManager: CommandManager,
|
||||||
): vscode.Disposable {
|
): vscode.Disposable {
|
||||||
const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' };
|
const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' };
|
||||||
|
|||||||
@@ -4,14 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient, LanguageClient, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
import { LanguageClient, ServerOptions, TransportKind } from 'vscode-languageclient/node';
|
||||||
import { startClient } from './client';
|
import { MdLanguageClient, startClient } from './client/client';
|
||||||
import { activateShared } from './extension.shared';
|
import { activateShared } from './extension.shared';
|
||||||
import { VsCodeOutputLogger } from './logging';
|
import { VsCodeOutputLogger } from './logging';
|
||||||
import { IMdParser, MarkdownItEngine } from './markdownEngine';
|
import { IMdParser, MarkdownItEngine } from './markdownEngine';
|
||||||
import { getMarkdownExtensionContributions } from './markdownExtensions';
|
import { getMarkdownExtensionContributions } from './markdownExtensions';
|
||||||
import { githubSlugifier } from './slugify';
|
import { githubSlugifier } from './slugify';
|
||||||
import { IMdWorkspace, VsCodeMdWorkspace } from './workspace';
|
|
||||||
|
|
||||||
export async function activate(context: vscode.ExtensionContext) {
|
export async function activate(context: vscode.ExtensionContext) {
|
||||||
const contributions = getMarkdownExtensionContributions(context);
|
const contributions = getMarkdownExtensionContributions(context);
|
||||||
@@ -22,17 +21,12 @@ export async function activate(context: vscode.ExtensionContext) {
|
|||||||
|
|
||||||
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
|
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
|
||||||
|
|
||||||
const workspace = new VsCodeMdWorkspace();
|
const client = await startServer(context, engine);
|
||||||
context.subscriptions.push(workspace);
|
context.subscriptions.push(client);
|
||||||
|
|
||||||
const client = await startServer(context, workspace, engine);
|
|
||||||
context.subscriptions.push({
|
|
||||||
dispose: () => client.stop()
|
|
||||||
});
|
|
||||||
activateShared(context, client, engine, logger, contributions);
|
activateShared(context, client, engine, logger, contributions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise<BaseLanguageClient> {
|
function startServer(context: vscode.ExtensionContext, parser: IMdParser): Promise<MdLanguageClient> {
|
||||||
const clientMain = vscode.extensions.getExtension('vscode.markdown-language-features')?.packageJSON?.main || '';
|
const clientMain = vscode.extensions.getExtension('vscode.markdown-language-features')?.packageJSON?.main || '';
|
||||||
|
|
||||||
const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/main`;
|
const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/main`;
|
||||||
@@ -49,5 +43,5 @@ function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace,
|
|||||||
};
|
};
|
||||||
return startClient((id, name, clientOptions) => {
|
return startClient((id, name, clientOptions) => {
|
||||||
return new LanguageClient(id, name, serverOptions, clientOptions);
|
return new LanguageClient(id, name, serverOptions, clientOptions);
|
||||||
}, workspace, parser);
|
}, parser);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient } from 'vscode-languageclient';
|
|
||||||
import type * as lsp from 'vscode-languageserver-types';
|
import type * as lsp from 'vscode-languageserver-types';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
import { MdLanguageClient } from '../client/client';
|
||||||
import { Command, CommandManager } from '../commandManager';
|
import { Command, CommandManager } from '../commandManager';
|
||||||
import { getReferencesToFileInWorkspace } from '../protocol';
|
|
||||||
|
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
@@ -18,7 +17,7 @@ export class FindFileReferencesCommand implements Command {
|
|||||||
public readonly id = 'markdown.findAllFileReferences';
|
public readonly id = 'markdown.findAllFileReferences';
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly client: BaseLanguageClient,
|
private readonly client: MdLanguageClient,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public async execute(resource?: vscode.Uri) {
|
public async execute(resource?: vscode.Uri) {
|
||||||
@@ -35,7 +34,7 @@ export class FindFileReferencesCommand implements Command {
|
|||||||
location: vscode.ProgressLocation.Window,
|
location: vscode.ProgressLocation.Window,
|
||||||
title: localize('progress.title', "Finding file references")
|
title: localize('progress.title', "Finding file references")
|
||||||
}, async (_progress, token) => {
|
}, async (_progress, token) => {
|
||||||
const locations = (await this.client.sendRequest(getReferencesToFileInWorkspace, { uri: resource!.toString() }, token)).map(loc => {
|
const locations = (await this.client.getReferencesToFileInWorkspace(resource!, token)).map(loc => {
|
||||||
return new vscode.Location(vscode.Uri.parse(loc.uri), convertRange(loc.range));
|
return new vscode.Location(vscode.Uri.parse(loc.uri), convertRange(loc.range));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -58,7 +57,7 @@ export function convertRange(range: lsp.Range): vscode.Range {
|
|||||||
|
|
||||||
export function registerFindFileReferenceSupport(
|
export function registerFindFileReferenceSupport(
|
||||||
commandManager: CommandManager,
|
commandManager: CommandManager,
|
||||||
client: BaseLanguageClient,
|
client: MdLanguageClient,
|
||||||
): vscode.Disposable {
|
): vscode.Disposable {
|
||||||
return commandManager.register(new FindFileReferencesCommand(client));
|
return commandManager.register(new FindFileReferencesCommand(client));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as picomatch from 'picomatch';
|
import * as picomatch from 'picomatch';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient, TextDocumentEdit } from 'vscode-languageclient';
|
import { TextDocumentEdit } from 'vscode-languageclient';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
import { getEditForFileRenames } from '../protocol';
|
import { MdLanguageClient } from '../client/client';
|
||||||
import { Delayer } from '../util/async';
|
import { Delayer } from '../util/async';
|
||||||
import { noopToken } from '../util/cancellation';
|
import { noopToken } from '../util/cancellation';
|
||||||
import { Disposable } from '../util/dispose';
|
import { Disposable } from '../util/dispose';
|
||||||
@@ -40,7 +40,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
|
|||||||
private readonly _pendingRenames = new Set<RenameAction>();
|
private readonly _pendingRenames = new Set<RenameAction>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly client: BaseLanguageClient,
|
private readonly client: MdLanguageClient,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
|
|||||||
newUri: vscode.Uri,
|
newUri: vscode.Uri,
|
||||||
token: vscode.CancellationToken,
|
token: vscode.CancellationToken,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const edit = await this.client.sendRequest(getEditForFileRenames, [{ oldUri: oldUri.toString(), newUri: newUri.toString() }], token);
|
const edit = await this.client.getEditForFileRenames([{ oldUri: oldUri.toString(), newUri: newUri.toString() }], token);
|
||||||
if (!edit.documentChanges?.length) {
|
if (!edit.documentChanges?.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -248,6 +248,6 @@ class UpdateLinksOnFileRenameHandler extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerUpdateLinksOnRename(client: BaseLanguageClient) {
|
export function registerUpdateLinksOnRename(client: MdLanguageClient) {
|
||||||
return new UpdateLinksOnFileRenameHandler(client);
|
return new UpdateLinksOnFileRenameHandler(client);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Disposable } from './util/dispose';
|
import { Disposable } from './util/dispose';
|
||||||
import { lazy } from './util/lazy';
|
|
||||||
|
|
||||||
enum Trace {
|
enum Trace {
|
||||||
Off,
|
Off,
|
||||||
@@ -33,7 +32,14 @@ export interface ILogger {
|
|||||||
export class VsCodeOutputLogger extends Disposable implements ILogger {
|
export class VsCodeOutputLogger extends Disposable implements ILogger {
|
||||||
private trace?: Trace;
|
private trace?: Trace;
|
||||||
|
|
||||||
private readonly outputChannel = lazy(() => this._register(vscode.window.createOutputChannel('Markdown')));
|
private _outputChannel?: vscode.OutputChannel;
|
||||||
|
|
||||||
|
private get outputChannel() {
|
||||||
|
if (!this._outputChannel) {
|
||||||
|
this._outputChannel = this._register(vscode.window.createOutputChannel('Markdown'));
|
||||||
|
}
|
||||||
|
return this._outputChannel;
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
@@ -66,7 +72,7 @@ export class VsCodeOutputLogger extends Disposable implements ILogger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private appendLine(value: string): void {
|
private appendLine(value: string): void {
|
||||||
this.outputChannel.value.appendLine(value);
|
this.outputChannel.appendLine(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private readTrace(): Trace {
|
private readTrace(): Trace {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import 'mocha';
|
import 'mocha';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { InMemoryDocument } from '../util/inMemoryDocument';
|
import { InMemoryDocument } from '../client/inMemoryDocument';
|
||||||
import { createNewMarkdownEngine } from './engine';
|
import { createNewMarkdownEngine } from './engine';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,7 @@ import * as vscode from 'vscode';
|
|||||||
export interface ITextDocument {
|
export interface ITextDocument {
|
||||||
readonly uri: vscode.Uri;
|
readonly uri: vscode.Uri;
|
||||||
readonly version: number;
|
readonly version: number;
|
||||||
readonly lineCount: number;
|
|
||||||
|
|
||||||
getText(range?: vscode.Range): string;
|
getText(): string;
|
||||||
positionAt(offset: number): vscode.Position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLine(doc: ITextDocument, line: number): string {
|
|
||||||
return doc.getText(new vscode.Range(line, 0, line, Number.MAX_VALUE)).replace(/\r?\n$/, '');
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
export interface Lazy<T> {
|
|
||||||
readonly value: T;
|
|
||||||
readonly hasValue: boolean;
|
|
||||||
map<R>(f: (x: T) => R): Lazy<R>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class LazyValue<T> implements Lazy<T> {
|
|
||||||
private _hasValue: boolean = false;
|
|
||||||
private _value?: T;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _getValue: () => T
|
|
||||||
) { }
|
|
||||||
|
|
||||||
get value(): T {
|
|
||||||
if (!this._hasValue) {
|
|
||||||
this._hasValue = true;
|
|
||||||
this._value = this._getValue();
|
|
||||||
}
|
|
||||||
return this._value!;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasValue(): boolean {
|
|
||||||
return this._hasValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public map<R>(f: (x: T) => R): Lazy<R> {
|
|
||||||
return new LazyValue(() => f(this.value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function lazy<T>(getValue: () => T): Lazy<T> {
|
|
||||||
return new LazyValue<T>(getValue);
|
|
||||||
}
|
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { BaseLanguageClient } from 'vscode-languageclient';
|
import { MdLanguageClient } from '../client/client';
|
||||||
import * as proto from '../protocol';
|
import * as proto from '../client/protocol';
|
||||||
|
|
||||||
enum OpenMarkdownLinks {
|
enum OpenMarkdownLinks {
|
||||||
beside = 'beside',
|
beside = 'beside',
|
||||||
@@ -15,15 +15,15 @@ enum OpenMarkdownLinks {
|
|||||||
export class MdLinkOpener {
|
export class MdLinkOpener {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly client: BaseLanguageClient,
|
private readonly client: MdLanguageClient,
|
||||||
) { }
|
) { }
|
||||||
|
|
||||||
public async resolveDocumentLink(linkText: string, fromResource: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> {
|
public async resolveDocumentLink(linkText: string, fromResource: vscode.Uri): Promise<proto.ResolvedDocumentLinkTarget> {
|
||||||
return this.client.sendRequest(proto.resolveLinkTarget, { linkText, uri: fromResource.toString() });
|
return this.client.resolveLinkTarget(linkText, fromResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async openDocumentLink(linkText: string, fromResource: vscode.Uri, viewColumn?: vscode.ViewColumn): Promise<void> {
|
public async openDocumentLink(linkText: string, fromResource: vscode.Uri, viewColumn?: vscode.ViewColumn): Promise<void> {
|
||||||
const resolved = await this.client.sendRequest(proto.resolveLinkTarget, { linkText, uri: fromResource.toString() });
|
const resolved = await this.client.resolveLinkTarget(linkText, fromResource);
|
||||||
if (!resolved) {
|
if (!resolved) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,11 +232,6 @@ vscode-languageserver-protocol@3.17.2:
|
|||||||
vscode-jsonrpc "8.0.2"
|
vscode-jsonrpc "8.0.2"
|
||||||
vscode-languageserver-types "3.17.2"
|
vscode-languageserver-types "3.17.2"
|
||||||
|
|
||||||
vscode-languageserver-textdocument@^1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.4.tgz#3cd56dd14cec1d09e86c4bb04b09a246cb3df157"
|
|
||||||
integrity sha512-/xhqXP/2A2RSs+J8JNXpiiNVvvNM0oTosNVmQnunlKvq9o4mupHOBAnnzH0lwIPKazXKvAKsVp1kr+H/K4lgoQ==
|
|
||||||
|
|
||||||
vscode-languageserver-textdocument@^1.0.5:
|
vscode-languageserver-textdocument@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c"
|
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c"
|
||||||
|
|||||||
Reference in New Issue
Block a user