Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions context/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ blocks and front matter. `Node#code_language` returns the first token of that
info string, while `Node#fence_info` remains available for compatibility on
those block nodes.

For a fenced code block, `Node#fence` returns a `Node::Fence` structure with
the fence character, length, and indentation:

``` ruby
block = Markly.parse(" ~~~~ ruby\n Object.new\n ~~~~").first_child

block.fence
# => #<struct Markly::Node::Fence character="~", length=4, indent=2>
```

Indented code blocks and other node types return `nil`.

## Extensions

Both `render_html` and `parse` take an optional `extensions:` argument defining the extensions you want enabled as your CommonMark document is being processed:
Expand Down
27 changes: 27 additions & 0 deletions ext/markly/markly.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
static VALUE rb_Markly;
static VALUE rb_Markly_Error;
static VALUE rb_Markly_Node;
static VALUE rb_Markly_Node_Fence;
static VALUE rb_Markly_Parser;

static VALUE sym_document;
Expand Down Expand Up @@ -1080,6 +1081,30 @@ static VALUE rb_node_set_code_info(VALUE self, VALUE info) {
return Qnil;
}

/*
* Public: Gets fencing details for the current node.
*
* Returns a {Markly::Node::Fence} for fenced code blocks, `nil` otherwise.
*/
static VALUE rb_node_get_fence(VALUE self) {
int fence_length = 0;
int fence_offset = 0;
char fence_character = '\0';
cmark_node *node;
TypedData_Get_Struct(self, cmark_node, &rb_Markly_Node_Type, node);

if (!cmark_node_get_fenced(node, &fence_length, &fence_offset, &fence_character)) {
return Qnil;
}

return rb_struct_new(
rb_Markly_Node_Fence,
rb_str_new(&fence_character, 1),
INT2NUM(fence_length),
INT2NUM(fence_offset)
);
}

static VALUE rb_node_get_tasklist_item_checked(VALUE self) {
int tasklist_state;
cmark_node *node;
Expand Down Expand Up @@ -1260,6 +1285,7 @@ __attribute__((visibility("default"))) void Init_markly(void) {
rb_Markly_Node = rb_define_class_under(rb_Markly, "Node", rb_cObject);
rb_undef_alloc_func(rb_Markly_Node);
rb_define_singleton_method(rb_Markly_Node, "new", rb_node_new, 1);
rb_Markly_Node_Fence = rb_struct_define_under(rb_Markly_Node, "Fence", "character", "length", "indent", NULL);

rb_define_method(rb_Markly_Node, "replace", rb_node_replace, 1);

Expand Down Expand Up @@ -1297,6 +1323,7 @@ __attribute__((visibility("default"))) void Init_markly(void) {
rb_define_method(rb_Markly_Node, "fence_info=", rb_node_set_fence_info, 1);
rb_define_method(rb_Markly_Node, "code_info", rb_node_get_code_info, 0);
rb_define_method(rb_Markly_Node, "code_info=", rb_node_set_code_info, 1);
rb_define_method(rb_Markly_Node, "fence", rb_node_get_fence, 0);
rb_define_method(rb_Markly_Node, "table_alignments", rb_node_get_table_alignments, 0);
rb_define_method(rb_Markly_Node, "tasklist_item_checked?", rb_node_get_tasklist_item_checked, 0);
rb_define_method(rb_Markly_Node, "tasklist_item_checked=", rb_node_set_tasklist_item_checked, 1);
Expand Down
12 changes: 12 additions & 0 deletions guides/getting-started/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,18 @@ blocks and front matter. `Node#code_language` returns the first token of that
info string, while `Node#fence_info` remains available for compatibility on
those block nodes.

For a fenced code block, `Node#fence` returns a `Node::Fence` structure with
the fence character, length, and indentation:

``` ruby
block = Markly.parse(" ~~~~ ruby\n Object.new\n ~~~~").first_child

block.fence
# => #<struct Markly::Node::Fence character="~", length=4, indent=2>
```

Indented code blocks and other node types return `nil`.

## Extensions

Both `render_html` and `parse` take an optional `extensions:` argument defining the extensions you want enabled as your CommonMark document is being processed:
Expand Down
1 change: 1 addition & 0 deletions releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Add opt-in language prefixes for inline code spans with `Markly::INLINE_CODE_INFO`, expose code metadata through `Node#code_info`, and provide `Node#code_language` as a convenient language accessor.
- Expose fenced code-block metadata through `Node#fence` and `Node::Fence`.

## v0.16.0

Expand Down
28 changes: 28 additions & 0 deletions test/markly/node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,34 @@
end
end

with "#fence" do
let(:fenced_backtick_node) {Markly.parse("``` ruby\nputs 'wow'\n```").first_child}
let(:fenced_tilde_node) {Markly.parse(" ~~~~ ruby\n puts 'wow'\n ~~~~").first_child}
let(:indented_code_node) {Markly.parse(" puts 'wow'").first_child}
let(:paragraph_node) {Markly.parse("hello").first_child}

it "returns fenced metadata for backtick fences" do
expect(fenced_backtick_node.fence).to be == Markly::Node::Fence.new("`", 3, 0)
end

it "returns fenced metadata for tilde fences" do
fence = fenced_tilde_node.fence

expect(fence).to be_a(Markly::Node::Fence)
expect(fence.character).to be == "~"
expect(fence.length).to be == 4
expect(fence.indent).to be == 2
end

it "returns nil for non-fenced code blocks" do
expect(indented_code_node.fence).to be_nil
end

it "returns nil for non-code nodes" do
expect(paragraph_node.fence).to be_nil
end
end

with "#find_header" do
let(:document) {Markly.parse("# Heading\n\n## Subheading")}

Expand Down
Loading