1
1
import type {
2
2
CursorlessCommandId ,
3
+ Range ,
3
4
ScopeProvider ,
5
+ ScopeRanges ,
4
6
ScopeSupportInfo ,
5
7
ScopeType ,
6
8
ScopeTypeInfo ,
9
+ Selection ,
10
+ TextEditor ,
7
11
} from "@cursorless/common" ;
8
12
import {
9
13
CURSORLESS_SCOPE_TREE_VIEW_ID ,
@@ -43,6 +47,7 @@ import type {
43
47
ScopeVisualizer ,
44
48
VisualizationType ,
45
49
} from "./ScopeVisualizerCommandApi" ;
50
+ import { getRangeLength } from "../../cursorless-engine/src/util/rangeUtils" ;
46
51
47
52
export const DONT_SHOW_TALON_UPDATE_MESSAGE_KEY = "dontShowUpdateTalonMessage" ;
48
53
@@ -51,6 +56,7 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
51
56
private treeView : TreeView < MyTreeItem > ;
52
57
private supportLevels : ScopeSupportInfo [ ] = [ ] ;
53
58
private shownUpdateTalonMessage = false ;
59
+ private selection : Selection | null = null ;
54
60
55
61
private _onDidChangeTreeData : EventEmitter <
56
62
MyTreeItem | undefined | null | void
@@ -66,8 +72,6 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
66
72
private customSpokenFormGenerator : CustomSpokenFormGenerator ,
67
73
private hasCommandServer : boolean ,
68
74
) {
69
- this . onChangeTextSelection = this . onChangeTextSelection . bind ( this ) ;
70
-
71
75
this . treeView = vscodeApi . window . createTreeView (
72
76
CURSORLESS_SCOPE_TREE_VIEW_ID ,
73
77
{
@@ -103,46 +107,6 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
103
107
}
104
108
}
105
109
106
- private onChangeTextSelection ( e : TextEditorSelectionChangeEvent ) {
107
- if ( e . selections . length !== 1 ) {
108
- return ;
109
- }
110
-
111
- const editor = ide ( ) . activeTextEditor ;
112
-
113
- if ( editor == null ) {
114
- return ;
115
- }
116
-
117
- const selection = fromVscodeSelection ( e . selections [ 0 ] ) ;
118
-
119
- console . log ( "selection" , selection . concise ( ) ) ;
120
-
121
- for ( const supportLevel of this . supportLevels ) {
122
- if ( supportLevel . support !== ScopeSupport . supportedAndPresentInEditor ) {
123
- continue ;
124
- }
125
-
126
- const scopes = this . scopeProvider . provideScopeRangesForRange (
127
- editor ,
128
- supportLevel . scopeType ,
129
- selection ,
130
- ) ;
131
-
132
- if ( scopes . length === 0 ) {
133
- continue ;
134
- }
135
-
136
- console . log ( supportLevel . scopeType . type ) ;
137
-
138
- for ( const scope of scopes ) {
139
- for ( const target of scope . targets ) {
140
- console . log ( target . contentRange . concise ( ) ) ;
141
- }
142
- }
143
- }
144
- }
145
-
146
110
private registerScopeSupportListener ( ) {
147
111
this . visibleDisposable = disposableFrom (
148
112
this . scopeProvider . onDidChangeScopeSupport ( ( supportLevels ) => {
@@ -152,9 +116,13 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
152
116
this . scopeVisualizer . onDidChangeScopeType ( ( ) => {
153
117
this . _onDidChangeTreeData . fire ( ) ;
154
118
} ) ,
155
- this . vscodeApi . window . onDidChangeTextEditorSelection (
156
- this . onChangeTextSelection ,
157
- ) ,
119
+ this . vscodeApi . window . onDidChangeTextEditorSelection ( ( e ) => {
120
+ this . selection =
121
+ e . selections . length === 1
122
+ ? fromVscodeSelection ( e . selections [ 0 ] )
123
+ : null ;
124
+ this . _onDidChangeTreeData . fire ( ) ;
125
+ } ) ,
158
126
) ;
159
127
}
160
128
@@ -172,6 +140,10 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
172
140
return this . getScopeTypesWithSupport ( element . scopeSupport ) ;
173
141
}
174
142
143
+ if ( element instanceof SelectedCategoryTreeItem ) {
144
+ return this . getSelectedScopeTypes ( ) ;
145
+ }
146
+
175
147
throw new Error ( "Unexpected element" ) ;
176
148
}
177
149
@@ -210,18 +182,7 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
210
182
private getScopeTypesWithSupport (
211
183
scopeSupport : ScopeSupport ,
212
184
) : ScopeSupportTreeItem [ ] {
213
- return this . supportLevels
214
- . filter (
215
- ( supportLevel ) =>
216
- supportLevel . support === scopeSupport &&
217
- // Skip scope if it doesn't have a spoken form and it's private. That
218
- // is the default state for scopes that are private; we don't want to
219
- // show these to the user.
220
- ! (
221
- supportLevel . spokenForm . type === "error" &&
222
- supportLevel . spokenForm . isPrivate
223
- ) ,
224
- )
185
+ return this . getScopeSupportInfo ( scopeSupport )
225
186
. map (
226
187
( supportLevel ) =>
227
188
new ScopeSupportTreeItem (
@@ -250,16 +211,69 @@ export class ScopeTreeProvider implements TreeDataProvider<MyTreeItem> {
250
211
} ) ;
251
212
}
252
213
214
+ private getSelectedScopeTypes ( ) : ScopeSupportTreeItem [ ] {
215
+ if ( this . selection == null ) {
216
+ return [ ] ;
217
+ }
218
+
219
+ const editor = ide ( ) . activeTextEditor ;
220
+ const selection = this . selection ;
221
+
222
+ if ( editor == null ) {
223
+ return [ ] ;
224
+ }
225
+
226
+ return this . getScopeSupportInfo ( ScopeSupport . supportedAndPresentInEditor )
227
+ . map ( ( supportLevel ) => {
228
+ const scopes = this . scopeProvider . provideScopeRangesForRange (
229
+ editor ,
230
+ supportLevel . scopeType ,
231
+ selection ,
232
+ ) ;
233
+ return {
234
+ supportLevel,
235
+ length : getSmallestTargetLength ( editor , selection , scopes ) ,
236
+ } ;
237
+ } )
238
+ . filter ( ( { length } ) => length > - 1 )
239
+ . sort ( ( a , b ) => a . length - b . length )
240
+ . map ( ( { supportLevel, length } ) => {
241
+ console . log ( serializeScopeType ( supportLevel . scopeType ) , length ) ;
242
+ return new ScopeSupportTreeItem (
243
+ supportLevel ,
244
+ isEqual ( supportLevel . scopeType , this . scopeVisualizer . scopeType ) ,
245
+ ) ;
246
+ } ) ;
247
+ }
248
+
249
+ private getScopeSupportInfo ( scopeSupport : ScopeSupport ) : ScopeSupportInfo [ ] {
250
+ return this . supportLevels . filter (
251
+ ( supportLevel ) =>
252
+ supportLevel . support === scopeSupport &&
253
+ // Skip scope if it doesn't have a spoken form and it's private. That
254
+ // is the default state for scopes that are private; we don't want to
255
+ // show these to the user.
256
+ ! (
257
+ supportLevel . spokenForm . type === "error" &&
258
+ supportLevel . spokenForm . isPrivate
259
+ ) ,
260
+ ) ;
261
+ }
262
+
253
263
dispose ( ) {
254
264
this . visibleDisposable ?. dispose ( ) ;
255
265
}
256
266
}
257
267
258
- function getSupportCategories ( ) : SupportCategoryTreeItem [ ] {
268
+ function getSupportCategories ( ) : (
269
+ | SupportCategoryTreeItem
270
+ | SelectedCategoryTreeItem
271
+ ) [ ] {
259
272
return [
260
273
new SupportCategoryTreeItem ( ScopeSupport . supportedAndPresentInEditor ) ,
261
274
new SupportCategoryTreeItem ( ScopeSupport . supportedButNotPresentInEditor ) ,
262
275
new SupportCategoryTreeItem ( ScopeSupport . unsupported ) ,
276
+ new SelectedCategoryTreeItem ( ) ,
263
277
] ;
264
278
}
265
279
@@ -378,7 +392,17 @@ class SupportCategoryTreeItem extends TreeItem {
378
392
}
379
393
}
380
394
381
- type MyTreeItem = ScopeSupportTreeItem | SupportCategoryTreeItem ;
395
+ class SelectedCategoryTreeItem extends TreeItem {
396
+ constructor ( ) {
397
+ super ( "Selected" , TreeItemCollapsibleState . Collapsed ) ;
398
+ this . description = "scopes" ;
399
+ }
400
+ }
401
+
402
+ type MyTreeItem =
403
+ | ScopeSupportTreeItem
404
+ | SupportCategoryTreeItem
405
+ | SelectedCategoryTreeItem ;
382
406
383
407
/**
384
408
* Get file extension example from vscode [Language Id](https://code.visualstudio.com/docs/languages/identifiers)
@@ -405,3 +429,29 @@ export function getLanguageExtensionSampleFromLanguageId(
405
429
}
406
430
}
407
431
}
432
+
433
+ function getSmallestTargetLength (
434
+ editor : TextEditor ,
435
+ selection : Range ,
436
+ scopes : ScopeRanges [ ] ,
437
+ ) : number {
438
+ let length : number | null = null ;
439
+ for ( const scope of scopes ) {
440
+ for ( const target of scope . targets ) {
441
+ // Don't use targets smaller than the selection
442
+ if (
443
+ selection . contains ( target . contentRange ) &&
444
+ ! selection . isRangeEqual ( target . contentRange )
445
+ ) {
446
+ continue ;
447
+ }
448
+ const targetIntersection = target . contentRange . intersection ( selection ) ;
449
+ if ( targetIntersection == null ) {
450
+ continue ;
451
+ }
452
+ const targetLength = getRangeLength ( editor , target . contentRange ) ;
453
+ length = length != null ? Math . min ( length , targetLength ) : targetLength ;
454
+ }
455
+ }
456
+ return length ?? - 1 ;
457
+ }
0 commit comments