diff --git a/README.md b/README.md
index ea875b22..1ee95ec3 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ require 'commonmarker'
Commonmarker.to_html('"Hi *there*"', options: {
parse: { smart: true }
})
-#
“Hi there”
\n
+# => “Hi there”
\n
```
(The second argument is optional--[see below](#options-and-plugins) for more information.)
@@ -49,10 +49,10 @@ require 'commonmarker'
doc = Commonmarker.parse("*Hello* world", options: {
parse: { smart: true }
})
-puts(doc.to_html) # Hello world
\n
+puts(doc.to_html) # => Hello world
\n
doc.walk do |node|
- puts node.type # [:document, :paragraph, :emph, :text, :text]
+ puts node.type # => [:document, :paragraph, :emph, :text, :text]
end
```
@@ -66,6 +66,14 @@ When it comes to modifying the document, you can perform the following operation
- `append_child`
- `detach`
+You can also get the source position of a node by calling `source_position`:
+
+```ruby
+doc = Commonmarker.parse("*Hello* world")
+puts doc.first_child.first_child.source_position
+# => {:start_line=>1, :start_column=>1, :end_line=>1, :end_column=>7}
+```
+
You can also modify the following attributes:
- `url`
diff --git a/ext/commonmarker/src/node.rs b/ext/commonmarker/src/node.rs
index 4801b663..476bf2ec 100644
--- a/ext/commonmarker/src/node.rs
+++ b/ext/commonmarker/src/node.rs
@@ -219,6 +219,21 @@ impl CommonmarkerNode {
})
}
+ fn get_sourcepos(&self) -> Result {
+ let node = self.inner.borrow();
+
+ let result = RHash::new();
+ result.aset(Symbol::new("start_line"), node.data.sourcepos.start.line)?;
+ result.aset(
+ Symbol::new("start_column"),
+ node.data.sourcepos.start.column,
+ )?;
+ result.aset(Symbol::new("end_line"), node.data.sourcepos.end.line)?;
+ result.aset(Symbol::new("end_column"), node.data.sourcepos.end.column)?;
+
+ Ok(result)
+ }
+
fn insert_node_before(&self, new_sibling: &CommonmarkerNode) -> Result {
let node = new_sibling.inner.clone();
node.detach();
@@ -546,6 +561,11 @@ pub fn init(m_commonmarker: RModule) -> Result<(), magnus::Error> {
c_node.define_method("detach", method!(CommonmarkerNode::detach_node, 0))?;
+ c_node.define_method(
+ "source_position",
+ method!(CommonmarkerNode::get_sourcepos, 0),
+ )?;
+
c_node.define_method("url", method!(CommonmarkerNode::get_url, 0))?;
c_node.define_method("url=", method!(CommonmarkerNode::set_url, 1))?;
c_node.define_method("title", method!(CommonmarkerNode::get_title, 0))?;
diff --git a/test/sourcepos_test.rb b/test/sourcepos_test.rb
index 0f2049c3..d50278ce 100644
--- a/test/sourcepos_test.rb
+++ b/test/sourcepos_test.rb
@@ -20,4 +20,59 @@ def test_to_html
assert_equal(expected, Commonmarker.to_html(md, options: { render: { sourcepos: true } }))
end
+
+ def test_can_generate_source_positions
+ md = <<~MARKDOWN
+ ## Try CommonMark
+
+ You can try CommonMark here. This dingus is powered by
+ [commonmark.js](https://github.com/jgm/commonmark.js), the
+ JavaScript reference implementation.
+
+ 1. item one
+ 2. item two
+ - sublist
+ - sublist
+ MARKDOWN
+ doc = Commonmarker.parse(md)
+
+ source_positions = []
+
+ doc.walk do |node|
+ source_positions << node.source_position
+ end
+
+ source_positions.delete_if { |h| h.values.all?(&:zero?) }
+
+ assert_equal(
+ [
+ { start_line: 1, start_column: 1, end_line: 10, end_column: 12 },
+ { start_line: 1, start_column: 1, end_line: 1, end_column: 17 },
+ { start_line: 1, start_column: 4, end_line: 1, end_column: 17 },
+ { start_line: 3, start_column: 1, end_line: 5, end_column: 36 },
+ { start_line: 3, start_column: 1, end_line: 3, end_column: 55 },
+ { start_line: 3, start_column: 56, end_line: 3, end_column: 56 },
+ { start_line: 4, start_column: 1, end_line: 4, end_column: 53 },
+ { start_line: 4, start_column: 2, end_line: 4, end_column: 14 },
+ { start_line: 4, start_column: 54, end_line: 4, end_column: 58 },
+ { start_line: 4, start_column: 59, end_line: 4, end_column: 59 },
+ { start_line: 5, start_column: 1, end_line: 5, end_column: 36 },
+ { start_line: 7, start_column: 1, end_line: 10, end_column: 12 },
+ { start_line: 7, start_column: 1, end_line: 7, end_column: 11 },
+ { start_line: 7, start_column: 4, end_line: 7, end_column: 11 },
+ { start_line: 7, start_column: 4, end_line: 7, end_column: 11 },
+ { start_line: 8, start_column: 1, end_line: 10, end_column: 12 },
+ { start_line: 8, start_column: 4, end_line: 8, end_column: 11 },
+ { start_line: 8, start_column: 4, end_line: 8, end_column: 11 },
+ { start_line: 9, start_column: 4, end_line: 10, end_column: 12 },
+ { start_line: 9, start_column: 4, end_line: 9, end_column: 12 },
+ { start_line: 9, start_column: 6, end_line: 9, end_column: 12 },
+ { start_line: 9, start_column: 6, end_line: 9, end_column: 12 },
+ { start_line: 10, start_column: 4, end_line: 10, end_column: 12 },
+ { start_line: 10, start_column: 6, end_line: 10, end_column: 12 },
+ { start_line: 10, start_column: 6, end_line: 10, end_column: 12 },
+ ],
+ source_positions,
+ )
+ end
end