mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-26 07:12:03 +03:00
Define language settings in the language crate
This commit is contained in:
parent
9ae10a5dd9
commit
39618ae32d
8
Cargo.lock
generated
8
Cargo.lock
generated
@ -1341,6 +1341,7 @@ dependencies = [
|
||||
"env_logger 0.9.3",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"language",
|
||||
"picker",
|
||||
"project",
|
||||
"serde_json",
|
||||
@ -1408,6 +1409,7 @@ dependencies = [
|
||||
"fs",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
"settings",
|
||||
"smol",
|
||||
"theme",
|
||||
@ -2034,6 +2036,7 @@ dependencies = [
|
||||
"pulldown-cmark",
|
||||
"rand 0.8.5",
|
||||
"rpc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"settings",
|
||||
@ -2243,6 +2246,7 @@ dependencies = [
|
||||
"env_logger 0.9.3",
|
||||
"fuzzy",
|
||||
"gpui",
|
||||
"language",
|
||||
"menu",
|
||||
"picker",
|
||||
"postage",
|
||||
@ -3427,6 +3431,7 @@ dependencies = [
|
||||
"futures 0.3.28",
|
||||
"fuzzy",
|
||||
"git",
|
||||
"glob",
|
||||
"gpui",
|
||||
"indoc",
|
||||
"lazy_static",
|
||||
@ -3437,6 +3442,7 @@ dependencies = [
|
||||
"rand 0.8.5",
|
||||
"regex",
|
||||
"rpc",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
@ -4872,6 +4878,7 @@ dependencies = [
|
||||
"editor",
|
||||
"futures 0.3.28",
|
||||
"gpui",
|
||||
"language",
|
||||
"menu",
|
||||
"postage",
|
||||
"project",
|
||||
@ -7818,6 +7825,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
name = "vim"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assets",
|
||||
"async-compat",
|
||||
"async-trait",
|
||||
|
@ -65,12 +65,14 @@ impl Setting for AutoUpdateSetting {
|
||||
|
||||
type FileContent = Option<bool>;
|
||||
|
||||
fn load(default_value: &Option<bool>, user_values: &[&Option<bool>], _: &AppContext) -> Self {
|
||||
Self(
|
||||
Self::json_merge(default_value, user_values)
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
)
|
||||
fn load(
|
||||
default_value: &Option<bool>,
|
||||
user_values: &[&Option<bool>],
|
||||
_: &AppContext,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(
|
||||
Self::json_merge(default_value, user_values)?.ok_or_else(Self::missing_default)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,17 +350,18 @@ impl settings::Setting for TelemetrySettings {
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
Self {
|
||||
diagnostics: user_values
|
||||
.first()
|
||||
.and_then(|v| v.diagnostics)
|
||||
.unwrap_or(default_value.diagnostics.unwrap()),
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
diagnostics: user_values.first().and_then(|v| v.diagnostics).unwrap_or(
|
||||
default_value
|
||||
.diagnostics
|
||||
.ok_or_else(Self::missing_default)?,
|
||||
),
|
||||
metrics: user_values
|
||||
.first()
|
||||
.and_then(|v| v.metrics)
|
||||
.unwrap_or(default_value.metrics.unwrap()),
|
||||
}
|
||||
.unwrap_or(default_value.metrics.ok_or_else(Self::missing_default)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,7 +186,10 @@ impl TestServer {
|
||||
})
|
||||
});
|
||||
|
||||
cx.update(|cx| client::init(&client, cx));
|
||||
cx.update(|cx| {
|
||||
client::init(&client, cx);
|
||||
language::init(cx);
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||
|
@ -18,6 +18,7 @@ use gpui::{
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, Formatter},
|
||||
tree_sitter_rust, Anchor, Diagnostic, DiagnosticEntry, FakeLspAdapter, Language,
|
||||
LanguageConfig, OffsetRangeExt, Point, Rope,
|
||||
};
|
||||
@ -26,7 +27,7 @@ use lsp::LanguageServerId;
|
||||
use project::{search::SearchQuery, DiagnosticSummary, HoverBlockKind, Project, ProjectPath};
|
||||
use rand::prelude::*;
|
||||
use serde_json::json;
|
||||
use settings::{Formatter, Settings};
|
||||
use settings::{SettingsStore};
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
env, future, mem,
|
||||
@ -4219,10 +4220,12 @@ async fn test_formatting_buffer(
|
||||
// Ensure buffer can be formatted using an external command. Notice how the
|
||||
// host's configuration is honored as opposed to using the guest's settings.
|
||||
cx_a.update(|cx| {
|
||||
cx.update_global(|settings: &mut Settings, _| {
|
||||
settings.editor_defaults.formatter = Some(Formatter::External {
|
||||
command: "awk".to_string(),
|
||||
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()],
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |file| {
|
||||
file.defaults.formatter = Some(Formatter::External {
|
||||
command: "awk".into(),
|
||||
arguments: vec!["{sub(/two/,\"{buffer_path}\")}1".to_string()].into(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -23,6 +23,7 @@ workspace = { path = "../workspace" }
|
||||
[dev-dependencies]
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
serde_json.workspace = true
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
@ -294,14 +294,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_command_palette(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
|
||||
deterministic.forbid_parking();
|
||||
let app_state = cx.update(AppState::test);
|
||||
|
||||
cx.update(|cx| {
|
||||
editor::init(cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
init(cx);
|
||||
});
|
||||
let app_state = init_test(cx);
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
@ -369,4 +362,15 @@ mod tests {
|
||||
assert!(palette.delegate().matches.is_empty())
|
||||
});
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||
cx.update(|cx| {
|
||||
let app_state = AppState::test(cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
init(cx);
|
||||
app_state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ use gpui::{
|
||||
actions, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task, WeakModelHandle,
|
||||
};
|
||||
use language::{
|
||||
language_settings::{all_language_settings, language_settings},
|
||||
point_from_lsp, point_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, Language, PointUtf16,
|
||||
ToPointUtf16,
|
||||
};
|
||||
@ -17,7 +18,7 @@ use log::{debug, error};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use node_runtime::NodeRuntime;
|
||||
use request::{LogMessage, StatusNotification};
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use smol::{fs, io::BufReader, stream::StreamExt};
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
@ -302,56 +303,34 @@ impl Copilot {
|
||||
node_runtime: Arc<NodeRuntime>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Self {
|
||||
cx.observe_global::<Settings, _>({
|
||||
let http = http.clone();
|
||||
let node_runtime = node_runtime.clone();
|
||||
move |this, cx| {
|
||||
if cx.global::<Settings>().features.copilot {
|
||||
if matches!(this.server, CopilotServer::Disabled) {
|
||||
let start_task = cx
|
||||
.spawn({
|
||||
let http = http.clone();
|
||||
let node_runtime = node_runtime.clone();
|
||||
move |this, cx| {
|
||||
Self::start_language_server(http, node_runtime, this, cx)
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
this.server = CopilotServer::Starting { task: start_task };
|
||||
cx.notify();
|
||||
}
|
||||
} else {
|
||||
this.server = CopilotServer::Disabled;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
let mut this = Self {
|
||||
http,
|
||||
node_runtime,
|
||||
server: CopilotServer::Disabled,
|
||||
buffers: Default::default(),
|
||||
};
|
||||
this.enable_or_disable_copilot(cx);
|
||||
cx.observe_global::<SettingsStore, _>(move |this, cx| this.enable_or_disable_copilot(cx))
|
||||
.detach();
|
||||
this
|
||||
}
|
||||
|
||||
if cx.global::<Settings>().features.copilot {
|
||||
let start_task = cx
|
||||
.spawn({
|
||||
let http = http.clone();
|
||||
let node_runtime = node_runtime.clone();
|
||||
move |this, cx| async {
|
||||
Self::start_language_server(http, node_runtime, this, cx).await
|
||||
}
|
||||
})
|
||||
.shared();
|
||||
|
||||
Self {
|
||||
http,
|
||||
node_runtime,
|
||||
server: CopilotServer::Starting { task: start_task },
|
||||
buffers: Default::default(),
|
||||
fn enable_or_disable_copilot(&mut self, cx: &mut ModelContext<Copilot>) {
|
||||
let http = self.http.clone();
|
||||
let node_runtime = self.node_runtime.clone();
|
||||
if all_language_settings(None, cx).copilot_enabled(None, None) {
|
||||
if matches!(self.server, CopilotServer::Disabled) {
|
||||
let start_task = cx
|
||||
.spawn({
|
||||
move |this, cx| Self::start_language_server(http, node_runtime, this, cx)
|
||||
})
|
||||
.shared();
|
||||
self.server = CopilotServer::Starting { task: start_task };
|
||||
cx.notify();
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
http,
|
||||
node_runtime,
|
||||
server: CopilotServer::Disabled,
|
||||
buffers: Default::default(),
|
||||
}
|
||||
self.server = CopilotServer::Disabled;
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,13 +784,14 @@ impl Copilot {
|
||||
let snapshot = registered_buffer.report_changes(buffer, cx);
|
||||
let buffer = buffer.read(cx);
|
||||
let uri = registered_buffer.uri.clone();
|
||||
let settings = cx.global::<Settings>();
|
||||
let position = position.to_point_utf16(buffer);
|
||||
let language = buffer.language_at(position);
|
||||
let language_name = language.map(|language| language.name());
|
||||
let language_name = language_name.as_deref();
|
||||
let tab_size = settings.tab_size(language_name);
|
||||
let hard_tabs = settings.hard_tabs(language_name);
|
||||
let settings = language_settings(
|
||||
None,
|
||||
buffer.language_at(position).map(|l| l.name()).as_deref(),
|
||||
cx,
|
||||
);
|
||||
let tab_size = settings.tab_size;
|
||||
let hard_tabs = settings.hard_tabs;
|
||||
let relative_path = buffer
|
||||
.file()
|
||||
.map(|file| file.path().to_path_buf())
|
||||
|
@ -15,6 +15,7 @@ editor = { path = "../editor" }
|
||||
fs = { path = "../fs" }
|
||||
context_menu = { path = "../context_menu" }
|
||||
gpui = { path = "../gpui" }
|
||||
language = { path = "../language" }
|
||||
settings = { path = "../settings" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
@ -9,6 +9,7 @@ use gpui::{
|
||||
AnyElement, AppContext, AsyncAppContext, Element, Entity, MouseState, Subscription, View,
|
||||
ViewContext, ViewHandle, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use language::language_settings::{self, all_language_settings, AllLanguageSettings};
|
||||
use settings::{update_settings_file, Settings, SettingsStore};
|
||||
use std::{path::Path, sync::Arc};
|
||||
use util::{paths, ResultExt};
|
||||
@ -40,12 +41,12 @@ impl View for CopilotButton {
|
||||
}
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
let settings = cx.global::<Settings>();
|
||||
|
||||
if !settings.features.copilot {
|
||||
let all_language_settings = &all_language_settings(None, cx);
|
||||
if !all_language_settings.copilot.feature_enabled {
|
||||
return Empty::new().into_any();
|
||||
}
|
||||
|
||||
let settings = cx.global::<Settings>();
|
||||
let theme = settings.theme.clone();
|
||||
let active = self.popup_menu.read(cx).visible();
|
||||
let Some(copilot) = Copilot::global(cx) else {
|
||||
@ -55,7 +56,7 @@ impl View for CopilotButton {
|
||||
|
||||
let enabled = self
|
||||
.editor_enabled
|
||||
.unwrap_or(settings.show_copilot_suggestions(None, None));
|
||||
.unwrap_or_else(|| all_language_settings.copilot_enabled(None, None));
|
||||
|
||||
Stack::new()
|
||||
.with_child(
|
||||
@ -192,14 +193,14 @@ impl CopilotButton {
|
||||
}
|
||||
|
||||
pub fn deploy_copilot_menu(&mut self, cx: &mut ViewContext<Self>) {
|
||||
let settings = cx.global::<Settings>();
|
||||
let fs = self.fs.clone();
|
||||
|
||||
let mut menu_options = Vec::with_capacity(8);
|
||||
|
||||
if let Some(language) = self.language.clone() {
|
||||
let fs = fs.clone();
|
||||
let language_enabled = settings.copilot_enabled_for_language(Some(language.as_ref()));
|
||||
let language_enabled =
|
||||
language_settings::language_settings(None, Some(language.as_ref()), cx)
|
||||
.show_copilot_suggestions;
|
||||
menu_options.push(ContextMenuItem::handler(
|
||||
format!(
|
||||
"{} Suggestions for {}",
|
||||
@ -210,6 +211,8 @@ impl CopilotButton {
|
||||
));
|
||||
}
|
||||
|
||||
let settings = settings::get_setting::<AllLanguageSettings>(None, cx);
|
||||
|
||||
if let Some(path) = self.path.as_ref() {
|
||||
let path_enabled = settings.copilot_enabled_for_path(path);
|
||||
let path = path.clone();
|
||||
@ -234,7 +237,7 @@ impl CopilotButton {
|
||||
));
|
||||
}
|
||||
|
||||
let globally_enabled = cx.global::<Settings>().features.copilot;
|
||||
let globally_enabled = settings.copilot_enabled(None, None);
|
||||
menu_options.push(ContextMenuItem::handler(
|
||||
if globally_enabled {
|
||||
"Hide Suggestions for All Files"
|
||||
@ -246,7 +249,7 @@ impl CopilotButton {
|
||||
|
||||
menu_options.push(ContextMenuItem::Separator);
|
||||
|
||||
let icon_style = settings.theme.copilot.out_link_icon.clone();
|
||||
let icon_style = cx.global::<Settings>().theme.copilot.out_link_icon.clone();
|
||||
menu_options.push(ContextMenuItem::action(
|
||||
move |state: &mut MouseState, style: &theme::ContextMenuItem| {
|
||||
Flex::row()
|
||||
@ -272,22 +275,19 @@ impl CopilotButton {
|
||||
|
||||
pub fn update_enabled(&mut self, editor: ViewHandle<Editor>, cx: &mut ViewContext<Self>) {
|
||||
let editor = editor.read(cx);
|
||||
|
||||
let snapshot = editor.buffer().read(cx).snapshot(cx);
|
||||
let settings = cx.global::<Settings>();
|
||||
let suggestion_anchor = editor.selections.newest_anchor().start;
|
||||
|
||||
let language_name = snapshot
|
||||
.language_at(suggestion_anchor)
|
||||
.map(|language| language.name());
|
||||
let path = snapshot
|
||||
.file_at(suggestion_anchor)
|
||||
.map(|file| file.path().clone());
|
||||
let path = snapshot.file_at(suggestion_anchor).map(|file| file.path());
|
||||
|
||||
self.editor_enabled =
|
||||
Some(settings.show_copilot_suggestions(language_name.as_deref(), path.as_deref()));
|
||||
self.editor_enabled = Some(
|
||||
all_language_settings(None, cx)
|
||||
.copilot_enabled(language_name.as_deref(), path.map(|p| p.as_ref())),
|
||||
);
|
||||
self.language = language_name;
|
||||
self.path = path;
|
||||
self.path = path.cloned();
|
||||
|
||||
cx.notify()
|
||||
}
|
||||
@ -328,27 +328,27 @@ async fn configure_disabled_globs(
|
||||
settings_editor.downgrade().update(&mut cx, |item, cx| {
|
||||
let text = item.buffer().read(cx).snapshot(cx).text();
|
||||
|
||||
let edits = cx
|
||||
.global::<SettingsStore>()
|
||||
.update::<Settings>(&text, |file| {
|
||||
let copilot = file.copilot.get_or_insert_with(Default::default);
|
||||
let globs = copilot.disabled_globs.get_or_insert_with(|| {
|
||||
cx.global::<Settings>()
|
||||
.copilot
|
||||
.disabled_globs
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|glob| glob.as_str().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
if let Some(path_to_disable) = &path_to_disable {
|
||||
globs.push(path_to_disable.to_string_lossy().into_owned());
|
||||
} else {
|
||||
globs.clear();
|
||||
}
|
||||
let settings = cx.global::<SettingsStore>();
|
||||
let edits = settings.edits_for_update::<AllLanguageSettings>(&text, |file| {
|
||||
let copilot = file.copilot.get_or_insert_with(Default::default);
|
||||
let globs = copilot.disabled_globs.get_or_insert_with(|| {
|
||||
settings
|
||||
.get::<AllLanguageSettings>(None)
|
||||
.copilot
|
||||
.disabled_globs
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|glob| glob.as_str().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
if let Some(path_to_disable) = &path_to_disable {
|
||||
globs.push(path_to_disable.to_string_lossy().into_owned());
|
||||
} else {
|
||||
globs.clear();
|
||||
}
|
||||
});
|
||||
|
||||
if !edits.is_empty() {
|
||||
item.change_selections(Some(Autoscroll::newest()), cx, |selections| {
|
||||
selections.select_ranges(edits.iter().map(|e| e.0.clone()));
|
||||
@ -365,31 +365,26 @@ async fn configure_disabled_globs(
|
||||
}
|
||||
|
||||
fn toggle_copilot_globally(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
let show_copilot_suggestions = cx.global::<Settings>().show_copilot_suggestions(None, None);
|
||||
update_settings_file::<Settings>(fs, cx, move |file_contents| {
|
||||
file_contents.editor.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
|
||||
let show_copilot_suggestions = all_language_settings(None, cx).copilot_enabled(None, None);
|
||||
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
|
||||
file.defaults.show_copilot_suggestions = Some((!show_copilot_suggestions).into())
|
||||
});
|
||||
}
|
||||
|
||||
fn toggle_copilot_for_language(language: Arc<str>, fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
let show_copilot_suggestions = cx
|
||||
.global::<Settings>()
|
||||
.show_copilot_suggestions(Some(&language), None);
|
||||
|
||||
update_settings_file::<Settings>(fs, cx, move |file_contents| {
|
||||
file_contents.languages.insert(
|
||||
language,
|
||||
settings::EditorSettings {
|
||||
show_copilot_suggestions: Some((!show_copilot_suggestions).into()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let show_copilot_suggestions =
|
||||
all_language_settings(None, cx).copilot_enabled(Some(&language), None);
|
||||
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
|
||||
file.languages
|
||||
.entry(language)
|
||||
.or_default()
|
||||
.show_copilot_suggestions = Some(!show_copilot_suggestions);
|
||||
});
|
||||
}
|
||||
|
||||
fn hide_copilot(fs: Arc<dyn Fs>, cx: &mut AppContext) {
|
||||
update_settings_file::<Settings>(fs, cx, move |file_contents| {
|
||||
file_contents.features.copilot = Some(false)
|
||||
update_settings_file::<AllLanguageSettings>(fs, cx, move |file| {
|
||||
file.features.get_or_insert(Default::default()).copilot = Some(false);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -820,11 +820,13 @@ mod tests {
|
||||
use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped};
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use unindent::Unindent as _;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_diagnostics(cx: &mut TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
@ -1227,7 +1229,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_diagnostics_multiple_servers(cx: &mut TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
@ -1491,6 +1494,14 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn editor_blocks(editor: &ViewHandle<Editor>, cx: &mut WindowContext) -> Vec<(u32, String)> {
|
||||
editor.update(cx, |editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
|
@ -49,6 +49,7 @@ workspace = { path = "../workspace" }
|
||||
aho-corasick = "0.7"
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
glob.workspace = true
|
||||
indoc = "1.0.4"
|
||||
itertools = "0.10"
|
||||
lazy_static.workspace = true
|
||||
@ -58,6 +59,7 @@ parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
pulldown-cmark = { version = "0.9.2", default-features = false }
|
||||
rand = { workspace = true, optional = true }
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
smallvec.workspace = true
|
||||
|
@ -13,8 +13,9 @@ use gpui::{
|
||||
fonts::{FontId, HighlightStyle},
|
||||
Entity, ModelContext, ModelHandle,
|
||||
};
|
||||
use language::{OffsetUtf16, Point, Subscription as BufferSubscription};
|
||||
use settings::Settings;
|
||||
use language::{
|
||||
language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
|
||||
};
|
||||
use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
|
||||
pub use suggestion_map::Suggestion;
|
||||
use suggestion_map::SuggestionMap;
|
||||
@ -276,8 +277,7 @@ impl DisplayMap {
|
||||
.as_singleton()
|
||||
.and_then(|buffer| buffer.read(cx).language())
|
||||
.map(|language| language.name());
|
||||
|
||||
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||
language_settings(None, language_name.as_deref(), cx).tab_size
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -844,8 +844,12 @@ pub mod tests {
|
||||
use super::*;
|
||||
use crate::{movement, test::marked_display_snapshot};
|
||||
use gpui::{color::Color, elements::*, test::observe, AppContext};
|
||||
use language::{Buffer, Language, LanguageConfig, SelectionGoal};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
|
||||
Buffer, Language, LanguageConfig, SelectionGoal,
|
||||
};
|
||||
use rand::{prelude::*, Rng};
|
||||
use settings::SettingsStore;
|
||||
use smol::stream::StreamExt;
|
||||
use std::{env, sync::Arc};
|
||||
use theme::SyntaxTheme;
|
||||
@ -882,9 +886,7 @@ pub mod tests {
|
||||
log::info!("wrap width: {:?}", wrap_width);
|
||||
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
|
||||
cx.set_global(settings)
|
||||
init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
|
||||
});
|
||||
|
||||
let buffer = cx.update(|cx| {
|
||||
@ -939,9 +941,11 @@ pub mod tests {
|
||||
tab_size = *tab_sizes.choose(&mut rng).unwrap();
|
||||
log::info!("setting tab size to {:?}", tab_size);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = NonZeroU32::new(tab_size);
|
||||
cx.set_global(settings)
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(tab_size);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
30..=44 => {
|
||||
@ -1119,7 +1123,7 @@ pub mod tests {
|
||||
#[gpui::test(retries = 5)]
|
||||
fn test_soft_wraps(cx: &mut AppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let font_cache = cx.font_cache();
|
||||
|
||||
@ -1131,7 +1135,6 @@ pub mod tests {
|
||||
.unwrap();
|
||||
let font_size = 12.0;
|
||||
let wrap_width = Some(64.);
|
||||
cx.set_global(Settings::test(cx));
|
||||
|
||||
let text = "one two three four five\nsix seven eight";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
@ -1211,7 +1214,8 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_text_chunks(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let text = sample_text(6, 6, 'a');
|
||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||
let family_id = cx
|
||||
@ -1225,6 +1229,7 @@ pub mod tests {
|
||||
let font_size = 14.0;
|
||||
let map =
|
||||
cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
|
||||
|
||||
buffer.update(cx, |buffer, cx| {
|
||||
buffer.edit(
|
||||
vec![
|
||||
@ -1289,11 +1294,8 @@ pub mod tests {
|
||||
.unwrap(),
|
||||
);
|
||||
language.set_theme(&theme);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
});
|
||||
|
||||
cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
|
||||
@ -1382,7 +1384,7 @@ pub mod tests {
|
||||
);
|
||||
language.set_theme(&theme);
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
cx.update(|cx| init_test(cx, |_| {}));
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
|
||||
buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
|
||||
@ -1429,9 +1431,8 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| init_test(cx, |_| {}));
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
let theme = SyntaxTheme::new(vec![
|
||||
("operator".to_string(), Color::red().into()),
|
||||
("string".to_string(), Color::green().into()),
|
||||
@ -1510,7 +1511,8 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_clip_point(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
|
||||
let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
|
||||
|
||||
@ -1559,7 +1561,7 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
fn assert(text: &str, cx: &mut gpui::AppContext) {
|
||||
let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
|
||||
@ -1578,7 +1580,8 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let text = "✅\t\tα\nβ\t\n🏀β\t\tγ";
|
||||
let buffer = MultiBuffer::build_simple(text, cx);
|
||||
let font_cache = cx.font_cache();
|
||||
@ -1639,7 +1642,8 @@ pub mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_max_point(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
|
||||
let font_cache = cx.font_cache();
|
||||
let family_id = font_cache
|
||||
@ -1718,4 +1722,13 @@ pub mod tests {
|
||||
}
|
||||
chunks
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -51,6 +51,7 @@ pub use items::MAX_TAB_TITLE_LEN;
|
||||
use itertools::Itertools;
|
||||
pub use language::{char_kind, CharKind};
|
||||
use language::{
|
||||
language_settings::{self, all_language_settings},
|
||||
AutoindentMode, BracketPair, Buffer, CodeAction, CodeLabel, Completion, CursorShape,
|
||||
Diagnostic, DiagnosticSeverity, File, IndentKind, IndentSize, Language, OffsetRangeExt,
|
||||
OffsetUtf16, Point, Selection, SelectionGoal, TransactionId,
|
||||
@ -436,7 +437,7 @@ pub enum EditorMode {
|
||||
Full,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum SoftWrap {
|
||||
None,
|
||||
EditorWidth,
|
||||
@ -471,7 +472,7 @@ pub struct Editor {
|
||||
select_larger_syntax_node_stack: Vec<Box<[Selection<usize>]>>,
|
||||
ime_transaction: Option<TransactionId>,
|
||||
active_diagnostics: Option<ActiveDiagnosticGroup>,
|
||||
soft_wrap_mode_override: Option<settings::SoftWrap>,
|
||||
soft_wrap_mode_override: Option<language_settings::SoftWrap>,
|
||||
get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
|
||||
override_text_style: Option<Box<OverrideTextStyle>>,
|
||||
project: Option<ModelHandle<Project>>,
|
||||
@ -1247,7 +1248,7 @@ impl Editor {
|
||||
let blink_manager = cx.add_model(|cx| BlinkManager::new(CURSOR_BLINK_INTERVAL, cx));
|
||||
|
||||
let soft_wrap_mode_override =
|
||||
(mode == EditorMode::SingleLine).then(|| settings::SoftWrap::None);
|
||||
(mode == EditorMode::SingleLine).then(|| language_settings::SoftWrap::None);
|
||||
let mut this = Self {
|
||||
handle: cx.weak_handle(),
|
||||
buffer: buffer.clone(),
|
||||
@ -3116,17 +3117,12 @@ impl Editor {
|
||||
snapshot: &MultiBufferSnapshot,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> bool {
|
||||
let settings = cx.global::<Settings>();
|
||||
|
||||
let path = snapshot.file_at(location).map(|file| file.path());
|
||||
let path = snapshot.file_at(location).map(|file| file.path().as_ref());
|
||||
let language_name = snapshot
|
||||
.language_at(location)
|
||||
.map(|language| language.name());
|
||||
if !settings.show_copilot_suggestions(language_name.as_deref(), path.map(|p| p.as_ref())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
let settings = all_language_settings(None, cx);
|
||||
settings.copilot_enabled(language_name.as_deref(), path)
|
||||
}
|
||||
|
||||
fn has_active_copilot_suggestion(&self, cx: &AppContext) -> bool {
|
||||
@ -3427,12 +3423,9 @@ impl Editor {
|
||||
{
|
||||
let indent_size =
|
||||
buffer.indent_size_for_line(line_buffer_range.start.row);
|
||||
let language_name = buffer
|
||||
.language_at(line_buffer_range.start)
|
||||
.map(|language| language.name());
|
||||
let indent_len = match indent_size.kind {
|
||||
IndentKind::Space => {
|
||||
cx.global::<Settings>().tab_size(language_name.as_deref())
|
||||
buffer.settings_at(line_buffer_range.start, cx).tab_size
|
||||
}
|
||||
IndentKind::Tab => NonZeroU32::new(1).unwrap(),
|
||||
};
|
||||
@ -3544,12 +3537,11 @@ impl Editor {
|
||||
}
|
||||
|
||||
// Otherwise, insert a hard or soft tab.
|
||||
let settings = cx.global::<Settings>();
|
||||
let language_name = buffer.language_at(cursor, cx).map(|l| l.name());
|
||||
let tab_size = if settings.hard_tabs(language_name.as_deref()) {
|
||||
let settings = buffer.settings_at(cursor, cx);
|
||||
let tab_size = if settings.hard_tabs {
|
||||
IndentSize::tab()
|
||||
} else {
|
||||
let tab_size = settings.tab_size(language_name.as_deref()).get();
|
||||
let tab_size = settings.tab_size.get();
|
||||
let char_column = snapshot
|
||||
.text_for_range(Point::new(cursor.row, 0)..cursor)
|
||||
.flat_map(str::chars)
|
||||
@ -3602,10 +3594,9 @@ impl Editor {
|
||||
delta_for_start_row: u32,
|
||||
cx: &AppContext,
|
||||
) -> u32 {
|
||||
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let settings = cx.global::<Settings>();
|
||||
let tab_size = settings.tab_size(language_name.as_deref()).get();
|
||||
let indent_kind = if settings.hard_tabs(language_name.as_deref()) {
|
||||
let settings = buffer.settings_at(selection.start, cx);
|
||||
let tab_size = settings.tab_size.get();
|
||||
let indent_kind = if settings.hard_tabs {
|
||||
IndentKind::Tab
|
||||
} else {
|
||||
IndentKind::Space
|
||||
@ -3674,11 +3665,8 @@ impl Editor {
|
||||
let buffer = self.buffer.read(cx);
|
||||
let snapshot = buffer.snapshot(cx);
|
||||
for selection in &selections {
|
||||
let language_name = buffer.language_at(selection.start, cx).map(|l| l.name());
|
||||
let tab_size = cx
|
||||
.global::<Settings>()
|
||||
.tab_size(language_name.as_deref())
|
||||
.get();
|
||||
let settings = buffer.settings_at(selection.start, cx);
|
||||
let tab_size = settings.tab_size.get();
|
||||
let mut rows = selection.spanned_rows(false, &display_map);
|
||||
|
||||
// Avoid re-outdenting a row that has already been outdented by a
|
||||
@ -6439,27 +6427,24 @@ impl Editor {
|
||||
}
|
||||
|
||||
pub fn soft_wrap_mode(&self, cx: &AppContext) -> SoftWrap {
|
||||
let language_name = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.and_then(|singleton_buffer| singleton_buffer.read(cx).language())
|
||||
.map(|l| l.name());
|
||||
|
||||
let settings = cx.global::<Settings>();
|
||||
let settings = self.buffer.read(cx).settings_at(0, cx);
|
||||
let mode = self
|
||||
.soft_wrap_mode_override
|
||||
.unwrap_or_else(|| settings.soft_wrap(language_name.as_deref()));
|
||||
.unwrap_or_else(|| settings.soft_wrap);
|
||||
match mode {
|
||||
settings::SoftWrap::None => SoftWrap::None,
|
||||
settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||
settings::SoftWrap::PreferredLineLength => {
|
||||
SoftWrap::Column(settings.preferred_line_length(language_name.as_deref()))
|
||||
language_settings::SoftWrap::None => SoftWrap::None,
|
||||
language_settings::SoftWrap::EditorWidth => SoftWrap::EditorWidth,
|
||||
language_settings::SoftWrap::PreferredLineLength => {
|
||||
SoftWrap::Column(settings.preferred_line_length)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_soft_wrap_mode(&mut self, mode: settings::SoftWrap, cx: &mut ViewContext<Self>) {
|
||||
pub fn set_soft_wrap_mode(
|
||||
&mut self,
|
||||
mode: language_settings::SoftWrap,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) {
|
||||
self.soft_wrap_mode_override = Some(mode);
|
||||
cx.notify();
|
||||
}
|
||||
@ -6474,8 +6459,8 @@ impl Editor {
|
||||
self.soft_wrap_mode_override.take();
|
||||
} else {
|
||||
let soft_wrap = match self.soft_wrap_mode(cx) {
|
||||
SoftWrap::None => settings::SoftWrap::EditorWidth,
|
||||
SoftWrap::EditorWidth | SoftWrap::Column(_) => settings::SoftWrap::None,
|
||||
SoftWrap::None => language_settings::SoftWrap::EditorWidth,
|
||||
SoftWrap::EditorWidth | SoftWrap::Column(_) => language_settings::SoftWrap::None,
|
||||
};
|
||||
self.soft_wrap_mode_override = Some(soft_wrap);
|
||||
}
|
||||
@ -6874,7 +6859,12 @@ impl Editor {
|
||||
.get("vim_mode")
|
||||
== Some(&serde_json::Value::Bool(true));
|
||||
let telemetry_settings = *settings::get_setting::<TelemetrySettings>(None, cx);
|
||||
let settings = cx.global::<Settings>();
|
||||
let copilot_enabled = all_language_settings(None, cx).copilot_enabled(None, None);
|
||||
let copilot_enabled_for_language = self
|
||||
.buffer
|
||||
.read(cx)
|
||||
.settings_at(0, cx)
|
||||
.show_copilot_suggestions;
|
||||
|
||||
let extension = Path::new(file.file_name(cx))
|
||||
.extension()
|
||||
@ -6893,15 +6883,8 @@ impl Editor {
|
||||
file_extension: extension.map(ToString::to_string),
|
||||
vim_mode,
|
||||
operation: name,
|
||||
copilot_enabled: settings.features.copilot,
|
||||
copilot_enabled_for_language: settings.show_copilot_suggestions(
|
||||
self.language_at(0, cx)
|
||||
.map(|language| language.name())
|
||||
.as_deref(),
|
||||
self.file_at(0, cx)
|
||||
.map(|file| file.path().clone())
|
||||
.as_deref(),
|
||||
),
|
||||
copilot_enabled,
|
||||
copilot_enabled_for_language,
|
||||
};
|
||||
telemetry.report_clickhouse_event(event, telemetry_settings)
|
||||
}
|
||||
|
@ -12,10 +12,12 @@ use gpui::{
|
||||
serde_json, TestAppContext,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent},
|
||||
BracketPairConfig, FakeLspAdapter, LanguageConfig, LanguageRegistry, Point,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use project::FakeFs;
|
||||
use settings::EditorSettings;
|
||||
use std::{cell::RefCell, future::Future, rc::Rc, time::Instant};
|
||||
use unindent::Unindent;
|
||||
use util::{
|
||||
@ -29,7 +31,8 @@ use workspace::{
|
||||
|
||||
#[gpui::test]
|
||||
fn test_edit_events(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = language::Buffer::new(0, "123456", cx);
|
||||
buffer.set_group_interval(Duration::from_secs(1));
|
||||
@ -156,7 +159,8 @@ fn test_edit_events(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut now = Instant::now();
|
||||
let buffer = cx.add_model(|cx| language::Buffer::new(0, "123456", cx));
|
||||
let group_interval = buffer.read_with(cx, |buffer, _| buffer.transaction_group_interval());
|
||||
@ -226,7 +230,8 @@ fn test_undo_redo_with_selection_restoration(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
let mut buffer = language::Buffer::new(0, "abcde", cx);
|
||||
// Ensure automatic grouping doesn't occur.
|
||||
@ -328,7 +333,7 @@ fn test_ime_composition(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_selection_with_mouse(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\nddddddd\n", cx);
|
||||
@ -395,7 +400,8 @@ fn test_selection_with_mouse(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_canceling_pending_selection(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -429,6 +435,8 @@ fn test_canceling_pending_selection(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_clone(cx: &mut TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (text, selection_ranges) = marked_text_ranges(
|
||||
indoc! {"
|
||||
one
|
||||
@ -439,7 +447,6 @@ fn test_clone(cx: &mut TestAppContext) {
|
||||
"},
|
||||
true,
|
||||
);
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&text, cx);
|
||||
@ -487,7 +494,8 @@ fn test_clone(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_navigation_history(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
cx.set_global(DragAndDrop::<Workspace>::default());
|
||||
use workspace::item::Item;
|
||||
|
||||
@ -600,7 +608,8 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_cancel(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaaaa\nbbbbbb\ncccccc\ndddddd\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -642,7 +651,8 @@ fn test_cancel(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_fold_action(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(
|
||||
&"
|
||||
@ -731,7 +741,8 @@ fn test_fold_action(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.update(|cx| MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx));
|
||||
let (_, view) = cx.add_window(|cx| build_editor(buffer.clone(), cx));
|
||||
|
||||
@ -806,7 +817,8 @@ fn test_move_cursor(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcde\nαβγδε\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
@ -910,7 +922,8 @@ fn test_move_cursor_multibyte(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("ⓐⓑⓒⓓⓔ\nabcd\nαβγ\nabcd\nⓐⓑⓒⓓⓔ\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
@ -959,7 +972,8 @@ fn test_move_cursor_different_line_lengths(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\n def", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -1121,7 +1135,8 @@ fn test_beginning_end_of_line(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("use std::str::{foo, bar}\n\n {baz.qux()}", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -1172,7 +1187,8 @@ fn test_prev_next_word_boundary(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("use one::{\n two::three::four::five\n};", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -1229,6 +1245,7 @@ fn test_prev_next_word_bounds_with_soft_wrap(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let line_height = cx.editor(|editor, cx| editor.style(cx).text.line_height(cx.font_cache()));
|
||||
@ -1343,6 +1360,7 @@ async fn test_move_page_up_page_down(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state("one «two threeˇ» four");
|
||||
cx.update_editor(|editor, cx| {
|
||||
@ -1353,7 +1371,8 @@ async fn test_delete_to_beginning_of_line(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("one two three four", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
@ -1388,7 +1407,8 @@ fn test_delete_to_word_boundary(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_newline(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("aaaa\n bbbb\n", cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
@ -1410,7 +1430,8 @@ fn test_newline(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_newline_with_old_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(
|
||||
"
|
||||
@ -1491,11 +1512,8 @@ fn test_newline_with_old_selections(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
@ -1506,8 +1524,9 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
||||
.with_indents_query(r#"(_ "(" ")" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
const a: ˇA = (
|
||||
(ˇ
|
||||
@ -1516,6 +1535,7 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
||||
)ˇ
|
||||
ˇ);ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, cx| e.newline_above(&NewlineAbove, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
ˇ
|
||||
@ -1540,11 +1560,8 @@ async fn test_newline_above(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
@ -1555,8 +1572,9 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
||||
.with_indents_query(r#"(_ "(" ")" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
cx.set_state(indoc! {"
|
||||
const a: ˇA = (
|
||||
(ˇ
|
||||
@ -1565,6 +1583,7 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
||||
)ˇ
|
||||
ˇ);ˇ
|
||||
"});
|
||||
|
||||
cx.update_editor(|e, cx| e.newline_below(&NewlineBelow, cx));
|
||||
cx.assert_editor_state(indoc! {"
|
||||
const a: A = (
|
||||
@ -1589,7 +1608,8 @@ async fn test_newline_below(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("a( X ), b( Y ), c( Z )", cx);
|
||||
let mut editor = build_editor(buffer.clone(), cx);
|
||||
@ -1615,12 +1635,11 @@ fn test_insert_with_old_selections(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_tab(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(NonZeroU32::new(3).unwrap());
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(3)
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state(indoc! {"
|
||||
ˇabˇc
|
||||
ˇ🏀ˇ🏀ˇefg
|
||||
@ -1646,6 +1665,8 @@ async fn test_tab(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
@ -1704,7 +1725,10 @@ async fn test_tab_in_leading_whitespace_auto_indents_lines(cx: &mut gpui::TestAp
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4)
|
||||
});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig::default(),
|
||||
@ -1713,14 +1737,9 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
||||
.with_indents_query(r#"(_ "{" "}" @end) @indent"#)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| buffer.set_language(Some(language), cx));
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.tab_size = Some(4.try_into().unwrap());
|
||||
});
|
||||
});
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
fn a() {
|
||||
if b {
|
||||
@ -1741,6 +1760,10 @@ async fn test_tab_with_mixed_whitespace(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.tab_size = NonZeroU32::new(4);
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
cx.set_state(indoc! {"
|
||||
@ -1810,13 +1833,12 @@ async fn test_indent_outdent(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.editor_overrides.hard_tabs = Some(true);
|
||||
});
|
||||
init_test(cx, |settings| {
|
||||
settings.defaults.hard_tabs = Some(true);
|
||||
});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
// select two ranges on one line
|
||||
cx.set_state(indoc! {"
|
||||
«oneˇ» «twoˇ»
|
||||
@ -1907,25 +1929,25 @@ async fn test_indent_outdent_with_hard_tabs(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
cx.set_global(
|
||||
Settings::test(cx)
|
||||
.with_language_defaults(
|
||||
"TOML",
|
||||
EditorSettings {
|
||||
tab_size: Some(2.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.with_language_defaults(
|
||||
"Rust",
|
||||
EditorSettings {
|
||||
tab_size: Some(4.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
);
|
||||
init_test(cx, |settings| {
|
||||
settings.languages.extend([
|
||||
(
|
||||
"TOML".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(2),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(4),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
let toml_language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TOML".into(),
|
||||
@ -2020,6 +2042,8 @@ fn test_indent_outdent_with_excerpts(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
// Basic backspace
|
||||
@ -2067,8 +2091,9 @@ async fn test_backspace(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state(indoc! {"
|
||||
onˇe two three
|
||||
fou«rˇ» five six
|
||||
@ -2095,7 +2120,8 @@ async fn test_delete(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_delete_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2119,7 +2145,6 @@ fn test_delete_line(cx: &mut TestAppContext) {
|
||||
);
|
||||
});
|
||||
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2139,7 +2164,8 @@ fn test_delete_line(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_duplicate_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndef\nghi\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2191,7 +2217,8 @@ fn test_duplicate_line(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_line_up_down(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2289,7 +2316,8 @@ fn test_move_line_up_down(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(10, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2315,7 +2343,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_transpose(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
_ = cx
|
||||
.add_window(|cx| {
|
||||
@ -2417,6 +2445,8 @@ fn test_transpose(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
cx.set_state("«one✅ ˇ»two «three ˇ»four «five ˇ»six ");
|
||||
@ -2497,6 +2527,8 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig::default(),
|
||||
@ -2609,7 +2641,8 @@ async fn test_paste_multiline(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_select_all(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\nde\nfgh", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2625,7 +2658,8 @@ fn test_select_all(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_select_line(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2671,7 +2705,8 @@ fn test_select_line(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_split_selection_into_lines(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(9, 5, 'a'), cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2741,7 +2776,8 @@ fn test_split_selection_into_lines(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_add_selection_above_below(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, view) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("abc\ndefghi\n\njk\nlmno\n", cx);
|
||||
build_editor(buffer, cx)
|
||||
@ -2935,6 +2971,8 @@ fn test_add_selection_above_below(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_next(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.set_state("abc\nˇabc abc\ndefabc\nabc");
|
||||
|
||||
@ -2959,7 +2997,8 @@ async fn test_select_next(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig::default(),
|
||||
Some(tree_sitter_rust::language()),
|
||||
@ -3100,7 +3139,8 @@ async fn test_select_larger_smaller_syntax_node(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
@ -3160,6 +3200,8 @@ async fn test_autoindent_selections(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
@ -3329,6 +3371,8 @@ async fn test_autoclose_pairs(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let html_language = Arc::new(
|
||||
@ -3563,6 +3607,8 @@ async fn test_autoclose_with_embedded_language(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let rust_language = Arc::new(
|
||||
@ -3660,7 +3706,8 @@ async fn test_autoclose_with_overrides(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
brackets: BracketPairConfig {
|
||||
@ -3814,7 +3861,8 @@ async fn test_surround_with_pair(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
brackets: BracketPairConfig {
|
||||
@ -3919,7 +3967,7 @@ async fn test_delete_autoclose_pair(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (text, insertion_ranges) = marked_text_ranges(
|
||||
indoc! {"
|
||||
@ -4027,7 +4075,7 @@ async fn test_snippets(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -4111,16 +4159,14 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||
|
||||
// Set rust language override and assert overriden tabsize is sent to language server
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
"Rust".into(),
|
||||
EditorSettings {
|
||||
tab_size: Some(8.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
})
|
||||
update_test_settings(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(8),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
|
||||
@ -4141,7 +4187,7 @@ async fn test_document_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -4227,16 +4273,14 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
assert!(!cx.read(|cx| editor.is_dirty(cx)));
|
||||
|
||||
// Set rust language override and assert overriden tabsize is sent to language server
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
"Rust".into(),
|
||||
EditorSettings {
|
||||
tab_size: Some(8.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
})
|
||||
update_test_settings(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
"Rust".into(),
|
||||
LanguageSettingsContent {
|
||||
tab_size: NonZeroU32::new(8),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
let save = editor.update(cx, |editor, cx| editor.save(project.clone(), cx));
|
||||
@ -4257,7 +4301,7 @@ async fn test_range_format_during_save(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -4342,7 +4386,7 @@ async fn test_document_format_manual_trigger(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
@ -4399,7 +4443,7 @@ async fn test_concurrent_format_requests(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
@ -4514,6 +4558,8 @@ async fn test_strip_whitespace_and_format_via_lsp(cx: &mut gpui::TestAppContext)
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completion(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
@ -4681,7 +4727,8 @@ async fn test_completion(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
line_comment: Some("// ".into()),
|
||||
@ -4764,8 +4811,7 @@ async fn test_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext) {
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
@ -4778,6 +4824,7 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext)
|
||||
let registry = Arc::new(LanguageRegistry::test());
|
||||
registry.add(language.clone());
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
cx.update_buffer(|buffer, cx| {
|
||||
buffer.set_language_registry(registry);
|
||||
buffer.set_language(Some(language), cx);
|
||||
@ -4897,6 +4944,8 @@ async fn test_advance_downward_on_toggle_comment(cx: &mut gpui::TestAppContext)
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let html_language = Arc::new(
|
||||
@ -5021,7 +5070,8 @@ async fn test_toggle_block_comment(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
let mut multibuffer = MultiBuffer::new(0);
|
||||
@ -5067,7 +5117,8 @@ fn test_editing_disjoint_excerpts(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let markers = vec![('[', ']').into(), ('(', ')').into()];
|
||||
let (initial_text, mut excerpt_ranges) = marked_text_ranges_by(
|
||||
indoc! {"
|
||||
@ -5140,7 +5191,8 @@ fn test_editing_overlapping_excerpts(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_refresh_selections(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
@ -5224,7 +5276,8 @@ fn test_refresh_selections(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(3, 4, 'a'), cx));
|
||||
let mut excerpt1_id = None;
|
||||
let multibuffer = cx.add_model(|cx| {
|
||||
@ -5282,7 +5335,8 @@ fn test_refresh_selections_while_selecting_with_mouse(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let language = Arc::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
@ -5355,7 +5409,8 @@ async fn test_extra_newline_insertion(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(16, 8, 'a'), cx);
|
||||
build_editor(buffer.clone(), cx)
|
||||
@ -5437,7 +5492,8 @@ fn test_highlighted_ranges(cx: &mut TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||
|
||||
@ -5576,7 +5632,8 @@ async fn test_following(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
let project = Project::test(fs, ["/file.rs".as_ref()], cx).await;
|
||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
@ -5805,6 +5862,8 @@ fn test_combine_syntax_and_fuzzy_match_highlights() {
|
||||
|
||||
#[gpui::test]
|
||||
async fn go_to_hunk(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorTestContext::new(cx);
|
||||
|
||||
let diff_base = r#"
|
||||
@ -5924,6 +5983,8 @@ fn test_split_words() {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_typescript(Default::default(), cx).await;
|
||||
let mut assert = |before, after| {
|
||||
let _state_context = cx.set_state(before);
|
||||
@ -5972,6 +6033,8 @@ async fn test_move_to_enclosing_bracket(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_copilot(deterministic: Arc<Deterministic>, cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
@ -6223,6 +6286,8 @@ async fn test_copilot_completion_invalidation(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
@ -6288,11 +6353,10 @@ async fn test_copilot_multibuffer(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
cx.set_global(copilot)
|
||||
});
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
|
||||
let buffer_1 = cx.add_model(|cx| Buffer::new(0, "a = 1\nb = 2\n", cx));
|
||||
let buffer_2 = cx.add_model(|cx| Buffer::new(0, "c = 3\nd = 4\n", cx));
|
||||
@ -6392,14 +6456,16 @@ async fn test_copilot_disabled_globs(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.copilot.disabled_globs = vec![glob::Pattern::new(".env*").unwrap()];
|
||||
cx.set_global(settings);
|
||||
cx.set_global(copilot)
|
||||
init_test(cx, |settings| {
|
||||
settings
|
||||
.copilot
|
||||
.get_or_insert(Default::default())
|
||||
.disabled_globs = Some(vec![".env*".to_string()]);
|
||||
});
|
||||
|
||||
let (copilot, copilot_lsp) = Copilot::fake(cx);
|
||||
cx.update(|cx| cx.set_global(copilot));
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/test",
|
||||
@ -6596,3 +6662,27 @@ fn handle_copilot_completion_request(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn update_test_settings(
|
||||
cx: &mut TestAppContext,
|
||||
f: impl Fn(&mut AllLanguageSettingsContent),
|
||||
) {
|
||||
cx.update(|cx| {
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, f);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) fn init_test(cx: &mut TestAppContext, f: fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
cx.set_global(Settings::test(cx));
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
});
|
||||
|
||||
update_test_settings(cx, f);
|
||||
}
|
||||
|
@ -35,9 +35,12 @@ use gpui::{
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use json::json;
|
||||
use language::{Bias, CursorShape, DiagnosticSeverity, OffsetUtf16, Selection};
|
||||
use language::{
|
||||
language_settings::ShowWhitespaceSetting, Bias, CursorShape, DiagnosticSeverity, OffsetUtf16,
|
||||
Selection,
|
||||
};
|
||||
use project::ProjectPath;
|
||||
use settings::{GitGutter, Settings, ShowWhitespaces};
|
||||
use settings::{GitGutter, Settings};
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
@ -708,6 +711,7 @@ impl EditorElement {
|
||||
let scroll_left = scroll_position.x() * max_glyph_width;
|
||||
let content_origin = bounds.origin() + vec2f(layout.gutter_margin, 0.);
|
||||
let line_end_overshoot = 0.15 * layout.position_map.line_height;
|
||||
let whitespace_setting = editor.buffer.read(cx).settings_at(0, cx).show_whitespaces;
|
||||
|
||||
scene.push_layer(Some(bounds));
|
||||
|
||||
@ -882,9 +886,10 @@ impl EditorElement {
|
||||
content_origin,
|
||||
scroll_left,
|
||||
visible_text_bounds,
|
||||
cx,
|
||||
whitespace_setting,
|
||||
&invisible_display_ranges,
|
||||
visible_bounds,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1738,9 +1743,10 @@ impl LineWithInvisibles {
|
||||
content_origin: Vector2F,
|
||||
scroll_left: f32,
|
||||
visible_text_bounds: RectF,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
whitespace_setting: ShowWhitespaceSetting,
|
||||
selection_ranges: &[Range<DisplayPoint>],
|
||||
visible_bounds: RectF,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let line_height = layout.position_map.line_height;
|
||||
let line_y = row as f32 * line_height - scroll_top;
|
||||
@ -1754,7 +1760,6 @@ impl LineWithInvisibles {
|
||||
);
|
||||
|
||||
self.draw_invisibles(
|
||||
cx,
|
||||
&selection_ranges,
|
||||
layout,
|
||||
content_origin,
|
||||
@ -1764,12 +1769,13 @@ impl LineWithInvisibles {
|
||||
scene,
|
||||
visible_bounds,
|
||||
line_height,
|
||||
whitespace_setting,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn draw_invisibles(
|
||||
&self,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
selection_ranges: &[Range<DisplayPoint>],
|
||||
layout: &LayoutState,
|
||||
content_origin: Vector2F,
|
||||
@ -1779,17 +1785,13 @@ impl LineWithInvisibles {
|
||||
scene: &mut SceneBuilder,
|
||||
visible_bounds: RectF,
|
||||
line_height: f32,
|
||||
whitespace_setting: ShowWhitespaceSetting,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let settings = cx.global::<Settings>();
|
||||
let allowed_invisibles_regions = match settings
|
||||
.editor_overrides
|
||||
.show_whitespaces
|
||||
.or(settings.editor_defaults.show_whitespaces)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
ShowWhitespaces::None => return,
|
||||
ShowWhitespaces::Selection => Some(selection_ranges),
|
||||
ShowWhitespaces::All => None,
|
||||
let allowed_invisibles_regions = match whitespace_setting {
|
||||
ShowWhitespaceSetting::None => return,
|
||||
ShowWhitespaceSetting::Selection => Some(selection_ranges),
|
||||
ShowWhitespaceSetting::All => None,
|
||||
};
|
||||
|
||||
for invisible in &self.invisibles {
|
||||
@ -2773,17 +2775,19 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
display_map::{BlockDisposition, BlockProperties},
|
||||
editor_tests::{init_test, update_test_settings},
|
||||
Editor, MultiBuffer,
|
||||
};
|
||||
use gpui::TestAppContext;
|
||||
use language::language_settings;
|
||||
use log::info;
|
||||
use settings::Settings;
|
||||
use std::{num::NonZeroU32, sync::Arc};
|
||||
use util::test::sample_text;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_layout_line_numbers(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple(&sample_text(6, 6, 'a'), cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
@ -2801,7 +2805,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_layout_with_placeholder_text_and_blocks(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let (_, editor) = cx.add_window(|cx| {
|
||||
let buffer = MultiBuffer::build_simple("", cx);
|
||||
Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
@ -2861,26 +2866,27 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_all_invisibles_drawing(cx: &mut TestAppContext) {
|
||||
let tab_size = 4;
|
||||
const TAB_SIZE: u32 = 4;
|
||||
|
||||
let input_text = "\t \t|\t| a b";
|
||||
let expected_invisibles = vec![
|
||||
Invisible::Tab {
|
||||
line_start_offset: 0,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize,
|
||||
line_offset: TAB_SIZE as usize,
|
||||
},
|
||||
Invisible::Tab {
|
||||
line_start_offset: tab_size as usize + 1,
|
||||
line_start_offset: TAB_SIZE as usize + 1,
|
||||
},
|
||||
Invisible::Tab {
|
||||
line_start_offset: tab_size as usize * 2 + 1,
|
||||
line_start_offset: TAB_SIZE as usize * 2 + 1,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize * 3 + 1,
|
||||
line_offset: TAB_SIZE as usize * 3 + 1,
|
||||
},
|
||||
Invisible::Whitespace {
|
||||
line_offset: tab_size as usize * 3 + 3,
|
||||
line_offset: TAB_SIZE as usize * 3 + 3,
|
||||
},
|
||||
];
|
||||
assert_eq!(
|
||||
@ -2892,12 +2898,11 @@ mod tests {
|
||||
"Hardcoded expected invisibles differ from the actual ones in '{input_text}'"
|
||||
);
|
||||
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
|
||||
cx.set_global(test_settings);
|
||||
init_test(cx, |s| {
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.tab_size = NonZeroU32::new(TAB_SIZE);
|
||||
});
|
||||
|
||||
let actual_invisibles =
|
||||
collect_invisibles_from_new_editor(cx, EditorMode::Full, &input_text, 500.0);
|
||||
|
||||
@ -2906,11 +2911,9 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_invisibles_dont_appear_in_certain_editors(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(4).unwrap());
|
||||
cx.set_global(test_settings);
|
||||
init_test(cx, |s| {
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.tab_size = NonZeroU32::new(4);
|
||||
});
|
||||
|
||||
for editor_mode_without_invisibles in [
|
||||
@ -2961,19 +2964,18 @@ mod tests {
|
||||
);
|
||||
info!("Expected invisibles: {expected_invisibles:?}");
|
||||
|
||||
init_test(cx, |_| {});
|
||||
|
||||
// Put the same string with repeating whitespace pattern into editors of various size,
|
||||
// take deliberately small steps during resizing, to put all whitespace kinds near the wrap point.
|
||||
let resize_step = 10.0;
|
||||
let mut editor_width = 200.0;
|
||||
while editor_width <= 1000.0 {
|
||||
cx.update(|cx| {
|
||||
let mut test_settings = Settings::test(cx);
|
||||
test_settings.editor_defaults.tab_size = Some(NonZeroU32::new(tab_size).unwrap());
|
||||
test_settings.editor_defaults.show_whitespaces = Some(ShowWhitespaces::All);
|
||||
test_settings.editor_defaults.preferred_line_length = Some(editor_width as u32);
|
||||
test_settings.editor_defaults.soft_wrap =
|
||||
Some(settings::SoftWrap::PreferredLineLength);
|
||||
cx.set_global(test_settings);
|
||||
update_test_settings(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(tab_size);
|
||||
s.defaults.show_whitespaces = Some(ShowWhitespaceSetting::All);
|
||||
s.defaults.preferred_line_length = Some(editor_width as u32);
|
||||
s.defaults.soft_wrap = Some(language_settings::SoftWrap::PreferredLineLength);
|
||||
});
|
||||
|
||||
let actual_invisibles =
|
||||
@ -3021,7 +3023,7 @@ mod tests {
|
||||
|
||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||
let (_, layout_state) = editor.update(cx, |editor, cx| {
|
||||
editor.set_soft_wrap_mode(settings::SoftWrap::EditorWidth, cx);
|
||||
editor.set_soft_wrap_mode(language_settings::SoftWrap::EditorWidth, cx);
|
||||
editor.set_wrap_width(Some(editor_width), cx);
|
||||
|
||||
let mut new_parents = Default::default();
|
||||
|
@ -33,12 +33,14 @@ pub fn refresh_matching_bracket_highlights(editor: &mut Editor, cx: &mut ViewCon
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use indoc::indoc;
|
||||
use language::{BracketPair, BracketPairConfig, Language, LanguageConfig};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_bracket_highlights(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new(
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
|
@ -694,7 +694,7 @@ impl DiagnosticPopover {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use gpui::fonts::Weight;
|
||||
use indoc::indoc;
|
||||
use language::{Diagnostic, DiagnosticSet};
|
||||
@ -706,6 +706,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_hover_info_popover(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
@ -773,6 +775,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_keyboard_hover_info_popover(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
@ -816,6 +820,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_hover_diagnostic_and_info_popovers(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
@ -882,7 +888,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_render_blocks(cx: &mut gpui::TestAppContext) {
|
||||
Settings::test_async(cx);
|
||||
init_test(cx, |_| {});
|
||||
|
||||
cx.add_window(|cx| {
|
||||
let editor = Editor::single_line(None, cx);
|
||||
let style = editor.style(cx);
|
||||
|
@ -1,10 +1,9 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use crate::{Anchor, DisplayPoint, Editor, EditorSnapshot, SelectPhase};
|
||||
use gpui::{Task, ViewContext};
|
||||
use language::{Bias, ToOffset};
|
||||
use project::LocationLink;
|
||||
use settings::Settings;
|
||||
use std::ops::Range;
|
||||
use util::TryFutureExt;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
@ -297,6 +296,8 @@ fn go_to_fetched_definition_of_kind(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use futures::StreamExt;
|
||||
use gpui::{
|
||||
platform::{self, Modifiers, ModifiersChangedEvent},
|
||||
@ -305,12 +306,10 @@ mod tests {
|
||||
use indoc::indoc;
|
||||
use lsp::request::{GotoDefinition, GotoTypeDefinition};
|
||||
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_link_go_to_type_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
@ -417,6 +416,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_link_go_to_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -57,13 +57,14 @@ pub fn deploy_context_menu(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
|
||||
use super::*;
|
||||
use crate::{editor_tests::init_test, test::editor_lsp_test_context::EditorLspTestContext};
|
||||
use indoc::indoc;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_mouse_context_menu(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx, |_| {});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(
|
||||
lsp::ServerCapabilities {
|
||||
hover_provider: Some(lsp::HoverProviderCapability::Simple(true)),
|
||||
|
@ -367,13 +367,15 @@ pub fn split_display_range_by_lines(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use settings::{Settings, SettingsStore};
|
||||
|
||||
use super::*;
|
||||
use crate::{test::marked_display_snapshot, Buffer, DisplayMap, ExcerptRange, MultiBuffer};
|
||||
use settings::Settings;
|
||||
|
||||
#[gpui::test]
|
||||
fn test_previous_word_start(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
@ -400,7 +402,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_previous_subword_start(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
@ -434,7 +437,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_find_preceding_boundary(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(
|
||||
marked_text: &str,
|
||||
cx: &mut gpui::AppContext,
|
||||
@ -466,7 +470,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_next_word_end(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
@ -490,7 +495,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_next_subword_end(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
@ -523,7 +529,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_find_boundary(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(
|
||||
marked_text: &str,
|
||||
cx: &mut gpui::AppContext,
|
||||
@ -555,7 +562,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_surrounding_word(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
fn assert(marked_text: &str, cx: &mut gpui::AppContext) {
|
||||
let (snapshot, display_points) = marked_display_snapshot(marked_text, cx);
|
||||
assert_eq!(
|
||||
@ -576,7 +584,8 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_move_up_and_down_with_excerpts(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_test(cx);
|
||||
|
||||
let family_id = cx
|
||||
.font_cache()
|
||||
.load_family(&["Helvetica"], &Default::default())
|
||||
@ -691,4 +700,11 @@ mod tests {
|
||||
(DisplayPoint::new(7, 2), SelectionGoal::Column(2)),
|
||||
);
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
cx.set_global(Settings::test(cx));
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ use git::diff::DiffHunk;
|
||||
use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
|
||||
pub use language::Completion;
|
||||
use language::{
|
||||
char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
char_kind,
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
|
||||
DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
|
||||
Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
|
||||
ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
|
||||
@ -1372,6 +1374,15 @@ 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 {
|
||||
let language = self.language_at(point, cx);
|
||||
language_settings(None, language.map(|l| l.name()).as_deref(), cx)
|
||||
}
|
||||
|
||||
pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle<Buffer>)) {
|
||||
self.buffers
|
||||
.borrow()
|
||||
@ -2764,6 +2775,16 @@ impl MultiBufferSnapshot {
|
||||
.and_then(|(buffer, offset)| buffer.language_at(offset))
|
||||
}
|
||||
|
||||
pub fn settings_at<'a, T: ToOffset>(
|
||||
&'a self,
|
||||
point: T,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a LanguageSettings {
|
||||
self.point_to_buffer_offset(point)
|
||||
.map(|(buffer, offset)| buffer.settings_at(offset, cx))
|
||||
.unwrap_or_else(|| language_settings(None, None, cx))
|
||||
}
|
||||
|
||||
pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
|
||||
self.point_to_buffer_offset(point)
|
||||
.and_then(|(buffer, offset)| buffer.language_scope_at(offset))
|
||||
|
@ -37,6 +37,7 @@ impl<'a> EditorLspTestContext<'a> {
|
||||
let app_state = cx.update(AppState::test);
|
||||
|
||||
cx.update(|cx| {
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
pane::init(cx);
|
||||
});
|
||||
|
@ -1,19 +1,16 @@
|
||||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
};
|
||||
use futures::Future;
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
};
|
||||
|
||||
use futures::Future;
|
||||
use indoc::indoc;
|
||||
|
||||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
};
|
||||
use gpui::{
|
||||
keymap_matcher::Keystroke, AppContext, ContextHandle, ModelContext, ViewContext, ViewHandle,
|
||||
};
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
use settings::Settings;
|
||||
use util::{
|
||||
assert_set_eq,
|
||||
test::{generate_marked_text, marked_text_ranges},
|
||||
@ -30,15 +27,10 @@ pub struct EditorTestContext<'a> {
|
||||
impl<'a> EditorTestContext<'a> {
|
||||
pub fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
|
||||
let (window_id, editor) = cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
crate::init(cx);
|
||||
|
||||
let (window_id, editor) = cx.add_window(Default::default(), |cx| {
|
||||
cx.add_window(Default::default(), |cx| {
|
||||
cx.focus_self();
|
||||
build_editor(MultiBuffer::build_simple("", cx), cx)
|
||||
});
|
||||
|
||||
(window_id, editor)
|
||||
})
|
||||
});
|
||||
|
||||
Self {
|
||||
|
@ -23,7 +23,9 @@ postage.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
serde_json.workspace = true
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
||||
serde_json.workspace = true
|
||||
ctor.workspace = true
|
||||
env_logger.workspace = true
|
||||
|
@ -270,6 +270,7 @@ impl PickerDelegate for FileFinderDelegate {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use editor::Editor;
|
||||
use gpui::TestAppContext;
|
||||
use menu::{Confirm, SelectNext};
|
||||
use serde_json::json;
|
||||
use workspace::{AppState, Workspace};
|
||||
@ -283,12 +284,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_paths(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = cx.update(|cx| {
|
||||
super::init(cx);
|
||||
editor::init(cx);
|
||||
AppState::test(cx)
|
||||
});
|
||||
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -339,7 +335,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_matching_cancellation(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -408,7 +404,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_ignored_files(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -462,7 +458,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_single_file_worktrees(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -516,9 +512,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_multiple_matches_with_same_relative_path(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -570,9 +564,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_path_distance_ordering(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -622,7 +614,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_worktree_without_files(cx: &mut gpui::TestAppContext) {
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -658,4 +650,15 @@ mod tests {
|
||||
assert_eq!(finder.delegate().matches.len(), 0);
|
||||
});
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
let state = AppState::test(cx);
|
||||
language::init(cx);
|
||||
super::init(cx);
|
||||
editor::init(cx);
|
||||
state
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -477,6 +477,14 @@ impl Deterministic {
|
||||
state.rng = StdRng::seed_from_u64(state.seed);
|
||||
}
|
||||
|
||||
pub fn allow_parking(&self) {
|
||||
use rand::prelude::*;
|
||||
|
||||
let mut state = self.state.lock();
|
||||
state.forbid_parking = false;
|
||||
state.rng = StdRng::seed_from_u64(state.seed);
|
||||
}
|
||||
|
||||
pub async fn simulate_random_delay(&self) {
|
||||
use rand::prelude::*;
|
||||
use smol::future::yield_now;
|
||||
@ -698,6 +706,14 @@ impl Foreground {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn allow_parking(&self) {
|
||||
match self {
|
||||
Self::Deterministic { executor, .. } => executor.allow_parking(),
|
||||
_ => panic!("this method can only be called on a deterministic executor"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn advance_clock(&self, duration: Duration) {
|
||||
match self {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use anyhow::Result;
|
||||
use chrono::{Datelike, Local, NaiveTime, Timelike};
|
||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||
use gpui::{actions, AppContext};
|
||||
@ -40,21 +41,8 @@ impl settings::Setting for JournalSettings {
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self {
|
||||
Self {
|
||||
path: Some(
|
||||
user_values
|
||||
.first()
|
||||
.and_then(|s| s.path.clone())
|
||||
.unwrap_or(default_value.path.clone().unwrap()),
|
||||
),
|
||||
hour_format: Some(
|
||||
user_values
|
||||
.first()
|
||||
.and_then(|s| s.hour_format.clone())
|
||||
.unwrap_or(default_value.hour_format.clone().unwrap()),
|
||||
),
|
||||
}
|
||||
fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,16 +36,19 @@ sum_tree = { path = "../sum_tree" }
|
||||
text = { path = "../text" }
|
||||
theme = { path = "../theme" }
|
||||
util = { path = "../util" }
|
||||
|
||||
anyhow.workspace = true
|
||||
async-broadcast = "0.4"
|
||||
async-trait.workspace = true
|
||||
futures.workspace = true
|
||||
glob.workspace = true
|
||||
lazy_static.workspace = true
|
||||
log.workspace = true
|
||||
parking_lot.workspace = true
|
||||
postage.workspace = true
|
||||
rand = { workspace = true, optional = true }
|
||||
regex.workspace = true
|
||||
schemars.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
@ -5,6 +5,7 @@ pub use crate::{
|
||||
};
|
||||
use crate::{
|
||||
diagnostic_set::{DiagnosticEntry, DiagnosticGroup},
|
||||
language_settings::{language_settings, LanguageSettings},
|
||||
outline::OutlineItem,
|
||||
syntax_map::{
|
||||
SyntaxMap, SyntaxMapCapture, SyntaxMapCaptures, SyntaxSnapshot, ToTreeSitterPoint,
|
||||
@ -18,7 +19,6 @@ use futures::FutureExt as _;
|
||||
use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, Task};
|
||||
use lsp::LanguageServerId;
|
||||
use parking_lot::Mutex;
|
||||
use settings::Settings;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
use smallvec::SmallVec;
|
||||
use smol::future::yield_now;
|
||||
@ -1827,11 +1827,11 @@ impl BufferSnapshot {
|
||||
|
||||
pub fn language_indent_size_at<T: ToOffset>(&self, position: T, cx: &AppContext) -> IndentSize {
|
||||
let language_name = self.language_at(position).map(|language| language.name());
|
||||
let settings = cx.global::<Settings>();
|
||||
if settings.hard_tabs(language_name.as_deref()) {
|
||||
let settings = language_settings(None, language_name.as_deref(), cx);
|
||||
if settings.hard_tabs {
|
||||
IndentSize::tab()
|
||||
} else {
|
||||
IndentSize::spaces(settings.tab_size(language_name.as_deref()).get())
|
||||
IndentSize::spaces(settings.tab_size.get())
|
||||
}
|
||||
}
|
||||
|
||||
@ -2146,6 +2146,15 @@ impl BufferSnapshot {
|
||||
.or(self.language.as_ref())
|
||||
}
|
||||
|
||||
pub fn settings_at<'a, D: ToOffset>(
|
||||
&self,
|
||||
position: D,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a LanguageSettings {
|
||||
let language = self.language_at(position);
|
||||
language_settings(None, language.map(|l| l.name()).as_deref(), cx)
|
||||
}
|
||||
|
||||
pub fn language_scope_at<D: ToOffset>(&self, position: D) -> Option<LanguageScope> {
|
||||
let offset = position.to_offset(self);
|
||||
|
||||
|
@ -1,3 +1,7 @@
|
||||
use crate::language_settings::{
|
||||
AllLanguageSettings, AllLanguageSettingsContent, LanguageSettingsContent,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use clock::ReplicaId;
|
||||
use collections::BTreeMap;
|
||||
@ -7,7 +11,7 @@ use indoc::indoc;
|
||||
use proto::deserialize_operation;
|
||||
use rand::prelude::*;
|
||||
use regex::RegexBuilder;
|
||||
use settings::Settings;
|
||||
use settings::SettingsStore;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
env,
|
||||
@ -36,7 +40,8 @@ fn init_logger() {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_line_endings(cx: &mut gpui::AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer =
|
||||
Buffer::new(0, "one\r\ntwo\rthree", cx).with_language(Arc::new(rust_lang()), cx);
|
||||
@ -862,8 +867,7 @@ fn test_range_for_syntax_ancestor(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "fn a() {}";
|
||||
@ -903,9 +907,9 @@ fn test_autoindent_with_soft_tabs(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.hard_tabs = Some(true);
|
||||
cx.set_global(settings);
|
||||
init_settings(cx, |settings| {
|
||||
settings.defaults.hard_tabs = Some(true);
|
||||
});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "fn a() {}";
|
||||
@ -945,8 +949,7 @@ fn test_autoindent_with_hard_tabs(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppContext) {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(
|
||||
@ -1082,8 +1085,7 @@ fn test_autoindent_does_not_adjust_lines_with_unchanged_suggestion(cx: &mut AppC
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut AppContext) {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(
|
||||
@ -1145,7 +1147,8 @@ fn test_autoindent_does_not_adjust_lines_within_newly_created_errors(cx: &mut Ap
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(
|
||||
0,
|
||||
@ -1201,7 +1204,8 @@ fn test_autoindent_adjusts_lines_when_only_text_changes(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "a\nb";
|
||||
let mut buffer = Buffer::new(0, text, cx).with_language(Arc::new(rust_lang()), cx);
|
||||
@ -1217,7 +1221,8 @@ fn test_autoindent_with_edit_at_end_of_buffer(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "
|
||||
const a: usize = 1;
|
||||
@ -1257,7 +1262,8 @@ fn test_autoindent_multi_line_insertion(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_block_mode(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = r#"
|
||||
fn a() {
|
||||
@ -1339,7 +1345,8 @@ fn test_autoindent_block_mode(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = r#"
|
||||
fn a() {
|
||||
@ -1417,7 +1424,8 @@ fn test_autoindent_block_mode_without_original_indent_columns(cx: &mut AppContex
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let text = "
|
||||
* one
|
||||
@ -1460,25 +1468,23 @@ fn test_autoindent_language_without_indents_query(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
|
||||
cx.set_global({
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.language_overrides.extend([
|
||||
init_settings(cx, |settings| {
|
||||
settings.languages.extend([
|
||||
(
|
||||
"HTML".into(),
|
||||
settings::EditorSettings {
|
||||
LanguageSettingsContent {
|
||||
tab_size: Some(2.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"JavaScript".into(),
|
||||
settings::EditorSettings {
|
||||
LanguageSettingsContent {
|
||||
tab_size: Some(8.try_into().unwrap()),
|
||||
..Default::default()
|
||||
},
|
||||
),
|
||||
]);
|
||||
settings
|
||||
])
|
||||
});
|
||||
|
||||
let html_language = Arc::new(
|
||||
@ -1574,9 +1580,10 @@ fn test_autoindent_with_injected_languages(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_defaults.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
init_settings(cx, |settings| {
|
||||
settings.defaults.tab_size = Some(2.try_into().unwrap());
|
||||
});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let mut buffer = Buffer::new(0, "", cx).with_language(Arc::new(ruby_lang()), cx);
|
||||
|
||||
@ -1617,7 +1624,8 @@ fn test_autoindent_query_with_outdent_captures(cx: &mut AppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
fn test_language_config_at(cx: &mut AppContext) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
let language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -2199,7 +2207,6 @@ fn assert_bracket_pairs(
|
||||
language: Language,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
cx.set_global(Settings::test(cx));
|
||||
let (expected_text, selection_ranges) = marked_text_ranges(selection_text, false);
|
||||
let buffer = cx.add_model(|cx| {
|
||||
Buffer::new(0, expected_text.clone(), cx).with_language(Arc::new(language), cx)
|
||||
@ -2222,3 +2229,11 @@ fn assert_bracket_pairs(
|
||||
bracket_pairs
|
||||
);
|
||||
}
|
||||
|
||||
fn init_settings(cx: &mut AppContext, f: fn(&mut AllLanguageSettingsContent)) {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
crate::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|settings, cx| {
|
||||
settings.update_user_settings::<AllLanguageSettings>(cx, f);
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
mod buffer;
|
||||
mod diagnostic_set;
|
||||
mod highlight_map;
|
||||
pub mod language_settings;
|
||||
mod outline;
|
||||
pub mod proto;
|
||||
mod syntax_map;
|
||||
@ -58,6 +59,10 @@ pub use lsp::LanguageServerId;
|
||||
pub use outline::{Outline, OutlineItem};
|
||||
pub use tree_sitter::{Parser, Tree};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
language_settings::init(cx);
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static PARSER: RefCell<Parser> = RefCell::new(Parser::new());
|
||||
}
|
||||
|
285
crates/language/src/language_settings.rs
Normal file
285
crates/language/src/language_settings.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use anyhow::Result;
|
||||
use collections::HashMap;
|
||||
use gpui::AppContext;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{num::NonZeroU32, path::Path, sync::Arc};
|
||||
|
||||
pub fn init(cx: &mut AppContext) {
|
||||
settings::register_setting::<AllLanguageSettings>(cx);
|
||||
}
|
||||
|
||||
pub fn language_settings<'a>(
|
||||
path: Option<&Path>,
|
||||
language: Option<&str>,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a LanguageSettings {
|
||||
settings::get_setting::<AllLanguageSettings>(path, cx).language(language)
|
||||
}
|
||||
|
||||
pub fn all_language_settings<'a>(
|
||||
path: Option<&Path>,
|
||||
cx: &'a AppContext,
|
||||
) -> &'a AllLanguageSettings {
|
||||
settings::get_setting::<AllLanguageSettings>(path, cx)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AllLanguageSettings {
|
||||
pub copilot: CopilotSettings,
|
||||
defaults: LanguageSettings,
|
||||
languages: HashMap<Arc<str>, LanguageSettings>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct LanguageSettings {
|
||||
pub tab_size: NonZeroU32,
|
||||
pub hard_tabs: bool,
|
||||
pub soft_wrap: SoftWrap,
|
||||
pub preferred_line_length: u32,
|
||||
pub format_on_save: FormatOnSave,
|
||||
pub remove_trailing_whitespace_on_save: bool,
|
||||
pub ensure_final_newline_on_save: bool,
|
||||
pub formatter: Formatter,
|
||||
pub enable_language_server: bool,
|
||||
pub show_copilot_suggestions: bool,
|
||||
pub show_whitespaces: ShowWhitespaceSetting,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CopilotSettings {
|
||||
pub feature_enabled: bool,
|
||||
pub disabled_globs: Vec<glob::Pattern>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct AllLanguageSettingsContent {
|
||||
#[serde(default)]
|
||||
pub features: Option<FeaturesContent>,
|
||||
#[serde(default)]
|
||||
pub copilot: Option<CopilotSettingsContent>,
|
||||
#[serde(flatten)]
|
||||
pub defaults: LanguageSettingsContent,
|
||||
#[serde(default, alias = "language_overrides")]
|
||||
pub languages: HashMap<Arc<str>, LanguageSettingsContent>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct LanguageSettingsContent {
|
||||
#[serde(default)]
|
||||
pub tab_size: Option<NonZeroU32>,
|
||||
#[serde(default)]
|
||||
pub hard_tabs: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub soft_wrap: Option<SoftWrap>,
|
||||
#[serde(default)]
|
||||
pub preferred_line_length: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub format_on_save: Option<FormatOnSave>,
|
||||
#[serde(default)]
|
||||
pub remove_trailing_whitespace_on_save: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub ensure_final_newline_on_save: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub formatter: Option<Formatter>,
|
||||
#[serde(default)]
|
||||
pub enable_language_server: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub show_copilot_suggestions: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub show_whitespaces: Option<ShowWhitespaceSetting>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CopilotSettingsContent {
|
||||
#[serde(default)]
|
||||
pub disabled_globs: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct FeaturesContent {
|
||||
pub copilot: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SoftWrap {
|
||||
None,
|
||||
EditorWidth,
|
||||
PreferredLineLength,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FormatOnSave {
|
||||
On,
|
||||
Off,
|
||||
LanguageServer,
|
||||
External {
|
||||
command: Arc<str>,
|
||||
arguments: Arc<[String]>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowWhitespaceSetting {
|
||||
Selection,
|
||||
None,
|
||||
All,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Formatter {
|
||||
LanguageServer,
|
||||
External {
|
||||
command: Arc<str>,
|
||||
arguments: Arc<[String]>,
|
||||
},
|
||||
}
|
||||
|
||||
impl AllLanguageSettings {
|
||||
pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
|
||||
if let Some(name) = language_name {
|
||||
if let Some(overrides) = self.languages.get(name) {
|
||||
return overrides;
|
||||
}
|
||||
}
|
||||
&self.defaults
|
||||
}
|
||||
|
||||
pub fn copilot_enabled_for_path(&self, path: &Path) -> bool {
|
||||
!self
|
||||
.copilot
|
||||
.disabled_globs
|
||||
.iter()
|
||||
.any(|glob| glob.matches_path(path))
|
||||
}
|
||||
|
||||
pub fn copilot_enabled(&self, language_name: Option<&str>, path: Option<&Path>) -> bool {
|
||||
if !self.copilot.feature_enabled {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(path) = path {
|
||||
if !self.copilot_enabled_for_path(path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
self.language(language_name).show_copilot_suggestions
|
||||
}
|
||||
}
|
||||
|
||||
impl settings::Setting for AllLanguageSettings {
|
||||
const KEY: Option<&'static str> = None;
|
||||
|
||||
type FileContent = AllLanguageSettingsContent;
|
||||
|
||||
fn load(
|
||||
default_value: &Self::FileContent,
|
||||
user_settings: &[&Self::FileContent],
|
||||
_: &AppContext,
|
||||
) -> Result<Self> {
|
||||
// A default is provided for all settings.
|
||||
let mut defaults: LanguageSettings =
|
||||
serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
|
||||
|
||||
let mut languages = HashMap::default();
|
||||
for (language_name, settings) in &default_value.languages {
|
||||
let mut language_settings = defaults.clone();
|
||||
merge_settings(&mut language_settings, &settings);
|
||||
languages.insert(language_name.clone(), language_settings);
|
||||
}
|
||||
|
||||
let mut copilot_enabled = default_value
|
||||
.features
|
||||
.as_ref()
|
||||
.and_then(|f| f.copilot)
|
||||
.ok_or_else(Self::missing_default)?;
|
||||
let mut copilot_globs = default_value
|
||||
.copilot
|
||||
.as_ref()
|
||||
.and_then(|c| c.disabled_globs.as_ref())
|
||||
.ok_or_else(Self::missing_default)?;
|
||||
|
||||
for user_settings in user_settings {
|
||||
if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
|
||||
copilot_enabled = copilot;
|
||||
}
|
||||
if let Some(globs) = user_settings
|
||||
.copilot
|
||||
.as_ref()
|
||||
.and_then(|f| f.disabled_globs.as_ref())
|
||||
{
|
||||
copilot_globs = globs;
|
||||
}
|
||||
|
||||
// A user's global settings override the default global settings and
|
||||
// all default language-specific settings.
|
||||
merge_settings(&mut defaults, &user_settings.defaults);
|
||||
for language_settings in languages.values_mut() {
|
||||
merge_settings(language_settings, &user_settings.defaults);
|
||||
}
|
||||
|
||||
// A user's language-specific settings override default language-specific settings.
|
||||
for (language_name, user_language_settings) in &user_settings.languages {
|
||||
merge_settings(
|
||||
languages
|
||||
.entry(language_name.clone())
|
||||
.or_insert_with(|| defaults.clone()),
|
||||
&user_language_settings,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
copilot: CopilotSettings {
|
||||
feature_enabled: copilot_enabled,
|
||||
disabled_globs: copilot_globs
|
||||
.iter()
|
||||
.filter_map(|pattern| glob::Pattern::new(pattern).ok())
|
||||
.collect(),
|
||||
},
|
||||
defaults,
|
||||
languages,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
|
||||
merge(&mut settings.tab_size, src.tab_size);
|
||||
merge(&mut settings.hard_tabs, src.hard_tabs);
|
||||
merge(&mut settings.soft_wrap, src.soft_wrap);
|
||||
merge(
|
||||
&mut settings.preferred_line_length,
|
||||
src.preferred_line_length,
|
||||
);
|
||||
merge(&mut settings.formatter, src.formatter.clone());
|
||||
merge(&mut settings.format_on_save, src.format_on_save.clone());
|
||||
merge(
|
||||
&mut settings.remove_trailing_whitespace_on_save,
|
||||
src.remove_trailing_whitespace_on_save,
|
||||
);
|
||||
merge(
|
||||
&mut settings.ensure_final_newline_on_save,
|
||||
src.ensure_final_newline_on_save,
|
||||
);
|
||||
merge(
|
||||
&mut settings.enable_language_server,
|
||||
src.enable_language_server,
|
||||
);
|
||||
merge(
|
||||
&mut settings.show_copilot_suggestions,
|
||||
src.show_copilot_suggestions,
|
||||
);
|
||||
merge(&mut settings.show_whitespaces, src.show_whitespaces);
|
||||
|
||||
fn merge<T>(target: &mut T, value: Option<T>) {
|
||||
if let Some(value) = value {
|
||||
*target = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ use gpui::{
|
||||
ModelHandle, Task, WeakModelHandle,
|
||||
};
|
||||
use language::{
|
||||
language_settings::{all_language_settings, language_settings, FormatOnSave, Formatter},
|
||||
point_to_lsp,
|
||||
proto::{
|
||||
deserialize_anchor, deserialize_fingerprint, deserialize_line_ending, deserialize_version,
|
||||
@ -44,7 +45,7 @@ use postage::watch;
|
||||
use rand::prelude::*;
|
||||
use search::SearchQuery;
|
||||
use serde::Serialize;
|
||||
use settings::{FormatOnSave, Formatter, Settings};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use sha2::{Digest, Sha256};
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
use std::{
|
||||
@ -64,9 +65,7 @@ use std::{
|
||||
},
|
||||
time::{Duration, Instant, SystemTime},
|
||||
};
|
||||
|
||||
use terminals::Terminals;
|
||||
|
||||
use util::{debug_panic, defer, merge_json_value_into, post_inc, ResultExt, TryFutureExt as _};
|
||||
|
||||
pub use fs::*;
|
||||
@ -454,7 +453,9 @@ impl Project {
|
||||
client_state: None,
|
||||
opened_buffer: watch::channel(),
|
||||
client_subscriptions: Vec::new(),
|
||||
_subscriptions: vec![cx.observe_global::<Settings, _>(Self::on_settings_changed)],
|
||||
_subscriptions: vec![
|
||||
cx.observe_global::<SettingsStore, _>(Self::on_settings_changed)
|
||||
],
|
||||
_maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
|
||||
_maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
|
||||
active_entry: None,
|
||||
@ -622,7 +623,7 @@ impl Project {
|
||||
}
|
||||
|
||||
fn on_settings_changed(&mut self, cx: &mut ModelContext<Self>) {
|
||||
let settings = cx.global::<Settings>();
|
||||
let settings = all_language_settings(None, cx);
|
||||
|
||||
let mut language_servers_to_start = Vec::new();
|
||||
for buffer in self.opened_buffers.values() {
|
||||
@ -630,7 +631,10 @@ impl Project {
|
||||
let buffer = buffer.read(cx);
|
||||
if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language())
|
||||
{
|
||||
if settings.enable_language_server(Some(&language.name())) {
|
||||
if settings
|
||||
.language(Some(&language.name()))
|
||||
.enable_language_server
|
||||
{
|
||||
let worktree = file.worktree.read(cx);
|
||||
language_servers_to_start.push((
|
||||
worktree.id(),
|
||||
@ -645,7 +649,10 @@ impl Project {
|
||||
let mut language_servers_to_stop = Vec::new();
|
||||
for language in self.languages.to_vec() {
|
||||
for lsp_adapter in language.lsp_adapters() {
|
||||
if !settings.enable_language_server(Some(&language.name())) {
|
||||
if !settings
|
||||
.language(Some(&language.name()))
|
||||
.enable_language_server
|
||||
{
|
||||
let lsp_name = &lsp_adapter.name;
|
||||
for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
|
||||
if lsp_name == started_lsp_name {
|
||||
@ -2178,10 +2185,7 @@ impl Project {
|
||||
language: Arc<Language>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) {
|
||||
if !cx
|
||||
.global::<Settings>()
|
||||
.enable_language_server(Some(&language.name()))
|
||||
{
|
||||
if !language_settings(None, Some(&language.name()), cx).enable_language_server {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3228,24 +3232,18 @@ impl Project {
|
||||
|
||||
let mut project_transaction = ProjectTransaction::default();
|
||||
for (buffer, buffer_abs_path, language_server) in &buffers_with_paths_and_servers {
|
||||
let (
|
||||
format_on_save,
|
||||
remove_trailing_whitespace,
|
||||
ensure_final_newline,
|
||||
formatter,
|
||||
tab_size,
|
||||
) = buffer.read_with(&cx, |buffer, cx| {
|
||||
let settings = cx.global::<Settings>();
|
||||
let settings = buffer.read_with(&cx, |buffer, cx| {
|
||||
let language_name = buffer.language().map(|language| language.name());
|
||||
(
|
||||
settings.format_on_save(language_name.as_deref()),
|
||||
settings.remove_trailing_whitespace_on_save(language_name.as_deref()),
|
||||
settings.ensure_final_newline_on_save(language_name.as_deref()),
|
||||
settings.formatter(language_name.as_deref()),
|
||||
settings.tab_size(language_name.as_deref()),
|
||||
)
|
||||
language_settings(buffer_abs_path.as_deref(), language_name.as_deref(), cx)
|
||||
.clone()
|
||||
});
|
||||
|
||||
let remove_trailing_whitespace = settings.remove_trailing_whitespace_on_save;
|
||||
let ensure_final_newline = settings.ensure_final_newline_on_save;
|
||||
let format_on_save = settings.format_on_save.clone();
|
||||
let formatter = settings.formatter.clone();
|
||||
let tab_size = settings.tab_size;
|
||||
|
||||
// First, format buffer's whitespace according to the settings.
|
||||
let trailing_whitespace_diff = if remove_trailing_whitespace {
|
||||
Some(
|
||||
|
@ -1,10 +1,9 @@
|
||||
use crate::{worktree::WorktreeHandle, Event, *};
|
||||
use fs::LineEnding;
|
||||
use fs::{FakeFs, RealFs};
|
||||
use fs::{FakeFs, LineEnding, RealFs};
|
||||
use futures::{future, StreamExt};
|
||||
use gpui::AppContext;
|
||||
use gpui::{executor::Deterministic, test::subscribe};
|
||||
use gpui::{executor::Deterministic, test::subscribe, AppContext};
|
||||
use language::{
|
||||
language_settings::{AllLanguageSettings, LanguageSettingsContent},
|
||||
tree_sitter_rust, tree_sitter_typescript, Diagnostic, FakeLspAdapter, LanguageConfig,
|
||||
OffsetRangeExt, Point, ToPoint,
|
||||
};
|
||||
@ -26,6 +25,9 @@ fn init_logger() {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_symlinks(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
cx.foreground().allow_parking();
|
||||
|
||||
let dir = temp_tree(json!({
|
||||
"root": {
|
||||
"apple": "",
|
||||
@ -65,7 +67,7 @@ async fn test_managing_language_servers(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut rust_language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -451,7 +453,7 @@ async fn test_managing_language_servers(
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -556,7 +558,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -648,7 +650,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -719,7 +721,7 @@ async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let progress_token = "the-progress-token";
|
||||
let mut language = Language::new(
|
||||
@ -847,7 +849,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let progress_token = "the-progress-token";
|
||||
let mut language = Language::new(
|
||||
@ -925,7 +927,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -973,11 +975,8 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_toggling_enable_language_server(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
deterministic.forbid_parking();
|
||||
async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut rust = Language::new(
|
||||
LanguageConfig {
|
||||
@ -1051,14 +1050,16 @@ async fn test_toggling_enable_language_server(
|
||||
|
||||
// Disable Rust language server, ensuring only that server gets stopped.
|
||||
cx.update(|cx| {
|
||||
cx.update_global(|settings: &mut Settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
Arc::from("Rust"),
|
||||
settings::EditorSettings {
|
||||
enable_language_server: Some(false),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
cx.update_global(|settings: &mut SettingsStore, cx| {
|
||||
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
Arc::from("Rust"),
|
||||
LanguageSettingsContent {
|
||||
enable_language_server: Some(false),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
fake_rust_server_1
|
||||
@ -1068,21 +1069,23 @@ async fn test_toggling_enable_language_server(
|
||||
// Enable Rust and disable JavaScript language servers, ensuring that the
|
||||
// former gets started again and that the latter stops.
|
||||
cx.update(|cx| {
|
||||
cx.update_global(|settings: &mut Settings, _| {
|
||||
settings.language_overrides.insert(
|
||||
Arc::from("Rust"),
|
||||
settings::EditorSettings {
|
||||
enable_language_server: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
settings.language_overrides.insert(
|
||||
Arc::from("JavaScript"),
|
||||
settings::EditorSettings {
|
||||
enable_language_server: Some(false),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
cx.update_global(|settings: &mut SettingsStore, cx| {
|
||||
settings.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
settings.languages.insert(
|
||||
Arc::from("Rust"),
|
||||
LanguageSettingsContent {
|
||||
enable_language_server: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
settings.languages.insert(
|
||||
Arc::from("JavaScript"),
|
||||
LanguageSettingsContent {
|
||||
enable_language_server: Some(false),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
});
|
||||
})
|
||||
});
|
||||
let mut fake_rust_server_2 = fake_rust_servers.next().await.unwrap();
|
||||
@ -1102,7 +1105,7 @@ async fn test_toggling_enable_language_server(
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -1388,7 +1391,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let text = concat!(
|
||||
"let one = ;\n", //
|
||||
@ -1457,9 +1460,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) {
|
||||
println!("hello from stdout");
|
||||
eprintln!("hello from stderr");
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "one two three" }))
|
||||
@ -1515,7 +1516,7 @@ async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppC
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -1673,7 +1674,7 @@ async fn test_edits_from_lsp_with_past_version(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let text = "
|
||||
use a::b;
|
||||
@ -1781,7 +1782,7 @@ async fn test_edits_from_lsp_with_edits_on_adjacent_lines(cx: &mut gpui::TestApp
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_invalid_edits_from_lsp(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let text = "
|
||||
use a::b;
|
||||
@ -1902,6 +1903,8 @@ fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
@ -2001,6 +2004,8 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
@ -2085,6 +2090,8 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
@ -2138,6 +2145,8 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
@ -2254,6 +2263,8 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test(iterations = 10)]
|
||||
async fn test_save_file(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2284,6 +2295,8 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2313,6 +2326,8 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_save_as(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree("/dir", json!({})).await;
|
||||
|
||||
@ -2373,6 +2388,9 @@ async fn test_rescan_and_remote_updates(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
cx.foreground().allow_parking();
|
||||
|
||||
let dir = temp_tree(json!({
|
||||
"a": {
|
||||
"file1": "",
|
||||
@ -2529,6 +2547,8 @@ async fn test_buffer_identity_across_renames(
|
||||
deterministic: Arc<Deterministic>,
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2577,6 +2597,8 @@ async fn test_buffer_identity_across_renames(
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2621,6 +2643,8 @@ async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2765,6 +2789,8 @@ async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let initial_contents = "aaa\nbbbbb\nc\n";
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -2844,6 +2870,8 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -2904,7 +2932,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -3146,7 +3174,7 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_rename(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -3284,6 +3312,8 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
@ -3339,6 +3369,8 @@ async fn test_search(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
@ -3447,6 +3479,8 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
@ -3554,6 +3588,8 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
@ -3680,3 +3716,12 @@ async fn search(
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
});
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ futures.workspace = true
|
||||
unicase = "2.6"
|
||||
|
||||
[dev-dependencies]
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
editor = { path = "../editor", features = ["test-support"] }
|
||||
gpui = { path = "../gpui", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
@ -1360,15 +1360,12 @@ mod tests {
|
||||
use gpui::{TestAppContext, ViewHandle};
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::{collections::HashSet, path::Path};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_visible_list(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
});
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -1456,11 +1453,7 @@ mod tests {
|
||||
|
||||
#[gpui::test(iterations = 30)]
|
||||
async fn test_editing_files(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
});
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -1776,11 +1769,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_copy_paste(cx: &mut gpui::TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
let settings = Settings::test(cx);
|
||||
cx.set_global(settings);
|
||||
});
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -1940,4 +1929,12 @@ mod tests {
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -30,3 +30,4 @@ gpui = { path = "../gpui", features = ["test-support"] }
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
lsp = { path = "../lsp", features = ["test-support"] }
|
||||
project = { path = "../project", features = ["test-support"] }
|
||||
workspace = { path = "../workspace", features = ["test-support"] }
|
||||
|
@ -244,12 +244,12 @@ mod tests {
|
||||
use gpui::{serde_json::json, TestAppContext};
|
||||
use language::{FakeLspAdapter, Language, LanguageConfig};
|
||||
use project::FakeFs;
|
||||
use settings::SettingsStore;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_project_symbols(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| cx.set_global(Settings::test(cx)));
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
@ -368,6 +368,15 @@ mod tests {
|
||||
});
|
||||
}
|
||||
|
||||
fn init_test(cx: &mut TestAppContext) {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
cx.set_global(Settings::test(cx));
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn symbol(name: &str, path: impl AsRef<Path>) -> lsp::SymbolInformation {
|
||||
#[allow(deprecated)]
|
||||
lsp::SymbolInformation {
|
||||
|
@ -655,19 +655,11 @@ mod tests {
|
||||
use editor::{DisplayPoint, Editor};
|
||||
use gpui::{color::Color, test::EmptyView, TestAppContext};
|
||||
use language::Buffer;
|
||||
use std::sync::Arc;
|
||||
use unindent::Unindent as _;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_search_simple(cx: &mut TestAppContext) {
|
||||
let fonts = cx.font_cache();
|
||||
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default);
|
||||
theme.search.match_background = Color::red();
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.theme = Arc::new(theme);
|
||||
cx.set_global(settings)
|
||||
});
|
||||
crate::project_search::tests::init_test(cx);
|
||||
|
||||
let buffer = cx.add_model(|cx| {
|
||||
Buffer::new(
|
||||
|
@ -1146,25 +1146,18 @@ impl ToolbarItemView for ProjectSearchBar {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use editor::DisplayPoint;
|
||||
use gpui::{color::Color, executor::Deterministic, TestAppContext};
|
||||
use project::FakeFs;
|
||||
use serde_json::json;
|
||||
use settings::SettingsStore;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_project_search(deterministic: Arc<Deterministic>, cx: &mut TestAppContext) {
|
||||
let fonts = cx.font_cache();
|
||||
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default);
|
||||
theme.search.match_background = Color::red();
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.theme = Arc::new(theme);
|
||||
cx.set_global(settings);
|
||||
cx.set_global(ActiveSearches::default());
|
||||
});
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.background());
|
||||
fs.insert_tree(
|
||||
@ -1279,4 +1272,20 @@ mod tests {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn init_test(cx: &mut TestAppContext) {
|
||||
let fonts = cx.font_cache();
|
||||
let mut theme = gpui::fonts::with_font_cache(fonts.clone(), theme::Theme::default);
|
||||
theme.search.match_background = Color::red();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
cx.set_global(ActiveSearches::default());
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.theme = Arc::new(theme);
|
||||
cx.set_global(settings);
|
||||
|
||||
language::init(cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ mod keymap_file;
|
||||
mod settings_file;
|
||||
mod settings_store;
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::{bail, Result};
|
||||
use gpui::{
|
||||
font_cache::{FamilyId, FontCache},
|
||||
fonts, AppContext, AssetSource,
|
||||
@ -19,7 +19,7 @@ use sqlez::{
|
||||
bindable::{Bind, Column, StaticColumnCount},
|
||||
statement::Statement,
|
||||
};
|
||||
use std::{borrow::Cow, collections::HashMap, num::NonZeroU32, path::Path, str, sync::Arc};
|
||||
use std::{borrow::Cow, collections::HashMap, str, sync::Arc};
|
||||
use theme::{Theme, ThemeRegistry};
|
||||
use util::ResultExt as _;
|
||||
|
||||
@ -33,7 +33,6 @@ pub const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settin
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Settings {
|
||||
pub features: Features,
|
||||
pub buffer_font_family_name: String,
|
||||
pub buffer_font_features: fonts::Features,
|
||||
pub buffer_font_family: FamilyId,
|
||||
@ -46,13 +45,8 @@ pub struct Settings {
|
||||
pub show_call_status_icon: bool,
|
||||
pub autosave: Autosave,
|
||||
pub default_dock_anchor: DockAnchor,
|
||||
pub editor_defaults: EditorSettings,
|
||||
pub editor_overrides: EditorSettings,
|
||||
pub git: GitSettings,
|
||||
pub git_overrides: GitSettings,
|
||||
pub copilot: CopilotSettings,
|
||||
pub language_defaults: HashMap<Arc<str>, EditorSettings>,
|
||||
pub language_overrides: HashMap<Arc<str>, EditorSettings>,
|
||||
pub lsp: HashMap<Arc<str>, LspSettings>,
|
||||
pub theme: Arc<Theme>,
|
||||
pub base_keymap: BaseKeymap,
|
||||
@ -67,7 +61,7 @@ impl Setting for Settings {
|
||||
defaults: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
cx: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
let buffer_font_features = defaults.buffer_font_features.clone().unwrap();
|
||||
let themes = cx.global::<Arc<ThemeRegistry>>();
|
||||
|
||||
@ -90,50 +84,18 @@ impl Setting for Settings {
|
||||
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
|
||||
autosave: defaults.autosave.unwrap(),
|
||||
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
|
||||
editor_defaults: EditorSettings {
|
||||
tab_size: defaults.editor.tab_size,
|
||||
hard_tabs: defaults.editor.hard_tabs,
|
||||
soft_wrap: defaults.editor.soft_wrap,
|
||||
preferred_line_length: defaults.editor.preferred_line_length,
|
||||
remove_trailing_whitespace_on_save: defaults
|
||||
.editor
|
||||
.remove_trailing_whitespace_on_save,
|
||||
ensure_final_newline_on_save: defaults.editor.ensure_final_newline_on_save,
|
||||
format_on_save: defaults.editor.format_on_save.clone(),
|
||||
formatter: defaults.editor.formatter.clone(),
|
||||
enable_language_server: defaults.editor.enable_language_server,
|
||||
show_copilot_suggestions: defaults.editor.show_copilot_suggestions,
|
||||
show_whitespaces: defaults.editor.show_whitespaces,
|
||||
},
|
||||
editor_overrides: Default::default(),
|
||||
copilot: CopilotSettings {
|
||||
disabled_globs: defaults
|
||||
.copilot
|
||||
.clone()
|
||||
.unwrap()
|
||||
.disabled_globs
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|s| glob::Pattern::new(&s).unwrap())
|
||||
.collect(),
|
||||
},
|
||||
git: defaults.git.unwrap(),
|
||||
git_overrides: Default::default(),
|
||||
language_defaults: defaults.languages.clone(),
|
||||
language_overrides: Default::default(),
|
||||
lsp: defaults.lsp.clone(),
|
||||
theme: themes.get(defaults.theme.as_ref().unwrap()).unwrap(),
|
||||
base_keymap: Default::default(),
|
||||
features: Features {
|
||||
copilot: defaults.features.copilot.unwrap(),
|
||||
},
|
||||
};
|
||||
|
||||
for value in user_values.into_iter().copied().cloned() {
|
||||
this.set_user_settings(value, themes.as_ref(), cx.font_cache());
|
||||
}
|
||||
|
||||
this
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
fn json_schema(
|
||||
@ -247,18 +209,6 @@ impl BaseKeymap {
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct CopilotSettings {
|
||||
pub disabled_globs: Vec<glob::Pattern>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct CopilotSettingsContent {
|
||||
#[serde(default)]
|
||||
pub disabled_globs: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct GitSettings {
|
||||
pub git_gutter: Option<GitGutter>,
|
||||
@ -273,52 +223,6 @@ pub enum GitGutter {
|
||||
Hide,
|
||||
}
|
||||
|
||||
pub struct GitGutterConfig {}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct EditorSettings {
|
||||
pub tab_size: Option<NonZeroU32>,
|
||||
pub hard_tabs: Option<bool>,
|
||||
pub soft_wrap: Option<SoftWrap>,
|
||||
pub preferred_line_length: Option<u32>,
|
||||
pub format_on_save: Option<FormatOnSave>,
|
||||
pub remove_trailing_whitespace_on_save: Option<bool>,
|
||||
pub ensure_final_newline_on_save: Option<bool>,
|
||||
pub formatter: Option<Formatter>,
|
||||
pub enable_language_server: Option<bool>,
|
||||
pub show_copilot_suggestions: Option<bool>,
|
||||
pub show_whitespaces: Option<ShowWhitespaces>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SoftWrap {
|
||||
None,
|
||||
EditorWidth,
|
||||
PreferredLineLength,
|
||||
}
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum FormatOnSave {
|
||||
On,
|
||||
Off,
|
||||
LanguageServer,
|
||||
External {
|
||||
command: String,
|
||||
arguments: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Formatter {
|
||||
LanguageServer,
|
||||
External {
|
||||
command: String,
|
||||
arguments: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum Autosave {
|
||||
@ -374,8 +278,6 @@ pub struct SettingsFileContent {
|
||||
#[serde(default)]
|
||||
pub buffer_font_features: Option<fonts::Features>,
|
||||
#[serde(default)]
|
||||
pub copilot: Option<CopilotSettingsContent>,
|
||||
#[serde(default)]
|
||||
pub active_pane_magnification: Option<f32>,
|
||||
#[serde(default)]
|
||||
pub cursor_blink: Option<bool>,
|
||||
@ -391,21 +293,14 @@ pub struct SettingsFileContent {
|
||||
pub autosave: Option<Autosave>,
|
||||
#[serde(default)]
|
||||
pub default_dock_anchor: Option<DockAnchor>,
|
||||
#[serde(flatten)]
|
||||
pub editor: EditorSettings,
|
||||
#[serde(default)]
|
||||
pub git: Option<GitSettings>,
|
||||
#[serde(default)]
|
||||
#[serde(alias = "language_overrides")]
|
||||
pub languages: HashMap<Arc<str>, EditorSettings>,
|
||||
#[serde(default)]
|
||||
pub lsp: HashMap<Arc<str>, LspSettings>,
|
||||
#[serde(default)]
|
||||
pub theme: Option<String>,
|
||||
#[serde(default)]
|
||||
pub base_keymap: Option<BaseKeymap>,
|
||||
#[serde(default)]
|
||||
pub features: FeaturesContent,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
@ -414,26 +309,6 @@ pub struct LspSettings {
|
||||
pub initialization_options: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Features {
|
||||
pub copilot: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct FeaturesContent {
|
||||
pub copilot: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ShowWhitespaces {
|
||||
#[default]
|
||||
Selection,
|
||||
None,
|
||||
All,
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn initial_user_settings_content(assets: &'static impl AssetSource) -> Cow<'static, str> {
|
||||
match assets.load(INITIAL_USER_SETTINGS_ASSET_PATH).unwrap() {
|
||||
@ -448,12 +323,6 @@ impl Settings {
|
||||
font_cache: &FontCache,
|
||||
themes: &ThemeRegistry,
|
||||
) -> Self {
|
||||
#[track_caller]
|
||||
fn required<T>(value: Option<T>) -> Option<T> {
|
||||
assert!(value.is_some(), "missing default setting value");
|
||||
value
|
||||
}
|
||||
|
||||
let defaults: SettingsFileContent = settings_store::parse_json_with_comments(
|
||||
str::from_utf8(assets.load(DEFAULT_SETTINGS_ASSET_PATH).unwrap().as_ref()).unwrap(),
|
||||
)
|
||||
@ -478,44 +347,11 @@ impl Settings {
|
||||
show_call_status_icon: defaults.show_call_status_icon.unwrap(),
|
||||
autosave: defaults.autosave.unwrap(),
|
||||
default_dock_anchor: defaults.default_dock_anchor.unwrap(),
|
||||
editor_defaults: EditorSettings {
|
||||
tab_size: required(defaults.editor.tab_size),
|
||||
hard_tabs: required(defaults.editor.hard_tabs),
|
||||
soft_wrap: required(defaults.editor.soft_wrap),
|
||||
preferred_line_length: required(defaults.editor.preferred_line_length),
|
||||
remove_trailing_whitespace_on_save: required(
|
||||
defaults.editor.remove_trailing_whitespace_on_save,
|
||||
),
|
||||
ensure_final_newline_on_save: required(
|
||||
defaults.editor.ensure_final_newline_on_save,
|
||||
),
|
||||
format_on_save: required(defaults.editor.format_on_save),
|
||||
formatter: required(defaults.editor.formatter),
|
||||
enable_language_server: required(defaults.editor.enable_language_server),
|
||||
show_copilot_suggestions: required(defaults.editor.show_copilot_suggestions),
|
||||
show_whitespaces: required(defaults.editor.show_whitespaces),
|
||||
},
|
||||
editor_overrides: Default::default(),
|
||||
copilot: CopilotSettings {
|
||||
disabled_globs: defaults
|
||||
.copilot
|
||||
.unwrap()
|
||||
.disabled_globs
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|s| glob::Pattern::new(&s).unwrap())
|
||||
.collect(),
|
||||
},
|
||||
git: defaults.git.unwrap(),
|
||||
git_overrides: Default::default(),
|
||||
language_defaults: defaults.languages,
|
||||
language_overrides: Default::default(),
|
||||
lsp: defaults.lsp.clone(),
|
||||
theme: themes.get(&defaults.theme.unwrap()).unwrap(),
|
||||
base_keymap: Default::default(),
|
||||
features: Features {
|
||||
copilot: defaults.features.copilot.unwrap(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -565,121 +401,11 @@ impl Settings {
|
||||
merge(&mut self.autosave, data.autosave);
|
||||
merge(&mut self.default_dock_anchor, data.default_dock_anchor);
|
||||
merge(&mut self.base_keymap, data.base_keymap);
|
||||
merge(&mut self.features.copilot, data.features.copilot);
|
||||
|
||||
if let Some(copilot) = data.copilot {
|
||||
if let Some(disabled_globs) = copilot.disabled_globs {
|
||||
self.copilot.disabled_globs = disabled_globs
|
||||
.into_iter()
|
||||
.filter_map(|s| glob::Pattern::new(&s).ok())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
self.editor_overrides = data.editor;
|
||||
self.git_overrides = data.git.unwrap_or_default();
|
||||
self.language_overrides = data.languages;
|
||||
self.lsp = data.lsp;
|
||||
}
|
||||
|
||||
pub fn with_language_defaults(
|
||||
mut self,
|
||||
language_name: impl Into<Arc<str>>,
|
||||
overrides: EditorSettings,
|
||||
) -> Self {
|
||||
self.language_defaults
|
||||
.insert(language_name.into(), overrides);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn features(&self) -> &Features {
|
||||
&self.features
|
||||
}
|
||||
|
||||
pub fn show_copilot_suggestions(&self, language: Option<&str>, path: Option<&Path>) -> bool {
|
||||
if !self.features.copilot {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.copilot_enabled_for_language(language) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(path) = path {
|
||||
if !self.copilot_enabled_for_path(path) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn copilot_enabled_for_path(&self, path: &Path) -> bool {
|
||||
!self
|
||||
.copilot
|
||||
.disabled_globs
|
||||
.iter()
|
||||
.any(|glob| glob.matches_path(path))
|
||||
}
|
||||
|
||||
pub fn copilot_enabled_for_language(&self, language: Option<&str>) -> bool {
|
||||
self.language_setting(language, |settings| settings.show_copilot_suggestions)
|
||||
}
|
||||
|
||||
pub fn tab_size(&self, language: Option<&str>) -> NonZeroU32 {
|
||||
self.language_setting(language, |settings| settings.tab_size)
|
||||
}
|
||||
|
||||
pub fn show_whitespaces(&self, language: Option<&str>) -> ShowWhitespaces {
|
||||
self.language_setting(language, |settings| settings.show_whitespaces)
|
||||
}
|
||||
|
||||
pub fn hard_tabs(&self, language: Option<&str>) -> bool {
|
||||
self.language_setting(language, |settings| settings.hard_tabs)
|
||||
}
|
||||
|
||||
pub fn soft_wrap(&self, language: Option<&str>) -> SoftWrap {
|
||||
self.language_setting(language, |settings| settings.soft_wrap)
|
||||
}
|
||||
|
||||
pub fn preferred_line_length(&self, language: Option<&str>) -> u32 {
|
||||
self.language_setting(language, |settings| settings.preferred_line_length)
|
||||
}
|
||||
|
||||
pub fn remove_trailing_whitespace_on_save(&self, language: Option<&str>) -> bool {
|
||||
self.language_setting(language, |settings| {
|
||||
settings.remove_trailing_whitespace_on_save.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ensure_final_newline_on_save(&self, language: Option<&str>) -> bool {
|
||||
self.language_setting(language, |settings| {
|
||||
settings.ensure_final_newline_on_save.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn format_on_save(&self, language: Option<&str>) -> FormatOnSave {
|
||||
self.language_setting(language, |settings| settings.format_on_save.clone())
|
||||
}
|
||||
|
||||
pub fn formatter(&self, language: Option<&str>) -> Formatter {
|
||||
self.language_setting(language, |settings| settings.formatter.clone())
|
||||
}
|
||||
|
||||
pub fn enable_language_server(&self, language: Option<&str>) -> bool {
|
||||
self.language_setting(language, |settings| settings.enable_language_server)
|
||||
}
|
||||
|
||||
fn language_setting<F, R>(&self, language: Option<&str>, f: F) -> R
|
||||
where
|
||||
F: Fn(&EditorSettings) -> Option<R>,
|
||||
{
|
||||
None.or_else(|| language.and_then(|l| self.language_overrides.get(l).and_then(&f)))
|
||||
.or_else(|| f(&self.editor_overrides))
|
||||
.or_else(|| language.and_then(|l| self.language_defaults.get(l).and_then(&f)))
|
||||
.or_else(|| f(&self.editor_defaults))
|
||||
.expect("missing default")
|
||||
}
|
||||
|
||||
pub fn git_gutter(&self) -> GitGutter {
|
||||
self.git_overrides.git_gutter.unwrap_or_else(|| {
|
||||
self.git
|
||||
@ -706,29 +432,11 @@ impl Settings {
|
||||
show_call_status_icon: true,
|
||||
autosave: Autosave::Off,
|
||||
default_dock_anchor: DockAnchor::Bottom,
|
||||
editor_defaults: EditorSettings {
|
||||
tab_size: Some(4.try_into().unwrap()),
|
||||
hard_tabs: Some(false),
|
||||
soft_wrap: Some(SoftWrap::None),
|
||||
preferred_line_length: Some(80),
|
||||
remove_trailing_whitespace_on_save: Some(true),
|
||||
ensure_final_newline_on_save: Some(true),
|
||||
format_on_save: Some(FormatOnSave::On),
|
||||
formatter: Some(Formatter::LanguageServer),
|
||||
enable_language_server: Some(true),
|
||||
show_copilot_suggestions: Some(true),
|
||||
show_whitespaces: Some(ShowWhitespaces::None),
|
||||
},
|
||||
editor_overrides: Default::default(),
|
||||
copilot: Default::default(),
|
||||
git: Default::default(),
|
||||
git_overrides: Default::default(),
|
||||
language_defaults: Default::default(),
|
||||
language_overrides: Default::default(),
|
||||
lsp: Default::default(),
|
||||
theme: gpui::fonts::with_font_cache(cx.font_cache().clone(), Default::default),
|
||||
base_keymap: Default::default(),
|
||||
features: Features { copilot: true },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,12 +42,12 @@ pub fn test_settings() -> String {
|
||||
serde_json::json!({
|
||||
"buffer_font_family": "Courier",
|
||||
"buffer_font_features": {},
|
||||
"default_buffer_font_size": 14,
|
||||
"preferred_line_length": 80,
|
||||
"buffer_font_size": 14,
|
||||
"theme": theme::EMPTY_THEME_NAME,
|
||||
}),
|
||||
&mut value,
|
||||
);
|
||||
value.as_object_mut().unwrap().remove("languages");
|
||||
serde_json::to_string(&value).unwrap()
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::{btree_map, hash_map, BTreeMap, HashMap, HashSet};
|
||||
use anyhow::Result;
|
||||
use collections::{btree_map, hash_map, BTreeMap, HashMap};
|
||||
use gpui::AppContext;
|
||||
use lazy_static::lazy_static;
|
||||
use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
|
||||
@ -8,7 +8,6 @@ use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{type_name, Any, TypeId},
|
||||
fmt::Debug,
|
||||
mem,
|
||||
ops::Range,
|
||||
path::Path,
|
||||
str,
|
||||
@ -37,7 +36,9 @@ pub trait Setting: 'static {
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
cx: &AppContext,
|
||||
) -> Self;
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
fn json_schema(generator: &mut SchemaGenerator, _: &SettingsJsonSchemaParams) -> RootSchema {
|
||||
generator.root_schema_for::<Self::FileContent>()
|
||||
@ -57,7 +58,7 @@ pub trait Setting: 'static {
|
||||
fn load_via_json_merge(
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
) -> Self
|
||||
) -> Result<Self>
|
||||
where
|
||||
Self: DeserializeOwned,
|
||||
{
|
||||
@ -65,7 +66,11 @@ pub trait Setting: 'static {
|
||||
for value in [default_value].iter().chain(user_values) {
|
||||
merge_non_null_json_value_into(serde_json::to_value(value).unwrap(), &mut merged);
|
||||
}
|
||||
serde_json::from_value(merged).unwrap()
|
||||
Ok(serde_json::from_value(merged)?)
|
||||
}
|
||||
|
||||
fn missing_default() -> anyhow::Error {
|
||||
anyhow::anyhow!("missing default")
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,10 +83,9 @@ pub struct SettingsJsonSchemaParams<'a> {
|
||||
#[derive(Default)]
|
||||
pub struct SettingsStore {
|
||||
setting_values: HashMap<TypeId, Box<dyn AnySettingValue>>,
|
||||
default_deserialized_settings: Option<DeserializedSettingMap>,
|
||||
user_deserialized_settings: Option<DeserializedSettingMap>,
|
||||
local_deserialized_settings: BTreeMap<Arc<Path>, DeserializedSettingMap>,
|
||||
changed_setting_types: HashSet<TypeId>,
|
||||
default_deserialized_settings: Option<serde_json::Value>,
|
||||
user_deserialized_settings: Option<serde_json::Value>,
|
||||
local_deserialized_settings: BTreeMap<Arc<Path>, serde_json::Value>,
|
||||
tab_size_callback: Option<(TypeId, Box<dyn Fn(&dyn Any) -> Option<usize>>)>,
|
||||
}
|
||||
|
||||
@ -98,9 +102,9 @@ trait AnySettingValue {
|
||||
fn load_setting(
|
||||
&self,
|
||||
default_value: &DeserializedSetting,
|
||||
custom: &[&DeserializedSetting],
|
||||
custom: &[DeserializedSetting],
|
||||
cx: &AppContext,
|
||||
) -> Box<dyn Any>;
|
||||
) -> Result<Box<dyn Any>>;
|
||||
fn value_for_path(&self, path: Option<&Path>) -> &dyn Any;
|
||||
fn set_global_value(&mut self, value: Box<dyn Any>);
|
||||
fn set_local_value(&mut self, path: Arc<Path>, value: Box<dyn Any>);
|
||||
@ -113,11 +117,6 @@ trait AnySettingValue {
|
||||
|
||||
struct DeserializedSetting(Box<dyn Any>);
|
||||
|
||||
struct DeserializedSettingMap {
|
||||
untyped: serde_json::Value,
|
||||
typed: HashMap<TypeId, DeserializedSetting>,
|
||||
}
|
||||
|
||||
impl SettingsStore {
|
||||
/// Add a new type of setting to the store.
|
||||
pub fn register_setting<T: Setting>(&mut self, cx: &AppContext) {
|
||||
@ -132,23 +131,27 @@ impl SettingsStore {
|
||||
local_values: Vec::new(),
|
||||
}));
|
||||
|
||||
if let Some(default_settings) = self.default_deserialized_settings.as_mut() {
|
||||
Self::load_setting_in_map(setting_type_id, setting_value, default_settings);
|
||||
if let Some(default_settings) = &self.default_deserialized_settings {
|
||||
if let Some(default_settings) = setting_value
|
||||
.deserialize_setting(default_settings)
|
||||
.log_err()
|
||||
{
|
||||
let mut user_values_stack = Vec::new();
|
||||
|
||||
let mut user_values_stack = Vec::new();
|
||||
if let Some(user_settings) = self.user_deserialized_settings.as_mut() {
|
||||
Self::load_setting_in_map(setting_type_id, setting_value, user_settings);
|
||||
if let Some(user_value) = user_settings.typed.get(&setting_type_id) {
|
||||
user_values_stack = vec![user_value];
|
||||
if let Some(user_settings) = &self.user_deserialized_settings {
|
||||
if let Some(user_settings) =
|
||||
setting_value.deserialize_setting(user_settings).log_err()
|
||||
{
|
||||
user_values_stack = vec![user_settings];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(default_deserialized_value) = default_settings.typed.get(&setting_type_id) {
|
||||
setting_value.set_global_value(setting_value.load_setting(
|
||||
default_deserialized_value,
|
||||
&user_values_stack,
|
||||
cx,
|
||||
));
|
||||
if let Some(setting) = setting_value
|
||||
.load_setting(&default_settings, &user_values_stack, cx)
|
||||
.log_err()
|
||||
{
|
||||
setting_value.set_global_value(setting);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -173,7 +176,7 @@ impl SettingsStore {
|
||||
pub fn untyped_user_settings(&self) -> &serde_json::Value {
|
||||
self.user_deserialized_settings
|
||||
.as_ref()
|
||||
.map_or(&serde_json::Value::Null, |s| &s.untyped)
|
||||
.unwrap_or(&serde_json::Value::Null)
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
@ -181,6 +184,7 @@ impl SettingsStore {
|
||||
let mut this = Self::default();
|
||||
this.set_default_settings(&crate::test_settings(), cx)
|
||||
.unwrap();
|
||||
this.set_user_settings("{}", cx).unwrap();
|
||||
this
|
||||
}
|
||||
|
||||
@ -194,11 +198,11 @@ impl SettingsStore {
|
||||
cx: &AppContext,
|
||||
update: impl FnOnce(&mut T::FileContent),
|
||||
) {
|
||||
let old_text = if let Some(user_settings) = &self.user_deserialized_settings {
|
||||
serde_json::to_string(&user_settings.untyped).unwrap()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
if self.user_deserialized_settings.is_none() {
|
||||
self.set_user_settings("{}", cx).unwrap();
|
||||
}
|
||||
let old_text =
|
||||
serde_json::to_string(self.user_deserialized_settings.as_ref().unwrap()).unwrap();
|
||||
let new_text = self.new_text_for_update::<T>(old_text, update);
|
||||
self.set_user_settings(&new_text, cx).unwrap();
|
||||
}
|
||||
@ -212,7 +216,7 @@ impl SettingsStore {
|
||||
) -> String {
|
||||
let edits = self.edits_for_update::<T>(&old_text, update);
|
||||
let mut new_text = old_text;
|
||||
for (range, replacement) in edits.into_iter().rev() {
|
||||
for (range, replacement) in edits.into_iter() {
|
||||
new_text.replace_range(range, &replacement);
|
||||
}
|
||||
new_text
|
||||
@ -226,26 +230,31 @@ impl SettingsStore {
|
||||
update: impl FnOnce(&mut T::FileContent),
|
||||
) -> Vec<(Range<usize>, String)> {
|
||||
let setting_type_id = TypeId::of::<T>();
|
||||
|
||||
let old_content = self
|
||||
.user_deserialized_settings
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.typed
|
||||
.setting_values
|
||||
.get(&setting_type_id)
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| panic!("unregistered setting type {}", type_name::<T>()))
|
||||
.deserialize_setting(
|
||||
self.user_deserialized_settings
|
||||
.as_ref()
|
||||
.expect("no user settings loaded"),
|
||||
)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"could not deserialize setting type {} from user settings: {}",
|
||||
type_name::<T>(),
|
||||
e
|
||||
)
|
||||
})
|
||||
.0
|
||||
.downcast_ref::<T::FileContent>()
|
||||
.unwrap()
|
||||
.clone();
|
||||
.downcast::<T::FileContent>()
|
||||
.unwrap();
|
||||
let mut new_content = old_content.clone();
|
||||
update(&mut new_content);
|
||||
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser.set_language(tree_sitter_json::language()).unwrap();
|
||||
let tree = parser.parse(text, None).unwrap();
|
||||
|
||||
let old_value = &serde_json::to_value(old_content).unwrap();
|
||||
let new_value = &serde_json::to_value(new_content).unwrap();
|
||||
let old_value = &serde_json::to_value(&old_content).unwrap();
|
||||
let new_value = serde_json::to_value(new_content).unwrap();
|
||||
|
||||
let mut key_path = Vec::new();
|
||||
if let Some(key) = T::KEY {
|
||||
@ -254,16 +263,15 @@ impl SettingsStore {
|
||||
|
||||
let mut edits = Vec::new();
|
||||
let tab_size = self.json_tab_size();
|
||||
let mut text = text.to_string();
|
||||
update_value_in_json_text(
|
||||
&text,
|
||||
&tree,
|
||||
&mut text,
|
||||
&mut key_path,
|
||||
tab_size,
|
||||
&old_value,
|
||||
&new_value,
|
||||
&mut edits,
|
||||
);
|
||||
edits.sort_unstable_by_key(|e| e.0.start);
|
||||
return edits;
|
||||
}
|
||||
|
||||
@ -300,19 +308,8 @@ impl SettingsStore {
|
||||
default_settings_content: &str,
|
||||
cx: &AppContext,
|
||||
) -> Result<()> {
|
||||
let deserialized_setting_map = self.load_setting_map(default_settings_content)?;
|
||||
if deserialized_setting_map.typed.len() != self.setting_values.len() {
|
||||
return Err(anyhow!(
|
||||
"default settings file is missing fields: {:?}",
|
||||
self.setting_values
|
||||
.iter()
|
||||
.filter(|(type_id, _)| !deserialized_setting_map.typed.contains_key(type_id))
|
||||
.map(|(name, _)| *name)
|
||||
.collect::<Vec<_>>()
|
||||
));
|
||||
}
|
||||
self.default_deserialized_settings = Some(deserialized_setting_map);
|
||||
self.recompute_values(false, None, None, cx);
|
||||
self.default_deserialized_settings = Some(serde_json::from_str(default_settings_content)?);
|
||||
self.recompute_values(None, cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -322,10 +319,8 @@ impl SettingsStore {
|
||||
user_settings_content: &str,
|
||||
cx: &AppContext,
|
||||
) -> Result<()> {
|
||||
let user_settings = self.load_setting_map(user_settings_content)?;
|
||||
let old_user_settings =
|
||||
mem::replace(&mut self.user_deserialized_settings, Some(user_settings));
|
||||
self.recompute_values(true, None, old_user_settings, cx);
|
||||
self.user_deserialized_settings = Some(serde_json::from_str(user_settings_content)?);
|
||||
self.recompute_values(None, cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -336,14 +331,13 @@ impl SettingsStore {
|
||||
settings_content: Option<&str>,
|
||||
cx: &AppContext,
|
||||
) -> Result<()> {
|
||||
let removed_map = if let Some(settings_content) = settings_content {
|
||||
if let Some(content) = settings_content {
|
||||
self.local_deserialized_settings
|
||||
.insert(path.clone(), self.load_setting_map(settings_content)?);
|
||||
None
|
||||
.insert(path.clone(), serde_json::from_str(content)?);
|
||||
} else {
|
||||
self.local_deserialized_settings.remove(&path)
|
||||
};
|
||||
self.recompute_values(true, Some(&path), removed_map, cx);
|
||||
self.local_deserialized_settings.remove(&path);
|
||||
}
|
||||
self.recompute_values(Some(&path), cx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -422,136 +416,78 @@ impl SettingsStore {
|
||||
|
||||
fn recompute_values(
|
||||
&mut self,
|
||||
user_settings_changed: bool,
|
||||
changed_local_path: Option<&Path>,
|
||||
old_settings_map: Option<DeserializedSettingMap>,
|
||||
cx: &AppContext,
|
||||
) {
|
||||
// Identify all of the setting types that have changed.
|
||||
let new_settings_map = if let Some(changed_path) = changed_local_path {
|
||||
self.local_deserialized_settings.get(changed_path)
|
||||
} else if user_settings_changed {
|
||||
self.user_deserialized_settings.as_ref()
|
||||
} else {
|
||||
self.default_deserialized_settings.as_ref()
|
||||
};
|
||||
self.changed_setting_types.clear();
|
||||
for map in [old_settings_map.as_ref(), new_settings_map] {
|
||||
if let Some(map) = map {
|
||||
self.changed_setting_types.extend(map.typed.keys());
|
||||
}
|
||||
}
|
||||
) -> Result<()> {
|
||||
// Reload the global and local values for every setting.
|
||||
let mut user_settings_stack = Vec::<DeserializedSetting>::new();
|
||||
let mut paths_stack = Vec::<Option<&Path>>::new();
|
||||
for setting_value in self.setting_values.values_mut() {
|
||||
if let Some(default_settings) = &self.default_deserialized_settings {
|
||||
let default_settings = setting_value.deserialize_setting(default_settings)?;
|
||||
|
||||
// Reload the global and local values for every changed setting.
|
||||
let mut user_values_stack = Vec::<&DeserializedSetting>::new();
|
||||
for setting_type_id in self.changed_setting_types.iter() {
|
||||
let setting_value = self.setting_values.get_mut(setting_type_id).unwrap();
|
||||
user_settings_stack.clear();
|
||||
paths_stack.clear();
|
||||
|
||||
// Build the prioritized list of deserialized values to pass to the setting's
|
||||
// load function.
|
||||
user_values_stack.clear();
|
||||
if let Some(user_settings) = &self.user_deserialized_settings {
|
||||
if let Some(user_value) = user_settings.typed.get(setting_type_id) {
|
||||
user_values_stack.push(&user_value);
|
||||
}
|
||||
}
|
||||
|
||||
let default_deserialized_value = if let Some(value) = self
|
||||
.default_deserialized_settings
|
||||
.as_ref()
|
||||
.and_then(|map| map.typed.get(setting_type_id))
|
||||
{
|
||||
value
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// If the global settings file changed, reload the global value for the field.
|
||||
if changed_local_path.is_none() {
|
||||
setting_value.set_global_value(setting_value.load_setting(
|
||||
default_deserialized_value,
|
||||
&user_values_stack,
|
||||
cx,
|
||||
));
|
||||
}
|
||||
|
||||
// Reload the local values for the setting.
|
||||
let user_value_stack_len = user_values_stack.len();
|
||||
for (path, deserialized_values) in &self.local_deserialized_settings {
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
if changed_local_path.map_or(false, |changed_local_path| {
|
||||
!path.starts_with(changed_local_path)
|
||||
}) {
|
||||
continue;
|
||||
if let Some(user_settings) = &self.user_deserialized_settings {
|
||||
if let Some(user_settings) =
|
||||
setting_value.deserialize_setting(user_settings).log_err()
|
||||
{
|
||||
user_settings_stack.push(user_settings);
|
||||
paths_stack.push(None);
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore recomputing settings for any path that hasn't customized that setting.
|
||||
let Some(deserialized_value) = deserialized_values.typed.get(setting_type_id) else {
|
||||
continue;
|
||||
};
|
||||
// If the global settings file changed, reload the global value for the field.
|
||||
if changed_local_path.is_none() {
|
||||
setting_value.set_global_value(setting_value.load_setting(
|
||||
&default_settings,
|
||||
&user_settings_stack,
|
||||
cx,
|
||||
)?);
|
||||
}
|
||||
|
||||
// Build a stack of all of the local values for that setting.
|
||||
user_values_stack.truncate(user_value_stack_len);
|
||||
for (preceding_path, preceding_deserialized_values) in
|
||||
&self.local_deserialized_settings
|
||||
{
|
||||
if preceding_path >= path {
|
||||
// Reload the local values for the setting.
|
||||
for (path, local_settings) in &self.local_deserialized_settings {
|
||||
// Build a stack of all of the local values for that setting.
|
||||
while let Some(prev_path) = paths_stack.last() {
|
||||
if let Some(prev_path) = prev_path {
|
||||
if !path.starts_with(prev_path) {
|
||||
paths_stack.pop();
|
||||
user_settings_stack.pop();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if !path.starts_with(preceding_path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(preceding_deserialized_value) =
|
||||
preceding_deserialized_values.typed.get(setting_type_id)
|
||||
if let Some(local_settings) =
|
||||
setting_value.deserialize_setting(&local_settings).log_err()
|
||||
{
|
||||
user_values_stack.push(&*preceding_deserialized_value);
|
||||
paths_stack.push(Some(path.as_ref()));
|
||||
user_settings_stack.push(local_settings);
|
||||
|
||||
// If a local settings file changed, then avoid recomputing local
|
||||
// settings for any path outside of that directory.
|
||||
if changed_local_path.map_or(false, |changed_local_path| {
|
||||
!path.starts_with(changed_local_path)
|
||||
}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
setting_value.set_local_value(
|
||||
path.clone(),
|
||||
setting_value.load_setting(
|
||||
&default_settings,
|
||||
&user_settings_stack,
|
||||
cx,
|
||||
)?,
|
||||
);
|
||||
}
|
||||
}
|
||||
user_values_stack.push(&*deserialized_value);
|
||||
|
||||
// Load the local value for the field.
|
||||
setting_value.set_local_value(
|
||||
path.clone(),
|
||||
setting_value.load_setting(default_deserialized_value, &user_values_stack, cx),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserialize the given JSON string into a map keyed by setting type.
|
||||
///
|
||||
/// Returns an error if the string doesn't contain a valid JSON object.
|
||||
fn load_setting_map(&self, json: &str) -> Result<DeserializedSettingMap> {
|
||||
let mut map = DeserializedSettingMap {
|
||||
untyped: parse_json_with_comments(json)?,
|
||||
typed: HashMap::default(),
|
||||
};
|
||||
for (setting_type_id, setting_value) in self.setting_values.iter() {
|
||||
Self::load_setting_in_map(*setting_type_id, setting_value, &mut map);
|
||||
}
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
fn load_setting_in_map(
|
||||
setting_type_id: TypeId,
|
||||
setting_value: &Box<dyn AnySettingValue>,
|
||||
map: &mut DeserializedSettingMap,
|
||||
) {
|
||||
let value = if let Some(setting_key) = setting_value.key() {
|
||||
if let Some(value) = map.untyped.get(setting_key) {
|
||||
value
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
&map.untyped
|
||||
};
|
||||
|
||||
if let Some(deserialized_value) = setting_value.deserialize_setting(&value).log_err() {
|
||||
map.typed.insert(setting_type_id, deserialized_value);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,18 +503,21 @@ impl<T: Setting> AnySettingValue for SettingValue<T> {
|
||||
fn load_setting(
|
||||
&self,
|
||||
default_value: &DeserializedSetting,
|
||||
user_values: &[&DeserializedSetting],
|
||||
user_values: &[DeserializedSetting],
|
||||
cx: &AppContext,
|
||||
) -> Box<dyn Any> {
|
||||
) -> Result<Box<dyn Any>> {
|
||||
let default_value = default_value.0.downcast_ref::<T::FileContent>().unwrap();
|
||||
let values: SmallVec<[&T::FileContent; 6]> = user_values
|
||||
.iter()
|
||||
.map(|value| value.0.downcast_ref().unwrap())
|
||||
.collect();
|
||||
Box::new(T::load(default_value, &values, cx))
|
||||
Ok(Box::new(T::load(default_value, &values, cx)?))
|
||||
}
|
||||
|
||||
fn deserialize_setting(&self, json: &serde_json::Value) -> Result<DeserializedSetting> {
|
||||
fn deserialize_setting(&self, mut json: &serde_json::Value) -> Result<DeserializedSetting> {
|
||||
if let Some(key) = T::KEY {
|
||||
json = json.get(key).unwrap_or(&serde_json::Value::Null);
|
||||
}
|
||||
let value = T::FileContent::deserialize(json)?;
|
||||
Ok(DeserializedSetting(Box::new(value)))
|
||||
}
|
||||
@ -593,7 +532,7 @@ impl<T: Setting> AnySettingValue for SettingValue<T> {
|
||||
}
|
||||
self.global_value
|
||||
.as_ref()
|
||||
.expect("no default value for setting")
|
||||
.unwrap_or_else(|| panic!("no default value for setting {}", self.setting_type_name()))
|
||||
}
|
||||
|
||||
fn set_global_value(&mut self, value: Box<dyn Any>) {
|
||||
@ -634,8 +573,7 @@ impl<T: Setting> AnySettingValue for SettingValue<T> {
|
||||
// }
|
||||
|
||||
fn update_value_in_json_text<'a>(
|
||||
text: &str,
|
||||
syntax_tree: &tree_sitter::Tree,
|
||||
text: &mut String,
|
||||
key_path: &mut Vec<&'a str>,
|
||||
tab_size: usize,
|
||||
old_value: &'a serde_json::Value,
|
||||
@ -653,7 +591,6 @@ fn update_value_in_json_text<'a>(
|
||||
let new_sub_value = new_object.get(key).unwrap_or(&serde_json::Value::Null);
|
||||
update_value_in_json_text(
|
||||
text,
|
||||
syntax_tree,
|
||||
key_path,
|
||||
tab_size,
|
||||
old_sub_value,
|
||||
@ -667,7 +604,6 @@ fn update_value_in_json_text<'a>(
|
||||
if !old_object.contains_key(key) {
|
||||
update_value_in_json_text(
|
||||
text,
|
||||
syntax_tree,
|
||||
key_path,
|
||||
tab_size,
|
||||
&serde_json::Value::Null,
|
||||
@ -679,14 +615,14 @@ fn update_value_in_json_text<'a>(
|
||||
}
|
||||
} else if old_value != new_value {
|
||||
let (range, replacement) =
|
||||
replace_value_in_json_text(text, syntax_tree, &key_path, tab_size, &new_value);
|
||||
replace_value_in_json_text(text, &key_path, tab_size, &new_value);
|
||||
text.replace_range(range.clone(), &replacement);
|
||||
edits.push((range, replacement));
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_value_in_json_text(
|
||||
text: &str,
|
||||
syntax_tree: &tree_sitter::Tree,
|
||||
key_path: &[&str],
|
||||
tab_size: usize,
|
||||
new_value: impl Serialize,
|
||||
@ -702,6 +638,10 @@ fn replace_value_in_json_text(
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let mut parser = tree_sitter::Parser::new();
|
||||
parser.set_language(tree_sitter_json::language()).unwrap();
|
||||
let syntax_tree = parser.parse(text, None).unwrap();
|
||||
|
||||
let mut cursor = tree_sitter::QueryCursor::new();
|
||||
|
||||
let has_language_overrides = text.contains(LANGUAGE_OVERRIDES);
|
||||
@ -1152,7 +1092,7 @@ mod tests {
|
||||
store.set_user_settings(&old_json, cx).ok();
|
||||
let edits = store.edits_for_update::<T>(&old_json, update);
|
||||
let mut new_json = old_json;
|
||||
for (range, replacement) in edits.into_iter().rev() {
|
||||
for (range, replacement) in edits.into_iter() {
|
||||
new_json.replace_range(range, &replacement);
|
||||
}
|
||||
pretty_assertions::assert_eq!(new_json, expected_new_json);
|
||||
@ -1180,7 +1120,7 @@ mod tests {
|
||||
default_value: &UserSettingsJson,
|
||||
user_values: &[&UserSettingsJson],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
@ -1196,7 +1136,7 @@ mod tests {
|
||||
default_value: &Option<bool>,
|
||||
user_values: &[&Option<bool>],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
@ -1224,7 +1164,7 @@ mod tests {
|
||||
default_value: &MultiKeySettingsJson,
|
||||
user_values: &[&MultiKeySettingsJson],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
@ -1257,7 +1197,7 @@ mod tests {
|
||||
default_value: &JournalSettingsJson,
|
||||
user_values: &[&JournalSettingsJson],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
@ -1278,7 +1218,7 @@ mod tests {
|
||||
|
||||
type FileContent = Self;
|
||||
|
||||
fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Self {
|
||||
fn load(default_value: &Self, user_values: &[&Self], _: &AppContext) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ impl settings::Setting for TerminalSettings {
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
) -> Result<Self> {
|
||||
Self::load_via_json_merge(default_value, user_values)
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ doctest = false
|
||||
neovim = ["nvim-rs", "async-compat", "async-trait", "tokio"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
itertools = "0.10"
|
||||
|
@ -17,14 +17,16 @@ pub struct VimTestContext<'a> {
|
||||
impl<'a> VimTestContext<'a> {
|
||||
pub async fn new(cx: &'a mut gpui::TestAppContext, enabled: bool) -> VimTestContext<'a> {
|
||||
let mut cx = EditorLspTestContext::new_rust(Default::default(), cx).await;
|
||||
|
||||
cx.update(|cx| {
|
||||
search::init(cx);
|
||||
crate::init(cx);
|
||||
});
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
|
||||
});
|
||||
|
||||
settings::KeymapFileContent::load("keymaps/vim.json", cx).unwrap();
|
||||
});
|
||||
|
||||
|
@ -10,8 +10,7 @@ mod state;
|
||||
mod utils;
|
||||
mod visual;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::Result;
|
||||
use collections::CommandPaletteFilter;
|
||||
use editor::{Bias, Cancel, Editor, EditorMode, Event};
|
||||
use gpui::{
|
||||
@ -24,6 +23,7 @@ use normal::normal_replace;
|
||||
use serde::Deserialize;
|
||||
use settings::{Setting, SettingsStore};
|
||||
use state::{Mode, Operator, VimState};
|
||||
use std::sync::Arc;
|
||||
use visual::visual_replace;
|
||||
use workspace::{self, Workspace};
|
||||
|
||||
@ -343,14 +343,10 @@ impl Setting for VimModeSetting {
|
||||
default_value: &Self::FileContent,
|
||||
user_values: &[&Self::FileContent],
|
||||
_: &AppContext,
|
||||
) -> Self {
|
||||
Self(
|
||||
user_values
|
||||
.first()
|
||||
.map(|e| **e)
|
||||
.flatten()
|
||||
.unwrap_or(default_value.unwrap()),
|
||||
)
|
||||
) -> Result<Self> {
|
||||
Ok(Self(user_values.iter().rev().find_map(|v| **v).unwrap_or(
|
||||
default_value.ok_or_else(Self::missing_default)?,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,8 +369,12 @@ pub struct AppState {
|
||||
impl AppState {
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn test(cx: &mut AppContext) -> Arc<Self> {
|
||||
cx.set_global(settings::SettingsStore::test(cx));
|
||||
cx.set_global(Settings::test(cx));
|
||||
use settings::SettingsStore;
|
||||
|
||||
if !cx.has_global::<SettingsStore>() {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
cx.set_global(Settings::test(cx));
|
||||
}
|
||||
|
||||
let fs = fs::FakeFs::new(cx.background().clone());
|
||||
let languages = Arc::new(LanguageRegistry::test());
|
||||
|
@ -249,16 +249,21 @@ impl super::LspAdapter for CLspAdapter {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::TestAppContext;
|
||||
use language::{AutoindentMode, Buffer};
|
||||
use settings::Settings;
|
||||
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||
use settings::SettingsStore;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_c_autoindent(cx: &mut TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
let language = crate::languages::language("c", tree_sitter_c::language(), None).await;
|
||||
|
||||
|
@ -170,8 +170,9 @@ impl LspAdapter for PythonLspAdapter {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{ModelContext, TestAppContext};
|
||||
use language::{AutoindentMode, Buffer};
|
||||
use settings::Settings;
|
||||
use language::{language_settings::AllLanguageSettings, AutoindentMode, Buffer};
|
||||
use settings::SettingsStore;
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_python_autoindent(cx: &mut TestAppContext) {
|
||||
@ -179,9 +180,13 @@ mod tests {
|
||||
let language =
|
||||
crate::languages::language("python", tree_sitter_python::language(), None).await;
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
cx.add_model(|cx| {
|
||||
|
@ -253,10 +253,13 @@ impl LspAdapter for RustLspAdapter {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use super::*;
|
||||
use crate::languages::language;
|
||||
use gpui::{color::Color, TestAppContext};
|
||||
use settings::Settings;
|
||||
use language::language_settings::AllLanguageSettings;
|
||||
use settings::SettingsStore;
|
||||
use theme::SyntaxTheme;
|
||||
|
||||
#[gpui::test]
|
||||
@ -435,9 +438,13 @@ mod tests {
|
||||
async fn test_rust_autoindent(cx: &mut TestAppContext) {
|
||||
cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
|
||||
cx.update(|cx| {
|
||||
let mut settings = Settings::test(cx);
|
||||
settings.editor_overrides.tab_size = Some(2.try_into().unwrap());
|
||||
cx.set_global(settings);
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
language::init(cx);
|
||||
cx.update_global::<SettingsStore, _, _>(|store, cx| {
|
||||
store.update_user_settings::<AllLanguageSettings>(cx, |s| {
|
||||
s.defaults.tab_size = NonZeroU32::new(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await;
|
||||
|
@ -2,10 +2,11 @@ use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::{future::BoxFuture, FutureExt, StreamExt};
|
||||
use gpui::AppContext;
|
||||
use language::{LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use language::{
|
||||
language_settings::language_settings, LanguageServerBinary, LanguageServerName, LspAdapter,
|
||||
};
|
||||
use node_runtime::NodeRuntime;
|
||||
use serde_json::Value;
|
||||
use settings::Settings;
|
||||
use smol::fs;
|
||||
use std::{
|
||||
any::Any,
|
||||
@ -100,14 +101,13 @@ impl LspAdapter for YamlLspAdapter {
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, cx: &mut AppContext) -> Option<BoxFuture<'static, Value>> {
|
||||
let settings = cx.global::<Settings>();
|
||||
Some(
|
||||
future::ready(serde_json::json!({
|
||||
"yaml": {
|
||||
"keyOrdering": false
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.tabSize": settings.tab_size(Some("YAML"))
|
||||
"editor.tabSize": language_settings(None, Some("YAML"), cx).tab_size,
|
||||
}
|
||||
}))
|
||||
.boxed(),
|
||||
|
@ -597,7 +597,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_open_paths_action(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -697,7 +697,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_window_edit_state(executor: Arc<Deterministic>, cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -777,7 +777,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_new_empty_workspace(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
cx.update(|cx| {
|
||||
open_new(&app_state, cx, |workspace, cx| {
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
@ -816,7 +816,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_open_entry(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -929,7 +929,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_open_paths(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
|
||||
app_state
|
||||
.fs
|
||||
@ -1099,7 +1099,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_save_conflicting_item(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -1143,7 +1143,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_open_and_save_new_file(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state.fs.create_dir(Path::new("/root")).await.unwrap();
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await;
|
||||
@ -1232,7 +1232,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_setting_language_when_saving_as_single_file_worktree(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state.fs.create_dir(Path::new("/root")).await.unwrap();
|
||||
|
||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||
@ -1271,7 +1271,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_pane_actions(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -1345,7 +1345,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_navigation(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -1622,7 +1622,7 @@ mod tests {
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_reopening_closed_items(cx: &mut TestAppContext) {
|
||||
let app_state = init(cx);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
@ -1843,7 +1843,7 @@ mod tests {
|
||||
cx.foreground().run_until_parked();
|
||||
}
|
||||
|
||||
fn init(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||
fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||
cx.foreground().forbid_parking();
|
||||
cx.update(|cx| {
|
||||
let mut app_state = AppState::test(cx);
|
||||
@ -1852,6 +1852,7 @@ mod tests {
|
||||
state.build_window_options = build_window_options;
|
||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
pane::init(cx);
|
||||
app_state
|
||||
|
Loading…
Reference in New Issue
Block a user