package completion
import (
"bytes"
"errors"
"regexp"
"strings"
"unicode"
"unicode/utf8"
"github.com/reeflective/console/internal/line"
)
func UnescapeValue (prefixComp , prefixLine , val string ) string {
quoted := strings .HasPrefix (prefixLine , "\"" ) ||
strings .HasPrefix (prefixLine , "'" )
if quoted {
val = strings .ReplaceAll (val , "\\ " , " " )
}
return val
}
func SplitArgs (line []rune , pos int ) (args []string , prefixComp , prefixLine string ) {
line = line [:pos ]
line = []rune (strip (string (line )))
args , remain , err := splitCompWords (string (line ))
mustComplete , args , remain := mustComplete (line , args , remain , err )
if mustComplete {
return sanitizeArgs (args ), "" , remain
}
arg , prefixComp , prefixLine := adjustQuotedPrefix (remain , err )
args = append (args , arg )
return sanitizeArgs (args ), prefixComp , prefixLine
}
func mustComplete(line []rune , args []string , remain string , err error ) (bool , []string , string ) {
dummyArg := ""
if len (args ) == 0 || len (line ) == 0 {
return true , append (args , dummyArg ), remain
}
if err != nil {
return false , args , remain
}
lastChar := line [len (line )-1 ]
if remain == "" && unicode .IsSpace (lastChar ) {
if strings .HasSuffix (string (line ), "\\ " ) {
return true , args , args [len (args )-1 ]
}
return true , append (args , dummyArg ), remain
}
return true , args , remain
}
func adjustQuotedPrefix(remain string , err error ) (arg , comp , input string ) {
arg = remain
switch {
case errors .Is (err , line .ErrUnterminatedDoubleQuote ):
comp = "\""
input = comp + arg
case errors .Is (err , line .ErrUnterminatedSingleQuote ):
comp = "'"
input = comp + arg
case errors .Is (err , line .ErrUnterminatedEscape ):
arg = strings .ReplaceAll (arg , "\\" , "" )
}
return arg , comp , input
}
func sanitizeArgs(args []string ) (sanitized []string ) {
for _ , arg := range args {
arg = replacer .Replace (arg )
sanitized = append (sanitized , arg )
}
return sanitized
}
func splitCompWords(input string ) (words []string , remainder string , err error ) {
var buf bytes .Buffer
words = make ([]string , 0 )
for len (input ) > 0 {
char , read := utf8 .DecodeRuneInString (input )
if strings .ContainsRune (line .SplitChars , char ) {
input = input [read :]
continue
} else if char == line .EscapeChar {
next := input [read :]
if len (next ) == 0 {
remainder = string (line .EscapeChar )
err = line .ErrUnterminatedEscape
return words , remainder , err
}
c2 , l2 := utf8 .DecodeRuneInString (next )
if c2 == '\n' {
input = next [l2 :]
continue
}
}
var word string
word , input , err = splitCompWord (input , &buf )
if err != nil {
return words , word + input , err
}
words = append (words , word )
}
return words , remainder , nil
}
func splitCompWord(input string , buf *bytes .Buffer ) (word string , remainder string , err error ) {
buf .Reset ()
raw :
{
cur := input
for len (cur ) > 0 {
char , read := utf8 .DecodeRuneInString (cur )
cur = cur [read :]
switch {
case char == line .SingleChar :
buf .WriteString (input [0 : len (input )-len (cur )-read ])
input = cur
goto single
case char == line .DoubleChar :
buf .WriteString (input [0 : len (input )-len (cur )-read ])
input = cur
goto double
case char == line .EscapeChar :
buf .WriteString (input [0 : len (input )-len (cur )-read ])
buf .WriteRune (char )
input = cur
goto escape
case strings .ContainsRune (line .SplitChars , char ):
buf .WriteString (input [0 : len (input )-len (cur )-read ])
return buf .String (), cur , nil
}
}
if len (input ) > 0 {
buf .WriteString (input )
input = ""
}
goto done
}
escape :
{
if len (input ) == 0 {
input = buf .String () + input
return "" , input , line .ErrUnterminatedEscape
}
c , l := utf8 .DecodeRuneInString (input )
if c != '\n' {
buf .WriteString (input [:l ])
}
input = input [l :]
}
goto raw
single :
{
i := strings .IndexRune (input , line .SingleChar )
if i == -1 {
return "" , input , line .ErrUnterminatedSingleQuote
}
buf .WriteString (input [0 :i ])
input = input [i +1 :]
goto raw
}
double :
{
cur := input
for len (cur ) > 0 {
c , read := utf8 .DecodeRuneInString (cur )
cur = cur [read :]
switch c {
case line .DoubleChar :
buf .WriteString (input [0 : len (input )-len (cur )-read ])
input = cur
goto raw
case line .EscapeChar :
char2 , l2 := utf8 .DecodeRuneInString (cur )
cur = cur [l2 :]
if strings .ContainsRune (line .DoubleEscapeChars , char2 ) {
buf .WriteString (input [0 : len (input )-len (cur )-read -l2 ])
if char2 != '\n' {
buf .WriteRune (char2 )
}
input = cur
}
}
}
return "" , input , line .ErrUnterminatedDoubleQuote
}
done :
return buf .String (), input , 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 )
func strip(str string ) string {
return re .ReplaceAllString (str , "" )
}
var replacer = strings .NewReplacer (
"\n" , ` ` ,
"\t" , ` ` ,
"\\ " , " " ,
)
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .