github.com/aergoio/aergo@v1.3.1/cmd/brick/exec/search.go (about)

     1  package exec
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  
    10  	"github.com/aergoio/aergo/cmd/brick/context"
    11  )
    12  
    13  var index = make(map[string]map[string]string)
    14  
    15  func Candidates(cmd string, chunks []context.Chunk, current int, symbol string) map[string]string {
    16  	if ret := search(cmd, chunks, current, symbol); ret != nil {
    17  		return ret
    18  	}
    19  
    20  	ret := make(map[string]string)
    21  
    22  	// if there is no suggestion, then return general
    23  	if descr, ok := context.Symbols[symbol]; ok {
    24  		ret[symbol] = descr
    25  	}
    26  
    27  	return ret
    28  }
    29  
    30  // search contract args using get abi
    31  func extractContractAndFuncName(cmd string, chunks []context.Chunk) (string, string) {
    32  	var contractName string
    33  	var funcName string
    34  
    35  	executor := GetExecutor(cmd)
    36  	if executor != nil {
    37  		symbols := strings.Fields(executor.Syntax())
    38  
    39  		for i, symbol := range symbols {
    40  			if len(chunks) <= i {
    41  				break
    42  			}
    43  			if symbol == context.ContractSymbol {
    44  				// compare with symbol in syntax and extract contract name
    45  				contractName = chunks[i].Text
    46  			} else if symbol == context.FunctionSymbol {
    47  				// extract function name
    48  				funcName = chunks[i].Text
    49  			}
    50  		}
    51  	}
    52  
    53  	return contractName, funcName
    54  }
    55  
    56  func search(cmd string, chunks []context.Chunk, current int, symbol string) map[string]string {
    57  	if keywords, ok := index[symbol]; ok {
    58  		return keywords
    59  	}
    60  
    61  	if symbol == context.FunctionSymbol {
    62  		contractName, _ := extractContractAndFuncName(cmd, chunks)
    63  		if contractName != "" {
    64  			return searchFuncHint(contractName)
    65  		}
    66  	} else if symbol == context.ContractArgsSymbol {
    67  		contractName, funcName := extractContractAndFuncName(cmd, chunks)
    68  		if contractName != "" && funcName != "" {
    69  			// search abi using contract and function name
    70  			return searchAbiHint(contractName, funcName)
    71  		}
    72  	} else if symbol == context.PathSymbol {
    73  		if len(chunks) <= current { //there is no word yet
    74  			return searchInPath(context.Chunk{Text: ".", Accent: false})
    75  		}
    76  		return searchInPath(chunks[current])
    77  	}
    78  
    79  	return nil
    80  }
    81  
    82  func searchFuncHint(contractName string) map[string]string {
    83  	// read receipt and extract abi functions
    84  	ret := make(map[string]string)
    85  
    86  	abi, err := context.Get().GetABI(contractName)
    87  	if err != nil {
    88  		ret["<error>"] = err.Error()
    89  		return ret
    90  	}
    91  	for _, contractFunc := range abi.Functions {
    92  		// gather functions
    93  		ret[contractFunc.Name] = ""
    94  	}
    95  
    96  	return ret
    97  }
    98  
    99  func searchAbiHint(contractName, funcName string) map[string]string {
   100  	abi, err := context.Get().GetABI(contractName)
   101  	if err != nil {
   102  		return nil
   103  	}
   104  
   105  	for _, contractFunc := range abi.Functions {
   106  		if contractFunc.Name == funcName {
   107  			argsHint := "`["
   108  			for i, funcArg := range contractFunc.GetArguments() {
   109  				argsHint += funcArg.Name
   110  				if i+1 != len(contractFunc.GetArguments()) {
   111  					argsHint += ", "
   112  				}
   113  			}
   114  			argsHint += "]`"
   115  
   116  			ret := make(map[string]string)
   117  			ret[argsHint] = context.Symbols[context.ContractArgsSymbol]
   118  			return ret
   119  		}
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  func searchInPath(chunk context.Chunk) map[string]string {
   126  
   127  	if strings.HasSuffix(chunk.Text, ".") {
   128  		// attach file sperator, to get files in this relative path
   129  		chunk.Text = fmt.Sprintf("%s%c", chunk.Text, filepath.Separator)
   130  	}
   131  	ret := make(map[string]string)
   132  
   133  	// extract parent directory path
   134  	dir := filepath.Dir(chunk.Text)
   135  
   136  	// navigate file list in the parent directory
   137  	fileInfo, err := ioutil.ReadDir(dir)
   138  	if err != nil {
   139  		ret[err.Error()] = ""
   140  		return ret
   141  	}
   142  
   143  	// detatch last base path
   144  	// other function internally use filepath.Clean() that remove text . or ..
   145  	// it makes prompt filter hard to match suggestions and the input
   146  
   147  	currentDir, _ := filepath.Split(chunk.Text)
   148  
   149  	for _, file := range fileInfo {
   150  		// generate suggestion text
   151  		fullPath := currentDir + file.Name()
   152  
   153  		if file.IsDir() {
   154  			fullPath += string(os.PathSeparator)
   155  		}
   156  		if chunk.Accent {
   157  			fullPath = "`" + fullPath // attach accent again
   158  		}
   159  		// if contains white space...
   160  		if wsIdx := strings.LastIndex(fullPath, " "); wsIdx != -1 {
   161  			// cut it because auto completer will switch text only after whitespace
   162  			fullPath = fullPath[wsIdx+1:]
   163  		}
   164  
   165  		ret[fullPath] = ""
   166  	}
   167  
   168  	return ret
   169  }
   170  
   171  func Index(symbol, text string) {
   172  	if _, ok := index[symbol]; !ok {
   173  		index[symbol] = make(map[string]string)
   174  	}
   175  	index[symbol][text] = symbol
   176  }