summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYomain2023-07-11 17:51:04 +0000
committerGitHub2023-07-11 17:51:04 +0000
commit8afc0282f28e73cf78d1bd7b11d78fd853ae2036 (patch)
tree6742e141f051d352ebe7017f15805613670864b9
parent1adb19464f002926e1042027b41acef4c81585f6 (diff)
Fix crash when cwd is deleted (#7185)
-rw-r--r--Cargo.lock1
-rw-r--r--helix-core/src/path.rs8
-rw-r--r--helix-loader/Cargo.toml1
-rw-r--r--helix-loader/src/lib.rs44
-rw-r--r--helix-lsp/src/lib.rs2
-rw-r--r--helix-term/src/application.rs2
-rw-r--r--helix-term/src/commands.rs23
-rw-r--r--helix-term/src/commands/dap.rs2
-rw-r--r--helix-term/src/commands/lsp.rs2
-rw-r--r--helix-term/src/commands/typed.rs18
-rw-r--r--helix-term/src/ui/mod.rs2
11 files changed, 81 insertions, 24 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1a18a4dd..68d5727f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1260,6 +1260,7 @@ version = "0.6.0"
dependencies = [
"anyhow",
"cc",
+ "dunce",
"etcetera",
"libloading",
"log",
diff --git a/helix-core/src/path.rs b/helix-core/src/path.rs
index efa46c46..85c60255 100644
--- a/helix-core/src/path.rs
+++ b/helix-core/src/path.rs
@@ -88,7 +88,7 @@ pub fn get_normalized_path(path: &Path) -> PathBuf {
pub fn get_canonicalized_path(path: &Path) -> std::io::Result<PathBuf> {
let path = expand_tilde(path);
let path = if path.is_relative() {
- std::env::current_dir().map(|current_dir| current_dir.join(path))?
+ helix_loader::current_working_dir().join(path)
} else {
path
};
@@ -99,9 +99,7 @@ pub fn get_canonicalized_path(path: &Path) -> std::io::Result<PathBuf> {
pub fn get_relative_path(path: &Path) -> PathBuf {
let path = PathBuf::from(path);
let path = if path.is_absolute() {
- let cwdir = std::env::current_dir()
- .map(|path| get_normalized_path(&path))
- .expect("couldn't determine current directory");
+ let cwdir = get_normalized_path(&helix_loader::current_working_dir());
get_normalized_path(&path)
.strip_prefix(cwdir)
.map(PathBuf::from)
@@ -142,7 +140,7 @@ pub fn get_relative_path(path: &Path) -> PathBuf {
/// ```
///
pub fn get_truncated_path<P: AsRef<Path>>(path: P) -> PathBuf {
- let cwd = std::env::current_dir().unwrap_or_default();
+ let cwd = helix_loader::current_working_dir();
let path = path
.as_ref()
.strip_prefix(cwd)
diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml
index 3e7fc2e7..903d36c0 100644
--- a/helix-loader/Cargo.toml
+++ b/helix-loader/Cargo.toml
@@ -29,6 +29,7 @@ which = "4.4"
cc = { version = "1" }
threadpool = { version = "1.0" }
tempfile = "3.6.0"
+dunce = "1.0.4"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
libloading = "0.8"
diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs
index c51c9c7d..7ff4cada 100644
--- a/helix-loader/src/lib.rs
+++ b/helix-loader/src/lib.rs
@@ -3,9 +3,12 @@ pub mod grammar;
use etcetera::base_strategy::{choose_base_strategy, BaseStrategy};
use std::path::{Path, PathBuf};
+use std::sync::RwLock;
pub const VERSION_AND_GIT_HASH: &str = env!("VERSION_AND_GIT_HASH");
+static CWD: RwLock<Option<PathBuf>> = RwLock::new(None);
+
static RUNTIME_DIRS: once_cell::sync::Lazy<Vec<PathBuf>> =
once_cell::sync::Lazy::new(prioritize_runtime_dirs);
@@ -13,6 +16,31 @@ static CONFIG_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCe
static LOG_FILE: once_cell::sync::OnceCell<PathBuf> = once_cell::sync::OnceCell::new();
+// Get the current working directory.
+// This information is managed internally as the call to std::env::current_dir
+// might fail if the cwd has been deleted.
+pub fn current_working_dir() -> PathBuf {
+ if let Some(path) = &*CWD.read().unwrap() {
+ return path.clone();
+ }
+
+ let path = std::env::current_dir()
+ .and_then(dunce::canonicalize)
+ .expect("Couldn't determine current working directory");
+ let mut cwd = CWD.write().unwrap();
+ *cwd = Some(path.clone());
+
+ path
+}
+
+pub fn set_current_working_dir(path: PathBuf) -> std::io::Result<()> {
+ let path = dunce::canonicalize(path)?;
+ std::env::set_current_dir(path.clone())?;
+ let mut cwd = CWD.write().unwrap();
+ *cwd = Some(path);
+ Ok(())
+}
+
pub fn initialize_config_file(specified_file: Option<PathBuf>) {
let config_file = specified_file.unwrap_or_else(default_config_file);
ensure_parent_dir(&config_file);
@@ -217,7 +245,7 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi
/// If no workspace was found returns (CWD, true).
/// Otherwise (workspace, false) is returned
pub fn find_workspace() -> (PathBuf, bool) {
- let current_dir = std::env::current_dir().expect("unable to determine current directory");
+ let current_dir = current_working_dir();
for ancestor in current_dir.ancestors() {
if ancestor.join(".git").exists() || ancestor.join(".helix").exists() {
return (ancestor.to_owned(), false);
@@ -243,10 +271,22 @@ fn ensure_parent_dir(path: &Path) {
mod merge_toml_tests {
use std::str;
- use super::merge_toml_values;
+ use super::{current_working_dir, merge_toml_values, set_current_working_dir};
use toml::Value;
#[test]
+ fn current_dir_is_set() {
+ let new_path = dunce::canonicalize(std::env::temp_dir()).unwrap();
+ let cwd = current_working_dir();
+ assert_ne!(cwd, new_path);
+
+ set_current_working_dir(new_path.clone()).expect("Couldn't set new path");
+
+ let cwd = current_working_dir();
+ assert_eq!(cwd, new_path);
+ }
+
+ #[test]
fn language_toml_map_merges() {
const USER: &str = r#"
[[language]]
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 277a4c28..95c61086 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -931,7 +931,7 @@ pub fn find_lsp_workspace(
let mut file = if file.is_absolute() {
file.to_path_buf()
} else {
- let current_dir = std::env::current_dir().expect("unable to determine current directory");
+ let current_dir = helix_loader::current_working_dir();
current_dir.join(file)
};
file = path::get_normalized_path(&file);
diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 4c86a19f..546a57a9 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -163,7 +163,7 @@ impl Application {
} else if !args.files.is_empty() {
let first = &args.files[0].0; // we know it's not empty
if first.is_dir() {
- std::env::set_current_dir(first).context("set current dir")?;
+ helix_loader::set_current_working_dir(first.clone())?;
editor.new_file(Action::VerticalSplit);
let picker = ui::file_picker(".".into(), &config.load().editor);
compositor.push(Box::new(overlaid(picker)));
diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs
index ae715887..47b1a175 100644
--- a/helix-term/src/commands.rs
+++ b/helix-term/src/commands.rs
@@ -2080,8 +2080,12 @@ fn global_search(cx: &mut Context) {
.binary_detection(BinaryDetection::quit(b'\x00'))
.build();
- let search_root = std::env::current_dir()
- .expect("Global search error: Failed to get current dir");
+ let search_root = helix_loader::current_working_dir();
+ if !search_root.exists() {
+ editor.set_error("Current working directory does not exist");
+ return;
+ }
+
let dedup_symlinks = file_picker_config.deduplicate_links;
let absolute_root = search_root
.canonicalize()
@@ -2173,7 +2177,9 @@ fn global_search(cx: &mut Context) {
let call: job::Callback = Callback::EditorCompositor(Box::new(
move |editor: &mut Editor, compositor: &mut Compositor| {
if all_matches.is_empty() {
- editor.set_status("No matches found");
+ if !editor.is_err() {
+ editor.set_status("No matches found");
+ }
return;
}
@@ -2518,6 +2524,10 @@ fn append_mode(cx: &mut Context) {
fn file_picker(cx: &mut Context) {
let root = find_workspace().0;
+ if !root.exists() {
+ cx.editor.set_error("Workspace directory does not exist");
+ return;
+ }
let picker = ui::file_picker(root, &cx.editor.config());
cx.push_layer(Box::new(overlaid(picker)));
}
@@ -2539,7 +2549,12 @@ fn file_picker_in_current_buffer_directory(cx: &mut Context) {
cx.push_layer(Box::new(overlaid(picker)));
}
fn file_picker_in_current_directory(cx: &mut Context) {
- let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("./"));
+ let cwd = helix_loader::current_working_dir();
+ if !cwd.exists() {
+ cx.editor
+ .set_error("Current working directory does not exist");
+ return;
+ }
let picker = ui::file_picker(cwd, &cx.editor.config());
cx.push_layer(Box::new(overlaid(picker)));
}
diff --git a/helix-term/src/commands/dap.rs b/helix-term/src/commands/dap.rs
index 70a5ec21..e26dc08d 100644
--- a/helix-term/src/commands/dap.rs
+++ b/helix-term/src/commands/dap.rs
@@ -217,7 +217,7 @@ pub fn dap_start_impl(
}
}
- args.insert("cwd", to_value(std::env::current_dir().unwrap())?);
+ args.insert("cwd", to_value(helix_loader::current_working_dir())?);
let args = to_value(args).unwrap();
diff --git a/helix-term/src/commands/lsp.rs b/helix-term/src/commands/lsp.rs
index 55153648..145bddd0 100644
--- a/helix-term/src/commands/lsp.rs
+++ b/helix-term/src/commands/lsp.rs
@@ -1033,7 +1033,7 @@ fn goto_impl(
locations: Vec<lsp::Location>,
offset_encoding: OffsetEncoding,
) {
- let cwdir = std::env::current_dir().unwrap_or_default();
+ let cwdir = helix_loader::current_working_dir();
match locations.as_slice() {
[location] => {
diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs
index 94cc33f0..dfc71dfd 100644
--- a/helix-term/src/commands/typed.rs
+++ b/helix-term/src/commands/typed.rs
@@ -1093,14 +1093,11 @@ fn change_current_directory(
.as_ref(),
);
- if let Err(e) = std::env::set_current_dir(dir) {
- bail!("Couldn't change the current working directory: {}", e);
- }
+ helix_loader::set_current_working_dir(dir)?;
- let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
cx.editor.set_status(format!(
"Current working directory is now {}",
- cwd.display()
+ helix_loader::current_working_dir().display()
));
Ok(())
}
@@ -1114,9 +1111,14 @@ fn show_current_directory(
return Ok(());
}
- let cwd = std::env::current_dir().context("Couldn't get the new working directory")?;
- cx.editor
- .set_status(format!("Current working directory is {}", cwd.display()));
+ let cwd = helix_loader::current_working_dir();
+ let message = format!("Current working directory is {}", cwd.display());
+
+ if cwd.exists() {
+ cx.editor.set_status(message);
+ } else {
+ cx.editor.set_error(format!("{} (deleted)", message));
+ }
Ok(())
}
diff --git a/helix-term/src/ui/mod.rs b/helix-term/src/ui/mod.rs
index 155f2435..3359155d 100644
--- a/helix-term/src/ui/mod.rs
+++ b/helix-term/src/ui/mod.rs
@@ -472,7 +472,7 @@ pub mod completers {
match path.parent() {
Some(path) if !path.as_os_str().is_empty() => path.to_path_buf(),
// Path::new("h")'s parent is Some("")...
- _ => std::env::current_dir().expect("couldn't determine current directory"),
+ _ => helix_loader::current_working_dir(),
}
};