Use raw literal for string formatting (#46)

When formatting strings, avoid using strconv.Quote if the string looks
like it has already been escaped. Instead, use a raw string literal
by wrapping the string with '`' characters if possible.
For now, we still use strconv.Quote if the input string contains newlines
to maintain the property that Format outputs a single line.

Also, prefix strings obtained by the Stringer.String method with a 's'.
This allows users to more easily distinguish when an output really is
of type string or if the String method was used.
diff --git a/cmp/compare_test.go b/cmp/compare_test.go
index c26ac3f..734e46a 100644
--- a/cmp/compare_test.go
+++ b/cmp/compare_test.go
@@ -206,7 +206,7 @@
 		label:    label,
 		x:        &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
 		y:        &struct{ R *bytes.Buffer }{},
-		wantDiff: "root.R:\n\t-: \"\"\n\t+: <nil>\n",
+		wantDiff: "root.R:\n\t-: s\"\"\n\t+: <nil>\n",
 	}, {
 		label: label,
 		x:     &struct{ R *bytes.Buffer }{new(bytes.Buffer)},
@@ -262,8 +262,8 @@
 		})},
 		wantDiff: `
 {[]*regexp.Regexp}[1]:
-	-: "a*b*c*"
-	+: "a*b*d*"`,
+	-: s"a*b*c*"
+	+: s"a*b*d*"`,
 	}, {
 		label: label,
 		x: func() ***int {
@@ -312,8 +312,8 @@
 		opts:  []cmp.Option{cmp.Comparer(func(x, y fmt.Stringer) bool { return x.String() == y.String() })},
 		wantDiff: `
 root:
-	-: "hello"
-	+: "hello2"`,
+	-: s"hello"
+	+: s"hello2"`,
 	}, {
 		label: label,
 		x:     md5.Sum([]byte{'a'}),
@@ -405,6 +405,20 @@
 {[]int}:
 	-: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 	+: []int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}`,
+	}, {
+		// Ensure reasonable Stringer formatting of map keys.
+		label: label,
+		x:     map[*pb.Stringer]*pb.Stringer{{"hello"}: {"world"}},
+		y:     map[*pb.Stringer]*pb.Stringer(nil),
+		wantDiff: `
+{map[*testprotos.Stringer]*testprotos.Stringer}:
+	-: map[*testprotos.Stringer]*testprotos.Stringer{s"hello": s"world"}
+	+: map[*testprotos.Stringer]*testprotos.Stringer(nil)`,
+	}, {
+		// Ensure Stringer avoids double-quote escaping if possible.
+		label:    label,
+		x:        []*pb.Stringer{{`multi\nline\nline\nline`}},
+		wantDiff: ":\n\t-: []*testprotos.Stringer{s`multi\\nline\\nline\\nline`}\n\t+: <non-existent>",
 	}}
 }
 
@@ -1535,7 +1549,7 @@
 			Args: &pb.MetaData{Stringer: pb.Stringer{"metadata2"}},
 		}}},
 		opts:     []cmp.Option{cmp.Comparer(pb.Equal)},
-		wantDiff: "{teststructs.Eagle}.Slaps[4].Args:\n\t-: \"metadata\"\n\t+: \"metadata2\"\n",
+		wantDiff: "{teststructs.Eagle}.Slaps[4].Args:\n\t-: s\"metadata\"\n\t+: s\"metadata2\"\n",
 	}, {
 		label: label,
 		x:     createEagle(),
@@ -1657,11 +1671,11 @@
 		opts: []cmp.Option{cmp.Comparer(pb.Equal), equalDish},
 		wantDiff: `
 {teststructs.GermBatch}.DirtyGerms[18][0->?]:
-	-: "germ2"
+	-: s"germ2"
 	+: <non-existent>
 {teststructs.GermBatch}.DirtyGerms[18][?->2]:
 	-: <non-existent>
