From: Rob Pike Date: Wed, 21 Jul 2010 02:53:59 +0000 (-0700) Subject: bytes: add Title X-Git-Tag: weekly.2010-07-29~65 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=de228c0e0c6e4f5736dcdde913afff527d0f76f5;p=gostls13.git bytes: add Title R=rsc CC=golang-dev https://golang.org/cl/1872042 --- diff --git a/src/pkg/bytes/bytes.go b/src/pkg/bytes/bytes.go index bcf7b8609b..25a5738324 100644 --- a/src/pkg/bytes/bytes.go +++ b/src/pkg/bytes/bytes.go @@ -332,6 +332,52 @@ func ToLower(s []byte) []byte { return Map(unicode.ToLower, s) } // ToTitle returns a copy of the byte array s with all Unicode letters mapped to their title case. func ToTitle(s []byte) []byte { return Map(unicode.ToTitle, s) } +// isSeparator reports whether the rune could mark a word boundary. +// TODO: update when package unicode captures more of the properties. +func isSeparator(rune int) bool { + // ASCII alphanumerics and underscore are not separators + if rune <= 0x7F { + switch { + case '0' <= rune && rune <= '9': + return false + case 'a' <= rune && rune <= 'z': + return false + case 'A' <= rune && rune <= 'Z': + return false + case rune == '_': + return false + } + return true + } + // Letters and digits are not separators + if unicode.IsLetter(rune) || unicode.IsDigit(rune) { + return false + } + // Otherwise, all we can do for now is treat spaces as separators. + return unicode.IsSpace(rune) +} + +// BUG(r): The rule Title uses for word boundaries does not handle Unicode punctuation properly. + +// Title returns a copy of s with all Unicode letters that begin words +// mapped to their title case. +func Title(s []byte) []byte { + // Use a closure here to remember state. + // Hackish but effective. Depends on Map scanning in order and calling + // the closure once per rune. + prev := ' ' + return Map( + func(r int) int { + if isSeparator(prev) { + prev = r + return unicode.ToTitle(r) + } + prev = r + return r + }, + s) +} + // TrimLeftFunc returns a subslice of s by slicing off all leading UTF-8 encoded // Unicode code points c that satisfy f(c). func TrimLeftFunc(s []byte, f func(r int) bool) []byte { diff --git a/src/pkg/bytes/bytes_test.go b/src/pkg/bytes/bytes_test.go index 8197543dcf..de5edd120f 100644 --- a/src/pkg/bytes/bytes_test.go +++ b/src/pkg/bytes/bytes_test.go @@ -685,3 +685,25 @@ func TestReplace(t *testing.T) { } } } + +type TitleTest struct { + in, out string +} + +var TitleTests = []TitleTest{ + TitleTest{"", ""}, + TitleTest{"a", "A"}, + TitleTest{" aaa aaa aaa ", " Aaa Aaa Aaa "}, + TitleTest{" Aaa Aaa Aaa ", " Aaa Aaa Aaa "}, + TitleTest{"123a456", "123a456"}, + TitleTest{"double-blind", "Double-Blind"}, + TitleTest{"ÿøû", "Ÿøû"}, +} + +func TestTitle(t *testing.T) { + for _, tt := range TitleTests { + if s := string(Title([]byte(tt.in))); s != tt.out { + t.Errorf("Title(%q) = %q, want %q", tt.in, s, tt.out) + } + } +}