diff options
Diffstat (limited to 'helix-loader')
-rw-r--r-- | helix-loader/src/grammar.rs | 23 | ||||
-rw-r--r-- | helix-loader/src/lib.rs | 81 |
2 files changed, 81 insertions, 23 deletions
diff --git a/helix-loader/src/grammar.rs b/helix-loader/src/grammar.rs index 01c966c8..a85cb274 100644 --- a/helix-loader/src/grammar.rs +++ b/helix-loader/src/grammar.rs @@ -67,8 +67,9 @@ pub fn get_language(name: &str) -> Result<Language> { #[cfg(not(target_arch = "wasm32"))] pub fn get_language(name: &str) -> Result<Language> { use libloading::{Library, Symbol}; - let mut library_path = crate::runtime_dir().join("grammars").join(name); - library_path.set_extension(DYLIB_EXTENSION); + let mut rel_library_path = PathBuf::new().join("grammars").join(name); + rel_library_path.set_extension(DYLIB_EXTENSION); + let library_path = crate::runtime_file(&rel_library_path); let library = unsafe { Library::new(&library_path) } .with_context(|| format!("Error opening dynamic library {:?}", library_path))?; @@ -252,7 +253,9 @@ fn fetch_grammar(grammar: GrammarConfiguration) -> Result<FetchStatus> { remote, revision, .. } = grammar.source { - let grammar_dir = crate::runtime_dir() + let grammar_dir = crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition .join("grammars") .join("sources") .join(&grammar.grammar_id); @@ -350,7 +353,9 @@ fn build_grammar(grammar: GrammarConfiguration, target: Option<&str>) -> Result< let grammar_dir = if let GrammarSource::Local { path } = &grammar.source { PathBuf::from(&path) } else { - crate::runtime_dir() + crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition .join("grammars") .join("sources") .join(&grammar.grammar_id) @@ -401,7 +406,10 @@ fn build_tree_sitter_library( None } }; - let parser_lib_path = crate::runtime_dir().join("grammars"); + let parser_lib_path = crate::runtime_dirs() + .first() + .expect("No runtime directories provided") // guaranteed by post-condition + .join("grammars"); let mut library_path = parser_lib_path.join(&grammar.grammar_id); library_path.set_extension(DYLIB_EXTENSION); @@ -511,9 +519,6 @@ fn mtime(path: &Path) -> Result<SystemTime> { /// Gives the contents of a file from a language's `runtime/queries/<lang>` /// directory pub fn load_runtime_file(language: &str, filename: &str) -> Result<String, std::io::Error> { - let path = crate::RUNTIME_DIR - .join("queries") - .join(language) - .join(filename); + let path = crate::runtime_file(&PathBuf::new().join("queries").join(language).join(filename)); std::fs::read_to_string(path) } diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index 8dc2928a..04b44b5a 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -2,11 +2,12 @@ pub mod config; pub mod grammar; use etcetera::base_strategy::{choose_base_strategy, BaseStrategy}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; pub const VERSION_AND_GIT_HASH: &str = env!("VERSION_AND_GIT_HASH"); -pub static RUNTIME_DIR: once_cell::sync::Lazy<PathBuf> = once_cell::sync::Lazy::new(runtime_dir); +static RUNTIME_DIRS: once_cell::sync::Lazy<Vec<PathBuf>> = + once_cell::sync::Lazy::new(prioritize_runtime_dirs); static CONFIG_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCell::new(); @@ -25,31 +26,83 @@ pub fn initialize_config_file(specified_file: Option<PathBuf>) { CONFIG_FILE.set(config_file).ok(); } -pub fn runtime_dir() -> PathBuf { - if let Ok(dir) = std::env::var("HELIX_RUNTIME") { - return dir.into(); - } - +/// A list of runtime directories from highest to lowest priority +/// +/// The priority is: +/// +/// 1. sibling directory to `CARGO_MANIFEST_DIR` (if environment variable is set) +/// 2. subdirectory of user config directory (always included) +/// 3. `HELIX_RUNTIME` (if environment variable is set) +/// 4. subdirectory of path to helix executable (always included) +/// +/// Postcondition: returns at least two paths (they might not exist). +fn prioritize_runtime_dirs() -> Vec<PathBuf> { + const RT_DIR: &str = "runtime"; + // Adding higher priority first + let mut rt_dirs = Vec::new(); if let Ok(dir) = std::env::var("CARGO_MANIFEST_DIR") { // this is the directory of the crate being run by cargo, we need the workspace path so we take the parent let path = std::path::PathBuf::from(dir).parent().unwrap().join(RT_DIR); log::debug!("runtime dir: {}", path.to_string_lossy()); - return path; + rt_dirs.push(path); } - const RT_DIR: &str = "runtime"; - let conf_dir = config_dir().join(RT_DIR); - if conf_dir.exists() { - return conf_dir; + let conf_rt_dir = config_dir().join(RT_DIR); + rt_dirs.push(conf_rt_dir); + + if let Ok(dir) = std::env::var("HELIX_RUNTIME") { + rt_dirs.push(dir.into()); } // fallback to location of the executable being run // canonicalize the path in case the executable is symlinked - std::env::current_exe() + let exe_rt_dir = std::env::current_exe() .ok() .and_then(|path| std::fs::canonicalize(path).ok()) .and_then(|path| path.parent().map(|path| path.to_path_buf().join(RT_DIR))) - .unwrap() + .unwrap(); + rt_dirs.push(exe_rt_dir); + rt_dirs +} + +/// Runtime directories ordered from highest to lowest priority +/// +/// All directories should be checked when looking for files. +/// +/// Postcondition: returns at least one path (it might not exist). +pub fn runtime_dirs() -> &'static [PathBuf] { + &RUNTIME_DIRS +} + +/// Find file with path relative to runtime directory +/// +/// `rel_path` should be the relative path from within the `runtime/` directory. +/// The valid runtime directories are searched in priority order and the first +/// file found to exist is returned, otherwise None. +fn find_runtime_file(rel_path: &Path) -> Option<PathBuf> { + RUNTIME_DIRS.iter().find_map(|rt_dir| { + let path = rt_dir.join(rel_path); + if path.exists() { + Some(path) + } else { + None + } + }) +} + +/// Find file with path relative to runtime directory +/// +/// `rel_path` should be the relative path from within the `runtime/` directory. +/// The valid runtime directories are searched in priority order and the first +/// file found to exist is returned, otherwise the path to the final attempt +/// that failed. +pub fn runtime_file(rel_path: &Path) -> PathBuf { + find_runtime_file(rel_path).unwrap_or_else(|| { + RUNTIME_DIRS + .last() + .map(|dir| dir.join(rel_path)) + .unwrap_or_default() + }) } pub fn config_dir() -> PathBuf { |