11 "github.com/zyedidia/micro/internal/util"
14 // A Completer is a function that takes a buffer and returns info
15 // describing what autocompletions should be inserted at the current
17 // It returns a list of string suggestions which will be inserted at
18 // the current cursor location if selected as well as a list of
19 // suggestion names which can be displayed in an autocomplete box or
21 type Completer func(*Buffer) ([]string, []string)
23 func (b *Buffer) GetSuggestions() {
27 // Autocomplete starts the autocomplete process
28 func (b *Buffer) Autocomplete(c Completer) bool {
29 b.Completions, b.Suggestions = c(b)
30 if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 {
34 b.CycleAutocomplete(true)
38 // CycleAutocomplete moves to the next suggestion
39 func (b *Buffer) CycleAutocomplete(forward bool) {
40 prevSuggestion := b.CurSuggestion
47 if b.CurSuggestion >= len(b.Suggestions) {
49 } else if b.CurSuggestion < 0 {
50 b.CurSuggestion = len(b.Suggestions) - 1
53 c := b.GetActiveCursor()
56 if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
57 start = end.Move(-utf8.RuneCountInString(b.Completions[prevSuggestion]), b)
59 // end = start.Move(1, b)
62 b.Replace(start, end, b.Completions[b.CurSuggestion])
63 if len(b.Suggestions) > 1 {
64 b.HasSuggestions = true
68 // GetWord gets the most recent word separated by any separator
69 // (whitespace, punctuation, any non alphanumeric character)
70 func GetWord(b *Buffer) ([]byte, int) {
71 c := b.GetActiveCursor()
73 l = util.SliceStart(l, c.X)
75 if c.X == 0 || util.IsWhitespace(b.RuneAt(c.Loc)) {
79 if util.IsNonAlphaNumeric(b.RuneAt(c.Loc)) {
83 args := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
84 input := args[len(args)-1]
85 return input, c.X - utf8.RuneCount(input)
88 // GetArg gets the most recent word (separated by ' ' only)
89 func GetArg(b *Buffer) (string, int) {
90 c := b.GetActiveCursor()
92 l = util.SliceStart(l, c.X)
94 args := bytes.Split(l, []byte{' '})
95 input := string(args[len(args)-1])
97 for i, a := range args {
101 argstart += utf8.RuneCount(a) + 1
104 return input, argstart
107 // FileComplete autocompletes filenames
108 func FileComplete(b *Buffer) ([]string, []string) {
109 c := b.GetActiveCursor()
110 input, argstart := GetArg(b)
112 sep := string(os.PathSeparator)
113 dirs := strings.Split(input, sep)
115 var files []os.FileInfo
118 directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
120 directories, _ = util.ReplaceHome(directories)
121 files, err = ioutil.ReadDir(directories)
123 files, err = ioutil.ReadDir(".")
130 var suggestions []string
131 for _, f := range files {
136 if strings.HasPrefix(name, dirs[len(dirs)-1]) {
137 suggestions = append(suggestions, name)
141 sort.Strings(suggestions)
142 completions := make([]string, len(suggestions))
143 for i := range suggestions {
146 complete = strings.Join(dirs[:len(dirs)-1], sep) + sep + suggestions[i]
148 complete = suggestions[i]
150 completions[i] = util.SliceEndStr(complete, c.X-argstart)
153 return completions, suggestions
156 // BufferComplete autocompletes based on previous words in the buffer
157 func BufferComplete(b *Buffer) ([]string, []string) {
158 c := b.GetActiveCursor()
159 input, argstart := GetWord(b)
162 return []string{}, []string{}
165 inputLen := utf8.RuneCount(input)
167 suggestionsSet := make(map[string]struct{})
169 var suggestions []string
170 for i := c.Y; i >= 0; i-- {
172 words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
173 for _, w := range words {
174 if bytes.HasPrefix(w, input) && utf8.RuneCount(w) > inputLen {
176 if _, ok := suggestionsSet[strw]; !ok {
177 suggestionsSet[strw] = struct{}{}
178 suggestions = append(suggestions, strw)
183 for i := c.Y + 1; i < b.LinesNum(); i++ {
185 words := bytes.FieldsFunc(l, util.IsNonAlphaNumeric)
186 for _, w := range words {
187 if bytes.HasPrefix(w, input) && utf8.RuneCount(w) > inputLen {
189 if _, ok := suggestionsSet[strw]; !ok {
190 suggestionsSet[strw] = struct{}{}
191 suggestions = append(suggestions, strw)
196 if len(suggestions) > 1 {
197 suggestions = append(suggestions, string(input))
200 completions := make([]string, len(suggestions))
201 for i := range suggestions {
202 completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
205 return completions, suggestions