package console

import (
	
	
	
	
	
	
	
	

	
	
	completer 
	

	
)

func ( *Console) ( []rune,  int) readline.Completions {
	 := .activeMenu()

	// Ensure the carapace library is called so that the function
	// completer.Complete() variable is correctly initialized before use.
	carapace.Gen(.Command)

	// Split the line as shell words, only using
	// what the right buffer (up to the cursor)
	, ,  := splitArgs(, )

	// Prepare arguments for the carapace completer
	// (we currently need those two dummies for avoiding a panic).
	 = append([]string{.name, "_carapace"}, ...)

	// Call the completer with our current command context.
	,  := completer.Complete(.Command, ...)

	// The completions are never nil: fill out our own object
	// with everything it contains, regardless of errors.
	 := make([]readline.Completion, len(.Values))

	for ,  := range .Values.Decolor() {
		[] = readline.Completion{
			Value:       unescapeValue(, , .Value),
			Display:     .Display,
			Description: .Description,
			Style:       .Style,
			Tag:         .Tag,
		}

		if !.Nospace.Matches(.Value) {
			[].Value = .Value + " "
		}
	}

	// Assign both completions and command/flags/args usage strings.
	 := readline.CompleteRaw()
	 = .Usage("%s", .Usage)
	 = .justifyCommandComps()

	// If any errors arose from the completion call itself.
	if  != nil {
		 = readline.CompleteMessage("failed to load config: " + .Error())
	}

	// Completion status/errors
	for ,  := range .Messages.Get() {
		 = .Merge(readline.CompleteMessage())
	}

	// Suffix matchers for the completions if any.
	,  := .Nospace.MarshalJSON()
	if len() > 0 &&  == nil {
		 = .NoSpace([]rune(string())...)
	}

	// If we have a quote/escape sequence unaccounted
	// for in our completions, add it to all of them.
	 = .Prefix()
	.PREFIX = 

	// Finally, reset our command tree for the next call.
	completer.ClearStorage()
	.resetPreRun()
	.hideFilteredCommands(.Command)

	return 
}

func ( *Console) ( readline.Completions) readline.Completions {
	 := []string{}

	.EachValue(func( readline.Completion) readline.Completion {
		if !strings.HasSuffix(.Tag, "commands") {
			return 
		}

		 = append(, .Tag)

		return 
	})

	if len() > 0 {
		return .JustifyDescriptions(...)
	}

	return 
}

func ( *Console) () {
	// If carapace config file is found, just return.
	if ,  := xdg.UserConfigDir();  == nil {
		,  := os.Stat(fmt.Sprintf("%v/carapace/styles.json", ))
		if  == nil {
			return
		}
	}

	// Overwrite all default styles for color
	for  := 1;  < 13; ++ {
		 := fmt.Sprintf("carapace.Highlight%d", )
		style.Set(, "bright-white")
	}

	// Overwrite all default styles for flags
	style.Set("carapace.FlagArg", "bright-white")
	style.Set("carapace.FlagMultiArg", "bright-white")
	style.Set("carapace.FlagNoArg", "bright-white")
	style.Set("carapace.FlagOptArg", "bright-white")
}

// splitArgs splits the line in valid words, prepares them in various ways before calling
// the completer with them, and also determines which parts of them should be used as
// prefixes, in the completions and/or in the line.
func splitArgs( []rune,  int) ( []string, ,  string) {
	 = [:]

	// Remove all colors from the string
	 = []rune(strip(string()))

	// Split the line as shellwords, return them if all went fine.
	, ,  := splitCompWords(string())

	// We might have either no error and args, or no error and
	// the cursor ready to complete a new word (last character
	// in line is a space).
	// In some of those cases we append a single dummy argument
	// for the completer to understand we want a new word comp.
	, ,  := mustComplete(, , , )
	if  {
		return sanitizeArgs(), "", 
	}

	// But the completion candidates themselves might need slightly
	// different prefixes, for an optimal completion experience.
	, ,  := adjustQuotedPrefix(, )

	// The remainder is everything following the open charater.
	// Pass it as is to the carapace completion engine.
	 = append(, )

	return sanitizeArgs(), , 
}

