1
- import { isUndefined , max , min } from 'lodash' ;
1
+ import { isUndefined , max , min , isNumber } from 'lodash' ;
2
2
import { deepEqual as equal } from '../util/object' ;
3
3
import { Scanner , ScannerState , Token } from './clojure-lexer' ;
4
4
import { LispTokenCursor } from './token-cursor' ;
5
+ import type { Selection , TextDocument } from 'vscode' ;
5
6
6
7
let scanner : Scanner ;
7
8
@@ -45,26 +46,59 @@ export class ModelEdit {
45
46
*
46
47
* This will be in line with vscode when it comes to anchor/active, but introduce our own terminology for the span of the selection. It will also keep the tradition of paredit with backward/forward and up/down.
47
48
*/
48
-
49
49
export class ModelEditSelection {
50
50
private _anchor : number ;
51
51
private _active : number ;
52
-
53
- constructor ( anchor : number , active ?: number ) {
54
- this . _anchor = anchor ;
55
- if ( active !== undefined ) {
56
- this . _active = active ;
52
+ private _start : number ;
53
+ private _end : number ;
54
+ private _isReversed : boolean ;
55
+
56
+ constructor ( anchor : number , active ?: number , start ?: number , end ?: number , isReversed ?: boolean ) ;
57
+ constructor ( selection : Selection , doc : TextDocument ) ;
58
+ constructor (
59
+ anchorOrSelection : number | Selection ,
60
+ activeOrDoc ?: number | TextDocument ,
61
+ start ?: number ,
62
+ end ?: number ,
63
+ isReversed ?: boolean
64
+ ) {
65
+ if ( isNumber ( anchorOrSelection ) ) {
66
+ const anchor = anchorOrSelection ;
67
+ this . _anchor = anchor ;
68
+ if ( activeOrDoc !== undefined && isNumber ( activeOrDoc ) ) {
69
+ this . _active = activeOrDoc ;
70
+ } else {
71
+ this . _active = anchor ;
72
+ }
73
+ isReversed = isReversed ?? this . _anchor > this . _active ;
74
+ this . _isReversed = isReversed ;
75
+ this . _start = start ?? isReversed ? this . _active : Math . min ( anchor , this . _active ) ;
76
+ this . _end = end ?? isReversed ? anchor : Math . max ( anchor , this . _active ) ;
57
77
} else {
58
- this . _active = anchor ;
78
+ const { active, anchor, start, end, isReversed } = anchorOrSelection ;
79
+ // const doc = getActiveTextEditor().document;
80
+ const doc = activeOrDoc as TextDocument ;
81
+ this . _active = doc . offsetAt ( active ) ;
82
+ this . _anchor = doc . offsetAt ( anchor ) ;
83
+ this . _start = doc . offsetAt ( start ) ;
84
+ this . _end = doc . offsetAt ( end ) ;
85
+ this . _isReversed = isReversed ;
59
86
}
60
87
}
61
88
89
+ private _updateDirection ( ) {
90
+ this . _start = Math . min ( this . _anchor , this . _active ) ;
91
+ this . _end = Math . max ( this . _anchor , this . _active ) ;
92
+ this . _isReversed = this . _active < this . _anchor ;
93
+ }
94
+
62
95
get anchor ( ) {
63
96
return this . _anchor ;
64
97
}
65
98
66
99
set anchor ( v : number ) {
67
100
this . _anchor = v ;
101
+ this . _updateDirection ( ) ;
68
102
}
69
103
70
104
get active ( ) {
@@ -73,6 +107,67 @@ export class ModelEditSelection {
73
107
74
108
set active ( v : number ) {
75
109
this . _active = v ;
110
+ this . _updateDirection ( ) ;
111
+ }
112
+
113
+ get start ( ) {
114
+ this . _updateDirection ( ) ;
115
+ return this . _start ;
116
+ }
117
+
118
+ /* set start(v: number) {
119
+ // TODO: figure out .start setter logic
120
+ this._start = v;
121
+ if (this._start === this._anchor) {
122
+ this._isReversed = false;
123
+ } else if (this._start === this._active) {
124
+ this._isReversed = true;
125
+ } else if (this._isReversed) {
126
+ this._active = this._start;
127
+ } else if (!this._isReversed) {
128
+ this._anchor = this._start;
129
+ }
130
+ } */
131
+
132
+ get end ( ) {
133
+ this . _updateDirection ( ) ;
134
+ return this . _end ;
135
+ }
136
+
137
+ /* set end(v: number) {
138
+ // TODO: figure out .end setter logic
139
+ // TODO: figure out .start setter logic
140
+ this.end = v;
141
+
142
+ if (this._end < this._start) {
143
+ this._start;
144
+ }
145
+
146
+ if (this.end === this._anchor) {
147
+ this._isReversed = true;
148
+ } else if (this.end === this._active) {
149
+ this._isReversed = false;
150
+ } else if (this._isReversed) {
151
+ this._anchor = this.end;
152
+ } else if (!this._isReversed) {
153
+ this._active = this.end;
154
+ }
155
+ } */
156
+
157
+ get isReversed ( ) {
158
+ this . _updateDirection ( ) ;
159
+ return this . _isReversed ;
160
+ }
161
+
162
+ set isReversed ( isReversed : boolean ) {
163
+ this . _isReversed = isReversed ;
164
+ if ( this . _isReversed ) {
165
+ this . _start = this . _active ;
166
+ this . _end = this . _anchor ;
167
+ } else {
168
+ this . _start = this . _anchor ;
169
+ this . _end = this . _active ;
170
+ }
76
171
}
77
172
78
173
clone ( ) {
@@ -88,6 +183,11 @@ export type ModelEditOptions = {
88
183
selections ?: ModelEditSelection [ ] ;
89
184
} ;
90
185
186
+ export type ModelEditResult = {
187
+ edits : ModelEdit [ ] ;
188
+ selections : ModelEditSelection [ ] ;
189
+ success : boolean ;
190
+ } ;
91
191
export interface EditableModel {
92
192
readonly lineEndingLength : number ;
93
193
@@ -96,7 +196,7 @@ export interface EditableModel {
96
196
* For some EditableModel's these are performed as one atomic set of edits.
97
197
* @param edits
98
198
*/
99
- edit : ( edits : ModelEdit [ ] , options : ModelEditOptions ) => Thenable < boolean > ;
199
+ edit : ( edits : ModelEdit [ ] , options : ModelEditOptions ) => Thenable < ModelEditResult > ;
100
200
101
201
getText : ( start : number , end : number , mustBeWithin ?: boolean ) => string ;
102
202
getLineText : ( line : number ) => string ;
@@ -105,10 +205,8 @@ export interface EditableModel {
105
205
}
106
206
107
207
export interface EditableDocument {
108
- selection : ModelEditSelection ;
109
208
selections : ModelEditSelection [ ] ;
110
209
model : EditableModel ;
111
- // selectionStack: ModelEditSelection[];
112
210
/**
113
211
* A stack of selections - that is, a 2d array, where the outer array index is a point in "selection/form nesting order" and the inner array index is which cursor that ModelEditSelection belongs to. That "selection/form nesting order" axis can be thought of as the axis for time, or something close to that. That is, .selectionStacks
114
212
* is only used when the user invokes the "Expand Selection" or "Shrink Selection" Paredit commands, such that each time the user invokes "Expand", it pushes an item onto the stack. Similarly, when "Shrink" is invoked, the last item
@@ -124,10 +222,10 @@ export interface EditableDocument {
124
222
selectionsStack : ModelEditSelection [ ] [ ] ;
125
223
getTokenCursor : ( offset ?: number , previous ?: boolean ) => LispTokenCursor ;
126
224
insertString : ( text : string ) => void ;
127
- getSelectionText : ( ) => string ;
128
225
getSelectionTexts : ( ) => string [ ] ;
129
- delete : ( ) => Thenable < boolean > ;
130
- backspace : ( ) => Thenable < boolean > ;
226
+ getSelectionText : ( index : number ) => string ;
227
+ delete : ( index ?: number ) => Thenable < ModelEditResult > ;
228
+ backspace : ( index ?: number ) => Thenable < ModelEditResult > ;
131
229
}
132
230
133
231
/** The underlying model for the REPL readline. */
@@ -372,7 +470,7 @@ export class LineInputModel implements EditableModel {
372
470
* Doesn't need to be atomic in the LineInputModel.
373
471
* @param edits
374
472
*/
375
- edit ( edits : ModelEdit [ ] , options : ModelEditOptions ) : Thenable < boolean > {
473
+ edit ( edits : ModelEdit [ ] , options : ModelEditOptions ) : Thenable < ModelEditResult > {
376
474
return new Promise ( ( resolve , reject ) => {
377
475
for ( const edit of edits ) {
378
476
switch ( edit . editFn ) {
@@ -396,9 +494,9 @@ export class LineInputModel implements EditableModel {
396
494
}
397
495
}
398
496
if ( this . document && options . selections ) {
399
- this . document . selections = [ options . selections [ 0 ] ] ;
497
+ this . document . selections = options . selections ;
400
498
}
401
- resolve ( true ) ;
499
+ resolve ( { edits , selections : options . selections , success : true } ) ;
402
500
} ) ;
403
501
}
404
502
@@ -548,7 +646,6 @@ export class StringDocument implements EditableDocument {
548
646
}
549
647
}
550
648
551
- selection : ModelEditSelection ;
552
649
selections : ModelEditSelection [ ] ;
553
650
554
651
model : LineInputModel = new LineInputModel ( 1 , this ) ;
@@ -563,30 +660,43 @@ export class StringDocument implements EditableDocument {
563
660
return this . model . getTokenCursor ( offset ) ;
564
661
}
565
662
566
- getSelectionsText : ( ) => string [ ] ;
567
663
insertString ( text : string ) {
568
664
this . model . insertString ( 0 , text ) ;
569
665
}
570
666
571
667
getSelectionTexts : ( ) => string [ ] ;
572
- getSelectionText : ( ) => string ;
573
-
574
- delete ( ) {
575
- return this . model . edit (
576
- [ this . selection ] . map ( ( { anchor : p } ) => new ModelEdit ( 'deleteRange' , [ p , 1 ] ) ) ,
577
- {
578
- selections : this . selections . map ( ( { anchor : p } ) => new ModelEditSelection ( p ) ) ,
579
- }
580
- ) ;
668
+ getSelectionText : ( index : number ) => string ;
669
+
670
+ delete ( index ?: number ) {
671
+ if ( isUndefined ( index ) ) {
672
+ return this . model . edit (
673
+ this . selections . map ( ( { anchor : p } ) => new ModelEdit ( 'deleteRange' , [ p , 1 ] ) ) ,
674
+ {
675
+ selections : this . selections . map ( ( { anchor : p } ) => new ModelEditSelection ( p ) ) ,
676
+ }
677
+ ) ;
678
+ } else {
679
+ return this . model . edit ( [ new ModelEdit ( 'deleteRange' , [ ( this . selections [ index ] . anchor , 1 ) ] ) ] , {
680
+ selections : [ new ModelEditSelection ( this . selections [ index ] . anchor ) ] ,
681
+ } ) ;
682
+ }
581
683
}
582
- getSelectionText : ( ) => string ;
583
684
584
- backspace ( ) {
585
- return this . model . edit (
586
- [ this . selection ] . map ( ( { anchor : p } ) => new ModelEdit ( 'deleteRange' , [ p - 1 , 1 ] ) ) ,
587
- {
588
- selections : [ this . selection ] . map ( ( { anchor : p } ) => new ModelEditSelection ( p - 1 ) ) ,
589
- }
590
- ) ;
685
+ backspace ( index ?: number ) {
686
+ if ( isUndefined ( index ) ) {
687
+ return this . model . edit (
688
+ this . selections . map ( ( { anchor : p } ) => new ModelEdit ( 'deleteRange' , [ p - 1 , 1 ] ) ) ,
689
+ {
690
+ selections : this . selections . map ( ( { anchor : p } ) => new ModelEditSelection ( p - 1 ) ) ,
691
+ }
692
+ ) ;
693
+ } else {
694
+ return this . model . edit (
695
+ [ new ModelEdit ( 'deleteRange' , [ this . selections [ index ] . anchor - 1 , 1 ] ) ] ,
696
+ {
697
+ selections : [ new ModelEditSelection ( this . selections [ index ] . anchor - 1 ) ] ,
698
+ }
699
+ ) ;
700
+ }
591
701
}
592
702
}
0 commit comments