@@ -993,6 +993,106 @@ class PhpDebugSession extends vscode.DebugSession {
993993 this . sendResponse ( response )
994994 }
995995
996+ protected async completionsRequest ( response : VSCodeDebugProtocol . CompletionsResponse , args : VSCodeDebugProtocol . CompletionsArguments ) {
997+ try {
998+ if ( ! args . frameId ) {
999+ throw new Error ( 'No stack frame given' ) ;
1000+ }
1001+ const lineIndex : number = args . line ? args . line - 1 : 0 ;
1002+ const lines : string [ ] = args . text . split ( '\n' ) ;
1003+ /** The text before the cursor */
1004+ const typed : string = [ ...lines . slice ( 0 , Math . max ( lineIndex - 1 , 0 ) ) , lines [ lineIndex ] . substring ( 0 , args . column ) ] . join ( '\n' ) ;
1005+ let i = typed . length ;
1006+ let containerName : string ;
1007+ let operator : string | undefined ;
1008+ let query : string ;
1009+ while ( true ) {
1010+ const substr = typed . substring ( 0 , i ) ;
1011+ if ( / \[ $ / . test ( substr ) ) {
1012+ // Numeric array index
1013+ operator = '[' ;
1014+ } else if ( / \[ ' $ / . test ( substr ) ) {
1015+ // String array index
1016+ operator = `['` ;
1017+ } else if ( / - > $ / . test ( substr ) ) {
1018+ operator = '->' ;
1019+ } else if ( i > 0 ) {
1020+ i -- ;
1021+ continue ;
1022+ }
1023+ query = typed . substr ( i ) . toLowerCase ( ) ;
1024+ containerName = typed . substring ( 0 , operator ? i - operator . length : i ) ;
1025+ break ;
1026+ }
1027+ const frame = this . _stackFrames . get ( args . frameId ) ;
1028+ const contexts = await frame . getContexts ( ) ;
1029+ const targets : VSCodeDebugProtocol . CompletionItem [ ] = [ ] ;
1030+ if ( ! containerName || ! operator ) {
1031+ const responses = await Promise . all ( contexts . map ( context => context . getProperties ( ) ) ) ;
1032+ for ( const properties of responses ) {
1033+ for ( const property of properties ) {
1034+ if ( property . name . toLowerCase ( ) . startsWith ( query ) ) {
1035+ const text = property . name [ 0 ] === '$' ? property . name . substr ( 1 ) : property . name ;
1036+ targets . push ( { label : property . name , text, type : 'variable' , start : i , length : property . name . length } ) ;
1037+ }
1038+ }
1039+ }
1040+ } else {
1041+ // Search all contexts
1042+ for ( const context of contexts ) {
1043+ let response : xdebug . PropertyGetResponse | undefined ;
1044+ try {
1045+ response = await frame . connection . sendPropertyGetCommand ( { context, fullName : containerName } ) ;
1046+ } catch ( err ) {
1047+ // ignore
1048+ }
1049+ if ( response ) {
1050+ for ( const property of response . children ) {
1051+ if ( property . name . toLowerCase ( ) . startsWith ( query ) ) {
1052+ let type : VSCodeDebugProtocol . CompletionItemType | undefined ;
1053+ let text : string = property . name ;
1054+ if ( operator === '->' ) {
1055+ // Object
1056+ type = 'property' ;
1057+ } else if ( operator [ 0 ] === '[' ) {
1058+ // Array
1059+ if ( parseInt ( property . name ) + '' === property . name ) {
1060+ // Numeric index
1061+ if ( operator [ 1 ] === `'` ) {
1062+ continue ;
1063+ }
1064+ type = 'value' ;
1065+ text += ']' ;
1066+ } else {
1067+ // String index
1068+ if ( operator [ 1 ] !== `'` ) {
1069+ if ( query ) {
1070+ continue ;
1071+ } else {
1072+ text = `'` + text ;
1073+ }
1074+ }
1075+ type = 'text' ;
1076+ text += `']` ;
1077+ }
1078+ }
1079+ targets . push ( { label : property . name , text, type, start : i , length : property . name . length } ) ;
1080+ }
1081+ }
1082+ // If we found the variable in one context (typically Locals), abort
1083+ break ;
1084+ }
1085+ }
1086+ }
1087+ response . body = { targets} ;
1088+ } catch ( err ) {
1089+ this . sendErrorResponse ( response , err ) ;
1090+ return ;
1091+ }
1092+ this . sendResponse ( response ) ;
1093+ }
1094+
1095+
9961096 protected async continueRequest (
9971097 response : VSCodeDebugProtocol . ContinueResponse ,
9981098 args : VSCodeDebugProtocol . ContinueArguments
0 commit comments