-
Notifications
You must be signed in to change notification settings - Fork 51
Expand file tree
/
Copy pathreference.go
More file actions
160 lines (133 loc) · 3.76 KB
/
Copy pathreference.go
File metadata and controls
160 lines (133 loc) · 3.76 KB
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
154
155
156
157
158
159
160
package booklit
import (
"fmt"
"github.com/sirupsen/logrus"
"github.com/vito/booklit/ast"
)
// AllowBrokenReferences can be set to true to allow broken references to tags.
//
// This is only meant as a development aid.
var AllowBrokenReferences bool
// Reference is flow content linking to a tag defined elsewhere.
type Reference struct {
// The section to resolve the reference within.
Section *Section
// The tag to link to.
TagName string
// Optional content to display for the reference. If not present, the tag's
// own display content will be used.
Content Content
// If set to true and resolving the tag does not succeed, display Content
// instead of displaying a link.
Optional bool
// The original source location of the reference. Used when generating error
// messages.
Location ast.Location
// The result of Resolving the reference.
tag *Tag
}
// IsFlow returns true.
func (con *Reference) IsFlow() bool {
return true
}
// String summarizes the content for debugging purposes.
func (con *Reference) String() string {
if con.Content != nil {
return con.Content.String()
}
tag, err := con.Tag()
if err != nil {
return fmt.Sprintf("{broken reference: %s: %s}", con.TagName, err)
}
return tag.Title.String()
}
// Visit calls VisitReference.
func (con *Reference) Visit(visitor Visitor) error {
return visitor.VisitReference(con)
}
// Display returns the content to display for the reference. If Content is set,
// it is returned, otherwise the resolved tag's Title is returned.
func (con *Reference) Display() (Content, error) {
if con.Content != nil {
return con.Content, nil
}
tag, err := con.Tag()
if err != nil {
return nil, err
}
return tag.Title, nil
}
// Tag finds the referenced tag through Section.FindTag.
//
// If 1 tag is found, it is assigned on the Reference.
//
// If 0 tags are found and AllowBrokenReferences is false,
// booklit.UnknownTagError is returned.
//
// If more than one tag is returned and AllowBrokenReferences is false,
// booklit.AmbiguousReferenceError is returned.
//
// If AllowBrokenReferences is true, a made-up tag is assigned on the Reference
// instead of returning either of the above errors.
func (con *Reference) Tag() (*Tag, error) {
if con.tag != nil {
return con.tag, nil
}
err := con.resolve()
if err != nil {
if AllowBrokenReferences {
logrus.WithFields(logrus.Fields{"section": con.Section.Path}).
Warnf("broken reference: %s", err)
return &Tag{
Name: con.TagName,
Anchor: "broken",
Title: String(fmt.Sprintf("{broken reference: %s}", con.TagName)),
Section: con.Section,
Location: con.Location,
}, nil
}
return nil, err
}
return con.tag, nil
}
// resolve resolves the reference to a tag, returning an error if the tag is
// unknown or ambiguous.
func (con *Reference) resolve() error {
tags := con.Section.FindTag(con.TagName)
switch len(tags) {
case 1: // unnambiguous reference
con.tag = &tags[0]
return nil
case 0: // broken reference
if con.Optional {
// allow nonexistant tag; template must handle nil Tag
return nil
}
return UnknownTagError{
TagName: con.TagName,
SimilarTags: con.Section.SimilarTags(con.TagName),
ErrorLocation: ErrorLocation{
FilePath: con.Section.FilePath(),
NodeLocation: con.Location,
Length: len("\\reference"),
},
}
default: // ambiguous reference
locs := []ErrorLocation{}
for _, t := range tags {
locs = append(locs, ErrorLocation{
FilePath: t.Section.FilePath(),
NodeLocation: t.Location,
})
}
return AmbiguousReferenceError{
TagName: con.TagName,
DefinedLocations: locs,
ErrorLocation: ErrorLocation{
FilePath: con.Section.FilePath(),
NodeLocation: con.Location,
Length: len("\\reference"),
},
}
}
}