Scaffold out basic markdown language server (#154293)

* Scaffold out basic markdown lsp

This scaffolds out a new markdown language server and then uses it to implement document symbols. After the change, the markdown extension will have the following structure:

- languageService — Where all the LSP language stuff will eventually land
- server — The actual language server. Consumes ` languageService`
- src — The current extension that launches the server and implements VS Code specific functions

* Adding build scripts

* a

* Use language service from github

* Remove ls build scripts

* Bump versions

* Only build ext

* Enable for web

* Fixing for browser
This commit is contained in:
Matt Bierner 2022-07-06 16:03:24 -07:00 committed by GitHub
parent 3d3bfced96
commit ad9675f099
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 626 additions and 151 deletions

View file

@ -46,6 +46,7 @@ steps:
compile-extension:html-language-features-server \
compile-extension:ipynb \
compile-extension:json-language-features-server \
compile-extension:markdown-language-features-server \
compile-extension:markdown-language-features \
compile-extension-media \
compile-extension:microsoft-authentication \

View file

@ -58,6 +58,7 @@ steps:
compile-extension:html-language-features-server \
compile-extension:ipynb \
compile-extension:json-language-features-server \
compile-extension:markdown-language-features-server \
compile-extension:markdown-language-features \
compile-extension-media \
compile-extension:microsoft-authentication \

View file

@ -52,6 +52,7 @@ steps:
compile-extension:html-language-features-server `
compile-extension:ipynb `
compile-extension:json-language-features-server `
compile-extension:markdown-language-features-server `
compile-extension:markdown-language-features `
compile-extension-media `
compile-extension:microsoft-authentication `

View file

@ -53,6 +53,7 @@ const compilations = [
'json-language-features/client/tsconfig.json',
'json-language-features/server/tsconfig.json',
'markdown-language-features/preview-src/tsconfig.json',
'markdown-language-features/server/tsconfig.json',
'markdown-language-features/tsconfig.json',
'markdown-math/tsconfig.json',
'merge-conflict/tsconfig.json',

View file

@ -28,6 +28,7 @@ exports.dirs = [
'extensions/jake',
'extensions/json-language-features',
'extensions/json-language-features/server',
'extensions/markdown-language-features/server',
'extensions/markdown-language-features',
'extensions/markdown-math',
'extensions/merge-conflict',

View file

@ -12,7 +12,7 @@ const withBrowserDefaults = require('../shared.webpack.config').browser;
module.exports = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/extension.ts'
extension: './src/extension.browser.ts'
}
}, {
configFile: 'tsconfig.browser.json'

View file

@ -15,6 +15,6 @@ module.exports = withDefaults({
mainFields: ['module', 'main']
},
entry: {
extension: './src/extension.ts',
extension: './src/extension.node.ts',
}
});

View file

@ -10,7 +10,7 @@
"engines": {
"vscode": "^1.20.0"
},
"main": "./out/extension",
"main": "./out/extension.node",
"browser": "./dist/browser/extension",
"categories": [
"Programming Languages"
@ -534,8 +534,8 @@
]
},
"scripts": {
"compile": "gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook",
"watch": "npm run build-preview && gulp watch-extension:markdown-language-features",
"compile": "gulp compile-extension:markdown-language-features-languageService && gulp compile-extension:markdown-language-features-server && gulp compile-extension:markdown-language-features && npm run build-preview && npm run build-notebook",
"watch": "npm run build-preview && gulp watch-extension:markdown-language-features watch-extension:markdown-language-features-languageService watch-extension:markdown-language-features-server",
"vscode:prepublish": "npm run build-ext && npm run build-preview",
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json",
"build-notebook": "node ./esbuild-notebook",
@ -551,6 +551,7 @@
"markdown-it-front-matter": "^0.2.1",
"morphdom": "^2.6.1",
"picomatch": "^2.3.1",
"vscode-languageclient": "^8.0.1",
"vscode-languageserver-textdocument": "^1.0.4",
"vscode-nls": "^5.0.0",
"vscode-uri": "^3.0.3"

View file

@ -0,0 +1,33 @@
{
"version": "0.1.0",
// List of configurations. Add new configurations or edit existing ones.
"configurations": [
{
"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": {},
"sourceMaps": true,
"outFiles": ["${workspaceFolder}/out/**/*.js"]
}
]
}

View file

@ -0,0 +1,9 @@
{
"version": "0.1.0",
"command": "npm",
"isShellCommand": true,
"showOutput": "silent",
"args": ["run", "watch"],
"isWatching": true,
"problemMatcher": "$tsc-watch"
}

View file

@ -0,0 +1,24 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withBrowserDefaults = require('../../shared.webpack.config').browser;
const path = require('path');
module.exports = withBrowserDefaults({
context: __dirname,
entry: {
extension: './src/browser/main.ts',
},
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist', 'browser'),
libraryTarget: 'var',
library: 'serverExportVar'
}
});

View file

@ -0,0 +1,22 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
const withDefaults = require('../../shared.webpack.config');
const path = require('path');
module.exports = withDefaults({
context: path.join(__dirname),
entry: {
extension: './src/node/main.ts',
},
output: {
filename: 'main.js',
path: path.join(__dirname, 'dist', 'node'),
}
});

View file

@ -0,0 +1,27 @@
{
"name": "vscode-markdown-languageserver",
"description": "Markdown language server",
"version": "1.0.0",
"author": "Microsoft Corporation",
"license": "MIT",
"engines": {
"node": "*"
},
"main": "./out/node/main",
"browser": "./dist/browser/main",
"dependencies": {
"vscode-languageserver": "^8.0.2-next.4",
"vscode-uri": "^3.0.3",
"vscode-languageserver-textdocument": "^1.0.5",
"vscode-languageserver-types": "^3.17.1",
"vscode-markdown-languageservice": "mjbvz/vscode-markdown-languageservice"
},
"devDependencies": {
"@types/node": "16.x"
},
"scripts": {
"postinstall": "cd node_modules/vscode-markdown-languageservice && yarn run compile-ext",
"compile": "gulp compile-extension:markdown-language-features-server",
"watch": "gulp watch-extension:markdown-language-features-server"
}
}

View file

@ -0,0 +1,16 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser';
import { startServer } from '../server';
declare let self: any;
const messageReader = new BrowserMessageReader(self);
const messageWriter = new BrowserMessageWriter(self);
const connection = createConnection(messageReader, messageWriter);
startServer(connection);

View file

@ -0,0 +1,42 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogger } from 'vscode-markdown-languageservice';
class ConsoleLogger implements ILogger {
public verbose(title: string, message: string, data?: any): void {
this.appendLine(`[Verbose ${ConsoleLogger.now()}] ${title}: ${message}`);
if (data) {
this.appendLine(ConsoleLogger.data2String(data));
}
}
private static now(): string {
const now = new Date();
return String(now.getUTCHours()).padStart(2, '0')
+ ':' + String(now.getMinutes()).padStart(2, '0')
+ ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0');
}
private appendLine(value: string): void {
console.log(value);
}
private static data2String(data: any): string {
if (data instanceof Error) {
if (typeof data.stack === 'string') {
return data.stack;
}
return data.message;
}
if (typeof data === 'string') {
return data;
}
return JSON.stringify(data, undefined, 2);
}
}
export const consoleLogger = new ConsoleLogger();

View file

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Connection, createConnection } from 'vscode-languageserver/node';
import { startServer } from '../server';
// Create a connection for the server.
const connection: Connection = createConnection();
console.log = connection.console.log.bind(connection.console);
console.error = connection.console.error.bind(connection.console);
process.on('unhandledRejection', (e: any) => {
connection.console.error(`Unhandled exception ${e}`);
});
startServer(connection);

View file

@ -0,0 +1,132 @@
/*---------------------------------------------------------------------------------------------
* 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, Event, InitializeParams, InitializeResult, RequestType, TextDocuments } from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { DocumentSymbol, Position, Range } from 'vscode-languageserver-types';
import * as md from 'vscode-markdown-languageservice';
import { URI } from 'vscode-uri';
import { consoleLogger } from './logging';
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 {
const pos = this._doc.positionAt(offset);
return md.makePosition(pos.line, pos.character);
}
}
export function startServer(connection: Connection) {
const documents = new TextDocuments(TextDocument);
documents.listen(connection);
connection.onInitialize((_params: InitializeParams): InitializeResult => {
return {
capabilities: {
documentSymbolProvider: true,
}
};
});
const parser = new class implements md.IMdParser {
slugifier = md.githubSlugifier;
async tokenize(document: md.ITextDocument): Promise<md.Token[]> {
return await connection.sendRequest(parseRequestType, { uri: document.uri.toString() });
}
};
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 provider = md.createLanguageService(workspace, parser, consoleLogger);
connection.onDocumentSymbol(async (documentSymbolParams, _token): Promise<DocumentSymbol[]> => {
try {
const document = documents.get(documentSymbolParams.textDocument.uri) as TextDocument | undefined;
if (document) {
const response = await provider.provideDocumentSymbols(new TextDocumentToITextDocumentAdapter(document));
// TODO: only required because extra methods returned on positions/ranges
return response.map(symbol => convertDocumentSymbol(symbol));
}
} catch (e) {
console.error(e.stack);
}
return [];
});
connection.listen();
}
function convertDocumentSymbol(sym: DocumentSymbol): DocumentSymbol {
return {
kind: sym.kind,
name: sym.name,
range: convertRange(sym.range),
selectionRange: convertRange(sym.selectionRange),
children: sym.children?.map(convertDocumentSymbol),
detail: sym.detail,
tags: sym.tags,
};
}
function convertRange(range: Range): Range {
return {
start: convertPosition(range.start),
end: convertPosition(range.end),
};
}
function convertPosition(start: Position): Position {
return {
character: start.character,
line: start.line,
};
}

View file

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./out"
},
"include": [
"src/**/*"
]
}

View file

@ -0,0 +1,60 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@types/mocha@^9.1.1":
version "9.1.1"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.1.tgz#e7c4f1001eefa4b8afbd1eee27a237fee3bf29c4"
integrity sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==
"@types/node@16.x":
version "16.11.43"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.43.tgz#555e5a743f76b6b897d47f945305b618525ddbe6"
integrity sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ==
vscode-jsonrpc@8.0.2-next.1:
version "8.0.2-next.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2-next.1.tgz#6bdc39fd194782032e34047eeefce562941259c6"
integrity sha512-sbbvGSWja7NVBLHPGawtgezc8DHYJaP4qfr/AaJiyDapWcSFtHyPtm18+LnYMLTmB7bhOUW/lf5PeeuLpP6bKA==
vscode-languageserver-protocol@3.17.2-next.6:
version "3.17.2-next.6"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2-next.6.tgz#8f1dc0fcb29366b85f623a3f9af726de433b5fcc"
integrity sha512-WtsebNOOkWyNn4oFYoAMPC8Q/ZDoJ/K7Ja53OzTixiitvrl/RpXZETrtzH79R8P5kqCyx6VFBPb6KQILJfkDkA==
dependencies:
vscode-jsonrpc "8.0.2-next.1"
vscode-languageserver-types "3.17.2-next.2"
vscode-languageserver-textdocument@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.5.tgz#838769940ece626176ec5d5a2aa2d0aa69f5095c"
integrity sha512-1ah7zyQjKBudnMiHbZmxz5bYNM9KKZYz+5VQLj+yr8l+9w3g+WAhCkUkWbhMEdC5u0ub4Ndiye/fDyS8ghIKQg==
vscode-languageserver-types@3.17.2-next.2:
version "3.17.2-next.2"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2-next.2.tgz#af5d6978eee7682aab87c1419323f5b141ac6596"
integrity sha512-TiAkLABgqkVWdAlC3XlOfdhdjIAdVU4YntPUm9kKGbXr+MGwpVnKz2KZMNBcvG0CFx8Hi8qliL0iq+ndPB720w==
vscode-languageserver-types@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
vscode-languageserver@^8.0.2-next.4:
version "8.0.2-next.5"
resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.0.2-next.5.tgz#39a2dd4c504fb88042375e7ac706a714bdaab4e5"
integrity sha512-2ZDb7O/4atS9mJKufPPz637z+51kCyZfgnobFW5eSrUdS3c0UB/nMS4Ng1EavYTX84GVaVMKCrmP0f2ceLmR0A==
dependencies:
vscode-languageserver-protocol "3.17.2-next.6"
vscode-markdown-languageservice@mjbvz/vscode-markdown-languageservice:
version "1.0.0"
resolved "https://codeload.github.com/mjbvz/vscode-markdown-languageservice/tar.gz/e410b5df64659fbc186cf0a7a7c882c451e07b8b"
dependencies:
vscode-languageserver-types "^3.17.1"
vscode-uri "^3.0.3"
vscode-uri@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84"
integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==

View file

@ -0,0 +1,49 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import Token = require('markdown-it/lib/token');
import * as vscode from 'vscode';
import { BaseLanguageClient, LanguageClientOptions, RequestType } from 'vscode-languageclient';
import * as nls from 'vscode-nls';
import { IMdParser } from './markdownEngine';
import { IMdWorkspace } from './workspace';
const localize = nls.loadMessageBundle();
const parseRequestType: RequestType<{ uri: string }, Token[], any> = new RequestType('markdown/parse');
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 clientOptions: LanguageClientOptions = {
documentSelector,
synchronize: {
configurationSection: ['markdown']
},
initializationOptions: {}
};
const client = factory('markdown', localize('markdownServer.name', 'Markdown Language Server'), clientOptions);
client.registerProposedFeatures();
client.onRequest(parseRequestType, async (e) => {
const uri = vscode.Uri.parse(e.uri);
const doc = await workspace.getOrLoadMarkdownDocument(uri);
if (doc) {
return parser.tokenize(doc);
} else {
return [];
}
});
await client.start();
return client;
}

View file

@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* 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 { LanguageClient, LanguageClientOptions } from 'vscode-languageclient/browser';
import { startClient } from './client';
import { activateShared } from './extension.shared';
import { VsCodeOutputLogger } from './logging';
import { IMdParser, MarkdownItEngine } from './markdownEngine';
import { getMarkdownExtensionContributions } from './markdownExtensions';
import { githubSlugifier } from './slugify';
import { IMdWorkspace, VsCodeMdWorkspace } from './workspace';
export function activate(context: vscode.ExtensionContext) {
const contributions = getMarkdownExtensionContributions(context);
context.subscriptions.push(contributions);
const logger = new VsCodeOutputLogger();
context.subscriptions.push(logger);
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
const workspace = new VsCodeMdWorkspace();
context.subscriptions.push(workspace);
activateShared(context, workspace, engine, logger, contributions);
startServer(context, workspace, engine);
}
async function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise<void> {
const serverMain = vscode.Uri.joinPath(context.extensionUri, 'server/dist/browser/main.js');
const worker = new Worker(serverMain.toString());
await startClient((id: string, name: string, clientOptions: LanguageClientOptions) => {
return new LanguageClient(id, name, clientOptions, worker);
}, workspace, parser);
}

View file

@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* 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 { LanguageClient, ServerOptions, TransportKind } from 'vscode-languageclient/node';
import { startClient } from './client';
import { activateShared } from './extension.shared';
import { VsCodeOutputLogger } from './logging';
import { IMdParser, MarkdownItEngine } from './markdownEngine';
import { getMarkdownExtensionContributions } from './markdownExtensions';
import { githubSlugifier } from './slugify';
import { IMdWorkspace, VsCodeMdWorkspace } from './workspace';
export function activate(context: vscode.ExtensionContext) {
const contributions = getMarkdownExtensionContributions(context);
context.subscriptions.push(contributions);
const logger = new VsCodeOutputLogger();
context.subscriptions.push(logger);
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
const workspace = new VsCodeMdWorkspace();
context.subscriptions.push(workspace);
activateShared(context, workspace, engine, logger, contributions);
startServer(context, workspace, engine);
}
async function startServer(context: vscode.ExtensionContext, workspace: IMdWorkspace, parser: IMdParser): Promise<void> {
const clientMain = vscode.extensions.getExtension('vscode.css-language-features')?.packageJSON?.main || '';
const serverMain = `./server/${clientMain.indexOf('/dist/') !== -1 ? 'dist' : 'out'}/node/main`;
const serverModule = context.asAbsolutePath(serverMain);
// The debug options for the server
const debugOptions = { execArgv: ['--nolazy', '--inspect=' + (7000 + Math.round(Math.random() * 999))] };
// If the extension is launch in debug mode the debug server options are use
// Otherwise the run options are used
const serverOptions: ServerOptions = {
run: { module: serverModule, transport: TransportKind.ipc },
debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions }
};
await startClient((id, name, clientOptions) => {
return new LanguageClient(id, name, serverOptions, clientOptions);
}, workspace, parser);
}

View file

@ -10,7 +10,7 @@ import { registerPasteSupport } from './languageFeatures/copyPaste';
import { registerDefinitionSupport } from './languageFeatures/definitions';
import { registerDiagnosticSupport } from './languageFeatures/diagnostics';
import { MdLinkProvider, registerDocumentLinkSupport } from './languageFeatures/documentLinks';
import { MdDocumentSymbolProvider, registerDocumentSymbolSupport } from './languageFeatures/documentSymbols';
import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbols';
import { registerDropIntoEditorSupport } from './languageFeatures/dropIntoEditor';
import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences';
import { registerFoldingSupport } from './languageFeatures/folding';
@ -19,36 +19,32 @@ import { MdReferencesProvider, registerReferencesSupport } from './languageFeatu
import { registerRenameSupport } from './languageFeatures/rename';
import { registerSmartSelectSupport } from './languageFeatures/smartSelect';
import { registerWorkspaceSymbolSupport } from './languageFeatures/workspaceSymbols';
import { ILogger, VsCodeOutputLogger } from './logging';
import { ILogger } from './logging';
import { IMdParser, MarkdownItEngine, MdParsingProvider } from './markdownEngine';
import { getMarkdownExtensionContributions } from './markdownExtensions';
import { MarkdownContributionProvider } from './markdownExtensions';
import { MdDocumentRenderer } from './preview/documentRenderer';
import { MarkdownPreviewManager } from './preview/previewManager';
import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security';
import { githubSlugifier } from './slugify';
import { MdTableOfContentsProvider } from './tableOfContents';
import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter';
import { IMdWorkspace, VsCodeMdWorkspace } from './workspace';
import { IMdWorkspace } from './workspace';
export function activate(context: vscode.ExtensionContext) {
export function activateShared(
context: vscode.ExtensionContext,
workspace: IMdWorkspace,
engine: MarkdownItEngine,
logger: ILogger,
contributions: MarkdownContributionProvider,
) {
const telemetryReporter = loadDefaultTelemetryReporter();
context.subscriptions.push(telemetryReporter);
const contributions = getMarkdownExtensionContributions(context);
context.subscriptions.push(contributions);
const logger = new VsCodeOutputLogger();
context.subscriptions.push(logger);
const cspArbiter = new ExtensionContentSecurityPolicyArbiter(context.globalState, context.workspaceState);
const commandManager = new CommandManager();
const engine = new MarkdownItEngine(contributions, githubSlugifier, logger);
const workspace = new VsCodeMdWorkspace();
const parser = new MdParsingProvider(engine, workspace);
const tocProvider = new MdTableOfContentsProvider(parser, workspace, logger);
context.subscriptions.push(workspace, parser, tocProvider);
context.subscriptions.push(parser, tocProvider);
const contentProvider = new MdDocumentRenderer(engine, context, cspArbiter, contributions, logger);
const previewManager = new MarkdownPreviewManager(contentProvider, workspace, logger, contributions, tocProvider);
@ -83,7 +79,6 @@ function registerMarkdownLanguageFeatures(
registerDefinitionSupport(selector, referencesProvider),
registerDiagnosticSupport(selector, workspace, linkProvider, commandManager, referencesProvider, tocProvider, logger),
registerDocumentLinkSupport(selector, linkProvider),
registerDocumentSymbolSupport(selector, tocProvider, logger),
registerDropIntoEditorSupport(selector),
registerFindFileReferenceSupport(commandManager, referencesProvider),
registerFoldingSupport(selector, parser, tocProvider),

View file

@ -75,11 +75,3 @@ export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
return '#'.repeat(entry.level) + ' ' + entry.text;
}
}
export function registerDocumentSymbolSupport(
selector: vscode.DocumentSelector,
tocProvider: MdTableOfContentsProvider,
logger: ILogger,
): vscode.Disposable {
return vscode.languages.registerDocumentSymbolProvider(selector, new MdDocumentSymbolProvider(tocProvider, logger));
}

View file

@ -1,121 +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 { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbols';
import { MdTableOfContentsProvider } from '../tableOfContents';
import { DisposableStore } from '../util/dispose';
import { InMemoryDocument } from '../util/inMemoryDocument';
import { createNewMarkdownEngine } from './engine';
import { InMemoryMdWorkspace } from './inMemoryWorkspace';
import { nulLogger } from './nulLogging';
import { joinLines, withStore, workspacePath } from './util';
function getSymbolsForFile(store: DisposableStore, fileContents: string) {
const doc = new InMemoryDocument(workspacePath('test.md'), fileContents);
const workspace = store.add(new InMemoryMdWorkspace([doc]));
const engine = createNewMarkdownEngine();
const tocProvider = store.add(new MdTableOfContentsProvider(engine, workspace, nulLogger));
const provider = new MdDocumentSymbolProvider(tocProvider, nulLogger);
return provider.provideDocumentSymbols(doc);
}
suite('Markdown: DocumentSymbolProvider', () => {
test('Should not return anything for empty document', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, '');
assert.strictEqual(symbols.length, 0);
}));
test('Should not return anything for document with no headers', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`a`,
`a`,
));
assert.strictEqual(symbols.length, 0);
}));
test('Should not return anything for document with # but no real headers', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`a#a`,
`a#`,
));
assert.strictEqual(symbols.length, 0);
}));
test('Should return single symbol for single header', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, '# h');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
}));
test('Should not care about symbol level for single header', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, '### h');
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '### h');
}));
test('Should put symbols of same level in flat list', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`## h`,
`## h2`,
));
assert.strictEqual(symbols.length, 2);
assert.strictEqual(symbols[0].name, '## h');
assert.strictEqual(symbols[1].name, '## h2');
}));
test('Should nest symbol of level - 1 under parent', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`# h`,
`## h2`,
`## h3`,
));
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 2);
assert.strictEqual(symbols[0].children[0].name, '## h2');
assert.strictEqual(symbols[0].children[1].name, '## h3');
}));
test('Should nest symbol of level - n under parent', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`# h`,
`#### h2`,
));
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 1);
assert.strictEqual(symbols[0].children[0].name, '#### h2');
}));
test('Should flatten children where lower level occurs first', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`# h`,
`### h2`,
`## h3`,
));
assert.strictEqual(symbols.length, 1);
assert.strictEqual(symbols[0].name, '# h');
assert.strictEqual(symbols[0].children.length, 2);
assert.strictEqual(symbols[0].children[0].name, '### h2');
assert.strictEqual(symbols[0].children[1].name, '## h3');
}));
test('Should handle line separator in file. Issue #63749', withStore(async (store) => {
const symbols = await getSymbolsForFile(store, joinLines(
`# A`,
`- foo`,
``,
`# B`,
`- bar`,
));
assert.strictEqual(symbols.length, 2);
assert.strictEqual(symbols[0].name, '# A');
assert.strictEqual(symbols[1].name, '# B');
}));
});

