// Reachable subset of Monaco/VS Code's line/column `Range`; methods with no
// product caller are deferred.
//
// Source: vscode/src/vs/editor/common/core/range.ts
// (checked-in vscode submodule).
//
// `(startLineNumber, startColumn)` is normalized to be <= `(endLineNumber,
// endColumn)`. Endpoints are held as `Position` (1-based `line_number`/`column`,
// matching Monaco); the methods here are pure coordinate comparisons.
///|
/// A range in the editor. `(start_line_number, start_column)` is <=
/// `(end_line_number, end_column)`.
pub(all) struct Range {
start_line_number : Int32
start_column : Int32
end_line_number : Int32
end_column : Int32
} derive(Eq, Debug)
///|
pub fn Range::Range(
start_line_number : Int32,
start_column : Int32,
end_line_number : Int32,
end_column : Int32,
) -> Range {
if start_line_number > end_line_number ||
(start_line_number == end_line_number && start_column > end_column) {
{
start_line_number: end_line_number,
start_column: end_column,
end_line_number: start_line_number,
end_column: start_column,
}
} else {
{ start_line_number, start_column, end_line_number, end_column, }
}
}
///|
/// Test if this range is empty.
pub fn Range::is_empty(self : Range) -> Bool {
Range::is_empty_range(self)
}
///|
/// Test if `range` is empty.
fn Range::is_empty_range(range : Range) -> Bool {
range.start_line_number == range.end_line_number &&
range.start_column == range.end_column
}
///|
/// Test if position is in this range. If the position is at the edges, returns true.
fn Range::contains_position_impl(self : Range, position : Position) -> Bool {
Range::contains_position_in(self, position)
}
///|
/// Test if position is in this range. If the position is at the edges, returns true.
pub fn Range::contains_position(self : Range, position : Position) -> Bool {
self.contains_position_impl(position)
}
///|
fn Range::contains_position_in(range : Range, position : Position) -> Bool {
if position.line_number < range.start_line_number ||
position.line_number > range.end_line_number {
return false
}
if position.line_number == range.start_line_number &&
position.column < range.start_column {
return false
}
if position.line_number == range.end_line_number &&
position.column > range.end_column {
return false
}
true
}
///|
/// Test if `other` is in this range. If equal, returns true.
fn Range::contains_range_impl(self : Range, other : Range) -> Bool {
Range::contains_range_in(self, other)
}
///|
fn Range::contains_range_in(range : Range, other_range : Range) -> Bool {
if other_range.start_line_number < range.start_line_number ||
other_range.end_line_number < range.start_line_number {
return false
}
if other_range.start_line_number > range.end_line_number ||
other_range.end_line_number > range.end_line_number {
return false
}
if other_range.start_line_number == range.start_line_number &&
other_range.start_column < range.start_column {
return false
}
if other_range.end_line_number == range.end_line_number &&
other_range.end_column > range.end_column {
return false
}
true
}
///|
/// Test if `other` is strictly in this range (must start after and end before).
fn Range::strict_contains_range_impl(self : Range, other : Range) -> Bool {
Range::strict_contains_range_in(self, other)
}
///|
fn Range::strict_contains_range_in(range : Range, other_range : Range) -> Bool {
if other_range.start_line_number < range.start_line_number ||
other_range.end_line_number < range.start_line_number {
return false
}
if other_range.start_line_number > range.end_line_number ||
other_range.end_line_number > range.end_line_number {
return false
}
if other_range.start_line_number == range.start_line_number &&
other_range.start_column <= range.start_column {
return false
}
if other_range.end_line_number == range.end_line_number &&
other_range.end_column >= range.end_column {
return false
}
true
}
///|
/// A reunion of the two ranges (smallest start, largest end).
fn Range::plus_range_impl(self : Range, other : Range) -> Range {
Range::plus_range_of(self, other)
}
///|
/// A reunion of the two ranges (smallest start, largest end).
pub fn Range::plus_range(self : Range, other : Range) -> Range {
self.plus_range_impl(other)
}
///|
fn Range::plus_range_of(a : Range, b : Range) -> Range {
let mut start_line_number = 0
let mut start_column = 0
let mut end_line_number = 0
let mut end_column = 0
if b.start_line_number < a.start_line_number {
start_line_number = b.start_line_number
start_column = b.start_column
} else if b.start_line_number == a.start_line_number {
start_line_number = b.start_line_number
start_column = b.start_column.min(a.start_column)
} else {
start_line_number = a.start_line_number
start_column = a.start_column
}
if b.end_line_number > a.end_line_number {
end_line_number = b.end_line_number
end_column = b.end_column
} else if b.end_line_number == a.end_line_number {
end_line_number = b.end_line_number
end_column = b.end_column.max(a.end_column)
} else {
end_line_number = a.end_line_number
end_column = a.end_column
}
Range(start_line_number, start_column, end_line_number, end_column)
}
///|
/// An intersection of the two ranges, or `None` if they do not intersect.
fn Range::intersect_ranges_impl(self : Range, other : Range) -> Range? {
Range::intersect_ranges_of(self, other)
}
///|
/// An intersection of the two ranges, or `None` if they do not intersect.
pub fn Range::intersect_ranges(self : Range, other : Range) -> Range? {
self.intersect_ranges_impl(other)
}
///|
fn Range::intersect_ranges_of(a : Range, b : Range) -> Range? {
let mut result_start_line_number = a.start_line_number
let mut result_start_column = a.start_column
let mut result_end_line_number = a.end_line_number
let mut result_end_column = a.end_column
let other_start_line_number = b.start_line_number
let other_start_column = b.start_column
let other_end_line_number = b.end_line_number
let other_end_column = b.end_column
if result_start_line_number < other_start_line_number {
result_start_line_number = other_start_line_number
result_start_column = other_start_column
} else if result_start_line_number == other_start_line_number {
result_start_column = result_start_column.max(other_start_column)
}
if result_end_line_number > other_end_line_number {
result_end_line_number = other_end_line_number
result_end_column = other_end_column
} else if result_end_line_number == other_end_line_number {
result_end_column = result_end_column.min(other_end_column)
}
if result_start_line_number > result_end_line_number {
return None
}
if result_start_line_number == result_end_line_number &&
result_start_column > result_end_column {
return None
}
Some(
Range(
result_start_line_number, result_start_column, result_end_line_number, result_end_column,
),
)
}
///|
/// The start position (before or equal to the end position).
pub fn Range::get_start_position(self : Range) -> Position {
Position(self.start_line_number, self.start_column)
}
///|
/// New range with this range's start and the given end position.
pub fn Range::set_end_position(
self : Range,
end_line_number : Int32,
end_column : Int32,
) -> Range {
Range(self.start_line_number, self.start_column, end_line_number, end_column)
}
///|
/// Build a range from two positions.
pub fn Range::from_positions(start : Position, end : Position) -> Range {
Range(start.line_number, start.column, end.line_number, end.column)
}
///|
/// Test if the two ranges are touching in any way.
pub fn Range::are_intersecting_or_touching(a : Range, b : Range) -> Bool {
if a.end_line_number < b.start_line_number ||
(a.end_line_number == b.start_line_number && a.end_column < b.start_column) {
return false
}
if b.end_line_number < a.start_line_number ||
(b.end_line_number == a.start_line_number && b.end_column < a.start_column) {
return false
}
true
}
///|
/// Test if the two ranges are intersecting. Touching ranges are not intersecting.
fn Range::are_intersecting_impl(a : Range, b : Range) -> Bool {
if a.end_line_number < b.start_line_number ||
(a.end_line_number == b.start_line_number && a.end_column <= b.start_column) {
return false
}
if b.end_line_number < a.start_line_number ||
(b.end_line_number == a.start_line_number && b.end_column <= a.start_column) {
return false
}
true
}
///|
/// Compare ranges by start position, then end position.
pub fn Range::compare_ranges_using_starts(a : Range, b : Range) -> Int32 {
if a.start_line_number == b.start_line_number {
if a.start_column == b.start_column {
if a.end_line_number == b.end_line_number {
return a.end_column - b.end_column
}
return a.end_line_number - b.end_line_number
}
return a.start_column - b.start_column
}
a.start_line_number - b.start_line_number
}
///|
/// Transform to a user-presentable string representation.
pub fn Range::to_string(self : Range) -> String {
"[\{self.start_line_number},\{self.start_column} -> \{self.end_line_number},\{self.end_column}]"
}
///|
pub extend Range with Debug::{to_repr}
///|
pub extend Range with Eq::{equal, not_equal}