use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::Range;
use std::sync::{Arc, RwLock};
use log::{debug, error, warn};
use ropey::Rope;
use streaming_iterator::StreamingIterator;
use tree_sitter::{InputEdit, Language, Parser, Point, Query, QueryCursor};
use crate::tsw::lang_id::LangId;
use crate::tsw::parsing_tuple::ParsingTuple;
use crate::tsw::rope_wrappers::RopeWrapper;
use crate::unpack_or_e;
static EMPTY_SLICE: [u8; 0] = [0; 0];
pub fn byte_offset_to_point(rope: &Rope, byte_offset: usize) -> Option<Point> {
let char_idx = rope.try_byte_to_char(byte_offset).ok()?;
let line_idx = rope.try_char_to_line(char_idx).ok()?;
let line_begin_char_idx = rope.try_line_to_char(line_idx).ok()?;
if char_idx < line_begin_char_idx {
None
} else {
let column_idx = char_idx - line_begin_char_idx;
Some(Point::new(line_idx, column_idx))
}
}
pub fn pack_rope_with_callback<'a>(rope: &'a Rope) -> Box<dyn FnMut(usize, Point) -> &'a [u8] + 'a> {
return Box::new(move |offset: usize, point: Point| {
debug!("request to parse point {:?}, offset {:?}", point, offset);
if offset >= rope.len_bytes() {
return &EMPTY_SLICE;
}
let point_from_offset = match byte_offset_to_point(rope, offset) {
Some(point) => point,
None => return &EMPTY_SLICE,
};
if point != point_from_offset {
error!(
"byte offset diverted from point. Point is {},{} and offset {}:{},{}",
point.column, point.row, offset, point_from_offset.column, point_from_offset.row
);
}
let (chunk, chunk_byte_idx, _, _) = rope.chunk_at_byte(offset);
let chunk_as_bytes = chunk.as_bytes();
debug_assert!(offset >= chunk_byte_idx); let cut_from_beginning = offset - chunk_byte_idx;
let result = &chunk_as_bytes[cut_from_beginning..];
debug!("parser reading bytes [{}-{}) |{}|", offset, offset + result.len(), result.len());
result
});
}
#[derive(Debug)]
pub struct TreeSitterTuple {
pub lang_id: LangId,
pub language: Language,
pub highlighter_query: String,
pub indent_query: Option<String>,
}
#[derive(Debug)]
pub struct TreeSitterWrapper {
languages: HashMap<LangId, TreeSitterTuple>,
}
impl TreeSitterWrapper {
pub fn new(languages: HashMap<LangId, TreeSitterTuple>) -> TreeSitterWrapper {
TreeSitterWrapper { languages }
}
pub fn indent_query(&self, lang_id: LangId) -> Option<&str> {
self.languages.get(&lang_id)?.indent_query.as_deref()
}
pub fn new_parse(&self, lang_id: LangId) -> Option<ParsingTuple> {
let tuple = self.languages.get(&lang_id)?;
let mut parser = Parser::new();
match parser.set_language(&tuple.language) {
Ok(_) => {}
Err(e) => {
error!("failed setting language: {}", e);
return None;
}
};
let highlight_query = match Query::new(&tuple.language, &tuple.highlighter_query) {
Ok(q) => q,
Err(e) => {
error!("failed compiling highlight query: {}", e);
return None;
}
};
let indent_query = match self.indent_query(lang_id) {
None => None,
Some(query_string) => match Query::new(&tuple.language, query_string) {
Ok(q) => Some(q),
Err(e) => {
error!("failed compiling indent query: {}", e);
None
}
},
};
let id_to_name: Vec<Arc<String>> = highlight_query.capture_names().iter().map(|cn| Arc::new(cn.to_string())).collect();
Some(ParsingTuple {
tree: None,
lang_id,
parser: Arc::new(RwLock::new(parser)),
language: tuple.language.clone(),
highlight_query: Arc::new(highlight_query),
indent_query: indent_query.map(Arc::new),
id_to_name: Arc::new(id_to_name),
})
}
}
#[derive(Clone, Debug)]
pub struct HighlightItem {
pub char_begin: usize,
pub char_end: usize,
pub identifier: Arc<String>,
}
impl ParsingTuple {
pub fn highlight_iter<'a>(&'a self, rope: &'a ropey::Rope, char_range_op: Option<Range<usize>>) -> Option<Vec<HighlightItem>> {
if self.tree.is_none() {
return None;
}
let mut cursor = QueryCursor::new();
if let Some(char_range) = char_range_op {
let begin_byte = rope.try_char_to_byte(char_range.start).ok()?;
let end_byte = rope.try_char_to_byte(char_range.end).ok()?;
cursor.set_byte_range(begin_byte..end_byte);
};
let mut query_matches = cursor.matches(&self.highlight_query, self.tree.as_ref()?.root_node(), RopeWrapper(&rope));
let mut results: Vec<HighlightItem> = vec![];
while let Some(m) = query_matches.next() {
if m.captures.len() != 1 {
warn!("unexpected number of captures (expected 1, got {})", m.captures.len());
}
for c in m.captures {
let begin_char = rope.try_byte_to_char(c.node.start_byte()).ok()?;
let end_char = rope.try_byte_to_char(c.node.end_byte()).ok()?;
let name = self.id_to_name.get(c.index as usize)?;
results.push(HighlightItem {
char_begin: begin_char,
char_end: end_char,
identifier: name.clone(),
})
}
}
debug!("highlight result size = {}", results.len());
Some(results)
}
pub fn try_reparse(&mut self, rope: &ropey::Rope) -> bool {
let mut callback = pack_rope_with_callback(rope);
let mut parser = unpack_or_e!(self.parser.try_write().ok(), false, "failed to lock parser");
debug!("doing reparse, tree = {:?}", self.tree);
let tree = unpack_or_e!(
parser.parse_with_options(&mut callback, self.tree.as_ref(), None),
false,
"failed parse"
);
self.tree = Some(tree);
true
}
pub fn update_parse_on_insert(&mut self, rope: &ropey::Rope, char_idx_begin: usize, char_idx_end: usize) -> bool {
if rope.len_chars() < char_idx_begin {
error!("rope.len_chars() < char_idx_begin: {} >= {}", rope.len_chars(), char_idx_begin);
return false;
}
let start_byte = match rope.try_char_to_byte(char_idx_begin) {
Ok(byte) => byte,
_ => return false,
};
let new_end_byte = match rope.try_char_to_byte(char_idx_end) {
Ok(byte) => byte,
_ => return false,
};
let start_point = match byte_offset_to_point(&rope, start_byte) {
Some(point) => point,
None => return false,
};
let new_end_point = match byte_offset_to_point(&rope, new_end_byte) {
Some(point) => point,
None => return false,
};
let input_edit = InputEdit {
start_byte,
old_end_byte: start_byte,
new_end_byte,
start_position: start_point,
old_end_position: start_point,
new_end_position: new_end_point,
};
self.tree.as_mut().map(|tree| tree.edit(&input_edit));
self.try_reparse(rope)
}
pub fn update_parse_on_delete(&mut self, rope: &ropey::Rope, char_idx_begin: usize, char_idx_end: usize) -> bool {
if char_idx_begin >= char_idx_end {
error!("char_idx_begin >= char_idx_end: {} >= {}", char_idx_begin, char_idx_end);
return false;
}
if rope.len_chars() < char_idx_begin {
error!("rope.len_chars() < char_idx_begin: {} >= {}", rope.len_chars(), char_idx_begin);
return false;
}
let start_byte = match rope.try_char_to_byte(char_idx_begin) {
Ok(byte) => byte,
_ => return false,
};
let old_end_byte = match rope.try_char_to_byte(char_idx_end) {
Ok(byte) => byte,
_ => return false,
};
let start_point = match byte_offset_to_point(&rope, start_byte) {
Some(point) => point,
None => return false,
};
let old_end_point = match byte_offset_to_point(&rope, old_end_byte) {
Some(point) => point,
None => return false,
};
let input_edit = InputEdit {
start_byte,
old_end_byte,
new_end_byte: start_byte,
start_position: start_point,
old_end_position: old_end_point,
new_end_position: start_point,
};
self.tree.as_mut().map(|tree| tree.edit(&input_edit));
self.try_reparse(rope)
}
}