View file

@ -69,6 +69,24 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
dompurify@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c"
@ -96,6 +114,13 @@ lodash.throttle@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
markdown-it-front-matter@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.2.1.tgz#dca49a827bb3cebb0528452c1d87dff276eb28dc"
@ -117,6 +142,13 @@ mdurl@^1.0.1:
resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e"
integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
minimatch@^3.0.4:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
morphdom@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e"
@ -127,16 +159,50 @@ picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
semver@^7.3.5:
version "7.3.7"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
dependencies:
lru-cache "^6.0.0"
uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
vscode-jsonrpc@8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz#f30b0625ebafa0fb3bc53e934ca47b706445e57e"
integrity sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==
vscode-languageclient@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz#bf5535c4463a78daeaca0bcb4f5868aec86bb301"
integrity sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==
dependencies:
minimatch "^3.0.4"
semver "^7.3.5"
vscode-languageserver-protocol "3.17.1"
vscode-languageserver-protocol@3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz#e801762c304f740208b6c804a0cf21f2c87509ed"
integrity sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==
dependencies:
vscode-jsonrpc "8.0.1"
vscode-languageserver-types "3.17.1"
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-types@3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz#c2d87fa7784f8cac389deb3ff1e2d9a7bef07e16"
integrity sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==
vscode-nls@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840"
@ -146,3 +212,8 @@ vscode-uri@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84"
integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==

View file

@ -1 +1,2 @@
notebook-out
languageService