Merge branch 'main' into panels
This commit is contained in:
commit
146809eef0
183 changed files with 10202 additions and 5720 deletions
|
@ -15,7 +15,7 @@ use anyhow::anyhow;
|
|||
use feedback::{
|
||||
feedback_info_text::FeedbackInfoText, submit_feedback_button::SubmitFeedbackButton,
|
||||
};
|
||||
use futures::StreamExt;
|
||||
use futures::{channel::mpsc, StreamExt};
|
||||
use gpui::{
|
||||
actions,
|
||||
anyhow::{self, Result},
|
||||
|
@ -30,15 +30,16 @@ use project_panel::ProjectPanel;
|
|||
use search::{BufferSearchBar, ProjectSearchBar};
|
||||
use serde::Deserialize;
|
||||
use serde_json::to_string_pretty;
|
||||
use settings::{Settings, DEFAULT_SETTINGS_ASSET_PATH};
|
||||
use settings::{KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH};
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use terminal_view::terminal_panel::{self, TerminalPanel};
|
||||
use util::{channel::ReleaseChannel, paths, ResultExt};
|
||||
use uuid::Uuid;
|
||||
use welcome::BaseKeymap;
|
||||
pub use workspace;
|
||||
use workspace::{
|
||||
create_and_open_local_file, dock::PanelHandle, open_new, AppState, NewFile, NewWindow,
|
||||
Workspace,
|
||||
Workspace, WorkspaceSettings,
|
||||
};
|
||||
|
||||
#[derive(Deserialize, Clone, PartialEq)]
|
||||
|
@ -73,8 +74,6 @@ actions!(
|
|||
]
|
||||
);
|
||||
|
||||
const MIN_FONT_SIZE: f32 = 6.0;
|
||||
|
||||
pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
||||
cx.add_action(about);
|
||||
cx.add_global_action(|_: &Hide, cx: &mut gpui::AppContext| {
|
||||
|
@ -118,30 +117,12 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
cx.add_global_action(quit);
|
||||
cx.add_global_action(move |action: &OpenBrowser, cx| cx.platform().open_url(&action.url));
|
||||
cx.add_global_action(move |_: &IncreaseBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size = (settings.buffer_font_size + 1.0).max(MIN_FONT_SIZE);
|
||||
if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() {
|
||||
*terminal_font_size = (*terminal_font_size + 1.0).max(MIN_FONT_SIZE);
|
||||
}
|
||||
cx.refresh_windows();
|
||||
});
|
||||
theme::adjust_font_size(cx, |size| *size += 1.0)
|
||||
});
|
||||
cx.add_global_action(move |_: &DecreaseBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size = (settings.buffer_font_size - 1.0).max(MIN_FONT_SIZE);
|
||||
if let Some(terminal_font_size) = settings.terminal_overrides.font_size.as_mut() {
|
||||
*terminal_font_size = (*terminal_font_size - 1.0).max(MIN_FONT_SIZE);
|
||||
}
|
||||
cx.refresh_windows();
|
||||
});
|
||||
});
|
||||
cx.add_global_action(move |_: &ResetBufferFontSize, cx| {
|
||||
cx.update_global::<Settings, _, _>(|settings, cx| {
|
||||
settings.buffer_font_size = settings.default_buffer_font_size;
|
||||
settings.terminal_overrides.font_size = settings.terminal_defaults.font_size;
|
||||
cx.refresh_windows();
|
||||
});
|
||||
theme::adjust_font_size(cx, |size| *size -= 1.0)
|
||||
});
|
||||
cx.add_global_action(move |_: &ResetBufferFontSize, cx| theme::reset_font_size(cx));
|
||||
cx.add_global_action(move |_: &install_cli::Install, cx| {
|
||||
cx.spawn(|cx| async move {
|
||||
install_cli::install_cli(&cx)
|
||||
|
@ -275,10 +256,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||
}
|
||||
}
|
||||
});
|
||||
activity_indicator::init(cx);
|
||||
lsp_log::init(cx);
|
||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||
settings::KeymapFileContent::load_defaults(cx);
|
||||
load_default_keymap(cx);
|
||||
}
|
||||
|
||||
pub fn initialize_workspace(
|
||||
|
@ -323,7 +301,8 @@ pub fn initialize_workspace(
|
|||
cx.add_view(|cx| CollabTitlebarItem::new(workspace, &workspace_handle, cx));
|
||||
workspace.set_titlebar_item(collab_titlebar_item.into_any(), cx);
|
||||
|
||||
let copilot = cx.add_view(|cx| copilot_button::CopilotButton::new(cx));
|
||||
let copilot =
|
||||
cx.add_view(|cx| copilot_button::CopilotButton::new(app_state.fs.clone(), cx));
|
||||
let diagnostic_summary =
|
||||
cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace, cx));
|
||||
let activity_indicator = activity_indicator::ActivityIndicator::new(
|
||||
|
@ -408,7 +387,7 @@ pub fn build_window_options(
|
|||
}
|
||||
|
||||
fn quit(_: &Quit, cx: &mut gpui::AppContext) {
|
||||
let should_confirm = cx.global::<Settings>().confirm_quit;
|
||||
let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
|
||||
cx.spawn(|mut cx| async move {
|
||||
let mut workspaces = cx
|
||||
.window_ids()
|
||||
|
@ -519,6 +498,51 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
|||
.detach();
|
||||
}
|
||||
|
||||
pub fn load_default_keymap(cx: &mut AppContext) {
|
||||
for path in ["keymaps/default.json", "keymaps/vim.json"] {
|
||||
KeymapFileContent::load_asset(path, cx).unwrap();
|
||||
}
|
||||
|
||||
if let Some(asset_path) = settings::get::<BaseKeymap>(cx).asset_path() {
|
||||
KeymapFileContent::load_asset(asset_path, cx).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_keymap_file_changes(
|
||||
mut user_keymap_file_rx: mpsc::UnboundedReceiver<String>,
|
||||
cx: &mut AppContext,
|
||||
) {
|
||||
cx.spawn(move |mut cx| async move {
|
||||
let mut settings_subscription = None;
|
||||
while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
|
||||
if let Ok(keymap_content) = KeymapFileContent::parse(&user_keymap_content) {
|
||||
cx.update(|cx| {
|
||||
cx.clear_bindings();
|
||||
load_default_keymap(cx);
|
||||
keymap_content.clone().add_to_cx(cx).log_err();
|
||||
});
|
||||
|
||||
let mut old_base_keymap = cx.read(|cx| *settings::get::<BaseKeymap>(cx));
|
||||
drop(settings_subscription);
|
||||
settings_subscription = Some(cx.update(|cx| {
|
||||
cx.observe_global::<SettingsStore, _>(move |cx| {
|
||||
let new_base_keymap = *settings::get::<BaseKeymap>(cx);
|
||||
if new_base_keymap != old_base_keymap {
|
||||
old_base_keymap = new_base_keymap.clone();
|
||||
|
||||
cx.clear_bindings();
|
||||
load_default_keymap(cx);
|
||||
keymap_content.clone().add_to_cx(cx).log_err();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}));
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
|
||||
workspace.with_local_workspace(cx, move |workspace, cx| {
|
||||
let app_state = workspace.app_state().clone();
|
||||
|
@ -620,16 +644,21 @@ mod tests {
|
|||
use super::*;
|
||||
use assets::Assets;
|
||||
use editor::{scroll::autoscroll::Autoscroll, DisplayPoint, Editor};
|
||||
use gpui::{executor::Deterministic, AppContext, AssetSource, TestAppContext, ViewHandle};
|
||||
use fs::{FakeFs, Fs};
|
||||
use gpui::{
|
||||
elements::Empty, executor::Deterministic, Action, AnyElement, AppContext, AssetSource,
|
||||
Element, Entity, TestAppContext, View, ViewHandle,
|
||||
};
|
||||
use language::LanguageRegistry;
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::{Project, ProjectPath};
|
||||
use serde_json::json;
|
||||
use settings::{handle_settings_file_changes, watch_config_file, SettingsStore};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use theme::{ThemeRegistry, ThemeSettings};
|
||||
use util::http::FakeHttpClient;
|
||||
use workspace::{
|
||||
item::{Item, ItemHandle},
|
||||
|
@ -638,7 +667,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()
|
||||
|
@ -738,7 +767,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()
|
||||
|
@ -819,7 +848,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)
|
||||
|
@ -858,7 +887,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()
|
||||
|
@ -971,7 +1000,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
|
||||
|
@ -1141,7 +1170,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()
|
||||
|
@ -1185,7 +1214,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;
|
||||
|
@ -1274,7 +1303,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;
|
||||
|
@ -1313,9 +1342,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_pane_actions(cx: &mut TestAppContext) {
|
||||
init(cx);
|
||||
|
||||
let app_state = cx.update(AppState::test);
|
||||
let app_state = init_test(cx);
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
|
@ -1389,7 +1416,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()
|
||||
|
@ -1665,7 +1692,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()
|
||||
|
@ -1828,6 +1855,175 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_base_keymap(cx: &mut gpui::TestAppContext) {
|
||||
struct TestView;
|
||||
|
||||
impl Entity for TestView {
|
||||
type Event = ();
|
||||
}
|
||||
|
||||
impl View for TestView {
|
||||
fn ui_name() -> &'static str {
|
||||
"TestView"
|
||||
}
|
||||
|
||||
fn render(&mut self, _: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
|
||||
let executor = cx.background();
|
||||
let fs = FakeFs::new(executor.clone());
|
||||
|
||||
actions!(test, [A, B]);
|
||||
// From the Atom keymap
|
||||
actions!(workspace, [ActivatePreviousPane]);
|
||||
// From the JetBrains keymap
|
||||
actions!(pane, [ActivatePrevItem]);
|
||||
|
||||
fs.save(
|
||||
"/settings.json".as_ref(),
|
||||
&r#"
|
||||
{
|
||||
"base_keymap": "Atom"
|
||||
}
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
fs.save(
|
||||
"/keymap.json".as_ref(),
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"backspace": "test::A"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.update(|cx| {
|
||||
cx.set_global(SettingsStore::test(cx));
|
||||
theme::init(Assets, cx);
|
||||
welcome::init(cx);
|
||||
|
||||
cx.add_global_action(|_: &A, _cx| {});
|
||||
cx.add_global_action(|_: &B, _cx| {});
|
||||
cx.add_global_action(|_: &ActivatePreviousPane, _cx| {});
|
||||
cx.add_global_action(|_: &ActivatePrevItem, _cx| {});
|
||||
|
||||
let settings_rx = watch_config_file(
|
||||
executor.clone(),
|
||||
fs.clone(),
|
||||
PathBuf::from("/settings.json"),
|
||||
);
|
||||
let keymap_rx =
|
||||
watch_config_file(executor.clone(), fs.clone(), PathBuf::from("/keymap.json"));
|
||||
|
||||
handle_keymap_file_changes(keymap_rx, cx);
|
||||
handle_settings_file_changes(settings_rx, cx);
|
||||
});
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
let (window_id, _view) = cx.add_window(|_| TestView);
|
||||
|
||||
// Test loading the keymap base at all
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the users keymap, while retaining the base keymap
|
||||
fs.save(
|
||||
"/keymap.json".as_ref(),
|
||||
&r#"
|
||||
[
|
||||
{
|
||||
"bindings": {
|
||||
"backspace": "test::B"
|
||||
}
|
||||
}
|
||||
]
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
// Test modifying the base, while retaining the users keymap
|
||||
fs.save(
|
||||
"/settings.json".as_ref(),
|
||||
&r#"
|
||||
{
|
||||
"base_keymap": "JetBrains"
|
||||
}
|
||||
"#
|
||||
.into(),
|
||||
Default::default(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
cx.foreground().run_until_parked();
|
||||
|
||||
assert_key_bindings_for(
|
||||
window_id,
|
||||
cx,
|
||||
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
||||
line!(),
|
||||
);
|
||||
|
||||
fn assert_key_bindings_for<'a>(
|
||||
window_id: usize,
|
||||
cx: &TestAppContext,
|
||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||
line: u32,
|
||||
) {
|
||||
for (key, action) in actions {
|
||||
// assert that...
|
||||
assert!(
|
||||
cx.available_actions(window_id, 0)
|
||||
.into_iter()
|
||||
.any(|(_, bound_action, b)| {
|
||||
// action names match...
|
||||
bound_action.name() == action.name()
|
||||
&& bound_action.namespace() == action.namespace()
|
||||
// and key strokes contain the given key
|
||||
&& b.iter()
|
||||
.any(|binding| binding.keystrokes().iter().any(|k| k.key == key))
|
||||
}),
|
||||
"On {} Failed to find {} with key binding {}",
|
||||
line,
|
||||
action.name(),
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_bundled_settings_and_themes(cx: &mut AppContext) {
|
||||
cx.platform()
|
||||
|
@ -1846,15 +2042,20 @@ mod tests {
|
|||
])
|
||||
.unwrap();
|
||||
let themes = ThemeRegistry::new(Assets, cx.font_cache().clone());
|
||||
let settings = Settings::defaults(Assets, cx.font_cache(), &themes);
|
||||
let mut settings = SettingsStore::default();
|
||||
settings
|
||||
.set_default_settings(&settings::default_settings(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(settings);
|
||||
theme::init(Assets, cx);
|
||||
|
||||
let mut has_default_theme = false;
|
||||
for theme_name in themes.list(false).map(|meta| meta.name) {
|
||||
let theme = themes.get(&theme_name).unwrap();
|
||||
if theme.meta.name == settings.theme.meta.name {
|
||||
assert_eq!(theme.meta.name, theme_name);
|
||||
if theme.meta.name == settings::get::<ThemeSettings>(cx).theme.meta.name {
|
||||
has_default_theme = true;
|
||||
}
|
||||
assert_eq!(theme.meta.name, theme_name);
|
||||
}
|
||||
assert!(has_default_theme);
|
||||
}
|
||||
|
@ -1864,25 +2065,26 @@ mod tests {
|
|||
let mut languages = LanguageRegistry::test();
|
||||
languages.set_executor(cx.background().clone());
|
||||
let languages = Arc::new(languages);
|
||||
let themes = ThemeRegistry::new((), cx.font_cache().clone());
|
||||
let http = FakeHttpClient::with_404_response();
|
||||
let node_runtime = NodeRuntime::new(http, cx.background().to_owned());
|
||||
languages::init(languages.clone(), themes, node_runtime);
|
||||
languages::init(languages.clone(), node_runtime);
|
||||
for name in languages.language_names() {
|
||||
languages.language_for_name(&name);
|
||||
}
|
||||
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);
|
||||
let state = Arc::get_mut(&mut app_state).unwrap();
|
||||
state.initialize_workspace = initialize_workspace;
|
||||
state.build_window_options = build_window_options;
|
||||
theme::init((), cx);
|
||||
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…
Add table
Add a link
Reference in a new issue