Skip to content

Commit df007ce

Browse files
committed
Add Node#fenced method for code block fence metadata
Returns a hash with {:info, :length, :character, :offset} for fenced code blocks, nil for non-fenced or non-code-block nodes.
1 parent f96983c commit df007ce

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

ext/markly/markly.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,36 @@ static VALUE rb_node_set_fence_info(VALUE self, VALUE info) {
10281028
return Qnil;
10291029
}
10301030

1031+
/*
1032+
* Public: Gets fencing details for the current node.
1033+
*
1034+
* Returns a {Hash} with `:info`, `:length`, `:character`, and `:offset` keys
1035+
* for fenced code blocks, `nil` otherwise.
1036+
*/
1037+
static VALUE rb_node_get_fenced(VALUE self) {
1038+
const char *fence_info;
1039+
int fence_length = 0;
1040+
int fence_offset = 0;
1041+
char fence_character = '\0';
1042+
cmark_node *node;
1043+
VALUE result;
1044+
TypedData_Get_Struct(self, cmark_node, &rb_Markly_Node_Type, node);
1045+
1046+
if (!cmark_node_get_fenced(node, &fence_length, &fence_offset, &fence_character)) {
1047+
return Qnil;
1048+
}
1049+
1050+
fence_info = cmark_node_get_fence_info(node);
1051+
1052+
result = rb_hash_new();
1053+
rb_hash_aset(result, CSTR2SYM("info"), fence_info ? rb_str_new2(fence_info) : rb_str_new("", 0));
1054+
rb_hash_aset(result, CSTR2SYM("length"), INT2NUM(fence_length));
1055+
rb_hash_aset(result, CSTR2SYM("character"), rb_str_new(&fence_character, 1));
1056+
rb_hash_aset(result, CSTR2SYM("offset"), INT2NUM(fence_offset));
1057+
1058+
return result;
1059+
}
1060+
10311061
static VALUE rb_node_get_tasklist_item_checked(VALUE self) {
10321062
int tasklist_state;
10331063
cmark_node *node;
@@ -1242,6 +1272,7 @@ __attribute__((visibility("default"))) void Init_markly(void) {
12421272
rb_define_method(rb_Markly_Node, "list_tight=", rb_node_set_list_tight, 1);
12431273
rb_define_method(rb_Markly_Node, "fence_info", rb_node_get_fence_info, 0);
12441274
rb_define_method(rb_Markly_Node, "fence_info=", rb_node_set_fence_info, 1);
1275+
rb_define_method(rb_Markly_Node, "fenced", rb_node_get_fenced, 0);
12451276
rb_define_method(rb_Markly_Node, "table_alignments", rb_node_get_table_alignments, 0);
12461277
rb_define_method(rb_Markly_Node, "tasklist_item_checked?", rb_node_get_tasklist_item_checked, 0);
12471278
rb_define_method(rb_Markly_Node, "tasklist_item_checked=", rb_node_set_tasklist_item_checked, 1);

test/markly/node.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,39 @@
215215
end
216216
end
217217

218+
with "#fenced" do
219+
let(:fenced_backtick_node) {Markly.parse("``` ruby\nputs 'wow'\n```").first_child}
220+
let(:fenced_tilde_node) {Markly.parse("~~~ ruby\nputs 'wow'\n~~~").first_child}
221+
let(:indented_code_node) {Markly.parse(" puts 'wow'").first_child}
222+
let(:paragraph_node) {Markly.parse("hello").first_child}
223+
224+
it "returns fenced metadata for backtick fences" do
225+
expect(fenced_backtick_node.fenced).to be == {
226+
info: "ruby",
227+
length: 3,
228+
character: "`",
229+
offset: 0
230+
}
231+
end
232+
233+
it "returns fenced metadata for tilde fences" do
234+
expect(fenced_tilde_node.fenced).to be == {
235+
info: "ruby",
236+
length: 3,
237+
character: "~",
238+
offset: 0
239+
}
240+
end
241+
242+
it "returns nil for non-fenced code blocks" do
243+
expect(indented_code_node.fenced).to be_nil
244+
end
245+
246+
it "returns nil for non-code nodes" do
247+
expect(paragraph_node.fenced).to be_nil
248+
end
249+
end
250+
218251
with "#find_header" do
219252
let(:document) {Markly.parse("# Heading\n\n## Subheading")}
220253

0 commit comments

Comments
 (0)