UpdateFieldInFrontmatter updates a field in the frontmatter while preserving the original formatting when possible. It tries to preserve whitespace, comments, and formatting by working with the raw frontmatter lines, similar to how addSourceToWorkflow works.
(content, fieldName, fieldValue string)
| 17 | // when possible. It tries to preserve whitespace, comments, and formatting by working with the raw |
| 18 | // frontmatter lines, similar to how addSourceToWorkflow works. |
| 19 | func UpdateFieldInFrontmatter(content, fieldName, fieldValue string) (string, error) { |
| 20 | frontmatterEditorLog.Printf("Updating frontmatter field: %s = %s", fieldName, fieldValue) |
| 21 | |
| 22 | // Parse frontmatter using parser package |
| 23 | result, err := parser.ExtractFrontmatterFromContent(content) |
| 24 | if err != nil { |
| 25 | frontmatterEditorLog.Printf("Failed to parse frontmatter: %v", err) |
| 26 | return "", fmt.Errorf("failed to parse frontmatter: %w", err) |
| 27 | } |
| 28 | |
| 29 | // Try to preserve original frontmatter formatting by manually updating the field |
| 30 | if len(result.FrontmatterLines) > 0 { |
| 31 | frontmatterEditorLog.Printf("Using raw frontmatter lines for field update (%d lines)", len(result.FrontmatterLines)) |
| 32 | // Look for existing field in the raw lines |
| 33 | fieldUpdated := false |
| 34 | skipChildren := false |
| 35 | fieldIndentLevel := 0 |
| 36 | newFrontmatterLines := make([]string, 0, len(result.FrontmatterLines)) |
| 37 | |
| 38 | for _, line := range result.FrontmatterLines { |
| 39 | trimmedLine := strings.TrimSpace(line) |
| 40 | |
| 41 | // If we just updated the field, skip its child lines (block mapping values) |
| 42 | if skipChildren { |
| 43 | currentIndent := len(line) - len(strings.TrimLeft(line, " \t")) |
| 44 | if currentIndent > fieldIndentLevel { |
| 45 | // This line is a child of the replaced field — drop it |
| 46 | continue |
| 47 | } |
| 48 | // No longer in the child block |
| 49 | skipChildren = false |
| 50 | } |
| 51 | |
| 52 | // Check if this line contains our field |
| 53 | if !fieldUpdated && strings.HasPrefix(trimmedLine, fieldName+":") { |
| 54 | // Preserve the original indentation and comments |
| 55 | leadingSpace := line[:len(line)-len(strings.TrimLeft(line, " \t"))] |
| 56 | |
| 57 | // Check if there's a comment on the same line |
| 58 | commentIndex := strings.Index(line, "#") |
| 59 | var comment string |
| 60 | if commentIndex > strings.Index(line, ":") && commentIndex != -1 { |
| 61 | comment = line[commentIndex:] |
| 62 | } |
| 63 | |
| 64 | // Update the field value while preserving formatting |
| 65 | var updatedLine string |
| 66 | if comment != "" { |
| 67 | updatedLine = fmt.Sprintf("%s%s: %s %s", leadingSpace, fieldName, fieldValue, comment) |
| 68 | } else { |
| 69 | updatedLine = fmt.Sprintf("%s%s: %s", leadingSpace, fieldName, fieldValue) |
| 70 | } |
| 71 | newFrontmatterLines = append(newFrontmatterLines, updatedLine) |
| 72 | fieldUpdated = true |
| 73 | // Track the indent level so we can skip any child lines that follow |
| 74 | fieldIndentLevel = len(leadingSpace) |
| 75 | skipChildren = true |
| 76 | frontmatterEditorLog.Printf("Updated existing field %s", fieldName) |