use crate::core::query::Queries;
use futures::Future;
use lazy_static::lazy_static;
use ropey::Rope;
use std::error;
use std::fmt::Display;
use tokio::select;
use tokio_util::sync::CancellationToken;
use tower_lsp::lsp_types::{Position, Range, Url};
use tree_sitter::{Language, Node};
pub struct ParseConstants {
pub queries: Queries,
pub uvl: Language,
pub json: Language,
}
impl ParseConstants {
pub fn new() -> ParseConstants {
let lang = tree_sitter_uvl::language();
let queries = Queries::new(&lang);
ParseConstants {
queries,
uvl: lang,
json: tree_sitter_json::language(),
}
}
}
lazy_static! {
pub static ref TS: ParseConstants = ParseConstants::new();
}
pub fn node_source(source: &Rope) -> impl tree_sitter::TextProvider<'_> {
|node: tree_sitter::Node| {
source
.byte_slice(node.byte_range())
.chunks()
.map(|i: &str| i.as_bytes())
}
}
pub fn node_range(node: Node, rope: &Rope) -> Range {
lsp_range(node.byte_range(), rope).unwrap()
}
pub fn lsp_position(byte: usize, source: &Rope) -> Option<Position> {
if byte == source.len_bytes() {
Some(Position {
line: (source.len_lines() - 1) as u32,
character: source.line(source.len_lines() - 1).len_utf16_cu() as u32,
})
} else if byte > source.len_bytes() {
None
} else {
let line = source.byte_to_line(byte);
let col = source.byte_to_char(byte) - source.line_to_char(line);
let col16 = source.line(line).char_to_utf16_cu(col);
Some(Position {
line: line as u32,
character: col16 as u32,
})
}
}
pub fn lsp_range(span: std::ops::Range<usize>, source: &Rope) -> Option<Range> {
lsp_position(span.start, source)
.and_then(|start| lsp_position(span.end, source).map(|end| Range { start, end }))
}
pub fn char_offset(pos: &Position, source: &Rope) -> usize {
if let Some(line) = source.get_line(pos.line as usize) {
if let Ok(end) = line.try_utf16_cu_to_char(pos.character as usize) {
source.line_to_char(pos.line as usize) + end
} else {
source.line_to_char(pos.line as usize) + line.len_chars()
}
} else {
source.len_chars()
}
}
pub fn byte_offset(pos: &Position, source: &Rope) -> usize {
source.char_to_byte(char_offset(pos, source))
}
pub fn containing_blk(mut node: Node) -> Option<Node> {
node = node.parent()?;
while node.kind() != "blk" {
node = node.parent()?
}
Some(node)
}
pub fn header_kind(node: Node) -> &str {
node.child_by_field_name("header").unwrap().kind()
}
pub type Result<T> = std::result::Result<T, Box<dyn error::Error + Send + Sync>>;
#[derive(Debug)]
struct CancelledError {}
impl Display for CancelledError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "cancelled")
}
}
impl error::Error for CancelledError {}
pub async fn maybe_cancel<'a, F: Future + 'a>(
token: &CancellationToken,
f: F,
) -> Result<F::Output> {
let token = token.clone();
select! {
_ = token.cancelled() => Err(CancelledError{})?,
out = f => Ok(out)
}
}
pub fn is_config(uri: &Url) -> bool {
uri.to_file_path()
.map(|fp| {
fp.extension()
.map(|ext| ext.to_str().unwrap() == "json")
.unwrap_or(false)
})
.unwrap_or(false)
}
pub fn create_new_uvl(old_uri: String, path: String) -> Option<Url> {
if let Some(name) = old_uri.split("/").collect::<Vec<&str>>().last() {
if let Some(dir) = old_uri.strip_suffix(name) {
let mut new_uri = dir.to_string();
new_uri.push_str(path.as_str());
if let Ok(url) = Url::parse(new_uri.as_str()) {
return Some(url);
}
}
}
None
}