-	+: "germ2"`,
+	+: s"germ2"`,
 	}, {
 		label: label,
 		x:     createBatch(),
@@ -1690,9 +1704,9 @@
 		wantDiff: `
 {teststructs.GermBatch}.DirtyGerms[17]:
 	-: <non-existent>
-	+: []*testprotos.Germ{"germ1"}
+	+: []*testprotos.Germ{s"germ1"}
 {teststructs.GermBatch}.DirtyGerms[18][2->?]:
-	-: "germ4"
+	-: s"germ4"
 	+: <non-existent>
 {teststructs.GermBatch}.DishMap[1]:
 	-: (*teststructs.Dish)(nil)
@@ -1777,14 +1791,14 @@
 	-: teststructs.DiscordState(554)
 	+: teststructs.DiscordState(500)
 λ({teststructs.Dirt}.Proto):
-	-: "blah"
-	+: "proto"
+	-: s"blah"
+	+: s"proto"
 {teststructs.Dirt}.wizard["albus"]:
-	-: "dumbledore"
+	-: s"dumbledore"
 	+: <non-existent>
 {teststructs.Dirt}.wizard["harry"]:
-	-: "potter"
-	+: "otter"`,
+	-: s"potter"
+	+: s"otter"`,
 	}}
 }
 
diff --git a/cmp/internal/value/format.go b/cmp/internal/value/format.go
index 9b27152..74d7f0c 100644
--- a/cmp/internal/value/format.go
+++ b/cmp/internal/value/format.go
@@ -8,9 +8,9 @@
 import (
 	"fmt"
 	"reflect"
+	"strconv"
 	"strings"
 	"unicode"
-	"unicode/utf8"
 )
 
 var stringerIface = reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
@@ -43,7 +43,10 @@
 		if (v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface) && v.IsNil() {
 			return "<nil>"
 		}
-		return fmt.Sprintf("%q", v.Interface().(fmt.Stringer).String())
+
+		const stringerPrefix = "s" // Indicates that the String method was used
+		s := v.Interface().(fmt.Stringer).String()
+		return stringerPrefix + formatString(s)
 	}
 
 	switch v.Kind() {
@@ -62,7 +65,7 @@
 	case reflect.Complex64, reflect.Complex128:
 		return formatPrimitive(v.Type(), v.Complex(), conf)
 	case reflect.String:
-		return formatPrimitive(v.Type(), fmt.Sprintf("%q", v), conf)
+		return formatPrimitive(v.Type(), formatString(v.String()), conf)
 	case reflect.UnsafePointer, reflect.Chan, reflect.Func:
 		return formatPointer(v, conf)
 	case reflect.Ptr:
@@ -123,11 +126,13 @@
 		visited = insertPointer(visited, v.Pointer())
 
 		var ss []string
-		subConf := conf
-		subConf.printType = v.Type().Elem().Kind() == reflect.Interface
+		keyConf, valConf := conf, conf
+		keyConf.printType = v.Type().Key().Kind() == reflect.Interface
+		keyConf.followPointers = false
+		valConf.printType = v.Type().Elem().Kind() == reflect.Interface
 		for _, k := range SortKeys(v.MapKeys()) {
-			sk := formatAny(k, formatConfig{realPointers: conf.realPointers}, visited)
-			sv := formatAny(v.MapIndex(k), subConf, visited)
+			sk := formatAny(k, keyConf, visited)
+			sv := formatAny(v.MapIndex(k), valConf, visited)
 			ss = append(ss, fmt.Sprintf("%s: %s", sk, sv))
 		}
 		s := fmt.Sprintf("{%s}", strings.Join(ss, ", "))
@@ -145,7 +150,7 @@
 				continue // Elide zero value fields
 			}
 			name := v.Type().Field(i).Name
-			subConf.useStringer = conf.useStringer && isExported(name)
+			subConf.useStringer = conf.useStringer
 			s := formatAny(vv, subConf, visited)
 			ss = append(ss, fmt.Sprintf("%s: %s", name, s))
 		}
@@ -159,6 +164,17 @@
 	}
 }
 
+func formatString(s string) string {
+	// Avoid performing quote-escaping if the string is already escaped.
+	hasEscapes := strings.ContainsAny(s, `\`)
+	allPrintable := strings.IndexFunc(s, unicode.IsPrint) >= 0
+	rawAllowed := !strings.ContainsAny(s, "`\n")
+	if hasEscapes && allPrintable && rawAllowed {
+		return "`" + s + "`"
+	}
+	return strconv.Quote(s)
+}
+
 func formatPrimitive(t reflect.Type, v interface{}, conf formatConfig) string {
 	if conf.printType && t.PkgPath() != "" {
 		return fmt.Sprintf("%v(%v)", t, v)
@@ -247,9 +263,3 @@
 	}
 	return false
 }
-
-// isExported reports whether the identifier is exported.
-func isExported(id string) bool {
-	r, _ := utf8.DecodeRuneInString(id)
-	return unicode.IsUpper(r)
-}
diff --git a/cmp/internal/value/format_test.go b/cmp/internal/value/format_test.go
index b56380f..36c4f23 100644
--- a/cmp/internal/value/format_test.go
+++ b/cmp/internal/value/format_test.go
@@ -47,7 +47,7 @@
 		want: "map[value.key]string{{a: 5, b: \"key\", c: (chan bool)(0x00)}: \"hello\"}",
 	}, {
 		in:   map[io.Reader]string{new(bytes.Reader): "hello"},
-		want: "map[io.Reader]string{0x00: \"hello\"}",
+		want: "map[io.Reader]string{(*bytes.Reader)(0x00): \"hello\"}",
 	}, {
 		in: func() interface{} {
 			var a = []interface{}{nil}