Scripts: leave a script out of Recent Script Files when its path can't be made absolute

Services::full_path now returns None when the working directory can't be
read, instead of the relative path. file.runScript still runs the script
but does not add it to the list, so a later working directory can't make
the entry open a different file.
This commit is contained in:
dotCooCoo
2026-10-28 09:37:46 -07:00
parent b8b1a3a6a2
commit 509e250c4f
4 changed files with 42 additions and 10 deletions
+2 -2
View File
@@ -152,8 +152,8 @@ impl Services for WebServices {
false
}
/// The table's keys are the paths themselves.
fn full_path(&self, path: &str) -> String {
path.to_string()
fn full_path(&self, path: &str) -> Option<String> {
Some(path.to_string())
}
}
+4 -2
View File
@@ -465,7 +465,9 @@ fn run_script(s: &mut Session, p: &Value) -> Result<Value> {
let name = std::path::Path::new(path).file_name().map(|n| n.to_string_lossy().to_string()).unwrap_or_else(|| path.to_string());
let out = run_js(s, &code, path, false)?;
let result = report_js(s, &name, out)?;
remember_script(s, crate::prefs::RecentScript::Path(full));
if let Some(full) = full {
remember_script(s, crate::prefs::RecentScript::Path(full));
}
return Ok(result);
}
let mut script_path = None;
@@ -473,7 +475,7 @@ fn run_script(s: &mut Session, p: &Value) -> Result<Value> {
(Some(Value::Array(a)), _) => a.clone(),
(_, Some(path)) => {
let bytes = s.services.read_file(path).map_err(|e| EngineError::Other(format!("cannot read {path}: {e}")))?;
script_path = Some(s.services.full_path(path));
script_path = s.services.full_path(path);
let text = String::from_utf8_lossy(&bytes).to_string();
match serde_json::from_str::<Value>(&text) {
Ok(Value::Array(a)) => a,
+32 -3
View File
@@ -661,11 +661,40 @@ mod tests {
fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> {
std::fs::write(self.at(path), data)
}
fn full_path(&self, path: &str) -> String {
self.at(path).to_string_lossy().into_owned()
fn full_path(&self, path: &str) -> Option<String> {
Some(self.at(path).to_string_lossy().into_owned())
}
}
/// Reads from a folder but can't make paths absolute.
struct NoFullPath(PathBuf);
impl crate::Services for NoFullPath {
fn read_file(&self, path: &str) -> std::io::Result<Vec<u8>> {
std::fs::read(self.0.join(path))
}
fn write_file(&self, path: &str, data: &[u8]) -> std::io::Result<()> {
std::fs::write(self.0.join(path), data)
}
fn full_path(&self, _path: &str) -> Option<String> {
None
}
}
/// A script whose path can't be made absolute still runs, and Recent Script Files does not
/// keep its relative path.
#[test]
fn unresolved_script_paths_are_not_remembered() {
let (mut s, root) = session_in_tmp("recent-unresolved");
std::fs::write(root.join("Tool.jsx"), "a").unwrap();
std::fs::write(root.join("steps.jsonl"), "[]").unwrap();
s.services = Arc::new(NoFullPath(root.clone()));
assert_eq!(s.execute("file.runScript", json!({"path": "Tool.jsx"})).unwrap()["ran"], "a");
s.execute("file.runScript", json!({"path": "steps.jsonl"})).unwrap();
assert!(s.prefs.recent_scripts.is_empty(), "{:?}", s.prefs.recent_scripts);
let _ = std::fs::remove_dir_all(&root);
}
/// A script run from a relative path runs the same file again from Recent Script Files after
/// the working directory changes, and two scripts with the same file name show their paths.
#[test]
@@ -694,7 +723,7 @@ mod tests {
#[test]
fn native_full_paths_are_absolute() {
use crate::Services;
assert!(std::path::Path::new(&crate::FsServices.full_path("Tool.jsx")).is_absolute());
assert!(crate::FsServices.full_path("Tool.jsx").is_some_and(|p| std::path::Path::new(&p).is_absolute()));
}
#[test]
+4 -3
View File
@@ -39,9 +39,10 @@ pub trait Services: Send + Sync {
}
/// `path` as a list of recent files keeps it (File ▸ Scripts ▸ Recent Script Files): a
/// relative path is made absolute against the working directory, so it opens the same file
/// after the working directory changes.
fn full_path(&self, path: &str) -> String {
std::path::absolute(path).map(|p| p.to_string_lossy().into_owned()).unwrap_or_else(|_| path.to_string())
/// after the working directory changes. `None` when the path can't be made absolute (the
/// working directory can't be read); the script is then not added to the list.
fn full_path(&self, path: &str) -> Option<String> {
std::path::absolute(path).ok().map(|p| p.to_string_lossy().into_owned())
}
}