commonmarker-merge 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/REEK ADDED
File without changes
data/RUBOCOP.md ADDED
@@ -0,0 +1,71 @@
1
+ # RuboCop Usage Guide
2
+
3
+ ## Overview
4
+
5
+ A tale of two RuboCop plugin gems.
6
+
7
+ ### RuboCop Gradual
8
+
9
+ This project uses `rubocop_gradual` instead of vanilla RuboCop for code style checking. The `rubocop_gradual` tool allows for gradual adoption of RuboCop rules by tracking violations in a lock file.
10
+
11
+ ### RuboCop LTS
12
+
13
+ This project uses `rubocop-lts` to ensure, on a best-effort basis, compatibility with Ruby >= 1.9.2.
14
+ RuboCop rules are meticulously configured by the `rubocop-lts` family of gems to ensure that a project is compatible with a specific version of Ruby. See: https://rubocop-lts.gitlab.io for more.
15
+
16
+ ## Checking RuboCop Violations
17
+
18
+ To check for RuboCop violations in this project, always use:
19
+
20
+ ```bash
21
+ bundle exec rake rubocop_gradual:check
22
+ ```
23
+
24
+ **Do not use** the standard RuboCop commands like:
25
+ - `bundle exec rubocop`
26
+ - `rubocop`
27
+
28
+ ## Understanding the Lock File
29
+
30
+ The `.rubocop_gradual.lock` file tracks all current RuboCop violations in the project. This allows the team to:
31
+
32
+ 1. Prevent new violations while gradually fixing existing ones
33
+ 2. Track progress on code style improvements
34
+ 3. Ensure CI builds don't fail due to pre-existing violations
35
+
36
+ ## Common Commands
37
+
38
+ - **Check violations**
39
+ - `bundle exec rake rubocop_gradual`
40
+ - `bundle exec rake rubocop_gradual:check`
41
+ - **(Safe) Autocorrect violations, and update lockfile if no new violations**
42
+ - `bundle exec rake rubocop_gradual:autocorrect`
43
+ - **Force update the lock file (w/o autocorrect) to match violations present in code**
44
+ - `bundle exec rake rubocop_gradual:force_update`
45
+
46
+ ## Workflow
47
+
48
+ 1. Before submitting a PR, run `bundle exec rake rubocop_gradual:autocorrect`
49
+ a. or just the default `bundle exec rake`, as autocorrection is a pre-requisite of the default task.
50
+ 2. If there are new violations, either:
51
+ - Fix them in your code
52
+ - Run `bundle exec rake rubocop_gradual:force_update` to update the lock file (only for violations you can't fix immediately)
53
+ 3. Commit the updated `.rubocop_gradual.lock` file along with your changes
54
+
55
+ ## Never add inline RuboCop disables
56
+
57
+ Do not add inline `rubocop:disable` / `rubocop:enable` comments anywhere in the codebase (including specs, except when following the few existing `rubocop:disable` patterns for a rule already being disabled elsewhere in the code). We handle exceptions in two supported ways:
58
+
59
+ - Permanent/structural exceptions: prefer adjusting the RuboCop configuration (e.g., in `.rubocop.yml`) to exclude a rule for a path or file pattern when it makes sense project-wide.
60
+ - Temporary exceptions while improving code: record the current violations in `.rubocop_gradual.lock` via the gradual workflow:
61
+ - `bundle exec rake rubocop_gradual:autocorrect` (preferred; will autocorrect what it can and update the lock only if no new violations were introduced)
62
+ - If needed, `bundle exec rake rubocop_gradual:force_update` (as a last resort when you cannot fix the newly reported violations immediately)
63
+
64
+ In general, treat the rules as guidance to follow; fix violations rather than ignore them. For example, RSpec conventions in this project expect `described_class` to be used in specs that target a specific class under test.
65
+
66
+ ## Benefits of rubocop_gradual
67
+
68
+ - Allows incremental adoption of code style rules
69
+ - Prevents CI failures due to pre-existing violations
70
+ - Provides a clear record of code style debt
71
+ - Enables focused efforts on improving code quality over time
data/SECURITY.md ADDED
@@ -0,0 +1,21 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ |----------|-----------|
7
+ | 1.latest | ✅ |
8
+
9
+ ## Security contact information
10
+
11
+ To report a security vulnerability, please use the
12
+ [Tidelift security contact](https://tidelift.com/security).
13
+ Tidelift will coordinate the fix and disclosure.
14
+
15
+ ## Additional Support
16
+
17
+ If you are interested in support for versions older than the latest release,
18
+ please consider sponsoring the project / maintainer @ https://liberapay.com/pboling/donate,
19
+ or find other sponsorship links in the [README].
20
+
21
+ [README]: README.md
@@ -0,0 +1,336 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # Commonmarker backend using the Commonmarker gem (comrak Rust parser)
6
+ #
7
+ # This backend wraps Commonmarker, a Ruby gem that provides bindings to
8
+ # comrak, a fast CommonMark-compliant Markdown parser written in Rust.
9
+ #
10
+ # @note This backend only parses Markdown source code
11
+ # @see https://github.com/gjtorikian/commonmarker Commonmarker gem
12
+ #
13
+ # @example Basic usage
14
+ # parser = TreeHaver::Parser.new
15
+ # parser.language = Commonmarker::Merge::Backend::Language.markdown
16
+ # tree = parser.parse(markdown_source)
17
+ # root = tree.root_node
18
+ # puts root.type # => "document"
19
+ module Backend
20
+ @load_attempted = false
21
+ @loaded = false
22
+
23
+ # Check if the Commonmarker backend is available
24
+ #
25
+ # @return [Boolean] true if commonmarker gem is available
26
+ class << self
27
+ def available?
28
+ return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
29
+ @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
30
+ begin
31
+ require "commonmarker"
32
+ @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
33
+ rescue LoadError
34
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
35
+ rescue StandardError
36
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
37
+ end
38
+ @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
39
+ end
40
+
41
+ # Reset the load state (primarily for testing)
42
+ #
43
+ # @return [void]
44
+ # @api private
45
+ def reset!
46
+ @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
47
+ @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
48
+ end
49
+
50
+ # Get capabilities supported by this backend
51
+ #
52
+ # @return [Hash{Symbol => Object}] capability map
53
+ def capabilities
54
+ return {} unless available?
55
+ {
56
+ backend: :commonmarker,
57
+ query: false,
58
+ bytes_field: false, # Commonmarker uses line/column
59
+ incremental: false,
60
+ pure_ruby: false, # Uses Rust via FFI
61
+ markdown_only: true,
62
+ error_tolerant: true, # Markdown is forgiving
63
+ }
64
+ end
65
+ end
66
+
67
+ # Commonmarker language wrapper
68
+ #
69
+ # Commonmarker only parses Markdown. This class exists for API compatibility.
70
+ #
71
+ # @example
72
+ # language = Commonmarker::Merge::Backend::Language.markdown
73
+ # parser.language = language
74
+ class Language < TreeHaver::Base::Language
75
+ # Create a new Commonmarker language instance
76
+ #
77
+ # @param name [Symbol] Language name (should be :markdown)
78
+ # @param options [Hash] Commonmarker parse options
79
+ def initialize(name = :markdown, options: {})
80
+ super(name, backend: :commonmarker, options: options)
81
+ end
82
+
83
+ class << self
84
+ # Create a Markdown language instance
85
+ #
86
+ # @param options [Hash] Commonmarker parse options
87
+ # @return [Language] Markdown language
88
+ def markdown(options: {})
89
+ new(:markdown, options: options)
90
+ end
91
+
92
+ # Load language from library path (API compatibility)
93
+ #
94
+ # @param _path [String] Ignored - Commonmarker doesn't load external grammars
95
+ # @param symbol [String, nil] Ignored
96
+ # @param name [String, nil] Language name hint (defaults to :markdown)
97
+ # @return [Language] Markdown language
98
+ # @raise [TreeHaver::NotAvailable] if requested language is not Markdown
99
+ def from_library(_path = nil, symbol: nil, name: nil)
100
+ lang_name = name || symbol&.to_s&.sub(/^tree_sitter_/, "")&.to_sym || :markdown
101
+
102
+ unless lang_name == :markdown
103
+ raise TreeHaver::NotAvailable,
104
+ "Commonmarker backend only supports Markdown, not #{lang_name}."
105
+ end
106
+
107
+ markdown
108
+ end
109
+ end
110
+ end
111
+
112
+ # Commonmarker parser wrapper
113
+ class Parser < TreeHaver::Base::Parser
114
+ # Parse Markdown source code
115
+ #
116
+ # @param source [String] Markdown source to parse
117
+ # @return [Tree] Parsed tree
118
+ def parse(source)
119
+ raise "Language not set" unless language
120
+ Backend.available? or raise "Commonmarker not available"
121
+
122
+ opts = language.options || {}
123
+ doc = ::Commonmarker.parse(source, options: opts)
124
+ Tree.new(doc, source)
125
+ end
126
+ end
127
+
128
+ # Commonmarker tree wrapper
129
+ class Tree < TreeHaver::Base::Tree
130
+ def initialize(document, source)
131
+ super(document, source: source)
132
+ end
133
+
134
+ def root_node
135
+ Node.new(inner_tree, source: source, lines: lines)
136
+ end
137
+ end
138
+
139
+ # Commonmarker node wrapper
140
+ #
141
+ # Wraps Commonmarker::Node to provide TreeHaver::Node-compatible interface.
142
+ class Node < TreeHaver::Base::Node
143
+ # Get the node type as a string
144
+ #
145
+ # @return [String] Node type
146
+ def type
147
+ inner_node.type.to_s
148
+ end
149
+
150
+ # Alias for TreeHaver compatibility
151
+ alias_method :kind, :type
152
+
153
+ # Get the text content of this node
154
+ #
155
+ # @return [String] Node text
156
+ def text
157
+ if inner_node.respond_to?(:string_content)
158
+ begin
159
+ content = inner_node.string_content.to_s
160
+ return content unless content.empty?
161
+ rescue TypeError
162
+ # Container node - fall through
163
+ end
164
+ end
165
+ children.map(&:text).join
166
+ end
167
+
168
+ # Get child nodes
169
+ #
170
+ # @return [Array<Node>] Child nodes
171
+ def children
172
+ return [] unless inner_node.respond_to?(:each)
173
+
174
+ result = []
175
+ inner_node.each { |child| result << Node.new(child, source: source, lines: lines) }
176
+ result
177
+ end
178
+
179
+ # Get start byte offset
180
+ def start_byte
181
+ sp = start_point
182
+ calculate_byte_offset(sp[:row], sp[:column])
183
+ end
184
+
185
+ # Get end byte offset
186
+ def end_byte
187
+ ep = end_point
188
+ calculate_byte_offset(ep[:row], ep[:column])
189
+ end
190
+
191
+ # Get start point (0-based row/column)
192
+ # @return [Point] Start position
193
+ def start_point
194
+ if inner_node.respond_to?(:source_position)
195
+ begin
196
+ pos = inner_node.source_position
197
+ if pos && pos[:start_line]
198
+ return Point.new(pos[:start_line] - 1, (pos[:start_column] || 1) - 1)
199
+ end
200
+ rescue
201
+ nil
202
+ end
203
+ end
204
+
205
+ # Fallback: check sourcepos (old API)
206
+ begin
207
+ pos = inner_node.sourcepos
208
+ return Point.new(pos[0] - 1, pos[1] - 1) if pos
209
+ rescue
210
+ nil
211
+ end
212
+
213
+ Point.new(0, 0)
214
+ end
215
+
216
+ # Get end point (0-based row/column)
217
+ # @return [Point] End position
218
+ def end_point
219
+ if inner_node.respond_to?(:source_position)
220
+ begin
221
+ pos = inner_node.source_position
222
+ if pos && pos[:end_line]
223
+ return Point.new(pos[:end_line] - 1, (pos[:end_column] || 1) - 1)
224
+ end
225
+ rescue
226
+ nil
227
+ end
228
+ end
229
+
230
+ begin
231
+ pos = inner_node.sourcepos
232
+ return Point.new(pos[2] - 1, pos[3] - 1) if pos
233
+ rescue
234
+ nil
235
+ end
236
+
237
+ Point.new(0, 0)
238
+ end
239
+
240
+ # Commonmarker-specific methods
241
+
242
+ # Get heading level (1-6)
243
+ # @return [Integer, nil]
244
+ def header_level
245
+ return unless type == "heading"
246
+ begin
247
+ inner_node.header_level
248
+ rescue
249
+ nil
250
+ end
251
+ end
252
+
253
+ # Get fence info for code blocks
254
+ # @return [String, nil]
255
+ def fence_info
256
+ return unless type == "code_block"
257
+ begin
258
+ inner_node.fence_info
259
+ rescue
260
+ nil
261
+ end
262
+ end
263
+
264
+ # Get URL for links/images
265
+ # @return [String, nil]
266
+ def url
267
+ inner_node.url
268
+ rescue
269
+ nil
270
+ end
271
+
272
+ # Get title for links/images
273
+ # @return [String, nil]
274
+ def title
275
+ inner_node.title
276
+ rescue
277
+ nil
278
+ end
279
+
280
+ # Get the next sibling
281
+ # @return [Node, nil]
282
+ def next_sibling
283
+ sibling = begin
284
+ inner_node.next_sibling
285
+ rescue
286
+ nil
287
+ end
288
+ sibling ? Node.new(sibling, source: source, lines: lines) : nil
289
+ end
290
+
291
+ # Get the previous sibling
292
+ # @return [Node, nil]
293
+ def prev_sibling
294
+ sibling = begin
295
+ inner_node.previous_sibling
296
+ rescue
297
+ nil
298
+ end
299
+ sibling ? Node.new(sibling, source: source, lines: lines) : nil
300
+ end
301
+
302
+ # Get the parent node
303
+ # @return [Node, nil]
304
+ def parent
305
+ p = begin
306
+ inner_node.parent
307
+ rescue
308
+ nil
309
+ end
310
+ p ? Node.new(p, source: source, lines: lines) : nil
311
+ end
312
+ end
313
+
314
+ # Alias Point to the base class for compatibility
315
+ Point = TreeHaver::Base::Point
316
+
317
+ # Register this backend with TreeHaver
318
+ # Register for generic :markdown language
319
+ ::TreeHaver.register_language(
320
+ :markdown,
321
+ backend_type: :commonmarker,
322
+ backend_module: self,
323
+ gem_name: "commonmarker",
324
+ )
325
+
326
+ # Register the full tag for RSpec dependency tags with require path
327
+ # This enables tree_haver to lazily load this gem when checking availability
328
+ ::TreeHaver::BackendRegistry.register_tag(
329
+ :commonmarker_backend,
330
+ category: :backend,
331
+ backend_name: :commonmarker,
332
+ require_path: "commonmarker/merge",
333
+ ) { available? }
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # Debug logging utility for Commonmarker::Merge operations.
6
+ #
7
+ # Extends Ast::Merge::DebugLogger to provide consistent logging
8
+ # across all merge gems. Logs are controlled via environment variables.
9
+ #
10
+ # @example Enable debug logging
11
+ # ENV["COMMONMARKER_MERGE_DEBUG"] = "1"
12
+ # DebugLogger.debug("Parsing markdown", { file: "README.md" })
13
+ #
14
+ # @example Time an operation
15
+ # result = DebugLogger.time("parse") { Commonmarker.parse(source) }
16
+ #
17
+ # @see Ast::Merge::DebugLogger Base module
18
+ module DebugLogger
19
+ extend Ast::Merge::DebugLogger
20
+
21
+ # Configure for commonmarker-merge
22
+ self.env_var_name = "COMMONMARKER_MERGE_DEBUG"
23
+ self.log_prefix = "[commonmarker-merge]"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # File analysis for Markdown files using CommonMarker.
6
+ #
7
+ # This is a thin wrapper around Markdown::Merge::FileAnalysis that:
8
+ # - Forces the :commonmarker backend
9
+ # - Sets the default freeze token to "commonmarker-merge"
10
+ # - Exposes commonmarker-specific options
11
+ #
12
+ # @example Basic usage
13
+ # analysis = FileAnalysis.new(markdown_source)
14
+ # analysis.statements.each do |node|
15
+ # puts "#{node.merge_type}: #{node.type}"
16
+ # end
17
+ #
18
+ # @example With custom freeze token
19
+ # analysis = FileAnalysis.new(source, freeze_token: "my-merge")
20
+ #
21
+ # @see Markdown::Merge::FileAnalysis Underlying implementation
22
+ class FileAnalysis < Markdown::Merge::FileAnalysis
23
+ # Default freeze token for commonmarker-merge
24
+ # @return [String]
25
+ DEFAULT_FREEZE_TOKEN = "commonmarker-merge"
26
+
27
+ # Initialize file analysis with CommonMarker backend.
28
+ #
29
+ # @param source [String] Markdown source code to analyze
30
+ # @param freeze_token [String] Token for freeze block markers (default: "commonmarker-merge")
31
+ # @param signature_generator [Proc, nil] Custom signature generator
32
+ # @param options [Hash] CommonMarker parse options
33
+ def initialize(source, freeze_token: DEFAULT_FREEZE_TOKEN, signature_generator: nil, options: {})
34
+ super(
35
+ source,
36
+ backend: :commonmarker,
37
+ freeze_token: freeze_token,
38
+ signature_generator: signature_generator,
39
+ options: options,
40
+ )
41
+ end
42
+
43
+ # Returns the FreezeNode class to use.
44
+ #
45
+ # @return [Class] Commonmarker::Merge::FreezeNode
46
+ def freeze_node_class
47
+ FreezeNode
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # Represents a frozen block of Markdown content that should be preserved during merges.
6
+ #
7
+ # Inherits from Markdown::Merge::FreezeNode which provides the generic
8
+ # freeze block handling.
9
+ #
10
+ # Freeze blocks are marked with HTML comments:
11
+ # <!-- commonmarker-merge:freeze -->
12
+ # ... frozen content ...
13
+ # <!-- commonmarker-merge:unfreeze -->
14
+ #
15
+ # @example Basic freeze block
16
+ # <!-- commonmarker-merge:freeze -->
17
+ # ## Custom Section
18
+ # This content will not be modified by merge operations.
19
+ # <!-- commonmarker-merge:unfreeze -->
20
+ #
21
+ # @example Freeze block with reason
22
+ # <!-- commonmarker-merge:freeze Manual TOC -->
23
+ # ## Table of Contents
24
+ # - [Introduction](#introduction)
25
+ # - [Usage](#usage)
26
+ # <!-- commonmarker-merge:unfreeze -->
27
+ #
28
+ # @see Markdown::Merge::FreezeNode
29
+ class FreezeNode < Markdown::Merge::FreezeNode
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # Orchestrates the smart merge process for Markdown files using CommonMarker.
6
+ #
7
+ # This is a thin wrapper around Markdown::Merge::SmartMerger that:
8
+ # - Forces the :commonmarker backend
9
+ # - Sets commonmarker-specific defaults (freeze token, inner_merge_code_blocks)
10
+ # - Exposes commonmarker-specific options (options hash)
11
+ #
12
+ # @example Basic merge (destination customizations preserved)
13
+ # merger = SmartMerger.new(template_content, dest_content)
14
+ # result = merger.merge
15
+ # if result.success?
16
+ # File.write("output.md", result.content)
17
+ # end
18
+ #
19
+ # @example Template updates win
20
+ # merger = SmartMerger.new(
21
+ # template_content,
22
+ # dest_content,
23
+ # preference: :template,
24
+ # add_template_only_nodes: true
25
+ # )
26
+ # result = merger.merge
27
+ #
28
+ # @example Custom signature matching
29
+ # sig_gen = ->(node) {
30
+ # canonical_type = Ast::Merge::NodeTyping.merge_type_for(node) || node.type
31
+ # if canonical_type == :heading
32
+ # [:heading, node.header_level] # Match by level only, not content
33
+ # else
34
+ # node # Fall through to default
35
+ # end
36
+ # }
37
+ # merger = SmartMerger.new(
38
+ # template_content,
39
+ # dest_content,
40
+ # signature_generator: sig_gen
41
+ # )
42
+ #
43
+ # @see Markdown::Merge::SmartMerger Underlying implementation
44
+ class SmartMerger < Markdown::Merge::SmartMerger
45
+ # Creates a new SmartMerger for intelligent Markdown file merging.
46
+ #
47
+ # @param template_content [String] Template Markdown source code
48
+ # @param dest_content [String] Destination Markdown source code
49
+ #
50
+ # @param signature_generator [Proc, nil] Optional proc to generate custom node signatures.
51
+ # The proc receives a node (wrapped with canonical merge_type) and should return one of:
52
+ # - An array representing the node's signature
53
+ # - `nil` to indicate the node should have no signature
54
+ # - The original node to fall through to default signature computation
55
+ #
56
+ # @param preference [Symbol] Controls which version to use when nodes
57
+ # have matching signatures but different content:
58
+ # - `:destination` (default) - Use destination version (preserves customizations)
59
+ # - `:template` - Use template version (applies updates)
60
+ #
61
+ # @param add_template_only_nodes [Boolean] Controls whether to add nodes that only
62
+ # exist in template:
63
+ # - `false` (default) - Skip template-only nodes
64
+ # - `true` - Add template-only nodes to result
65
+ #
66
+ # @param freeze_token [String] Token to use for freeze block markers.
67
+ # Default: "commonmarker-merge"
68
+ # Looks for: <!-- commonmarker-merge:freeze --> / <!-- commonmarker-merge:unfreeze -->
69
+ #
70
+ # @param options [Hash] CommonMarker parse options
71
+ #
72
+ # @param match_refiner [#call, nil] Optional match refiner for fuzzy matching of
73
+ # unmatched nodes. Default: nil (fuzzy matching disabled).
74
+ # Set to TableMatchRefiner.new to enable fuzzy table matching.
75
+ #
76
+ # @param node_typing [Hash{Symbol,String => #call}, nil] Node typing configuration
77
+ # for per-node-type merge preferences.
78
+ # @param extra_options [Hash] Additional options for forward compatibility
79
+ #
80
+ # @raise [TemplateParseError] If template has syntax errors
81
+ # @raise [DestinationParseError] If destination has syntax errors
82
+ def initialize(
83
+ template_content,
84
+ dest_content,
85
+ signature_generator: nil,
86
+ preference: :destination,
87
+ add_template_only_nodes: false,
88
+ freeze_token: DEFAULT_FREEZE_TOKEN,
89
+ options: {},
90
+ match_refiner: nil,
91
+ node_typing: nil,
92
+ **extra_options
93
+ )
94
+ super(
95
+ template_content,
96
+ dest_content,
97
+ backend: :commonmarker,
98
+ signature_generator: signature_generator,
99
+ preference: preference,
100
+ add_template_only_nodes: add_template_only_nodes,
101
+ inner_merge_code_blocks: DEFAULT_INNER_MERGE_CODE_BLOCKS,
102
+ freeze_token: freeze_token,
103
+ match_refiner: match_refiner,
104
+ node_typing: node_typing,
105
+ options: options,
106
+ **extra_options
107
+ )
108
+ end
109
+
110
+ # Returns the TemplateParseError class to use.
111
+ #
112
+ # @return [Class] Commonmarker::Merge::TemplateParseError
113
+ def template_parse_error_class
114
+ TemplateParseError
115
+ end
116
+
117
+ # Returns the DestinationParseError class to use.
118
+ #
119
+ # @return [Class] Commonmarker::Merge::DestinationParseError
120
+ def destination_parse_error_class
121
+ DestinationParseError
122
+ end
123
+
124
+ # Create a FileAnalysis instance for parsing.
125
+ #
126
+ # @param content [String] Markdown content to analyze
127
+ # @param options [Hash] Analysis options
128
+ # @return [Commonmarker::Merge::FileAnalysis] File analysis instance
129
+ def create_file_analysis(content, **opts)
130
+ FileAnalysis.new(
131
+ content,
132
+ freeze_token: opts[:freeze_token],
133
+ signature_generator: opts[:signature_generator],
134
+ options: opts[:options] || {},
135
+ )
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Commonmarker
4
+ module Merge
5
+ # Version information for Commonmarker::Merge
6
+ module Version
7
+ # Current version of the commonmarker-merge gem
8
+ VERSION = "1.0.0"
9
+ end
10
+ VERSION = Version::VERSION # traditional location
11
+ end
12
+ end