@@ -650,6 +650,143 @@ async function pickAdditions(
650650 } ) ;
651651}
652652
653+ /**
654+ * Show an expandable QuickPick to let the user pick a single class from the server.
655+ * Packages can be expanded to reveal their classes. Only leaf class items can be accepted.
656+ * Returns the class name (without the `.cls` extension), or `undefined` if the user escapes.
657+ */
658+ export async function pickClass ( api : AtelierAPI , title : string ) : Promise < string | undefined > {
659+ const query = "SELECT Name, Type FROM %Library.RoutineMgr_StudioOpenDialog(?,1,1,?,0,0,?)" ;
660+ let sys : "0" | "1" = "0" ;
661+ let gen : "0" | "1" = "0" ;
662+
663+ return new Promise < string | undefined > ( ( resolve ) => {
664+ const quickPick = vscode . window . createQuickPick < PickAdditionsItem > ( ) ;
665+ quickPick . title = title ;
666+ quickPick . ignoreFocusOut = true ;
667+ quickPick . keepScrollPosition = true ;
668+ quickPick . matchOnDescription = true ;
669+ quickPick . buttons = [
670+ {
671+ iconPath : new vscode . ThemeIcon ( "library" ) ,
672+ tooltip : "System" ,
673+ location : vscode . QuickInputButtonLocation . Input ,
674+ toggle : { checked : false } ,
675+ } ,
676+ {
677+ iconPath : new vscode . ThemeIcon ( "server-process" ) ,
678+ tooltip : "Generated" ,
679+ location : vscode . QuickInputButtonLocation . Input ,
680+ toggle : { checked : false } ,
681+ } ,
682+ ] ;
683+
684+ const getRootItems = ( ) : Promise < void > => {
685+ return api
686+ . actionQuery ( query , [ "*.cls" , sys , gen ] )
687+ . then ( ( data ) => {
688+ quickPick . items = data . result . content . map ( ( i ) => sodItemToPickAdditionsItem ( i ) ) ;
689+ quickPick . busy = false ;
690+ } )
691+ . catch ( ( error ) => {
692+ quickPick . hide ( ) ;
693+ handleError ( error , "Failed to get namespace contents." ) ;
694+ } ) ;
695+ } ;
696+
697+ const expandItem = ( itemIdx : number ) : Promise < void > => {
698+ const item = quickPick . items [ itemIdx ] ;
699+ // Switch the expand button to a collapse button
700+ const newItems = [ ...quickPick . items ] ;
701+ newItems [ itemIdx ] = {
702+ ...item ,
703+ buttons : [ { iconPath : new vscode . ThemeIcon ( "chevron-down" ) , tooltip : "Collapse" } ] ,
704+ } ;
705+ quickPick . items = newItems ;
706+ return api
707+ . actionQuery ( query , [ item . fullName + "/*.cls" , sys , gen ] )
708+ . then ( ( data ) => {
709+ const insertItems : PickAdditionsItem [ ] = data . result . content . map ( ( i ) =>
710+ sodItemToPickAdditionsItem ( i , item . fullName , item . label . search ( / \S / ) )
711+ ) ;
712+ const updatedItems = [ ...quickPick . items ] ;
713+ updatedItems . splice ( itemIdx + 1 , 0 , ...insertItems ) ;
714+ quickPick . items = updatedItems ;
715+ quickPick . busy = false ;
716+ } )
717+ . catch ( ( error ) => {
718+ quickPick . hide ( ) ;
719+ handleError ( error , "Failed to get namespace contents." ) ;
720+ } ) ;
721+ } ;
722+
723+ quickPick . onDidTriggerButton ( ( button ) => {
724+ quickPick . busy = true ;
725+ if ( button . tooltip == "System" ) {
726+ sys = button . toggle . checked ? "1" : "0" ;
727+ } else {
728+ gen = button . toggle . checked ? "1" : "0" ;
729+ }
730+ getRootItems ( ) ;
731+ } ) ;
732+
733+ quickPick . onDidTriggerItemButton ( ( event ) => {
734+ quickPick . busy = true ;
735+ const itemIdx = quickPick . items . findIndex ( ( i ) => i . fullName === event . item . fullName ) ;
736+ if ( event . button . tooltip . charAt ( 0 ) == "E" ) {
737+ expandItem ( itemIdx ) ;
738+ } else {
739+ // Collapse: remove the button and all descendants
740+ const newItems = [ ...quickPick . items ] ;
741+ newItems [ itemIdx ] = {
742+ ...newItems [ itemIdx ] ,
743+ buttons : [ { iconPath : new vscode . ThemeIcon ( "chevron-right" ) , tooltip : "Expand" } ] ,
744+ } ;
745+ quickPick . items = newItems . filter (
746+ ( i , idx ) => idx <= itemIdx || ! i . fullName . startsWith ( event . item . fullName + "." )
747+ ) ;
748+ quickPick . busy = false ;
749+ }
750+ } ) ;
751+
752+ quickPick . onDidChangeValue ( ( filter : string ) => {
753+ // Auto-expand a package when the user types its name followed by a dot
754+ if ( filter . endsWith ( "." ) ) {
755+ const itemIdx = quickPick . items . findIndex (
756+ ( i ) => i . fullName . toLowerCase ( ) === filter . slice ( 0 , - 1 ) . toLowerCase ( )
757+ ) ;
758+ if (
759+ itemIdx != - 1 &&
760+ quickPick . items [ itemIdx ] . buttons ?. length &&
761+ quickPick . items [ itemIdx ] . buttons [ 0 ] . tooltip . charAt ( 0 ) == "E"
762+ ) {
763+ quickPick . busy = true ;
764+ expandItem ( itemIdx ) ;
765+ }
766+ }
767+ } ) ;
768+
769+ quickPick . onDidAccept ( ( ) => {
770+ const selected = quickPick . activeItems [ 0 ] ;
771+ if ( selected && ! selected . buttons ?. length ) {
772+ // Leaf class item (no expand button): strip the .cls extension and resolve
773+ const name = selected . fullName . endsWith ( ".cls" ) ? selected . fullName . slice ( 0 , - 4 ) : selected . fullName ;
774+ resolve ( name ) ;
775+ quickPick . hide ( ) ;
776+ }
777+ } ) ;
778+
779+ quickPick . onDidHide ( ( ) => {
780+ resolve ( undefined ) ;
781+ quickPick . dispose ( ) ;
782+ } ) ;
783+
784+ quickPick . busy = true ;
785+ quickPick . show ( ) ;
786+ getRootItems ( ) ;
787+ } ) ;
788+ }
789+
653790export async function modifyProject (
654791 nodeOrUri : NodeBase | vscode . Uri | undefined ,
655792 type : "add" | "remove"
0 commit comments