Skip to content

Commit

Permalink
feat: Add editorconfig support
Browse files Browse the repository at this point in the history
Fixes #8534
  • Loading branch information
BuonOmo committed Aug 16, 2024
1 parent f7f5a25 commit f759684
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 45 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/language/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ async-trait.workspace = true
async-watch.workspace = true
clock.workspace = true
collections.workspace = true
ec4rs = "1.1"
futures.workspace = true
fuzzy.workspace = true
git.workspace = true
Expand Down
6 changes: 1 addition & 5 deletions crates/language/src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2624,11 +2624,7 @@ impl BufferSnapshot {
}

/// Returns the settings for the language at the given location.
pub fn settings_at<'a, D: ToOffset>(
&self,
position: D,
cx: &'a AppContext,
) -> &'a LanguageSettings {
pub fn settings_at<D: ToOffset>(&self, position: D, cx: &AppContext) -> LanguageSettings {
language_settings(self.language_at(position), self.file.as_ref(), cx)
}

Expand Down
71 changes: 67 additions & 4 deletions crates/language/src/language_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{File, Language, LanguageServerName};
use anyhow::Result;
use collections::{HashMap, HashSet};
use core::slice;
use ec4rs::property::{FinalNewline, IndentSize, IndentStyle, TabWidth, TrimTrailingWs};
use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
use gpui::AppContext;
use itertools::{Either, Itertools};
Expand Down Expand Up @@ -35,13 +36,75 @@ pub fn init(cx: &mut AppContext) {
}

/// Returns the settings for the specified language from the provided file.
pub fn language_settings<'a>(
pub fn language_settings(
language: Option<&Arc<Language>>,
file: Option<&Arc<dyn File>>,
cx: &'a AppContext,
) -> &'a LanguageSettings {
cx: &AppContext,
) -> LanguageSettings {
let language_name = language.map(|l| l.name());
all_language_settings(file, cx).language(language_name.as_deref())
let mut settings = all_language_settings(file, cx)
.language(language_name.as_deref())
.clone();
let path = file
.and_then(|f| f.as_local())
.map(|f| f.abs_path(cx))
.unwrap_or_else(|| std::path::PathBuf::new());
let mut cfg = ec4rs::properties_of(path).unwrap();
cfg.use_fallbacks();
let editor_config_content = LanguageSettingsContent {
tab_size: cfg
.get::<IndentSize>()
.map(|v| match v {
IndentSize::Value(u) => NonZeroU32::new(u as u32),
IndentSize::UseTabWidth => cfg
.get::<TabWidth>()
.map(|w| match w {
TabWidth::Value(u) => NonZeroU32::new(u as u32),
})
.ok()
.flatten(),
})
.ok()
.flatten(),
hard_tabs: cfg
.get::<IndentStyle>()
.map(|v| v.eq(&IndentStyle::Tabs))
.ok(),
soft_wrap: None,
preferred_line_length: None,
show_wrap_guides: None,
wrap_guides: None,
format_on_save: None,
remove_trailing_whitespace_on_save: cfg
.get::<TrimTrailingWs>()
.map(|v| match v {
TrimTrailingWs::Value(b) => b,
})
.ok(),
ensure_final_newline_on_save: cfg
.get::<FinalNewline>()
.map(|v| match v {
FinalNewline::Value(b) => b,
})
.ok(),
formatter: None,
prettier: None,
enable_language_server: None,
show_whitespaces: None,
extend_comment_on_newline: None,
inlay_hints: None,
use_autoclose: None,
code_actions_on_format: None,
indent_guides: None,
language_servers: None,
show_inline_completions: None,
use_auto_surround: None,
always_treat_brackets_as_autoclosed: None,
linked_edits: None,
tasks: None,
};
merge_settings(&mut settings, &editor_config_content);
settings
}

/// Returns the settings for all languages from the provided file.
Expand Down
8 changes: 2 additions & 6 deletions crates/multi_buffer/src/multi_buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1721,11 +1721,7 @@ impl MultiBuffer {
.and_then(|(buffer, offset, _)| buffer.read(cx).language_at(offset))
}

