github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/cmd/nash/completer.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "os" 6 "strconv" 7 8 "github.com/madlambda/nash" 9 "github.com/madlambda/nash/readline" 10 "github.com/madlambda/nash/sh" 11 ) 12 13 var runes = readline.Runes{} 14 15 type Completer struct { 16 op *readline.Operation 17 term *readline.Terminal 18 sh *nash.Shell 19 } 20 21 func NewCompleter(op *readline.Operation, term *readline.Terminal, sh *nash.Shell) *Completer { 22 return &Completer{op, term, sh} 23 } 24 25 func (c *Completer) Do(line []rune, pos int) ([][]rune, int) { 26 const op = "Completer.Do" 27 28 defer c.op.Refresh() 29 defer c.term.PauseRead(false) 30 31 fnDef, err := c.sh.GetFn("nash_complete") 32 if err != nil { 33 c.Log(op, "skipping autocompletion") 34 return [][]rune{[]rune{'\t'}}, 0 35 } 36 37 nashFunc := fnDef.Build() 38 lineArg := sh.NewStrObj(string(line)) 39 posArg := sh.NewStrObj(strconv.Itoa(pos)) 40 err = nashFunc.SetArgs([]sh.Obj{lineArg, posArg}) 41 if err != nil { 42 fmt.Fprintf(os.Stderr, "%s:error setting args on autocomplete function:%v\n", op, err) 43 return nil, 0 44 } 45 46 nashFunc.SetStdin(c.sh.Stdin()) 47 nashFunc.SetStdout(c.sh.Stdout()) 48 nashFunc.SetStderr(c.sh.Stderr()) 49 50 if err = nashFunc.Start(); err != nil { 51 fmt.Fprintf(os.Stderr, "%s:error starting autocomplete function:%v\n", op, err) 52 return nil, 0 53 } 54 55 if err = nashFunc.Wait(); err != nil { 56 fmt.Fprintf(os.Stderr, "%s:error waiting for autocomplete function:%v\n", op, err) 57 return nil, 0 58 } 59 60 ret := nashFunc.Results() 61 62 if len(ret) != 1 || ret[0].Type() != sh.ListType { 63 fmt.Fprintf(os.Stderr, "%s:ignoring unexpected autocomplete func return (expected list):%+v\n", op, ret) 64 return nil, 0 65 } 66 67 retlist := ret[0].(*sh.ListObj) 68 69 if len(retlist.List()) != 2 { 70 c.Log(op, "no results from autocomplete") 71 return nil, pos 72 } 73 74 newline := retlist.List()[0] 75 newpos := retlist.List()[1] 76 77 if newline.Type() != sh.StringType || newpos.Type() != sh.StringType { 78 fmt.Fprintf(os.Stderr, "%s:ignoring autocomplete value:(%s) (%s)\n", op, newline, newpos) 79 return nil, 0 80 } 81 82 objline := newline.(*sh.StrObj) 83 objpos := newpos.(*sh.StrObj) 84 85 c.Log(op, "autocomplete result:line %q:pos %q", objline, objpos) 86 87 offset, err := strconv.Atoi(objpos.Str()) 88 89 if err != nil { 90 fmt.Fprintf(os.Stderr, "%s:autocomplete func returned non number position:%v\n", op, err) 91 return nil, 0 92 } 93 94 c.Log(op, "success:line %q:offset %d", objline, offset) 95 return [][]rune{[]rune(objline.Str())}, offset 96 } 97 98 func (c *Completer) Log(op string, format string, args ...interface{}) { 99 c.sh.Log(op+":"+format, args...) 100 }