@@ -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.
3333func 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+
10073func 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+
171182func parseFrontmatterYAML (frontmatterYAML string ) (map [string ]any , error ) {
172183 frontmatterYAML = strings .ReplaceAll (frontmatterYAML , "\u00A0 " , " " )
173184 var frontmatter map [string ]any
0 commit comments