Skip to content

Commit 0b786e9

Browse files
committed
Fit interior range to content
1 parent 2ee7ca1 commit 0b786e9

File tree

10 files changed

+206
-42
lines changed

10 files changed

+206
-42
lines changed
Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Range, TextEditor } from "vscode";
2-
import { fitRangeToLineContent } from "../modifiers/scopeTypeStages/LineStage";
1+
import { Range } from "vscode";
2+
import { shrinkRangeToFitContent } from "../../util/selectionUtils";
33
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
4-
import WeakTarget from "./WeakTarget";
4+
import PlainTarget from "./PlainTarget";
55

66
export default class DocumentTarget extends BaseTarget {
77
insertionDelimiter = "\n";
@@ -23,10 +23,11 @@ export default class DocumentTarget extends BaseTarget {
2323

2424
getInteriorStrict() {
2525
return [
26-
new WeakTarget({
26+
// Use plain target instead of interior target since we want the same content and removal range for a document interior.
27+
new PlainTarget({
2728
editor: this.editor,
2829
isReversed: this.isReversed,
29-
contentRange: getDocumentContentRange(this.editor),
30+
contentRange: shrinkRangeToFitContent(this.editor, this.contentRange),
3031
}),
3132
];
3233
}
@@ -35,31 +36,3 @@ export default class DocumentTarget extends BaseTarget {
3536
return this.state;
3637
}
3738
}
38-
39-
function getDocumentContentRange(editor: TextEditor) {
40-
const { document } = editor;
41-
let firstLineNum = 0;
42-
let lastLineNum = document.lineCount - 1;
43-
44-
for (let i = firstLineNum; i < document.lineCount; ++i) {
45-
if (!document.lineAt(i).isEmptyOrWhitespace) {
46-
firstLineNum = i;
47-
break;
48-
}
49-
}
50-
51-
for (let i = lastLineNum; i > -1; --i) {
52-
if (!document.lineAt(i).isEmptyOrWhitespace) {
53-
lastLineNum = i;
54-
break;
55-
}
56-
}
57-
58-
const firstLine = document.lineAt(firstLineNum);
59-
const lastLine = document.lineAt(lastLineNum);
60-
61-
return fitRangeToLineContent(
62-
editor,
63-
new Range(firstLine.range.start, lastLine.range.end)
64-
);
65-
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { shrinkRangeToFitContent } from "../../util/selectionUtils";
2+
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
3+
4+
export default class InteriorTarget extends BaseTarget {
5+
insertionDelimiter = "";
6+
7+
constructor(parameters: CommonTargetParameters) {
8+
super(parameters);
9+
}
10+
11+
get contentRange() {
12+
return shrinkRangeToFitContent(this.editor, this.state.contentRange);
13+
}
14+
15+
getLeadingDelimiterTarget = () => undefined;
16+
getTrailingDelimiterTarget = () => undefined;
17+
getRemovalRange = () => this.state.contentRange;
18+
19+
protected getCloneParameters() {
20+
return this.state;
21+
}
22+
}

src/processTargets/targets/SurroundingPairTarget.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
getTokenTrailingDelimiterTarget,
77
} from "../targetUtil/insertionRemovalBehaviors/TokenInsertionRemovalBehavior";
88
import BaseTarget, { CommonTargetParameters } from "./BaseTarget";
9+
import InteriorTarget from "./InteriorTarget";
910
import TokenTarget from "./TokenTarget";
10-
import WeakTarget from "./WeakTarget";
1111

1212
interface SurroundingPairTargetParameters extends CommonTargetParameters {
1313
/**
@@ -48,7 +48,7 @@ export default class SurroundingPairTarget extends BaseTarget {
4848

4949
getInteriorStrict() {
5050
return [
51-
new WeakTarget({
51+
new InteriorTarget({
5252
editor: this.editor,
5353
isReversed: this.isReversed,
5454
contentRange: this.interiorRange_,
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
languageId: typescript
2+
command:
3+
spokenForm: chuck core
4+
version: 2
5+
targets:
6+
- type: primitive
7+
modifiers:
8+
- {type: interiorOnly}
9+
usePrePhraseSnapshot: true
10+
action: {name: remove}
11+
initialState:
12+
documentContents: |-
13+
[
14+
"foo",
15+
"bar"
16+
]
17+
selections:
18+
- anchor: {line: 1, character: 10}
19+
active: {line: 1, character: 10}
20+
marks: {}
21+
finalState:
22+
documentContents: "[]"
23+
selections:
24+
- anchor: {line: 0, character: 1}
25+
active: {line: 0, character: 1}
26+
thatMark:
27+
- anchor: {line: 0, character: 1}
28+
active: {line: 0, character: 1}
29+
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: interiorOnly}]}]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
languageId: plaintext
2+
command:
3+
spokenForm: chuck core
4+
version: 2
5+
targets:
6+
- type: primitive
7+
modifiers:
8+
- {type: interiorOnly}
9+
usePrePhraseSnapshot: true
10+
action: {name: remove}
11+
initialState:
12+
documentContents: ( hello )
13+
selections:
14+
- anchor: {line: 0, character: 6}
15+
active: {line: 0, character: 6}
16+
marks: {}
17+
finalState:
18+
documentContents: ()
19+
selections:
20+
- anchor: {line: 0, character: 1}
21+
active: {line: 0, character: 1}
22+
thatMark:
23+
- anchor: {line: 0, character: 1}
24+
active: {line: 0, character: 1}
25+
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: interiorOnly}]}]

src/test/suite/fixtures/recorded/surroundingPair/parseTree/python/clearMatching9.yml renamed to src/test/suite/fixtures/recorded/surroundingPair/parseTree/python/clearInside.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
languageId: python
22
command:
33
version: 1
4-
spokenForm: clear matching
4+
spokenForm: clear inside
55
action: clearAndSetSelection
66
targets:
77
- type: primitive
@@ -17,7 +17,9 @@ initialState:
1717
active: {line: 1, character: 0}
1818
marks: {}
1919
finalState:
20-
documentContents: "\"\"\"\"\"\""
20+
documentContents: |-
21+
"""
22+
"""
2123
selections:
2224
- anchor: {line: 0, character: 3}
2325
active: {line: 0, character: 3}

src/test/suite/fixtures/recorded/surroundingPair/parseTree/typescript/takeCore.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ finalState:
2929
)
3030
}
3131
selections:
32-
- anchor: {line: 1, character: 45}
33-
active: {line: 3, character: 4}
32+
- anchor: {line: 2, character: 4}
33+
active: {line: 2, character: 30}
3434
thatMark:
35-
- anchor: {line: 1, character: 45}
36-
active: {line: 3, character: 4}
35+
- anchor: {line: 2, character: 4}
36+
active: {line: 2, character: 30}
3737
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: containingScope, scopeType: {type: surroundingPair, delimiter: any}}, {type: interiorOnly}]}]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
languageId: typescript
2+
command:
3+
spokenForm: take core
4+
version: 2
5+
targets:
6+
- type: primitive
7+
modifiers:
8+
- {type: interiorOnly}
9+
usePrePhraseSnapshot: true
10+
action: {name: setSelection}
11+
initialState:
12+
documentContents: |-
13+
[
14+
"foo",
15+
"bar"
16+
]
17+
selections:
18+
- anchor: {line: 1, character: 10}
19+
active: {line: 1, character: 10}
20+
marks: {}
21+
finalState:
22+
documentContents: |-
23+
[
24+
"foo",
25+
"bar"
26+
]
27+
selections:
28+
- anchor: {line: 1, character: 4}
29+
active: {line: 2, character: 9}
30+
thatMark:
31+
- anchor: {line: 1, character: 4}
32+
active: {line: 2, character: 9}
33+
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: interiorOnly}]}]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
languageId: plaintext
2+
command:
3+
spokenForm: take core
4+
version: 2
5+
targets:
6+
- type: primitive
7+
modifiers:
8+
- {type: interiorOnly}
9+
usePrePhraseSnapshot: true
10+
action: {name: setSelection}
11+
initialState:
12+
documentContents: ( hello )
13+
selections:
14+
- anchor: {line: 0, character: 6}
15+
active: {line: 0, character: 6}
16+
marks: {}
17+
finalState:
18+
documentContents: ( hello )
19+
selections:
20+
- anchor: {line: 0, character: 2}
21+
active: {line: 0, character: 7}
22+
thatMark:
23+
- anchor: {line: 0, character: 2}
24+
active: {line: 0, character: 7}
25+
fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: interiorOnly}]}]

src/util/selectionUtils.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Position, Range, Selection } from "vscode";
1+
import { Position, Range, Selection, TextEditor } from "vscode";
22
import { SelectionWithEditor } from "../typings/Types";
33

44
export function isForward(selection: Selection) {
@@ -42,3 +42,58 @@ export function selectionFromRange(isReversed: boolean, range: Range) {
4242
const { start, end } = range;
4343
return isReversed ? new Selection(end, start) : new Selection(start, end);
4444
}
45+
46+
export function shrinkRangeToFitContent(editor: TextEditor, range: Range) {
47+
const { lineAt } = editor.document;
48+
49+
const start = (() => {
50+
const firstLine = lineAt(range.start);
51+
const text = firstLine.text.substring(range.start.character);
52+
if (text.trim().length === 0) {
53+
for (let i = firstLine.lineNumber + 1; i <= range.end.line; ++i) {
54+
const line = lineAt(i);
55+
if (!line.isEmptyOrWhitespace) {
56+
return new Position(i, line.firstNonWhitespaceCharacterIndex);
57+
}
58+
}
59+
return range.start;
60+
}
61+
return new Position(
62+
firstLine.lineNumber,
63+
firstNonWhitespaceCharacterIndex(range, text)
64+
);
65+
})();
66+
67+
const end = (() => {
68+
const endLine = lineAt(range.end);
69+
const text = endLine.text.substring(0, range.end.character);
70+
if (text.trim().length === 0) {
71+
for (let i = endLine.lineNumber - 1; i >= range.start.line; --i) {
72+
const line = lineAt(i);
73+
if (!line.isEmptyOrWhitespace) {
74+
return new Position(
75+
i,
76+
lastNonWhitespaceCharacterIndex(line.range, line.text)
77+
);
78+
}
79+
}
80+
return range.end;
81+
}
82+
return new Position(
83+
endLine.lineNumber,
84+
lastNonWhitespaceCharacterIndex(range, text)
85+
);
86+
})();
87+
88+
return new Range(start, end);
89+
}
90+
91+
function firstNonWhitespaceCharacterIndex(range: Range, text: string) {
92+
const characterDelta = text.length - text.trimStart().length;
93+
return range.start.character + characterDelta;
94+
}
95+
96+
function lastNonWhitespaceCharacterIndex(range: Range, text: string) {
97+
const characterDelta = text.length - text.trimEnd().length;
98+
return range.end.character - characterDelta;
99+
}

0 commit comments

Comments
 (0)