mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-04 11:25:37 +00:00
wip - comments
This commit is contained in:
parent
491ab8e70f
commit
5c166e3038
@ -1,9 +1,8 @@
|
|||||||
service "cat" {
|
# Arithmetic with literals and application-provided variables
|
||||||
process "main" {
|
sum = 1 + addend
|
||||||
command = ["/usr/local/bin/awesome-app", "server"]
|
|
||||||
}
|
|
||||||
|
|
||||||
process "mgmt" {
|
# String interpolation and templates
|
||||||
command = ["/usr/local/bin/awesome-app", "mgmt"]
|
message = "Hello, ${name}!"
|
||||||
}
|
|
||||||
}
|
# Application-provided functions
|
||||||
|
shouty_message = upper(message)
|
||||||
@ -64,8 +64,9 @@ func extractLineComment(src []byte, endPos int) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractLeadingComments extracts comments from the very beginning of the file
|
// extractLeadingComments extracts comments from the very beginning of the file.
|
||||||
func extractLeadingComments(src []byte) string {
|
// It returns the comment text and the byte position of the last character in that leading block.
|
||||||
|
func extractLeadingComments(src []byte) (string, int) {
|
||||||
var comments []string
|
var comments []string
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
@ -74,6 +75,8 @@ func extractLeadingComments(src []byte) string {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lastPos := -1
|
||||||
|
|
||||||
// Extract comment lines from the start
|
// Extract comment lines from the start
|
||||||
for i < len(src) && src[i] == '#' {
|
for i < len(src) && src[i] == '#' {
|
||||||
lineStart := i
|
lineStart := i
|
||||||
@ -81,6 +84,7 @@ func extractLeadingComments(src []byte) string {
|
|||||||
for i < len(src) && src[i] != '\n' {
|
for i < len(src) && src[i] != '\n' {
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
lastPos = i - 1
|
||||||
comments = append(comments, strings.TrimSpace(string(src[lineStart:i])))
|
comments = append(comments, strings.TrimSpace(string(src[lineStart:i])))
|
||||||
// Skip newline
|
// Skip newline
|
||||||
if i < len(src) && src[i] == '\n' {
|
if i < len(src) && src[i] == '\n' {
|
||||||
@ -93,51 +97,46 @@ func extractLeadingComments(src []byte) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(comments) > 0 {
|
if len(comments) > 0 {
|
||||||
return strings.Join(comments, "\n")
|
return strings.Join(comments, "\n"), lastPos
|
||||||
}
|
}
|
||||||
return ""
|
return "", -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractHeadComment extracts comments before a given start position
|
// extractHeadComment extracts comments before a given start position
|
||||||
func extractHeadComment(src []byte, startPos int) string {
|
func extractHeadComment(src []byte, startPos int) string {
|
||||||
var comments []string
|
var comments []string
|
||||||
|
|
||||||
// Look backwards from startPos for comment lines
|
// Start just before the token and skip trailing whitespace
|
||||||
i := startPos - 1
|
i := startPos - 1
|
||||||
|
|
||||||
// Skip whitespace backwards to find comment
|
|
||||||
for i >= 0 && (src[i] == ' ' || src[i] == '\t' || src[i] == '\n' || src[i] == '\r') {
|
for i >= 0 && (src[i] == ' ' || src[i] == '\t' || src[i] == '\n' || src[i] == '\r') {
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we found a #, extract the comment
|
for i >= 0 {
|
||||||
if i >= 0 && src[i] == '#' {
|
// Find line boundaries
|
||||||
// Find the start of this line
|
lineEnd := i
|
||||||
lineStart := i
|
for i >= 0 && src[i] != '\n' {
|
||||||
for lineStart > 0 && src[lineStart-1] != '\n' {
|
|
||||||
lineStart--
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract from line start to comment end
|
|
||||||
comments = append(comments, strings.TrimSpace(string(src[lineStart:i+1])))
|
|
||||||
|
|
||||||
// Look for more comment lines above this one
|
|
||||||
i = lineStart - 1
|
|
||||||
for i >= 0 && (src[i] == '\n' || src[i] == '\r') {
|
|
||||||
i--
|
i--
|
||||||
}
|
}
|
||||||
|
lineStart := i + 1
|
||||||
|
|
||||||
for i >= 0 && src[i] == '#' {
|
line := strings.TrimRight(string(src[lineStart:lineEnd+1]), " \t\r")
|
||||||
lineStart = i
|
trimmed := strings.TrimSpace(line)
|
||||||
for lineStart > 0 && src[lineStart-1] != '\n' {
|
|
||||||
lineStart--
|
|
||||||
}
|
|
||||||
comments = append([]string{strings.TrimSpace(string(src[lineStart : i+1]))}, comments...)
|
|
||||||
|
|
||||||
i = lineStart - 1
|
if trimmed == "" {
|
||||||
for i >= 0 && (src[i] == '\n' || src[i] == '\r') {
|
break
|
||||||
i--
|
}
|
||||||
}
|
|
||||||
|
if !strings.HasPrefix(trimmed, "#") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
comments = append([]string{trimmed}, comments...)
|
||||||
|
|
||||||
|
// Move to previous line (skip any whitespace/newlines)
|
||||||
|
i = lineStart - 1
|
||||||
|
for i >= 0 && (src[i] == ' ' || src[i] == '\t' || src[i] == '\n' || src[i] == '\r') {
|
||||||
|
i--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +175,9 @@ func (dec *hclDecoder) Decode() (*CandidateNode, error) {
|
|||||||
root := &CandidateNode{Kind: MappingNode}
|
root := &CandidateNode{Kind: MappingNode}
|
||||||
|
|
||||||
// Extract file-level head comments (comments at the very beginning of the file)
|
// Extract file-level head comments (comments at the very beginning of the file)
|
||||||
if leadingComment := extractLeadingComments(dec.fileBytes); leadingComment != "" {
|
leadingComment, _ := extractLeadingComments(dec.fileBytes)
|
||||||
|
leadingUsed := false
|
||||||
|
if leadingComment != "" {
|
||||||
root.HeadComment = leadingComment
|
root.HeadComment = leadingComment
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +189,18 @@ func (dec *hclDecoder) Decode() (*CandidateNode, error) {
|
|||||||
|
|
||||||
// Attach comments if any
|
// Attach comments if any
|
||||||
attrRange := attrWithName.Attr.Range()
|
attrRange := attrWithName.Attr.Range()
|
||||||
if headComment := extractHeadComment(dec.fileBytes, attrRange.Start.Byte); headComment != "" {
|
headComment := extractHeadComment(dec.fileBytes, attrRange.Start.Byte)
|
||||||
|
if !leadingUsed && leadingComment != "" {
|
||||||
|
// Avoid double-applying the leading file comment to the first attribute
|
||||||
|
switch headComment {
|
||||||
|
case leadingComment:
|
||||||
|
headComment = ""
|
||||||
|
case "":
|
||||||
|
headComment = leadingComment
|
||||||
|
}
|
||||||
|
leadingUsed = true
|
||||||
|
}
|
||||||
|
if headComment != "" {
|
||||||
valNode.HeadComment = headComment
|
valNode.HeadComment = headComment
|
||||||
}
|
}
|
||||||
if lineComment := extractLineComment(dec.fileBytes, attrRange.End.Byte); lineComment != "" {
|
if lineComment := extractLineComment(dec.fileBytes, attrRange.End.Byte); lineComment != "" {
|
||||||
|
|||||||
@ -114,22 +114,37 @@ func (he *hclEncoder) injectComments(output []byte, commentMap map[string]string
|
|||||||
// Convert output to string for easier manipulation
|
// Convert output to string for easier manipulation
|
||||||
result := string(output)
|
result := string(output)
|
||||||
|
|
||||||
// Look for head comments at the root level
|
// Root-level head comment (stored as ".head")
|
||||||
// These are typically comments before the first attribute
|
for path, comment := range commentMap {
|
||||||
|
if path == ".head" {
|
||||||
|
trimmed := strings.TrimSpace(comment)
|
||||||
|
if trimmed != "" && !strings.HasPrefix(result, trimmed) {
|
||||||
|
result = trimmed + "\n" + result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute head comments: insert above matching assignment
|
||||||
for path, comment := range commentMap {
|
for path, comment := range commentMap {
|
||||||
parts := strings.Split(path, ".")
|
parts := strings.Split(path, ".")
|
||||||
if len(parts) < 2 {
|
if len(parts) != 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
commentType := parts[len(parts)-1] // "head", "line", or "foot"
|
key := parts[0]
|
||||||
|
commentType := parts[1]
|
||||||
|
if commentType != "head" || key == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if commentType == "head" && len(parts) == 2 {
|
trimmed := strings.TrimSpace(comment)
|
||||||
// Root-level head comment - inject at the beginning
|
if trimmed == "" {
|
||||||
// Check if comment not already there
|
continue
|
||||||
if !strings.HasPrefix(result, strings.TrimSpace(comment)) {
|
}
|
||||||
result = comment + "\n" + result
|
|
||||||
}
|
re := regexp.MustCompile(`(?m)^(\s*)` + regexp.QuoteMeta(key) + `\s*=`)
|
||||||
|
if re.MatchString(result) {
|
||||||
|
result = re.ReplaceAllString(result, "$1"+trimmed+"\n$0")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,23 @@ var multipleBlockLabelKeysExpectedYaml = `service:
|
|||||||
- "management"
|
- "management"
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var roundtripSample = `# Arithmetic with literals and application-provided variables
|
||||||
|
sum = 1 + addend
|
||||||
|
|
||||||
|
# String interpolation and templates
|
||||||
|
message = "Hello, ${name}!"
|
||||||
|
|
||||||
|
# Application-provided functions
|
||||||
|
shouty_message = upper(message)`
|
||||||
|
|
||||||
|
var roundtripSampleExpected = `# Arithmetic with literals and application-provided variables
|
||||||
|
sum = 1 + addend
|
||||||
|
# String interpolation and templates
|
||||||
|
message = "Hello, ${name}!"
|
||||||
|
# Application-provided functions
|
||||||
|
shouty_message = upper(message)
|
||||||
|
`
|
||||||
|
|
||||||
var hclFormatScenarios = []formatScenario{
|
var hclFormatScenarios = []formatScenario{
|
||||||
{
|
{
|
||||||
description: "Simple decode",
|
description: "Simple decode",
|
||||||
@ -250,6 +267,12 @@ var hclFormatScenarios = []formatScenario{
|
|||||||
expected: "# Configuration\nport = 8080\n",
|
expected: "# Configuration\nport = 8080\n",
|
||||||
scenarioType: "roundtrip",
|
scenarioType: "roundtrip",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: "roundtrip example",
|
||||||
|
input: roundtripSample,
|
||||||
|
expected: roundtripSampleExpected,
|
||||||
|
scenarioType: "roundtrip",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func testHclScenario(t *testing.T, s formatScenario) {
|
func testHclScenario(t *testing.T, s formatScenario) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user