Add glob support for custom file type language (#12043)

Release Notes:

- Added glob support for file_types configuration
([#10765](https://github.com/zed-industries/zed/issues/10765)).

`file_types` can now be written like this:

```json
"file_types": {
  "Dockerfile": [
    "Dockerfile",
    "Dockerfile.*",
  ]
}
```
This commit is contained in:
Joshua Farayola 2024-05-20 09:13:35 +01:00 committed by GitHub
parent 4e935f9f0f
commit ab7ce32888
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 15 deletions

View file

@ -184,6 +184,10 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
settings.file_types.extend([
("TypeScript".into(), vec!["js".into()]),
("C++".into(), vec!["c".into()]),
(
"Dockerfile".into(),
vec!["Dockerfile".into(), "Dockerfile.*".into()],
),
]);
})
});
@ -223,6 +227,14 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
},
..Default::default()
},
LanguageConfig {
name: "Dockerfile".into(),
matcher: LanguageMatcher {
path_suffixes: vec!["Dockerfile".to_string()],
..Default::default()
},
..Default::default()
},
] {
languages.add(Arc::new(Language::new(config, None)));
}
@ -237,6 +249,11 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
.await
.unwrap();
assert_eq!(language.name().as_ref(), "C++");
let language = cx
.read(|cx| languages.language_for_file(&file("Dockerfile.dev"), None, cx))
.await
.unwrap();
assert_eq!(language.name().as_ref(), "Dockerfile");
}
fn file(path: &str) -> Arc<dyn File> {

View file

@ -14,6 +14,7 @@ use futures::{
future::Shared,
Future, FutureExt as _,
};
use globset::GlobSet;
use gpui::{AppContext, BackgroundExecutor, Task};
use lsp::LanguageServerId;
use parking_lot::{Mutex, RwLock};
@ -506,23 +507,25 @@ impl LanguageRegistry {
self: &Arc<Self>,
path: &Path,
content: Option<&Rope>,
user_file_types: Option<&HashMap<Arc<str>, Vec<String>>>,
user_file_types: Option<&HashMap<Arc<str>, GlobSet>>,
) -> impl Future<Output = Result<Arc<Language>>> {
let filename = path.file_name().and_then(|name| name.to_str());
let extension = path.extension_or_hidden_file_name();
let path_suffixes = [extension, filename];
let empty = Vec::new();
let empty = GlobSet::empty();
let rx = self.get_or_load_language(move |language_name, config| {
let path_matches_default_suffix = config
.path_suffixes
.iter()
.any(|suffix| path_suffixes.contains(&Some(suffix.as_str())));
let path_matches_custom_suffix = user_file_types
let custom_suffixes = user_file_types
.and_then(|types| types.get(language_name))
.unwrap_or(&empty)
.unwrap_or(&empty);
let path_matches_custom_suffix = path_suffixes
.iter()
.any(|suffix| path_suffixes.contains(&Some(suffix.as_str())));
.map(|suffix| suffix.unwrap_or(""))
.any(|suffix| custom_suffixes.is_match(suffix));
let content_matches = content.zip(config.first_line_pattern.as_ref()).map_or(
false,
|(content, pattern)| {

View file

@ -3,7 +3,7 @@
use crate::{File, Language, LanguageServerName};
use anyhow::Result;
use collections::{HashMap, HashSet};
use globset::GlobMatcher;
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use gpui::AppContext;
use itertools::{Either, Itertools};
use schemars::{
@ -55,7 +55,7 @@ pub struct AllLanguageSettings {
pub inline_completions: InlineCompletionSettings,
defaults: LanguageSettings,
languages: HashMap<Arc<str>, LanguageSettings>,
pub(crate) file_types: HashMap<Arc<str>, Vec<String>>,
pub(crate) file_types: HashMap<Arc<str>, GlobSet>,
}
/// The settings for a particular language.
@ -573,7 +573,7 @@ impl settings::Settings for AllLanguageSettings {
.and_then(|c| c.disabled_globs.as_ref())
.ok_or_else(Self::missing_default)?;
let mut file_types: HashMap<Arc<str>, Vec<String>> = HashMap::default();
let mut file_types: HashMap<Arc<str>, GlobSet> = HashMap::default();
for user_settings in sources.customizations() {
if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
copilot_enabled = Some(copilot);
@ -611,10 +611,13 @@ impl settings::Settings for AllLanguageSettings {
}
for (language, suffixes) in &user_settings.file_types {
file_types
.entry(language.clone())
.or_default()
.extend_from_slice(suffixes);
let mut builder = GlobSetBuilder::new();
for suffix in suffixes {
builder.add(Glob::new(suffix)?);
}
file_types.insert(language.clone(), builder.build()?);
}
}