add markdown-language-features to sqlops (#2338)

This commit is contained in:
Abbie Petchtes
2018-08-28 11:46:31 -07:00
committed by GitHub
parent 86a0f2d4a7
commit b66ac2781c
67 changed files with 11180 additions and 0 deletions

View File

@@ -0,0 +1,18 @@
/*---------------------------------------------------------------------------------------------
* 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 { MarkdownEngine } from '../markdownEngine';
import { MarkdownContributions } from '../markdownExtensions';
export function createNewMarkdownEngine(): MarkdownEngine {
return new MarkdownEngine(new class implements MarkdownContributions {
readonly previewScripts: vscode.Uri[] = [];
readonly previewStyles: vscode.Uri[] = [];
readonly previewResourceRoots: vscode.Uri[] = [];
readonly markdownItPlugins: Promise<(md: any) => any>[] = [];
});
}

View File

@@ -0,0 +1,88 @@
/*---------------------------------------------------------------------------------------------
* 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 * as vscode from 'vscode';
import 'mocha';
import MarkdownFoldingProvider from '../features/foldingProvider';
import { InMemoryDocument } from './inMemoryDocument';
import { createNewMarkdownEngine } from './engine';
const testFileName = vscode.Uri.parse('test.md');
suite('markdown.FoldingProvider', () => {
test('Should not return anything for empty document', async () => {
const folds = await getFoldsForDocument(``);
assert.strictEqual(folds.length, 0);
});
test('Should not return anything for document without headers', async () => {
const folds = await getFoldsForDocument(`a
**b** afas
a#b
a`);
assert.strictEqual(folds.length, 0);
});
test('Should fold from header to end of document', async () => {
const folds = await getFoldsForDocument(`a
# b
c
d`);
assert.strictEqual(folds.length, 1);
const firstFold = folds[0];
assert.strictEqual(firstFold.start, 1);
assert.strictEqual(firstFold.end, 3);
});
test('Should leave single newline before next header', async () => {
const folds = await getFoldsForDocument(`
# a
x
# b
y`);
assert.strictEqual(folds.length, 2);
const firstFold = folds[0];
assert.strictEqual(firstFold.start, 1);
assert.strictEqual(firstFold.end, 3);
});
test('Should collapse multuple newlines to single newline before next header', async () => {
const folds = await getFoldsForDocument(`
# a
x
# b
y`);
assert.strictEqual(folds.length, 2);
const firstFold = folds[0];
assert.strictEqual(firstFold.start, 1);
assert.strictEqual(firstFold.end, 5);
});
test('Should not collapse if there is no newline before next header', async () => {
const folds = await getFoldsForDocument(`
# a
x
# b
y`);
assert.strictEqual(folds.length, 2);
const firstFold = folds[0];
assert.strictEqual(firstFold.start, 1);
assert.strictEqual(firstFold.end, 2);
});
});
async function getFoldsForDocument(contents: string) {
const doc = new InMemoryDocument(testFileName, contents);
const provider = new MarkdownFoldingProvider(createNewMarkdownEngine());
return await provider.provideFoldingRanges(doc, {}, new vscode.CancellationTokenSource().token);
}

View File

@@ -0,0 +1,65 @@
/*---------------------------------------------------------------------------------------------
* 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';
export class InMemoryDocument implements vscode.TextDocument {
private readonly _lines: string[];
constructor(
public readonly uri: vscode.Uri,
private readonly _contents: string
) {
this._lines = this._contents.split(/\n/g);
}
isUntitled: boolean = false;
languageId: string = '';
version: number = 1;
isDirty: boolean = false;
isClosed: boolean = false;
eol: vscode.EndOfLine = vscode.EndOfLine.LF;
get fileName(): string {
return this.uri.fsPath;
}
get lineCount(): number {
return this._lines.length;
}
lineAt(line: any): vscode.TextLine {
return {
lineNumber: line,
text: this._lines[line],
range: new vscode.Range(0, 0, 0, 0),
firstNonWhitespaceCharacterIndex: 0,
rangeIncludingLineBreak: new vscode.Range(0, 0, 0, 0),
isEmptyOrWhitespace: false
};
}
offsetAt(_position: vscode.Position): never {
throw new Error('Method not implemented.');
}
positionAt(_offset: number): never {
throw new Error('Method not implemented.');
}
getText(_range?: vscode.Range | undefined): string {
return this._contents;
}
getWordRangeAtPosition(_position: vscode.Position, _regex?: RegExp | undefined): never {
throw new Error('Method not implemented.');
}
validateRange(_range: vscode.Range): never {
throw new Error('Method not implemented.');
}
validatePosition(_position: vscode.Position): never {
throw new Error('Method not implemented.');
}
save(): never {
throw new Error('Method not implemented.');
}
}

View File

@@ -0,0 +1,28 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//
// PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
//
// This file is providing the test runner to use when running extension tests.
// By default the test runner in use is Mocha based.
//
// You can provide your own test runner if you want to override it by exporting
// a function run(testRoot: string, clb: (error:Error) => void) that the extension
// host can call to run the tests. The test runner is expected to use console.log
// to report the results back to the caller. When the tests are finished, return
// a possible error to the callback or null if none.
const testRunner = require('vscode/lib/testrunner');
// You can directly control Mocha options by uncommenting the following lines
// See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
testRunner.configure({
ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.)
useColors: process.platform !== 'win32', // colored output from test results (only windows cannot handle)
timeout: 60000
});
export = testRunner;

View File

@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* 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 * as vscode from 'vscode';
import 'mocha';
import { TableOfContentsProvider } from '../tableOfContentsProvider';
import { InMemoryDocument } from './inMemoryDocument';
import { createNewMarkdownEngine } from './engine';
const testFileName = vscode.Uri.parse('test.md');
suite('markdown.TableOfContentsProvider', () => {
test('Lookup should not return anything for empty document', async () => {
const doc = new InMemoryDocument(testFileName, '');
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual(await provider.lookup(''), undefined);
assert.strictEqual(await provider.lookup('foo'), undefined);
});
test('Lookup should not return anything for document with no headers', async () => {
const doc = new InMemoryDocument(testFileName, 'a *b*\nc');
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual(await provider.lookup(''), undefined);
assert.strictEqual(await provider.lookup('foo'), undefined);
assert.strictEqual(await provider.lookup('a'), undefined);
assert.strictEqual(await provider.lookup('b'), undefined);
});
test('Lookup should return basic #header', async () => {
const doc = new InMemoryDocument(testFileName, `# a\nx\n# c`);
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
{
const entry = await provider.lookup('a');
assert.ok(entry);
assert.strictEqual(entry!.line, 0);
}
{
assert.strictEqual(await provider.lookup('x'), undefined);
}
{
const entry = await provider.lookup('c');
assert.ok(entry);
assert.strictEqual(entry!.line, 2);
}
});
test('Lookups should be case in-sensitive', async () => {
const doc = new InMemoryDocument(testFileName, `# fOo\n`);
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual((await provider.lookup('fOo'))!.line, 0);
assert.strictEqual((await provider.lookup('foo'))!.line, 0);
assert.strictEqual((await provider.lookup('FOO'))!.line, 0);
});
test('Lookups should ignore leading and trailing white-space, and collapse internal whitespace', async () => {
const doc = new InMemoryDocument(testFileName, `# f o o \n`);
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
assert.strictEqual((await provider.lookup(' f o o'))!.line, 0);
assert.strictEqual((await provider.lookup(' f o o '))!.line, 0);
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
assert.strictEqual((await provider.lookup('f o o'))!.line, 0);
assert.strictEqual(await provider.lookup('f'), undefined);
assert.strictEqual(await provider.lookup('foo'), undefined);
assert.strictEqual(await provider.lookup('fo o'), undefined);
});
test('should normalize special characters #44779', async () => {
const doc = new InMemoryDocument(testFileName, `# Indentação\n`);
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual((await provider.lookup('indentacao'))!.line, 0);
});
test('should map special З, #37079', async () => {
const doc = new InMemoryDocument(testFileName, `### Заголовок Header 3`);
const provider = new TableOfContentsProvider(createNewMarkdownEngine(), doc);
assert.strictEqual((await provider.lookup('Заголовок-header-3'))!.line, 0);
assert.strictEqual((await provider.lookup('3аголовок-header-3'))!.line, 0);
});
});

View File

@@ -0,0 +1,142 @@
/*---------------------------------------------------------------------------------------------
* 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 * as vscode from 'vscode';
import MDDocumentSymbolProvider from '../features/documentSymbolProvider';
import MarkdownWorkspaceSymbolProvider, { WorkspaceMarkdownDocumentProvider } from '../features/workspaceSymbolProvider';
import { createNewMarkdownEngine } from './engine';
import { InMemoryDocument } from './inMemoryDocument';
const symbolProvider = new MDDocumentSymbolProvider(createNewMarkdownEngine());
suite('markdown.WorkspaceSymbolProvider', () => {
test('Should not return anything for empty workspace', async () => {
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider([]));
assert.deepEqual(await provider.provideWorkspaceSymbols(''), []);
});
test('Should return symbols from workspace with one markdown file', async () => {
const testFileName = vscode.Uri.parse('test.md');
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider([
new InMemoryDocument(testFileName, `# header1\nabc\n## header2`)
]));
const symbols = await provider.provideWorkspaceSymbols('');
assert.strictEqual(symbols.length, 2);
assert.strictEqual(symbols[0].name, '# header1');
assert.strictEqual(symbols[1].name, '## header2');
});
test('Should return all content basic workspace', async () => {
const fileNameCount = 10;
const files: vscode.TextDocument[] = [];
for (let i = 0; i < fileNameCount; ++i) {
const testFileName = vscode.Uri.parse(`test${i}.md`);
files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`));
}
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocumentProvider(files));
const symbols = await provider.provideWorkspaceSymbols('');
assert.strictEqual(symbols.length, fileNameCount * 2);
});
test('Should update results when markdown file changes symbols', async () => {
const testFileName = vscode.Uri.parse('test.md');
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
new InMemoryDocument(testFileName, `# header1`)
]);
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider);
assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1);
// Update file
workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`));
const newSymbols = await provider.provideWorkspaceSymbols('');
assert.strictEqual(newSymbols.length, 2);
assert.strictEqual(newSymbols[0].name, '# new header');
assert.strictEqual(newSymbols[1].name, '## header2');
});
test('Should remove results when file is deleted', async () => {
const testFileName = vscode.Uri.parse('test.md');
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
new InMemoryDocument(testFileName, `# header1`)
]);
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider);
assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1);
// delete file
workspaceFileProvider.deleteDocument(testFileName);
const newSymbols = await provider.provideWorkspaceSymbols('');
assert.strictEqual(newSymbols.length, 0);
});
test('Should update results when markdown file is created', async () => {
const testFileName = vscode.Uri.parse('test.md');
const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocumentProvider([
new InMemoryDocument(testFileName, `# header1`)
]);
const provider = new MarkdownWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider);
assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1);
// Creat file
workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.parse('test2.md'), `# new header\nabc\n## header2`));
const newSymbols = await provider.provideWorkspaceSymbols('');
assert.strictEqual(newSymbols.length, 3);
});
});
class InMemoryWorkspaceMarkdownDocumentProvider implements WorkspaceMarkdownDocumentProvider {
private readonly _documents = new Map<string, vscode.TextDocument>();
constructor(documents: vscode.TextDocument[]) {
for( const doc of documents) {
this._documents.set(doc.fileName, doc);
}
}
async getAllMarkdownDocuments() {
return Array.from(this._documents.values());
}
private readonly _onDidChangeMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidChangeMarkdownDocument = this._onDidChangeMarkdownDocumentEmitter.event;
private readonly _onDidCreateMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.TextDocument>();
public onDidCreateMarkdownDocument = this._onDidCreateMarkdownDocumentEmitter.event;
private readonly _onDidDeleteMarkdownDocumentEmitter = new vscode.EventEmitter<vscode.Uri>();
public onDidDeleteMarkdownDocument = this._onDidDeleteMarkdownDocumentEmitter.event;
public updateDocument(document: vscode.TextDocument) {
this._documents.set(document.fileName, document);
this._onDidChangeMarkdownDocumentEmitter.fire(document);
}
public createDocument(document: vscode.TextDocument) {
assert.ok(!this._documents.has(document.uri.fsPath));
this._documents.set(document.uri.fsPath, document);
this._onDidCreateMarkdownDocumentEmitter.fire(document);
}
public deleteDocument(resource: vscode.Uri) {
this._documents.delete(resource.fsPath);
this._onDidDeleteMarkdownDocumentEmitter.fire(resource);
}
}