]> Cypherpunks repositories - gostls13.git/commitdiff
fmt: debugging formats for characters: %+q %#U
authorRob Pike <r@golang.org>
Sat, 11 Jun 2011 00:03:02 +0000 (00:03 +0000)
committerRob Pike <r@golang.org>
Sat, 11 Jun 2011 00:03:02 +0000 (00:03 +0000)
%+q uses strconv.Quote[Rune]ToASCII, guaranteeing ASCII-only output.
%#U a quoted character if the rune is printable: 'x'=U+0078; otherwise
it's as before: U+000A.

R=golang-dev, gri, rsc
CC=golang-dev
https://golang.org/cl/4589047

src/pkg/fmt/doc.go
src/pkg/fmt/fmt_test.go
src/pkg/fmt/format.go
src/pkg/fmt/print.go

index 79fe5758c9344dd0b052e1b06cc0899723f3bb55..35a11e19fa18fbbdf6139b0448a5e453050908d6 100644 (file)
        number of characters to output, truncating if necessary.
 
        Other flags:
-               +       always print a sign for numeric values
+               +       always print a sign for numeric values;
+                       guarantee ASCII-only output for %q (%+q)
                -       pad with spaces on the right rather than the left (left-justify the field)
                #       alternate format: add leading 0 for octal (%#o), 0x for hex (%#x);
                        0X for hex (%#X); suppress 0x for %p (%#p);
-                       print a raw (backquoted) string if possible for %q (%#q)
+                       print a raw (backquoted) string if possible for %q (%#q);
+                       write e.g. U+0078 'x' if the character is printable for %U (%#U).
                ' '     (space) leave a space for elided sign in numbers (% d);
                        put spaces between bytes printing strings or slices in hex (% x, % X)
                0       pad with leading zeros rather than spaces
index 122b9516bad04f481a26b31d5542b28ac57b4f13..3d255c3d1f29552597d7158adc600c4df4dc4eb0 100644 (file)
@@ -133,6 +133,7 @@ var fmttests = []struct {
        {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`},
        {"%q", "abc\xffdef", `"abc\xffdef"`},
        {"%q", "\u263a", `"☺"`},
+       {"%+q", "\u263a", `"\u263a"`},
        {"%q", "\U0010ffff", `"\U0010ffff"`},
 
        // escaped characters
@@ -145,6 +146,8 @@ var fmttests = []struct {
        {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`},
        {"%q", '"', `'"'`},
        {"%q", '\'', `'\''`},
+       {"%q", "\u263a", `"☺"`},
+       {"%+q", "\u263a", `"\u263a"`},
 
        // width
        {"%5s", "abc", "  abc"},
@@ -187,6 +190,12 @@ var fmttests = []struct {
        {"%U", 0x12345, "U+12345"},
        {"%10.6U", 0xABC, "  U+000ABC"},
        {"%-10.6U", 0xABC, "U+000ABC  "},
+       {"%U", '\n', `U+000A`},
+       {"%#U", '\n', `U+000A`},
+       {"%U", 'x', `U+0078`},
+       {"%#U", 'x', `U+0078 'x'`},
+       {"%U", '\u263a', `U+263A`},
+       {"%#U", '\u263a', `U+263A '☺'`},
 
        // floats
        {"%+.3e", 0.0, "+0.000e+00"},
index 5dcfb967744bf058a02549b7bfe726ebf57d18e1..bec55f75ba0d60a53e4eb04b6b6df33684e0b384 100644 (file)
@@ -7,6 +7,7 @@ package fmt
 import (
        "bytes"
        "strconv"
+       "unicode"
        "utf8"
 )
 
@@ -50,6 +51,7 @@ type fmt struct {
        sharp       bool
        space       bool
        unicode     bool
+       uniQuote    bool // Use 'x'= prefix for %U if printable.
        zero        bool
 }
 
@@ -63,6 +65,7 @@ func (f *fmt) clearflags() {
        f.sharp = false
        f.space = false
        f.unicode = false
+       f.uniQuote = false
        f.zero = false
 }
 
@@ -232,6 +235,24 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) {
                i--
                buf[i] = ' '
        }
+
+       // If we want a quoted char for %#U, move the data up to make room.
+       if f.unicode && f.uniQuote && a >= 0 && a <= unicode.MaxRune && unicode.IsPrint(int(a)) {
+               runeWidth := utf8.RuneLen(int(a))
+               width := 1 + 1 + runeWidth + 1 // space, quote, rune, quote
+               copy(buf[i-width:], buf[i:])   // guaranteed to have enough room.
+               i -= width
+               // Now put " 'x'" at the end.
+               j := len(buf) - width
+               buf[j] = ' '
+               j++
+               buf[j] = '\''
+               j++
+               utf8.EncodeRune(buf[j:], int(a))
+               j += runeWidth
+               buf[j] = '\''
+       }
+
        f.pad(buf[i:])
 }
 
@@ -291,7 +312,11 @@ func (f *fmt) fmt_q(s string) {
        if f.sharp && strconv.CanBackquote(s) {
                quoted = "`" + s + "`"
        } else {
-               quoted = strconv.Quote(s)
+               if f.plus {
+                       quoted = strconv.QuoteToASCII(s)
+               } else {
+                       quoted = strconv.Quote(s)
+               }
        }
        f.padString(quoted)
 }
@@ -299,7 +324,12 @@ func (f *fmt) fmt_q(s string) {
 // fmt_qc formats the integer as a single-quoted, escaped Go character constant.
 // If the character is not valid Unicode, it will print '\ufffd'.
 func (f *fmt) fmt_qc(c int64) {
-       quoted := strconv.QuoteRune(int(c))
+       var quoted string
+       if f.plus {
+               quoted = strconv.QuoteRuneToASCII(int(c))
+       } else {
+               quoted = strconv.QuoteRune(int(c))
+       }
        f.padString(quoted)
 }
 
index c18a8ea38d2eb468248f6ac27b3b91bb48ec4308..2b2a7192703706004bcd75109ad38d1945302522 100644 (file)
@@ -363,6 +363,8 @@ func (p *pp) fmt0x64(v uint64, leading0x bool) {
 // temporarily turning on the unicode flag and tweaking the precision.
 func (p *pp) fmtUnicode(v int64) {
        precPresent := p.fmt.precPresent
+       sharp := p.fmt.sharp
+       p.fmt.sharp = false
        prec := p.fmt.prec
        if !precPresent {
                // If prec is already set, leave it alone; otherwise 4 is minimum.
@@ -370,10 +372,13 @@ func (p *pp) fmtUnicode(v int64) {
                p.fmt.precPresent = true
        }
        p.fmt.unicode = true // turn on U+
+       p.fmt.uniQuote = sharp
        p.fmt.integer(int64(v), 16, unsigned, udigits)
        p.fmt.unicode = false
+       p.fmt.uniQuote = false
        p.fmt.prec = prec
        p.fmt.precPresent = precPresent
+       p.fmt.sharp = sharp
 }
 
 func (p *pp) fmtUint64(v uint64, verb int, goSyntax bool, value interface{}) {