github.com/hoop33/elvish@v0.0.0-20160801152013-6d25485beab4/edit/arg_completer.go (about)

     1  package edit
     2  
     3  import (
     4  	"errors"
     5  	"os"
     6  
     7  	"github.com/elves/elvish/eval"
     8  )
     9  
    10  // CompleterTable provides $le:completer. It implements eval.IndexSetter.
    11  type CompleterTable map[string]ArgCompleter
    12  
    13  var _ eval.IndexSetter = CompleterTable(nil)
    14  
    15  var (
    16  	ErrCompleterIndexMustBeString = errors.New("index of completer table must be string")
    17  	ErrCompleterValueMustBeFunc   = errors.New("value of completer table must be function")
    18  )
    19  
    20  func (CompleterTable) Kind() string {
    21  	return "map"
    22  }
    23  
    24  func (ct CompleterTable) Repr(indent int) string {
    25  	return "<repr not implemented yet>"
    26  }
    27  
    28  func (ct CompleterTable) IndexOne(idx eval.Value) eval.Value {
    29  	head, ok := idx.(eval.String)
    30  	if !ok {
    31  		throw(ErrCompleterIndexMustBeString)
    32  	}
    33  	v := ct[string(head)]
    34  	if fac, ok := v.(FnAsArgCompleter); ok {
    35  		return fac.Fn
    36  	}
    37  	return eval.String("<get not implemented yet>")
    38  }
    39  
    40  func (ct CompleterTable) IndexSet(idx eval.Value, v eval.Value) {
    41  	head, ok := idx.(eval.String)
    42  	if !ok {
    43  		throw(ErrCompleterIndexMustBeString)
    44  	}
    45  	value, ok := v.(eval.FnValue)
    46  	if !ok {
    47  		throw(ErrCompleterValueMustBeFunc)
    48  	}
    49  	ct[string(head)] = FnAsArgCompleter{value}
    50  }
    51  
    52  // ArgCompleter is an argument completer. Its Complete method is called with all
    53  // words of the form. There are at least two words: the first one being the form
    54  // head and the last word being the current argument to complete. It should
    55  // return a list of candidates for the current argument and errors.
    56  type ArgCompleter interface {
    57  	Complete([]string, *Editor) ([]*candidate, error)
    58  }
    59  
    60  type FuncArgCompleter struct {
    61  	impl func([]string, *Editor) ([]*candidate, error)
    62  }
    63  
    64  func (fac FuncArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
    65  	return fac.impl(words, ed)
    66  }
    67  
    68  var DefaultArgCompleter = ""
    69  var argCompleter map[string]ArgCompleter
    70  
    71  func init() {
    72  	argCompleter = map[string]ArgCompleter{
    73  		DefaultArgCompleter: FuncArgCompleter{complFilename},
    74  		"sudo":              FuncArgCompleter{complSudo},
    75  	}
    76  }
    77  
    78  func completeArg(words []string, ed *Editor) ([]*candidate, error) {
    79  	Logger.Printf("completing argument: %q", words)
    80  	compl, ok := argCompleter[words[0]]
    81  	if !ok {
    82  		compl = argCompleter[DefaultArgCompleter]
    83  	}
    84  	return compl.Complete(words, ed)
    85  }
    86  
    87  func complFilename(words []string, ed *Editor) ([]*candidate, error) {
    88  	return complFilenameInner(words[len(words)-1], false)
    89  }
    90  
    91  func complSudo(words []string, ed *Editor) ([]*candidate, error) {
    92  	if len(words) == 2 {
    93  		return complFormHeadInner(words[1], ed)
    94  	}
    95  	return completeArg(words[1:], ed)
    96  }
    97  
    98  type FnAsArgCompleter struct {
    99  	Fn eval.FnValue
   100  }
   101  
   102  func (fac FnAsArgCompleter) Complete(words []string, ed *Editor) ([]*candidate, error) {
   103  	in, err := makeClosedStdin()
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	ports := []*eval.Port{in, &eval.Port{File: os.Stdout}, &eval.Port{File: os.Stderr}}
   108  
   109  	wordValues := make([]eval.Value, len(words))
   110  	for i, word := range words {
   111  		wordValues[i] = eval.String(word)
   112  	}
   113  
   114  	// XXX There is no source to pass to NewTopEvalCtx.
   115  	ec := eval.NewTopEvalCtx(ed.evaler, "[editor completer]", "", ports)
   116  	values, err := ec.PCaptureOutput(fac.Fn, wordValues)
   117  	if err != nil {
   118  		ed.notify("completer error: %v", err)
   119  		return nil, err
   120  	}
   121  
   122  	cands := make([]*candidate, len(values))
   123  	for i, v := range values {
   124  		s := eval.ToString(v)
   125  		cands[i] = &candidate{text: s}
   126  	}
   127  	return cands, nil
   128  }