mirror of
https://github.com/Silverquark/dance.git
synced 2024-09-11 10:45:43 +03:00
Fix search.selection for punctuation and non-ASCII words (fixes #179).
This commit is contained in:
parent
b1bf0b8550
commit
938fcb2873
44
src/commands/README.md
generated
44
src/commands/README.md
generated
@ -76,9 +76,9 @@
|
||||
<tr><td><a href="./search.ts#L26"><code>search.backward</code></a></td><td>Search backward</td><td><code>Alt+/</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L27"><code>search.backward.extend</code></a></td><td>Search backward (extend)</td><td><code>Shift+Alt+/</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L25"><code>search.extend</code></a></td><td>Search (extend)</td><td><code>Shift+/</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L138"><code>search.next.add</code></a></td><td>Add next match</td><td><code>Shift+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L139"><code>search.previous</code></a></td><td>Select previous match</td><td><code>Alt+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L140"><code>search.previous.add</code></a></td><td>Add previous match</td><td><code>Shift+Alt+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L162"><code>search.next.add</code></a></td><td>Add next match</td><td><code>Shift+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L163"><code>search.previous</code></a></td><td>Select previous match</td><td><code>Alt+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L164"><code>search.previous.add</code></a></td><td>Add previous match</td><td><code>Shift+Alt+N</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="./search.ts#L94"><code>search.selection.smart</code></a></td><td>Search current selection (smart)</td><td><code>Shift+8</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td><a href="#searchselection"><code>search.selection</code></a></td><td>Search current selection</td><td><code>Shift+Alt+8</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
<tr><td rowspan=35><a href="#seek"><code>seek</code></a></td><td><a href="#seekenclosing"><code>seek.enclosing</code></a></td><td>Select to next enclosing character</td><td><code>M</code> (<code>editorTextFocus && dance.mode == 'normal'</code>)</td></tr>
|
||||
@ -172,17 +172,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#L311"><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#L310"><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#L697"><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#L661"><code>selections.faceBackward</code></a></td><td>Backward selections</td><td></td></tr>
|
||||
<tr><td><a href="./selections.ts#L660"><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#L693"><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#L657"><code>selections.faceBackward</code></a></td><td>Backward selections</td><td></td></tr>
|
||||
<tr><td><a href="./selections.ts#L656"><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#L308"><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#L309"><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#L777"><code>selections.hideIndices</code></a></td><td>Hide selection indices</td><td></td></tr>
|
||||
<tr><td><a href="./selections.ts#L773"><code>selections.hideIndices</code></a></td><td>Hide selection indices</td><td></td></tr>
|
||||
<tr><td><a href="./selections.ts#L246"><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#L247"><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#L245"><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#L579"><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#L776"><code>selections.showIndices</code></a></td><td>Show selection indices</td><td></td></tr>
|
||||
<tr><td><a href="./selections.ts#L575"><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#L772"><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>
|
||||
@ -757,7 +757,7 @@ This command:
|
||||
- accepts a register (by default, it uses `slash`).
|
||||
- takes an argument `smart` of type `boolean`.
|
||||
|
||||
### [`search.next`](./search.ts#L131-L150)
|
||||
### [`search.next`](./search.ts#L155-L174)
|
||||
|
||||
Select next match.
|
||||
|
||||
@ -1151,7 +1151,7 @@ This command:
|
||||
- takes an argument `inverse` of type `boolean`.
|
||||
- takes an input of type `Input<string>`.
|
||||
|
||||
### [`selections.select`](./selections.ts#L352-L363)
|
||||
### [`selections.select`](./selections.ts#L350-L361)
|
||||
|
||||
Select within selections.
|
||||
|
||||
@ -1160,7 +1160,7 @@ This command:
|
||||
- takes an argument `interactive` of type `boolean`.
|
||||
- takes an input of type `Input<string | RegExp>`.
|
||||
|
||||
### [`selections.split`](./selections.ts#L384-L396)
|
||||
### [`selections.split`](./selections.ts#L380-L392)
|
||||
|
||||
Split selections.
|
||||
|
||||
@ -1170,7 +1170,7 @@ This command:
|
||||
- takes an argument `interactive` of type `boolean`.
|
||||
- takes an input of type `Input<string | RegExp>`.
|
||||
|
||||
### [`selections.splitLines`](./selections.ts#L421-L432)
|
||||
### [`selections.splitLines`](./selections.ts#L417-L428)
|
||||
|
||||
Split selections at line boundaries.
|
||||
|
||||
@ -1179,25 +1179,25 @@ This command:
|
||||
- may be repeated with a given number of repetitions.
|
||||
- takes an argument `excludeEol` of type `boolean`.
|
||||
|
||||
### [`selections.expandToLines`](./selections.ts#L475-L482)
|
||||
### [`selections.expandToLines`](./selections.ts#L471-L478)
|
||||
|
||||
Expand to lines.
|
||||
|
||||
Expand selections to contain full lines (including end-of-line characters).
|
||||
|
||||
### [`selections.trimLines`](./selections.ts#L509-L516)
|
||||
### [`selections.trimLines`](./selections.ts#L505-L512)
|
||||
|
||||
Trim lines.
|
||||
|
||||
Trim selections to only contain full lines (from start to line break).
|
||||
|
||||
### [`selections.trimWhitespace`](./selections.ts#L541-L548)
|
||||
### [`selections.trimWhitespace`](./selections.ts#L537-L544)
|
||||
|
||||
Trim whitespace.
|
||||
|
||||
Trim whitespace at beginning and end of selections.
|
||||
|
||||
### [`selections.reduce`](./selections.ts#L567-L586)
|
||||
### [`selections.reduce`](./selections.ts#L563-L582)
|
||||
|
||||
Reduce selections to their cursor.
|
||||
|
||||
@ -1213,7 +1213,7 @@ This command:
|
||||
- takes an argument `empty` of type `boolean`.
|
||||
- takes an argument `where` of type `"active" | "anchor" | "start" | "end" | "both"`.
|
||||
|
||||
### [`selections.changeDirection`](./selections.ts#L648-L663)
|
||||
### [`selections.changeDirection`](./selections.ts#L644-L659)
|
||||
|
||||
Change direction of selections.
|
||||
|
||||
@ -1226,7 +1226,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#L688-L706)
|
||||
### [`selections.copy`](./selections.ts#L684-L702)
|
||||
|
||||
Copy selections below.
|
||||
|
||||
@ -1240,15 +1240,15 @@ Copy selections below.
|
||||
This command:
|
||||
- may be repeated with a given number of repetitions.
|
||||
|
||||
### [`selections.merge`](./selections.ts#L740-L745)
|
||||
### [`selections.merge`](./selections.ts#L736-L741)
|
||||
|
||||
Merge contiguous selections.
|
||||
|
||||
### [`selections.open`](./selections.ts#L749-L752)
|
||||
### [`selections.open`](./selections.ts#L745-L748)
|
||||
|
||||
Open selected file.
|
||||
|
||||
### [`selections.toggleIndices`](./selections.ts#L767-L784)
|
||||
### [`selections.toggleIndices`](./selections.ts#L763-L780)
|
||||
|
||||
Toggle selection indices.
|
||||
|
||||
|
@ -106,25 +106,49 @@ export function selection(
|
||||
for (const selection of selections) {
|
||||
let text = escapeForRegExp(document.getText(selection));
|
||||
|
||||
if (smart) {
|
||||
const firstLine = document.lineAt(selection.start).text,
|
||||
firstLineStart = selection.start.character;
|
||||
if (text.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstLineStart === 0 || !isWord!(firstLine.charCodeAt(firstLineStart - 1))) {
|
||||
text = `\\b${text}`;
|
||||
if (smart) {
|
||||
let firstLine: string | undefined,
|
||||
isBeginningOfWord = isWord!(text.charCodeAt(0));
|
||||
|
||||
const firstLineStart = selection.start.character;
|
||||
|
||||
if (isBeginningOfWord && firstLineStart > 0) {
|
||||
firstLine = document.lineAt(selection.start).text;
|
||||
isBeginningOfWord = !isWord!(firstLine.charCodeAt(firstLineStart - 1));
|
||||
}
|
||||
|
||||
const lastLine = selection.isSingleLine ? firstLine : document.lineAt(selection.end).text,
|
||||
lastLineEnd = selection.end.character;
|
||||
const lastLineEnd = selection.end.character,
|
||||
lastLine = selection.isSingleLine && firstLine !== undefined
|
||||
? firstLine
|
||||
: document.lineAt(selection.end).text,
|
||||
isEndOfWord = lastLineEnd + 1 < lastLine.length
|
||||
&& isWord!(lastLine.charCodeAt(lastLineEnd - 1))
|
||||
&& !isWord!(lastLine.charCodeAt(lastLineEnd));
|
||||
|
||||
if (lastLineEnd >= lastLine.length || !isWord!(lastLine.charCodeAt(lastLineEnd))) {
|
||||
text = `${text}\\b`;
|
||||
if (isBeginningOfWord) {
|
||||
const prefix = text.charCodeAt(0) < 0x80 ? "\\b" : "(?<=^|\\P{L})";
|
||||
|
||||
text = prefix + text;
|
||||
}
|
||||
|
||||
if (isEndOfWord) {
|
||||
const suffix = text.charCodeAt(text.length - 1) < 0x80 ? "\\b" : "(?=\\P{L}|$)";
|
||||
|
||||
text += suffix;
|
||||
}
|
||||
}
|
||||
|
||||
texts.push(text);
|
||||
}
|
||||
|
||||
if (texts.length === 0) {
|
||||
throw new Error("all selections are empty");
|
||||
}
|
||||
|
||||
register.set(texts);
|
||||
}
|
||||
|
||||
|
@ -347,8 +347,6 @@ export function filter(
|
||||
});
|
||||
}
|
||||
|
||||
let lastSelectInput: RegExp | undefined;
|
||||
|
||||
/**
|
||||
* Select within selections.
|
||||
*
|
||||
@ -372,8 +370,6 @@ export function select(
|
||||
input = new RegExp(input, "mu");
|
||||
}
|
||||
|
||||
lastSelectInput = input;
|
||||
|
||||
Selections.set(Selections.bottomToTop(Selections.selectWithin(input, selections)));
|
||||
|
||||
return Promise.resolve(input);
|
||||
|
53
test/suite/commands/search-selected.md
Normal file
53
test/suite/commands/search-selected.md
Normal file
@ -0,0 +1,53 @@
|
||||
# ascii
|
||||
|
||||
```
|
||||
a a
|
||||
^ 0
|
||||
```
|
||||
|
||||
## ascii search-selected-smart
|
||||
[up](#ascii)
|
||||
|
||||
- .search.selection.smart
|
||||
- .search.next
|
||||
|
||||
```
|
||||
a a
|
||||
^ 0
|
||||
```
|
||||
|
||||
# ascii-punct
|
||||
|
||||
```
|
||||
. .
|
||||
^ 0
|
||||
```
|
||||
|
||||
## ascii-punct search-selected-smart
|
||||
[up](#ascii-punct)
|
||||
|
||||
- .search.selection.smart
|
||||
- .search.next
|
||||
|
||||
```
|
||||
. .
|
||||
^ 0
|
||||
```
|
||||
|
||||
# unicode
|
||||
|
||||
```
|
||||
é é
|
||||
^ 0
|
||||
```
|
||||
|
||||
## unicode search-selected-smart
|
||||
[up](#unicode)
|
||||
|
||||
- .search.selection.smart
|
||||
- .search.next
|
||||
|
||||
```
|
||||
é é
|
||||
^ 0
|
||||
```
|
78
test/suite/commands/search-selected.test.ts
generated
Normal file
78
test/suite/commands/search-selected.test.ts
generated
Normal file
@ -0,0 +1,78 @@
|
||||
import * as vscode from "vscode";
|
||||
|
||||
import { executeCommand, ExpectedDocument, groupTestsByParentName } from "../utils";
|
||||
|
||||
suite("./test/suite/commands/search-selected.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("ascii > search-selected-smart", async function () {
|
||||
// Set-up document to be in expected initial state.
|
||||
await ExpectedDocument.apply(editor, 6, String.raw`
|
||||
a a
|
||||
^ 0
|
||||
`);
|
||||
|
||||
// Perform all operations.
|
||||
await executeCommand("dance.search.selection.smart");
|
||||
await executeCommand("dance.search.next");
|
||||
|
||||
// Ensure document is as expected.
|
||||
ExpectedDocument.assertEquals(editor, "./test/suite/commands/search-selected.md:8:1", 6, String.raw`
|
||||
a a
|
||||
^ 0
|
||||
`);
|
||||
});
|
||||
|
||||
test("ascii-punct > search-selected-smart", async function () {
|
||||
// Set-up document to be in expected initial state.
|
||||
await ExpectedDocument.apply(editor, 6, String.raw`
|
||||
. .
|
||||
^ 0
|
||||
`);
|
||||
|
||||
// Perform all operations.
|
||||
await executeCommand("dance.search.selection.smart");
|
||||
await executeCommand("dance.search.next");
|
||||
|
||||
// Ensure document is as expected.
|
||||
ExpectedDocument.assertEquals(editor, "./test/suite/commands/search-selected.md:26:1", 6, String.raw`
|
||||
. .
|
||||
^ 0
|
||||
`);
|
||||
});
|
||||
|
||||
test("unicode > search-selected-smart", async function () {
|
||||
// Set-up document to be in expected initial state.
|
||||
await ExpectedDocument.apply(editor, 6, String.raw`
|
||||
é é
|
||||
^ 0
|
||||
`);
|
||||
|
||||
// Perform all operations.
|
||||
await executeCommand("dance.search.selection.smart");
|
||||
await executeCommand("dance.search.next");
|
||||
|
||||
// Ensure document is as expected.
|
||||
ExpectedDocument.assertEquals(editor, "./test/suite/commands/search-selected.md:44:1", 6, String.raw`
|
||||
é é
|
||||
^ 0
|
||||
`);
|
||||
});
|
||||
|
||||
groupTestsByParentName(this);
|
||||
});
|
Loading…
Reference in New Issue
Block a user