pub fn settings_at<'a, T: ToOffset>(
&self,
point: T,
cx: &'a AppContext,
) -> &'a LanguageSettings {
pub fn settings_at<T: ToOffset>(&self, point: T, cx: &AppContext) -> LanguageSettings {
let mut language = None;
let mut file = None;
if let Some((buffer, offset, _)) = self.point_to_buffer_offset(point, cx) {
Expand Down Expand Up @@ -3461,7 +3457,7 @@ impl MultiBufferSnapshot {
&'a self,
point: T,
cx: &'a AppContext,
) -> &'a LanguageSettings {
) -> LanguageSettings {
let mut language = None;
let mut file = None;
if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
Expand Down
2 changes: 1 addition & 1 deletion crates/project/src/lsp_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,7 +2278,7 @@ impl LspCommand for OnTypeFormatting {
.await?;

let options = buffer.update(&mut cx, |buffer, cx| {
lsp_formatting_options(language_settings(buffer.language(), buffer.file(), cx))
lsp_formatting_options(&language_settings(buffer.language(), buffer.file(), cx))
})?;

Ok(Self {
Expand Down
2 changes: 1 addition & 1 deletion crates/project/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7029,7 +7029,7 @@ impl Project {
cx: &mut ModelContext<Self>,
) -> Task<Result<Option<Transaction>>> {
let options = buffer.update(cx, |buffer, cx| {
lsp_command::lsp_formatting_options(language_settings(
lsp_command::lsp_formatting_options(&language_settings(
buffer.language_at(position).as_ref(),
buffer.file(),
cx,
Expand Down
67 changes: 39 additions & 28 deletions crates/project/src/project_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,46 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) {
#[gpui::test]
async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) {
init_test(cx);

let fs = FakeFs::new(cx.executor());
fs.insert_tree(
"/the-root",
json!({
".zed": {
"settings.json": r#"{ "tab_size": 8 }"#,
"tasks.json": r#"[{
let dir = temp_tree(json!({
".zed": {
"settings.json": r#"{ "tab_size": 8, "ensure_final_newline_on_save": false }"#,
"tasks.json": r#"[{
"label": "cargo check",
"command": "cargo",
"args": ["check", "--all"]
},]"#,
},
"a": {
"a.rs": "fn a() {\n A\n}"
},
"b": {
".zed": {
"settings.json": r#"{ "tab_size": 2 }"#,
"tasks.json": r#"[{
},
".editorconfig": r#"root = true
[*.rs]
insert_final_newline = true
"#,
"a": {
"a.rs": "fn a() {\n A\n}"
},
"b": {
".zed": {
"settings.json": r#"{ "tab_size": 2 }"#,
"tasks.json": r#"[{
"label": "cargo check",
"command": "cargo",
"args": ["check"]
},]"#,
},
"b.rs": "fn b() {\n B\n}"
}
}),
)
.await;
},
"b.rs": "fn b() {\n B\n}"
},
"c": {
"c.rs": "fn c() {\n C\n}",
".editorconfig": r#"[*.rs]
indent_size = 4
"#,
}
}));

let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
let path = dir.path().as_ref();
let fs = FakeFs::new(cx.executor());
fs.insert_tree_from_real_fs(path, path).await;

let project = Project::test(fs.clone(), [path], cx).await;
let worktree = project.update(cx, |project, cx| project.worktrees(cx).next().unwrap());
let task_context = TaskContext::default();

Expand All @@ -136,7 +144,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
});
let global_task_source_kind = TaskSourceKind::Worktree {
id: worktree_id,
abs_path: PathBuf::from("/the-root/.zed/tasks.json"),
abs_path: PathBuf::from(format!("{}/.zed/tasks.json", path.display())),
id_base: "local_tasks_for_worktree".into(),
};

Expand Down Expand Up @@ -194,7 +202,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
(
TaskSourceKind::Worktree {
id: worktree_id,
abs_path: PathBuf::from("/the-root/b/.zed/tasks.json"),
abs_path: PathBuf::from(format!("{}/b/.zed/tasks.json", path.display())),
id_base: "local_tasks_for_worktree".into(),
},
"cargo check".to_string(),
Expand Down Expand Up @@ -235,7 +243,10 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
cx.update(|cx| {
project.update(cx, |project, cx| {
project.task_inventory().update(cx, |inventory, cx| {
inventory.remove_local_static_source(Path::new("/the-root/.zed/tasks.json"));
inventory.remove_local_static_source(&PathBuf::from(format!(
"{}/.zed/tasks.json",
path.display()
)));
inventory.add_source(
global_task_source_kind.clone(),
|tx, cx| StaticSource::new(TrackedFile::new(rx, tx, cx)),
Expand Down Expand Up @@ -267,7 +278,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
(
TaskSourceKind::Worktree {
id: worktree_id,
abs_path: PathBuf::from("/the-root/.zed/tasks.json"),
abs_path: PathBuf::from(format!("{}/.zed/tasks.json", path.display())),
id_base: "local_tasks_for_worktree".into(),
},
"cargo check".to_string(),
Expand All @@ -284,7 +295,7 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
(
TaskSourceKind::Worktree {
id: worktree_id,
abs_path: PathBuf::from("/the-root/b/.zed/tasks.json"),
abs_path: PathBuf::from(format!("{}/b/.zed/tasks.json", path.display())),
id_base: "local_tasks_for_worktree".into(),
},
"cargo check".to_string(),
Expand Down

0 comments on commit f759684

Please sign in to comment.