Files
zed/crates/worktree/src/worktree_settings.rs
T
Tom Houlé 4823db5177 settings: Remove the project_name project setting (#54511)
The `project_name` worktree setting was added in #36713 to let users
override the name shown in the window title. Its description ("The
displayed name of this project. If left empty, the root directory name
will be displayed.") suggests broader coverage, and #46440 reports the
reasonable expectation that it should also apply in the project
switcher. In practice the setting has only ever affected
`Workspace::update_window_title`, so everywhere else (recent projects,
the multi-worktree pane, ...) keeps falling back to the worktree root
name.

Rather than plumb the setting through each of those surfaces, I'm
removing it. Having a project-level setting control how your editor
displays the project has downsides. For example it means a checkout can
dictate UI in someone else's Zed. The natural home for a custom display
name is the workspace DB, set from the UI, which is what we should do if
we want this feature back.

If you want this back, the path forward is to store the display name in
`WorkspaceDb`, expose a UI affordance to edit it, and read it from
`update_window_title`, `recent_projects::get_recent_projects` /
`get_open_folders`, and any other places that currently derive a display
name from the worktree root.

Closes #46440

Release Notes:

- Removed the `project_name` project setting. It only ever affected the
OS window title, and the expectation that it would show up in the
project switcher and elsewhere is better served by a future UI-driven,
per-workspace setting stored locally.
2026-04-22 17:43:16 +02:00

107 lines
3.8 KiB
Rust

use std::path::Path;
use anyhow::Context as _;
use settings::{RegisterSetting, Settings};
use util::{
ResultExt,
paths::{PathMatcher, PathStyle},
rel_path::RelPath,
};
#[derive(Clone, PartialEq, Eq, RegisterSetting)]
pub struct WorktreeSettings {
/// Whether to prevent this project from being shared in public channels.
pub prevent_sharing_in_public_channels: bool,
pub file_scan_exclusions: PathMatcher,
pub file_scan_inclusions: PathMatcher,
/// This field contains all ancestors of the `file_scan_inclusions`. It's used to
/// determine whether to terminate worktree scanning for a given dir.
pub parent_dir_scan_inclusions: PathMatcher,
pub private_files: PathMatcher,
pub hidden_files: PathMatcher,
pub read_only_files: PathMatcher,
}
impl WorktreeSettings {
pub fn is_path_private(&self, path: &RelPath) -> bool {
path.ancestors()
.any(|ancestor| self.private_files.is_match(ancestor))
}
pub fn is_path_excluded(&self, path: &RelPath) -> bool {
path.ancestors()
.any(|ancestor| self.file_scan_exclusions.is_match(ancestor))
}
pub fn is_path_always_included(&self, path: &RelPath, is_dir: bool) -> bool {
if is_dir {
self.parent_dir_scan_inclusions.is_match(path)
} else {
self.file_scan_inclusions.is_match(path)
}
}
pub fn is_path_hidden(&self, path: &RelPath) -> bool {
path.ancestors()
.any(|ancestor| self.hidden_files.is_match(ancestor))
}
pub fn is_path_read_only(&self, path: &RelPath) -> bool {
self.read_only_files.is_match(path)
}
pub fn is_std_path_read_only(&self, path: &Path) -> bool {
self.read_only_files.is_match_std_path(path)
}
}
impl Settings for WorktreeSettings {
fn from_settings(content: &settings::SettingsContent) -> Self {
let worktree = content.project.worktree.clone();
let file_scan_exclusions = worktree.file_scan_exclusions.unwrap();
let file_scan_inclusions = worktree.file_scan_inclusions.unwrap();
let private_files = worktree.private_files.unwrap().0;
let hidden_files = worktree.hidden_files.unwrap();
let read_only_files = worktree.read_only_files.unwrap_or_default();
let parsed_file_scan_inclusions: Vec<String> = file_scan_inclusions
.iter()
.flat_map(|glob| {
Path::new(glob)
.ancestors()
.skip(1)
.map(|a| a.to_string_lossy().into())
})
.filter(|p: &String| !p.is_empty())
.collect();
Self {
prevent_sharing_in_public_channels: worktree.prevent_sharing_in_public_channels,
file_scan_exclusions: path_matchers(file_scan_exclusions, "file_scan_exclusions")
.log_err()
.unwrap_or_default(),
parent_dir_scan_inclusions: path_matchers(
parsed_file_scan_inclusions,
"file_scan_inclusions",
)
.unwrap(),
file_scan_inclusions: path_matchers(file_scan_inclusions, "file_scan_inclusions")
.unwrap(),
private_files: path_matchers(private_files, "private_files")
.log_err()
.unwrap_or_default(),
hidden_files: path_matchers(hidden_files, "hidden_files")
.log_err()
.unwrap_or_default(),
read_only_files: path_matchers(read_only_files, "read_only_files")
.log_err()
.unwrap_or_default(),
}
}
}
fn path_matchers(mut values: Vec<String>, context: &'static str) -> anyhow::Result<PathMatcher> {
values.sort();
PathMatcher::new(values, PathStyle::local())
.with_context(|| format!("Failed to parse globs from {}", context))
}