Class: Ibex::Runtime::CST::SourceText
- Inherits:
-
Object
- Object
- Ibex::Runtime::CST::SourceText
- Defined in:
- lib/ibex/runtime/cst/source_text.rb
Overview
Immutable byte-oriented source with lazy line/column conversion.
Instance Attribute Summary collapse
-
#file ⇒ Object
readonly
: String?.
-
#text ⇒ Object
readonly
@rbs! type position = [Integer, Integer].
Instance Method Summary collapse
-
#apply(edits) ⇒ Object
Apply non-overlapping byte edits expressed against this source.
- #bytesize ⇒ Object
-
#initialize(text, file: nil) ⇒ SourceText
constructor
A new instance of SourceText.
- #line_text(line) ⇒ Object
- #location(range) ⇒ Object
-
#position(offset) ⇒ Object
Return one-based line and Unicode-scalar column for a byte offset.
Constructor Details
#initialize(text, file: nil) ⇒ SourceText
Returns a new instance of SourceText.
15 16 17 18 19 20 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 15 def initialize(text, file: nil) @text = text.encoding == Encoding::BINARY && text.frozen? ? text : text.b.freeze @file = file&.then { |value| value.frozen? ? value : value.dup.freeze } @line_starts = build_line_starts.freeze #: Array[Integer] freeze end |
Instance Attribute Details
#file ⇒ Object (readonly)
: String?
12 13 14 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 12 def file @file end |
#text ⇒ Object (readonly)
@rbs! type position = [Integer, Integer]
11 12 13 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 11 def text @text end |
Instance Method Details
#apply(edits) ⇒ Object
Apply non-overlapping byte edits expressed against this source.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 27 def apply(edits) ordered = TextEdit.normalize(edits) output = String.new(encoding: Encoding::BINARY) cursor = 0 ordered.each do |edit| raise ArgumentError, "text edits overlap" if edit.start < cursor raise RangeError, "text edit exceeds source" if edit.range.end > @text.bytesize output << (@text.byteslice(cursor, edit.start - cursor) || "".b) output << edit.insert_text cursor = edit.range.end end output << (@text.byteslice(cursor, @text.bytesize - cursor) || "".b) SourceText.new(output, file: @file) end |
#bytesize ⇒ Object
23 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 23 def bytesize = @text.bytesize |
#line_text(line) ⇒ Object
68 69 70 71 72 73 74 75 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 68 def line_text(line) raise ArgumentError, "line must be positive" unless line.positive? start_offset = @line_starts.fetch(line - 1) end_offset = @line_starts.fetch(line, @text.bytesize) value = @text.byteslice(start_offset, end_offset - start_offset) || "".b value.delete_suffix("\n".b).delete_suffix("\r".b) end |
#location(range) ⇒ Object
57 58 59 60 61 62 63 64 65 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 57 def location(range) start_offset, end_offset = normalized_range(range) line, column = position(start_offset) end_line, end_column = position(end_offset) Ibex::Location.new( file: @file, line: line, column: column, end_line: end_line, end_column: end_column, start_byte: start_offset, end_byte: end_offset, source_line: line_text(line) ) end |
#position(offset) ⇒ Object
Return one-based line and Unicode-scalar column for a byte offset. Invalid UTF-8 bytes count as one replacement scalar each.
46 47 48 49 50 51 52 53 54 |
# File 'lib/ibex/runtime/cst/source_text.rb', line 46 def position(offset) validate_offset(offset) line_index = @line_starts.bsearch_index { |start| start > offset } line_index = line_index ? line_index - 1 : @line_starts.length - 1 line_start = @line_starts.fetch(line_index) prefix = @text.byteslice(line_start, offset - line_start) || "".b value = [line_index + 1, unicode_scalar_length(prefix) + 1] #: position value.freeze end |