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}