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:
parent
4e935f9f0f
commit
ab7ce32888
4 changed files with 39 additions and 15 deletions
|
@ -184,6 +184,10 @@ async fn test_language_for_file_with_custom_file_types(cx: &mut TestAppContext)
|
||||||
settings.file_types.extend([
|
settings.file_types.extend([
|
||||||
("TypeScript".into(), vec!["js".into()]),
|
("TypeScript".into(), vec!["js".into()]),
|
||||||
("C++".into(), vec!["c".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()
|
..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)));
|
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
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(language.name().as_ref(), "C++");
|
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> {
|
fn file(path: &str) -> Arc<dyn File> {
|
||||||
|
|
|
@ -14,6 +14,7 @@ use futures::{
|
||||||
future::Shared,
|
future::Shared,
|
||||||
Future, FutureExt as _,
|
Future, FutureExt as _,
|
||||||
};
|
};
|
||||||
|
use globset::GlobSet;
|
||||||
use gpui::{AppContext, BackgroundExecutor, Task};
|
use gpui::{AppContext, BackgroundExecutor, Task};
|
||||||
use lsp::LanguageServerId;
|
use lsp::LanguageServerId;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
@ -506,23 +507,25 @@ impl LanguageRegistry {
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
content: Option<&Rope>,
|
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>>> {
|
) -> impl Future<Output = Result<Arc<Language>>> {
|
||||||
let filename = path.file_name().and_then(|name| name.to_str());
|
let filename = path.file_name().and_then(|name| name.to_str());
|
||||||
let extension = path.extension_or_hidden_file_name();
|
let extension = path.extension_or_hidden_file_name();
|
||||||
let path_suffixes = [extension, filename];
|
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 rx = self.get_or_load_language(move |language_name, config| {
|
||||||
let path_matches_default_suffix = config
|
let path_matches_default_suffix = config
|
||||||
.path_suffixes
|
.path_suffixes
|
||||||
.iter()
|
.iter()
|
||||||
.any(|suffix| path_suffixes.contains(&Some(suffix.as_str())));
|
.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))
|
.and_then(|types| types.get(language_name))
|
||||||
.unwrap_or(&empty)
|
.unwrap_or(&empty);
|
||||||
|
let path_matches_custom_suffix = path_suffixes
|
||||||
.iter()
|
.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(
|
let content_matches = content.zip(config.first_line_pattern.as_ref()).map_or(
|
||||||
false,
|
false,
|
||||||
|(content, pattern)| {
|
|(content, pattern)| {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
use crate::{File, Language, LanguageServerName};
|
use crate::{File, Language, LanguageServerName};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use globset::GlobMatcher;
|
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
|
||||||
use gpui::AppContext;
|
use gpui::AppContext;
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use schemars::{
|
use schemars::{
|
||||||
|
@ -55,7 +55,7 @@ pub struct AllLanguageSettings {
|
||||||
pub inline_completions: InlineCompletionSettings,
|
pub inline_completions: InlineCompletionSettings,
|
||||||
defaults: LanguageSettings,
|
defaults: LanguageSettings,
|
||||||
languages: HashMap<Arc<str>, 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.
|
/// The settings for a particular language.
|
||||||
|
@ -573,7 +573,7 @@ impl settings::Settings for AllLanguageSettings {
|
||||||
.and_then(|c| c.disabled_globs.as_ref())
|
.and_then(|c| c.disabled_globs.as_ref())
|
||||||
.ok_or_else(Self::missing_default)?;
|
.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() {
|
for user_settings in sources.customizations() {
|
||||||
if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
|
if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
|
||||||
copilot_enabled = Some(copilot);
|
copilot_enabled = Some(copilot);
|
||||||
|
@ -611,10 +611,13 @@ impl settings::Settings for AllLanguageSettings {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (language, suffixes) in &user_settings.file_types {
|
for (language, suffixes) in &user_settings.file_types {
|
||||||
file_types
|
let mut builder = GlobSetBuilder::new();
|
||||||
.entry(language.clone())
|
|
||||||
.or_default()
|
for suffix in suffixes {
|
||||||
.extend_from_slice(suffixes);
|
builder.add(Glob::new(suffix)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_types.insert(language.clone(), builder.build()?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -651,18 +651,19 @@ The result is still `)))` and not `))))))`, which is what it would be by default
|
||||||
## File Types
|
## File Types
|
||||||
|
|
||||||
- Setting: `file_types`
|
- Setting: `file_types`
|
||||||
- Description: Configure how Zed selects a language for a file based on its filename or extension.
|
- Description: Configure how Zed selects a language for a file based on its filename or extension. Supports glob entries.
|
||||||
- Default: `{}`
|
- Default: `{}`
|
||||||
|
|
||||||
**Examples**
|
**Examples**
|
||||||
|
|
||||||
To interpret all `.c` files as C++, and files called `MyLockFile` as TOML:
|
To interpret all `.c` files as C++, files called `MyLockFile` as TOML and files starting with `Dockerfile` as Dockerfile:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"file_types": {
|
"file_types": {
|
||||||
"C++": ["c"],
|
"C++": ["c"],
|
||||||
"TOML": ["MyLockFile"]
|
"TOML": ["MyLockFile"],
|
||||||
|
"Dockerfile": ["Dockerfile*"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue