package edit
import (
)
type complexCandidateOpts struct {
CodeSuffix string
Display string
}
func (*complexCandidateOpts) () {}
func ( *eval.Frame, complexCandidateOpts, string) complexItem {
:= .Display
if == "" {
=
}
return complexItem{
Stem: ,
CodeSuffix: .CodeSuffix,
Display: ,
}
}
func ( cli.App, tk.Bindings, complete.Config, bool) {
:= .CodeArea().CopyState().Buffer
, := complete.Complete(
complete.CodeBuffer{Content: .Content, Dot: .Dot}, )
if != nil {
.Notify(.Error())
return
}
if {
:= ""
for , := range .Items {
if == 0 {
= .ToInsert
continue
}
= commonPrefix(, .ToInsert)
if == "" {
break
}
}
if != "" {
:= false
.CodeArea().MutateState(func( *tk.CodeAreaState) {
:= .Buffer.Content[.Replace.From:.Replace.To]
if len() > len() && strings.HasPrefix(, ) {
.Pending = tk.PendingCode{
Content: ,
From: .Replace.From, To: .Replace.To}
.ApplyPending()
= true
}
})
if {
return
}
}
}
, := mode.NewCompletion(, mode.CompletionSpec{
Name: .Name, Replace: .Replace, Items: .Items,
Filter: filterSpec, Bindings: ,
})
if != nil {
.SetAddon(, false)
}
if != nil {
.Notify(.Error())
}
}
func ( *Editor, *eval.Evaler, eval.NsBuilder) {
:= newBindingVar(emptyBindingsMap)
:= newMapBindings(, , )
:= newMapVar(vals.EmptyMap)
:= newMapVar(vals.EmptyMap)
:= func() complete.Config {
return complete.Config{
PureEvaler: pureEvaler{},
Filterer: adaptMatcherMap(
, , .Get().(vals.Map)),
ArgGenerator: adaptArgGeneratorMap(
, .Get().(vals.Map)),
}
}
:= func( []string) ([]complete.RawItem, error) {
return complete.GenerateForSudo((), )
}
.AddGoFns("<edit>", map[string]interface{}{
"complete-filename": wrapArgGenerator(complete.GenerateFileNames),
"complete-getopt": completeGetopt,
"complete-sudo": wrapArgGenerator(),
"complex-candidate": complexCandidate,
"match-prefix": wrapMatcher(strings.HasPrefix),
"match-subseq": wrapMatcher(strutil.HasSubseq),
"match-substr": wrapMatcher(strings.Contains),
})
:= .app
.AddNs("completion",
eval.NsBuilder{
"arg-completer": ,
"binding": ,
"matcher": ,
}.AddGoFns("<edit:completion>:", map[string]interface{}{
"accept": func() { listingAccept() },
"smart-start": func() { completionStart(, , (), true) },
"start": func() { completionStart(, , (), false) },
"up": func() { listingUp() },
"down": func() { listingDown() },
"up-cycle": func() { listingUpCycle() },
"down-cycle": func() { listingDownCycle() },
"left": func() { listingLeft() },
"right": func() { listingRight() },
}).Ns())
}
type complexItem complete.ComplexItem
func ( complexItem) ( interface{}) (interface{}, bool) {
switch {
case "stem":
return .Stem, true
case "code-suffix":
return .CodeSuffix, true
case "display":
return .Display, true
}
return nil, false
}
func ( complexItem) ( func(interface{}) bool) {
vals.Feed(, "stem", "code-suffix", "display")
}
func ( complexItem) () string { return "map" }
func ( complexItem) ( interface{}) bool {
, := .(complexItem)
return && .Stem == .Stem &&
.CodeSuffix == .CodeSuffix && .Display == .Display
}
func ( complexItem) () uint32 {
:= hash.DJBInit
= hash.DJBCombine(, hash.String(.Stem))
= hash.DJBCombine(, hash.String(.CodeSuffix))
= hash.DJBCombine(, hash.String(.Display))
return
}
func ( complexItem) ( int) string {
return fmt.Sprintf("(edit:complex-candidate %s &code-suffix=%s &display=%s)",
parse.Quote(.Stem), parse.Quote(.CodeSuffix), parse.Quote(.Display))
}
type wrappedArgGenerator func(*eval.Frame, ...string) error
func ( complete.ArgGenerator) wrappedArgGenerator {
return func( *eval.Frame, ...string) error {
, := ()
if != nil {
return
}
:= .OutputChan()
for , := range {
switch rawItem := .(type) {
case complete.ComplexItem:
<- complexItem()
case complete.PlainItem:
<- string()
default:
<-
}
}
return nil
}
}
func (, string) string {
for , := range {
if == "" {
break
}
, := utf8.DecodeRuneInString()
if != {
return [:]
}
= [:]
}
return
}
type matcher func(text, seed string) bool
type matcherOpts struct {
IgnoreCase bool
SmartCase bool
}
func (*matcherOpts) () {}
type wrappedMatcher func(fm *eval.Frame, opts matcherOpts, seed string, inputs eval.Inputs)
func ( matcher) wrappedMatcher {
return func( *eval.Frame, matcherOpts, string, eval.Inputs) {
:= .OutputChan()
if .IgnoreCase || (.SmartCase && == strings.ToLower()) {
if .IgnoreCase {
= strings.ToLower()
}
(func( interface{}) {
<- (strings.ToLower(vals.ToString()), )
})
} else {
(func( interface{}) {
<- (vals.ToString(), )
})
}
}
}
func ( notifier, *eval.Evaler, vals.Map) complete.Filterer {
return func(, string, []complete.RawItem) []complete.RawItem {
, := lookupFn(, )
if ! {
.notifyf(
"matcher for %s not a function, falling back to prefix matching", )
}
if == nil {
return complete.FilterPrefix(, , )
}
:= make(chan interface{})
:= make(chan struct{})
defer close()
go func() {
defer close()
for , := range {
select {
case <- .String():
case <-:
return
}
}
}()
, , := eval.CapturePort()
if != nil {
.notifyf("cannot create pipe to run completion matcher: %v", )
return nil
}
= .Call(,
eval.CallCfg{Args: []interface{}{}, From: "[editor matcher]"},
eval.EvalCfg{Ports: []*eval.Port{
{Chan: , File: eval.DevNull}, , {File: os.Stderr}}})
:= ()
if != nil {
.notifyError("matcher", )
}
if len() != len() {
.notifyf(
"matcher has output %v values, not equal to %v inputs",
len(), len())
}
:= []complete.RawItem{}
for := 0; < len() && < len(); ++ {
if vals.Bool([]) {
= append(, [])
}
}
return
}
}
func ( *eval.Evaler, vals.Map) complete.ArgGenerator {
return func( []string) ([]complete.RawItem, error) {
, := lookupFn(, [0])
if ! {
return nil, fmt.Errorf("arg completer for %s not a function", [0])
}
if == nil {
return complete.GenerateFileNames()
}
:= make([]interface{}, len())
for , := range {
[] =
}
var []complete.RawItem
var sync.Mutex
:= func( complete.RawItem) {
.Lock()
defer .Unlock()
= append(, )
}
:= func( <-chan interface{}) {
for := range {
switch v := .(type) {
case string:
(complete.PlainItem())
case complexItem:
(complete.ComplexItem())
default:
(complete.PlainItem(vals.ToString()))
}
}
}
:= func( *os.File) {
:= bufio.NewReader()
for {
, := .ReadString('\n')
if != "" {
(complete.PlainItem(strutil.ChopLineEnding()))
}
if != nil {
break
}
}
}
, , := eval.PipePort(, )
if != nil {
panic()
}
= .Call(,
eval.CallCfg{Args: , From: "[editor arg generator]"},
eval.EvalCfg{Ports: []*eval.Port{
nil, , {File: os.Stderr}}})
()
return ,
}
}
func ( vals.Map, string) (eval.Callable, bool) {
, := .Index()
if ! {
, = .Index("")
}
if ! {
return nil, true
}
, := .(eval.Callable)
if ! {
return nil, false
}
return , true
}
type pureEvaler struct{ ev *eval.Evaler }
func (pureEvaler) ( func(string)) { fsutil.EachExternal() }
func (pureEvaler) ( func(string)) {
for := range eval.IsBuiltinSpecial {
()
}
}
func ( pureEvaler) ( func(string)) {
eachNsInTop(.ev.Builtin(), .ev.Global(), )
}
func ( pureEvaler) ( string, func(string)) {
eachVariableInTop(.ev.Builtin(), .ev.Global(), , )
}
func ( pureEvaler) ( *parse.Primary) interface{} {
return .ev.PurelyEvalPrimary()
}
func ( pureEvaler) ( *parse.Compound) (string, bool) {
return .ev.PurelyEvalCompound()
}
func ( pureEvaler) ( *parse.Compound, int) (string, bool) {
return .ev.PurelyEvalPartialCompound(, )
}