Class: RBS::Rewriter

Inherits:
Object
  • Object
show all
Defined in:
sig/rewriter.rbs,
lib/rbs/rewriter.rb

Overview

Rewriter performs targeted character-range replacements on Buffer content.

Unlike Writer which regenerates entire source from AST, Rewriter preserves everything outside the rewritten ranges, including non-documentation comments.

Rewrite requests are buffered and applied all at once when #string is called.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(buffer) ⇒ Rewriter

Initialize with a toplevel buffer. Raises if the buffer has a parent (non-toplevel).

Parameters:



15
16
17
18
19
20
# File 'sig/rewriter.rbs', line 15

def initialize(buffer)
  raise "Rewriter only supports toplevel buffers" if buffer.parent

  @buffer = buffer
  @rewrites = []
end

Instance Attribute Details

#bufferBuffer (readonly)

Returns the value of attribute buffer.

Returns:



5
6
7
# File 'lib/rbs/rewriter.rb', line 5

def buffer
  @buffer
end

Instance Method Details

#add_comment(*locations, content:) ⇒ self

Add a new comment before the earliest of the given locations.

Parameters:

  • locations (Location[untyped, untyped])
  • content: (String)

Returns:

  • (self)


23
24
25
26
27
28
29
30
31
32
# File 'sig/rewriter.rbs', line 23

def add_comment(*locations, content:)
  earliest = locations.min_by(&:start_pos) or raise "At least one location is required"
  insert_pos = earliest.start_pos
  indent = " " * earliest.start_column

  formatted = format_comment(content, indent)

  loc = Location.new(buffer, insert_pos, insert_pos)
  rewrite(loc, "#{formatted}\n#{indent}")
end

#delete_comment(comment) ⇒ self

Delete an existing comment.

Parameters:

Returns:

  • (self)


31
32
33
34
35
36
37
# File 'sig/rewriter.rbs', line 31

def delete_comment(comment)
  location = comment.location or raise "Comment must have a location"
  line_start = location.start_pos - location.start_column
  line_end = location.end_pos + 1
  loc = Location.new(buffer, line_start, line_end)
  rewrite(loc, "")
end

#format_comment(content, indent) ⇒ String

Parameters:

  • content (String)
  • indent (String)

Returns:

  • (String)


63
64
65
66
67
68
# File 'lib/rbs/rewriter.rb', line 63

def format_comment(content, indent)
  content.lines.map do |line|
    line = line.chomp
    line.empty? ? "#" : "# #{line}"
  end.join("\n#{indent}")
end

#replace_comment(comment, content:) ⇒ self

Replace an existing comment's content.

Parameters:

Returns:

  • (self)


27
28
29
30
31
32
# File 'sig/rewriter.rbs', line 27

def replace_comment(comment, content:)
  location = comment.location or raise "Comment must have a location"
  indent = " " * location.start_column

  rewrite(location, format_comment(content, indent))
end

#rewrite(location, string) ⇒ self

Register a rewrite request for the given location.

Parameters:

  • location (Location[untyped, untyped])
  • string (String)

Returns:

  • (self)


19
20
21
22
23
24
25
26
27
28
# File 'sig/rewriter.rbs', line 19

def rewrite(location, string)
  @rewrites.each do |existing_location, _|
    if location.start_pos < existing_location.end_pos && existing_location.start_pos < location.end_pos
      raise "Overlapping rewrites: #{existing_location} and #{location}"
    end
  end

  @rewrites << [location, string]
  self
end

#stringString

Apply all buffered rewrites and return the resulting string.

Raises if any rewrite ranges overlap.

Returns:

  • (String)


37
38
39
40
41
42
43
44
45
# File 'sig/rewriter.rbs', line 37

def string
  result = buffer.content.dup

  @rewrites.sort_by { |location, _| location.start_pos }.reverse_each do |location, replacement|
    result[location.start_pos...location.end_pos] = replacement
  end

  result
end