use crate::languages::{LanguageCommon, LanguageName, traits::LanguageEditor};
use anyhow::Result;
use rustpython_parser::ast::TextSize;
pub fn language() -> Result<LanguageCommon> {
let language = tree_sitter_python::LANGUAGE.into();
let editor = Box::new(PythonEditor);
Ok(LanguageCommon {
name: LanguageName::Python,
file_extensions: &["py", "pyi"],
language,
editor,
validation_query: None,
})
}
pub struct PythonEditor;
impl PythonEditor {
pub fn new() -> Self {
Self
}
}
impl Default for PythonEditor {
fn default() -> Self {
Self::new()
}
}
impl LanguageEditor for PythonEditor {
fn collect_errors(&self, _tree: &tree_sitter::Tree, content: &str) -> Vec<usize> {
if let Some(err) =
rustpython_parser::parse(content, rustpython_parser::Mode::Module, "anonymous.py").err()
{
let converter = LineConverter::new(content);
vec![converter.textsize_to_line(err.offset)]
} else {
vec![]
}
}
}
struct LineConverter {
newline_positions: Vec<usize>,
}
impl LineConverter {
fn new(text: &str) -> Self {
let newline_positions = std::iter::once(0)
.chain(text.match_indices('\n').map(|(i, _)| i + 1))
.chain(std::iter::once(text.len())) .collect();
Self { newline_positions }
}
fn textsize_to_line(&self, offset: TextSize) -> usize {
let byte_offset = usize::from(offset); match self.newline_positions.binary_search(&byte_offset) {
Ok(line) => line + 1,
Err(line) => line,
}
}
}