Support absolute disabled_globs
(#25755)
Closes: #25556 We were always comparing `disabled_globs` against the relative file path, we'll now use the absolute path if the glob is also absolute. Release Notes: - Support absolute globs in `edit_predictions.disabled_globs`
This commit is contained in:
parent
c5632f8c31
commit
6eb2ffe77a
8 changed files with 191 additions and 16 deletions
|
@ -829,6 +829,8 @@
|
|||
// A list of globs representing files that edit predictions should be disabled for.
|
||||
// There's a sensible default list of globs already included.
|
||||
// Any addition to this list will be merged with the default list.
|
||||
// Globs are matched relative to the worktree root,
|
||||
// except when starting with a slash (/) or equivalent in Windows.
|
||||
"disabled_globs": [
|
||||
"**/.env*",
|
||||
"**/*.pem",
|
||||
|
|
|
@ -192,7 +192,7 @@ impl EditPredictionProvider for CopilotCompletionProvider {
|
|||
fn discard(&mut self, cx: &mut Context<Self>) {
|
||||
let settings = AllLanguageSettings::get_global(cx);
|
||||
|
||||
let copilot_enabled = settings.show_inline_completions(None, cx);
|
||||
let copilot_enabled = settings.show_edit_predictions(None, cx);
|
||||
|
||||
if !copilot_enabled {
|
||||
return;
|
||||
|
|
|
@ -5005,7 +5005,7 @@ impl Editor {
|
|||
return Some(true);
|
||||
};
|
||||
let settings = all_language_settings(Some(file), cx);
|
||||
Some(settings.inline_completions_enabled_for_path(file.path()))
|
||||
Some(settings.edit_predictions_enabled_for_file(file, cx))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
|
|
@ -1739,6 +1739,7 @@ mod tests {
|
|||
let file = TestFile {
|
||||
path: Path::new("").into(),
|
||||
root_name: String::new(),
|
||||
local_root: None,
|
||||
};
|
||||
assert_eq!(path_for_file(&file, 0, false, cx), None);
|
||||
}
|
||||
|
|
|
@ -456,7 +456,7 @@ impl InlineCompletionButton {
|
|||
}
|
||||
|
||||
let settings = AllLanguageSettings::get_global(cx);
|
||||
let globally_enabled = settings.show_inline_completions(None, cx);
|
||||
let globally_enabled = settings.show_edit_predictions(None, cx);
|
||||
menu = menu.toggleable_entry("All Files", globally_enabled, IconPosition::Start, None, {
|
||||
let fs = fs.clone();
|
||||
move |_, cx| toggle_inline_completions_globally(fs.clone(), cx)
|
||||
|
@ -702,7 +702,7 @@ impl InlineCompletionButton {
|
|||
Some(
|
||||
file.map(|file| {
|
||||
all_language_settings(Some(file), cx)
|
||||
.inline_completions_enabled_for_path(file.path())
|
||||
.edit_predictions_enabled_for_file(file, cx)
|
||||
})
|
||||
.unwrap_or(true),
|
||||
)
|
||||
|
@ -825,7 +825,7 @@ async fn open_disabled_globs_setting_in_editor(
|
|||
}
|
||||
|
||||
fn toggle_inline_completions_globally(fs: Arc<dyn Fs>, cx: &mut App) {
|
||||
let show_edit_predictions = all_language_settings(None, cx).show_inline_completions(None, cx);
|
||||
let show_edit_predictions = all_language_settings(None, cx).show_edit_predictions(None, cx);
|
||||
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
|
||||
file.defaults.show_edit_predictions = Some(!show_edit_predictions)
|
||||
});
|
||||
|
@ -845,7 +845,7 @@ fn toggle_show_inline_completions_for_language(
|
|||
cx: &mut App,
|
||||
) {
|
||||
let show_edit_predictions =
|
||||
all_language_settings(None, cx).show_inline_completions(Some(&language), cx);
|
||||
all_language_settings(None, cx).show_edit_predictions(Some(&language), cx);
|
||||
update_settings_file::<AllLanguageSettings>(fs, cx, move |file, _| {
|
||||
file.languages
|
||||
.entry(language.name())
|
||||
|
|
|
@ -4477,6 +4477,7 @@ impl IndentSize {
|
|||
pub struct TestFile {
|
||||
pub path: Arc<Path>,
|
||||
pub root_name: String,
|
||||
pub local_root: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -4490,8 +4491,12 @@ impl File for TestFile {
|
|||
}
|
||||
|
||||
fn as_local(&self) -> Option<&dyn LocalFile> {
|
||||
if self.local_root.is_some() {
|
||||
Some(self)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn disk_state(&self) -> DiskState {
|
||||
unimplemented!()
|
||||
|
@ -4518,6 +4523,23 @@ impl File for TestFile {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
impl LocalFile for TestFile {
|
||||
fn abs_path(&self, _cx: &App) -> PathBuf {
|
||||
PathBuf::from(self.local_root.as_ref().unwrap())
|
||||
.join(&self.root_name)
|
||||
.join(self.path.as_ref())
|
||||
}
|
||||
|
||||
fn load(&self, _cx: &App) -> Task<Result<String>> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn load_bytes(&self, _cx: &App) -> Task<Result<Vec<u8>>> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn contiguous_ranges(
|
||||
values: impl Iterator<Item = u32>,
|
||||
max_len: usize,
|
||||
|
|
|
@ -254,6 +254,7 @@ fn file(path: &str) -> Arc<dyn File> {
|
|||
Arc::new(TestFile {
|
||||
path: Path::new(path).into(),
|
||||
root_name: "zed".into(),
|
||||
local_root: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -231,13 +231,33 @@ pub struct EditPredictionSettings {
|
|||
/// A list of globs representing files that edit predictions should be disabled for.
|
||||
/// This list adds to a pre-existing, sensible default set of globs.
|
||||
/// Any additional ones you add are combined with them.
|
||||
pub disabled_globs: Vec<GlobMatcher>,
|
||||
pub disabled_globs: Vec<DisabledGlob>,
|
||||
/// Configures how edit predictions are displayed in the buffer.
|
||||
pub mode: EditPredictionsMode,
|
||||
/// Settings specific to GitHub Copilot.
|
||||
pub copilot: CopilotSettings,
|
||||
}
|
||||
|
||||
impl EditPredictionSettings {
|
||||
/// Returns whether edit predictions are enabled for the given path.
|
||||
pub fn enabled_for_file(&self, file: &Arc<dyn File>, cx: &App) -> bool {
|
||||
!self.disabled_globs.iter().any(|glob| {
|
||||
if glob.is_absolute {
|
||||
file.as_local()
|
||||
.map_or(false, |local| glob.matcher.is_match(local.abs_path(cx)))
|
||||
} else {
|
||||
glob.matcher.is_match(file.path())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DisabledGlob {
|
||||
matcher: GlobMatcher,
|
||||
is_absolute: bool,
|
||||
}
|
||||
|
||||
/// The mode in which edit predictions should be displayed.
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
|
@ -965,16 +985,12 @@ impl AllLanguageSettings {
|
|||
}
|
||||
|
||||
/// Returns whether edit predictions are enabled for the given path.
|
||||
pub fn inline_completions_enabled_for_path(&self, path: &Path) -> bool {
|
||||
!self
|
||||
.edit_predictions
|
||||
.disabled_globs
|
||||
.iter()
|
||||
.any(|glob| glob.is_match(path))
|
||||
pub fn edit_predictions_enabled_for_file(&self, file: &Arc<dyn File>, cx: &App) -> bool {
|
||||
self.edit_predictions.enabled_for_file(file, cx)
|
||||
}
|
||||
|
||||
/// Returns whether edit predictions are enabled for the given language and path.
|
||||
pub fn show_inline_completions(&self, language: Option<&Arc<Language>>, cx: &App) -> bool {
|
||||
pub fn show_edit_predictions(&self, language: Option<&Arc<Language>>, cx: &App) -> bool {
|
||||
self.language(None, language.map(|l| l.name()).as_ref(), cx)
|
||||
.show_edit_predictions
|
||||
}
|
||||
|
@ -1199,7 +1215,12 @@ impl settings::Settings for AllLanguageSettings {
|
|||
},
|
||||
disabled_globs: completion_globs
|
||||
.iter()
|
||||
.filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
|
||||
.filter_map(|g| {
|
||||
Some(DisabledGlob {
|
||||
matcher: globset::Glob::new(g).ok()?.compile_matcher(),
|
||||
is_absolute: Path::new(g).is_absolute(),
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
mode: edit_predictions_mode,
|
||||
copilot: copilot_settings,
|
||||
|
@ -1357,6 +1378,8 @@ pub struct PrettierSettings {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::TestAppContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -1401,6 +1424,132 @@ mod tests {
|
|||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_edit_predictions_enabled_for_file(cx: &mut TestAppContext) {
|
||||
use crate::TestFile;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let cx = cx.app.borrow_mut();
|
||||
|
||||
let build_settings = |globs: &[&str]| -> EditPredictionSettings {
|
||||
EditPredictionSettings {
|
||||
disabled_globs: globs
|
||||
.iter()
|
||||
.map(|glob_str| {
|
||||
#[cfg(windows)]
|
||||
let glob_str = {
|
||||
let mut g = String::new();
|
||||
|
||||
if glob_str.starts_with('/') {
|
||||
g.push_str("C:");
|
||||
}
|
||||
|
||||
g.push_str(&glob_str.replace('/', "\\"));
|
||||
g
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let glob_str = glob_str.as_str();
|
||||
|
||||
DisabledGlob {
|
||||
matcher: globset::Glob::new(glob_str).unwrap().compile_matcher(),
|
||||
is_absolute: Path::new(glob_str).is_absolute(),
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
const WORKTREE_NAME: &str = "project";
|
||||
let make_test_file = |segments: &[&str]| -> Arc<dyn File> {
|
||||
let mut path_buf = PathBuf::new();
|
||||
path_buf.extend(segments);
|
||||
|
||||
Arc::new(TestFile {
|
||||
path: path_buf.as_path().into(),
|
||||
root_name: WORKTREE_NAME.to_string(),
|
||||
local_root: Some(PathBuf::from(if cfg!(windows) {
|
||||
"C:\\absolute\\"
|
||||
} else {
|
||||
"/absolute/"
|
||||
})),
|
||||
})
|
||||
};
|
||||
|
||||
let test_file = make_test_file(&["src", "test", "file.rs"]);
|
||||
|
||||
// Test relative globs
|
||||
let settings = build_settings(&["*.rs"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["*.txt"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test absolute globs
|
||||
let settings = build_settings(&["/absolute/**/*.rs"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["/other/**/*.rs"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test exact path match relative
|
||||
let settings = build_settings(&["src/test/file.rs"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["src/test/otherfile.rs"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test exact path match absolute
|
||||
let settings = build_settings(&[&format!("/absolute/{}/src/test/file.rs", WORKTREE_NAME)]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["/other/test/otherfile.rs"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test * glob
|
||||
let settings = build_settings(&["*"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["*.txt"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test **/* glob
|
||||
let settings = build_settings(&["**/*"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["other/**/*"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test directory/** glob
|
||||
let settings = build_settings(&["src/**"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
let test_file_root: Arc<dyn File> = Arc::new(TestFile {
|
||||
path: PathBuf::from("file.rs").as_path().into(),
|
||||
root_name: WORKTREE_NAME.to_string(),
|
||||
local_root: Some(PathBuf::from("/absolute/")),
|
||||
});
|
||||
assert!(settings.enabled_for_file(&test_file_root, &cx));
|
||||
|
||||
let settings = build_settings(&["other/**"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test **/directory/* glob
|
||||
let settings = build_settings(&["**/test/*"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["**/other/*"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test multiple globs
|
||||
let settings = build_settings(&["*.rs", "*.txt", "src/**"]);
|
||||
assert!(!settings.enabled_for_file(&test_file, &cx));
|
||||
let settings = build_settings(&["*.txt", "*.md", "other/**"]);
|
||||
assert!(settings.enabled_for_file(&test_file, &cx));
|
||||
|
||||
// Test dot files
|
||||
let dot_file = make_test_file(&[".config", "settings.json"]);
|
||||
let settings = build_settings(&[".*/**"]);
|
||||
assert!(!settings.enabled_for_file(&dot_file, &cx));
|
||||
|
||||
let dot_env_file = make_test_file(&[".env"]);
|
||||
let settings = build_settings(&[".env"]);
|
||||
assert!(!settings.enabled_for_file(&dot_env_file, &cx));
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_resolve_language_servers() {
|
||||
fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue