Fix issues with select (fixes #149) and splitLines (fixes #150).

This commit is contained in:
Grégoire Geis 2021-05-08 14:51:52 +02:00
parent e6dd3dda50
commit 80b8258da5
8 changed files with 257 additions and 25 deletions

View File

@ -1691,6 +1691,29 @@ export namespace Selections {
*/
export const from = fromAnchorActive;
/**
* Sorts selections in the given direction. If `Forward`, selections will be
* sorted from top to bottom. Otherwise, they will be sorted from bottom to
* top.
*/
export function sort(direction: Direction, selections: vscode.Selection[] = current.slice()) {
return selections.sort(direction === Direction.Forward ? sortTopToBottom : sortBottomToTop);
}
/**
* Sorts selections from top to bottom.
*/
export function topToBottom(selections: vscode.Selection[] = current.slice()) {
return selections.sort(sortTopToBottom);
}
/**
* Sorts selections from bottom to top.
*/
export function bottomToTop(selections: vscode.Selection[] = current.slice()) {
return selections.sort(sortBottomToTop);
}
/**
* Shifts empty selections by one character to the left.
*/
@ -1932,3 +1955,11 @@ export namespace Selections {
return caretModeSelections;
}
}
function sortTopToBottom(a: vscode.Selection, b: vscode.Selection) {
return a.start.compareTo(b.start);
}
function sortBottomToTop(a: vscode.Selection, b: vscode.Selection) {
return b.start.compareTo(a.start);
}

35
src/commands/README.md generated
View File

@ -173,17 +173,17 @@
<tr><td><a href="#selectionsselect"><code>selections.select</code></a></td><td>Select within selections</td><td><code>S</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L301"><code>selections.clear.main</code></a></td><td>Clear main selections</td><td><code>Alt+Space</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L300"><code>selections.clear.secondary</code></a></td><td>Clear secondary selections</td><td><code>Space</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L658"><code>selections.copy.above</code></a></td><td>Copy selections above</td><td><code>Shift+Alt+C</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L622"><code>selections.faceBackward</code></a></td><td>Backward selections</td><td></td></tr>
<tr><td><a href="./selections.ts#L621"><code>selections.faceForward</code></a></td><td>Forward selections</td><td><code>Shift+Alt+;</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L662"><code>selections.copy.above</code></a></td><td>Copy selections above</td><td><code>Shift+Alt+C</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L626"><code>selections.faceBackward</code></a></td><td>Backward selections</td><td></td></tr>
<tr><td><a href="./selections.ts#L625"><code>selections.faceForward</code></a></td><td>Forward selections</td><td><code>Shift+Alt+;</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L298"><code>selections.filter.regexp</code></a></td><td>Keep matching selections</td><td><code>Alt+K</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L299"><code>selections.filter.regexp.inverse</code></a></td><td>Clear matching selections</td><td><code>Shift+Alt+K</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L738"><code>selections.hideIndices</code></a></td><td>Hide selection indices</td><td></td></tr>
<tr><td><a href="./selections.ts#L742"><code>selections.hideIndices</code></a></td><td>Hide selection indices</td><td></td></tr>
<tr><td><a href="./selections.ts#L235"><code>selections.pipe.append</code></a></td><td>Pipe and append</td><td><code>Shift+1</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L236"><code>selections.pipe.prepend</code></a></td><td>Pipe and prepend</td><td><code>Shift+Alt+1</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L234"><code>selections.pipe.replace</code></a></td><td>Pipe and replace</td><td><code>Shift+\</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L566"><code>selections.reduce.edges</code></a></td><td>Reduce selections to their ends</td><td><code>Shift+Alt+S</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L737"><code>selections.showIndices</code></a></td><td>Show selection indices</td><td></td></tr>
<tr><td><a href="./selections.ts#L570"><code>selections.reduce.edges</code></a></td><td>Reduce selections to their ends</td><td><code>Shift+Alt+S</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="./selections.ts#L741"><code>selections.showIndices</code></a></td><td>Show selection indices</td><td></td></tr>
<tr><td><a href="#selectionssplit"><code>selections.split</code></a></td><td>Split selections</td><td><code>Shift+S</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="#selectionssplitLines"><code>selections.splitLines</code></a></td><td>Split selections at line boundaries</td><td><code>Alt+S</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
<tr><td><a href="#selectionstoggleIndices"><code>selections.toggleIndices</code></a></td><td>Toggle selection indices</td><td><code>Enter</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
@ -1047,7 +1047,7 @@ This command:
- takes an argument `interactive` of type `boolean`.
- takes an input of type `Input<string | RegExp>`.
### [`selections.split`](./selections.ts#L376-L388)
### [`selections.split`](./selections.ts#L377-L389)
Split selections.
@ -1057,33 +1057,34 @@ This command:
- takes an argument `interactive` of type `boolean`.
- takes an input of type `Input<string | RegExp>`.
### [`selections.splitLines`](./selections.ts#L411-L421)
### [`selections.splitLines`](./selections.ts#L412-L423)
Split selections at line boundaries.
This command:
- may be repeated with a given number of repetitions.
- takes an argument `excludeEol` of type `boolean`.
### [`selections.expandToLines`](./selections.ts#L462-L469)
### [`selections.expandToLines`](./selections.ts#L466-L473)
Expand to lines.
Expand selections to contain full lines (including end-of-line characters).
### [`selections.trimLines`](./selections.ts#L496-L503)
### [`selections.trimLines`](./selections.ts#L500-L507)
Trim lines.
Trim selections to only contain full lines (from start to line break).
### [`selections.trimWhitespace`](./selections.ts#L528-L535)
### [`selections.trimWhitespace`](./selections.ts#L532-L539)
Trim whitespace.
Trim whitespace at beginning and end of selections.
### [`selections.reduce`](./selections.ts#L554-L572)
### [`selections.reduce`](./selections.ts#L558-L576)
Reduce selections to their cursor.
@ -1098,7 +1099,7 @@ Reduce selections to their cursor.
This command:
- takes an argument `where` of type `"active" | "anchor" | "start" | "end" | "both"`.
### [`selections.changeDirection`](./selections.ts#L609-L624)
### [`selections.changeDirection`](./selections.ts#L613-L628)
Change direction of selections.
@ -1111,7 +1112,7 @@ Change direction of selections.
| Forward selections | `faceForward` | `a-:` (normal) | `[".selections.changeDirection", { direction: 1 }]` |
| Backward selections | `faceBackward` | | `[".selections.changeDirection", { direction: -1 }]` |
### [`selections.copy`](./selections.ts#L649-L667)
### [`selections.copy`](./selections.ts#L653-L671)
Copy selections below.
@ -1125,15 +1126,15 @@ Copy selections below.
This command:
- may be repeated with a given number of repetitions.
### [`selections.merge`](./selections.ts#L701-L706)
### [`selections.merge`](./selections.ts#L705-L710)
Merge contiguous selections.
### [`selections.open`](./selections.ts#L710-L713)
### [`selections.open`](./selections.ts#L714-L717)
Open selected file.
### [`selections.toggleIndices`](./selections.ts#L728-L745)
### [`selections.toggleIndices`](./selections.ts#L732-L749)
Toggle selection indices.

View File

@ -1123,7 +1123,7 @@ async function loadSelectionsModule(): Promise<CommandDescriptor[]> {
),
new CommandDescriptor(
"dance.selections.splitLines",
(_, argument) => _.runAsync((_) => splitLines(_, _.document, _.selections, getRepetitions(_, argument))),
(_, argument) => _.runAsync((_) => splitLines(_, _.document, _.selections, getRepetitions(_, argument), argument.excludeEol)),
CommandDescriptor.Flags.RequiresActiveEditor,
),
new CommandDescriptor(

View File

@ -365,7 +365,8 @@ export function select(
lastSelectInput = input;
Selections.set(Selections.selectWithin(input, selections));
Selections.set(
Selections.bottomToTop(Selections.selectWithin(input, selections)));
return Promise.resolve(input);
});
@ -402,7 +403,7 @@ export function split(
split = split.filter((s) => !s.isEmpty);
}
Selections.set(split);
Selections.set(Selections.bottomToTop(split));
return Promise.resolve(input);
});
@ -418,8 +419,10 @@ export function splitLines(
document: vscode.TextDocument,
selections: readonly vscode.Selection[],
repetitions: number,
excludeEol: Argument<boolean> = false,
) {
const newSelections = [] as vscode.Selection[];
const newSelections = [] as vscode.Selection[],
lineEnd = excludeEol ? Positions.lineEnd : Positions.lineBreak;
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i],
@ -435,28 +438,29 @@ export function splitLines(
return;
}
// Add start line.
newSelections.push(
Selections.fromStartEnd(start, Positions.lineEnd(startLine, document), isReversed, document),
Selections.fromStartEnd(start, lineEnd(startLine, document), isReversed, document),
);
// Add intermediate lines.
for (let line = startLine + repetitions; line < endLine; line += repetitions) {
const start = Positions.lineStart(line),
end = Positions.lineEnd(line, document);
end = lineEnd(line, document);
newSelections.push(Selections.fromStartEnd(start, end, isReversed, document));
}
// Add end line.
if (endLine % repetitions === 0) {
if (endLine % repetitions === 0 && end.character > 0) {
newSelections.push(
Selections.fromStartEnd(Positions.lineStart(endLine), end, isReversed, document),
);
}
}
Selections.set(newSelections);
Selections.set(Selections.bottomToTop(newSelections));
}
/**

View File

@ -0,0 +1,20 @@
# 1
```
foo bar
^^^^^^^^ 0
baz
^^^ 0
```
## 1 select
[up](#1)
- .selections.select { input: "b" }
```
foo bar
^ 1
baz
^ 0
```

View File

@ -0,0 +1,45 @@
import * as vscode from "vscode";
import { executeCommand, ExpectedDocument, groupTestsByParentName } from "../utils";
suite("./test/suite/commands/selections-select.md", function () {
// Set up document.
let document: vscode.TextDocument,
editor: vscode.TextEditor;
this.beforeAll(async () => {
document = await vscode.workspace.openTextDocument();
editor = await vscode.window.showTextDocument(document);
editor.options.insertSpaces = true;
editor.options.tabSize = 2;
await executeCommand("dance.dev.setSelectionBehavior", { mode: "normal", value: "caret" });
});
this.afterAll(async () => {
await executeCommand("workbench.action.closeActiveEditor");
});
test("1 > select", async function () {
// Set-up document to be in expected initial state.
await ExpectedDocument.apply(editor, 6, String.raw`
foo bar
^^^^^^^^ 0
baz
^^^ 0
`);
// Perform all operations.
await executeCommand("dance.selections.select", { input: "b" });
// Ensure document is as expected.
ExpectedDocument.assertEquals(editor, "./test/suite/commands/selections-select.md:10:1", 6, String.raw`
foo bar
^ 1
baz
^ 0
`);
});
groupTestsByParentName(this);
});

View File

@ -0,0 +1,53 @@
# 1
```
first line
^^^^^^^^^^^ 0
second line
^^^^^^^^^^^^ 0
third line
^^^^^^^^^^^ 0
```
## 1 split
[up](#1)
- .selections.splitLines
```
first line
^^^^^^^^^^^ 2
second line
^^^^^^^^^^^^ 1
third line
^^^^^^^^^^^ 0
```
# 2
```
first line
^^^^^^^^^^^ 0
second line
^^^^^ 0 ^^^^ 1
third line
^^^^^ 1
```
## 2 split
[up](#2)
- .selections.splitLines
```
first line
^^^^^^^^^^^ 3
second line
^^^^^ 2 ^^^^ 1
third line
^^^^^ 0
```

View File

@ -0,0 +1,78 @@
import * as vscode from "vscode";
import { executeCommand, ExpectedDocument, groupTestsByParentName } from "../utils";
suite("./test/suite/commands/selections-split.md", function () {
// Set up document.
let document: vscode.TextDocument,
editor: vscode.TextEditor;
this.beforeAll(async () => {
document = await vscode.workspace.openTextDocument();
editor = await vscode.window.showTextDocument(document);
editor.options.insertSpaces = true;
editor.options.tabSize = 2;
await executeCommand("dance.dev.setSelectionBehavior", { mode: "normal", value: "caret" });
});
this.afterAll(async () => {
await executeCommand("workbench.action.closeActiveEditor");
});
test("1 > split", async function () {
// Set-up document to be in expected initial state.
await ExpectedDocument.apply(editor, 6, String.raw`
first line
^^^^^^^^^^^ 0
second line
^^^^^^^^^^^^ 0
third line
^^^^^^^^^^^ 0
`);
// Perform all operations.
await executeCommand("dance.selections.splitLines");
// Ensure document is as expected.
ExpectedDocument.assertEquals(editor, "./test/suite/commands/selections-split.md:13:1", 6, String.raw`
first line
^^^^^^^^^^^ 2
second line
^^^^^^^^^^^^ 1
third line
^^^^^^^^^^^ 0
`);
});
test("2 > split", async function () {
// Set-up document to be in expected initial state.
await ExpectedDocument.apply(editor, 6, String.raw`
first line
^^^^^^^^^^^ 0
second line
^^^^^ 0 ^^^^ 1
third line
^^^^^ 1
`);
// Perform all operations.
await executeCommand("dance.selections.splitLines");
// Ensure document is as expected.
ExpectedDocument.assertEquals(editor, "./test/suite/commands/selections-split.md:40:1", 6, String.raw`
first line
^^^^^^^^^^^ 3
second line
^^^^^ 2 ^^^^ 1
third line
^^^^^ 0
`);
});
groupTestsByParentName(this);
});