@@ -143,6 +143,7 @@ class PhpDebugSession extends vscode.DebugSession {
143143 supportsEvaluateForHovers : false ,
144144 supportsConditionalBreakpoints : true ,
145145 supportsFunctionBreakpoints : true ,
146+ supportsCompletionsRequest : true ,
146147 exceptionBreakpointFilters : [
147148 {
148149 filter : 'Notice' ,
@@ -706,6 +707,105 @@ class PhpDebugSession extends vscode.DebugSession {
706707 this . sendResponse ( response ) ;
707708 }
708709
710+ protected async completionsRequest ( response : VSCodeDebugProtocol . CompletionsResponse , args : VSCodeDebugProtocol . CompletionsArguments ) {
711+ try {
712+ if ( ! args . frameId ) {
713+ throw new Error ( 'No stack frame given' ) ;
714+ }
715+ const lineIndex : number = args . line ? args . line - 1 : 0 ;
716+ const lines : string [ ] = args . text . split ( '\n' ) ;
717+ /** The text before the cursor */
718+ const typed : string = [ ...lines . slice ( 0 , Math . max ( lineIndex - 1 , 0 ) ) , lines [ lineIndex ] . substring ( 0 , args . column ) ] . join ( '\n' ) ;
719+ let i = typed . length ;
720+ let containerName : string ;
721+ let operator : string | undefined ;
722+ let query : string ;
723+ while ( true ) {
724+ const substr = typed . substring ( 0 , i ) ;
725+ if ( / \[ $ / . test ( substr ) ) {
726+ // Numeric array index
727+ operator = '[' ;
728+ } else if ( / \[ ' $ / . test ( substr ) ) {
729+ // String array index
730+ operator = `['` ;
731+ } else if ( / - > $ / . test ( substr ) ) {
732+ operator = '->' ;
733+ } else if ( i > 0 ) {
734+ i -- ;
735+ continue ;
736+ }
737+ query = typed . substr ( i ) . toLowerCase ( ) ;
738+ containerName = typed . substring ( 0 , operator ? i - operator . length : i ) ;
739+ break ;
740+ }
741+ const frame = this . _stackFrames . get ( args . frameId ) ;
742+ const contexts = await frame . getContexts ( ) ;
743+ const targets : VSCodeDebugProtocol . CompletionItem [ ] = [ ] ;
744+ if ( ! containerName || ! operator ) {
745+ const responses = await Promise . all ( contexts . map ( context => context . getProperties ( ) ) ) ;
746+ for ( const properties of responses ) {
747+ for ( const property of properties ) {
748+ if ( property . name . toLowerCase ( ) . startsWith ( query ) ) {
749+ const text = property . name [ 0 ] === '$' ? property . name . substr ( 1 ) : property . name ;
750+ targets . push ( { label : property . name , text, type : 'variable' , start : i , length : property . name . length } ) ;
751+ }
752+ }
753+ }
754+ } else {
755+ // Search all contexts
756+ for ( const context of contexts ) {
757+ let response : xdebug . PropertyGetResponse | undefined ;
758+ try {
759+ response = await frame . connection . sendPropertyGetCommand ( { context, fullName : containerName } ) ;
760+ } catch ( err ) {
761+ // ignore
762+ }
763+ if ( response ) {
764+ for ( const property of response . children ) {
765+ if ( property . name . toLowerCase ( ) . startsWith ( query ) ) {
766+ let type : VSCodeDebugProtocol . CompletionItemType | undefined ;
767+ let text : string = property . name ;
768+ if ( operator === '->' ) {
769+ // Object
770+ type = 'property' ;
771+ } else if ( operator [ 0 ] === '[' ) {
772+ // Array
773+ if ( parseInt ( property . name ) + '' === property . name ) {
774+ // Numeric index
775+ if ( operator [ 1 ] === `'` ) {
776+ continue ;
777+ }
778+ type = 'value' ;
779+ text += ']' ;
780+ } else {
781+ // String index
782+ if ( operator [ 1 ] !== `'` ) {
783+ if ( query ) {
784+ continue ;
785+ } else {
786+ text = `'` + text ;
787+ }
788+ }
789+ type = 'text' ;
790+ text += `']` ;
791+ }
792+ }
793+ targets . push ( { label : property . name , text, type, start : i , length : property . name . length } ) ;
794+ }
795+ }
796+ // If we found the variable in one context (typically Locals), abort
797+ break ;
798+ }
799+ }
800+ }
801+ response . body = { targets} ;
802+ } catch ( err ) {
803+ this . sendErrorResponse ( response , err ) ;
804+ return ;
805+ }
806+ this . sendResponse ( response ) ;
807+ }
808+
709809 protected async continueRequest ( response : VSCodeDebugProtocol . ContinueResponse , args : VSCodeDebugProtocol . ContinueArguments ) {
710810 let xdebugResponse : xdebug . StatusResponse | undefined ;
711811 try {
0 commit comments