package edit

import (
	
	
	
	
	
	

	
	
	
	
	
	
	
	
	
	
)

//elvdoc:var completion:arg-completer
//
// A map containing argument completers.

//elvdoc:var completion:binding
//
// Keybinding for the completion mode.

//elvdoc:var completion:matcher
//
// A map mapping from context names to matcher functions. See the
// [Matcher](#matcher) section.

//elvdoc:fn complete-filename
//
// ```elvish
// edit:complete-filename $args...
// ```
//
// Produces a list of filenames found in the directory of the last argument. All
// other arguments are ignored. If the last argument does not contain a path
// (either absolute or relative to the current directory), then the current
// directory is used. Relevant files are output as `edit:complex-candidate`
// objects.
//
// This function is the default handler for any commands without
// explicit handlers in `$edit:completion:arg-completer`. See [Argument
// Completer](#argument-completer).
//
// Example:
//
// ```elvish-transcript
// ~> edit:complete-filename ''
// ▶ (edit:complex-candidate Applications &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate Books &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate Desktop &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate Docsafe &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate Documents &code-suffix=/ &style='01;34')
// ...
// ~> edit:complete-filename .elvish/
// ▶ (edit:complex-candidate .elvish/aliases &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate .elvish/db &code-suffix=' ' &style='')
// ▶ (edit:complex-candidate .elvish/epm-installed &code-suffix=' ' &style='')
// ▶ (edit:complex-candidate .elvish/lib &code-suffix=/ &style='01;34')
// ▶ (edit:complex-candidate .elvish/rc.elv &code-suffix=' ' &style='')
// ```

//elvdoc:fn complex-candidate
//
// ```elvish
// edit:complex-candidate $stem &display='' &code-suffix=''
// ```
//
// Builds a complex candidate. This is mainly useful in [argument
// completers](#argument-completer).
//
// The `&display` option controls how the candidate is shown in the UI. It can
// be a string or a [styled](builtin.html#styled) text. If it is empty, `$stem`
// is used.
//
// The `&code-suffix` option affects how the candidate is inserted into the code
// when it is accepted. By default, a quoted version of `$stem` is inserted. If
// `$code-suffix` is non-empty, it is added to that text, and the suffix is not
// quoted.

type complexCandidateOpts struct {
	CodeSuffix string
	Display    string
}

func (*complexCandidateOpts) () {}

func ( *eval.Frame,  complexCandidateOpts,  string) complexItem {
	 := .Display
	if  == "" {
		 = 
	}
	return complexItem{
		Stem:       ,
		CodeSuffix: .CodeSuffix,
		Display:    ,
	}
}

//elvdoc:fn match-prefix
//
// ```elvish
// edit:match-prefix $seed $inputs?
// ```
//
// For each input, outputs whether the input has $seed as a prefix. Uses the
// result of `to-string` for non-string inputs.
//
// Roughly equivalent to the following Elvish function, but more efficient:
//
// ```elvish
// use str
// fn match-prefix [seed @input]{
//   each [x]{ str:has-prefix (to-string $x) $seed } $@input
// }
// ```

//elvdoc:fn match-subseq
//
// ```elvish
// edit:match-subseq $seed $inputs?
// ```
//
// For each input, outputs whether the input has $seed as a
// [subsequence](https://en.wikipedia.org/wiki/Subsequence). Uses the result of
// `to-string` for non-string inputs.

//elvdoc:fn match-substr
//
// ```elvish
// edit:match-substr $seed $inputs?
// ```
//
// For each input, outputs whether the input has $seed as a substring. Uses the
// result of `to-string` for non-string inputs.
//
// Roughly equivalent to the following Elvish function, but more efficient:
//
// ```elvish
// use str
// fn match-substr [seed @input]{
//   each [x]{ str:has-contains (to-string $x) $seed } $@input
// }
// ```

//elvdoc:fn completion:start
//
// Start the completion mode.

//elvdoc:fn completion:smart-start
//
// Starts the completion mode. However, if all the candidates share a non-empty
// prefix and that prefix starts with the seed, inserts the prefix instead.

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())
	}
}

//elvdoc:fn completion:close
//
// Closes the completion mode UI.

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())
}

// A wrapper type implementing Elvish value methods.
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 {
	// TODO(xiaq): Pretty-print when indent >= 0
	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

// Wraps an ArgGenerator into a function that can be then passed to
// eval.NewGoFn.
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 
}

// The type for a native Go matcher. This is not equivalent to the Elvish
// counterpart, which streams input and output. This is because we can actually
// afford calling a Go function for each item, so omitting the streaming
// behavior makes the implementation simpler.
//
// Native Go matchers are wrapped into Elvish matchers, but never the other way
// around.
//
// This type is satisfied by strings.Contains and strings.HasPrefix; they are
// wrapped into match-substr and match-prefix respectively.
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(), )
			})
		}
	}
}

// Adapts $edit:completion:matcher into a Filterer.
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()
		// Feed a string representing all raw candidates to the input channel.
		go func() {
			defer close()
			for ,  := range  {
				select {
				case  <- .String():
				case <-:
					return
				}
			}
		}()

		// TODO: Supply the Chan component of port 2.
		, ,  := 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{
				// TODO: Supply the Chan component of port 2.
				{Chan: , File: eval.DevNull}, , {File: os.Stderr}}})
		 := ()

		if  != nil {
			.notifyError("matcher", )
			// Continue with whatever values have been output
		}
		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{
				// TODO: Supply the Chan component of port 2.
				nil, , {File: os.Stderr}}})
		()

		return , 
	}
}

func ( vals.Map,  string) (eval.Callable, bool) {
	,  := .Index()
	if ! {
		,  = .Index("")
	}
	if ! {
		// No matcher, but not an error either
		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(, )
}