use crate::syntax::{SyntaxElement, SyntaxKind, SyntaxNode};
use rowan::TextRange;
use rowan::TextSize;
pub(crate) mod iter;
pub(crate) mod shared;
mod escape;
pub mod syntax;
pub use escape::check_escape;
pub use escape::{escape, unescape};
pub(crate) mod allowed_chars {
pub(crate) fn comment(s: &str) -> Result<(), Vec<usize>> {
let mut err_indices = Vec::new();
for (i, c) in s.chars().enumerate() {
if c != '\t' && c.is_control() {
err_indices.push(i);
}
}
if err_indices.is_empty() {
Ok(())
} else {
Err(err_indices)
}
}
pub(crate) fn string(s: &str) -> Result<(), Vec<usize>> {
let mut err_indices = Vec::new();
for (i, c) in s.chars().enumerate() {
if c != '\t'
&& (('\u{0000}'..='\u{0008}').contains(&c)
|| ('\u{000A}'..='\u{001F}').contains(&c)
|| c == '\u{007F}')
{
err_indices.push(i);
}
}
if err_indices.is_empty() {
Ok(())
} else {
Err(err_indices)
}
}
pub(crate) fn multi_line_string(s: &str) -> Result<(), Vec<usize>> {
let mut err_indices = Vec::new();
for (i, c) in s.chars().enumerate() {
if c != '\t'
&& c != '\n'
&& c != '\r'
&& (('\u{0000}'..='\u{0008}').contains(&c)
|| ('\u{000A}'..='\u{001F}').contains(&c)
|| c == '\u{007F}')
{
err_indices.push(i);
}
}
if err_indices.is_empty() {
Ok(())
} else {
Err(err_indices)
}
}
pub(crate) fn string_literal(s: &str) -> Result<(), Vec<usize>> {
let mut err_indices = Vec::new();
for (i, c) in s.chars().enumerate() {
if c != '\t' && c.is_control() {
err_indices.push(i);
}
}
if err_indices.is_empty() {
Ok(())
} else {
Err(err_indices)
}
}
pub(crate) fn multi_line_string_literal(s: &str) -> Result<(), Vec<usize>> {
let mut err_indices = Vec::new();
for (i, c) in s.chars().enumerate() {
if c != '\t' && c != '\n' && c != '\r' && c.is_control() {
err_indices.push(i);
}
}
if err_indices.is_empty() {
Ok(())
} else {
Err(err_indices)
}
}
}
pub trait StrExt {
fn strip_quotes(self) -> Self;
}
impl StrExt for &str {
fn strip_quotes(self) -> Self {
if self.starts_with('\"') || self.starts_with('\'') {
&self[1..self.len() - 1]
} else {
self
}
}
}
pub trait SyntaxExt {
fn find_node(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode>;
fn find_node_deep(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode> {
let mut node = self.find_node(offset, inclusive);
while let Some(n) = &node {
let new_node = n.find_node(offset, inclusive);
if new_node.is_some() {
node = new_node;
} else {
break;
}
}
node
}
fn find(&self, kind: SyntaxKind) -> Option<SyntaxElement>;
}
impl SyntaxExt for SyntaxNode {
fn find_node(&self, offset: TextSize, inclusive: bool) -> Option<SyntaxNode> {
for d in self.descendants().skip(1) {
let range = d.text_range();
if (inclusive && range.contains_inclusive(offset)) || range.contains(offset) {
return Some(d);
}
}
None
}
fn find(&self, kind: SyntaxKind) -> Option<SyntaxElement> {
self.descendants_with_tokens().find(|d| d.kind() == kind)
}
}
pub fn join_ranges<I: IntoIterator<Item = TextRange>>(ranges: I) -> TextRange {
ranges
.into_iter()
.fold(None, |ranges, range| match ranges {
Some(r) => Some(range.cover(r)),
None => Some(range),
})
.unwrap()
}
pub fn try_join_ranges<I: IntoIterator<Item = TextRange>>(ranges: I) -> Option<TextRange> {
ranges.into_iter().fold(None, |ranges, range| match ranges {
Some(r) => Some(range.cover(r)),
None => Some(range),
})
}
pub fn overlaps(range: TextRange, other: TextRange) -> bool {
range.contains_range(other)
|| other.contains_range(range)
|| range.contains(other.start())
|| range.contains(other.end())
|| other.contains(range.start())
|| other.contains(range.end())
}