snapshot of 962fc3c82c774ab371e3fc4e84c55ef26e61268d Annotations about the code that implements koment.

internal/store/anchor.go

1 package store
2
3 import (
4 "fmt"
5 "strings"
6
7 yaml "go.yaml.in/yaml/v3"
8 )
9
10 type Scope string
11
12 const (
13 ScopeFile Scope = "file"
14 ScopeExcerpt Scope = "excerpt"
15 )
16
17 func ParseScope(text string) (Scope, error) {
18 switch Scope(text) {
19 case ScopeFile:
20 return ScopeFile, nil
21 case ScopeExcerpt:
22 return ScopeExcerpt, nil
23 }
24 return "", fmt.Errorf("unknown scope %q, want one of %s, %s", text, ScopeFile, ScopeExcerpt)
25 }
26
27 // Anchor is where an annotation aims. It carries no line, because a line is
28 // something a reader observes rather than something the author decided; that
29 // lives in Status.
30 type Anchor struct {
31 Scope Scope `yaml:"scope"`
32 Excerpt string `yaml:"excerpt,omitempty"`
33 Before string `yaml:"before,omitempty"`
34 After string `yaml:"after,omitempty"`
35 }
36
37 func (a Anchor) MarshalYAML() (any, error) {
38 node := &yaml.Node{Kind: yaml.MappingNode, Tag: "!!map"}
39 appendScalar := func(key, value string, style yaml.Style) {
40 node.Content = append(node.Content,
41 &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: key},
42 &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: value, Style: style},
43 )
44 }
45 appendScalar("scope", string(a.Scope), 0)
46 if a.Excerpt != "" {
47 appendScalar("excerpt", a.Excerpt, safeStringStyle(a.Excerpt))
48 }
49 if a.Before != "" {
50 appendScalar("before", a.Before, safeStringStyle(a.Before))
51 }
52 if a.After != "" {
53 appendScalar("after", a.After, safeStringStyle(a.After))
54 }
55 return node, nil
56 }
57
58 func safeStringStyle(value string) yaml.Style {
59 if strings.Contains(value, "\t") {
60 return yaml.DoubleQuotedStyle
61 }
62 if strings.Contains(value, "\n") {
63 return yaml.LiteralStyle
64 }
65 return 0
66 }
67
68 func (a Anchor) Validate(id string) error {
69 switch a.Scope {
70 case ScopeFile:
71 if a.Excerpt != "" || a.Before != "" || a.After != "" {
72 return fmt.Errorf("annotation %s: file anchor must not carry excerpt context", id)
73 }
74 return nil
75 case ScopeExcerpt:
76 if a.Excerpt == "" {
77 return fmt.Errorf("annotation %s: excerpt anchor requires a non-empty excerpt", id)
78 }
79 if err := validateContext("before", a.Before); err != nil {
80 return fmt.Errorf("annotation %s: %w", id, err)
81 }
82 if err := validateContext("after", a.After); err != nil {
83 return fmt.Errorf("annotation %s: %w", id, err)
84 }
85 return nil
86 default:
87 _, err := ParseScope(string(a.Scope))
88 return fmt.Errorf("annotation %s: %w", id, err)
89 }
90 }
91
92 func validateContext(name, context string) error {
93 if context == "" {
94 return nil
95 }
96 if strings.Count(strings.TrimSuffix(context, "\n"), "\n") >= 3 {
97 return fmt.Errorf("anchor.%s contains more than three lines", name)
98 }
99 return nil
100 }

Find an annotation

Search file paths, rationale, kinds, and authors.

moveEnter openEsc close