#![warn(missing_docs)]
pub struct TextEditHelpers<'a> {
source: &'a str,
lines: &'a [String],
}
impl<'a> TextEditHelpers<'a> {
#[must_use]
pub fn new(source: &'a str, lines: &'a [String]) -> Self {
Self { source, lines }
}
#[must_use]
pub fn lines(&self) -> &'a [String] {
self.lines
}
#[must_use]
pub fn find_statement_start(&self, pos: usize) -> usize {
let after_semi = self
.source
.char_indices()
.take_while(|(idx, _)| *idx < pos)
.filter(|(_, ch)| *ch == ';')
.map(|(idx, _)| idx + 1)
.last()
.unwrap_or(0);
if self.source.as_bytes().get(after_semi) == Some(&b'\n') {
after_semi + 1
} else {
after_semi
}
}
#[must_use]
pub fn find_subroutine_insert_position(&self, current_pos: usize) -> usize {
let search_end = current_pos.min(self.source.len());
self.source[..search_end].rfind("sub ").unwrap_or(self.source.len())
}
#[must_use]
pub fn find_pragma_insert_position(&self) -> usize {
if self.source.starts_with("#!")
&& let Some(pos) = self.source.find('\n')
{
return pos + 1;
}
0
}
#[must_use]
pub fn find_import_insert_position(&self) -> usize {
let mut pos = self.find_pragma_insert_position();
for line in self.lines {
if line.starts_with("use ") || line.starts_with("require ") {
pos = self.source.find(line).unwrap_or(0) + line.len() + 1;
} else if !line.is_empty() && !line.starts_with('#') {
break;
}
}
pos
}
#[must_use]
pub fn get_indent_at(&self, pos: usize) -> String {
let safe_pos = pos.min(self.source.len());
let line_start = self.source[..safe_pos].rfind('\n').map_or(0, |p| p + 1);
self.source[line_start..].chars().take_while(|ch| *ch == ' ' || *ch == '\t').collect()
}
#[must_use]
pub fn truncate_expr(&self, expr: &str, max_len: usize) -> String {
if expr.chars().count() <= max_len {
return expr.to_string();
}
if max_len <= 3 {
return "...".to_string();
}
format!("{}...", expr.chars().take(max_len - 3).collect::<String>())
}
#[must_use]
pub fn has_non_ascii_content(&self) -> bool {
!self.source.is_ascii()
}
}