Skip to content

Commit b20b030

Browse files
authored
Reduce duplicate frontmatter scanning in ParseWorkflow (#34819)
1 parent 17c225a commit b20b030

2 files changed

Lines changed: 52 additions & 32 deletions

File tree

pkg/parser/frontmatter_content.go

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -31,35 +31,7 @@ type FrontmatterResult struct {
3131
// opening "---" delimiter). The returned line numbers are absolute: they can be used
3232
// directly as file:line positions for IDE-navigable error messages.
3333
func extractTopLevelFieldLines(yamlContent string, frontmatterStart int) map[string]int {
34-
fieldLines := make(map[string]int)
35-
relLine := 0
36-
for line := range strings.SplitSeq(yamlContent, "\n") {
37-
relLine++
38-
// Skip empty lines and YAML comments
39-
trimmed := strings.TrimSpace(line)
40-
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
41-
continue
42-
}
43-
// Top-level keys have no leading indentation
44-
if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
45-
colonIdx := strings.Index(trimmed, ":")
46-
if colonIdx > 0 {
47-
key := strings.TrimSpace(trimmed[:colonIdx])
48-
// Accept simple unquoted keys only. Bracket characters in the key position
49-
// ({, }, [, ]) indicate inline YAML maps/sequences rather than plain string keys
50-
// (e.g. `[anchor]: value` or `{implicit_key}: value`). These forms are not used
51-
// in workflow frontmatter, so we skip them to avoid false positives.
52-
// Quoted YAML keys such as `"key[0]"` are also not used in workflow frontmatter
53-
// and are excluded by this check (the extracted substring will contain the quote).
54-
if key != "" && !strings.ContainsAny(key, " \t{}[]\"'") {
55-
if _, alreadySeen := fieldLines[key]; !alreadySeen {
56-
// absoluteLine = relLine + frontmatterStart - 1
57-
fieldLines[key] = relLine + frontmatterStart - 1
58-
}
59-
}
60-
}
61-
}
62-
}
34+
_, fieldLines := extractFrontmatterMetadata(yamlContent, frontmatterStart)
6335
return fieldLines
6436
}
6537

@@ -79,24 +51,25 @@ func ExtractFrontmatterFromContent(content string) (*FrontmatterResult, error) {
7951
}
8052

8153
frontmatterYAML := content[searchStart:frontmatterEndStart]
82-
frontmatterLines := splitFrontmatterLines(frontmatterYAML)
54+
frontmatterLines, fieldLines := extractFrontmatterMetadata(frontmatterYAML, frontmatterStartLine)
8355
frontmatter, err := parseFrontmatterYAML(frontmatterYAML)
8456
if err != nil {
8557
return nil, err
8658
}
8759
markdown := extractMarkdownAfterFrontmatter(content, markdownStart)
8860

8961
parserLog.Printf("Successfully extracted frontmatter: fields=%d, markdown_size=%d bytes", len(frontmatter), len(markdown))
90-
const frontmatterStartLine = 2 // Line 2 is where frontmatter content starts (after opening ---)
9162
return &FrontmatterResult{
9263
Frontmatter: frontmatter,
9364
Markdown: strings.TrimSpace(markdown),
9465
FrontmatterLines: frontmatterLines,
9566
FrontmatterStart: frontmatterStartLine,
96-
FieldLines: extractTopLevelFieldLines(frontmatterYAML, frontmatterStartLine),
67+
FieldLines: fieldLines,
9768
}, nil
9869
}
9970

71+
const frontmatterStartLine = 2 // Line 2 is where frontmatter content starts (after opening ---)
72+
10073
func splitFirstLine(content string) (int, string) {
10174
firstNewline := strings.IndexByte(content, '\n')
10275
if firstNewline < 0 {
@@ -168,6 +141,44 @@ func splitFrontmatterLines(frontmatterYAML string) []string {
168141
return lines
169142
}
170143

144+
func extractFrontmatterMetadata(frontmatterYAML string, frontmatterStart int) ([]string, map[string]int) {
145+
if frontmatterYAML == "" {
146+
return []string{}, map[string]int{}
147+
}
148+
149+
lines := make([]string, 0, strings.Count(frontmatterYAML, "\n")+1)
150+
fieldLines := make(map[string]int)
151+
relLine := 0
152+
153+
for line := range strings.SplitSeq(frontmatterYAML, "\n") {
154+
relLine++
155+
lines = append(lines, line)
156+
157+
trimmed := strings.TrimSpace(line)
158+
if trimmed == "" || strings.HasPrefix(trimmed, "#") {
159+
continue
160+
}
161+
162+
if len(line) > 0 && line[0] != ' ' && line[0] != '\t' {
163+
colonIdx := strings.IndexByte(trimmed, ':')
164+
if colonIdx > 0 {
165+
key := strings.TrimSpace(trimmed[:colonIdx])
166+
if key != "" && !strings.ContainsAny(key, " \t{}[]\"'") {
167+
if _, alreadySeen := fieldLines[key]; !alreadySeen {
168+
fieldLines[key] = relLine + frontmatterStart - 1
169+
}
170+
}
171+
}
172+
}
173+
}
174+
175+
if strings.HasSuffix(frontmatterYAML, "\n") {
176+
lines = lines[:len(lines)-1]
177+
}
178+
179+
return lines, fieldLines
180+
}
181+
171182
func parseFrontmatterYAML(frontmatterYAML string) (map[string]any, error) {
172183
frontmatterYAML = strings.ReplaceAll(frontmatterYAML, "\u00A0", " ")
173184
var frontmatter map[string]any

pkg/parser/frontmatter_field_lines_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,13 @@ func TestFrontmatterResultFieldLines(t *testing.T) {
103103
// FieldLines is nil when there is no frontmatter
104104
assert.Nil(t, result.FieldLines, "FieldLines should be nil when there is no frontmatter")
105105
})
106+
107+
t.Run("preserves blank frontmatter lines while tracking top level fields", func(t *testing.T) {
108+
content := "---\nengine: copilot\n\n# comment\non: push\n---\n# Workflow\n"
109+
result, err := ExtractFrontmatterFromContent(content)
110+
require.NoError(t, err, "Should parse frontmatter without error")
111+
112+
assert.Equal(t, []string{"engine: copilot", "", "# comment", "on: push"}, result.FrontmatterLines)
113+
assert.Equal(t, map[string]int{"engine": 2, "on": 5}, result.FieldLines)
114+
})
106115
}

0 commit comments

Comments
 (0)