Module: Flexr::Source::Passthrough
- Defined in:
- lib/flexr/source/passthrough.rb
Constant Summary collapse
- ENCODING_COMMENT =
/\A[ \t]*#.*(?:coding|encoding)\s*[:=]\s*[-A-Za-z0-9_]+/i- FROZEN_COMMENT =
/\A[ \t]*#\s*frozen_string_literal\s*:\s*(?:true|false)\b/i
Class Method Summary collapse
- .indentation(source, offset) ⇒ Object
- .insert_after_preamble(source, payload) ⇒ Object
- .line_start_offset(source, offset) ⇒ Object
- .magic_comment?(line) ⇒ Boolean
- .next_line(source, offset) ⇒ Object
- .remove_spans(source, spans, insertion:, payload:) ⇒ Object
- .rewrite(source, edits, insertion:, payload:) ⇒ Object
- .validate_non_overlapping!(edits) ⇒ Object
Class Method Details
.indentation(source, offset) ⇒ Object
52 53 54 55 |
# File 'lib/flexr/source/passthrough.rb', line 52 def indentation(source, offset) line_start = source.rindex("\n", offset - 1) source[(line_start ? line_start + 1 : 0)...offset].to_s[/\A[ \t]*/].to_s end |
.insert_after_preamble(source, payload) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/flexr/source/passthrough.rb', line 36 def insert_after_preamble(source, payload) offset = 0 line, next_offset = next_line(source, offset) if line&.start_with?("#!") offset = next_offset line, next_offset = next_line(source, offset) end while line && magic_comment?(line) offset = next_offset line, next_offset = next_line(source, offset) end source.dup.insert(offset, payload) end |
.line_start_offset(source, offset) ⇒ Object
57 58 59 60 61 62 |
# File 'lib/flexr/source/passthrough.rb', line 57 def line_start_offset(source, offset) line_start = source.rindex("\n", offset - 1) unless offset.zero? line_start = line_start ? line_start + 1 : 0 prefix = source.byteslice(line_start...offset) prefix&.match?(/\A[ \t]*\z/) ? line_start : offset end |
.magic_comment?(line) ⇒ Boolean
71 72 73 |
# File 'lib/flexr/source/passthrough.rb', line 71 def magic_comment?(line) ENCODING_COMMENT.match?(line) || FROZEN_COMMENT.match?(line) end |
.next_line(source, offset) ⇒ Object
64 65 66 67 68 69 |
# File 'lib/flexr/source/passthrough.rb', line 64 def next_line(source, offset) return [nil, offset] if offset >= source.bytesize ending = source.index("\n", offset) ending ? [source.byteslice(offset..ending), ending + 1] : [source.byteslice(offset..), source.bytesize] end |
.remove_spans(source, spans, insertion:, payload:) ⇒ Object
11 12 13 14 |
# File 'lib/flexr/source/passthrough.rb', line 11 def remove_spans(source, spans, insertion:, payload:) rewrite(source, spans.map { |start_offset, end_offset| [start_offset, end_offset, ""] }, insertion: insertion, payload: payload) end |
.rewrite(source, edits, insertion:, payload:) ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/flexr/source/passthrough.rb', line 16 def rewrite(source, edits, insertion:, payload:) result = source.dup normalized_edits = edits.map do |start_offset, end_offset, replacement| start_offset = line_start_offset(source, start_offset) if replacement.empty? && start_offset != insertion [start_offset, end_offset, replacement] end validate_non_overlapping!(normalized_edits) adjusted_insertion = insertion + normalized_edits.sum do |start_offset, end_offset, replacement| next 0 if start_offset >= insertion replaced_size = [end_offset, insertion].min - start_offset replacement.bytesize - replaced_size end normalized_edits.sort_by(&:first).reverse_each do |start_offset, end_offset, replacement| result[start_offset...end_offset] = replacement end result.insert(adjusted_insertion, payload) result end |
.validate_non_overlapping!(edits) ⇒ Object
75 76 77 78 79 80 81 |
# File 'lib/flexr/source/passthrough.rb', line 75 def validate_non_overlapping!(edits) edits.sort_by(&:first).each_cons(2) do |left, right| next if left[1] <= right[0] raise ArgumentError, "overlapping source edits" end end |