internal/commentpolicy/marker.go
1
package commentpolicy
3
import (
4
"bytes"
5
"strings"
7
"github.com/koment-dev/koment/internal/policy"
8
)
10
const binarySniffLimit = 8 << 10
12
type markerDetector struct{}
14
func (markerDetector) Handles(file string) bool {
15
_, commentable := syntaxFor(file)
16
return commentable
17
}
19
func (markerDetector) Name() string { return "marker scan" }
21
func (markerDetector) Scan(file string, content []byte, configured policy.Policy) ([]SourceComment, []bool, error) {
22
syntax, commentable := syntaxFor(file)
23
if !commentable || isBinary(content) {
24
return nil, nil, nil
25
}
27
spans := scanSpans(content, syntax)
28
comments := make([]SourceComment, 0, len(spans))
29
intrinsic := make([]bool, 0, len(spans))
30
for _, span := range spans {
31
raw := string(content[span.start:span.end])
32
body := markerBody(raw, syntax)
33
comments = append(comments, SourceComment{
34
File: file, Raw: raw, Body: body, Line: span.line,
35
Start: span.start, End: span.end,
36
})
37
intrinsic = append(intrinsic, isMarkerIntrinsic(raw, body, span, syntax, configured))
38
}
39
return comments, intrinsic, nil
40
}
42
func isBinary(content []byte) bool {
43
head := content
44
if len(head) > binarySniffLimit {
45
head = head[:binarySniffLimit]
46
}
47
return bytes.IndexByte(head, 0) >= 0
48
}
50
type span struct {
51
start int
52
end int
53
line int
54
ownsLine bool
55
shebang bool
56
}
58
func scanSpans(content []byte, syntax commentSyntax) []span {
59
var spans []span
60
starts := lineStarts(content)
62
for index := 0; index < len(starts); index++ {
63
lineFrom := starts[index]
64
lineTo := lineEnd(content, starts, index)
65
text := string(content[lineFrom:lineTo])
67
at, block := commentStart(text, syntax)
68
if at < 0 {
69
continue
70
}
72
found := span{
73
start: lineFrom + at,
74
line: index + 1,
75
ownsLine: strings.TrimSpace(text[:at]) == "",
76
}
77
found.shebang = index == 0 && strings.HasPrefix(text, "#!")
79
if block.open != "" {
80
end, lastLine := blockEnd(content, starts, index, found.start+len(block.open), block.close)
81
found.end = end
82
spans = append(spans, found)
83
index = lastLine
84
continue
85
}
87
found.end = lineTo
88
if found.ownsLine && !found.shebang && !isDirectiveLine(text, syntax) {
89
index = extendLineGroup(content, starts, index, syntax, &found)
90
}
91
spans = append(spans, found)
92
}
93
return spans
94
}
96
func extendLineGroup(content []byte, starts []int, index int, syntax commentSyntax, group *span) int {
97
last := index
98
for next := index + 1; next < len(starts); next++ {
99
lineFrom := starts[next]
100
lineTo := lineEnd(content, starts, next)
101
text := string(content[lineFrom:lineTo])
102
at, block := commentStart(text, syntax)
103
if at < 0 || block.open != "" || strings.TrimSpace(text[:at]) != "" || isDirectiveLine(text, syntax) {
104
break
105
}
106
group.end = lineTo
107
last = next
108
}
109
return last
110
}
112
func isDirectiveLine(text string, syntax commentSyntax) bool {
113
return matchesAnyDirective(markerBody(strings.TrimSpace(text), syntax), syntax.directives)
114
}
116
func blockEnd(content []byte, starts []int, index, from int, closing string) (int, int) {
117
relative := bytes.Index(content[from:], []byte(closing))
118
if relative < 0 {
119
return len(content), len(starts) - 1
120
}
121
end := from + relative + len(closing)
122
line := index
123
for line+1 < len(starts) && starts[line+1] <= end {
124
line++
125
}
126
return end, line
127
}
129
func commentStart(text string, syntax commentSyntax) (int, blockDelimiter) {
130
best, bestBlock := -1, blockDelimiter{}
131
consider := func(at int, block blockDelimiter) {
132
if at < 0 || (best >= 0 && at >= best) {
133
return
134
}
135
if !markerIsReal(text, at) {
136
return
137
}
138
best, bestBlock = at, block
139
}
140
for _, block := range syntax.block {
141
consider(strings.Index(text, block.open), block)
142
}
143
for _, marker := range syntax.line {
144
consider(strings.Index(text, marker), blockDelimiter{})
145
}
146
return best, bestBlock
147
}
149
func markerIsReal(text string, at int) bool {
150
before := text[:at]
151
if strings.TrimSpace(before) != "" && !strings.HasSuffix(before, " ") && !strings.HasSuffix(before, "\t") {
152
return false
153
}
154
return !insideQuotes(before)
155
}
157
func insideQuotes(before string) bool {
158
single, double, escaped := 0, 0, false
159
for _, character := range before {
160
switch {
161
case escaped:
162
escaped = false
163
case character == '\\':
164
escaped = true
165
case character == '\'' && double%2 == 0:
166
single++
167
case character == '"' && single%2 == 0:
168
double++
169
}
170
}
171
return single%2 == 1 || double%2 == 1
172
}
174
func markerBody(raw string, syntax commentSyntax) string {
175
lines := strings.Split(raw, "\n")
176
cleaned := make([]string, 0, len(lines))
177
for _, line := range lines {
178
line = strings.TrimSpace(line)
179
for _, block := range syntax.block {
180
line = strings.TrimSpace(strings.TrimPrefix(line, block.open))
181
line = strings.TrimSpace(strings.TrimSuffix(line, block.close))
182
}
183
for _, marker := range syntax.line {
184
line = strings.TrimSpace(strings.TrimPrefix(line, marker))
185
}
186
line = strings.TrimSpace(strings.TrimPrefix(line, "*"))
187
cleaned = append(cleaned, line)
188
}
189
return strings.TrimSpace(strings.Join(cleaned, "\n"))
190
}
192
func isMarkerIntrinsic(raw, body string, found span, syntax commentSyntax, configured policy.Policy) bool {
193
switch {
194
case isManagedRegionMarker(body):
195
return true
196
case found.shebang && configured.Allows(policy.IntrinsicToolchain):
197
return true
198
case configured.Allows(policy.IntrinsicDeprecated) && strings.Contains(body, "Deprecated:"):
199
return true
200
case configured.Allows(policy.IntrinsicUpstreamLink) &&
201
(strings.Contains(raw, "https://") || strings.Contains(raw, "http://")):
202
return true
203
case configured.Allows(policy.IntrinsicGeneratedMarker) && isGeneratedMarker(body):
204
return true
205
case configured.Allows(policy.IntrinsicToolchain) && hasConfiguredDirective(body, syntax):
206
return true
207
case configured.MatchesAllowedAnnotation(body):
208
return true
209
default:
210
return false
211
}
212
}
214
func isManagedRegionMarker(body string) bool {
215
return strings.HasPrefix(strings.TrimSpace(body), "koment:managed-")
216
}
218
func isGeneratedMarker(body string) bool {
219
upper := strings.ToUpper(body)
220
return strings.Contains(upper, "DO NOT EDIT") ||
221
strings.Contains(upper, "@GENERATED") ||
222
(strings.Contains(body, "Code generated") && strings.Contains(upper, "DO NOT EDIT"))
223
}
225
func hasConfiguredDirective(body string, syntax commentSyntax) bool {
226
if body == "" {
227
return false
228
}
229
for _, line := range strings.Split(body, "\n") {
230
line = strings.TrimSpace(line)
231
if line == "" {
232
continue
233
}
234
if !matchesAnyDirective(line, syntax.directives) {
235
return false
236
}
237
}
238
return true
239
}
241
func matchesAnyDirective(line string, directives []string) bool {
242
for _, prefix := range directives {
243
if strings.HasPrefix(line, prefix) {
244
return true
245
}
246
}
247
return false
248
}