mirror of
https://github.com/go-sylixos/elvish.git
synced 2024-12-04 02:37:50 +08:00
parse: Add QuoteAs, move [qQ]uote* to quote.go.
This commit is contained in:
parent
4d63220e14
commit
a657a2989a
|
@ -1038,84 +1038,7 @@ func isSpaceOrNewline(r rune) bool {
|
|||
return isSpace(r) || r == '\n'
|
||||
}
|
||||
|
||||
// Helpers.
|
||||
|
||||
func addChild(p Node, ch Node) {
|
||||
p.n().children = append(p.n().children, ch)
|
||||
ch.n().parent = p
|
||||
}
|
||||
|
||||
// Quote returns a representation of s in elvish syntax. Bareword is tried
|
||||
// first, then single quoted string and finally double quoted string.
|
||||
func Quote(s string) string {
|
||||
if s == "" {
|
||||
return `""`
|
||||
}
|
||||
bare := s[0] != '~'
|
||||
for _, r := range s {
|
||||
if !unicode.IsPrint(r) && r != '\n' {
|
||||
return quoteDouble(s)
|
||||
}
|
||||
if !allowedInBareword(r) {
|
||||
bare = false
|
||||
}
|
||||
}
|
||||
if bare {
|
||||
return s
|
||||
}
|
||||
return quoteSingle(s)
|
||||
}
|
||||
|
||||
func quoteSingle(s string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('\'')
|
||||
for _, r := range s {
|
||||
buf.WriteRune(r)
|
||||
if r == '\'' {
|
||||
buf.WriteByte('\'')
|
||||
}
|
||||
}
|
||||
buf.WriteByte('\'')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func rtohex(r rune, w int) []byte {
|
||||
bytes := make([]byte, w)
|
||||
for i := w - 1; i >= 0; i-- {
|
||||
d := byte(r % 16)
|
||||
r /= 16
|
||||
if d <= 9 {
|
||||
bytes[i] = '0' + d
|
||||
} else {
|
||||
bytes[i] = 'a' + d - 10
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func quoteDouble(s string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('"')
|
||||
for _, r := range s {
|
||||
if r == '\\' || r == '"' {
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteRune(r)
|
||||
} else if !unicode.IsPrint(r) {
|
||||
buf.WriteByte('\\')
|
||||
if r <= 0xff {
|
||||
buf.WriteByte('x')
|
||||
buf.Write(rtohex(r, 2))
|
||||
} else if r <= 0xffff {
|
||||
buf.WriteByte('u')
|
||||
buf.Write(rtohex(r, 4))
|
||||
} else {
|
||||
buf.WriteByte('U')
|
||||
buf.Write(rtohex(r, 8))
|
||||
}
|
||||
} else {
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
return buf.String()
|
||||
}
|
||||
|
|
|
@ -350,28 +350,3 @@ func TestParseError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
var quoteTests = []struct {
|
||||
text, quoted string
|
||||
}{
|
||||
// Empty string is quoted with double quote.
|
||||
{"", `""`},
|
||||
// Bareword when possible.
|
||||
{"x-y,z@h/d", "x-y,z@h/d"},
|
||||
// Single quote when there is special char but no unprintable.
|
||||
{"x$y[]\nef'", "'x$y[]\nef'''"},
|
||||
// Tilde needs quoting only when appearing at the beginning
|
||||
{"~x", "'~x'"},
|
||||
{"x~", "x~"},
|
||||
// Double quote when there is unprintable char.
|
||||
{"\x1b\"\\", `"\x1b\"\\"`},
|
||||
}
|
||||
|
||||
func TestQuote(t *testing.T) {
|
||||
for _, tc := range quoteTests {
|
||||
got := Quote(tc.text)
|
||||
if got != tc.quoted {
|
||||
t.Errorf("Quote(%q) => %s, want %s", tc.text, got, tc.quoted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
98
parse/quote.go
Normal file
98
parse/quote.go
Normal file
|
@ -0,0 +1,98 @@
|
|||
package parse
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Quote returns a representation of s in elvish syntax. Bareword is tried
|
||||
// first, then single quoted string and finally double quoted string.
|
||||
func Quote(s string) string {
|
||||
s, _ = QuoteAs(s, Bareword)
|
||||
return s
|
||||
}
|
||||
|
||||
// QuoteAs returns a representation of s in elvish syntax, using the syntax
|
||||
// specified by q, which must be one of Bareword, SingleQuoted, or
|
||||
// DoubleQuoted. It returns the quoted string and the actual quoting.
|
||||
func QuoteAs(s string, q PrimaryType) (string, PrimaryType) {
|
||||
if q == DoubleQuoted {
|
||||
// Everything can be quoted using double quotes, return directly.
|
||||
return quoteDouble(s), DoubleQuoted
|
||||
}
|
||||
if s == "" {
|
||||
return "''", SingleQuoted
|
||||
}
|
||||
|
||||
// Keep track of whether it is a valid bareword.
|
||||
bare := s[0] != '~'
|
||||
for _, r := range s {
|
||||
if !unicode.IsPrint(r) && r != '\n' {
|
||||
// Contains unprintable character that is not a newline. Force
|
||||
// double quote.
|
||||
return quoteDouble(s), DoubleQuoted
|
||||
}
|
||||
if !allowedInBareword(r) {
|
||||
bare = false
|
||||
}
|
||||
}
|
||||
|
||||
if q == Bareword && bare {
|
||||
return s, Bareword
|
||||
}
|
||||
return quoteSingle(s), SingleQuoted
|
||||
}
|
||||
|
||||
func quoteSingle(s string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('\'')
|
||||
for _, r := range s {
|
||||
buf.WriteRune(r)
|
||||
if r == '\'' {
|
||||
buf.WriteByte('\'')
|
||||
}
|
||||
}
|
||||
buf.WriteByte('\'')
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func rtohex(r rune, w int) []byte {
|
||||
bytes := make([]byte, w)
|
||||
for i := w - 1; i >= 0; i-- {
|
||||
d := byte(r % 16)
|
||||
r /= 16
|
||||
if d <= 9 {
|
||||
bytes[i] = '0' + d
|
||||
} else {
|
||||
bytes[i] = 'a' + d - 10
|
||||
}
|
||||
}
|
||||
return bytes
|
||||
}
|
||||
|
||||
func quoteDouble(s string) string {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('"')
|
||||
for _, r := range s {
|
||||
if r == '\\' || r == '"' {
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteRune(r)
|
||||
} else if !unicode.IsPrint(r) {
|
||||
buf.WriteByte('\\')
|
||||
if r <= 0xff {
|
||||
buf.WriteByte('x')
|
||||
buf.Write(rtohex(r, 2))
|
||||
} else if r <= 0xffff {
|
||||
buf.WriteByte('u')
|
||||
buf.Write(rtohex(r, 4))
|
||||
} else {
|
||||
buf.WriteByte('U')
|
||||
buf.Write(rtohex(r, 8))
|
||||
}
|
||||
} else {
|
||||
buf.WriteRune(r)
|
||||
}
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
return buf.String()
|
||||
}
|
28
parse/quote_test.go
Normal file
28
parse/quote_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package parse
|
||||
|
||||
import "testing"
|
||||
|
||||
var quoteTests = []struct {
|
||||
text, quoted string
|
||||
}{
|
||||
// Empty string is quoted with single quote.
|
||||
{"", `''`},
|
||||
// Bareword when possible.
|
||||
{"x-y,z@h/d", "x-y,z@h/d"},
|
||||
// Single quote when there is special char but no unprintable.
|
||||
{"x$y[]\nef'", "'x$y[]\nef'''"},
|
||||
// Tilde needs quoting only when appearing at the beginning
|
||||
{"~x", "'~x'"},
|
||||
{"x~", "x~"},
|
||||
// Double quote when there is unprintable char.
|
||||
{"\x1b\"\\", `"\x1b\"\\"`},
|
||||
}
|
||||
|
||||
func TestQuote(t *testing.T) {
|
||||
for _, tc := range quoteTests {
|
||||
got := Quote(tc.text)
|
||||
if got != tc.quoted {
|
||||
t.Errorf("Quote(%q) => %s, want %s", tc.text, got, tc.quoted)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user