github.com/julianthome/gore@v0.0.0-20231109011145-b3a6bbe6fe55/complete.go (about)

     1  package gore
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"unicode"
     7  
     8  	"github.com/x-motemen/gore/gocode"
     9  )
    10  
    11  func (s *Session) completeWord(line string, pos int) (string, []string, string) {
    12  	if strings.HasPrefix(strings.TrimSpace(line), ":") {
    13  		// complete commands
    14  		var idx int
    15  		in := strings.TrimLeftFunc(line[:pos], func(c rune) bool {
    16  			if c == ':' || unicode.IsSpace(c) {
    17  				idx++
    18  				return true
    19  			}
    20  			return false
    21  		})
    22  		var cmd string
    23  		if tokens := strings.Fields(in); len(tokens) > 0 {
    24  			cmd = tokens[0]
    25  		}
    26  
    27  		if !strings.Contains(in, " ") {
    28  			pre, post := line[:idx], line[pos:]
    29  			var result []string
    30  			for _, command := range commands {
    31  				name := pre + fmt.Sprint(command.name)
    32  				if cmd == "" || command.name.matchesPrefix(cmd) {
    33  					if !strings.HasPrefix(post, " ") && command.arg != "" {
    34  						name += " "
    35  					}
    36  					result = append(result, name)
    37  				}
    38  			}
    39  			return "", result, post
    40  		}
    41  
    42  		// complete command arguments
    43  		for _, command := range commands {
    44  			if command.complete == nil || !command.name.matches(cmd) {
    45  				continue
    46  			}
    47  			cmdPrefix := line[:idx] + cmd + " "
    48  			return cmdPrefix, command.complete(s, line[len(cmdPrefix):pos]), ""
    49  		}
    50  
    51  		return "", nil, ""
    52  	}
    53  
    54  	if !gocode.Available() {
    55  		return "", nil, ""
    56  	}
    57  
    58  	if strings.TrimSpace(line[:pos]) == "" {
    59  		return "", []string{line[:pos] + indent}, line[pos:]
    60  	}
    61  
    62  	// code completion
    63  	pos, cands, err := s.completeCode(line, pos, true)
    64  	if err != nil {
    65  		errorf("completeCode: %s", err)
    66  		return "", nil, ""
    67  	}
    68  
    69  	return line[0:pos], cands, ""
    70  }
    71  
    72  // completeCode does code completion within the session using gocode.
    73  // in and pos specifies the current input and the cursor position (0 <= pos <= len(in)) respectively.
    74  // If exprMode is set to true, the completion is done as an expression (e.g. appends "(" to functions).
    75  // Return value keep specifies how many characters of in should be kept and candidates are what follow in[0:keep].
    76  func (s *Session) completeCode(in string, pos int, exprMode bool) (keep int, candidates []string, err error) {
    77  	s.clearQuickFix()
    78  
    79  	source, err := s.source(false)
    80  	if err != nil {
    81  		return
    82  	}
    83  
    84  	// Kind of dirty hack :/
    85  	p := strings.LastIndex(source, "}")
    86  	editingSource := source[0:p] + in + source[p:]
    87  	cursor := len(source[0:p]) + pos
    88  
    89  	result, err := gocode.Query([]byte(editingSource), cursor)
    90  	if err != nil {
    91  		return
    92  	}
    93  
    94  	keep = pos - result.Cursor
    95  	candidates = make([]string, 0, len(result.Candidates))
    96  	for _, e := range result.Candidates {
    97  		cand := e.Name
    98  		if cand == printerName && e.Class == "func" {
    99  			continue
   100  		}
   101  		if exprMode && e.Class == "func" {
   102  			cand += "("
   103  		}
   104  		candidates = append(candidates, cand)
   105  	}
   106  
   107  	return
   108  }