Return correct path and line in query errors from the CLI

This commit is contained in:
Max Brunsfeld 2020-09-24 13:47:27 -07:00
parent 297e2bcb28
commit 518916f221
5 changed files with 372 additions and 234 deletions

View file

@ -5,12 +5,13 @@ use regex::{Regex, RegexBuilder};
use serde_derive::Deserialize;
use std::collections::HashMap;
use std::io::BufReader;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Mutex;
use std::time::SystemTime;
use std::{fs, mem};
use tree_sitter::Language;
use tree_sitter::{Language, QueryError};
use tree_sitter_highlight::HighlightConfiguration;
use tree_sitter_tags::TagsConfiguration;
@ -543,13 +544,32 @@ impl Loader {
impl<'a> LanguageConfiguration<'a> {
pub fn highlight_config(&self, language: Language) -> Result<Option<&HighlightConfiguration>> {
fn include_path_in_error<'a>(
mut error: QueryError,
ranges: &'a Vec<(String, Range<usize>)>,
source: &str,
start_offset: usize,
) -> (&'a str, QueryError) {
let offset = error.offset - start_offset;
let (path, range) = ranges
.iter()
.find(|(_, range)| range.contains(&offset))
.unwrap();
error.row = source[range.start..offset]
.chars()
.filter(|c| *c == '\n')
.count();
(path.as_ref(), error)
}
self.highlight_config
.get_or_try_init(|| {
let highlights_query =
let (highlights_query, highlight_ranges) =
self.read_queries(&self.highlights_filenames, "highlights.scm")?;
let injections_query =
let (injections_query, injection_ranges) =
self.read_queries(&self.injections_filenames, "injections.scm")?;
let locals_query = self.read_queries(&self.locals_filenames, "locals.scm")?;
let (locals_query, locals_ranges) =
self.read_queries(&self.locals_filenames, "locals.scm")?;
if highlights_query.is_empty() {
Ok(None)
@ -560,9 +580,25 @@ impl<'a> LanguageConfiguration<'a> {
&injections_query,
&locals_query,
)
.map_err(Error::wrap(|| {
format!("Failed to load queries in {:?}", self.root_path)
}))?;
.map_err(|error| {
if error.offset < injections_query.len() {
include_path_in_error(error, &injection_ranges, &injections_query, 0)
} else if error.offset < injections_query.len() + locals_query.len() {
include_path_in_error(
error,
&locals_ranges,
&locals_query,
injections_query.len(),
)
} else {
include_path_in_error(
error,
&highlight_ranges,
&highlights_query,
injections_query.len() + locals_query.len(),
)
}
})?;
let mut all_highlight_names = self.highlight_names.lock().unwrap();
if self.use_all_highlight_names {
for capture_name in result.query.capture_names() {
@ -581,8 +617,8 @@ impl<'a> LanguageConfiguration<'a> {
pub fn tags_config(&self, language: Language) -> Result<Option<&TagsConfiguration>> {
self.tags_config
.get_or_try_init(|| {
let tags_query = self.read_queries(&self.tags_filenames, "tags.scm")?;
let locals_query = self.read_queries(&self.locals_filenames, "locals.scm")?;
let (tags_query, _) = self.read_queries(&self.tags_filenames, "tags.scm")?;
let (locals_query, _) = self.read_queries(&self.locals_filenames, "locals.scm")?;
if tags_query.is_empty() {
Ok(None)
} else {
@ -596,27 +632,34 @@ impl<'a> LanguageConfiguration<'a> {
.map(Option::as_ref)
}
fn read_queries(&self, paths: &Option<Vec<String>>, default_path: &str) -> Result<String> {
fn read_queries(
&self,
paths: &Option<Vec<String>>,
default_path: &str,
) -> Result<(String, Vec<(String, Range<usize>)>)> {
let mut query = String::new();
let mut path_ranges = Vec::new();
if let Some(paths) = paths.as_ref() {
let mut query = String::new();
for path in paths {
let path = self.root_path.join(path);
query += &fs::read_to_string(&path).map_err(Error::wrap(|| {
let abs_path = self.root_path.join(path);
let prev_query_len = query.len();
query += &fs::read_to_string(&abs_path).map_err(Error::wrap(|| {
format!("Failed to read query file {:?}", path)
}))?;
path_ranges.push((path.clone(), prev_query_len..query.len()));
}
Ok(query)
} else {
let queries_path = self.root_path.join("queries");
let path = queries_path.join(default_path);
if path.exists() {
fs::read_to_string(&path).map_err(Error::wrap(|| {
query = fs::read_to_string(&path).map_err(Error::wrap(|| {
format!("Failed to read query file {:?}", path)
}))
} else {
Ok(String::new())
}))?;
path_ranges.push((default_path.to_string(), 0..query.len()));
}
}
Ok((query, path_ranges))
}
}