relpaths: Fix repeated usages of RelPath::unix on static paths (#39675)

Piotr Osiewicz created

- **paths: Cache away results of static construction of RelPath**
- **agent: Cache away results of converting rules file names into
relpaths**

This PR fixed a regression from relpath PR where we've started doing
more work when working with static (Rel-)Paths.

Release Notes:

- N/A

Change summary

crates/agent/src/thread_store.rs | 37 +++++++++++++++++----------------
crates/paths/src/paths.rs        | 30 ++++++++++++++++++++-------
2 files changed, 41 insertions(+), 26 deletions(-)

Detailed changes

crates/agent/src/thread_store.rs 🔗

@@ -38,7 +38,7 @@ use std::{
     cell::{Ref, RefCell},
     path::{Path, PathBuf},
     rc::Rc,
-    sync::{Arc, Mutex},
+    sync::{Arc, LazyLock, Mutex},
 };
 use util::{ResultExt as _, rel_path::RelPath};
 
@@ -74,17 +74,19 @@ impl Column for DataType {
     }
 }
 
-const RULES_FILE_NAMES: [&str; 9] = [
-    ".rules",
-    ".cursorrules",
-    ".windsurfrules",
-    ".clinerules",
-    ".github/copilot-instructions.md",
-    "CLAUDE.md",
-    "AGENT.md",
-    "AGENTS.md",
-    "GEMINI.md",
-];
+static RULES_FILE_NAMES: LazyLock<[&RelPath; 9]> = LazyLock::new(|| {
+    [
+        RelPath::unix(".rules").unwrap(),
+        RelPath::unix(".cursorrules").unwrap(),
+        RelPath::unix(".windsurfrules").unwrap(),
+        RelPath::unix(".clinerules").unwrap(),
+        RelPath::unix(".github/copilot-instructions.md").unwrap(),
+        RelPath::unix("CLAUDE.md").unwrap(),
+        RelPath::unix("AGENT.md").unwrap(),
+        RelPath::unix("AGENTS.md").unwrap(),
+        RelPath::unix("GEMINI.md").unwrap(),
+    ]
+});
 
 pub fn init(fs: Arc<dyn Fs>, cx: &mut App) {
     ThreadsDatabase::init(fs, cx);
@@ -232,11 +234,10 @@ impl ThreadStore {
                 self.enqueue_system_prompt_reload();
             }
             project::Event::WorktreeUpdatedEntries(_, items) => {
-                if items.iter().any(|(path, _, _)| {
-                    RULES_FILE_NAMES
-                        .iter()
-                        .any(|name| path.as_ref() == RelPath::unix(name).unwrap())
-                }) {
+                if items
+                    .iter()
+                    .any(|(path, _, _)| RULES_FILE_NAMES.iter().any(|name| path.as_ref() == *name))
+                {
                     self.enqueue_system_prompt_reload();
                 }
             }
@@ -368,7 +369,7 @@ impl ThreadStore {
             .into_iter()
             .filter_map(|name| {
                 worktree
-                    .entry_for_path(RelPath::unix(name).unwrap())
+                    .entry_for_path(name)
                     .filter(|entry| entry.is_file())
                     .map(|entry| entry.path.clone())
             })

crates/paths/src/paths.rs 🔗

@@ -2,7 +2,7 @@
 
 use std::env;
 use std::path::{Path, PathBuf};
-use std::sync::OnceLock;
+use std::sync::{LazyLock, OnceLock};
 
 pub use util::paths::home_dir;
 use util::rel_path::RelPath;
@@ -31,12 +31,16 @@ static CONFIG_DIR: OnceLock<PathBuf> = OnceLock::new();
 
 /// Returns the relative path to the zed_server directory on the ssh host.
 pub fn remote_server_dir_relative() -> &'static RelPath {
-    RelPath::unix(".zed_server").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".zed_server").unwrap());
+    *CACHED
 }
 
 /// Returns the relative path to the zed_wsl_server directory on the wsl host.
 pub fn remote_wsl_server_dir_relative() -> &'static RelPath {
-    RelPath::unix(".zed_wsl_server").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".zed_wsl_server").unwrap());
+    *CACHED
 }
 
 /// Sets a custom directory for all user data, overriding the default data directory.
@@ -410,17 +414,23 @@ pub fn local_vscode_folder_name() -> &'static str {
 
 /// Returns the relative path to a `settings.json` file within a project.
 pub fn local_settings_file_relative_path() -> &'static RelPath {
-    RelPath::unix(".zed/settings.json").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".zed/settings.json").unwrap());
+    *CACHED
 }
 
 /// Returns the relative path to a `tasks.json` file within a project.
 pub fn local_tasks_file_relative_path() -> &'static RelPath {
-    RelPath::unix(".zed/tasks.json").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".zed/tasks.json").unwrap());
+    *CACHED
 }
 
 /// Returns the relative path to a `.vscode/tasks.json` file within a project.
 pub fn local_vscode_tasks_file_relative_path() -> &'static RelPath {
-    RelPath::unix(".vscode/tasks.json").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".vscode/tasks.json").unwrap());
+    *CACHED
 }
 
 pub fn debug_task_file_name() -> &'static str {
@@ -434,12 +444,16 @@ pub fn task_file_name() -> &'static str {
 /// Returns the relative path to a `debug.json` file within a project.
 /// .zed/debug.json
 pub fn local_debug_file_relative_path() -> &'static RelPath {
-    RelPath::unix(".zed/debug.json").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".zed/debug.json").unwrap());
+    *CACHED
 }
 
 /// Returns the relative path to a `.vscode/launch.json` file within a project.
 pub fn local_vscode_launch_file_relative_path() -> &'static RelPath {
-    RelPath::unix(".vscode/launch.json").unwrap()
+    static CACHED: LazyLock<&'static RelPath> =
+        LazyLock::new(|| RelPath::unix(".vscode/launch.json").unwrap());
+    *CACHED
 }
 
 pub fn user_ssh_config_file() -> PathBuf {