From 08e556dc5df567fbbd342be0c8dcdb182d8f6561 Mon Sep 17 00:00:00 2001
From: meganrogge
Date: Wed, 4 Nov 2020 10:15:30 -0800
Subject: [PATCH] fix #109486 and #109489
---
.../src/features/smartSelect.ts | 214 ++++++++----------
.../src/test/smartSelect.test.ts | 124 +++++++---
2 files changed, 184 insertions(+), 154 deletions(-)
diff --git a/extensions/markdown-language-features/src/features/smartSelect.ts b/extensions/markdown-language-features/src/features/smartSelect.ts
index 0be50d0cae1..2fad25f62d4 100644
--- a/extensions/markdown-language-features/src/features/smartSelect.ts
+++ b/extensions/markdown-language-features/src/features/smartSelect.ts
@@ -2,7 +2,6 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* 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';
@@ -15,7 +14,7 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide
) { }
public async provideSelectionRanges(document: vscode.TextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise {
- let promises = await Promise.all(positions.map((position) => {
+ const promises = await Promise.all(positions.map((position) => {
return this.provideSelectionRange(document, position, _token);
}));
return promises.filter(item => item !== undefined) as vscode.SelectionRange[];
@@ -24,173 +23,142 @@ export default class MarkdownSmartSelect implements vscode.SelectionRangeProvide
private async provideSelectionRange(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise {
const headerRange = await this.getHeaderSelectionRange(document, position);
const blockRange = await this.getBlockSelectionRange(document, position, headerRange);
- return blockRange ? blockRange : headerRange ? headerRange : undefined;
+ return blockRange || headerRange;
}
private async getBlockSelectionRange(document: vscode.TextDocument, position: vscode.Position, headerRange?: vscode.SelectionRange): Promise {
const tokens = await this.engine.parse(document);
- let blockTokens = getTokensForPosition(tokens, position);
+ const blockTokens = getTokensForPosition(tokens, position);
if (blockTokens.length === 0) {
return undefined;
}
- let parentRange = headerRange ? headerRange : createBlockRange(document, position.line, blockTokens.shift());
- let currentRange: vscode.SelectionRange | undefined;
+ let currentRange: vscode.SelectionRange | undefined = headerRange ? headerRange : createBlockRange(blockTokens.shift()!, document, position.line);
- for (const token of blockTokens) {
- currentRange = createBlockRange(document, position.line, token, parentRange);
- if (currentRange) {
- parentRange = currentRange;
- }
- }
- if (currentRange) {
- return currentRange;
- } else {
- return parentRange;
+ for (let i = 0; i < blockTokens.length; i++) {
+ currentRange = createBlockRange(blockTokens[i], document, position.line, currentRange);
}
+ return currentRange;
}
private async getHeaderSelectionRange(document: vscode.TextDocument, position: vscode.Position): Promise {
+
const tocProvider = new TableOfContentsProvider(this.engine, document);
const toc = await tocProvider.getToc();
- let headerInfo = getHeadersForPosition(toc, position);
+ const headerInfo = getHeadersForPosition(toc, position);
- let headers = headerInfo.headers;
+ const headers = headerInfo.headers;
- let parentRange: vscode.SelectionRange | undefined;
let currentRange: vscode.SelectionRange | undefined;
for (let i = 0; i < headers.length; i++) {
- currentRange = createHeaderRange(i === headers.length - 1, headerInfo.headerOnThisLine, headers[i], parentRange, getFirstChildHeader(document, headers[i], toc));
- if (currentRange && currentRange.parent) {
- parentRange = currentRange;
- }
+ currentRange = createHeaderRange(headers[i], i === headers.length - 1, headerInfo.headerOnThisLine, currentRange, getFirstChildHeader(document, headers[i], toc));
}
return currentRange;
}
}
-function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, toc?: TocEntry[]): vscode.Position | undefined {
- let childRange: vscode.Position | undefined;
- if (header && toc) {
- let children = toc.filter(t => header.location.range.contains(t.location.range) && t.location.range.start.line > header.location.range.start.line).sort((t1, t2) => t1.line - t2.line);
- if (children.length > 0) {
- childRange = children[0].location.range.start;
- let lineText = document.lineAt(childRange.line - 1).text;
- return childRange ? childRange.translate(-1, lineText.length) : undefined;
- }
- }
- return undefined;
-}
-
-function getTokensForPosition(tokens: Token[], position: vscode.Position): Token[] {
- let enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && isBlockElement(token));
-
- if (enclosingTokens.length === 0) {
- return [];
- }
-
- let sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0]));
- return sortedTokens;
-}
-
function getHeadersForPosition(toc: TocEntry[], position: vscode.Position): { headers: TocEntry[], headerOnThisLine: boolean } {
- let enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line);
- let sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line));
- let onThisLine = toc.find(header => header.line === position.line) !== undefined;
+ const enclosingHeaders = toc.filter(header => header.location.range.start.line <= position.line && header.location.range.end.line >= position.line);
+ const sortedHeaders = enclosingHeaders.sort((header1, header2) => (header1.line - position.line) - (header2.line - position.line));
+ const onThisLine = toc.find(header => header.line === position.line) !== undefined;
return {
headers: sortedHeaders,
headerOnThisLine: onThisLine
};
}
-function isBlockElement(token: Token): boolean {
- return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type);
-}
-
-function createHeaderRange(isClosestHeaderToPosition: boolean, onHeaderLine: boolean, header?: TocEntry, parent?: vscode.SelectionRange, childStart?: vscode.Position): vscode.SelectionRange | undefined {
- if (header) {
- let contentRange = new vscode.Range(header.location.range.start.translate(1), header.location.range.end);
- let headerPlusContentRange = header.location.range;
- let partialContentRange = childStart && isClosestHeaderToPosition ? contentRange.with(undefined, childStart) : undefined;
- if (onHeaderLine && isClosestHeaderToPosition && childStart) {
- return new vscode.SelectionRange(header.location.range.with(undefined, childStart), new vscode.SelectionRange(header.location.range, parent));
- } else if (onHeaderLine && isClosestHeaderToPosition) {
- return new vscode.SelectionRange(header.location.range, parent);
- } else if (parent && parent.range.contains(headerPlusContentRange)) {
- if (partialContentRange) {
- return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange, parent))));
- } else {
- return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange, parent));
- }
- } else if (partialContentRange) {
- return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange))));
+function createHeaderRange(header: TocEntry, isClosestHeaderToPosition: boolean, onHeaderLine: boolean, parent?: vscode.SelectionRange, childStart?: vscode.Position): vscode.SelectionRange | undefined {
+ const contentRange = new vscode.Range(header.location.range.start.translate(1), header.location.range.end);
+ const headerPlusContentRange = header.location.range;
+ const partialContentRange = childStart && isClosestHeaderToPosition ? contentRange.with(undefined, childStart) : undefined;
+ if (onHeaderLine && isClosestHeaderToPosition && childStart) {
+ return new vscode.SelectionRange(header.location.range.with(undefined, childStart), new vscode.SelectionRange(header.location.range, parent));
+ } else if (onHeaderLine && isClosestHeaderToPosition) {
+ return new vscode.SelectionRange(header.location.range, parent);
+ } else if (parent && parent.range.contains(headerPlusContentRange)) {
+ if (partialContentRange) {
+ return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange, parent))));
} else {
- return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange));
+ return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange, parent));
}
+ } else if (partialContentRange) {
+ return new vscode.SelectionRange(partialContentRange, new vscode.SelectionRange(contentRange, (new vscode.SelectionRange(headerPlusContentRange))));
} else {
- return undefined;
+ return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(headerPlusContentRange));
}
}
+function getTokensForPosition(tokens: Token[], position: vscode.Position): Token[] {
+ const enclosingTokens = tokens.filter(token => token.map && (token.map[0] <= position.line && token.map[1] > position.line) && isBlockElement(token));
+ if (enclosingTokens.length === 0) {
+ return [];
+ }
+ const sortedTokens = enclosingTokens.sort((token1, token2) => (token2.map[1] - token2.map[0]) - (token1.map[1] - token1.map[0]));
+ return sortedTokens;
+}
-function createBlockRange(document: vscode.TextDocument, cursorLine: number, block?: Token, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined {
- if (block) {
- if (block.type === 'fence') {
- return createFencedRange(block, cursorLine, document, parent);
- } else {
- let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0];
- let endLine = startLine !== block.map[1] && isList(block.type) ? block.map[1] - 1 : block.map[1];
- let startPos = new vscode.Position(startLine, 0);
- let endPos = new vscode.Position(endLine, getEndCharacter(document, startLine, endLine));
- let range = new vscode.Range(startPos, endPos);
- if (parent && parent.range.contains(range) && !parent.range.isEqual(range)) {
- return new vscode.SelectionRange(range, parent);
- } else if (parent) {
- if (rangeLinesEqual(range, parent.range)) {
- return range.end.character > parent.range.end.character ? new vscode.SelectionRange(range) : parent;
- } else if (parent.range.end.line + 1 === range.end.line) {
- let adjustedRange = new vscode.Range(range.start, range.end.translate(-1, parent.range.end.character));
- if (adjustedRange.isEqual(parent.range)) {
- return parent;
- } else {
- return new vscode.SelectionRange(adjustedRange, parent);
- }
- } else if (parent.range.end.line === range.end.line) {
- let adjustedRange = new vscode.Range(parent.range.start, range.end.translate(undefined, parent.range.end.character));
- if (adjustedRange.isEqual(parent.range)) {
- return parent;
- } else {
- return new vscode.SelectionRange(adjustedRange, parent.parent);
- }
- } else {
+function createBlockRange(block: Token, document: vscode.TextDocument, cursorLine: number, parent?: vscode.SelectionRange): vscode.SelectionRange | undefined {
+ if (block.type === 'fence') {
+ return createFencedRange(block, cursorLine, document, parent);
+ } else {
+ let startLine = document.lineAt(block.map[0]).isEmptyOrWhitespace ? block.map[0] + 1 : block.map[0];
+ let endLine = startLine === block.map[1] ? block.map[1] : block.map[1] - 1;
+ if (block.type === 'paragraph_open' && block.map[1] - block.map[0] === 2) {
+ startLine = endLine = cursorLine;
+ } else if (isList(block) && document.lineAt(endLine).isEmptyOrWhitespace) {
+ endLine = endLine - 1;
+ }
+ const startPos = new vscode.Position(startLine, 0);
+ const endPos = new vscode.Position(endLine, document.lineAt(endLine).text?.length ?? 0);
+ const range = new vscode.Range(startPos, endPos);
+ if (parent && parent.range.contains(range) && !parent.range.isEqual(range)) {
+ return new vscode.SelectionRange(range, parent);
+ } else if (parent?.range.isEqual(range)) {
+ return parent;
+ } else if (parent) {
+ // parent doesn't contain range
+ if (rangeLinesEqual(range, parent.range)) {
+ return range.end.character > parent.range.end.character ? new vscode.SelectionRange(range) : parent;
+ } else if (parent.range.end.line + 1 === range.end.line) {
+ const adjustedRange = new vscode.Range(range.start, range.end.translate(-1, parent.range.end.character));
+ if (adjustedRange.isEqual(parent.range)) {
return parent;
+ } else {
+ return new vscode.SelectionRange(adjustedRange, parent);
+ }
+ } else if (parent.range.end.line === range.end.line) {
+ const adjustedRange = new vscode.Range(parent.range.start, range.end.translate(undefined, range.end.character));
+ if (adjustedRange.isEqual(parent.range)) {
+ return parent;
+ } else {
+ return new vscode.SelectionRange(adjustedRange, parent.parent);
}
} else {
- return new vscode.SelectionRange(range);
+ return parent;
}
+ } else {
+ return new vscode.SelectionRange(range);
}
- } else {
- return undefined;
}
}
function createFencedRange(token: Token, cursorLine: number, document: vscode.TextDocument, parent?: vscode.SelectionRange): vscode.SelectionRange {
const startLine = token.map[0];
const endLine = token.map[1] - 1;
- let onFenceLine = cursorLine === startLine || cursorLine === endLine;
- let fenceRange = new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length));
- let contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(new vscode.Position(startLine + 1, 0), new vscode.Position(endLine - 1, getEndCharacter(document, startLine + 1, endLine))) : undefined;
+ const onFenceLine = cursorLine === startLine || cursorLine === endLine;
+ const fenceRange = new vscode.Range(new vscode.Position(startLine, 0), new vscode.Position(endLine, document.lineAt(endLine).text.length));
+ const contentRange = endLine - startLine > 2 && !onFenceLine ? new vscode.Range(new vscode.Position(startLine + 1, 0), new vscode.Position(endLine - 1, document.lineAt(endLine - 1).text.length)) : undefined;
if (parent && contentRange) {
if (parent.range.contains(fenceRange) && !parent.range.isEqual(fenceRange)) {
return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(fenceRange, parent));
} else if (parent.range.isEqual(fenceRange)) {
return new vscode.SelectionRange(contentRange, parent);
} else if (rangeLinesEqual(fenceRange, parent.range)) {
- let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range;
+ const revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range;
return new vscode.SelectionRange(contentRange, new vscode.SelectionRange(revisedRange, getRealParent(parent, revisedRange)));
} else if (parent.range.end.line === fenceRange.end.line) {
parent.range.end.translate(undefined, fenceRange.end.character);
@@ -204,7 +172,7 @@ function createFencedRange(token: Token, cursorLine: number, document: vscode.Te
} else if (parent.range.isEqual(fenceRange)) {
return parent;
} else if (rangeLinesEqual(fenceRange, parent.range)) {
- let revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range;
+ const revisedRange = fenceRange.end.character > parent.range.end.character ? fenceRange : parent.range;
return new vscode.SelectionRange(revisedRange, parent.parent);
} else if (parent.range.end.line === fenceRange.end.line) {
parent.range.end.translate(undefined, fenceRange.end.character);
@@ -214,15 +182,12 @@ function createFencedRange(token: Token, cursorLine: number, document: vscode.Te
return new vscode.SelectionRange(fenceRange, parent);
}
-function isList(type: string): boolean {
- return type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(type) : false;
+function isList(token: Token): boolean {
+ return token.type ? ['ordered_list_open', 'list_item_open', 'bullet_list_open'].includes(token.type) : false;
}
-function getEndCharacter(document: vscode.TextDocument, startLine: number, endLine: number): number {
- let startLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0;
- let endLength = document.lineAt(startLine).text ? document.lineAt(startLine).text.length : 0;
- let endChar = Math.max(startLength, endLength);
- return startLine !== endLine ? 0 : endChar;
+function isBlockElement(token: Token): boolean {
+ return !['list_item_close', 'paragraph_close', 'bullet_list_close', 'inline', 'heading_close', 'heading_open'].includes(token.type);
}
function getRealParent(parent: vscode.SelectionRange, range: vscode.Range) {
@@ -233,6 +198,19 @@ function getRealParent(parent: vscode.SelectionRange, range: vscode.Range) {
return currentParent;
}
+function getFirstChildHeader(document: vscode.TextDocument, header?: TocEntry, toc?: TocEntry[]): vscode.Position | undefined {
+ let childRange: vscode.Position | undefined;
+ if (header && toc) {
+ let children = toc.filter(t => header.location.range.contains(t.location.range) && t.location.range.start.line > header.location.range.start.line).sort((t1, t2) => t1.line - t2.line);
+ if (children.length > 0) {
+ childRange = children[0].location.range.start;
+ const lineText = document.lineAt(childRange.line - 1).text;
+ return childRange ? childRange.translate(-1, lineText.length) : undefined;
+ }
+ }
+ return undefined;
+}
+
function rangeLinesEqual(range: vscode.Range, parent: vscode.Range) {
return range.start.line === parent.start.line && range.end.line === parent.end.line;
}
diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts
index 787df59acc3..3849f47cb19 100644
--- a/extensions/markdown-language-features/src/test/smartSelect.test.ts
+++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts
@@ -14,10 +14,10 @@ const CURSOR = '$$CURSOR$$';
const testFileName = vscode.Uri.file('test.md');
-suite.only('markdown.SmartSelect', () => {
+suite('markdown.SmartSelect', () => {
test('Smart select single word', async () => {
const ranges = await getSelectionRangesForDocument(`Hel${CURSOR}lo`);
- assertNestedRangesEqual(ranges![0], [0, 1]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 0]);
});
test('Smart select multi-line paragraph', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -26,12 +26,12 @@ suite.only('markdown.SmartSelect', () => {
`For example, the[node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter]`,
`(https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`
));
- assertNestedRangesEqual(ranges![0], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 2]);
});
test('Smart select paragraph', async () => {
const ranges = await getSelectionRangesForDocument(`Many of the core components and extensions to ${CURSOR}VS Code live in their own repositories on GitHub. For example, the [node debug adapter](https://github.com/microsoft/vscode-node-debug) and the [mono debug adapter](https://github.com/microsoft/vscode-mono-debug) have their own repositories. For a complete list, please visit the [Related Projects](https://github.com/microsoft/vscode/wiki/Related-Projects) page on our [wiki](https://github.com/microsoft/vscode/wiki).`);
- assertNestedRangesEqual(ranges![0], [0, 1]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 0]);
});
test('Smart select html block', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -40,7 +40,7 @@ suite.only('markdown.SmartSelect', () => {
`${CURSOR}
`,
`
`));
- assertNestedRangesEqual(ranges![0], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 2]);
});
test('Smart select header on header line', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -48,7 +48,7 @@ suite.only('markdown.SmartSelect', () => {
`# Header${CURSOR}`,
`Hello`));
- assertNestedRangesEqual(ranges![0], [0, 1]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 1]);
});
test('Smart select single word w grandparent header on text line', async () => {
@@ -59,7 +59,7 @@ suite.only('markdown.SmartSelect', () => {
`${CURSOR}Hello`
));
- assertNestedRangesEqual(ranges![0], [2, 2], [1, 2]);
+ assertNestedLineNumbersEqual(ranges![0], [2, 2], [1, 2]);
});
test('Smart select html block w parent header', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -69,7 +69,7 @@ suite.only('markdown.SmartSelect', () => {
`
`,
``));
- assertNestedRangesEqual(ranges![0], [1, 3], [1, 3], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 3], [0, 3]);
});
test('Smart select fenced code block', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -78,7 +78,7 @@ suite.only('markdown.SmartSelect', () => {
`a${CURSOR}`,
`~~~`));
- assertNestedRangesEqual(ranges![0], [0, 2]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 2]);
});
test('Smart select list', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -87,8 +87,7 @@ suite.only('markdown.SmartSelect', () => {
`- ${CURSOR}item 2`,
`- item 3`,
`- item 4`));
-
- assertNestedRangesEqual(ranges![0], [1, 1], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [1, 1], [0, 3]);
});
test('Smart select list with fenced code block', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -100,7 +99,7 @@ suite.only('markdown.SmartSelect', () => {
`- item 3`,
`- item 4`));
- assertNestedRangesEqual(ranges![0], [1, 3], [0, 5]);
+ assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 5]);
});
test('Smart select multi cursor', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -112,8 +111,8 @@ suite.only('markdown.SmartSelect', () => {
`- ${CURSOR}item 3`,
`- item 4`));
- assertNestedRangesEqual(ranges![0], [0, 0], [0, 5]);
- assertNestedRangesEqual(ranges![1], [4, 4], [0, 5]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 0], [0, 5]);
+ assertNestedLineNumbersEqual(ranges![1], [4, 4], [0, 5]);
});
test('Smart select nested block quotes', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -122,7 +121,7 @@ suite.only('markdown.SmartSelect', () => {
`> item 2`,
`>> ${CURSOR}item 3`,
`>> item 4`));
- assertNestedRangesEqual(ranges![0], [2, 4], [0, 4]);
+ assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [0, 3]);
});
test('Smart select multi nested block quotes', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -131,8 +130,7 @@ suite.only('markdown.SmartSelect', () => {
`>> item 2`,
`>>> ${CURSOR}item 3`,
`>>>> item 4`));
-
- assertNestedRangesEqual(ranges![0], [2, 3], [2, 4], [1, 4], [0, 4]);
+ assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [0, 3]);
});
test('Smart select subheader content', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -143,7 +141,7 @@ suite.only('markdown.SmartSelect', () => {
`${CURSOR}content 2`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [3, 3], [2, 3], [1, 3], [0, 3]);
});
test('Smart select subheader line', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -154,7 +152,7 @@ suite.only('markdown.SmartSelect', () => {
`content 2`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [2, 3], [1, 3], [0, 3]);
});
test('Smart select blank line', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -165,7 +163,7 @@ suite.only('markdown.SmartSelect', () => {
`content 2`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [1, 3], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [1, 3], [0, 3]);
});
test('Smart select line between paragraphs', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -174,7 +172,7 @@ suite.only('markdown.SmartSelect', () => {
`${CURSOR}`,
`paragraph 2`));
- assertNestedRangesEqual(ranges![0], [0, 3]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 2]);
});
test('Smart select empty document', async () => {
const ranges = await getSelectionRangesForDocument(``, [new vscode.Position(0, 0)]);
@@ -196,7 +194,7 @@ suite.only('markdown.SmartSelect', () => {
`more content`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]);
+ assertNestedLineNumbersEqual(ranges![0], [4, 6], [3, 9], [3, 10], [2, 10], [1, 10], [0, 10]);
});
test('Smart select list with one element without selecting child subheader', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -209,8 +207,7 @@ suite.only('markdown.SmartSelect', () => {
``,
`content 2`,
`# main header 2`));
-
- assertNestedRangesEqual(ranges![0], [2, 3], [1, 3], [1, 6], [0, 6]);
+ assertNestedLineNumbersEqual(ranges![0], [2, 2], [2, 3], [1, 3], [1, 6], [0, 6]);
});
test('Smart select content under header then subheaders and their content', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -224,7 +221,7 @@ suite.only('markdown.SmartSelect', () => {
`content 2`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [0, 3], [0, 6]);
+ assertNestedLineNumbersEqual(ranges![0], [0, 3], [0, 6]);
});
test('Smart select last blockquote element under header then subheaders and their content', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -242,7 +239,7 @@ suite.only('markdown.SmartSelect', () => {
`content 2`,
`# main header 2`));
- assertNestedRangesEqual(ranges![0], [4, 6], [2, 6], [1, 7], [1, 10], [0, 10]);
+ assertNestedLineNumbersEqual(ranges![0], [5, 5], [4, 5], [2, 5], [1, 7], [1, 10], [0, 10]);
});
test('Smart select content of subheader then subheader then content of main header then main header', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -266,7 +263,7 @@ suite.only('markdown.SmartSelect', () => {
`- content 2`,
`content 2`));
- assertNestedRangesEqual(ranges![0], [11, 12], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]);
+ assertNestedLineNumbersEqual(ranges![0], [11, 11], [9, 12], [9, 17], [8, 17], [1, 17], [0, 17]);
});
test('Smart select last line content of subheader then subheader then content of main header then main header', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -288,9 +285,9 @@ suite.only('markdown.SmartSelect', () => {
`- content 2`,
`- content 2`,
`- content 2`,
- `${CURSOR}content 2`));
+ `- ${CURSOR}content 2`));
- assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]);
+ assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]);
});
test('Smart select last line content after content of subheader then subheader then content of main header then main header', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -312,9 +309,9 @@ suite.only('markdown.SmartSelect', () => {
`- content 2`,
`- content 2`,
`- content 2`,
- `content 2${CURSOR}`));
+ `- content 2${CURSOR}`));
- assertNestedRangesEqual(ranges![0], [16, 17], [14, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]);
+ assertNestedLineNumbersEqual(ranges![0], [17, 17], [14, 17], [13, 17], [9, 17], [8, 17], [1, 17], [0, 17]);
});
test('Smart select fenced code block then list then rest of content', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -338,7 +335,7 @@ suite.only('markdown.SmartSelect', () => {
`- content 2`,
`- content 2`));
- assertNestedRangesEqual(ranges![0], [9, 11], [8, 12], [7, 17], [1, 17], [0, 17]);
+ assertNestedLineNumbersEqual(ranges![0], [9, 11], [8, 12], [7, 17], [1, 17], [0, 17]);
});
test('Smart select fenced code block then list then rest of content on fenced line', async () => {
const ranges = await getSelectionRangesForDocument(
@@ -362,15 +359,70 @@ suite.only('markdown.SmartSelect', () => {
`- content 2`,
`- content 2`));
- assertNestedRangesEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]);
+ assertNestedLineNumbersEqual(ranges![0], [8, 12], [7, 17], [1, 17], [0, 17]);
+ });
+ test('Smart select without multiple ranges', async () => {
+ const ranges = await getSelectionRangesForDocument(
+ joinLines(
+ `# main header 1`,
+ ``,
+ ``,
+ `- ${CURSOR}paragraph`,
+ `- content`));
+
+ assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [1, 4], [0, 4]);
+ });
+ test('Smart select on second level of a list', async () => {
+ const ranges = await getSelectionRangesForDocument(
+ joinLines(
+ `* level 0`,
+ ` * level 1`,
+ ` * level 1`,
+ ` * level 2`,
+ ` * level 1`,
+ ` * level ${CURSOR}1`,
+ `* level 0`));
+
+ assertNestedLineNumbersEqual(ranges![0], [5, 5], [1, 5], [0, 5], [0, 6]);
+ });
+ test('Smart select on third level of a list', async () => {
+ const ranges = await getSelectionRangesForDocument(
+ joinLines(
+ `* level 0`,
+ ` * level 1`,
+ ` * level 1`,
+ ` * level ${CURSOR}2`,
+ ` * level 2`,
+ ` * level 1`,
+ ` * level 1`,
+ `* level 0`));
+ assertNestedLineNumbersEqual(ranges![0], [3, 3], [3, 4], [2, 4], [1, 6], [0, 6], [0, 7]);
+ });
+ test('Smart select level 2 then level 1', async () => {
+ const ranges = await getSelectionRangesForDocument(
+ joinLines(
+ `* level 1`,
+ ` * level ${CURSOR}2`,
+ ` * level 2`,
+ `* level 1`));
+ assertNestedLineNumbersEqual(ranges![0], [1, 1], [1, 2], [0, 2], [0, 3]);
+ });
+ test('Smart select last list item', async () => {
+ const ranges = await getSelectionRangesForDocument(
+ joinLines(
+ `- level 1`,
+ `- level 2`,
+ `- level 2`,
+ `- level ${CURSOR}1`));
+ assertNestedLineNumbersEqual(ranges![0], [3, 3], [0, 3]);
});
});
-function assertNestedRangesEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) {
+function assertNestedLineNumbersEqual(range: vscode.SelectionRange, ...expectedRanges: [number, number][]) {
const lineage = getLineage(range);
assert.strictEqual(lineage.length, expectedRanges.length, `expected depth: ${expectedRanges.length}, but was ${lineage.length}`);
for (let i = 0; i < lineage.length; i++) {
- assertRangesEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`);
+ assertLineNumbersEqual(lineage[i], expectedRanges[i][0], expectedRanges[i][1], `parent at a depth of ${i}`);
}
}
@@ -384,7 +436,7 @@ function getLineage(range: vscode.SelectionRange): vscode.SelectionRange[] {
return result;
}
-function assertRangesEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) {
+function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine: number, endLine: number, message: string) {
assert.strictEqual(selectionRange.range.start.line, startLine, `failed on start line ${message}`);
assert.strictEqual(selectionRange.range.end.line, endLine, `failed on end line ${message}`);
}