mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 12:19:20 +00:00
Extract workspaceContents to own file
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Command } from '../commandManager';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { SkinnyTextDocument } from '../tableOfContentsProvider';
|
||||
import { SkinnyTextDocument } from '../workspaceContents';
|
||||
|
||||
export class RenderDocument implements Command {
|
||||
public readonly id = 'markdown.api.render';
|
||||
|
||||
@@ -21,6 +21,7 @@ import { MarkdownPreviewManager } from './preview/previewManager';
|
||||
import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security';
|
||||
import { githubSlugifier } from './slugify';
|
||||
import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter';
|
||||
import { VsCodeMdWorkspaceContents } from './workspaceContents';
|
||||
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
@@ -57,13 +58,14 @@ function registerMarkdownLanguageFeatures(
|
||||
const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' };
|
||||
|
||||
const linkProvider = new MdLinkProvider(engine);
|
||||
const w = new VsCodeMdWorkspaceContents();
|
||||
|
||||
return vscode.Disposable.from(
|
||||
vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider),
|
||||
vscode.languages.registerDocumentLinkProvider(selector, linkProvider),
|
||||
vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)),
|
||||
vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)),
|
||||
vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider)),
|
||||
vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, w)),
|
||||
MdPathCompletionProvider.register(selector, engine, linkProvider),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { MarkdownEngine } from '../markdownEngine';
|
||||
import { SkinnyTextDocument, TableOfContents, TocEntry } from '../tableOfContentsProvider';
|
||||
import { TableOfContents, TocEntry } from '../tableOfContentsProvider';
|
||||
import { SkinnyTextDocument } from '../workspaceContents';
|
||||
|
||||
interface MarkdownSymbol {
|
||||
readonly level: number;
|
||||
|
||||
@@ -4,129 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { SkinnyTextDocument, SkinnyTextLine } from '../tableOfContentsProvider';
|
||||
import { Disposable } from '../util/dispose';
|
||||
import { isMarkdownFile } from '../util/file';
|
||||
import { Lazy, lazy } from '../util/lazy';
|
||||
import { SkinnyTextDocument, MdWorkspaceContents } from '../workspaceContents';
|
||||
import { MdDocumentSymbolProvider } from './documentSymbolProvider';
|
||||
|
||||
export interface WorkspaceMarkdownDocumentProvider {
|
||||
getAllMarkdownDocuments(): Thenable<Iterable<SkinnyTextDocument>>;
|
||||
|
||||
readonly onDidChangeMarkdownDocument: vscode.Event<SkinnyTextDocument>;
|
||||
readonly onDidCreateMarkdownDocument: vscode.Event<SkinnyTextDocument>;
|
||||
readonly onDidDeleteMarkdownDocument: vscode.Event<vscode.Uri>;
|
||||
}
|
||||
|
||||
class VSCodeWorkspaceMarkdownDocumentProvider extends Disposable implements WorkspaceMarkdownDocumentProvider {
|
||||
|
||||
private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>());
|
||||
private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>());
|
||||
private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<vscode.Uri>());
|
||||
|
||||
private _watcher: vscode.FileSystemWatcher | undefined;
|
||||
|
||||
private readonly utf8Decoder = new TextDecoder('utf-8');
|
||||
|
||||
/**
|
||||
* Reads and parses all .md documents in the workspace.
|
||||
* Files are processed in batches, to keep the number of open files small.
|
||||
*
|
||||
* @returns Array of processed .md files.
|
||||
*/
|
||||
async getAllMarkdownDocuments(): Promise<SkinnyTextDocument[]> {
|
||||
const maxConcurrent = 20;
|
||||
const docList: SkinnyTextDocument[] = [];
|
||||
const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**');
|
||||
|
||||
for (let i = 0; i < resources.length; i += maxConcurrent) {
|
||||
const resourceBatch = resources.slice(i, i + maxConcurrent);
|
||||
const documentBatch = (await Promise.all(resourceBatch.map(x => this.getMarkdownDocument(x)))).filter((doc) => !!doc) as SkinnyTextDocument[];
|
||||
docList.push(...documentBatch);
|
||||
}
|
||||
return docList;
|
||||
}
|
||||
|
||||
public get onDidChangeMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidChangeMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
public get onDidCreateMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidCreateMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
public get onDidDeleteMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidDeleteMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
private ensureWatcher(): void {
|
||||
if (this._watcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md'));
|
||||
|
||||
this._register(this._watcher.onDidChange(async resource => {
|
||||
const document = await this.getMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidChangeMarkdownDocumentEmitter.fire(document);
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._watcher.onDidCreate(async resource => {
|
||||
const document = await this.getMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidCreateMarkdownDocumentEmitter.fire(document);
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._watcher.onDidDelete(resource => {
|
||||
this._onDidDeleteMarkdownDocumentEmitter.fire(resource);
|
||||
}));
|
||||
|
||||
this._register(vscode.workspace.onDidChangeTextDocument(e => {
|
||||
if (isMarkdownFile(e.document)) {
|
||||
this._onDidChangeMarkdownDocumentEmitter.fire(e.document);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async getMarkdownDocument(resource: vscode.Uri): Promise<SkinnyTextDocument | undefined> {
|
||||
const matchingDocument = vscode.workspace.textDocuments.find((doc) => doc.uri.toString() === resource.toString());
|
||||
if (matchingDocument) {
|
||||
return matchingDocument;
|
||||
}
|
||||
|
||||
const bytes = await vscode.workspace.fs.readFile(resource);
|
||||
|
||||
// We assume that markdown is in UTF-8
|
||||
const text = this.utf8Decoder.decode(bytes);
|
||||
|
||||
const lines: SkinnyTextLine[] = [];
|
||||
const parts = text.split(/(\r?\n)/);
|
||||
const lineCount = Math.floor(parts.length / 2) + 1;
|
||||
for (let line = 0; line < lineCount; line++) {
|
||||
lines.push({
|
||||
text: parts[line * 2]
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
uri: resource,
|
||||
version: 0,
|
||||
lineCount: lineCount,
|
||||
lineAt: (index) => {
|
||||
return lines[index];
|
||||
},
|
||||
getText: () => {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class MdWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider {
|
||||
|
||||
@@ -135,7 +17,7 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work
|
||||
|
||||
public constructor(
|
||||
private _symbolProvider: MdDocumentSymbolProvider,
|
||||
private _workspaceMarkdownDocumentProvider: WorkspaceMarkdownDocumentProvider = new VSCodeWorkspaceMarkdownDocumentProvider()
|
||||
private _workspaceMarkdownDocumentProvider: MdWorkspaceContents,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import Token = require('markdown-it/lib/token');
|
||||
import * as vscode from 'vscode';
|
||||
import { MarkdownContributionProvider } from './markdownExtensions';
|
||||
import { Slugifier } from './slugify';
|
||||
import { SkinnyTextDocument } from './tableOfContentsProvider';
|
||||
import { stringHash } from './util/hash';
|
||||
import { WebviewResourceProvider } from './util/resources';
|
||||
import { isOfScheme, Schemes } from './util/schemes';
|
||||
import { SkinnyTextDocument } from './workspaceContents';
|
||||
|
||||
const UNICODE_NEWLINE_REGEX = /\u2028|\u2029/g;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
|
||||
import { MarkdownEngine } from './markdownEngine';
|
||||
import { githubSlugifier, Slug } from './slugify';
|
||||
import { isMarkdownFile } from './util/file';
|
||||
import { SkinnyTextDocument } from './workspaceContents';
|
||||
|
||||
export interface TocEntry {
|
||||
readonly slug: Slug;
|
||||
@@ -16,19 +17,6 @@ export interface TocEntry {
|
||||
readonly location: vscode.Location;
|
||||
}
|
||||
|
||||
export interface SkinnyTextLine {
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface SkinnyTextDocument {
|
||||
readonly uri: vscode.Uri;
|
||||
readonly version: number;
|
||||
readonly lineCount: number;
|
||||
|
||||
lineAt(line: number): SkinnyTextLine;
|
||||
getText(): string;
|
||||
}
|
||||
|
||||
export class TableOfContents {
|
||||
|
||||
public static async create(engine: MarkdownEngine, document: SkinnyTextDocument,): Promise<TableOfContents> {
|
||||
|
||||
@@ -7,7 +7,8 @@ import * as assert from 'assert';
|
||||
import 'mocha';
|
||||
import * as vscode from 'vscode';
|
||||
import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider';
|
||||
import { MdWorkspaceSymbolProvider, WorkspaceMarkdownDocumentProvider } from '../languageFeatures/workspaceSymbolProvider';
|
||||
import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbolProvider';
|
||||
import { MdWorkspaceContents } from '../workspaceContents';
|
||||
import { createNewMarkdownEngine } from './engine';
|
||||
import { InMemoryDocument } from './inMemoryDocument';
|
||||
|
||||
@@ -16,7 +17,7 @@ const symbolProvider = new MdDocumentSymbolProvider(createNewMarkdownEngine());
|
||||
|
||||
suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should not return anything for empty workspace', async () => {
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider([]));
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([]));
|
||||
|
||||
assert.deepStrictEqual(await provider.provideWorkspaceSymbols(''), []);
|
||||
});
|
||||
@@ -24,7 +25,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should return symbols from workspace with one markdown file', async () => {
|
||||
const testFileName = vscode.Uri.file('test.md');
|
||||
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider([
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([
|
||||
new InMemoryDocument(testFileName, `# header1\nabc\n## header2`)
|
||||
]));
|
||||
|
||||
@@ -42,7 +43,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`));
|
||||
}
|
||||
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider(files));
|
||||
const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments(files));
|
||||
|
||||
const symbols = await provider.provideWorkspaceSymbols('');
|
||||
assert.strictEqual(symbols.length, fileNameCount * 2);
|
||||
@@ -51,7 +52,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should update results when markdown file changes symbols', async () => {
|
||||
const testFileName = vscode.Uri.file('test.md');
|
||||
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([
|
||||
new InMemoryDocument(testFileName, `# header1`, 1 /* version */)
|
||||
]);
|
||||
|
||||
@@ -70,7 +71,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should remove results when file is deleted', async () => {
|
||||
const testFileName = vscode.Uri.file('test.md');
|
||||
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([
|
||||
new InMemoryDocument(testFileName, `# header1`)
|
||||
]);
|
||||
|
||||
@@ -86,7 +87,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
test('Should update results when markdown file is created', async () => {
|
||||
const testFileName = vscode.Uri.file('test.md');
|
||||
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
|
||||
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([
|
||||
new InMemoryDocument(testFileName, `# header1`)
|
||||
]);
|
||||
|
||||
@@ -101,7 +102,7 @@ suite('markdown.WorkspaceSymbolProvider', () => {
|
||||
});
|
||||
|
||||
|
||||
class InMemoryWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocumentProvider {
|
||||
class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents {
|
||||
private readonly _documents = new Map<string, vscode.TextDocument>();
|
||||
|
||||
constructor(documents: vscode.TextDocument[]) {
|
||||
|
||||
156
extensions/markdown-language-features/src/workspaceContents.ts
Normal file
156
extensions/markdown-language-features/src/workspaceContents.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { Disposable } from './util/dispose';
|
||||
import { isMarkdownFile } from './util/file';
|
||||
|
||||
/**
|
||||
* Minimal version of {@link vscode.TextLine}. Used for mocking out in testing.
|
||||
*/
|
||||
export interface SkinnyTextLine {
|
||||
readonly text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimal version of {@link vscode.TextDocument}. Used for mocking out in testing.
|
||||
*/
|
||||
export interface SkinnyTextDocument {
|
||||
readonly uri: vscode.Uri;
|
||||
readonly version: number;
|
||||
readonly lineCount: number;
|
||||
|
||||
lineAt(line: number): SkinnyTextLine;
|
||||
getText(): string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides set of markdown files in the current workspace.
|
||||
*/
|
||||
export interface MdWorkspaceContents {
|
||||
/**
|
||||
* Get list of all known markdown files.
|
||||
*/
|
||||
getAllMarkdownDocuments(): Promise<Iterable<SkinnyTextDocument>>;
|
||||
|
||||
readonly onDidChangeMarkdownDocument: vscode.Event<SkinnyTextDocument>;
|
||||
readonly onDidCreateMarkdownDocument: vscode.Event<SkinnyTextDocument>;
|
||||
readonly onDidDeleteMarkdownDocument: vscode.Event<vscode.Uri>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides set of markdown files known to VS Code.
|
||||
*
|
||||
* This includes both opened text documents and markdown files in the workspace.
|
||||
*/
|
||||
export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspaceContents {
|
||||
|
||||
private readonly _onDidChangeMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>());
|
||||
private readonly _onDidCreateMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<SkinnyTextDocument>());
|
||||
private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter<vscode.Uri>());
|
||||
|
||||
private _watcher: vscode.FileSystemWatcher | undefined;
|
||||
|
||||
private readonly utf8Decoder = new TextDecoder('utf-8');
|
||||
|
||||
/**
|
||||
* Reads and parses all .md documents in the workspace.
|
||||
* Files are processed in batches, to keep the number of open files small.
|
||||
*
|
||||
* @returns Array of processed .md files.
|
||||
*/
|
||||
async getAllMarkdownDocuments(): Promise<SkinnyTextDocument[]> {
|
||||
const maxConcurrent = 20;
|
||||
const docList: SkinnyTextDocument[] = [];
|
||||
const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**');
|
||||
|
||||
for (let i = 0; i < resources.length; i += maxConcurrent) {
|
||||
const resourceBatch = resources.slice(i, i + maxConcurrent);
|
||||
const documentBatch = (await Promise.all(resourceBatch.map(x => this.getMarkdownDocument(x)))).filter((doc) => !!doc) as SkinnyTextDocument[];
|
||||
docList.push(...documentBatch);
|
||||
}
|
||||
return docList;
|
||||
}
|
||||
|
||||
public get onDidChangeMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidChangeMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
public get onDidCreateMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidCreateMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
public get onDidDeleteMarkdownDocument() {
|
||||
this.ensureWatcher();
|
||||
return this._onDidDeleteMarkdownDocumentEmitter.event;
|
||||
}
|
||||
|
||||
private ensureWatcher(): void {
|
||||
if (this._watcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md'));
|
||||
|
||||
this._register(this._watcher.onDidChange(async resource => {
|
||||
const document = await this.getMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidChangeMarkdownDocumentEmitter.fire(document);
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._watcher.onDidCreate(async resource => {
|
||||
const document = await this.getMarkdownDocument(resource);
|
||||
if (document) {
|
||||
this._onDidCreateMarkdownDocumentEmitter.fire(document);
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._watcher.onDidDelete(resource => {
|
||||
this._onDidDeleteMarkdownDocumentEmitter.fire(resource);
|
||||
}));
|
||||
|
||||
this._register(vscode.workspace.onDidChangeTextDocument(e => {
|
||||
if (isMarkdownFile(e.document)) {
|
||||
this._onDidChangeMarkdownDocumentEmitter.fire(e.document);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private async getMarkdownDocument(resource: vscode.Uri): Promise<SkinnyTextDocument | undefined> {
|
||||
const matchingDocument = vscode.workspace.textDocuments.find((doc) => doc.uri.toString() === resource.toString());
|
||||
if (matchingDocument) {
|
||||
return matchingDocument;
|
||||
}
|
||||
|
||||
const bytes = await vscode.workspace.fs.readFile(resource);
|
||||
|
||||
// We assume that markdown is in UTF-8
|
||||
const text = this.utf8Decoder.decode(bytes);
|
||||
|
||||
const lines: SkinnyTextLine[] = [];
|
||||
const parts = text.split(/(\r?\n)/);
|
||||
const lineCount = Math.floor(parts.length / 2) + 1;
|
||||
for (let line = 0; line < lineCount; line++) {
|
||||
lines.push({
|
||||
text: parts[line * 2]
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
uri: resource,
|
||||
version: 0,
|
||||
lineCount: lineCount,
|
||||
lineAt: (index) => {
|
||||
return lines[index];
|
||||
},
|
||||
getText: () => {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user