import vm from 'vm' ; import { runtime } from './inspector' ; export function getAllPropertyNames ( obj ) { const proto = Object . getPrototypeOf ( obj ) ; const props = Object . getOwnPropertyNames ( obj ) ; return [ props ] . concat ( proto ? getAllPropertyNames ( proto ) : [ ] ) ; } export async function getGlobalLexicalScopeNames ( contextId ) { const { names } = await runtime . globalLexicalScopeNames ( { executionContextId : contextId } ) ; return names ; } const moveItemToStart = < T > ( item , array ) : T [ ] => { const index = array . indexOf ( item ) ; if ( index === - 1 ) { return array ; } return [ array [ index ] ] . concat ( array . slice ( 0 , index ) , array . slice ( index + 1 ) ) ; } ; export type Completions = string [ ] [ ] ; export interface CompletionsMeta { completee ? : string ; completions : Completions ; } export default async ( context , contextId , line , cursor ) : Promise < CompletionsMeta > => { if ( ! vm . isContext ( context ) ) { throw new TypeError ( 'Expected `context` to be vm.Context: ' + 'https://nodejs.org/api/vm.html#vm_what_does_it_mean_to' + '_contextify_an_object' ) ; } if ( line === '' ) { return { completions : [ Object . getOwnPropertyNames ( context ) ] } ; } if ( line . slice ( cursor ) . split ( '' ) . some ( x => ! [ ')' , '}' , ']' ] . includes ( x ) ) ) { return { completions : [ ] } ; } const res = /(\w*(?:\.\w*)*?)(\.\w*)?$/ . exec ( line . slice ( 0 , cursor ) ) ; if ( ! res ) { return { completions : [ ] } ; } const [ , variable , filter ] = res ; const ret = ( completions ) => ( { completee : filter ? filter . slice ( 1 ) : variable , completions } ) ; const filtered = ( filter ) => ( levels ) : Completions => filter ? levels . map ( level => moveItemToStart ( filter , level . filter ( item => item . startsWith ( filter ) ) ) ) . filter ( arr => arr . length > 0 ) : levels ; let variableInstance ; try { variableInstance = vm . runInContext ( variable , context ) ; } catch ( _ ) { const completions = [ await getGlobalLexicalScopeNames ( contextId ) ] . concat ( getAllPropertyNames ( context ) ) ; return { completee : variable + ( filter ? filter : '' ) , completions : filtered ( variable ) ( completions ) } ; } if ( variableInstance !== null && variableInstance !== undefined ) { if ( filter ) { return ret ( filtered ( filter . slice ( 1 ) ) ( getAllPropertyNames ( variableInstance ) ) ) ; } return ret ( filtered ( variable ) ( getAllPropertyNames ( context ) ) ) ; } return ret ( [ ] ) ; } ;