micro/internal/buffer/autocomplete.go

129 lines
2.8 KiB
Go
Raw Normal View History

2019-01-20 22:49:20 +00:00
package buffer
import (
"bytes"
"io/ioutil"
"os"
2019-01-25 03:10:57 +00:00
"sort"
2019-01-20 22:49:20 +00:00
"strings"
"unicode/utf8"
2019-02-04 04:17:24 +00:00
"github.com/zyedidia/micro/internal/util"
2019-01-20 22:49:20 +00:00
)
2019-01-24 23:09:57 +00:00
// A Completer is a function that takes a buffer and returns info
// describing what autocompletions should be inserted at the current
// cursor location
// It returns a list of string suggestions which will be inserted at
// the current cursor location if selected as well as a list of
// suggestion names which can be displayed in a autocomplete box or
// other UI element
type Completer func(*Buffer) ([]string, []string)
2019-01-20 22:49:20 +00:00
func (b *Buffer) GetSuggestions() {
}
func (b *Buffer) Autocomplete(c Completer) {
2019-01-24 23:09:57 +00:00
b.Completions, b.Suggestions = c(b)
if len(b.Completions) != len(b.Suggestions) || len(b.Completions) == 0 {
return
}
b.CurSuggestion = -1
2019-01-25 03:10:57 +00:00
b.CycleAutocomplete(true)
2019-01-24 23:09:57 +00:00
}
2019-01-25 03:10:57 +00:00
func (b *Buffer) CycleAutocomplete(forward bool) {
2019-01-24 23:09:57 +00:00
prevSuggestion := b.CurSuggestion
2019-01-25 03:10:57 +00:00
if forward {
b.CurSuggestion++
} else {
b.CurSuggestion--
}
if b.CurSuggestion >= len(b.Suggestions) {
2019-01-24 23:09:57 +00:00
b.CurSuggestion = 0
2019-01-25 03:10:57 +00:00
} else if b.CurSuggestion < 0 {
b.CurSuggestion = len(b.Suggestions) - 1
2019-01-24 23:09:57 +00:00
}
2019-01-20 22:49:20 +00:00
2019-01-24 23:09:57 +00:00
c := b.GetActiveCursor()
start := c.Loc
end := c.Loc
if prevSuggestion < len(b.Suggestions) && prevSuggestion >= 0 {
start = end.Move(-utf8.RuneCountInString(b.Completions[prevSuggestion]), b)
} else {
end = start.Move(1, b)
}
b.Replace(start, end, b.Completions[b.CurSuggestion])
b.HasSuggestions = true
2019-01-20 22:49:20 +00:00
}
2019-01-21 00:38:23 +00:00
func GetArg(b *Buffer) (string, int) {
2019-01-20 22:49:20 +00:00
c := b.GetActiveCursor()
l := b.LineBytes(c.Y)
l = util.SliceStart(l, c.X)
args := bytes.Split(l, []byte{' '})
input := string(args[len(args)-1])
argstart := 0
for i, a := range args {
if i == len(args)-1 {
break
}
argstart += utf8.RuneCount(a) + 1
}
2019-01-21 00:38:23 +00:00
return input, argstart
}
// FileComplete autocompletes filenames
2019-01-24 23:09:57 +00:00
func FileComplete(b *Buffer) ([]string, []string) {
2019-01-21 00:38:23 +00:00
c := b.GetActiveCursor()
input, argstart := GetArg(b)
2019-01-20 22:49:20 +00:00
sep := string(os.PathSeparator)
dirs := strings.Split(input, sep)
var files []os.FileInfo
var err error
if len(dirs) > 1 {
directories := strings.Join(dirs[:len(dirs)-1], sep) + sep
directories, _ = util.ReplaceHome(directories)
files, err = ioutil.ReadDir(directories)
} else {
files, err = ioutil.ReadDir(".")
}
if err != nil {
2019-01-24 23:09:57 +00:00
return nil, nil
2019-01-20 22:49:20 +00:00
}
2019-01-24 23:09:57 +00:00
var suggestions []string
2019-01-20 22:49:20 +00:00
for _, f := range files {
name := f.Name()
if f.IsDir() {
name += sep
}
if strings.HasPrefix(name, dirs[len(dirs)-1]) {
suggestions = append(suggestions, name)
}
}
2019-01-25 03:10:57 +00:00
sort.Strings(suggestions)
2019-01-24 23:09:57 +00:00
completions := make([]string, len(suggestions))
for i := range suggestions {
var complete string
2019-01-20 22:49:20 +00:00
if len(dirs) > 1 {
2019-01-24 23:09:57 +00:00
complete = strings.Join(dirs[:len(dirs)-1], sep) + sep + suggestions[i]
2019-01-20 22:49:20 +00:00
} else {
2019-01-24 23:09:57 +00:00
complete = suggestions[i]
2019-01-20 22:49:20 +00:00
}
2019-01-24 23:09:57 +00:00
completions[i] = util.SliceEndStr(complete, c.X-argstart)
2019-01-20 22:49:20 +00:00
}
2019-01-24 23:09:57 +00:00
return completions, suggestions
2019-01-20 22:49:20 +00:00
}