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

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

Returns:

  • (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