Louis Maddox 2025-06-14 23:57:48 +03:00 committed by GitHub
commit 6ad173ed2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 71 additions and 4 deletions

View File

@ -44,7 +44,7 @@ log = "0.4"
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
toml = { version = "0.8", features = ["parse"] }
imara-diff = "0.2.0"
encoding_rs = "0.8"

View File

@ -1,4 +1,5 @@
use crate::syntax::{config::Configuration, Loader, LoaderError};
use serde::de::Error;
/// Language configuration based on built-in languages.toml.
pub fn default_lang_config() -> Configuration {
@ -36,10 +37,76 @@ pub fn user_lang_config() -> Result<Configuration, toml::de::Error> {
/// Language configuration loader based on user configured languages.toml.
pub fn user_lang_loader() -> Result<Loader, LanguageLoaderError> {
let config: Configuration = helix_loader::config::user_lang_config()
.map_err(LanguageLoaderError::DeserializeError)?
.try_into()
let toml_value = helix_loader::config::user_lang_config()
.map_err(LanguageLoaderError::DeserializeError)?;
// Convert to string so we can get span information on parse errors
let toml_string = toml::to_string(&toml_value)
.map_err(|e| {
eprintln!("Failed to serialize TOML value: {}", e);
LanguageLoaderError::DeserializeError(toml::de::Error::custom("Failed to serialize TOML"))
})?;
let config: Configuration = toml::from_str(&toml_string)
.map_err(|e| {
// Now we have span information
if let Some(span) = e.span() {
let (line, col) = byte_pos_to_line_col(&toml_string, span.start);
eprintln!("Error at line {}, column {}: {}", line, col, e);
show_error_context(&toml_string, span);
}
LanguageLoaderError::DeserializeError(e)
})?;
Loader::new(config).map_err(LanguageLoaderError::LoaderError)
}
fn byte_pos_to_line_col(source: &str, pos: usize) -> (usize, usize) {
let mut line = 1;
let mut col = 1;
for (i, ch) in source.char_indices() {
if i >= pos {
break;
}
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
}
(line, col)
}
fn show_error_context(content: &str, span: std::ops::Range<usize>) {
let (line_num, _) = byte_pos_to_line_col(content, span.start);
let lines: Vec<&str> = content.lines().collect();
eprintln!("Context around error:");
// If the error line starts with [[, it's a section header - start from that line
// Otherwise, show the 3 preceding lines for context
let start_line = if line_num <= lines.len() && lines[line_num-1].trim().starts_with("[[") {
line_num
} else {
line_num.saturating_sub(3).max(1)
};
// Find the end of this section (next [[...]] or reasonable limit)
let mut end_line = line_num + 50; // reasonable limit
for i in (line_num + 1)..=lines.len().min(line_num + 50) {
if i <= lines.len() && lines[i-1].trim().starts_with("[[") {
end_line = i - 1;
break;
}
}
end_line = end_line.min(lines.len());
for i in start_line..=end_line {
let marker = if i == line_num { ">>> " } else { " " };
if i > 0 && i <= lines.len() {
eprintln!(" {}{:4}: {}", marker, i, lines[i-1]);
}
}
}