Skip to content

Conversation

@ingydotnet
Copy link
Member

Heavy refactoring and feature enhancement for the go-yaml CLI.

Adds a test suite for the CLI.

Note: Claude Code was used for much of this work so. Should be interesting to review.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for COMMENT tokens in the go-yaml CLI tool and exports Comment struct fields in the internal libyaml package. The changes enable the CLI to emit explicit COMMENT tokens in token output mode, provide better position formatting, and support both compact and profuse node/event modes.

Key changes:

  • Export Comment struct fields (Head, Line, Foot, ScanMark, TokenMark, StartMark, EndMark) from private to public
  • Add COMMENT_TOKEN type and implement comment token generation in the CLI parser
  • Implement compact node representation format and improve position formatting (from semicolon to slash separator)
  • Add comprehensive test infrastructure with test data files

Reviewed Changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/libyaml/yaml.go Exports Comment struct fields and adds COMMENT_TOKEN type
internal/libyaml/scanner.go Updates Comment field references to use exported names and adds EndMark to scanLineComment
internal/libyaml/parser.go Updates Comment field references to use exported names
internal/libyaml/api.go Adds GetPendingComments() and GetCommentsHead() methods for CLI access
cmd/go-yaml/parser.go Implements comment token generation with processComments method
cmd/go-yaml/token.go Adds CommentType field and updates field names to lowercase, implements COMMENT token formatting
cmd/go-yaml/node.go Adds MapSlice type for key ordering, implements FormatNodeCompact, updates formatStyle and formatTag
cmd/go-yaml/main.go Adds -N/--NODE flag support and multi-document handling
cmd/go-yaml/event.go Implements implicit/explicit document markers, adds position format improvements
cmd/go-yaml/cli_test.go New test infrastructure for CLI testing
cmd/go-yaml/testdata/*.yaml New test data files for various CLI modes
GNUmakefile Adds test-cli target and reorganizes test commands

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

Copilot AI commented Nov 3, 2025

@ingydotnet I've opened a new pull request, #169, to work on those changes. Once the pull request is ready, I'll request review from you.

@ingydotnet ingydotnet requested a review from Copilot November 3, 2025 18:20
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +368 to +373
func adjustColumn(col int) int {
if col > 0 {
return col - 1
}
return 0
}
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function lacks documentation explaining why column adjustment is necessary. Add a comment clarifying that yaml.Node uses 1-based column indexing while libyaml uses 0-based indexing, and this function converts between the two.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +471 to 479
startCol := adjustColumn(node.Column)
endLine := node.Line
endColumn := node.Column
endColumn := startCol
if node.Value != "" {
// For single-line values, add the length to the column
if !strings.Contains(node.Value, "\n") {
endColumn += len(node.Value)
} else {
// For multi-line values, we'd need more complex logic
// For now, just use the start position
endColumn = node.Column
endColumn = startCol + len(node.Value)
}
}
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The end column calculation uses byte length instead of rune count. For multi-byte UTF-8 characters, this will produce incorrect column positions. Use utf8.RuneCountInString(node.Value) instead of len(node.Value) to correctly handle Unicode characters.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var buf bytes.Buffer
enc := yaml.NewEncoder(&buf)
enc.SetIndent(2)
enc.CompactSeqIndent()
Copy link

Copilot AI Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The encoder's CompactSeqIndent() method is called without checking if it exists or handling potential errors. Verify this is a valid method on yaml.Encoder. If it's a custom extension, ensure it's properly available in the imported yaml package version.

Suggested change
enc.CompactSeqIndent()

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@ccoVeille ccoVeille left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm doubtful.

The complexity of the code in go-yaml binary makes me think it's hard to maintain it

Comment on lines +58 to +66
// Build the CLI binary if it doesn't exist
binaryPath := "../../go-yaml"
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
t.Logf("Building go-yaml binary...")
cmd := exec.Command("go", "build", "-o", binaryPath, ".")
if output, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("Failed to build go-yaml: %v\n%s", err, output)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not logic to build it in Go.

go run can be used

Also, code should be written in a way go test could be enough to emulate everything

The usual and idiomatic way in Go is to use os.ExecPipe and to play with it to pass data to Stdin and read result of Stdout

Using exec and string.Buffer is not the way to run and test go code. It would be of the binary launched was a blackbox binary

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for @SVilgelm


// appendCommentTokenIfNotEmpty creates and appends a comment token if the value is not empty.
func (p *Parser) appendCommentTokenIfNotEmpty(value []byte, commentType string, comment *libyaml.Comment) {
if len(value) > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if len(value) > 0 {
if len(value) == 0 {
return
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for @SVilgelm

Comment on lines +217 to +230
if tag := formatTag(n.Tag, n.Style, false); tag != "" && tag != "!!str" {
result = append(result, MapItem{Key: "tag", Value: tag})
}
if n.Anchor != "" {
result = append(result, MapItem{Key: "anchor", Value: n.Anchor})
}
if n.HeadComment != "" {
result = append(result, MapItem{Key: "head", Value: n.HeadComment})
}
if n.LineComment != "" {
result = append(result, MapItem{Key: "line", Value: n.LineComment})
}
if n.FootComment != "" {
result = append(result, MapItem{Key: "foot", Value: n.FootComment})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!!str, tag, head, foot, …

All of them could be constants

It would avoid possible errors such as typo like "stt", "hesd"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for @SVilgelm

go test$(if $v, -v)

test-yts: $(GO-DEPS) $(YTS-DIR)
go test$(if $v, -v) ./yts -count=$(count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
go test$(if $v, -v) ./yts -count=$(count)
go test$(if $v, -v) ./yts/... -count=$(count)


# Test rules:
test: $(GO-DEPS)
go test$(if $v, -v) -vet=off ./...
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are removing the vet=off we just added 😁 also the ./...

@RUNFAILING=1 bash -c "$$yts_pass_fail"

test-cli: $(GO-DEPS) cli
go test$(if $v, -v) ./cmd/go-yaml -count=$(count)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
go test$(if $v, -v) ./cmd/go-yaml -count=$(count)
go test$(if $v, -v) ./cmd/go-yaml/... -count=$(count)

Comment on lines -89 to -91
fmt: $(GOLANGCI-LINT-VERSIONED)
$< fmt ./...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no reason to remove make fmt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants