1
1
import type { Edit , TextEditor } from "@cursorless/common" ;
2
- import { FlashStyle , Range } from "@cursorless/common" ;
2
+ import { FlashStyle , Range , zipStrict } from "@cursorless/common" ;
3
3
import { range as iterRange , map , pairwise } from "itertools" ;
4
- import { flatten , zip } from "lodash-es" ;
4
+ import { flatten } from "lodash-es" ;
5
5
import type { RangeUpdater } from "../core/updateSelections/RangeUpdater" ;
6
6
import { performEditsAndUpdateSelections } from "../core/updateSelections/updateSelections" ;
7
+ import { containingLineIfUntypedModifier } from "../processTargets/modifiers/commonContainingScopeIfUntypedModifiers" ;
8
+ import type { ModifierStageFactory } from "../processTargets/ModifierStageFactory" ;
9
+ import type { ModifierStage } from "../processTargets/PipelineStages.types" ;
7
10
import { ide } from "../singletons/ide.singleton" ;
11
+ import { getMatcher } from "../tokenizer" ;
8
12
import type { Target } from "../typings/target.types" ;
13
+ import { generateMatchesInRange } from "../util/getMatchesInRange" ;
9
14
import { flashTargets , runOnTargetsForEachEditor } from "../util/targetUtils" ;
10
15
import type { ActionReturnValue } from "./actions.types" ;
11
16
12
17
export default class JoinLines {
13
- constructor ( private rangeUpdater : RangeUpdater ) {
18
+ getFinalStages ( ) : ModifierStage [ ] {
19
+ return [ this . modifierStageFactory . create ( containingLineIfUntypedModifier ) ] ;
20
+ }
21
+
22
+ constructor (
23
+ private rangeUpdater : RangeUpdater ,
24
+ private modifierStageFactory : ModifierStageFactory ,
25
+ ) {
14
26
this . run = this . run . bind ( this ) ;
15
27
}
16
28
17
29
async run ( targets : Target [ ] ) : Promise < ActionReturnValue > {
18
- await flashTargets ( ide ( ) , targets , FlashStyle . pendingModification0 ) ;
30
+ await flashTargets (
31
+ ide ( ) ,
32
+ targets . map ( ( { thatTarget } ) => thatTarget ) ,
33
+ FlashStyle . pendingModification0 ,
34
+ ) ;
19
35
20
36
const thatSelections = flatten (
21
37
await runOnTargetsForEachEditor ( targets , async ( editor , targets ) => {
22
- const contentRanges = targets . map ( ( { contentRange } ) => contentRange ) ;
23
-
24
- const { contentRanges : updatedRanges } =
38
+ const { thatRanges : updatedThatRanges } =
25
39
await performEditsAndUpdateSelections ( {
26
40
rangeUpdater : this . rangeUpdater ,
27
41
editor : ide ( ) . getEditableTextEditor ( editor ) ,
28
- edits : getEdits ( editor , contentRanges ) ,
42
+ edits : getEdits ( editor , targets ) ,
29
43
selections : {
30
- contentRanges ,
44
+ thatRanges : targets . map ( ( { contentRange } ) => contentRange ) ,
31
45
} ,
32
46
} ) ;
33
47
34
- return zip ( targets , updatedRanges ) . map ( ( [ target , range ] ) => ( {
35
- editor : target ! . editor ,
36
- selection : range ! . toSelection ( target ! . isReversed ) ,
48
+ return zipStrict ( targets , updatedThatRanges ) . map ( ( [ target , range ] ) => ( {
49
+ editor,
50
+ selection : range . toSelection ( target . isReversed ) ,
37
51
} ) ) ;
38
52
} ) ,
39
53
) ;
@@ -42,28 +56,60 @@ export default class JoinLines {
42
56
}
43
57
}
44
58
45
- function getEdits ( editor : TextEditor , contentRanges : Range [ ] ) : Edit [ ] {
46
- const { document } = editor ;
59
+ function getEdits ( editor : TextEditor , targets : Target [ ] ) : Edit [ ] {
47
60
const edits : Edit [ ] = [ ] ;
48
61
49
- for ( const range of contentRanges ) {
50
- const startLine = range . start . line ;
51
- const endLine = range . isSingleLine ? startLine + 1 : range . end . line ;
62
+ for ( const target of targets ) {
63
+ const targetsEdits =
64
+ target . joinAs === "line"
65
+ ? getLineTargetEdits ( target )
66
+ : getTokenTargetEdits ( target ) ;
52
67
53
- const lineIter = map ( iterRange ( startLine , endLine + 1 ) , ( i ) =>
54
- document . lineAt ( i ) ,
55
- ) ;
56
- for ( const [ line1 , line2 ] of pairwise ( lineIter ) ) {
57
- edits . push ( {
58
- range : new Range (
59
- line1 . rangeTrimmed ?. end ?? line1 . range . end ,
60
- line2 . rangeTrimmed ?. start ?? line2 . range . start ,
61
- ) ,
62
- text : line2 . isEmptyOrWhitespace ? "" : " " ,
63
- isReplace : true ,
64
- } ) ;
65
- }
68
+ edits . push ( ...targetsEdits ) ;
66
69
}
67
70
68
71
return edits ;
69
72
}
73
+
74
+ function getTokenTargetEdits ( target : Target ) : Edit [ ] {
75
+ const { editor, contentRange } = target ;
76
+ const regex = getMatcher ( editor . document . languageId ) . tokenMatcher ;
77
+ const matches = generateMatchesInRange (
78
+ regex ,
79
+ editor ,
80
+ contentRange ,
81
+ "forward" ,
82
+ ) ;
83
+
84
+ return Array . from ( pairwise ( matches ) ) . map (
85
+ ( [ range1 , range2 ] ) : Edit => ( {
86
+ range : new Range ( range1 . end , range2 . start ) ,
87
+ text : "" ,
88
+ isReplace : true ,
89
+ } ) ,
90
+ ) ;
91
+ }
92
+
93
+ function getLineTargetEdits ( target : Target ) : Edit [ ] {
94
+ const { document } = target . editor ;
95
+ const range = target . contentRange ;
96
+ const startLine = range . start . line ;
97
+ const endLine = range . isSingleLine
98
+ ? Math . min ( startLine + 1 , document . lineCount - 1 )
99
+ : range . end . line ;
100
+
101
+ const lines = map ( iterRange ( startLine , endLine + 1 ) , ( i ) =>
102
+ document . lineAt ( i ) ,
103
+ ) ;
104
+
105
+ return Array . from ( pairwise ( lines ) ) . map (
106
+ ( [ line1 , line2 ] ) : Edit => ( {
107
+ range : new Range (
108
+ line1 . rangeTrimmed ?. end ?? line1 . range . end ,
109
+ line2 . rangeTrimmed ?. start ?? line2 . range . start ,
110
+ ) ,
111
+ text : line2 . isEmptyOrWhitespace ? "" : " " ,
112
+ isReplace : true ,
113
+ } ) ,
114
+ ) ;
115
+ }
0 commit comments