11import * as vscode from 'vscode'
2- import { getActiveRegularEditor } from '@zardoy/vscode-utils'
3- import { registerExtensionCommand } from 'vscode-framework'
2+ import { getActiveRegularEditor , rangeToSelection } from '@zardoy/vscode-utils'
3+ import { getExtensionCommandId , registerExtensionCommand , VSCodeQuickPickItem } from 'vscode-framework'
44import { showQuickPick } from '@zardoy/vscode-utils/build/quickPick'
5+ import _ from 'lodash'
6+ import { compact } from '@zardoy/utils'
57import { RequestOptionsTypes , RequestResponseTypes } from '../typescript/src/ipcTypes'
68import { sendCommand } from './sendCommand'
7- import { tsRangeToVscode } from './util'
9+ import { tsRangeToVscode , tsRangeToVscodeSelection } from './util'
810
911export default ( ) => {
1012 registerExtensionCommand ( 'removeFunctionArgumentsTypesInSelection' , async ( ) => {
@@ -55,45 +57,51 @@ export default () => {
5557 editor . selection = new vscode . Selection ( currentValueRange . start , currentValueRange . end )
5658 } )
5759
58- registerExtensionCommand ( 'pickAndInsertFunctionArguments' , async ( ) => {
59- const editor = getActiveRegularEditor ( )
60- if ( ! editor ) return
61- const result = await sendCommand < RequestResponseTypes [ 'pickAndInsertFunctionArguments' ] > ( 'pickAndInsertFunctionArguments' )
62- if ( ! result ) return
60+ const nodePicker = async < T > ( data : T [ ] , renderItem : ( item : T ) => Omit < VSCodeQuickPickItem , 'value' > & { nodeRange : [ number , number ] } ) => {
61+ const editor = vscode . window . activeTextEditor !
6362 const originalSelections = editor . selections
64-
65- const renderArgs = ( args : Array < [ name : string , type : string ] > ) => `${ args . map ( ( [ name , type ] ) => ( type ? `${ name } : ${ type } ` : name ) ) . join ( ', ' ) } `
66-
6763 let revealBack = true
68- const selectedFunction = await showQuickPick (
69- result . functions . map ( func => {
70- const [ name , _decl , args ] = func
64+
65+ // todo-p1 button to merge nodes with duplicated contents (e.g. same strings)
66+ const selected = await showQuickPick (
67+ data . map ( item => {
68+ const custom = renderItem ( item )
7169 return {
72- label : name ,
73- value : func ,
74- description : `(${ renderArgs ( args ) } )` ,
70+ ...custom ,
71+ value : item ,
7572 buttons : [
7673 {
7774 iconPath : new vscode . ThemeIcon ( 'go-to-file' ) ,
7875 tooltip : 'Go to declaration' ,
76+ action : 'goToStartPos' ,
77+ } ,
78+ {
79+ iconPath : new vscode . ThemeIcon ( 'arrow-both' ) ,
80+ tooltip : 'Add to selection' ,
81+ action : 'addSelection' ,
7982 } ,
8083 ] ,
8184 }
8285 } ) ,
8386 {
84- onDidTriggerItemButton ( event ) {
87+ title : 'Select node...' ,
88+ onDidTriggerItemButton ( { item, button } ) {
89+ const { action } = button as any
90+ const sel = tsRangeToVscodeSelection ( editor . document , ( item as any ) . nodeRange )
8591 revealBack = false
86- this . hide ( )
87- const pos = editor . document . positionAt ( event . item . value [ 1 ] [ 0 ] )
88- editor . selection = new vscode . Selection ( pos , pos )
89- editor . revealRange ( editor . selection , vscode . TextEditorRevealType . InCenterIfOutsideViewport )
92+ if ( action === 'goToStartPos' ) {
93+ editor . selection = new vscode . Selection ( sel . start , sel . start )
94+ editor . revealRange ( editor . selection , vscode . TextEditorRevealType . InCenterIfOutsideViewport )
95+ this . hide ( )
96+ } else {
97+ editor . selections = [ ...editor . selections , sel ]
98+ }
9099 } ,
91100 onDidChangeFirstActive ( item ) {
92- const pos = editor . document . positionAt ( item . value [ 1 ] [ 0 ] )
101+ const pos = editor . document . positionAt ( ( item as any ) . nodeRange [ 0 ] )
93102 editor . selection = new vscode . Selection ( pos , pos )
94103 editor . revealRange ( editor . selection , vscode . TextEditorRevealType . InCenterIfOutsideViewport )
95104 } ,
96- onDidShow ( ) { } ,
97105 matchOnDescription : true ,
98106 } ,
99107 )
@@ -102,6 +110,23 @@ export default () => {
102110 editor . revealRange ( editor . selection )
103111 }
104112
113+ return selected
114+ }
115+
116+ registerExtensionCommand ( 'pickAndInsertFunctionArguments' , async ( ) => {
117+ const editor = getActiveRegularEditor ( )
118+ if ( ! editor ) return
119+ const result = await sendCommand < RequestResponseTypes [ 'pickAndInsertFunctionArguments' ] > ( 'pickAndInsertFunctionArguments' )
120+ if ( ! result ) return
121+
122+ const renderArgs = ( args : Array < [ name : string , type : string ] > ) => `${ args . map ( ( [ name , type ] ) => ( type ? `${ name } : ${ type } ` : name ) ) . join ( ', ' ) } `
123+
124+ const selectedFunction = await nodePicker ( result . functions , ( [ name , decl , args ] ) => ( {
125+ label : name ,
126+ description : `(${ renderArgs ( args ) } )` ,
127+ nodeRange : decl ,
128+ } ) )
129+
105130 if ( ! selectedFunction ) return
106131 const selectedArgs = await showQuickPick (
107132 selectedFunction [ 2 ] . map ( arg => {
@@ -130,4 +155,64 @@ export default () => {
130155 }
131156 } )
132157 } )
158+
159+ registerExtensionCommand ( 'goToNodeBySyntaxKind' , async ( _arg , { filterWithSelection = false } : { filterWithSelection ?: boolean } = { } ) => {
160+ const editor = vscode . window . activeTextEditor
161+ if ( ! editor ) return
162+ const { document } = editor
163+ const result = await sendCommand < RequestResponseTypes [ 'filterBySyntaxKind' ] > ( 'filterBySyntaxKind' )
164+ if ( ! result ) return
165+ // todo optimize
166+ if ( filterWithSelection ) {
167+ result . nodesByKind = Object . fromEntries (
168+ compact (
169+ Object . entries ( result . nodesByKind ) . map ( ( [ kind , nodes ] ) => {
170+ const filteredNodes = nodes . filter ( ( { range : tsRange } ) =>
171+ editor . selections . some ( sel => sel . contains ( tsRangeToVscode ( document , tsRange ) ) ) ,
172+ )
173+ if ( filteredNodes . length === 0 ) return
174+ return [ kind , filteredNodes ]
175+ } ) ,
176+ ) ,
177+ )
178+ }
179+
180+ const selectedKindNodes = await showQuickPick (
181+ _ . sortBy ( Object . entries ( result . nodesByKind ) , ( [ , nodes ] ) => nodes . length )
182+ . reverse ( )
183+ . map ( ( [ kind , nodes ] ) => ( {
184+ label : kind ,
185+ description : nodes . length . toString ( ) ,
186+ value : nodes ,
187+ buttons : [
188+ {
189+ iconPath : new vscode . ThemeIcon ( 'arrow-both' ) ,
190+ tooltip : 'Select all nodes of this kind' ,
191+ } ,
192+ ] ,
193+ } ) ) ,
194+ {
195+ onDidTriggerItemButton ( button ) {
196+ editor . selections = button . item . value . map ( ( { range } ) => tsRangeToVscodeSelection ( document , range ) )
197+ this . hide ( )
198+ } ,
199+ } ,
200+ )
201+ if ( ! selectedKindNodes ) return
202+ const selectedNode = await nodePicker ( selectedKindNodes , node => ( {
203+ label : document
204+ . getText ( tsRangeToVscode ( document , node . range ) )
205+ . trim ( )
206+ . replace ( / \r ? \n \s + / g, ' ' ) ,
207+ nodeRange : node . range ,
208+ value : node ,
209+ } ) )
210+ if ( ! selectedNode ) return
211+ editor . selection = tsRangeToVscodeSelection ( document , selectedNode . range )
212+ editor . revealRange ( editor . selection )
213+ } )
214+
215+ registerExtensionCommand ( 'goToNodeBySyntaxKindWithinSelection' , async ( ) => {
216+ await vscode . commands . executeCommand ( getExtensionCommandId ( 'goToNodeBySyntaxKind' ) , { filterWithSelection : true } )
217+ } )
133218}
0 commit comments