github.com/ethereum/go-ethereum@v1.14.3/internal/jsre/completion.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package jsre
    18  
    19  import (
    20  	"regexp"
    21  	"sort"
    22  	"strings"
    23  
    24  	"github.com/dop251/goja"
    25  )
    26  
    27  // JS numerical token
    28  var numerical = regexp.MustCompile(`^(NaN|-?((\d*\.\d+|\d+)([Ee][+-]?\d+)?|Infinity))$`)
    29  
    30  // CompleteKeywords returns potential continuations for the given line. Since line is
    31  // evaluated, callers need to make sure that evaluating line does not have side effects.
    32  func (jsre *JSRE) CompleteKeywords(line string) []string {
    33  	var results []string
    34  	jsre.Do(func(vm *goja.Runtime) {
    35  		results = getCompletions(vm, line)
    36  	})
    37  	return results
    38  }
    39  
    40  func getCompletions(vm *goja.Runtime, line string) (results []string) {
    41  	parts := strings.Split(line, ".")
    42  	if len(parts) == 0 {
    43  		return nil
    44  	}
    45  
    46  	// Find the right-most fully named object in the line. e.g. if line = "x.y.z"
    47  	// and "x.y" is an object, obj will reference "x.y".
    48  	obj := vm.GlobalObject()
    49  	for i := 0; i < len(parts)-1; i++ {
    50  		if numerical.MatchString(parts[i]) {
    51  			return nil
    52  		}
    53  		v := obj.Get(parts[i])
    54  		if v == nil || goja.IsNull(v) || goja.IsUndefined(v) {
    55  			return nil // No object was found
    56  		}
    57  		obj = v.ToObject(vm)
    58  	}
    59  
    60  	// Go over the keys of the object and retain the keys matching prefix.
    61  	// Example: if line = "x.y.z" and "x.y" exists and has keys "zebu", "zebra"
    62  	// and "platypus", then "x.y.zebu" and "x.y.zebra" will be added to results.
    63  	prefix := parts[len(parts)-1]
    64  	iterOwnAndConstructorKeys(vm, obj, func(k string) {
    65  		if strings.HasPrefix(k, prefix) {
    66  			if len(parts) == 1 {
    67  				results = append(results, k)
    68  			} else {
    69  				results = append(results, strings.Join(parts[:len(parts)-1], ".")+"."+k)
    70  			}
    71  		}
    72  	})
    73  
    74  	// Append opening parenthesis (for functions) or dot (for objects)
    75  	// if the line itself is the only completion.
    76  	if len(results) == 1 && results[0] == line {
    77  		// Accessing the property will cause it to be evaluated.
    78  		// This can cause an error, e.g. in case of web3.eth.protocolVersion
    79  		// which has been dropped from geth. Ignore the error for autocompletion
    80  		// purposes.
    81  		obj := SafeGet(obj, parts[len(parts)-1])
    82  		if obj != nil {
    83  			if _, isfunc := goja.AssertFunction(obj); isfunc {
    84  				results[0] += "("
    85  			} else {
    86  				results[0] += "."
    87  			}
    88  		}
    89  	}
    90  
    91  	sort.Strings(results)
    92  	return results
    93  }