2018-10-15 16:44:23 -05:00
|
|
|
use std::{fmt, ops};
|
2018-08-28 06:06:30 -05:00
|
|
|
|
2019-01-08 12:50:04 -06:00
|
|
|
use crate::{SyntaxNode, TextRange, TextUnit};
|
2018-08-28 06:06:30 -05:00
|
|
|
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct SyntaxText<'a> {
|
2019-01-07 07:15:47 -06:00
|
|
|
node: &'a SyntaxNode,
|
2018-08-28 06:06:30 -05:00
|
|
|
range: TextRange,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> SyntaxText<'a> {
|
2019-01-07 07:15:47 -06:00
|
|
|
pub(crate) fn new(node: &'a SyntaxNode) -> SyntaxText<'a> {
|
2018-08-28 06:06:30 -05:00
|
|
|
SyntaxText {
|
|
|
|
node,
|
2018-10-15 16:44:23 -05:00
|
|
|
range: node.range(),
|
2018-08-28 06:06:30 -05:00
|
|
|
}
|
|
|
|
}
|
2018-10-15 16:44:23 -05:00
|
|
|
pub fn chunks(&self) -> impl Iterator<Item = &'a str> {
|
2018-08-28 06:06:30 -05:00
|
|
|
let range = self.range;
|
2018-10-15 16:44:23 -05:00
|
|
|
self.node.descendants().filter_map(move |node| {
|
|
|
|
let text = node.leaf_text()?;
|
2019-01-08 12:50:04 -06:00
|
|
|
let range = range.intersection(&node.range())?;
|
2018-10-15 16:44:23 -05:00
|
|
|
let range = range - node.range().start();
|
|
|
|
Some(&text[range])
|
|
|
|
})
|
2018-08-28 06:06:30 -05:00
|
|
|
}
|
2018-08-28 14:58:02 -05:00
|
|
|
pub fn push_to(&self, buf: &mut String) {
|
|
|
|
self.chunks().for_each(|it| buf.push_str(it));
|
|
|
|
}
|
2018-08-28 06:06:30 -05:00
|
|
|
pub fn to_string(&self) -> String {
|
|
|
|
self.chunks().collect()
|
|
|
|
}
|
|
|
|
pub fn contains(&self, c: char) -> bool {
|
|
|
|
self.chunks().any(|it| it.contains(c))
|
|
|
|
}
|
|
|
|
pub fn find(&self, c: char) -> Option<TextUnit> {
|
|
|
|
let mut acc: TextUnit = 0.into();
|
|
|
|
for chunk in self.chunks() {
|
|
|
|
if let Some(pos) = chunk.find(c) {
|
|
|
|
let pos: TextUnit = (pos as u32).into();
|
|
|
|
return Some(acc + pos);
|
|
|
|
}
|
|
|
|
acc += TextUnit::of_str(chunk);
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
pub fn len(&self) -> TextUnit {
|
|
|
|
self.range.len()
|
|
|
|
}
|
|
|
|
pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
|
2018-10-15 16:44:23 -05:00
|
|
|
let range = range.restrict(self.range).unwrap_or_else(|| {
|
|
|
|
panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
|
|
|
|
});
|
|
|
|
SyntaxText {
|
|
|
|
node: self.node,
|
|
|
|
range,
|
|
|
|
}
|
2018-08-28 06:06:30 -05:00
|
|
|
}
|
2018-08-31 06:52:29 -05:00
|
|
|
pub fn char_at(&self, offset: TextUnit) -> Option<char> {
|
|
|
|
let mut start: TextUnit = 0.into();
|
|
|
|
for chunk in self.chunks() {
|
|
|
|
let end = start + TextUnit::of_str(chunk);
|
|
|
|
if start <= offset && offset < end {
|
|
|
|
let off: usize = u32::from(offset - start) as usize;
|
|
|
|
return Some(chunk[off..].chars().next().unwrap());
|
|
|
|
}
|
|
|
|
start = end;
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
2018-08-28 06:06:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Debug for SyntaxText<'a> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
fmt::Debug::fmt(&self.to_string(), f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> fmt::Display for SyntaxText<'a> {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
fmt::Display::fmt(&self.to_string(), f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait SyntaxTextSlice: fmt::Debug {
|
|
|
|
fn restrict(&self, range: TextRange) -> Option<TextRange>;
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyntaxTextSlice for TextRange {
|
|
|
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
2019-01-08 12:50:04 -06:00
|
|
|
self.intersection(&range)
|
2018-08-28 06:06:30 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyntaxTextSlice for ops::RangeTo<TextUnit> {
|
|
|
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
2019-01-08 12:50:04 -06:00
|
|
|
if !range.contains_inclusive(self.end) {
|
2018-08-28 06:06:30 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(TextRange::from_to(range.start(), self.end))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SyntaxTextSlice for ops::RangeFrom<TextUnit> {
|
|
|
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
2019-01-08 12:50:04 -06:00
|
|
|
if !range.contains_inclusive(self.start) {
|
2018-08-28 06:06:30 -05:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
Some(TextRange::from_to(self.start, range.end()))
|
|
|
|
}
|
|
|
|
}
|
2018-08-28 13:45:59 -05:00
|
|
|
|
|
|
|
impl SyntaxTextSlice for ops::Range<TextUnit> {
|
|
|
|
fn restrict(&self, range: TextRange) -> Option<TextRange> {
|
|
|
|
TextRange::from_to(self.start, self.end).restrict(range)
|
|
|
|
}
|
|
|
|
}
|
2019-01-03 09:59:17 -06:00
|
|
|
|
|
|
|
impl From<SyntaxText<'_>> for String {
|
|
|
|
fn from(text: SyntaxText) -> String {
|
|
|
|
text.to_string()
|
|
|
|
}
|
|
|
|
}
|
2019-01-05 06:28:07 -06:00
|
|
|
|
|
|
|
impl PartialEq<str> for SyntaxText<'_> {
|
|
|
|
fn eq(&self, mut rhs: &str) -> bool {
|
|
|
|
for chunk in self.chunks() {
|
|
|
|
if !rhs.starts_with(chunk) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
rhs = &rhs[chunk.len()..];
|
|
|
|
}
|
|
|
|
rhs.is_empty()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialEq<&'_ str> for SyntaxText<'_> {
|
|
|
|
fn eq(&self, rhs: &&str) -> bool {
|
|
|
|
self == *rhs
|
|
|
|
}
|
|
|
|
}
|