~mna/runes

ref: fadde3859f490efbe4eca1048174940c68a6e357 runes/print.go -rw-r--r-- 2.9 KiB
fadde385Martin Angers doc 1 year, 5 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"sort"
	"strings"
	"unicode"
	"unicode/utf16"
	"unicode/utf8"

	"github.com/mattn/go-runewidth"
	"golang.org/x/text/unicode/runenames"
)

type printer interface {
	printStart(w io.Writer, count int) error
	printRune(runeInfo) error
	printEnd() error
}

type runeInfo struct {
	Rune       rune     `json:"rune"`
	Name       string   `json:"name"`
	Valid      bool     `json:"valid"`
	Categories []string `json:"categories"`
	UTF8       []byte   `json:"-"`
	UTF16      []uint16 `json:"utf16"`
	UTF8JSON   []uint16 `json:"utf8"`
}

// return a filled runeInfo struct for that rune r.
func info(r rune) runeInfo {
	var buf [utf8.UTFMax]byte

	ri := runeInfo{Rune: r}
	ri.Valid = utf8.ValidRune(r)
	if !ri.Valid {
		return ri
	}

	ri.Name = runenames.Name(r)

	n := utf8.EncodeRune(buf[:], r)
	ri.UTF8 = buf[:n]
	r1, r2 := utf16.EncodeRune(r)
	if r1 == utf8.RuneError && r2 == utf8.RuneError {
		ri.UTF16 = []uint16{uint16(r)}
	} else {
		ri.UTF16 = []uint16{uint16(r1), uint16(r2)}
	}

	var cats []string
	for nm, rt := range unicode.Categories {
		if unicode.Is(rt, r) {
			cats = append(cats, nm)
		}
	}
	sort.Strings(cats)
	ri.Categories = cats
	return ri
}

// print a single rune.
func printRune(p printer, r rune) error {
	ri := info(r)
	return p.printRune(ri)
}

// print an explicit list of runes.
func printRunes(p printer, rs []rune) error {
	for _, r := range rs {
		if err := printRune(p, r); err != nil {
			return err
		}
	}
	return nil
}

type textPrinter struct {
	bw *bufio.Writer
}

func (tp *textPrinter) printStart(w io.Writer, count int) error {
	tp.bw = bufio.NewWriter(w)
	return nil
}

func (tp *textPrinter) printRune(ri runeInfo) error {
	var catgs string
	if ri.Valid {
		catgs = fmt.Sprintf("%v", ri.Categories)
	} else {
		catgs = "[!]"
	}
	fmt.Fprintf(tp.bw, "%-7s", catgs)

	//wd := runewidth.RuneWidth(ri.Rune)
	rn := fmt.Sprintf("%#U", ri.Rune)
	if n := runewidth.StringWidth(rn); n < 15 {
		rn += strings.Repeat(" ", 15-n)
	}
	fmt.Fprintf(tp.bw, "%s", rn)

	u8 := fmt.Sprintf("[% X]", ri.UTF8)
	fmt.Fprintf(tp.bw, "%-14s", u8)

	var u16 string
	if len(ri.UTF16) == 2 {
		u16 = fmt.Sprintf("[%X %X]", ri.UTF16[0], ri.UTF16[1])
	} else {
		u16 = fmt.Sprintf("[%X]", ri.UTF16[0])
	}
	fmt.Fprintf(tp.bw, "%-12s", u16)

	fmt.Fprintln(tp.bw, ri.Name)
	return nil
}

func (tp *textPrinter) printEnd() error {
	return tp.bw.Flush()
}

type jsonPrinter struct {
	ris []runeInfo
	bw  *bufio.Writer
}

func (jp *jsonPrinter) printStart(w io.Writer, count int) error {
	jp.bw = bufio.NewWriter(w)
	jp.ris = make([]runeInfo, 0, count)
	return nil
}

func (jp *jsonPrinter) printRune(ri runeInfo) error {
	ri.UTF8JSON = make([]uint16, len(ri.UTF8))
	for i, b := range ri.UTF8 {
		ri.UTF8JSON[i] = uint16(b)
	}
	jp.ris = append(jp.ris, ri)
	return nil
}

func (jp *jsonPrinter) printEnd() error {
	enc := json.NewEncoder(jp.bw)
	enc.SetIndent("", "  ")
	if err := enc.Encode(jp.ris); err != nil {
		return err
	}
	return jp.bw.Flush()
}