func mustComplete( []rune,  []string,  string,  error) (bool, []string, string) {
	 := ""

	// Empty command line, complete the root command.
	if len() == 0 || len() == 0 {
		return true, append(, ), 
	}

	// If we have an error, we must handle it later.
	if  != nil {
		return false, , 
	}

	 := [len()-1]

	// No remain and a trailing space means we want to complete
	// for the next word, except when this last space was escaped.
	if  == "" && unicode.IsSpace() {
		if strings.HasSuffix(string(), "\\ ") {
			return true, , [len()-1]
		}

		return true, append(, ), 
	}

	// Else there is a character under the cursor, which means we are
	// in the middle/at the end of a posentially completed word.
	return true, , 
}

func adjustQuotedPrefix( string,  error) (, ,  string) {
	 = 

	switch {
	case errors.Is(, errUnterminatedDoubleQuote):
		 = "\""
		 =  + 
	case errors.Is(, errUnterminatedSingleQuote):
		 = "'"
		 =  + 
	case errors.Is(, errUnterminatedEscape):
		 = strings.ReplaceAll(, "\\", "")
	}

	return , , 
}

// sanitizeArg unescapes a restrained set of characters.
func sanitizeArgs( []string) ( []string) {
	for ,  := range  {
		 = replacer.Replace()
		 = append(, )
	}

	return 
}

// when the completer has returned us some completions, we sometimes
// needed to post-process them a little before passing them to our shell.
func unescapeValue(, ,  string) string {
	 := strings.HasPrefix(, "\"") ||
		strings.HasPrefix(, "'")

	if  {
		 = strings.ReplaceAll(, "\\ ", " ")
	}

	return 
}

// split has been copied from go-shellquote and slightly modified so as to also
// return the remainder when the parsing failed because of an unterminated quote.
func splitCompWords( string) ( []string,  string,  error) {
	var  bytes.Buffer
	 = make([]string, 0)

	for len() > 0 {
		// skip any splitChars at the start
		,  := utf8.DecodeRuneInString()
		if strings.ContainsRune(splitChars, ) {
			 = [:]
			continue
		} else if  == escapeChar {
			// Look ahead for escaped newline so we can skip over it
			 := [:]
			if len() == 0 {
				 = string(escapeChar)
				 = errUnterminatedEscape

				return , , 
			}

			,  := utf8.DecodeRuneInString()
			if  == '\n' {
				 = [:]
				continue
			}
		}

		var  string

		, ,  = splitCompWord(, &)
		if  != nil {
			return ,  + , 
		}

		 = append(, )
	}

	return , , nil
}

// splitWord has been modified to return the remainder of the input (the part that has not been
// added to the buffer) even when an error is returned.
func splitCompWord( string,  *bytes.Buffer) ( string,  string,  error) {
	.Reset()

:
	{
		 := 
		for len() > 0 {
			,  := utf8.DecodeRuneInString()
			 = [:]
			switch {
			case  == singleChar:
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			case  == doubleChar:
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			case  == escapeChar:
				.WriteString([0 : len()-len()-])
				.WriteRune()
				 = 
				goto 
			case strings.ContainsRune(splitChars, ):
				.WriteString([0 : len()-len()-])
				return .String(), , nil
			}
		}
		if len() > 0 {
			.WriteString()
			 = ""
		}
		goto 
	}

:
	{
		if len() == 0 {
			 = .String() + 
			return "", , errUnterminatedEscape
		}
		,  := utf8.DecodeRuneInString()
		if  != '\n' {
			.WriteString([:])
		}
		 = [:]
	}

	goto 

:
	{
		 := strings.IndexRune(, singleChar)
		if  == -1 {
			return "", , errUnterminatedSingleQuote
		}
		.WriteString([0:])
		 = [+1:]
		goto 
	}

:
	{
		 := 
		for len() > 0 {
			,  := utf8.DecodeRuneInString()
			 = [:]
			switch  {
			case doubleChar:
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			case escapeChar:
				// bash only supports certain escapes in double-quoted strings
				,  := utf8.DecodeRuneInString()
				 = [:]
				if strings.ContainsRune(doubleEscapeChars, ) {
					.WriteString([0 : len()-len()--])

					if  != '\n' {
						.WriteRune()
					}
					 = 
				}
			}
		}

		return "", , errUnterminatedDoubleQuote
	}

:
	return .String(), , nil
}

const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"

var re = regexp.MustCompile(ansi)

// strip removes all ANSI escaped color sequences in a string.
func strip( string) string {
	return re.ReplaceAllString(, "")
}

var replacer = strings.NewReplacer(
	"\n", ` `,
	"\t", ` `,
	"\\ ", " ", // User-escaped spaces in words.
)