Add a setting for custom associations between languages and files (#9290)

Closes #5178

Release Notes:

- Added a `file_types` setting that can be used to associate languages
with file names and file extensions. For example, to interpret all `.c`
files as C++, and files called `MyLockFile` as TOML, add the following
to `settings.json`:

    ```json
    {
      "file_types": {
        "C++": ["c"],
        "TOML": ["MyLockFile"]
      }
    }
    ```

As with most zed settings, this can be configured on a per-directory
basis by including a local `.zed/settings.json` file in that directory.

---------

Co-authored-by: Marshall <marshall@zed.dev>
This commit is contained in:
Max Brunsfeld 2024-03-13 10:23:30 -07:00 committed by GitHub
parent 77de5689a3
commit 724c19a223
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
30 changed files with 640 additions and 415 deletions

View file

@ -8,7 +8,7 @@ use util::asset_str;
pub use keymap_file::KeymapFile;
pub use settings_file::*;
pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsStore};
pub use settings_store::{Settings, SettingsJsonSchemaParams, SettingsLocation, SettingsStore};
#[derive(RustEmbed)]
#[folder = "../../assets"]

View file

@ -86,9 +86,8 @@ pub trait Settings: 'static + Send + Sync {
});
}
/// path is a (worktree ID, Path)
#[track_caller]
fn get<'a>(path: Option<(usize, &Path)>, cx: &'a AppContext) -> &'a Self
fn get<'a>(path: Option<SettingsLocation>, cx: &'a AppContext) -> &'a Self
where
Self: Sized,
{
@ -120,6 +119,12 @@ pub trait Settings: 'static + Send + Sync {
}
}
#[derive(Clone, Copy)]
pub struct SettingsLocation<'a> {
pub worktree_id: usize,
pub path: &'a Path,
}
pub struct SettingsJsonSchemaParams<'a> {
pub staff_mode: bool,
pub language_names: &'a [String],
@ -168,7 +173,7 @@ trait AnySettingValue: 'static + Send + Sync {
custom: &[DeserializedSetting],
cx: &mut AppContext,
) -> Result<Box<dyn Any>>;
fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any;
fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any;
fn set_global_value(&mut self, value: Box<dyn Any>);
fn set_local_value(&mut self, root_id: usize, path: Arc<Path>, value: Box<dyn Any>);
fn json_schema(
@ -234,7 +239,7 @@ impl SettingsStore {
///
/// Panics if the given setting type has not been registered, or if there is no
/// value for this setting.
pub fn get<T: Settings>(&self, path: Option<(usize, &Path)>) -> &T {
pub fn get<T: Settings>(&self, path: Option<SettingsLocation>) -> &T {
self.setting_values
.get(&TypeId::of::<T>())
.unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
@ -659,10 +664,10 @@ impl<T: Settings> AnySettingValue for SettingValue<T> {
Ok(DeserializedSetting(Box::new(value)))
}
fn value_for_path(&self, path: Option<(usize, &Path)>) -> &dyn Any {
if let Some((root_id, path)) = path {
fn value_for_path(&self, path: Option<SettingsLocation>) -> &dyn Any {
if let Some(SettingsLocation { worktree_id, path }) = path {
for (settings_root_id, settings_path, value) in self.local_values.iter().rev() {
if root_id == *settings_root_id && path.starts_with(settings_path) {
if worktree_id == *settings_root_id && path.starts_with(settings_path) {
return value;
}
}
@ -1010,7 +1015,10 @@ mod tests {
.unwrap();
assert_eq!(
store.get::<UserSettings>(Some((1, Path::new("/root1/something")))),
store.get::<UserSettings>(Some(SettingsLocation {
worktree_id: 1,
path: Path::new("/root1/something"),
})),
&UserSettings {
name: "John Doe".to_string(),
age: 31,
@ -1018,7 +1026,10 @@ mod tests {
}
);
assert_eq!(
store.get::<UserSettings>(Some((1, Path::new("/root1/subdir/something")))),
store.get::<UserSettings>(Some(SettingsLocation {
worktree_id: 1,
path: Path::new("/root1/subdir/something")
})),
&UserSettings {
name: "Jane Doe".to_string(),
age: 31,
@ -1026,7 +1037,10 @@ mod tests {
}
);
assert_eq!(
store.get::<UserSettings>(Some((1, Path::new("/root2/something")))),
store.get::<UserSettings>(Some(SettingsLocation {
worktree_id: 1,
path: Path::new("/root2/something")
})),
&UserSettings {
name: "John Doe".to_string(),
age: 42,
@ -1034,7 +1048,10 @@ mod tests {
}
);
assert_eq!(
store.get::<MultiKeySettings>(Some((1, Path::new("/root2/something")))),
store.get::<MultiKeySettings>(Some(SettingsLocation {
worktree_id: 1,
path: Path::new("/root2/something")
})),
&MultiKeySettings {
key1: "a".to_string(),
key2: "b".to_string(),