all repos

clerk @ e5cc255b298b03abb550f8ba04e126dc88146551

missing tooling for ledger/hledger

clerk/internal/tags/tags.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
generate tags, 2 days ago
1
package tags
2
3
import (
4
	"fmt"
5
	"io"
6
	"slices"
7
	"sort"
8
	"strings"
9
)
10
11
type Entry struct {
12
	Name     string
13
	Kind     byte
14
	File     string // file path (relative or absolute)
15
	Line     int    // line number
16
	Pattern  string // the full line content (without /^ $/ wrappers)
17
	Language string // language field, emitted per-entry when non-empty
18
}
19
20
type Writer struct {
21
	entries []Entry
22
	kinds   map[byte]string // kind: description
23
}
24
25
func NewWriter() *Writer {
26
	return &Writer{kinds: make(map[byte]string)}
27
}
28
29
func (w *Writer) DescribeKind(kind byte, description string) {
30
	w.kinds[kind] = description
31
}
32
33
func (w *Writer) Add(entry Entry) {
34
	w.entries = append(w.entries, entry)
35
}
36
37
func (w *Writer) Write(to io.Writer) error {
38
	// Pseudo-tags
39
	_, _ = fmt.Fprintf(to, "!_TAG_FILE_FORMAT\t2\t/extended format/\n")
40
	_, _ = fmt.Fprintf(to, "!_TAG_FILE_SORTED\t1\t/1=sorted/\n")
41
	_, _ = fmt.Fprintf(to, "!_TAG_PROGRAM_NAME\tclerk\t//\n")
42
	_, _ = fmt.Fprintf(to, "!_TAG_PROGRAM_URL\thttps://olexsmir.xyz/clerk\t//\n") // TODO:
43
	_, _ = fmt.Fprintf(to, "!_TAG_PROGRAM_VERSION\t0.1.0\t//\n")
44
45
	// Kind descriptions
46
	var kindLetters []byte
47
	for k := range w.kinds {
48
		kindLetters = append(kindLetters, k)
49
	}
50
51
	slices.Sort(kindLetters)
52
	for _, k := range kindLetters {
53
		_, _ = fmt.Fprintf(to, "!_TAG_KIND_DESCRIPTION!%c\t%c,%s\t/%s/\n", k, k, w.kinds[k], w.kinds[k])
54
	}
55
56
	// Tags
57
	sort.Slice(w.entries, func(i, j int) bool {
58
		if w.entries[i].Name != w.entries[j].Name {
59
			return w.entries[i].Name < w.entries[j].Name
60
		}
61
		if w.entries[i].Kind != w.entries[j].Kind {
62
			return w.entries[i].Kind < w.entries[j].Kind
63
		}
64
		if w.entries[i].File != w.entries[j].File {
65
			return w.entries[i].File < w.entries[j].File
66
		}
67
		return w.entries[i].Line < w.entries[j].Line
68
	})
69
70
	for _, e := range w.entries {
71
		_, _ = fmt.Fprintf(to, "%s\t%s\t/^%s$/;\"\tkind:%c\tline:%d", e.Name, e.File, escapePattern(e.Pattern), e.Kind, e.Line)
72
		if e.Language != "" {
73
			_, _ = fmt.Fprintf(to, "\tlanguage:%s", e.Language)
74
		}
75
		_, _ = fmt.Fprintf(to, "\n")
76
	}
77
78
	return nil
79
}
80
81
func escapePattern(s string) string {
82
	s = strings.ReplaceAll(s, `\`, `\\`)
83
	s = strings.ReplaceAll(s, `/`, `\/`)
84
	return s
85
}