paths.rs

  1use std::path::{Path, PathBuf};
  2
  3use serde::{Deserialize, Serialize};
  4
  5lazy_static::lazy_static! {
  6    pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory");
  7    pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed");
  8    pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed");
  9    pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed");
 10    pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages");
 11    pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot");
 12    pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db");
 13    pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json");
 14    pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json");
 15    pub static ref LAST_USERNAME: PathBuf = CONFIG_DIR.join("last-username.txt");
 16    pub static ref LOG: PathBuf = LOGS_DIR.join("Zed.log");
 17    pub static ref OLD_LOG: PathBuf = LOGS_DIR.join("Zed.log.old");
 18}
 19
 20pub mod legacy {
 21    use std::path::PathBuf;
 22
 23    lazy_static::lazy_static! {
 24        static ref CONFIG_DIR: PathBuf = super::HOME.join(".zed");
 25        pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json");
 26        pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json");
 27    }
 28}
 29
 30/// Compacts a given file path by replacing the user's home directory
 31/// prefix with a tilde (`~`).
 32///
 33/// # Arguments
 34///
 35/// * `path` - A reference to a `Path` representing the file path to compact.
 36///
 37/// # Examples
 38///
 39/// ```
 40/// use std::path::{Path, PathBuf};
 41/// use util::paths::compact;
 42/// let path: PathBuf = [
 43///     util::paths::HOME.to_string_lossy().to_string(),
 44///     "some_file.txt".to_string(),
 45///  ]
 46///  .iter()
 47///  .collect();
 48/// if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
 49///     assert_eq!(compact(&path).to_str(), Some("~/some_file.txt"));
 50/// } else {
 51///     assert_eq!(compact(&path).to_str(), path.to_str());
 52/// }
 53/// ```
 54///
 55/// # Returns
 56///
 57/// * A `PathBuf` containing the compacted file path. If the input path
 58///   does not have the user's home directory prefix, or if we are not on
 59///   Linux or macOS, the original path is returned unchanged.
 60pub fn compact(path: &Path) -> PathBuf {
 61    if cfg!(target_os = "linux") || cfg!(target_os = "macos") {
 62        match path.strip_prefix(HOME.as_path()) {
 63            Ok(relative_path) => {
 64                let mut shortened_path = PathBuf::new();
 65                shortened_path.push("~");
 66                shortened_path.push(relative_path);
 67                shortened_path
 68            }
 69            Err(_) => path.to_path_buf(),
 70        }
 71    } else {
 72        path.to_path_buf()
 73    }
 74}
 75
 76pub const FILE_ROW_COLUMN_DELIMITER: char = ':';
 77
 78#[derive(Debug, Clone, Serialize, Deserialize)]
 79pub struct PathLikeWithPosition<P> {
 80    pub path_like: P,
 81    pub row: Option<u32>,
 82    pub column: Option<u32>,
 83}
 84
 85impl<P> PathLikeWithPosition<P> {
 86    pub fn parse_str<F, E>(s: &str, parse_path_like_str: F) -> Result<Self, E>
 87    where
 88        F: Fn(&str) -> Result<P, E>,
 89    {
 90        let mut components = s.splitn(3, FILE_ROW_COLUMN_DELIMITER).map(str::trim).fuse();
 91        let path_like_str = components.next().filter(|str| !str.is_empty());
 92        let row = components.next().and_then(|row| row.parse::<u32>().ok());
 93        let column = components
 94            .next()
 95            .filter(|_| row.is_some())
 96            .and_then(|col| col.parse::<u32>().ok());
 97
 98        Ok(match path_like_str {
 99            Some(path_like_str) => Self {
100                path_like: parse_path_like_str(path_like_str)?,
101                row,
102                column,
103            },
104            None => Self {
105                path_like: parse_path_like_str(s)?,
106                row: None,
107                column: None,
108            },
109        })
110    }
111
112    pub fn convert_path<P2, E>(
113        self,
114        mapping: impl FnOnce(P) -> Result<P2, E>,
115    ) -> Result<PathLikeWithPosition<P2>, E> {
116        Ok(PathLikeWithPosition {
117            path_like: mapping(self.path_like)?,
118            row: self.row,
119            column: self.column,
120        })
121    }
122
123    pub fn to_string<F>(&self, path_like_to_string: F) -> String
124    where
125        F: Fn(&P) -> String,
126    {
127        let path_like_string = path_like_to_string(&self.path_like);
128        if let Some(row) = self.row {
129            if let Some(column) = self.column {
130                format!("{path_like_string}:{row}:{column}")
131            } else {
132                format!("{path_like_string}:{row}")
133            }
134        } else {
135            path_like_string
136        }
137    }
138}