diff --git a/extensions/markdown-language-features/src/features/smartSelect.ts b/extensions/markdown-language-features/src/features/smartSelect.ts index 11230c35408baa7d4bd7d81f3da66f6afa2b4497..2fc5b2bdcf611cd08632804f7e6d79ec741bece7 100644 --- a/extensions/markdown-language-features/src/features/smartSelect.ts +++ b/extensions/markdown-language-features/src/features/smartSelect.ts @@ -3,71 +3,58 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Token } from 'markdown-it'; import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContentsProvider } from '../tableOfContentsProvider'; import { flatten } from '../util/arrays'; - -const isStartRegion = (t: string) => /^\s*/.test(t); -const isEndRegion = (t: string) => /^\s*/.test(t); - -const isRegionMarker = (token: Token) => - token.type === 'html_block' && (isStartRegion(token.content) || isEndRegion(token.content)); - export default class MarkdownSmartSelect implements vscode.SelectionRangeProvider { constructor( private readonly engine: MarkdownEngine ) { } public async provideSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[], token: vscode.CancellationToken): Promise { - console.log(positions); - let headerRegions = await Promise.all([ - await this.getHeaderSelectionRanges(document) + let blockRegions = await Promise.all([ + await this.getBlockSelectionRanges(document, positions) ]); - return flatten(headerRegions); + return flatten(blockRegions); } - private async getBlockSelectionRanges(document: vscode.TextDocument): Promise { - - const isBlockToken = (token: Token): boolean => { - switch (token.type) { - case 'fence': - case 'list_item_open': - return token.map[1] > token.map[0]; - - case 'html_block': - if (isRegionMarker(token)) { - return false; - } - return token.map[1] > token.map[0] + 1; - - default: - return false; - } - }; - + private async getBlockSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[]): Promise { + let position = positions[0]; const tokens = await this.engine.parse(document); - const multiLineListItems = tokens.filter(isBlockToken); - return multiLineListItems.map(listItem => { - const start = listItem.map[0]; - let end = listItem.map[1] - 1; - if (document.lineAt(end).isEmptyOrWhitespace && end >= start + 1) { - end = end - 1; - } - let beg = new vscode.Position(0, start); - let endi = new vscode.Position(0, end); - return new vscode.SelectionRange(new vscode.Range(beg, endi)); - }); - } - private async getHeaderSelectionRanges(document: vscode.TextDocument) { - const tocProvider = new TableOfContentsProvider(this.engine, document); - const toc = await tocProvider.getToc(); - return toc.map(entry => { - let start = entry.location.range.start; - let end = entry.location.range.end; - return new vscode.SelectionRange(new vscode.Range(new vscode.Position(start.line, start.character), new vscode.Position(end.line, end.character))); + let tokes = tokens.filter(token => token.map && (token.meta || token.content)); + let poss = tokes.map(token => { + const start = token.map[0]; + let startPos = new vscode.Position(position.line, start); + let endPos = new vscode.Position(getEndLine(token.meta ? token.meta : token.content), getEndPos(token.meta ? token.meta : token.content)); + return new vscode.SelectionRange(new vscode.Range(startPos, endPos)); }); + return poss; } } + +let getEndLine = (meta: string) => { + let numLines = 0; + for (let i = 0; i < meta.length; i++) { + if (meta[i] === '\n') { + numLines++; + } + } + return numLines; +}; + +let getEndPos = (meta: string) => { + let maxLineLength = 0; + let currMax = 0; + for (let i = 0; i < meta.length; i++) { + if (meta[i] === '\n') { + currMax = 0; + } else { + currMax++; + if (currMax > maxLineLength) { + maxLineLength = currMax; + } + } + } + return maxLineLength; +}; diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts index a66f45fd27ae10a3d8fbbbfd1179369151c56bb9..5c8001c5d76ddd1d4ccfd807399ad51b7b1b0990 100644 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -13,16 +13,31 @@ import { createNewMarkdownEngine } from './engine'; const testFileName = vscode.Uri.file('test.md'); +suite('markdown.SmartSelect', () => { + test('Smart select single word', async () => { + const selections = await getSelectionRangesForDocument(`Hello`); + assert.deepStrictEqual(selections[0].range, new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 5))); + }); + + test('Smart select fenced code blocks', async () => { + const selections = await getSelectionRangesForDocument(`--- + title: bla + --- + + ~~~ts + a + ~~~ + + a + a + b + a`); + assert.deepStrictEqual(selections[0].range, new vscode.Range(new vscode.Position(0, 0), new vscode.Position(10, 12))); + }); +}); + async function getSelectionRangesForDocument(contents: string) { const doc = new InMemoryDocument(testFileName, contents); const provider = new MarkdownSmartSelect(createNewMarkdownEngine()); - return await provider.provideSelectionRanges(doc, [], new vscode.CancellationTokenSource().token); + return await provider.provideSelectionRanges(doc, [new vscode.Position(0, 0)], new vscode.CancellationTokenSource().token); } - -suite('markdown.SmartSelect', () => { - test('Smart select', async () => { - const selections = await getSelectionRangesForDocument(`hello`); - console.log(selections); - assert.strictEqual(0, 0); - }); -});