Merge branch 'main' into panels
This commit is contained in:
commit
146809eef0
183 changed files with 10202 additions and 5720 deletions
|
@ -3,7 +3,6 @@ pub use language::*;
|
|||
use node_runtime::NodeRuntime;
|
||||
use rust_embed::RustEmbed;
|
||||
use std::{borrow::Cow, str, sync::Arc};
|
||||
use theme::ThemeRegistry;
|
||||
|
||||
mod c;
|
||||
mod elixir;
|
||||
|
@ -32,11 +31,7 @@ mod yaml;
|
|||
#[exclude = "*.rs"]
|
||||
struct LanguageDir;
|
||||
|
||||
pub fn init(
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
node_runtime: Arc<NodeRuntime>,
|
||||
) {
|
||||
pub fn init(languages: Arc<LanguageRegistry>, node_runtime: Arc<NodeRuntime>) {
|
||||
fn adapter_arc(adapter: impl LspAdapter) -> Arc<dyn LspAdapter> {
|
||||
Arc::new(adapter)
|
||||
}
|
||||
|
@ -69,7 +64,6 @@ pub fn init(
|
|||
vec![adapter_arc(json::JsonLspAdapter::new(
|
||||
node_runtime.clone(),
|
||||
languages.clone(),
|
||||
themes.clone(),
|
||||
))],
|
||||
),
|
||||
("markdown", tree_sitter_markdown::language(), vec![]),
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use gpui::AppContext;
|
|||
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter};
|
||||
use node_runtime::NodeRuntime;
|
||||
use serde_json::json;
|
||||
use settings::{keymap_file_json_schema, settings_file_json_schema};
|
||||
use settings::{keymap_file_json_schema, SettingsJsonSchemaParams, SettingsStore};
|
||||
use smol::fs;
|
||||
use staff_mode::StaffMode;
|
||||
use std::{
|
||||
|
@ -16,7 +16,6 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use theme::ThemeRegistry;
|
||||
use util::http::HttpClient;
|
||||
use util::{paths, ResultExt};
|
||||
|
||||
|
@ -30,20 +29,11 @@ fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
|||
pub struct JsonLspAdapter {
|
||||
node: Arc<NodeRuntime>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
}
|
||||
|
||||
impl JsonLspAdapter {
|
||||
pub fn new(
|
||||
node: Arc<NodeRuntime>,
|
||||
languages: Arc<LanguageRegistry>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
) -> Self {
|
||||
JsonLspAdapter {
|
||||
node,
|
||||
languages,
|
||||
themes,
|
||||
}
|
||||
pub fn new(node: Arc<NodeRuntime>, languages: Arc<LanguageRegistry>) -> Self {
|
||||
JsonLspAdapter { node, languages }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,12 +118,15 @@ impl LspAdapter for JsonLspAdapter {
|
|||
cx: &mut AppContext,
|
||||
) -> Option<BoxFuture<'static, serde_json::Value>> {
|
||||
let action_names = cx.all_action_names().collect::<Vec<_>>();
|
||||
let theme_names = self
|
||||
.themes
|
||||
.list(**cx.default_global::<StaffMode>())
|
||||
.map(|meta| meta.name)
|
||||
.collect();
|
||||
let language_names = self.languages.language_names();
|
||||
let staff_mode = cx.default_global::<StaffMode>().0;
|
||||
let language_names = &self.languages.language_names();
|
||||
let settings_schema = cx.global::<SettingsStore>().json_schema(
|
||||
&SettingsJsonSchemaParams {
|
||||
language_names,
|
||||
staff_mode,
|
||||
},
|
||||
cx,
|
||||
);
|
||||
Some(
|
||||
future::ready(serde_json::json!({
|
||||
"json": {
|
||||
|
@ -143,7 +136,7 @@ impl LspAdapter for JsonLspAdapter {
|
|||
"schemas": [
|
||||
{
|
||||
"fileMatch": [schema_file_match(&paths::SETTINGS)],
|
||||
"schema": settings_file_json_schema(theme_names, &language_names),
|
||||
"schema": settings_schema,
|
||||
},
|
||||
{
|
||||
"fileMatch": [schema_file_match(&paths::KEYMAP)],
|
||||
|
|
|
@ -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(Some("YAML"), cx).tab_size,
|
||||
}
|
||||
}))
|
||||
.boxed(),
|
||||
|
|
|
@ -6,55 +6,61 @@ use assets::Assets;
|
|||
use backtrace::Backtrace;
|
||||
use cli::{
|
||||
ipc::{self, IpcSender},
|
||||
CliRequest, CliResponse, IpcHandshake,
|
||||
CliRequest, CliResponse, IpcHandshake, FORCE_CLI_MODE_ENV_VAR_NAME,
|
||||
};
|
||||
use client::{self, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
||||
use client::{self, TelemetrySettings, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN};
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use editor::{scroll::autoscroll::Autoscroll, Editor};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
FutureExt, SinkExt, StreamExt,
|
||||
};
|
||||
use gpui::{Action, App, AppContext, AssetSource, AsyncAppContext, Task, ViewContext};
|
||||
use isahc::{config::Configurable, Request};
|
||||
use language::LanguageRegistry;
|
||||
use language::{LanguageRegistry, Point};
|
||||
use log::LevelFilter;
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use project::Fs;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use settings::{
|
||||
self, settings_file::SettingsFile, KeymapFileContent, Settings, SettingsFileContent,
|
||||
WorkingDirectory,
|
||||
};
|
||||
use settings::{default_settings, handle_settings_file_changes, watch_config_file, SettingsStore};
|
||||
use simplelog::ConfigBuilder;
|
||||
use smol::process::Command;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env,
|
||||
ffi::OsStr,
|
||||
fs::OpenOptions,
|
||||
io::Write as _,
|
||||
os::unix::prelude::OsStrExt,
|
||||
panic,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Weak},
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc, Weak,
|
||||
},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
use terminal_view::{get_working_directory, TerminalView};
|
||||
use util::http::{self, HttpClient};
|
||||
use sum_tree::Bias;
|
||||
use terminal_view::{get_working_directory, TerminalSettings, TerminalView};
|
||||
use util::{
|
||||
http::{self, HttpClient},
|
||||
paths::PathLikeWithPosition,
|
||||
};
|
||||
use welcome::{show_welcome_experience, FIRST_OPEN};
|
||||
|
||||
use fs::RealFs;
|
||||
use settings::watched_json::WatchedJsonFile;
|
||||
#[cfg(debug_assertions)]
|
||||
use staff_mode::StaffMode;
|
||||
use theme::ThemeRegistry;
|
||||
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
|
||||
use workspace::{
|
||||
item::ItemHandle, notifications::NotifyResultExt, AppState, OpenSettings, Workspace,
|
||||
};
|
||||
use zed::{self, build_window_options, initialize_workspace, languages, menus};
|
||||
use zed::{
|
||||
self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let http = http::client();
|
||||
|
@ -74,10 +80,10 @@ fn main() {
|
|||
load_embedded_fonts(&app);
|
||||
|
||||
let fs = Arc::new(RealFs);
|
||||
|
||||
let themes = ThemeRegistry::new(Assets, app.font_cache());
|
||||
let default_settings = Settings::defaults(Assets, &app.font_cache(), &themes);
|
||||
let config_files = load_config_files(&app, fs.clone());
|
||||
let user_settings_file_rx =
|
||||
watch_config_file(app.background(), fs.clone(), paths::SETTINGS.clone());
|
||||
let user_keymap_file_rx =
|
||||
watch_config_file(app.background(), fs.clone(), paths::KEYMAP.clone());
|
||||
|
||||
let login_shell_env_loaded = if stdout_is_a_pty() {
|
||||
Task::ready(())
|
||||
|
@ -88,29 +94,17 @@ fn main() {
|
|||
};
|
||||
|
||||
let (cli_connections_tx, mut cli_connections_rx) = mpsc::unbounded();
|
||||
let cli_connections_tx = Arc::new(cli_connections_tx);
|
||||
let (open_paths_tx, mut open_paths_rx) = mpsc::unbounded();
|
||||
let open_paths_tx = Arc::new(open_paths_tx);
|
||||
let urls_callback_triggered = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let callback_cli_connections_tx = Arc::clone(&cli_connections_tx);
|
||||
let callback_open_paths_tx = Arc::clone(&open_paths_tx);
|
||||
let callback_urls_callback_triggered = Arc::clone(&urls_callback_triggered);
|
||||
app.on_open_urls(move |urls, _| {
|
||||
if let Some(server_name) = urls.first().and_then(|url| url.strip_prefix("zed-cli://")) {
|
||||
if let Some(cli_connection) = connect_to_cli(server_name).log_err() {
|
||||
cli_connections_tx
|
||||
.unbounded_send(cli_connection)
|
||||
.map_err(|_| anyhow!("no listener for cli connections"))
|
||||
.log_err();
|
||||
};
|
||||
} else {
|
||||
let paths: Vec<_> = urls
|
||||
.iter()
|
||||
.flat_map(|url| url.strip_prefix("file://"))
|
||||
.map(|url| {
|
||||
let decoded = urlencoding::decode_binary(url.as_bytes());
|
||||
PathBuf::from(OsStr::from_bytes(decoded.as_ref()))
|
||||
})
|
||||
.collect();
|
||||
open_paths_tx
|
||||
.unbounded_send(paths)
|
||||
.map_err(|_| anyhow!("no listener for open urls requests"))
|
||||
.log_err();
|
||||
}
|
||||
callback_urls_callback_triggered.store(true, Ordering::Release);
|
||||
open_urls(urls, &callback_cli_connections_tx, &callback_open_paths_tx);
|
||||
})
|
||||
.on_reopen(move |cx| {
|
||||
if cx.has_global::<Weak<AppState>>() {
|
||||
|
@ -129,26 +123,13 @@ fn main() {
|
|||
#[cfg(debug_assertions)]
|
||||
cx.set_global(StaffMode(true));
|
||||
|
||||
let (settings_file_content, keymap_file) = cx.background().block(config_files).unwrap();
|
||||
|
||||
//Setup settings global before binding actions
|
||||
cx.set_global(SettingsFile::new(
|
||||
&paths::SETTINGS,
|
||||
settings_file_content.clone(),
|
||||
fs.clone(),
|
||||
));
|
||||
|
||||
settings::watch_files(
|
||||
default_settings,
|
||||
settings_file_content,
|
||||
themes.clone(),
|
||||
keymap_file,
|
||||
cx,
|
||||
);
|
||||
|
||||
if !stdout_is_a_pty() {
|
||||
upload_previous_panics(http.clone(), cx);
|
||||
}
|
||||
let mut store = SettingsStore::default();
|
||||
store
|
||||
.set_default_settings(default_settings().as_ref(), cx)
|
||||
.unwrap();
|
||||
cx.set_global(store);
|
||||
handle_settings_file_changes(user_settings_file_rx, cx);
|
||||
handle_keymap_file_changes(user_keymap_file_rx, cx);
|
||||
|
||||
let client = client::Client::new(http.clone(), cx);
|
||||
let mut languages = LanguageRegistry::new(login_shell_env_loaded);
|
||||
|
@ -157,15 +138,17 @@ fn main() {
|
|||
let languages = Arc::new(languages);
|
||||
let node_runtime = NodeRuntime::new(http.clone(), cx.background().to_owned());
|
||||
|
||||
languages::init(languages.clone(), themes.clone(), node_runtime.clone());
|
||||
languages::init(languages.clone(), node_runtime.clone());
|
||||
let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx));
|
||||
|
||||
cx.set_global(client.clone());
|
||||
|
||||
theme::init(Assets, cx);
|
||||
context_menu::init(cx);
|
||||
project::Project::init(&client);
|
||||
client::init(client.clone(), cx);
|
||||
project::Project::init(&client, cx);
|
||||
client::init(&client, cx);
|
||||
command_palette::init(cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
go_to_line::init(cx);
|
||||
file_finder::init(cx);
|
||||
|
@ -179,13 +162,12 @@ fn main() {
|
|||
theme_testbench::init(cx);
|
||||
copilot::init(http.clone(), node_runtime, cx);
|
||||
|
||||
cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx))
|
||||
.detach();
|
||||
cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach();
|
||||
|
||||
languages.set_theme(cx.global::<Settings>().theme.clone());
|
||||
cx.observe_global::<Settings, _>({
|
||||
languages.set_theme(theme::current(cx).clone());
|
||||
cx.observe_global::<SettingsStore, _>({
|
||||
let languages = languages.clone();
|
||||
move |cx| languages.set_theme(cx.global::<Settings>().theme.clone())
|
||||
move |cx| languages.set_theme(theme::current(cx).clone())
|
||||
})
|
||||
.detach();
|
||||
|
||||
|
@ -193,12 +175,11 @@ fn main() {
|
|||
client.telemetry().report_mixpanel_event(
|
||||
"start app",
|
||||
Default::default(),
|
||||
cx.global::<Settings>().telemetry(),
|
||||
*settings::get::<TelemetrySettings>(cx),
|
||||
);
|
||||
|
||||
let app_state = Arc::new(AppState {
|
||||
languages,
|
||||
themes,
|
||||
client: client.clone(),
|
||||
user_store,
|
||||
fs,
|
||||
|
@ -207,7 +188,7 @@ fn main() {
|
|||
background_actions,
|
||||
});
|
||||
cx.set_global(Arc::downgrade(&app_state));
|
||||
auto_update::init(http, client::ZED_SERVER_URL.clone(), cx);
|
||||
auto_update::init(http.clone(), client::ZED_SERVER_URL.clone(), cx);
|
||||
|
||||
workspace::init(app_state.clone(), cx);
|
||||
recent_projects::init(cx);
|
||||
|
@ -215,10 +196,13 @@ fn main() {
|
|||
journal::init(app_state.clone(), cx);
|
||||
language_selector::init(cx);
|
||||
theme_selector::init(cx);
|
||||
zed::init(&app_state, cx);
|
||||
activity_indicator::init(cx);
|
||||
lsp_log::init(cx);
|
||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||
collab_ui::init(&app_state, cx);
|
||||
feedback::init(cx);
|
||||
welcome::init(cx);
|
||||
zed::init(&app_state, cx);
|
||||
|
||||
cx.set_menus(menus::menus());
|
||||
|
||||
|
@ -232,6 +216,16 @@ fn main() {
|
|||
workspace::open_paths(&paths, &app_state, None, cx).detach_and_log_err(cx);
|
||||
}
|
||||
} else {
|
||||
upload_previous_panics(http.clone(), cx);
|
||||
|
||||
// TODO Development mode that forces the CLI mode usually runs Zed binary as is instead
|
||||
// of an *app, hence gets no specific callbacks run. Emulate them here, if needed.
|
||||
if std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_some()
|
||||
&& !urls_callback_triggered.load(Ordering::Acquire)
|
||||
{
|
||||
open_urls(collect_url_args(), &cli_connections_tx, &open_paths_tx)
|
||||
}
|
||||
|
||||
if let Ok(Some(connection)) = cli_connections_rx.try_next() {
|
||||
cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx))
|
||||
.detach();
|
||||
|
@ -282,6 +276,37 @@ fn main() {
|
|||
});
|
||||
}
|
||||
|
||||
fn open_urls(
|
||||
urls: Vec<String>,
|
||||
cli_connections_tx: &mpsc::UnboundedSender<(
|
||||
mpsc::Receiver<CliRequest>,
|
||||
IpcSender<CliResponse>,
|
||||
)>,
|
||||
open_paths_tx: &mpsc::UnboundedSender<Vec<PathBuf>>,
|
||||
) {
|
||||
if let Some(server_name) = urls.first().and_then(|url| url.strip_prefix("zed-cli://")) {
|
||||
if let Some(cli_connection) = connect_to_cli(server_name).log_err() {
|
||||
cli_connections_tx
|
||||
.unbounded_send(cli_connection)
|
||||
.map_err(|_| anyhow!("no listener for cli connections"))
|
||||
.log_err();
|
||||
};
|
||||
} else {
|
||||
let paths: Vec<_> = urls
|
||||
.iter()
|
||||
.flat_map(|url| url.strip_prefix("file://"))
|
||||
.map(|url| {
|
||||
let decoded = urlencoding::decode_binary(url.as_bytes());
|
||||
PathBuf::from(OsStr::from_bytes(decoded.as_ref()))
|
||||
})
|
||||
.collect();
|
||||
open_paths_tx
|
||||
.unbounded_send(paths)
|
||||
.map_err(|_| anyhow!("no listener for open urls requests"))
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncAppContext) {
|
||||
if let Some(location) = workspace::last_opened_workspace_paths().await {
|
||||
cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))
|
||||
|
@ -412,7 +437,7 @@ fn init_panic_hook(app_version: String) {
|
|||
}
|
||||
|
||||
fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
||||
let diagnostics_telemetry = cx.global::<Settings>().telemetry_diagnostics();
|
||||
let telemetry_settings = *settings::get::<TelemetrySettings>(cx);
|
||||
|
||||
cx.background()
|
||||
.spawn({
|
||||
|
@ -442,7 +467,7 @@ fn upload_previous_panics(http: Arc<dyn HttpClient>, cx: &mut AppContext) {
|
|||
continue;
|
||||
};
|
||||
|
||||
if diagnostics_telemetry {
|
||||
if telemetry_settings.diagnostics {
|
||||
let panic_data_text = smol::fs::read_to_string(&child_path)
|
||||
.await
|
||||
.context("error reading panic file")?;
|
||||
|
@ -512,7 +537,8 @@ async fn load_login_shell_environment() -> Result<()> {
|
|||
}
|
||||
|
||||
fn stdout_is_a_pty() -> bool {
|
||||
unsafe { libc::isatty(libc::STDOUT_FILENO as i32) != 0 }
|
||||
std::env::var(FORCE_CLI_MODE_ENV_VAR_NAME).ok().is_none()
|
||||
&& unsafe { libc::isatty(libc::STDOUT_FILENO as i32) != 0 }
|
||||
}
|
||||
|
||||
fn collect_path_args() -> Vec<PathBuf> {
|
||||
|
@ -525,7 +551,11 @@ fn collect_path_args() -> Vec<PathBuf> {
|
|||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn collect_url_args() -> Vec<String> {
|
||||
env::args().skip(1).collect()
|
||||
}
|
||||
|
||||
fn load_embedded_fonts(app: &App) {
|
||||
|
@ -547,11 +577,7 @@ fn load_embedded_fonts(app: &App) {
|
|||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
async fn watch_themes(
|
||||
fs: Arc<dyn Fs>,
|
||||
themes: Arc<ThemeRegistry>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Option<()> {
|
||||
async fn watch_themes(fs: Arc<dyn Fs>, mut cx: AsyncAppContext) -> Option<()> {
|
||||
let mut events = fs
|
||||
.watch("styles/src".as_ref(), Duration::from_millis(100))
|
||||
.await;
|
||||
|
@ -563,7 +589,7 @@ async fn watch_themes(
|
|||
.await
|
||||
.log_err()?;
|
||||
if output.status.success() {
|
||||
cx.update(|cx| theme_selector::reload(themes.clone(), cx))
|
||||
cx.update(|cx| theme_selector::reload(cx))
|
||||
} else {
|
||||
eprintln!(
|
||||
"build script failed {}",
|
||||
|
@ -575,35 +601,10 @@ async fn watch_themes(
|
|||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
async fn watch_themes(
|
||||
_fs: Arc<dyn Fs>,
|
||||
_themes: Arc<ThemeRegistry>,
|
||||
_cx: AsyncAppContext,
|
||||
) -> Option<()> {
|
||||
async fn watch_themes(_fs: Arc<dyn Fs>, _cx: AsyncAppContext) -> Option<()> {
|
||||
None
|
||||
}
|
||||
|
||||
fn load_config_files(
|
||||
app: &App,
|
||||
fs: Arc<dyn Fs>,
|
||||
) -> oneshot::Receiver<(
|
||||
WatchedJsonFile<SettingsFileContent>,
|
||||
WatchedJsonFile<KeymapFileContent>,
|
||||
)> {
|
||||
let executor = app.background();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
executor
|
||||
.clone()
|
||||
.spawn(async move {
|
||||
let settings_file =
|
||||
WatchedJsonFile::new(fs.clone(), &executor, paths::SETTINGS.clone()).await;
|
||||
let keymap_file = WatchedJsonFile::new(fs, &executor, paths::KEYMAP.clone()).await;
|
||||
tx.send((settings_file, keymap_file)).ok()
|
||||
})
|
||||
.detach();
|
||||
rx
|
||||
}
|
||||
|
||||
fn connect_to_cli(
|
||||
server_name: &str,
|
||||
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
|
||||
|
@ -641,13 +642,38 @@ async fn handle_cli_connection(
|
|||
if let Some(request) = requests.next().await {
|
||||
match request {
|
||||
CliRequest::Open { paths, wait } => {
|
||||
let mut caret_positions = HashMap::new();
|
||||
|
||||
let paths = if paths.is_empty() {
|
||||
workspace::last_opened_workspace_paths()
|
||||
.await
|
||||
.map(|location| location.paths().to_vec())
|
||||
.unwrap_or(paths)
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
paths
|
||||
.into_iter()
|
||||
.filter_map(|path_with_position_string| {
|
||||
let path_with_position = PathLikeWithPosition::parse_str(
|
||||
&path_with_position_string,
|
||||
|path_str| {
|
||||
Ok::<_, std::convert::Infallible>(
|
||||
Path::new(path_str).to_path_buf(),
|
||||
)
|
||||
},
|
||||
)
|
||||
.expect("Infallible");
|
||||
let path = path_with_position.path_like;
|
||||
if let Some(row) = path_with_position.row {
|
||||
if path.is_file() {
|
||||
let row = row.saturating_sub(1);
|
||||
let col =
|
||||
path_with_position.column.unwrap_or(0).saturating_sub(1);
|
||||
caret_positions.insert(path.clone(), Point::new(row, col));
|
||||
}
|
||||
}
|
||||
Some(path)
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let mut errored = false;
|
||||
|
@ -657,11 +683,32 @@ async fn handle_cli_connection(
|
|||
{
|
||||
Ok((workspace, items)) => {
|
||||
let mut item_release_futures = Vec::new();
|
||||
cx.update(|cx| {
|
||||
for (item, path) in items.into_iter().zip(&paths) {
|
||||
match item {
|
||||
Some(Ok(item)) => {
|
||||
let released = oneshot::channel();
|
||||
|
||||
for (item, path) in items.into_iter().zip(&paths) {
|
||||
match item {
|
||||
Some(Ok(item)) => {
|
||||
if let Some(point) = caret_positions.remove(path) {
|
||||
if let Some(active_editor) = item.downcast::<Editor>() {
|
||||
active_editor
|
||||
.downgrade()
|
||||
.update(&mut cx, |editor, cx| {
|
||||
let snapshot =
|
||||
editor.snapshot(cx).display_snapshot;
|
||||
let point = snapshot
|
||||
.buffer_snapshot
|
||||
.clip_point(point, Bias::Left);
|
||||
editor.change_selections(
|
||||
Some(Autoscroll::center()),
|
||||
cx,
|
||||
|s| s.select_ranges([point..point]),
|
||||
);
|
||||
})
|
||||
.log_err();
|
||||
}
|
||||
}
|
||||
|
||||
let released = oneshot::channel();
|
||||
cx.update(|cx| {
|
||||
item.on_release(
|
||||
cx,
|
||||
Box::new(move |_| {
|
||||
|
@ -669,23 +716,20 @@ async fn handle_cli_connection(
|
|||
}),
|
||||
)
|
||||
.detach();
|
||||
item_release_futures.push(released.1);
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!(
|
||||
"error opening {:?}: {}",
|
||||
path, err
|
||||
),
|
||||
})
|
||||
.log_err();
|
||||
errored = true;
|
||||
}
|
||||
None => {}
|
||||
});
|
||||
item_release_futures.push(released.1);
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
responses
|
||||
.send(CliResponse::Stderr {
|
||||
message: format!("error opening {:?}: {}", path, err),
|
||||
})
|
||||
.log_err();
|
||||
errored = true;
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if wait {
|
||||
let background = cx.background();
|
||||
|
@ -748,13 +792,9 @@ pub fn dock_default_item_factory(
|
|||
workspace: &mut Workspace,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) -> Option<Box<dyn ItemHandle>> {
|
||||
let strategy = cx
|
||||
.global::<Settings>()
|
||||
.terminal_overrides
|
||||
let strategy = settings::get::<TerminalSettings>(cx)
|
||||
.working_directory
|
||||
.clone()
|
||||
.unwrap_or(WorkingDirectory::CurrentProjectDirectory);
|
||||
|
||||
.clone();
|
||||
let working_directory = get_working_directory(workspace, cx, strategy);
|
||||
|
||||
let window_id = cx.window_id();
|
||||
|
|
|
@ -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