package line

import (
	
	
	
	

	
	
)

var (
	SplitChars        = " \n\t"
	SingleChar        = '\''
	DoubleChar        = '"'
	EscapeChar        = '\\'
	DoubleEscapeChars = "$`\"\n\\"
)

var (
	ErrUnterminatedSingleQuote = errors.New("unterminated single-quoted string")
	ErrUnterminatedDoubleQuote = errors.New("unterminated double-quoted string")
	ErrUnterminatedEscape      = errors.New("unterminated backslash-escape")
)

// Parse is in charge of removing all comments from the input line
// before execution, and if successfully parsed, split into words.
func ( string) ( []string,  error) {
	 := strings.NewReader()
	 := syntax.NewParser(syntax.KeepComments(false))

	// Parse the shell string a syntax, removing all comments.
	,  := .Parse(, "")
	if  != nil {
		return nil, 
	}

	var  bytes.Buffer

	 = syntax.NewPrinter().Print(&, )
	if  != nil {
		return nil, 
	}

	// Split the line into shell words.
	return shellquote.Split(.String())
}

// acceptMultiline determines if the line just accepted is complete (in which case
// we should execute it), or incomplete (in which case we must read in multiline).
func ( []rune) ( bool) {
	// Errors are either: unterminated quotes, or unterminated escapes.
	, ,  := Split(string(), false)
	if  == nil {
		return true
	}

	// Currently, unterminated quotes are obvious to treat: keep reading.
	switch  {
	case ErrUnterminatedDoubleQuote, ErrUnterminatedSingleQuote:
		return false
	case ErrUnterminatedEscape:
		if len() > 0 && [len()-1] == '\\' {
			return false
		}

		return true
	}

	return true
}

// IsEmpty checks if a given input line is empty.
// It accepts a list of characters that we consider to be irrelevant,
// that is, if the given line only contains these characters, it will
// be considered empty.
func ( string,  ...rune) bool {
	 := true

	for ,  := range  {
		if !strings.ContainsRune(string(), ) {
			 = false
			break
		}
	}

	return 
}

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

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

	return 
}

// TrimSpaces removes all leading/trailing spaces from words
func ( []string) ( []string) {
	for ,  := range  {
		 = append(, strings.TrimSpace())
	}

	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 ( string,  bool) ( []string,  string,  error) {
	var  bytes.Buffer
	 = make([]string, 0)

	for len() > 0 {
		// skip any splitChars at the start
		,  := utf8.DecodeRuneInString()
		if strings.ContainsRune(SplitChars, ) {
			// Keep these characters in the result when higlighting the line.
			if  {
				if len() == 0 {
					 = append(, string())
				} else {
					[len()-1] += string()
				}
			}

			 = [:]

			continue
		} else if  == EscapeChar {
			// Look ahead for escaped newline so we can skip over it
			 := [:]
			if len() == 0 {
				if  {
					 = string(EscapeChar)
				}

				 = ErrUnterminatedEscape

				return , , 
			}

			,  := utf8.DecodeRuneInString()
			if  == '\n' {
				if  {
					if len() == 0 {
						 = append(, string()+string())
					} else {
						[len()-1] += string() + string()
					}
				}

				 = [:]

				continue
			}
		}

		var  string

		, ,  = splitWord(, &, )
		if  != nil {
			 = 
			return , , 
		}

		 = append(, )
	}

	return , , 
}

// 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 splitWord( string,  *bytes.Buffer,  bool) ( string,  string,  error) {
	.Reset()

:
	{
		 := 
		for len() > 0 {
			,  := utf8.DecodeRuneInString()
			 = [:]
			if  == SingleChar {
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			} else if  == DoubleChar {
				.WriteString([0 : len()-len()-])
				 = 
				goto 
			} else if  == EscapeChar {
				.WriteString([0 : len()-len()-])
				if  {
					.WriteRune()
				}
				 = 
				goto 
			} else if strings.ContainsRune(SplitChars, ) {
				.WriteString([0 : len()-len()-])
				if  {
					.WriteRune()
				}

				return .String(), , nil
			}
		}
		if len() > 0 {
			.WriteString()
			 = ""
		}
		goto 
	}

:
	{
		if len() == 0 {
			if  {
				 = .String() + 
			}
			return "", , ErrUnterminatedEscape
		}
		,  := utf8.DecodeRuneInString()
		if  == '\n' {
			// a backslash-escaped newline is elided from the output entirely
		} else {
			.WriteString([:])
		}
		 = [:]
	}

	goto 

:
	{
		 := strings.IndexRune(, SingleChar)
		if  == -1 {
			if  {
				 = .String() + YellowFG + string(SingleChar) + 
			}
			return "", , ErrUnterminatedSingleQuote
		}
		// Catch up opening quote
		if  {
			.WriteString(YellowFG)
			.WriteRune(SingleChar)
		}

		.WriteString([0:])
		 = [+1:]

		if  {
			.WriteRune(SingleChar)
			.WriteString(ResetFG)
		}
		goto 
	}

:
	{
		 := 
		for len() > 0 {
			,  := utf8.DecodeRuneInString()
			 = [:]
			if  == DoubleChar {
				// Catch up opening quote
				if  {
					.WriteString(YellowFG)
					.WriteRune()
				}

				.WriteString([0 : len()-len()-])

				if  {
					.WriteRune()
					.WriteString(ResetFG)
				}
				 = 
				goto 
			} else if  == EscapeChar && ! {
				// bash only supports certain escapes in double-quoted strings
				,  := utf8.DecodeRuneInString()
				 = [:]
				if strings.ContainsRune(DoubleEscapeChars, ) {
					.WriteString([0 : len()-len()--])
					if  == '\n' {
						// newline is special, skip the backslash entirely
					} else {
						.WriteRune()
					}
					 = 
				}
			}
		}

		if  {
			 = .String() + YellowFG + string(DoubleChar) + 
		}

		return "", , ErrUnterminatedDoubleQuote
	}

:
	return .String(), , nil
}