10 "github.com/zyedidia/micro/v2/internal/util"
13 // A Completer is a function that takes a buffer and returns info
14 // describing what autocompletions should be inserted at the current
16 // It returns a list of string suggestions which will be inserted at
17 // the current cursor location if selected as well as a list of
18 // suggestion names which can be displayed in an autocomplete box or
20 type Completer func(*Buffer) ([]string, []string)
22 func (b *Buffer) GetSuggestions() {
26 // Autocomplete starts the autocomplete process
27 func (b *Buffer) Autocomplete(c Completer) bool {
28 b.Completions, b.Suggestions = c(b)
29 if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 {
33 b.CycleAutocomplete(true)
37 // CycleAutocomplete moves to the next suggestion
38 func (b *Buffer) CycleAutocomplete(forward bool) {
39 prevSuggestion := b.CurSuggestion
46 if b.CurSuggestion >= len(b.Suggestions) {
48 } else if b.CurSuggestion < 0 {
49 b.CurSuggestion = len(b.Suggestions) - 1
52 c := b.GetActiveCursor()
55 if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
56 start = end.Move(-util.CharacterCountInString(b.Completions[prevSuggestion]), b)
59 b.Replace(start, end, b.Completions[b.CurSuggestion])
60 if len(b.Suggestions) > 1 {
61 b.HasSuggestions = true
65 // GetWord gets the most recent word separated by any separator
66 // (whitespace, punctuation, any non alphanumeric character)
67 func GetWord(b *Buffer) ([]byte, int) {
68 c := b.GetActiveCursor()
70 l = util.SliceStart(l, c.X)
72 if c.X == 0 || util.IsWhitespace(b.RuneAt(c.Loc.Move(-1, b))) {
76 if util.IsNonAlphaNumeric(b.RuneAt(c.Loc.Move(-1, b))) {
80 args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
81 input := args[len(args)-1]
82 return input, c.X - util.CharacterCount(input)
85 // GetArg gets the most recent word (separated by ' ' only)
86 func GetArg(b *Buffer) (string, int) {
87 c := b.GetActiveCursor()
89 l = util.SliceStart(l, c.X)
91 args := bytes.Split(l, []byte{' '})
92 input := string(args[len(args)-1])
94 for i, a := range args {
98 argstart += util.CharacterCount(a) + 1
101 return input, argstart
104 // FileComplete autocompletes filenames
105 func FileComplete(b *Buffer) ([]string, []string) {
106 c := b.GetActiveCursor()
107 input, argstart := GetArg(b)
109 sep := string(os.PathSeparator)
110 dirs := strings.Split(input, sep)
112 var files []os.FileInfo
115 directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
117 directories, _ = util.ReplaceHome(directories)
118 files, err = ioutil.ReadDir(directories)
120 files, err = ioutil.ReadDir(".")
127 var suggestions []string
128 for _, f := range files {
133 if strings.HasPrefix(name, dirs[len(dirs)-1]) {
134 suggestions = append(suggestions, name)
138 sort.Strings(suggestions)
139 completions := make([]string, len(suggestions))
140 for i := range suggestions {
143 complete = strings.Join(dirs[:len(dirs)-1], sep) + sep + suggestions[i]
145 complete = suggestions[i]
147 completions[i] = util.SliceEndStr(complete, c.X-argstart)
150 return completions, suggestions
153 // BufferComplete autocompletes based on previous words in the buffer
154 func BufferComplete(b *Buffer) ([]string, []string) {
155 c := b.GetActiveCursor()
156 input, argstart := GetWord(b)
159 return []string{}, []string{}
162 inputLen := util.CharacterCount(input)
164 suggestionsSet := make(map[string]struct{})
166 var suggestions []string
167 for i := c.Y; i >= 0; i-- {
169 words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
170 for _, w := range words {
171 if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
173 if _, ok := suggestionsSet[strw]; !ok {
174 suggestionsSet[strw] = struct{}{}
175 suggestions = append(suggestions, strw)
180 for i := c.Y + 1; i < b.LinesNum(); i++ {
182 words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
183 for _, w := range words {
184 if bytes.HasPrefix(w, input) && util.CharacterCount(w) > inputLen {
186 if _, ok := suggestionsSet[strw]; !ok {
187 suggestionsSet[strw] = struct{}{}
188 suggestions = append(suggestions, strw)
193 if len(suggestions) > 1 {
194 suggestions = append(suggestions, string(input))
197 completions := make([]string, len(suggestions))
198 for i := range suggestions {
199 completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
202 return completions, suggestions