package completion

import (
	
	
	
	
	
	

	
)

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

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

	return 
}

// 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 ( []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(, line.ErrUnterminatedDoubleQuote):
		 = "\""
		 =  + 
	case errors.Is(, line.ErrUnterminatedSingleQuote):
		 = "'"
		 =  + 
	case errors.Is(, line.ErrUnterminatedEscape):
		 = strings.ReplaceAll(, "\\", "")
	}

	return , ,  
}

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

	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(line.SplitChars, ) {
			 = [:]
			continue
		} else if  == line.EscapeChar {
			// Look ahead for escaped newline so we can skip over it
			 := [:]
			if len() == 0 {
				 = string(line.EscapeChar)
				 = line.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  == line.SingleChar:
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			case  == line.DoubleChar:
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			case  == line.EscapeChar:
				.WriteString([0 : len()-len()-])
				.WriteRune()
				 = 
				goto 
			case strings.ContainsRune(line.SplitChars, ):
				.WriteString([0 : len()-len()-])
				return .String(), , nil
			}
		}
		if len() > 0 {
			.WriteString()
			 = ""
		}
		goto 
	}

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

	goto 

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

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

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

		return "", , line.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.
)