Module: Docscribe::InlineRewriter::SourceHelpers
- Defined in:
- lib/docscribe/inline_rewriter/source_helpers.rb
Overview
Source-level helpers: ranges, insertion positions, indentation, and comment-block detection.
These helpers operate on raw source text and parser source locations rather than Ruby semantics.
Class Method Summary collapse
-
.already_has_doc_immediately_above?(buffer, insert_pos) ⇒ Boolean
Whether any comment exists immediately above the insertion point.
-
.build_block_info(lines, start_idx, preserved_start_idx, end_idx) ⇒ Hash
Build block info hash from computed line ranges.
-
.comment_block_removal_range(buffer, def_bol_pos) ⇒ Parser::Source::Range?
Compute the removable range for an existing doc-like block above a method.
-
.compute_positions(lines, start_idx, doc_start_idx, end_pos_idx) ⇒ Hash{start_pos: Integer, doc_start_pos: Integer, end_pos: Integer}
Compute source positions for a comment block.
-
.compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos) ⇒ Parser::Source::Range
Compute the removal range for preserved start position.
-
.doc_comment_block_info(buffer, def_bol_pos) ⇒ Hash?
Return structured information about a contiguous doc-like comment block above a method.
-
.doc_marker?(lines, range) ⇒ Boolean
Whether a comment block range contains documentation markers.
-
.doc_marker_line?(line) ⇒ Boolean
Whether a comment line looks like documentation content.
-
.find_comment_block_range(lines, def_line_idx) ⇒ Hash{start_idx: Integer, end_idx: Integer}?
Find the range of a contiguous comment block directly above a method definition.
-
.find_preserved_start_idx(lines, start_idx, end_idx) ⇒ Integer
Find the first index in a comment block after preserved directive-style lines.
-
.line_indent(node) ⇒ String
Return the indentation prefix of a node’s source line.
-
.line_start_range(buffer, node) ⇒ Parser::Source::Range
Return a zero-width range at the beginning of the line containing a node.
-
.node_name(node) ⇒ Symbol?
Extract the method name from a ‘:def` or `:defs` node.
-
.preserved_comment_line?(line) ⇒ Boolean
Whether a comment line should be preserved during aggressive replacement.
Class Method Details
.already_has_doc_immediately_above?(buffer, insert_pos) ⇒ Boolean
module_function: when included, also defines #already_has_doc_immediately_above? (instance visibility: private)
Whether any comment exists immediately above the insertion point.
This helper is retained for compatibility/legacy behavior checks.
246 247 248 249 250 251 252 253 254 255 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 246 def already_has_doc_immediately_above?(buffer, insert_pos) src = buffer.source lines = src.lines current_line_index = (src[0...insert_pos] || '').count("\n") i = current_line_index - 1 i -= 1 while i >= 0 && lines[i].strip.empty? return false if i.negative? !!(lines[i] =~ /^\s*#/) end |
.build_block_info(lines, start_idx, preserved_start_idx, end_idx) ⇒ Hash
module_function: when included, also defines #build_block_info (instance visibility: private)
Build block info hash from computed line ranges.
145 146 147 148 149 150 151 152 153 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 145 def build_block_info(lines, start_idx, preserved_start_idx, end_idx) positions = compute_positions(lines, start_idx, preserved_start_idx, end_idx) { lines: lines[start_idx..end_idx], preserved_lines: lines[start_idx...preserved_start_idx], doc_lines: lines[preserved_start_idx..end_idx], **positions } end |
.comment_block_removal_range(buffer, def_bol_pos) ⇒ Parser::Source::Range?
module_function: when included, also defines #comment_block_removal_range (instance visibility: private)
Compute the removable range for an existing doc-like block above a method.
Preserved directive lines (such as RuboCop directives or magic comments) are excluded from the returned range.
77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 77 def comment_block_removal_range(buffer, def_bol_pos) src = buffer.source lines = src.lines def_line_idx = (src[0...def_bol_pos] || '').count("\n") block_range = find_comment_block_range(lines, def_line_idx) return nil unless block_range preserved_start_idx = find_preserved_start_idx(lines, block_range[:start_idx], block_range[:end_idx]) return nil unless doc_marker?(lines, preserved_start_idx..block_range[:end_idx]) compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos) end |
.compute_positions(lines, start_idx, doc_start_idx, end_pos_idx) ⇒ Hash{start_pos: Integer, doc_start_pos: Integer, end_pos: Integer}
module_function: when included, also defines #compute_positions (instance visibility: private)
Compute source positions for a comment block.
176 177 178 179 180 181 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 176 def compute_positions(lines, start_idx, doc_start_idx, end_pos_idx) start_pos = start_idx.positive? ? (lines[0...start_idx] || []).join.length : 0 doc_start_pos = doc_start_idx.positive? ? (lines[0...doc_start_idx] || []).join.length : 0 end_pos = (lines[0..end_pos_idx] || []).join.length { start_pos: start_pos, doc_start_pos: doc_start_pos, end_pos: end_pos } end |
.compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos) ⇒ Parser::Source::Range
module_function: when included, also defines #compute_removal_range (instance visibility: private)
Compute the removal range for preserved start position.
163 164 165 166 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 163 def compute_removal_range(buffer, lines, preserved_start_idx, def_bol_pos) start_pos = preserved_start_idx.positive? ? (lines[0...preserved_start_idx] || []).join.length : 0 Parser::Source::Range.new(buffer, start_pos, def_bol_pos) end |
.doc_comment_block_info(buffer, def_bol_pos) ⇒ Hash?
module_function: when included, also defines #doc_comment_block_info (instance visibility: private)
Return structured information about a contiguous doc-like comment block above a method.
Result includes:
-
all lines in the contiguous block
-
preserved directive prefix lines
-
editable doc lines
-
source positions for replacement
Returns nil if no doc-like block is present.
54 55 56 57 58 59 60 61 62 63 64 65 66 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 54 def doc_comment_block_info(buffer, def_bol_pos) lines = buffer.source.lines def_line_idx = (buffer.source[0...def_bol_pos] || '').count("\n") block_range = find_comment_block_range(lines, def_line_idx) return nil unless block_range start_idx = block_range[:start_idx] end_idx = block_range[:end_idx] preserved_start_idx = find_preserved_start_idx(lines, start_idx, end_idx) return nil unless doc_marker?(lines, preserved_start_idx..end_idx) build_block_info(lines, start_idx, preserved_start_idx, end_idx) end |
.doc_marker?(lines, range) ⇒ Boolean
module_function: when included, also defines #doc_marker? (instance visibility: private)
Whether a comment block range contains documentation markers.
133 134 135 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 133 def doc_marker?(lines, range) (lines[range] || []).any? { |line| doc_marker_line?(line) } end |
.doc_marker_line?(line) ⇒ Boolean
module_function: when included, also defines #doc_marker_line? (instance visibility: private)
Whether a comment line looks like documentation content.
Recognized forms include:
-
Docscribe header lines
-
YARD tags/directives beginning with ‘@`
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 219 def doc_marker_line?(line) # Docscribe header line: # # +A#foo+ -> Integer return true if line =~ /^\s*#\s*\+\S.*\+\s*->\s*\S/ # YARD tags and directives: # # @param ... # # @return ... # # @raise ... # # @private / @protected # # @!attribute ... # also matches indented attribute tag lines like: # # @return [Type] return true if line =~ /^\s*#\s*@/ false end |
.find_comment_block_range(lines, def_line_idx) ⇒ Hash{start_idx: Integer, end_idx: Integer}?
module_function: when included, also defines #find_comment_block_range (instance visibility: private)
Find the range of a contiguous comment block directly above a method definition.
Walks upward from def_line_idx, skipping blank lines, then includes all contiguous comment lines.
99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 99 def find_comment_block_range(lines, def_line_idx) i = def_line_idx - 1 i -= 1 while i >= 0 && lines[i].strip.empty? return nil unless i >= 0 && lines[i] =~ /^\s*#/ start_idx = i start_idx -= 1 while start_idx >= 0 && lines[start_idx] =~ /^\s*#/ start_idx += 1 { start_idx: start_idx, end_idx: i } end |
.find_preserved_start_idx(lines, start_idx, end_idx) ⇒ Integer
module_function: when included, also defines #find_preserved_start_idx (instance visibility: private)
Find the first index in a comment block after preserved directive-style lines.
Preserved lines include RuboCop directives and Ruby magic comments.
121 122 123 124 125 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 121 def find_preserved_start_idx(lines, start_idx, end_idx) idx = start_idx idx += 1 while idx <= end_idx && preserved_comment_line?(lines[idx]) idx end |
.line_indent(node) ⇒ String
module_function: when included, also defines #line_indent (instance visibility: private)
Return the indentation prefix of a node’s source line.
Tabs and spaces are preserved exactly.
265 266 267 268 269 270 271 272 273 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 265 def line_indent(node) line = node.loc.expression.source_line return '' unless line # Preserve tabs/spaces exactly. line[/\A[ \t]*/] || '' rescue StandardError '' end |
.line_start_range(buffer, node) ⇒ Parser::Source::Range
module_function: when included, also defines #line_start_range (instance visibility: private)
Return a zero-width range at the beginning of the line containing a node.
Used as the insertion point for generated documentation.
33 34 35 36 37 38 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 33 def line_start_range(buffer, node) start_pos = node.loc.expression.begin_pos src = buffer.source bol = start_pos <= 0 ? -1 : src.rindex("\n", start_pos - 1) || -1 Parser::Source::Range.new(buffer, bol + 1, bol + 1) end |
.node_name(node) ⇒ Symbol?
module_function: when included, also defines #node_name (instance visibility: private)
Extract the method name from a ‘:def` or `:defs` node.
18 19 20 21 22 23 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 18 def node_name(node) case node.type when :def then node.children[0] when :defs then node.children[1] end end |
.preserved_comment_line?(line) ⇒ Boolean
module_function: when included, also defines #preserved_comment_line? (instance visibility: private)
Whether a comment line should be preserved during aggressive replacement.
Preserved lines include:
-
RuboCop directives
-
Ruby magic comments
-
tool directives such as ‘:nocov:` / `:stopdoc:`
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
# File 'lib/docscribe/inline_rewriter/source_helpers.rb', line 193 def preserved_comment_line?(line) # RuboCop directives return true if line =~ /^\s*#\s*rubocop:(disable|enable|todo)\b/ # Ruby magic comments return true if line =~ /^\s*#\s*(?:frozen_string_literal|warn_indent)\s*:\s*(?:true|false)\b/i return true if line =~ /^\s*#.*\b(?:encoding|coding)\s*:\s*[\w.-]+\b/i # Tool directives like: # # :nocov: # # :stopdoc: # # :nodoc: return true if line =~ /^\s*#\s*:\s*[\w-]+\s*:(?=\s|\z)/i false end |