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  }