///|
pub(all) struct LineIndex {
starts : Array[Int]
ends : Array[Int]
text_length : Int
} derive(Eq, Debug)
///|
pub fn SourceFile::line_index(self : SourceFile) -> LineIndex {
let starts : Array[Int] = [0]
let ends : Array[Int] = []
let chars = self.text.to_array()
for index, character in chars {
if character == '\n' {
ends.push(index)
starts.push(index + 1)
}
}
ends.push(chars.length())
{ starts, ends, text_length: chars.length() }
}
///|
pub fn LineIndex::line_count(self : LineIndex) -> Int {
self.starts.length()
}
///|
pub fn LineIndex::contains_offset(self : LineIndex, offset : Int) -> Bool {
offset >= 0 && offset <= self.text_length
}
///|
pub fn LineIndex::line_at(self : LineIndex, offset : Int) -> Int {
if self.starts.length() == 0 {
return 0
}
let safe = if offset < 0 {
0
} else if offset > self.text_length {
self.text_length
} else {
offset
}
let mut result = 0
for index, start in self.starts {
if start <= safe {
result = index
}
}
result
}
///|
pub fn LineIndex::column_at(self : LineIndex, offset : Int) -> Int {
let line = self.line_at(offset)
let safe = if offset < 0 {
0
} else if offset > self.text_length {
self.text_length
} else {
offset
}
safe - self.starts[line]
}
///|
pub fn LineIndex::span_for_line(self : LineIndex, line : Int) -> Span? {
if line < 0 || line >= self.starts.length() {
None
} else {
Some(Span::new(self.starts[line], self.ends[line]))
}
}
///|
pub fn LineIndex::offset_at(self : LineIndex, line : Int, column : Int) -> Int? {
match self.span_for_line(line) {
None => None
Some(span) => {
let safe_column = if column < 0 {
0
} else if column > span.length() {
span.length()
} else {
column
}
Some(span.start + safe_column)
}
}
}
///|
pub fn LineIndex::line_starts(self : LineIndex) -> Array[Int] {
self.starts.copy()
}
///|
pub fn LineIndex::line_ends(self : LineIndex) -> Array[Int] {
self.ends.copy()
}
///|
pub fn SourceFile::offset_line_column(
self : SourceFile,
offset : Int,
) -> (Int, Int) {
let index = self.line_index()
(index.line_at(offset), index.column_at(offset))
}
///|
pub fn SourceFile::span_text(self : SourceFile, line : Int) -> String? {
match self.line_index().span_for_line(line) {
Some(span) => Some(self.slice(span).trim_end().to_owned())
None => None
}
}
///|
pub fn SourceFile::offset_after_text(self : SourceFile, text : String) -> Int? {
let chars = self.text.to_array()
let wanted = text.to_array()
if wanted.length() == 0 {
return Some(0)
}
if wanted.length() > chars.length() {
return None
}
for start in 0..<=(chars.length() - wanted.length()) {
let mut matched = true
for index in 0.. Span {
Span::new(
if left.start < right.start {
left.start
} else {
right.start
},
if left.end > right.end {
left.end
} else {
right.end
},
)
}
///|
pub fn source_spans_union(spans : Array[Span]) -> Span? {
if spans.length() == 0 {
return None
}
let mut result = spans[0]
for span in spans[1:] {
result = source_span_union(result, span)
}
Some(result)
}