Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
116 lines (102 sloc) 3.59 KB
// Package complete implements the code completion algorithm for Elvish.
package complete
import (
"errors"
"sort"
"github.com/elves/elvish/pkg/cli/addons/completion"
"github.com/elves/elvish/pkg/diag"
"github.com/elves/elvish/pkg/parse"
"github.com/elves/elvish/pkg/parse/parseutil"
)
// An error returned by Complete if the config has not supplied a PureEvaler.
var errNoPureEvaler = errors.New("no PureEvaler supplied")
// An error returned by Complete as well as the completers if there is no
// applicable completion.
var errNoCompletion = errors.New("no completion")
// Config stores the configuration required for code completion.
type Config struct {
// An interface to access the runtime. Complete will return an error if this
// is nil.
PureEvaler PureEvaler
// A function for filtering raw candidates. If nil, no filtering is done.
Filterer Filterer
// Used to generate candidates for a command argument. Defaults to
// Filenames.
ArgGenerator ArgGenerator
}
// Filterer is the type of functions that filter raw candidates.
type Filterer func(ctxName, seed string, rawItems []RawItem) []RawItem
// ArgGenerator is the type of functions that generate raw candidates for a
// command argument. It takes all the existing arguments, the last being the
// argument to complete, and returns raw candidates or an error.
type ArgGenerator func(args []string) ([]RawItem, error)
// Result keeps the result of the completion algorithm.
type Result struct {
Name string
Replace diag.Ranging
Items []completion.Item
}
// RawItem represents completion items before the quoting pass.
type RawItem interface {
String() string
Cook(parse.PrimaryType) completion.Item
}
// PureEvaler encapsulates the functionality the completion algorithm needs from
// the language runtime.
type PureEvaler interface {
EachExternal(func(cmd string))
EachSpecial(func(special string))
EachNs(func(string))
EachVariableInNs(string, func(string))
PurelyEvalPrimary(pn *parse.Primary) interface{}
PurelyEvalCompound(*parse.Compound) (string, error)
PurelyEvalPartialCompound(*parse.Compound, *parse.Indexing) (string, error)
}
// CodeBuffer is the same the type in github.com/elves/elvish/pkg/el/codearea,
// replicated here to avoid an unnecessary dependency.
type CodeBuffer struct {
Content string
Dot int
}
// Complete runs the code completion algorithm in the given context, and returns
// the completion type, items and any error encountered.
func Complete(code CodeBuffer, cfg Config) (*Result, error) {
if cfg.PureEvaler == nil {
return nil, errNoPureEvaler
}
if cfg.Filterer == nil {
cfg.Filterer = FilterPrefix
}
if cfg.ArgGenerator == nil {
cfg.ArgGenerator = GenerateFileNames
}
// Ignore the error; the function always returns a valid *ChunkNode.
chunk, _ := parse.AsChunk("[interactive]", code.Content)
leaf := parseutil.FindLeafNode(chunk, code.Dot)
for _, completer := range completers {
ctx, rawItems, err := completer(leaf, cfg)
if err == errNoCompletion {
continue
}
rawItems = cfg.Filterer(ctx.name, ctx.seed, rawItems)
items := make([]completion.Item, len(rawItems))
for i, rawCand := range rawItems {
items[i] = rawCand.Cook(ctx.quote)
}
sort.Slice(items, func(i, j int) bool {
return items[i].ToShow < items[j].ToShow
})
items = dedup(items)
return &Result{Name: ctx.name, Items: items, Replace: ctx.interval}, nil
}
return nil, errNoCompletion
}
func dedup(items []completion.Item) []completion.Item {
var result []completion.Item
for i, item := range items {
if i == 0 || item.ToInsert != items[i-1].ToInsert {
result = append(result, item)
}
}
return result
}
You can’t perform that action at this time.