Skip to content

Commit

Permalink
Add inline comment support
Browse files Browse the repository at this point in the history
  • Loading branch information
adammw committed Aug 1, 2024
1 parent 7d8e0f3 commit ca16b99
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 19 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ Returns an array of leading comments. Each comment must start with `#`.

Extends [Psych::Nodes::Node](https://docs.ruby-lang.org/en/3.1/Psych/Nodes/Node.html).

### `Psych::Nodes::Node#inline_comment` -> `String | nil`

Returns the inline comment. Each comment must start with `#`.

Extends [Psych::Nodes::Node](https://docs.ruby-lang.org/en/3.1/Psych/Nodes/Node.html).

### `Psych::Nodes::Node#trailing_comments` -> `Array<String>`

Returns an array of leading comments. Each comment must start with `#`.
Expand Down
12 changes: 10 additions & 2 deletions lib/psych/comments/emitter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def emit(node)
else
print stringify_adjust_scalar(node, INDENT * @indent)
end
if node.inline_comment
space!
emit_comment(node.inline_comment)
end
when Psych::Nodes::Mapping
set_flow(flow?(node)) do
if @flow
Expand Down Expand Up @@ -161,6 +165,7 @@ def emit(node)
emit(value)
end
end
emit_comment(node.inline_comment, newline: false) if node.inline_comment
newline!
end
end
Expand Down Expand Up @@ -192,6 +197,7 @@ def emit(node)
emit(subnode)
end
end
emit_comment(node.inline_comment, newline: false) if node.inline_comment
newline!
end
end
Expand Down Expand Up @@ -236,12 +242,12 @@ def emit_lookahead_comments(node)
@comment_lookahead.push(node)
end

def emit_comment(comment)
def emit_comment(comment, newline: true)
unless /\A#[^\r\n]*\z/.match?(comment)
raise ArgumentError, "Invalid comment: #{comment.inspect}"
end
print comment
newline!
newline! if newline
end

def indented(&block)
Expand All @@ -267,6 +273,8 @@ def single_line?(node)
end

def flow?(node)
return false if node.inline_comment # inline comments cannot be in flow

case node
when Psych::Nodes::Scalar, Psych::Nodes::Alias
true
Expand Down
2 changes: 2 additions & 0 deletions lib/psych/comments/node_ext.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

class Psych::Nodes::Node
attr_accessor :inline_comment

def leading_comments
@leading_comments ||= []
end
Expand Down
11 changes: 10 additions & 1 deletion lib/psych/comments/parsing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def sublines(sl, sc, el, ec)
end
end

def line_remaining(l, sc)
sublines(l, sc, l, nil)
end

def char_at(l, c)
(@lines[l] || "")[c]
end
Expand Down Expand Up @@ -46,7 +50,10 @@ def visit(node)
case node
when Psych::Nodes::Scalar, Psych::Nodes::Alias
node.leading_comments.push(*read_comments(node.start_line, node.start_column))
@last = [node.end_line, node.end_column]
has_delim = char_at(node.end_line, node.end_column) == ":"
inline_comment = line_remaining(node.end_line, node.end_column + (has_delim ? 1 : 0)).match(/^\s*(#.*)?$/)
node.inline_comment = inline_comment[1] if inline_comment && inline_comment[1]
@last = [node.end_line, node.end_column + (inline_comment&.values_at(0)&.first&.length || 0)]
when Psych::Nodes::Sequence, Psych::Nodes::Mapping
has_delim = /[\[{]/.match?(char_at(node.start_line, node.start_column))
has_bullet = node.is_a?(Psych::Nodes::Sequence) && !has_delim
Expand All @@ -58,6 +65,8 @@ def visit(node)
end
if has_delim
target = node.children[-1] || node
inline_comment = line_remaining(node.end_line, node.end_column).match(/^\s*(#.*)?$/)
node.inline_comment = inline_comment[1] if inline_comment && inline_comment[1]
target.trailing_comments.push(*read_comments(node.end_line, node.end_column))
end
when Psych::Nodes::Document
Expand Down
3 changes: 1 addition & 2 deletions spec/emitter/example2-out.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# foo
- foo
# foo2
- foo # foo2
# foo
- foo
- foo: bar
Expand Down
3 changes: 1 addition & 2 deletions spec/emitter/example5-out.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ foo:
bar: baz
bar2:
# test
foo: foo
#testtest
foo: foo #testtest
# test
bar: bar
bar3:
Expand Down
89 changes: 77 additions & 12 deletions spec/parsing_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,22 @@
end

it "attaches comments to a scalar" do
ast = Psych::Comments.parse("# foo\nbar" + <<~YAML)
ast = Psych::Comments.parse(<<~YAML)
# foo
bar
YAML
expect(ast.root.leading_comments).to eq(["# foo"])
expect(ast.root.inline_comment).to eq(nil)
expect(ast.root.trailing_comments).to eq([])
end

it "attaches inline comments to a scalar" do
ast = Psych::Comments.parse(<<~YAML)
bar # foo
YAML
expect(ast.root.leading_comments).to eq([])
expect(ast.root.inline_comment).to eq("# foo")
expect(ast.root.trailing_comments).to eq([])
end

it "attaches multiple comments to a scalar" do
Expand All @@ -37,7 +48,7 @@
expect(ast.root.leading_comments).to eq(["# foo", "# bar"])
end

it "attaches comments to a mapping key" do
it "attaches leading comments to a mapping key" do
ast = Psych::Comments.parse(<<~YAML)
# foo
bar: baz
Expand All @@ -46,7 +57,26 @@
expect(ast.root.children[0].leading_comments).to eq(["# foo"])
end

it "attaches comments to a mapping value" do
it "attaches inline comments to a mapping key" do
ast = Psych::Comments.parse(<<~YAML)
bar: # foo
baz
YAML
expect(ast.root).to be_a(Psych::Nodes::Mapping)
expect(ast.root.children[0].inline_comment).to eq("# foo")
expect(ast.root.children[1].inline_comment).to eq(nil)
end

it "attaches inline comments to a mapping value" do
ast = Psych::Comments.parse(<<~YAML)
bar: baz # foo
YAML
expect(ast.root).to be_a(Psych::Nodes::Mapping)
expect(ast.root.children[0].inline_comment).to eq(nil)
expect(ast.root.children[1].inline_comment).to eq("# foo")
end

it "attaches leading comments to a mapping value" do
ast = Psych::Comments.parse(<<~YAML)
bar:
# foo
Expand All @@ -64,38 +94,73 @@
bar2
# baz-a
- # baz-b
foo3: bar3
foo3: bar3 # baz-c
YAML
expect(ast.root).to be_a(Psych::Nodes::Sequence)
expect(ast.root.children[0].leading_comments).to eq(["# foo"])
expect(ast.root.children[1].leading_comments).to eq(["# bar"])
expect(ast.root.children[2].leading_comments).to eq(["# baz-a"])
expect(ast.root.children[2].children[1].inline_comment).to eq("# baz-c")
end

it "attaches inline comments to empty flow mapping/sequence" do
ast = Psych::Comments.parse(<<~YAML)
foo: {} # foo
bar: [] # bar
YAML

expect(ast.root).to be_a(Psych::Nodes::Mapping)
expect(ast.root.children[1].inline_comment).to eq("# foo")
expect(ast.root.children[3].inline_comment).to eq("# bar")
end

it "attaches inline comments only to flow mapping/sequence" do
ast = Psych::Comments.parse(<<~YAML)
foo: { key: "name", values: ["a", "b", "c"] } # foo
bar: [{ key: "name", values: ["a", "b", "c"]}] # bar
baz:
- { key: "name", values: ["a", "b", "c"] } # baz
YAML
expected_comments = {
ast.root.children[1] => "# foo",
ast.root.children[3] => "# bar",
ast.root.children[5].children[0] => "# baz"
}
visit = lambda do |node|
expect(node.inline_comment).to eq(expected_comments[node])
node.children&.each { |child| visit.call(child) }
end
visit.call(ast.root)
end

it "attaches comments to flow mapping" do
ast = Psych::Comments.parse(<<~YAML)
# foo
# leading
{
foo: bar
foo: bar # foo
# bar
}
} # inline
YAML
expect(ast.root).to be_a(Psych::Nodes::Mapping)
expect(ast.root.leading_comments).to eq(["# foo"])
expect(ast.root.leading_comments).to eq(["# leading"])
expect(ast.root.inline_comment).to eq("# inline")
expect(ast.root.children[1].inline_comment).to eq("# foo")
expect(ast.root.children[1].trailing_comments).to eq(["# bar"])
end

it "attaches comments to flow sequence" do
ast = Psych::Comments.parse(<<~YAML)
# foo
[
foo
# bar
]
foo # bar
# baz
] # bazza
YAML
expect(ast.root).to be_a(Psych::Nodes::Sequence)
expect(ast.root.leading_comments).to eq(["# foo"])
expect(ast.root.children[0].trailing_comments).to eq(["# bar"])
expect(ast.root.inline_comment).to eq("# bazza")
expect(ast.root.children[0].inline_comment).to eq("# bar")
expect(ast.root.children[0].trailing_comments).to eq(["# baz"])
end

it "attaches comments to document" do
Expand Down

0 comments on commit ca16b99

Please sign in to comment.