use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive};
use bincode::{Decode, Encode};
macro_rules! implPartialEq {
($self:ident: $Self:ty, $other:ident: $Other:ty, $($impl:tt)+) => {
impl PartialEq<$Other> for $Self {
fn eq(&self, other: &$Other) -> bool {
let ($self, $other) = (&self, other);
$($impl)+
}
}
}
}
pub(super) use implPartialEq;
use crate::text::Strs;
macro_rules! implTextRange {
($range:ident, $r:ident, $max:ident, $sb:expr, $eb:expr, $sp:expr, $ep:expr) => {
impl TextRange for $range<usize> {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
let $max = max;
let $r = self;
$crate::utils::get_range($sb..$eb, max)
}
fn try_to_range(self, max: usize) -> Option<Range<usize>> {
let $max = max;
let $r = self;
$crate::utils::try_get_range($sb..$eb, max)
}
}
impl TextRange for $range<Point> {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
let $max = max;
let $r = self;
$crate::utils::get_range($sp..$ep, max)
}
fn try_to_range(self, max: usize) -> Option<Range<usize>> {
let $max = max;
let $r = self;
$crate::utils::try_get_range($sp..$ep, max)
}
}
};
}
macro_rules! implTextRangeOrIndex {
($range:ident) => {
impl TextRangeOrIndex for $range<usize> {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
TextRange::to_range(self, max)
}
}
impl TextRangeOrIndex for $range<Point> {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
TextRange::to_range(self, max)
}
}
};
}
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
pub struct Point {
byte: u32,
char: u32,
line: u32,
}
impl Point {
pub const fn new() -> Self {
Point { byte: 0, char: 0, line: 0 }
}
pub const fn from_raw(b: usize, c: usize, l: usize) -> Self {
let (b, c, l) = (b as u32, c as u32, l as u32);
Self { byte: b, char: c, line: l }
}
pub const fn to_two_points_before(self) -> TwoPoints {
TwoPoints::new_before_ghost(self)
}
pub const fn to_two_points_after(self) -> TwoPoints {
TwoPoints::new_after_ghost(self)
}
pub fn end_point_of(str: impl AsRef<str>) -> Self {
let str = str.as_ref();
Self {
byte: str.len() as u32,
char: str.chars().count() as u32,
line: str.bytes().filter(|c| *c == b'\n').count() as u32,
}
}
pub const fn byte(&self) -> usize {
self.byte as usize
}
pub const fn char(&self) -> usize {
self.char as usize
}
pub const fn line(&self) -> usize {
self.line as usize
}
pub fn byte_col(&self, strs: &Strs) -> usize {
self.byte() - strs.point_at_coords(self.line(), 0).byte()
}
pub fn char_col(&self, strs: &Strs) -> usize {
self.char() - strs.point_at_coords(self.line(), 0).char()
}
pub fn checked_sub(self, rhs: Point) -> Option<Point> {
Some(Self {
byte: self.byte.checked_sub(rhs.byte)?,
char: self.char.checked_sub(rhs.char)?,
line: self.line.checked_sub(rhs.line)?,
})
}
#[inline(always)]
pub(crate) const fn fwd(self, char: char) -> Self {
Self {
byte: self.byte + char.len_utf8() as u32,
char: self.char + 1,
line: self.line + (char == '\n') as u32,
}
}
#[inline(always)]
pub(crate) const fn rev(self, char: char) -> Self {
Self {
byte: self.byte - char.len_utf8() as u32,
char: self.char - 1,
line: self.line - (char == '\n') as u32,
}
}
pub(crate) const fn shift_by(self, [b, c, l]: [i32; 3]) -> Self {
Self {
byte: (self.byte as i32 + b) as u32,
char: (self.char as i32 + c) as u32,
line: (self.line as i32 + l) as u32,
}
}
pub(crate) const fn as_signed(self) -> [i32; 3] {
[self.byte as i32, self.char as i32, self.line as i32]
}
}
impl std::fmt::Debug for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Point {{ b: {}, c: {}, l: {} }}",
self.byte, self.char, self.line
)
}
}
impl std::fmt::Display for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}, {}, {}", self.byte, self.char, self.line)
}
}
impl std::ops::Add for Point {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
byte: self.byte + rhs.byte,
char: self.char + rhs.char,
line: self.line + rhs.line,
}
}
}
impl std::ops::AddAssign for Point {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl std::ops::Sub for Point {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
Self {
byte: self.byte - rhs.byte,
char: self.char - rhs.char,
line: self.line - rhs.line,
}
}
}
impl std::ops::SubAssign for Point {
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
#[doc(hidden)]
pub trait TextIndex: Clone + Copy + std::fmt::Debug {
fn to_byte_index(self) -> usize;
}
impl TextIndex for Point {
fn to_byte_index(self) -> usize {
self.byte()
}
}
impl TextIndex for usize {
fn to_byte_index(self) -> usize {
self
}
}
#[doc(hidden)]
pub trait TextRange: Clone + std::fmt::Debug {
fn to_range(self, max: usize) -> Range<usize>;
fn try_to_range(self, max: usize) -> Option<Range<usize>>;
}
implTextRange!(Range, r, _max, r.start, r.end, r.start.byte(), r.end.byte());
implTextRange!(
RangeInclusive,
r,
_max,
*r.start(),
r.end() + 1,
r.start().byte(),
r.end().byte() + 1
);
implTextRange!(RangeTo, r, _max, 0, r.end, 0, r.end.byte());
implTextRange!(RangeToInclusive, r, _max, 0, r.end, 0, r.end.byte());
implTextRange!(RangeFrom, r, max, r.start, max, r.start.byte(), max);
impl TextRange for RangeFull {
fn to_range(self, max: usize) -> Range<usize> {
0..max
}
fn try_to_range(self, max: usize) -> Option<Range<usize>> {
Some(0..max)
}
}
#[doc(hidden)]
pub trait TextRangeOrIndex {
fn to_range(self, max: usize) -> Range<usize>;
}
impl TextRangeOrIndex for usize {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
crate::utils::get_range(self..self + 1, max)
}
}
impl TextRangeOrIndex for Point {
#[track_caller]
fn to_range(self, max: usize) -> Range<usize> {
crate::utils::get_range(self.byte()..self.byte() + 1, max)
}
}
impl TextRangeOrIndex for RangeFull {
fn to_range(self, max: usize) -> Range<usize> {
0..max
}
}
implTextRangeOrIndex!(Range);
implTextRangeOrIndex!(RangeInclusive);
implTextRangeOrIndex!(RangeTo);
implTextRangeOrIndex!(RangeToInclusive);
implTextRangeOrIndex!(RangeFrom);
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Encode, Decode, Hash)]
pub struct TwoPoints {
pub real: Point,
pub ghost: Option<Point>,
}
impl TwoPoints {
pub const fn new(real: Point, ghost: Point) -> Self {
Self { real, ghost: Some(ghost) }
}
pub const fn new_before_ghost(real: Point) -> Self {
Self { real, ghost: Some(Point::new()) }
}
pub const fn new_after_ghost(real: Point) -> Self {
Self { real, ghost: None }
}
}
impl std::cmp::PartialOrd for TwoPoints {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for TwoPoints {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match self.real.cmp(&other.real) {
core::cmp::Ordering::Equal => {}
ord => return ord,
}
match (&self.ghost, &other.ghost) {
(Some(l), Some(r)) => l.cmp(r),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => std::cmp::Ordering::Equal,
}
}
}
#[inline]
pub const fn utf8_char_width(b: u8) -> u32 {
const UTF8_CHAR_WIDTH: &[u8; 256] = &[
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
UTF8_CHAR_WIDTH[b as usize] as u32
}