Merge branch 'main' into project-panel2
This commit is contained in:
commit
b893ac2a02
65 changed files with 9847 additions and 8976 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
@ -2012,7 +2012,7 @@ dependencies = [
|
|||
"serde_derive",
|
||||
"settings2",
|
||||
"smol",
|
||||
"theme",
|
||||
"theme2",
|
||||
"util",
|
||||
]
|
||||
|
||||
|
@ -2768,7 +2768,6 @@ dependencies = [
|
|||
"copilot2",
|
||||
"ctor",
|
||||
"db2",
|
||||
"drag_and_drop",
|
||||
"env_logger 0.9.3",
|
||||
"futures 0.3.28",
|
||||
"fuzzy2",
|
||||
|
|
|
@ -36,7 +36,7 @@ impl FakeServer {
|
|||
peer: Peer::new(0),
|
||||
state: Default::default(),
|
||||
user_id: client_user_id,
|
||||
executor: cx.executor().clone(),
|
||||
executor: cx.executor(),
|
||||
};
|
||||
|
||||
client
|
||||
|
|
|
@ -510,7 +510,7 @@ fn test_fuzzy_like_string() {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_fuzzy_search_users(cx: &mut TestAppContext) {
|
||||
let test_db = TestDb::postgres(cx.executor().clone());
|
||||
let test_db = TestDb::postgres(cx.executor());
|
||||
let db = test_db.db();
|
||||
for (i, github_login) in [
|
||||
"California",
|
||||
|
|
|
@ -4,6 +4,7 @@ use gpui::{Model, TestAppContext};
|
|||
mod channel_buffer_tests;
|
||||
mod channel_message_tests;
|
||||
mod channel_tests;
|
||||
mod editor_tests;
|
||||
mod following_tests;
|
||||
mod integration_tests;
|
||||
mod notification_tests;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,32 @@
|
|||
// use editor::{
|
||||
// test::editor_test_context::EditorTestContext, ConfirmCodeAction, ConfirmCompletion,
|
||||
// ConfirmRename, Editor, Redo, Rename, ToggleCodeActions, Undo,
|
||||
//todo(partially ported)
|
||||
// use std::{
|
||||
// path::Path,
|
||||
// sync::{
|
||||
// atomic::{self, AtomicBool, AtomicUsize},
|
||||
// Arc,
|
||||
// },
|
||||
// };
|
||||
|
||||
//todo!(editor)
|
||||
// use call::ActiveCall;
|
||||
// use editor::{
|
||||
// test::editor_test_context::{AssertionContextManager, EditorTestContext},
|
||||
// Anchor, ConfirmCodeAction, ConfirmCompletion, ConfirmRename, Editor, Redo, Rename,
|
||||
// ToggleCodeActions, Undo,
|
||||
// };
|
||||
// use gpui::{BackgroundExecutor, TestAppContext, VisualContext, VisualTestContext};
|
||||
// use indoc::indoc;
|
||||
// use language::{
|
||||
// language_settings::{AllLanguageSettings, InlayHintSettings},
|
||||
// tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig,
|
||||
// };
|
||||
// use rpc::RECEIVE_TIMEOUT;
|
||||
// use serde_json::json;
|
||||
// use settings::SettingsStore;
|
||||
// use text::Point;
|
||||
// use workspace::Workspace;
|
||||
|
||||
// use crate::{rpc::RECONNECT_TIMEOUT, tests::TestServer};
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_host_disconnect(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -11,7 +34,7 @@
|
|||
// cx_b: &mut TestAppContext,
|
||||
// cx_c: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let mut server = TestServer::start(executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// let client_c = server.create_client(cx_c, "user_c").await;
|
||||
|
@ -25,7 +48,7 @@
|
|||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// serde_json::json!({
|
||||
// "a.txt": "a-contents",
|
||||
// "b.txt": "b-contents",
|
||||
// }),
|
||||
|
@ -35,7 +58,7 @@
|
|||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
|
||||
// let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees(cx).next().unwrap());
|
||||
// let worktree_a = project_a.read_with(cx_a, |project, cx| project.worktrees().next().unwrap());
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
|
@ -46,21 +69,25 @@
|
|||
|
||||
// assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
|
||||
|
||||
// let window_b =
|
||||
// let workspace_b =
|
||||
// cx_b.add_window(|cx| Workspace::new(0, project_b.clone(), client_b.app_state.clone(), cx));
|
||||
// let workspace_b = window_b.root(cx_b);
|
||||
// let cx_b = &mut VisualTestContext::from_window(*workspace_b, cx_b);
|
||||
|
||||
// let editor_b = workspace_b
|
||||
// .update(cx_b, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "b.txt"), None, true, cx)
|
||||
// })
|
||||
// .unwrap()
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// assert!(window_b.read_with(cx_b, |cx| editor_b.is_focused(cx)));
|
||||
// //TODO: focus
|
||||
// assert!(cx_b.update_view(&editor_b, |editor, cx| editor.is_focused(cx)));
|
||||
// editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
|
||||
// assert!(window_b.is_edited(cx_b));
|
||||
// //todo(is_edited)
|
||||
// // assert!(workspace_b.is_edited(cx_b));
|
||||
|
||||
// // Drop client A's connection. Collaborators should disappear and the project should not be shown as shared.
|
||||
// server.forbid_connections();
|
||||
|
@ -77,10 +104,10 @@
|
|||
|
||||
// // Ensure client B's edited state is reset and that the whole window is blurred.
|
||||
|
||||
// window_b.read_with(cx_b, |cx| {
|
||||
// workspace_b.update(cx_b, |_, cx| {
|
||||
// assert_eq!(cx.focused_view_id(), None);
|
||||
// });
|
||||
// assert!(!window_b.is_edited(cx_b));
|
||||
// // assert!(!workspace_b.is_edited(cx_b));
|
||||
|
||||
// // Ensure client B is not prompted to save edits when closing window after disconnecting.
|
||||
// let can_close = workspace_b
|
||||
|
@ -120,7 +147,6 @@
|
|||
// project_a.read_with(cx_a, |project, _| assert!(!project.is_shared()));
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test]
|
||||
// async fn test_newline_above_or_below_does_not_move_guest_cursor(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -152,12 +178,14 @@
|
|||
// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_a = cx_a.add_window(|_| EmptyView);
|
||||
// let editor_a = window_a.add_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
|
||||
// let window_a = cx_a.add_empty_window();
|
||||
// let editor_a =
|
||||
// window_a.build_view(cx_a, |cx| Editor::for_buffer(buffer_a, Some(project_a), cx));
|
||||
// let mut editor_cx_a = EditorTestContext {
|
||||
// cx: cx_a,
|
||||
// window: window_a.into(),
|
||||
// editor: editor_a,
|
||||
// assertion_cx: AssertionContextManager::new(),
|
||||
// };
|
||||
|
||||
// // Open a buffer as client B
|
||||
|
@ -165,12 +193,14 @@
|
|||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_b = cx_b.add_window(|_| EmptyView);
|
||||
// let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
|
||||
// let window_b = cx_b.add_empty_window();
|
||||
// let editor_b =
|
||||
// window_b.build_view(cx_b, |cx| Editor::for_buffer(buffer_b, Some(project_b), cx));
|
||||
// let mut editor_cx_b = EditorTestContext {
|
||||
// cx: cx_b,
|
||||
// window: window_b.into(),
|
||||
// editor: editor_b,
|
||||
// assertion_cx: AssertionContextManager::new(),
|
||||
// };
|
||||
|
||||
// // Test newline above
|
||||
|
@ -214,7 +244,6 @@
|
|||
// "});
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_collaborating_with_completion(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -275,8 +304,8 @@
|
|||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_b = cx_b.add_window(|_| EmptyView);
|
||||
// let editor_b = window_b.add_view(cx_b, |cx| {
|
||||
// let window_b = cx_b.add_empty_window();
|
||||
// let editor_b = window_b.build_view(cx_b, |cx| {
|
||||
// Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
||||
// });
|
||||
|
||||
|
@ -384,7 +413,7 @@
|
|||
// );
|
||||
|
||||
// // The additional edit is applied.
|
||||
// cx_a.foreground().run_until_parked();
|
||||
// cx_a.executor().run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(
|
||||
|
@ -400,7 +429,7 @@
|
|||
// );
|
||||
// });
|
||||
// }
|
||||
//todo!(editor)
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_collaborating_with_code_actions(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -619,7 +648,6 @@
|
|||
// });
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_collaborating_with_renames(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -813,7 +841,6 @@
|
|||
// })
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_language_server_statuses(
|
||||
// executor: BackgroundExecutor,
|
||||
|
@ -937,8 +964,8 @@
|
|||
// cx_b: &mut TestAppContext,
|
||||
// cx_c: &mut TestAppContext,
|
||||
// ) {
|
||||
// let window_b = cx_b.add_window(|_| EmptyView);
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let window_b = cx_b.add_empty_window();
|
||||
// let mut server = TestServer::start(executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// let client_c = server.create_client(cx_c, "user_c").await;
|
||||
|
@ -1052,7 +1079,7 @@
|
|||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let editor_b = window_b.add_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||
// let editor_b = window_b.build_view(cx_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||
|
||||
// // Client A sees client B's selection
|
||||
// executor.run_until_parked();
|
||||
|
@ -1106,3 +1133,757 @@
|
|||
// == 0
|
||||
// });
|
||||
// }
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_on_input_format_from_host_to_guest(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// // Set up a fake language server.
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
|
||||
// first_trigger_character: ":".to_string(),
|
||||
// more_trigger_character: Some(vec![">".to_string()]),
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// client_a.language_registry().add(Arc::new(language));
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a }",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
// // Open a file in an editor as the host.
|
||||
// let buffer_a = project_a
|
||||
// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_a = cx_a.add_empty_window();
|
||||
// let editor_a = window_a
|
||||
// .update(cx_a, |_, cx| {
|
||||
// cx.build_view(|cx| Editor::for_buffer(buffer_a, Some(project_a.clone()), cx))
|
||||
// })
|
||||
// .unwrap();
|
||||
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// executor.run_until_parked();
|
||||
|
||||
// // Receive an OnTypeFormatting request as the host's language server.
|
||||
// // Return some formattings from the host's language server.
|
||||
// fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
|
||||
// |params, _| async move {
|
||||
// assert_eq!(
|
||||
// params.text_document_position.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// assert_eq!(
|
||||
// params.text_document_position.position,
|
||||
// lsp::Position::new(0, 14),
|
||||
// );
|
||||
|
||||
// Ok(Some(vec![lsp::TextEdit {
|
||||
// new_text: "~<".to_string(),
|
||||
// range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
|
||||
// }]))
|
||||
// },
|
||||
// );
|
||||
|
||||
// // Open the buffer on the guest and see that the formattings worked
|
||||
// let buffer_b = project_b
|
||||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Type a on type formatting trigger character as the guest.
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// cx.focus(&editor_a);
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input(">", cx);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a>~< }")
|
||||
// });
|
||||
|
||||
// // Undo should remove LSP edits first
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a>~< }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a> }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a> }")
|
||||
// });
|
||||
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a> }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a }")
|
||||
// });
|
||||
// }
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_on_input_format_from_guest_to_host(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// // Set up a fake language server.
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
|
||||
// first_trigger_character: ":".to_string(),
|
||||
// more_trigger_character: Some(vec![">".to_string()]),
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// client_a.language_registry().add(Arc::new(language));
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a }",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
// // Open a file in an editor as the guest.
|
||||
// let buffer_b = project_b
|
||||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_b = cx_b.add_empty_window();
|
||||
// let editor_b = window_b.build_view(cx_b, |cx| {
|
||||
// Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
|
||||
// });
|
||||
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// executor.run_until_parked();
|
||||
// // Type a on type formatting trigger character as the guest.
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input(":", cx);
|
||||
// cx.focus(&editor_b);
|
||||
// });
|
||||
|
||||
// // Receive an OnTypeFormatting request as the host's language server.
|
||||
// // Return some formattings from the host's language server.
|
||||
// cx_a.foreground().start_waiting();
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
|
||||
// assert_eq!(
|
||||
// params.text_document_position.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// assert_eq!(
|
||||
// params.text_document_position.position,
|
||||
// lsp::Position::new(0, 14),
|
||||
// );
|
||||
|
||||
// Ok(Some(vec![lsp::TextEdit {
|
||||
// new_text: "~:".to_string(),
|
||||
// range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
|
||||
// }]))
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// cx_a.foreground().finish_waiting();
|
||||
|
||||
// // Open the buffer on the host and see that the formattings worked
|
||||
// let buffer_a = project_a
|
||||
// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a:~: }")
|
||||
// });
|
||||
|
||||
// // Undo should remove LSP edits first
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a:~: }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a: }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a: }")
|
||||
// });
|
||||
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a: }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a }")
|
||||
// });
|
||||
// }
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
// let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
// cx_a.update(editor::init);
|
||||
// cx_b.update(editor::init);
|
||||
|
||||
// cx_a.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// cx_b.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// let language = Arc::new(language);
|
||||
// client_a.language_registry().add(Arc::clone(&language));
|
||||
// client_b.language_registry().add(language);
|
||||
|
||||
// // Client A opens a project.
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// active_call_a
|
||||
// .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Client B joins the project
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
// active_call_b
|
||||
// .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root_view(cx_a);
|
||||
// cx_a.foreground().start_waiting();
|
||||
|
||||
// // The host opens a rust file.
|
||||
// let _buffer_a = project_a
|
||||
// .update(cx_a, |project, cx| {
|
||||
// project.open_local_buffer("/a/main.rs", cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// let editor_a = workspace_a
|
||||
// .update(cx_a, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// // Set up the language server to return an additional inlay hint on each request.
|
||||
// let edits_made = Arc::new(AtomicUsize::new(0));
|
||||
// let closure_edits_made = Arc::clone(&edits_made);
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
|
||||
// let task_edits_made = Arc::clone(&closure_edits_made);
|
||||
// async move {
|
||||
// assert_eq!(
|
||||
// params.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
|
||||
// Ok(Some(vec![lsp::InlayHint {
|
||||
// position: lsp::Position::new(0, edits_made as u32),
|
||||
// label: lsp::InlayHintLabel::String(edits_made.to_string()),
|
||||
// kind: None,
|
||||
// text_edits: None,
|
||||
// tooltip: None,
|
||||
// padding_left: None,
|
||||
// padding_right: None,
|
||||
// data: None,
|
||||
// }]))
|
||||
// }
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// executor.run_until_parked();
|
||||
|
||||
// let initial_edit = edits_made.load(atomic::Ordering::Acquire);
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![initial_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Host should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Host editor update the cache version after every cache/view change",
|
||||
// );
|
||||
// });
|
||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
// let editor_b = workspace_b
|
||||
// .update(cx_b, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![initial_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Client should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Guest editor update the cache version after every cache/view change"
|
||||
// );
|
||||
// });
|
||||
|
||||
// let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
|
||||
// editor.handle_input(":", cx);
|
||||
// cx.focus(&editor_b);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_client_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 2);
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_client_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 2);
|
||||
// });
|
||||
|
||||
// let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input("a change to increment both buffers' versions", cx);
|
||||
// cx.focus(&editor_a);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_host_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 3);
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_host_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 3);
|
||||
// });
|
||||
|
||||
// let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// fake_language_server
|
||||
// .request::<lsp::request::InlayHintRefreshRequest>(())
|
||||
// .await
|
||||
// .expect("inlay refresh request failed");
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_special_edit_for_refresh.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Host should react to /refresh LSP request"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 4,
|
||||
// "Host should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_special_edit_for_refresh.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Guest should get a /refresh LSP request propagated by host"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 4,
|
||||
// "Guest should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_inlay_hint_refresh_is_forwarded(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
// let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
// cx_a.update(editor::init);
|
||||
// cx_b.update(editor::init);
|
||||
|
||||
// cx_a.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: false,
|
||||
// show_type_hints: false,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: false,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// cx_b.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: true,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// let language = Arc::new(language);
|
||||
// client_a.language_registry().add(Arc::clone(&language));
|
||||
// client_b.language_registry().add(language);
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// active_call_a
|
||||
// .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
// active_call_b
|
||||
// .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
// cx_a.foreground().start_waiting();
|
||||
// cx_b.foreground().start_waiting();
|
||||
|
||||
// let editor_a = workspace_a
|
||||
// .update(cx_a, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// let editor_b = workspace_b
|
||||
// .update(cx_b, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// let other_hints = Arc::new(AtomicBool::new(false));
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// let closure_other_hints = Arc::clone(&other_hints);
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
|
||||
// let task_other_hints = Arc::clone(&closure_other_hints);
|
||||
// async move {
|
||||
// assert_eq!(
|
||||
// params.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
|
||||
// let character = if other_hints { 0 } else { 2 };
|
||||
// let label = if other_hints {
|
||||
// "other hint"
|
||||
// } else {
|
||||
// "initial hint"
|
||||
// };
|
||||
// Ok(Some(vec![lsp::InlayHint {
|
||||
// position: lsp::Position::new(0, character),
|
||||
// label: lsp::InlayHintLabel::String(label.to_string()),
|
||||
// kind: None,
|
||||
// text_edits: None,
|
||||
// tooltip: None,
|
||||
// padding_left: None,
|
||||
// padding_right: None,
|
||||
// data: None,
|
||||
// }]))
|
||||
// }
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// cx_a.foreground().finish_waiting();
|
||||
// cx_b.foreground().finish_waiting();
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert!(
|
||||
// extract_hint_labels(editor).is_empty(),
|
||||
// "Host should get no hints due to them turned off"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 0,
|
||||
// "Turned off hints should not generate version updates"
|
||||
// );
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec!["initial hint".to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Client should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Should update cache verison after first hints"
|
||||
// );
|
||||
// });
|
||||
|
||||
// other_hints.fetch_or(true, atomic::Ordering::Release);
|
||||
// fake_language_server
|
||||
// .request::<lsp::request::InlayHintRefreshRequest>(())
|
||||
// .await
|
||||
// .expect("inlay refresh request failed");
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert!(
|
||||
// extract_hint_labels(editor).is_empty(),
|
||||
// "Host should get nop hints due to them turned off, even after the /refresh"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 0,
|
||||
// "Turned off hints should not generate version updates, again"
|
||||
// );
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec!["other hint".to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Guest should get a /refresh LSP request propagated by host despite host hints are off"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 2,
|
||||
// "Guest should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// fn extract_hint_labels(editor: &Editor) -> Vec<String> {
|
||||
// let mut labels = Vec::new();
|
||||
// for hint in editor.inlay_hint_cache().hints() {
|
||||
// match hint.label {
|
||||
// project::InlayHintLabel::String(s) => labels.push(s),
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
// }
|
||||
// labels
|
||||
// }
|
||||
|
|
|
@ -5717,758 +5717,3 @@ async fn test_join_call_after_screen_was_shared(
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_on_input_format_from_host_to_guest(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// // Set up a fake language server.
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
|
||||
// first_trigger_character: ":".to_string(),
|
||||
// more_trigger_character: Some(vec![">".to_string()]),
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// client_a.language_registry().add(Arc::new(language));
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a }",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
// // Open a file in an editor as the host.
|
||||
// let buffer_a = project_a
|
||||
// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_a = cx_a.add_window(|_| EmptyView);
|
||||
// let editor_a = window_a.add_view(cx_a, |cx| {
|
||||
// Editor::for_buffer(buffer_a, Some(project_a.clone()), cx)
|
||||
// });
|
||||
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// executor.run_until_parked();
|
||||
|
||||
// // Receive an OnTypeFormatting request as the host's language server.
|
||||
// // Return some formattings from the host's language server.
|
||||
// fake_language_server.handle_request::<lsp::request::OnTypeFormatting, _, _>(
|
||||
// |params, _| async move {
|
||||
// assert_eq!(
|
||||
// params.text_document_position.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// assert_eq!(
|
||||
// params.text_document_position.position,
|
||||
// lsp::Position::new(0, 14),
|
||||
// );
|
||||
|
||||
// Ok(Some(vec![lsp::TextEdit {
|
||||
// new_text: "~<".to_string(),
|
||||
// range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
|
||||
// }]))
|
||||
// },
|
||||
// );
|
||||
|
||||
// // Open the buffer on the guest and see that the formattings worked
|
||||
// let buffer_b = project_b
|
||||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Type a on type formatting trigger character as the guest.
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// cx.focus(&editor_a);
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input(">", cx);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a>~< }")
|
||||
// });
|
||||
|
||||
// // Undo should remove LSP edits first
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a>~< }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a> }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a> }")
|
||||
// });
|
||||
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a> }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_b.read_with(cx_b, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a }")
|
||||
// });
|
||||
// }
|
||||
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_on_input_format_from_guest_to_host(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
// // Set up a fake language server.
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// document_on_type_formatting_provider: Some(lsp::DocumentOnTypeFormattingOptions {
|
||||
// first_trigger_character: ":".to_string(),
|
||||
// more_trigger_character: Some(vec![">".to_string()]),
|
||||
// }),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// client_a.language_registry().add(Arc::new(language));
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a }",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
|
||||
// // Open a file in an editor as the guest.
|
||||
// let buffer_b = project_b
|
||||
// .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window_b = cx_b.add_window(|_| EmptyView);
|
||||
// let editor_b = window_b.add_view(cx_b, |cx| {
|
||||
// Editor::for_buffer(buffer_b, Some(project_b.clone()), cx)
|
||||
// });
|
||||
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// executor.run_until_parked();
|
||||
// // Type a on type formatting trigger character as the guest.
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input(":", cx);
|
||||
// cx.focus(&editor_b);
|
||||
// });
|
||||
|
||||
// // Receive an OnTypeFormatting request as the host's language server.
|
||||
// // Return some formattings from the host's language server.
|
||||
// cx_a.foreground().start_waiting();
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::OnTypeFormatting, _, _>(|params, _| async move {
|
||||
// assert_eq!(
|
||||
// params.text_document_position.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// assert_eq!(
|
||||
// params.text_document_position.position,
|
||||
// lsp::Position::new(0, 14),
|
||||
// );
|
||||
|
||||
// Ok(Some(vec![lsp::TextEdit {
|
||||
// new_text: "~:".to_string(),
|
||||
// range: lsp::Range::new(lsp::Position::new(0, 14), lsp::Position::new(0, 14)),
|
||||
// }]))
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// cx_a.foreground().finish_waiting();
|
||||
|
||||
// // Open the buffer on the host and see that the formattings worked
|
||||
// let buffer_a = project_a
|
||||
// .update(cx_a, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a:~: }")
|
||||
// });
|
||||
|
||||
// // Undo should remove LSP edits first
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a:~: }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a: }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a: }")
|
||||
// });
|
||||
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), "fn main() { a: }");
|
||||
// editor.undo(&Undo, cx);
|
||||
// assert_eq!(editor.text(cx), "fn main() { a }");
|
||||
// });
|
||||
// executor.run_until_parked();
|
||||
|
||||
// buffer_a.read_with(cx_a, |buffer, _| {
|
||||
// assert_eq!(buffer.text(), "fn main() { a }")
|
||||
// });
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_mutual_editor_inlay_hint_cache_update(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
// let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
// cx_a.update(editor::init);
|
||||
// cx_b.update(editor::init);
|
||||
|
||||
// cx_a.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// cx_b.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// let language = Arc::new(language);
|
||||
// client_a.language_registry().add(Arc::clone(&language));
|
||||
// client_b.language_registry().add(language);
|
||||
|
||||
// // Client A opens a project.
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// active_call_a
|
||||
// .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// // Client B joins the project
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
// active_call_b
|
||||
// .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
// cx_a.foreground().start_waiting();
|
||||
|
||||
// // The host opens a rust file.
|
||||
// let _buffer_a = project_a
|
||||
// .update(cx_a, |project, cx| {
|
||||
// project.open_local_buffer("/a/main.rs", cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// let editor_a = workspace_a
|
||||
// .update(cx_a, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// // Set up the language server to return an additional inlay hint on each request.
|
||||
// let edits_made = Arc::new(AtomicUsize::new(0));
|
||||
// let closure_edits_made = Arc::clone(&edits_made);
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
|
||||
// let task_edits_made = Arc::clone(&closure_edits_made);
|
||||
// async move {
|
||||
// assert_eq!(
|
||||
// params.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// let edits_made = task_edits_made.load(atomic::Ordering::Acquire);
|
||||
// Ok(Some(vec![lsp::InlayHint {
|
||||
// position: lsp::Position::new(0, edits_made as u32),
|
||||
// label: lsp::InlayHintLabel::String(edits_made.to_string()),
|
||||
// kind: None,
|
||||
// text_edits: None,
|
||||
// tooltip: None,
|
||||
// padding_left: None,
|
||||
// padding_right: None,
|
||||
// data: None,
|
||||
// }]))
|
||||
// }
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// executor.run_until_parked();
|
||||
|
||||
// let initial_edit = edits_made.load(atomic::Ordering::Acquire);
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![initial_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Host should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Host editor update the cache version after every cache/view change",
|
||||
// );
|
||||
// });
|
||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
// let editor_b = workspace_b
|
||||
// .update(cx_b, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![initial_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Client should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Guest editor update the cache version after every cache/view change"
|
||||
// );
|
||||
// });
|
||||
|
||||
// let after_client_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// editor_b.update(cx_b, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13].clone()));
|
||||
// editor.handle_input(":", cx);
|
||||
// cx.focus(&editor_b);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_client_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 2);
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_client_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 2);
|
||||
// });
|
||||
|
||||
// let after_host_edit = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// editor_a.update(cx_a, |editor, cx| {
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
|
||||
// editor.handle_input("a change to increment both buffers' versions", cx);
|
||||
// cx.focus(&editor_a);
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_host_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 3);
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_host_edit.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(inlay_cache.version(), 3);
|
||||
// });
|
||||
|
||||
// let after_special_edit_for_refresh = edits_made.fetch_add(1, atomic::Ordering::Release) + 1;
|
||||
// fake_language_server
|
||||
// .request::<lsp::request::InlayHintRefreshRequest>(())
|
||||
// .await
|
||||
// .expect("inlay refresh request failed");
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_special_edit_for_refresh.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Host should react to /refresh LSP request"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 4,
|
||||
// "Host should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec![after_special_edit_for_refresh.to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Guest should get a /refresh LSP request propagated by host"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 4,
|
||||
// "Guest should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
//todo!(editor)
|
||||
// #[gpui::test(iterations = 10)]
|
||||
// async fn test_inlay_hint_refresh_is_forwarded(
|
||||
// executor: BackgroundExecutor,
|
||||
// cx_a: &mut TestAppContext,
|
||||
// cx_b: &mut TestAppContext,
|
||||
// ) {
|
||||
// let mut server = TestServer::start(&executor).await;
|
||||
// let client_a = server.create_client(cx_a, "user_a").await;
|
||||
// let client_b = server.create_client(cx_b, "user_b").await;
|
||||
// server
|
||||
// .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)])
|
||||
// .await;
|
||||
// let active_call_a = cx_a.read(ActiveCall::global);
|
||||
// let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
// cx_a.update(editor::init);
|
||||
// cx_b.update(editor::init);
|
||||
|
||||
// cx_a.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: false,
|
||||
// show_type_hints: false,
|
||||
// show_parameter_hints: false,
|
||||
// show_other_hints: false,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
// cx_b.update(|cx| {
|
||||
// cx.update_global(|store: &mut SettingsStore, cx| {
|
||||
// store.update_user_settings::<AllLanguageSettings>(cx, |settings| {
|
||||
// settings.defaults.inlay_hints = Some(InlayHintSettings {
|
||||
// enabled: true,
|
||||
// show_type_hints: true,
|
||||
// show_parameter_hints: true,
|
||||
// show_other_hints: true,
|
||||
// })
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
// let mut language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// );
|
||||
// let mut fake_language_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities: lsp::ServerCapabilities {
|
||||
// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
|
||||
// ..Default::default()
|
||||
// },
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
// let language = Arc::new(language);
|
||||
// client_a.language_registry().add(Arc::clone(&language));
|
||||
// client_b.language_registry().add(language);
|
||||
|
||||
// client_a
|
||||
// .fs()
|
||||
// .insert_tree(
|
||||
// "/a",
|
||||
// json!({
|
||||
// "main.rs": "fn main() { a } // and some long comment to ensure inlay hints are not trimmed out",
|
||||
// "other.rs": "// Test file",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let (project_a, worktree_id) = client_a.build_local_project("/a", cx_a).await;
|
||||
// active_call_a
|
||||
// .update(cx_a, |call, cx| call.set_location(Some(&project_a), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let project_id = active_call_a
|
||||
// .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let project_b = client_b.build_remote_project(project_id, cx_b).await;
|
||||
// active_call_b
|
||||
// .update(cx_b, |call, cx| call.set_location(Some(&project_b), cx))
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// let workspace_a = client_a.build_workspace(&project_a, cx_a).root(cx_a);
|
||||
// let workspace_b = client_b.build_workspace(&project_b, cx_b).root(cx_b);
|
||||
// cx_a.foreground().start_waiting();
|
||||
// cx_b.foreground().start_waiting();
|
||||
|
||||
// let editor_a = workspace_a
|
||||
// .update(cx_a, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// let editor_b = workspace_b
|
||||
// .update(cx_b, |workspace, cx| {
|
||||
// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .downcast::<Editor>()
|
||||
// .unwrap();
|
||||
|
||||
// let other_hints = Arc::new(AtomicBool::new(false));
|
||||
// let fake_language_server = fake_language_servers.next().await.unwrap();
|
||||
// let closure_other_hints = Arc::clone(&other_hints);
|
||||
// fake_language_server
|
||||
// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
|
||||
// let task_other_hints = Arc::clone(&closure_other_hints);
|
||||
// async move {
|
||||
// assert_eq!(
|
||||
// params.text_document.uri,
|
||||
// lsp::Url::from_file_path("/a/main.rs").unwrap(),
|
||||
// );
|
||||
// let other_hints = task_other_hints.load(atomic::Ordering::Acquire);
|
||||
// let character = if other_hints { 0 } else { 2 };
|
||||
// let label = if other_hints {
|
||||
// "other hint"
|
||||
// } else {
|
||||
// "initial hint"
|
||||
// };
|
||||
// Ok(Some(vec![lsp::InlayHint {
|
||||
// position: lsp::Position::new(0, character),
|
||||
// label: lsp::InlayHintLabel::String(label.to_string()),
|
||||
// kind: None,
|
||||
// text_edits: None,
|
||||
// tooltip: None,
|
||||
// padding_left: None,
|
||||
// padding_right: None,
|
||||
// data: None,
|
||||
// }]))
|
||||
// }
|
||||
// })
|
||||
// .next()
|
||||
// .await
|
||||
// .unwrap();
|
||||
// cx_a.foreground().finish_waiting();
|
||||
// cx_b.foreground().finish_waiting();
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert!(
|
||||
// extract_hint_labels(editor).is_empty(),
|
||||
// "Host should get no hints due to them turned off"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 0,
|
||||
// "Turned off hints should not generate version updates"
|
||||
// );
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec!["initial hint".to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Client should get its first hints when opens an editor"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 1,
|
||||
// "Should update cache verison after first hints"
|
||||
// );
|
||||
// });
|
||||
|
||||
// other_hints.fetch_or(true, atomic::Ordering::Release);
|
||||
// fake_language_server
|
||||
// .request::<lsp::request::InlayHintRefreshRequest>(())
|
||||
// .await
|
||||
// .expect("inlay refresh request failed");
|
||||
// executor.run_until_parked();
|
||||
// editor_a.update(cx_a, |editor, _| {
|
||||
// assert!(
|
||||
// extract_hint_labels(editor).is_empty(),
|
||||
// "Host should get nop hints due to them turned off, even after the /refresh"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 0,
|
||||
// "Turned off hints should not generate version updates, again"
|
||||
// );
|
||||
// });
|
||||
|
||||
// executor.run_until_parked();
|
||||
// editor_b.update(cx_b, |editor, _| {
|
||||
// assert_eq!(
|
||||
// vec!["other hint".to_string()],
|
||||
// extract_hint_labels(editor),
|
||||
// "Guest should get a /refresh LSP request propagated by host despite host hints are off"
|
||||
// );
|
||||
// let inlay_cache = editor.inlay_hint_cache();
|
||||
// assert_eq!(
|
||||
// inlay_cache.version(),
|
||||
// 2,
|
||||
// "Guest should accepted all edits and bump its cache version every time"
|
||||
// );
|
||||
// });
|
||||
// }
|
||||
|
||||
// fn extract_hint_labels(editor: &Editor) -> Vec<String> {
|
||||
// let mut labels = Vec::new();
|
||||
// for hint in editor.inlay_hint_cache().hints() {
|
||||
// match hint.label {
|
||||
// project::InlayHintLabel::String(s) => labels.push(s),
|
||||
// _ => unreachable!(),
|
||||
// }
|
||||
// }
|
||||
// labels
|
||||
// }
|
||||
|
|
|
@ -208,11 +208,11 @@ impl TestServer {
|
|||
})
|
||||
});
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http, cx));
|
||||
let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
|
||||
let mut language_registry = LanguageRegistry::test();
|
||||
language_registry.set_executor(cx.executor().clone());
|
||||
language_registry.set_executor(cx.executor());
|
||||
let app_state = Arc::new(workspace::AppState {
|
||||
client: client.clone(),
|
||||
user_store: user_store.clone(),
|
||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{v_stack, HighlightedLabel, StyledExt};
|
||||
use ui::{h_stack, v_stack, HighlightedLabel, KeyBinding, StyledExt};
|
||||
use util::{
|
||||
channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
|
||||
ResultExt,
|
||||
|
@ -318,66 +318,16 @@ impl PickerDelegate for CommandPaletteDelegate {
|
|||
.rounded_md()
|
||||
.when(selected, |this| this.bg(colors.ghost_element_selected))
|
||||
.hover(|this| this.bg(colors.ghost_element_hover))
|
||||
.child(
|
||||
h_stack()
|
||||
.justify_between()
|
||||
.child(HighlightedLabel::new(
|
||||
command.name.clone(),
|
||||
r#match.positions.clone(),
|
||||
))
|
||||
.children(KeyBinding::for_action(&*command.action, cx)),
|
||||
)
|
||||
}
|
||||
|
||||
// fn render_match(
|
||||
// &self,
|
||||
// ix: usize,
|
||||
// mouse_state: &mut MouseState,
|
||||
// selected: bool,
|
||||
// cx: &gpui::AppContext,
|
||||
// ) -> AnyElement<Picker<Self>> {
|
||||
// let mat = &self.matches[ix];
|
||||
// let command = &self.actions[mat.candidate_id];
|
||||
// let theme = theme::current(cx);
|
||||
// let style = theme.picker.item.in_state(selected).style_for(mouse_state);
|
||||
// let key_style = &theme.command_palette.key.in_state(selected);
|
||||
// let keystroke_spacing = theme.command_palette.keystroke_spacing;
|
||||
|
||||
// Flex::row()
|
||||
// .with_child(
|
||||
// Label::new(mat.string.clone(), style.label.clone())
|
||||
// .with_highlights(mat.positions.clone()),
|
||||
// )
|
||||
// .with_children(command.keystrokes.iter().map(|keystroke| {
|
||||
// Flex::row()
|
||||
// .with_children(
|
||||
// [
|
||||
// (keystroke.ctrl, "^"),
|
||||
// (keystroke.alt, "⌥"),
|
||||
// (keystroke.cmd, "⌘"),
|
||||
// (keystroke.shift, "⇧"),
|
||||
// ]
|
||||
// .into_iter()
|
||||
// .filter_map(|(modifier, label)| {
|
||||
// if modifier {
|
||||
// Some(
|
||||
// Label::new(label, key_style.label.clone())
|
||||
// .contained()
|
||||
// .with_style(key_style.container),
|
||||
// )
|
||||
// } else {
|
||||
// None
|
||||
// }
|
||||
// }),
|
||||
// )
|
||||
// .with_child(
|
||||
// Label::new(keystroke.key.clone(), key_style.label.clone())
|
||||
// .contained()
|
||||
// .with_style(key_style.container),
|
||||
// )
|
||||
// .contained()
|
||||
// .with_margin_left(keystroke_spacing)
|
||||
// .flex_float()
|
||||
// }))
|
||||
// .contained()
|
||||
// .with_style(style.container)
|
||||
// .into_any()
|
||||
// }
|
||||
}
|
||||
|
||||
fn humanize_action_name(name: &str) -> String {
|
||||
|
|
|
@ -24,7 +24,7 @@ collections = { path = "../collections" }
|
|||
gpui = { package = "gpui2", path = "../gpui2" }
|
||||
language = { package = "language2", path = "../language2" }
|
||||
settings = { package = "settings2", path = "../settings2" }
|
||||
theme = { path = "../theme" }
|
||||
theme = { package = "theme2", path = "../theme2" }
|
||||
lsp = { package = "lsp2", path = "../lsp2" }
|
||||
node_runtime = { path = "../node_runtime"}
|
||||
util = { path = "../util" }
|
||||
|
|
|
@ -351,28 +351,29 @@ impl Copilot {
|
|||
}
|
||||
}
|
||||
|
||||
// #[cfg(any(test, feature = "test-support"))]
|
||||
// pub fn fake(cx: &mut gpui::TestAppContext) -> (ModelHandle<Self>, lsp::FakeLanguageServer) {
|
||||
// use node_runtime::FakeNodeRuntime;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub fn fake(cx: &mut gpui::TestAppContext) -> (Model<Self>, lsp::FakeLanguageServer) {
|
||||
use node_runtime::FakeNodeRuntime;
|
||||
|
||||
// let (server, fake_server) =
|
||||
// LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
|
||||
// let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
|
||||
// let node_runtime = FakeNodeRuntime::new();
|
||||
// let this = cx.add_model(|_| Self {
|
||||
// server_id: LanguageServerId(0),
|
||||
// http: http.clone(),
|
||||
// node_runtime,
|
||||
// server: CopilotServer::Running(RunningCopilotServer {
|
||||
// name: LanguageServerName(Arc::from("copilot")),
|
||||
// lsp: Arc::new(server),
|
||||
// sign_in_status: SignInStatus::Authorized,
|
||||
// registered_buffers: Default::default(),
|
||||
// }),
|
||||
// buffers: Default::default(),
|
||||
// });
|
||||
// (this, fake_server)
|
||||
// }
|
||||
let (server, fake_server) =
|
||||
LanguageServer::fake("copilot".into(), Default::default(), cx.to_async());
|
||||
let http = util::http::FakeHttpClient::create(|_| async { unreachable!() });
|
||||
let node_runtime = FakeNodeRuntime::new();
|
||||
let this = cx.build_model(|cx| Self {
|
||||
server_id: LanguageServerId(0),
|
||||
http: http.clone(),
|
||||
node_runtime,
|
||||
server: CopilotServer::Running(RunningCopilotServer {
|
||||
name: LanguageServerName(Arc::from("copilot")),
|
||||
lsp: Arc::new(server),
|
||||
sign_in_status: SignInStatus::Authorized,
|
||||
registered_buffers: Default::default(),
|
||||
}),
|
||||
_subscription: cx.on_app_quit(Self::shutdown_language_server),
|
||||
buffers: Default::default(),
|
||||
});
|
||||
(this, fake_server)
|
||||
}
|
||||
|
||||
fn start_language_server(
|
||||
new_server_id: LanguageServerId,
|
||||
|
|
|
@ -27,7 +27,6 @@ client = { package = "client2", path = "../client2" }
|
|||
clock = { path = "../clock" }
|
||||
copilot = { package="copilot2", path = "../copilot2" }
|
||||
db = { package="db2", path = "../db2" }
|
||||
drag_and_drop = { path = "../drag_and_drop" }
|
||||
collections = { path = "../collections" }
|
||||
# context_menu = { path = "../context_menu" }
|
||||
fuzzy = { package = "fuzzy2", path = "../fuzzy2" }
|
||||
|
|
|
@ -2023,24 +2023,24 @@ impl Editor {
|
|||
dispatch_context
|
||||
}
|
||||
|
||||
// pub fn new_file(
|
||||
// workspace: &mut Workspace,
|
||||
// _: &workspace::NewFile,
|
||||
// cx: &mut ViewContext<Workspace>,
|
||||
// ) {
|
||||
// let project = workspace.project().clone();
|
||||
// if project.read(cx).is_remote() {
|
||||
// cx.propagate();
|
||||
// } else if let Some(buffer) = project
|
||||
// .update(cx, |project, cx| project.create_buffer("", None, cx))
|
||||
// .log_err()
|
||||
// {
|
||||
// workspace.add_item(
|
||||
// Box::new(cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
|
||||
// cx,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
pub fn new_file(
|
||||
workspace: &mut Workspace,
|
||||
_: &workspace::NewFile,
|
||||
cx: &mut ViewContext<Workspace>,
|
||||
) {
|
||||
let project = workspace.project().clone();
|
||||
if project.read(cx).is_remote() {
|
||||
cx.propagate();
|
||||
} else if let Some(buffer) = project
|
||||
.update(cx, |project, cx| project.create_buffer("", None, cx))
|
||||
.log_err()
|
||||
{
|
||||
workspace.add_item(
|
||||
Box::new(cx.build_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx))),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn new_file_in_direction(
|
||||
// workspace: &mut Workspace,
|
||||
|
@ -2124,17 +2124,17 @@ impl Editor {
|
|||
// )
|
||||
// }
|
||||
|
||||
// pub fn mode(&self) -> EditorMode {
|
||||
// self.mode
|
||||
// }
|
||||
pub fn mode(&self) -> EditorMode {
|
||||
self.mode
|
||||
}
|
||||
|
||||
// pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
|
||||
// self.collaboration_hub.as_deref()
|
||||
// }
|
||||
pub fn collaboration_hub(&self) -> Option<&dyn CollaborationHub> {
|
||||
self.collaboration_hub.as_deref()
|
||||
}
|
||||
|
||||
// pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
|
||||
// self.collaboration_hub = Some(hub);
|
||||
// }
|
||||
pub fn set_collaboration_hub(&mut self, hub: Box<dyn CollaborationHub>) {
|
||||
self.collaboration_hub = Some(hub);
|
||||
}
|
||||
|
||||
pub fn set_placeholder_text(
|
||||
&mut self,
|
||||
|
@ -10056,76 +10056,76 @@ pub fn diagnostic_style(
|
|||
}
|
||||
}
|
||||
|
||||
// pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||
// text: &str,
|
||||
// default_style: HighlightStyle,
|
||||
// syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
|
||||
// match_indices: &[usize],
|
||||
// ) -> Vec<(Range<usize>, HighlightStyle)> {
|
||||
// let mut result = Vec::new();
|
||||
// let mut match_indices = match_indices.iter().copied().peekable();
|
||||
pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||
text: &str,
|
||||
default_style: HighlightStyle,
|
||||
syntax_ranges: impl Iterator<Item = (Range<usize>, HighlightStyle)>,
|
||||
match_indices: &[usize],
|
||||
) -> Vec<(Range<usize>, HighlightStyle)> {
|
||||
let mut result = Vec::new();
|
||||
let mut match_indices = match_indices.iter().copied().peekable();
|
||||
|
||||
// for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
|
||||
// {
|
||||
// syntax_highlight.weight = None;
|
||||
for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
|
||||
{
|
||||
syntax_highlight.font_weight = None;
|
||||
|
||||
// // Add highlights for any fuzzy match characters before the next
|
||||
// // syntax highlight range.
|
||||
// while let Some(&match_index) = match_indices.peek() {
|
||||
// if match_index >= range.start {
|
||||
// break;
|
||||
// }
|
||||
// match_indices.next();
|
||||
// let end_index = char_ix_after(match_index, text);
|
||||
// let mut match_style = default_style;
|
||||
// match_style.weight = Some(FontWeight::BOLD);
|
||||
// result.push((match_index..end_index, match_style));
|
||||
// }
|
||||
// Add highlights for any fuzzy match characters before the next
|
||||
// syntax highlight range.
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.start {
|
||||
break;
|
||||
}
|
||||
match_indices.next();
|
||||
let end_index = char_ix_after(match_index, text);
|
||||
let mut match_style = default_style;
|
||||
match_style.font_weight = Some(FontWeight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
}
|
||||
|
||||
// if range.start == usize::MAX {
|
||||
// break;
|
||||
// }
|
||||
if range.start == usize::MAX {
|
||||
break;
|
||||
}
|
||||
|
||||
// // Add highlights for any fuzzy match characters within the
|
||||
// // syntax highlight range.
|
||||
// let mut offset = range.start;
|
||||
// while let Some(&match_index) = match_indices.peek() {
|
||||
// if match_index >= range.end {
|
||||
// break;
|
||||
// }
|
||||
// Add highlights for any fuzzy match characters within the
|
||||
// syntax highlight range.
|
||||
let mut offset = range.start;
|
||||
while let Some(&match_index) = match_indices.peek() {
|
||||
if match_index >= range.end {
|
||||
break;
|
||||
}
|
||||
|
||||
// match_indices.next();
|
||||
// if match_index > offset {
|
||||
// result.push((offset..match_index, syntax_highlight));
|
||||
// }
|
||||
match_indices.next();
|
||||
if match_index > offset {
|
||||
result.push((offset..match_index, syntax_highlight));
|
||||
}
|
||||
|
||||
// let mut end_index = char_ix_after(match_index, text);
|
||||
// while let Some(&next_match_index) = match_indices.peek() {
|
||||
// if next_match_index == end_index && next_match_index < range.end {
|
||||
// end_index = char_ix_after(next_match_index, text);
|
||||
// match_indices.next();
|
||||
// } else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
let mut end_index = char_ix_after(match_index, text);
|
||||
while let Some(&next_match_index) = match_indices.peek() {
|
||||
if next_match_index == end_index && next_match_index < range.end {
|
||||
end_index = char_ix_after(next_match_index, text);
|
||||
match_indices.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// let mut match_style = syntax_highlight;
|
||||
// match_style.weight = Some(FontWeight::BOLD);
|
||||
// result.push((match_index..end_index, match_style));
|
||||
// offset = end_index;
|
||||
// }
|
||||
let mut match_style = syntax_highlight;
|
||||
match_style.font_weight = Some(FontWeight::BOLD);
|
||||
result.push((match_index..end_index, match_style));
|
||||
offset = end_index;
|
||||
}
|
||||
|
||||
// if offset < range.end {
|
||||
// result.push((offset..range.end, syntax_highlight));
|
||||
// }
|
||||
// }
|
||||
if offset < range.end {
|
||||
result.push((offset..range.end, syntax_highlight));
|
||||
}
|
||||
}
|
||||
|
||||
// fn char_ix_after(ix: usize, text: &str) -> usize {
|
||||
// ix + text[ix..].chars().next().unwrap().len_utf8()
|
||||
// }
|
||||
fn char_ix_after(ix: usize, text: &str) -> usize {
|
||||
ix + text[ix..].chars().next().unwrap().len_utf8()
|
||||
}
|
||||
|
||||
// result
|
||||
// }
|
||||
result
|
||||
}
|
||||
|
||||
// pub fn styled_runs_for_code_label<'a>(
|
||||
// label: &'a CodeLabel,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1683,21 +1683,24 @@ impl EditorElement {
|
|||
ShowScrollbar::Never => false,
|
||||
};
|
||||
|
||||
let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Hsla)> = fold_ranges
|
||||
.into_iter()
|
||||
.map(|(id, fold)| {
|
||||
todo!("folds!")
|
||||
// let color = self
|
||||
// .style
|
||||
// .folds
|
||||
// .ellipses
|
||||
// .background
|
||||
// .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
|
||||
// .color;
|
||||
let fold_ranges: Vec<(BufferRow, Range<DisplayPoint>, Hsla)> = Vec::new();
|
||||
// todo!()
|
||||
|
||||
// (id, fold, color)
|
||||
})
|
||||
.collect();
|
||||
// fold_ranges
|
||||
// .into_iter()
|
||||
// .map(|(id, fold)| {
|
||||
// // todo!("folds!")
|
||||
// // let color = self
|
||||
// // .style
|
||||
// // .folds
|
||||
// // .ellipses
|
||||
// // .background
|
||||
// // .style_for(&mut cx.mouse_state::<FoldMarkers>(id as usize))
|
||||
// // .color;
|
||||
|
||||
// // (id, fold, color)
|
||||
// })
|
||||
// .collect();
|
||||
|
||||
let head_for_relative = newest_selection_head.unwrap_or_else(|| {
|
||||
let newest = editor.selections.newest::<Point>(cx);
|
||||
|
|
|
@ -30,6 +30,7 @@ use std::{
|
|||
};
|
||||
use text::Selection;
|
||||
use theme::{ActiveTheme, Theme};
|
||||
use ui::{Label, LabelColor};
|
||||
use util::{paths::PathExt, ResultExt, TryFutureExt};
|
||||
use workspace::item::{BreadcrumbText, FollowEvent, FollowableEvents, FollowableItemHandle};
|
||||
use workspace::{
|
||||
|
@ -595,16 +596,19 @@ impl Item for Editor {
|
|||
.flex_row()
|
||||
.items_center()
|
||||
.gap_2()
|
||||
.child(self.title(cx).to_string())
|
||||
.child(Label::new(self.title(cx).to_string()))
|
||||
.children(detail.and_then(|detail| {
|
||||
let path = path_for_buffer(&self.buffer, detail, false, cx)?;
|
||||
let description = path.to_string_lossy();
|
||||
|
||||
Some(
|
||||
div()
|
||||
.text_color(theme.colors().text_muted)
|
||||
.text_xs()
|
||||
.child(util::truncate_and_trailoff(&description, MAX_TAB_TITLE_LEN)),
|
||||
div().child(
|
||||
Label::new(util::truncate_and_trailoff(
|
||||
&description,
|
||||
MAX_TAB_TITLE_LEN,
|
||||
))
|
||||
.color(LabelColor::Muted),
|
||||
),
|
||||
)
|
||||
})),
|
||||
)
|
||||
|
|
|
@ -315,11 +315,14 @@ impl SelectionsCollection {
|
|||
|
||||
let layed_out_line = display_map.lay_out_line_for_row(row, &text_layout_details);
|
||||
|
||||
dbg!("****START COL****");
|
||||
let start_col = layed_out_line.closest_index_for_x(positions.start) as u32;
|
||||
if start_col < line_len || (is_empty && positions.start == layed_out_line.width) {
|
||||
let start = DisplayPoint::new(row, start_col);
|
||||
dbg!("****END COL****");
|
||||
let end_col = layed_out_line.closest_index_for_x(positions.end) as u32;
|
||||
let end = DisplayPoint::new(row, end_col);
|
||||
dbg!(start_col, end_col);
|
||||
|
||||
Some(Selection {
|
||||
id: post_inc(&mut self.next_selection_id),
|
||||
|
|
|
@ -1,81 +1,74 @@
|
|||
pub mod editor_lsp_test_context;
|
||||
pub mod editor_test_context;
|
||||
|
||||
// todo!()
|
||||
// use crate::{
|
||||
// display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
||||
// DisplayPoint, Editor, EditorMode, MultiBuffer,
|
||||
// };
|
||||
use crate::{
|
||||
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
|
||||
DisplayPoint, Editor, EditorMode, MultiBuffer,
|
||||
};
|
||||
|
||||
// use gpui::{Model, ViewContext};
|
||||
use gpui::{Context, Model, Pixels, ViewContext};
|
||||
|
||||
// use project::Project;
|
||||
// use util::test::{marked_text_offsets, marked_text_ranges};
|
||||
use project::Project;
|
||||
use util::test::{marked_text_offsets, marked_text_ranges};
|
||||
|
||||
// #[cfg(test)]
|
||||
// #[ctor::ctor]
|
||||
// fn init_logger() {
|
||||
// if std::env::var("RUST_LOG").is_ok() {
|
||||
// env_logger::init();
|
||||
// }
|
||||
// }
|
||||
#[cfg(test)]
|
||||
#[ctor::ctor]
|
||||
fn init_logger() {
|
||||
if std::env::var("RUST_LOG").is_ok() {
|
||||
env_logger::init();
|
||||
}
|
||||
}
|
||||
|
||||
// // Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one.
|
||||
// pub fn marked_display_snapshot(
|
||||
// text: &str,
|
||||
// cx: &mut gpui::AppContext,
|
||||
// ) -> (DisplaySnapshot, Vec<DisplayPoint>) {
|
||||
// let (unmarked_text, markers) = marked_text_offsets(text);
|
||||
// Returns a snapshot from text containing '|' character markers with the markers removed, and DisplayPoints for each one.
|
||||
pub fn marked_display_snapshot(
|
||||
text: &str,
|
||||
cx: &mut gpui::AppContext,
|
||||
) -> (DisplaySnapshot, Vec<DisplayPoint>) {
|
||||
let (unmarked_text, markers) = marked_text_offsets(text);
|
||||
|
||||
// let family_id = cx
|
||||
// .font_cache()
|
||||
// .load_family(&["Helvetica"], &Default::default())
|
||||
// .unwrap();
|
||||
// let font_id = cx
|
||||
// .font_cache()
|
||||
// .select_font(family_id, &Default::default())
|
||||
// .unwrap();
|
||||
// let font_size = 14.0;
|
||||
let font = cx.text_style().font();
|
||||
let font_size: Pixels = 14.into();
|
||||
|
||||
// let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
|
||||
// let display_map =
|
||||
// cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
|
||||
// let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
// let markers = markers
|
||||
// .into_iter()
|
||||
// .map(|offset| offset.to_display_point(&snapshot))
|
||||
// .collect();
|
||||
let buffer = MultiBuffer::build_simple(&unmarked_text, cx);
|
||||
let display_map = cx.build_model(|cx| DisplayMap::new(buffer, font, font_size, None, 1, 1, cx));
|
||||
let snapshot = display_map.update(cx, |map, cx| map.snapshot(cx));
|
||||
let markers = markers
|
||||
.into_iter()
|
||||
.map(|offset| offset.to_display_point(&snapshot))
|
||||
.collect();
|
||||
|
||||
// (snapshot, markers)
|
||||
// }
|
||||
(snapshot, markers)
|
||||
}
|
||||
|
||||
// pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
|
||||
// let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
|
||||
// assert_eq!(editor.text(cx), unmarked_text);
|
||||
// editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
|
||||
// }
|
||||
pub fn select_ranges(editor: &mut Editor, marked_text: &str, cx: &mut ViewContext<Editor>) {
|
||||
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
|
||||
assert_eq!(editor.text(cx), unmarked_text);
|
||||
editor.change_selections(None, cx, |s| s.select_ranges(text_ranges));
|
||||
}
|
||||
|
||||
// pub fn assert_text_with_selections(
|
||||
// editor: &mut Editor,
|
||||
// marked_text: &str,
|
||||
// cx: &mut ViewContext<Editor>,
|
||||
// ) {
|
||||
// let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
|
||||
// assert_eq!(editor.text(cx), unmarked_text);
|
||||
// assert_eq!(editor.selections.ranges(cx), text_ranges);
|
||||
// }
|
||||
pub fn assert_text_with_selections(
|
||||
editor: &mut Editor,
|
||||
marked_text: &str,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) {
|
||||
let (unmarked_text, text_ranges) = marked_text_ranges(marked_text, true);
|
||||
assert_eq!(editor.text(cx), unmarked_text);
|
||||
assert_eq!(editor.selections.ranges(cx), text_ranges);
|
||||
}
|
||||
|
||||
// // RA thinks this is dead code even though it is used in a whole lot of tests
|
||||
// #[allow(dead_code)]
|
||||
// #[cfg(any(test, feature = "test-support"))]
|
||||
// pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
|
||||
// Editor::new(EditorMode::Full, buffer, None, None, cx)
|
||||
// }
|
||||
// RA thinks this is dead code even though it is used in a whole lot of tests
|
||||
#[allow(dead_code)]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub(crate) fn build_editor(buffer: Model<MultiBuffer>, cx: &mut ViewContext<Editor>) -> Editor {
|
||||
// todo!()
|
||||
Editor::new(EditorMode::Full, buffer, None, /*None,*/ cx)
|
||||
}
|
||||
|
||||
// pub(crate) fn build_editor_with_project(
|
||||
// project: Model<Project>,
|
||||
// buffer: Model<MultiBuffer>,
|
||||
// cx: &mut ViewContext<Editor>,
|
||||
// ) -> Editor {
|
||||
// Editor::new(EditorMode::Full, buffer, Some(project), None, cx)
|
||||
// }
|
||||
pub(crate) fn build_editor_with_project(
|
||||
project: Model<Project>,
|
||||
buffer: Model<MultiBuffer>,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Editor {
|
||||
// todo!()
|
||||
Editor::new(EditorMode::Full, buffer, Some(project), /*None,*/ cx)
|
||||
}
|
||||
|
|
|
@ -1,297 +1,298 @@
|
|||
// use std::{
|
||||
// borrow::Cow,
|
||||
// ops::{Deref, DerefMut, Range},
|
||||
// sync::Arc,
|
||||
// };
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
// use anyhow::Result;
|
||||
use anyhow::Result;
|
||||
use serde_json::json;
|
||||
|
||||
// use crate::{Editor, ToPoint};
|
||||
// use collections::HashSet;
|
||||
// use futures::Future;
|
||||
// use gpui::{json, View, ViewContext};
|
||||
// use indoc::indoc;
|
||||
// use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
||||
// use lsp::{notification, request};
|
||||
// use multi_buffer::ToPointUtf16;
|
||||
// use project::Project;
|
||||
// use smol::stream::StreamExt;
|
||||
// use workspace::{AppState, Workspace, WorkspaceHandle};
|
||||
use crate::{Editor, ToPoint};
|
||||
use collections::HashSet;
|
||||
use futures::Future;
|
||||
use gpui::{View, ViewContext, VisualTestContext};
|
||||
use indoc::indoc;
|
||||
use language::{point_to_lsp, FakeLspAdapter, Language, LanguageConfig, LanguageQueries};
|
||||
use lsp::{notification, request};
|
||||
use multi_buffer::ToPointUtf16;
|
||||
use project::Project;
|
||||
use smol::stream::StreamExt;
|
||||
use workspace::{AppState, Workspace, WorkspaceHandle};
|
||||
|
||||
// use super::editor_test_context::EditorTestContext;
|
||||
use super::editor_test_context::{AssertionContextManager, EditorTestContext};
|
||||
|
||||
// pub struct EditorLspTestContext<'a> {
|
||||
// pub cx: EditorTestContext<'a>,
|
||||
// pub lsp: lsp::FakeLanguageServer,
|
||||
// pub workspace: View<Workspace>,
|
||||
// pub buffer_lsp_url: lsp::Url,
|
||||
// }
|
||||
pub struct EditorLspTestContext<'a> {
|
||||
pub cx: EditorTestContext<'a>,
|
||||
pub lsp: lsp::FakeLanguageServer,
|
||||
pub workspace: View<Workspace>,
|
||||
pub buffer_lsp_url: lsp::Url,
|
||||
}
|
||||
|
||||
// impl<'a> EditorLspTestContext<'a> {
|
||||
// pub async fn new(
|
||||
// mut language: Language,
|
||||
// capabilities: lsp::ServerCapabilities,
|
||||
// cx: &'a mut gpui::TestAppContext,
|
||||
// ) -> EditorLspTestContext<'a> {
|
||||
// use json::json;
|
||||
impl<'a> EditorLspTestContext<'a> {
|
||||
pub async fn new(
|
||||
mut language: Language,
|
||||
capabilities: lsp::ServerCapabilities,
|
||||
cx: &'a mut gpui::TestAppContext,
|
||||
) -> EditorLspTestContext<'a> {
|
||||
let app_state = cx.update(AppState::test);
|
||||
|
||||
// let app_state = cx.update(AppState::test);
|
||||
cx.update(|cx| {
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
Project::init_settings(cx);
|
||||
});
|
||||
|
||||
// cx.update(|cx| {
|
||||
// language::init(cx);
|
||||
// crate::init(cx);
|
||||
// workspace::init(app_state.clone(), cx);
|
||||
// Project::init_settings(cx);
|
||||
// });
|
||||
let file_name = format!(
|
||||
"file.{}",
|
||||
language
|
||||
.path_suffixes()
|
||||
.first()
|
||||
.expect("language must have a path suffix for EditorLspTestContext")
|
||||
);
|
||||
|
||||
// let file_name = format!(
|
||||
// "file.{}",
|
||||
// language
|
||||
// .path_suffixes()
|
||||
// .first()
|
||||
// .expect("language must have a path suffix for EditorLspTestContext")
|
||||
// );
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
capabilities,
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
// let mut fake_servers = language
|
||||
// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
// capabilities,
|
||||
// ..Default::default()
|
||||
// }))
|
||||
// .await;
|
||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||
|
||||
// let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||
// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
|
||||
project.update(cx, |project, _| project.languages().add(Arc::new(language)));
|
||||
|
||||
// app_state
|
||||
// .fs
|
||||
// .as_fake()
|
||||
// .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
|
||||
// .await;
|
||||
app_state
|
||||
.fs
|
||||
.as_fake()
|
||||
.insert_tree("/root", json!({ "dir": { file_name.clone(): "" }}))
|
||||
.await;
|
||||
|
||||
// let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
// let workspace = window.root(cx);
|
||||
// project
|
||||
// .update(cx, |project, cx| {
|
||||
// project.find_or_create_local_worktree("/root", true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
// cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
|
||||
// .await;
|
||||
let window = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||
|
||||
// let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
|
||||
// let item = workspace
|
||||
// .update(cx, |workspace, cx| {
|
||||
// workspace.open_path(file, None, true, cx)
|
||||
// })
|
||||
// .await
|
||||
// .expect("Could not open test file");
|
||||
let workspace = window.root_view(cx).unwrap();
|
||||
|
||||
// let editor = cx.update(|cx| {
|
||||
// item.act_as::<Editor>(cx)
|
||||
// .expect("Opened test file wasn't an editor")
|
||||
// });
|
||||
// editor.update(cx, |_, cx| cx.focus_self());
|
||||
let mut cx = VisualTestContext::from_window(*window.deref(), cx);
|
||||
project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.find_or_create_local_worktree("/root", true, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
cx.read(|cx| workspace.read(cx).worktree_scans_complete(cx))
|
||||
.await;
|
||||
let file = cx.read(|cx| workspace.file_project_paths(cx)[0].clone());
|
||||
let item = workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
workspace.open_path(file, None, true, cx)
|
||||
})
|
||||
.await
|
||||
.expect("Could not open test file");
|
||||
let editor = cx.update(|cx| {
|
||||
item.act_as::<Editor>(cx)
|
||||
.expect("Opened test file wasn't an editor")
|
||||
});
|
||||
editor.update(&mut cx, |editor, cx| editor.focus(cx));
|
||||
|
||||
// let lsp = fake_servers.next().await.unwrap();
|
||||
let lsp = fake_servers.next().await.unwrap();
|
||||
Self {
|
||||
cx: EditorTestContext {
|
||||
cx,
|
||||
window: window.into(),
|
||||
editor,
|
||||
assertion_cx: AssertionContextManager::new(),
|
||||
},
|
||||
lsp,
|
||||
workspace,
|
||||
buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
// Self {
|
||||
// cx: EditorTestContext {
|
||||
// cx,
|
||||
// window: window.into(),
|
||||
// editor,
|
||||
// },
|
||||
// lsp,
|
||||
// workspace,
|
||||
// buffer_lsp_url: lsp::Url::from_file_path(format!("/root/dir/{file_name}")).unwrap(),
|
||||
// }
|
||||
// }
|
||||
pub async fn new_rust(
|
||||
capabilities: lsp::ServerCapabilities,
|
||||
cx: &'a mut gpui::TestAppContext,
|
||||
) -> EditorLspTestContext<'a> {
|
||||
let language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
)
|
||||
.with_queries(LanguageQueries {
|
||||
indents: Some(Cow::from(indoc! {r#"
|
||||
[
|
||||
((where_clause) _ @end)
|
||||
(field_expression)
|
||||
(call_expression)
|
||||
(assignment_expression)
|
||||
(let_declaration)
|
||||
(let_chain)
|
||||
(await_expression)
|
||||
] @indent
|
||||
|
||||
// pub async fn new_rust(
|
||||
// capabilities: lsp::ServerCapabilities,
|
||||
// cx: &'a mut gpui::TestAppContext,
|
||||
// ) -> EditorLspTestContext<'a> {
|
||||
// let language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Rust".into(),
|
||||
// path_suffixes: vec!["rs".to_string()],
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_rust::language()),
|
||||
// )
|
||||
// .with_queries(LanguageQueries {
|
||||
// indents: Some(Cow::from(indoc! {r#"
|
||||
// [
|
||||
// ((where_clause) _ @end)
|
||||
// (field_expression)
|
||||
// (call_expression)
|
||||
// (assignment_expression)
|
||||
// (let_declaration)
|
||||
// (let_chain)
|
||||
// (await_expression)
|
||||
// ] @indent
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "<" ">" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent"#})),
|
||||
brackets: Some(Cow::from(indoc! {r#"
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("<" @open ">" @close)
|
||||
("\"" @open "\"" @close)
|
||||
(closure_parameters "|" @open "|" @close)"#})),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("Could not parse queries");
|
||||
|
||||
// (_ "[" "]" @end) @indent
|
||||
// (_ "<" ">" @end) @indent
|
||||
// (_ "{" "}" @end) @indent
|
||||
// (_ "(" ")" @end) @indent"#})),
|
||||
// brackets: Some(Cow::from(indoc! {r#"
|
||||
// ("(" @open ")" @close)
|
||||
// ("[" @open "]" @close)
|
||||
// ("{" @open "}" @close)
|
||||
// ("<" @open ">" @close)
|
||||
// ("\"" @open "\"" @close)
|
||||
// (closure_parameters "|" @open "|" @close)"#})),
|
||||
// ..Default::default()
|
||||
// })
|
||||
// .expect("Could not parse queries");
|
||||
Self::new(language, capabilities, cx).await
|
||||
}
|
||||
|
||||
// Self::new(language, capabilities, cx).await
|
||||
// }
|
||||
pub async fn new_typescript(
|
||||
capabilities: lsp::ServerCapabilities,
|
||||
cx: &'a mut gpui::TestAppContext,
|
||||
) -> EditorLspTestContext<'a> {
|
||||
let mut word_characters: HashSet<char> = Default::default();
|
||||
word_characters.insert('$');
|
||||
word_characters.insert('#');
|
||||
let language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Typescript".into(),
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
brackets: language::BracketPairConfig {
|
||||
pairs: vec![language::BracketPair {
|
||||
start: "{".to_string(),
|
||||
end: "}".to_string(),
|
||||
close: true,
|
||||
newline: true,
|
||||
}],
|
||||
disabled_scopes_by_bracket_ix: Default::default(),
|
||||
},
|
||||
word_characters,
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::language_typescript()),
|
||||
)
|
||||
.with_queries(LanguageQueries {
|
||||
brackets: Some(Cow::from(indoc! {r#"
|
||||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("<" @open ">" @close)
|
||||
("\"" @open "\"" @close)"#})),
|
||||
indents: Some(Cow::from(indoc! {r#"
|
||||
[
|
||||
(call_expression)
|
||||
(assignment_expression)
|
||||
(member_expression)
|
||||
(lexical_declaration)
|
||||
(variable_declaration)
|
||||
(assignment_expression)
|
||||
(if_statement)
|
||||
(for_statement)
|
||||
] @indent
|
||||
|
||||
// pub async fn new_typescript(
|
||||
// capabilities: lsp::ServerCapabilities,
|
||||
// cx: &'a mut gpui::TestAppContext,
|
||||
// ) -> EditorLspTestContext<'a> {
|
||||
// let mut word_characters: HashSet<char> = Default::default();
|
||||
// word_characters.insert('$');
|
||||
// word_characters.insert('#');
|
||||
// let language = Language::new(
|
||||
// LanguageConfig {
|
||||
// name: "Typescript".into(),
|
||||
// path_suffixes: vec!["ts".to_string()],
|
||||
// brackets: language::BracketPairConfig {
|
||||
// pairs: vec![language::BracketPair {
|
||||
// start: "{".to_string(),
|
||||
// end: "}".to_string(),
|
||||
// close: true,
|
||||
// newline: true,
|
||||
// }],
|
||||
// disabled_scopes_by_bracket_ix: Default::default(),
|
||||
// },
|
||||
// word_characters,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// Some(tree_sitter_typescript::language_typescript()),
|
||||
// )
|
||||
// .with_queries(LanguageQueries {
|
||||
// brackets: Some(Cow::from(indoc! {r#"
|
||||
// ("(" @open ")" @close)
|
||||
// ("[" @open "]" @close)
|
||||
// ("{" @open "}" @close)
|
||||
// ("<" @open ">" @close)
|
||||
// ("\"" @open "\"" @close)"#})),
|
||||
// indents: Some(Cow::from(indoc! {r#"
|
||||
// [
|
||||
// (call_expression)
|
||||
// (assignment_expression)
|
||||
// (member_expression)
|
||||
// (lexical_declaration)
|
||||
// (variable_declaration)
|
||||
// (assignment_expression)
|
||||
// (if_statement)
|
||||
// (for_statement)
|
||||
// ] @indent
|
||||
(_ "[" "]" @end) @indent
|
||||
(_ "<" ">" @end) @indent
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
||||
"#})),
|
||||
..Default::default()
|
||||
})
|
||||
.expect("Could not parse queries");
|
||||
|
||||
// (_ "[" "]" @end) @indent
|
||||
// (_ "<" ">" @end) @indent
|
||||
// (_ "{" "}" @end) @indent
|
||||
// (_ "(" ")" @end) @indent
|
||||
// "#})),
|
||||
// ..Default::default()
|
||||
// })
|
||||
// .expect("Could not parse queries");
|
||||
Self::new(language, capabilities, cx).await
|
||||
}
|
||||
|
||||
// Self::new(language, capabilities, cx).await
|
||||
// }
|
||||
// Constructs lsp range using a marked string with '[', ']' range delimiters
|
||||
pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
||||
let ranges = self.ranges(marked_text);
|
||||
self.to_lsp_range(ranges[0].clone())
|
||||
}
|
||||
|
||||
// // Constructs lsp range using a marked string with '[', ']' range delimiters
|
||||
// pub fn lsp_range(&mut self, marked_text: &str) -> lsp::Range {
|
||||
// let ranges = self.ranges(marked_text);
|
||||
// self.to_lsp_range(ranges[0].clone())
|
||||
// }
|
||||
pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
let start_point = range.start.to_point(&snapshot.buffer_snapshot);
|
||||
let end_point = range.end.to_point(&snapshot.buffer_snapshot);
|
||||
|
||||
// pub fn to_lsp_range(&mut self, range: Range<usize>) -> lsp::Range {
|
||||
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
// let start_point = range.start.to_point(&snapshot.buffer_snapshot);
|
||||
// let end_point = range.end.to_point(&snapshot.buffer_snapshot);
|
||||
self.editor(|editor, cx| {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
let start = point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(start_point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
);
|
||||
let end = point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(end_point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
);
|
||||
|
||||
// self.editor(|editor, cx| {
|
||||
// let buffer = editor.buffer().read(cx);
|
||||
// let start = point_to_lsp(
|
||||
// buffer
|
||||
// .point_to_buffer_offset(start_point, cx)
|
||||
// .unwrap()
|
||||
// .1
|
||||
// .to_point_utf16(&buffer.read(cx)),
|
||||
// );
|
||||
// let end = point_to_lsp(
|
||||
// buffer
|
||||
// .point_to_buffer_offset(end_point, cx)
|
||||
// .unwrap()
|
||||
// .1
|
||||
// .to_point_utf16(&buffer.read(cx)),
|
||||
// );
|
||||
lsp::Range { start, end }
|
||||
})
|
||||
}
|
||||
|
||||
// lsp::Range { start, end }
|
||||
// })
|
||||
// }
|
||||
pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
|
||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
let point = offset.to_point(&snapshot.buffer_snapshot);
|
||||
|
||||
// pub fn to_lsp(&mut self, offset: usize) -> lsp::Position {
|
||||
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
// let point = offset.to_point(&snapshot.buffer_snapshot);
|
||||
self.editor(|editor, cx| {
|
||||
let buffer = editor.buffer().read(cx);
|
||||
point_to_lsp(
|
||||
buffer
|
||||
.point_to_buffer_offset(point, cx)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_point_utf16(&buffer.read(cx)),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// self.editor(|editor, cx| {
|
||||
// let buffer = editor.buffer().read(cx);
|
||||
// point_to_lsp(
|
||||
// buffer
|
||||
// .point_to_buffer_offset(point, cx)
|
||||
// .unwrap()
|
||||
// .1
|
||||
// .to_point_utf16(&buffer.read(cx)),
|
||||
// )
|
||||
// })
|
||||
// }
|
||||
pub fn update_workspace<F, T>(&mut self, update: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
|
||||
{
|
||||
self.workspace.update(&mut self.cx.cx, update)
|
||||
}
|
||||
|
||||
// pub fn update_workspace<F, T>(&mut self, update: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&mut Workspace, &mut ViewContext<Workspace>) -> T,
|
||||
// {
|
||||
// self.workspace.update(self.cx.cx, update)
|
||||
// }
|
||||
pub fn handle_request<T, F, Fut>(
|
||||
&self,
|
||||
mut handler: F,
|
||||
) -> futures::channel::mpsc::UnboundedReceiver<()>
|
||||
where
|
||||
T: 'static + request::Request,
|
||||
T::Params: 'static + Send,
|
||||
F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
|
||||
Fut: 'static + Send + Future<Output = Result<T::Result>>,
|
||||
{
|
||||
let url = self.buffer_lsp_url.clone();
|
||||
self.lsp.handle_request::<T, _, _>(move |params, cx| {
|
||||
let url = url.clone();
|
||||
handler(url, params, cx)
|
||||
})
|
||||
}
|
||||
|
||||
// pub fn handle_request<T, F, Fut>(
|
||||
// &self,
|
||||
// mut handler: F,
|
||||
// ) -> futures::channel::mpsc::UnboundedReceiver<()>
|
||||
// where
|
||||
// T: 'static + request::Request,
|
||||
// T::Params: 'static + Send,
|
||||
// F: 'static + Send + FnMut(lsp::Url, T::Params, gpui::AsyncAppContext) -> Fut,
|
||||
// Fut: 'static + Send + Future<Output = Result<T::Result>>,
|
||||
// {
|
||||
// let url = self.buffer_lsp_url.clone();
|
||||
// self.lsp.handle_request::<T, _, _>(move |params, cx| {
|
||||
// let url = url.clone();
|
||||
// handler(url, params, cx)
|
||||
// })
|
||||
// }
|
||||
pub fn notify<T: notification::Notification>(&self, params: T::Params) {
|
||||
self.lsp.notify::<T>(params);
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn notify<T: notification::Notification>(&self, params: T::Params) {
|
||||
// self.lsp.notify::<T>(params);
|
||||
// }
|
||||
// }
|
||||
impl<'a> Deref for EditorLspTestContext<'a> {
|
||||
type Target = EditorTestContext<'a>;
|
||||
|
||||
// impl<'a> Deref for EditorLspTestContext<'a> {
|
||||
// type Target = EditorTestContext<'a>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cx
|
||||
}
|
||||
}
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// &self.cx
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl<'a> DerefMut for EditorLspTestContext<'a> {
|
||||
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// &mut self.cx
|
||||
// }
|
||||
// }
|
||||
impl<'a> DerefMut for EditorLspTestContext<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cx
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,331 +1,400 @@
|
|||
use crate::{
|
||||
display_map::ToDisplayPoint, AnchorRangeExt, Autoscroll, DisplayPoint, Editor, MultiBuffer,
|
||||
};
|
||||
use collections::BTreeMap;
|
||||
use futures::Future;
|
||||
use gpui::{
|
||||
AnyWindowHandle, AppContext, ForegroundExecutor, Keystroke, ModelContext, View, ViewContext,
|
||||
VisualTestContext, WindowHandle,
|
||||
};
|
||||
use indoc::indoc;
|
||||
use itertools::Itertools;
|
||||
use language::{Buffer, BufferSnapshot};
|
||||
use parking_lot::RwLock;
|
||||
use project::{FakeFs, Project};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc,
|
||||
},
|
||||
};
|
||||
use util::{
|
||||
assert_set_eq,
|
||||
test::{generate_marked_text, marked_text_ranges},
|
||||
};
|
||||
|
||||
// use super::build_editor_with_project;
|
||||
use super::build_editor_with_project;
|
||||
|
||||
// pub struct EditorTestContext<'a> {
|
||||
// pub cx: &'a mut gpui::TestAppContext,
|
||||
// pub window: AnyWindowHandle,
|
||||
// pub editor: View<Editor>,
|
||||
// }
|
||||
pub struct EditorTestContext<'a> {
|
||||
pub cx: gpui::VisualTestContext<'a>,
|
||||
pub window: AnyWindowHandle,
|
||||
pub editor: View<Editor>,
|
||||
pub assertion_cx: AssertionContextManager,
|
||||
}
|
||||
|
||||
// impl<'a> EditorTestContext<'a> {
|
||||
// pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
|
||||
// let fs = FakeFs::new(cx.background());
|
||||
// // fs.insert_file("/file", "".to_owned()).await;
|
||||
// fs.insert_tree(
|
||||
// "/root",
|
||||
// gpui::serde_json::json!({
|
||||
// "file": "",
|
||||
// }),
|
||||
// )
|
||||
// .await;
|
||||
// let project = Project::test(fs, ["/root".as_ref()], cx).await;
|
||||
// let buffer = project
|
||||
// .update(cx, |project, cx| {
|
||||
// project.open_local_buffer("/root/file", cx)
|
||||
// })
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let window = cx.add_window(|cx| {
|
||||
// cx.focus_self();
|
||||
// build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx)
|
||||
// });
|
||||
// let editor = window.root(cx);
|
||||
// Self {
|
||||
// cx,
|
||||
// window: window.into(),
|
||||
// editor,
|
||||
// }
|
||||
// }
|
||||
impl<'a> EditorTestContext<'a> {
|
||||
pub async fn new(cx: &'a mut gpui::TestAppContext) -> EditorTestContext<'a> {
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
// fs.insert_file("/file", "".to_owned()).await;
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
gpui::serde_json::json!({
|
||||
"file": "",
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
let project = Project::test(fs, ["/root".as_ref()], cx).await;
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/root/file", cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let editor = cx.add_window(|cx| {
|
||||
let editor =
|
||||
build_editor_with_project(project, MultiBuffer::build_from_buffer(buffer, cx), cx);
|
||||
editor.focus(cx);
|
||||
editor
|
||||
});
|
||||
let editor_view = editor.root_view(cx).unwrap();
|
||||
Self {
|
||||
cx: VisualTestContext::from_window(*editor.deref(), cx),
|
||||
window: editor.into(),
|
||||
editor: editor_view,
|
||||
assertion_cx: AssertionContextManager::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn condition(
|
||||
// &self,
|
||||
// predicate: impl FnMut(&Editor, &AppContext) -> bool,
|
||||
// ) -> impl Future<Output = ()> {
|
||||
// self.editor.condition(self.cx, predicate)
|
||||
// }
|
||||
pub fn condition(
|
||||
&self,
|
||||
predicate: impl FnMut(&Editor, &AppContext) -> bool,
|
||||
) -> impl Future<Output = ()> {
|
||||
self.editor.condition::<crate::Event>(&self.cx, predicate)
|
||||
}
|
||||
|
||||
// pub fn editor<F, T>(&self, read: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
|
||||
// {
|
||||
// self.editor.update(self.cx, read)
|
||||
// }
|
||||
#[track_caller]
|
||||
pub fn editor<F, T>(&mut self, read: F) -> T
|
||||
where
|
||||
F: FnOnce(&Editor, &ViewContext<Editor>) -> T,
|
||||
{
|
||||
self.editor
|
||||
.update(&mut self.cx, |this, cx| read(&this, &cx))
|
||||
}
|
||||
|
||||
// pub fn update_editor<F, T>(&mut self, update: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
|
||||
// {
|
||||
// self.editor.update(self.cx, update)
|
||||
// }
|
||||
#[track_caller]
|
||||
pub fn update_editor<F, T>(&mut self, update: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Editor, &mut ViewContext<Editor>) -> T,
|
||||
{
|
||||
self.editor.update(&mut self.cx, update)
|
||||
}
|
||||
|
||||
// pub fn multibuffer<F, T>(&self, read: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&MultiBuffer, &AppContext) -> T,
|
||||
// {
|
||||
// self.editor(|editor, cx| read(editor.buffer().read(cx), cx))
|
||||
// }
|
||||
pub fn multibuffer<F, T>(&mut self, read: F) -> T
|
||||
where
|
||||
F: FnOnce(&MultiBuffer, &AppContext) -> T,
|
||||
{
|
||||
self.editor(|editor, cx| read(editor.buffer().read(cx), cx))
|
||||
}
|
||||
|
||||
// pub fn update_multibuffer<F, T>(&mut self, update: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T,
|
||||
// {
|
||||
// self.update_editor(|editor, cx| editor.buffer().update(cx, update))
|
||||
// }
|
||||
pub fn update_multibuffer<F, T>(&mut self, update: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut MultiBuffer, &mut ModelContext<MultiBuffer>) -> T,
|
||||
{
|
||||
self.update_editor(|editor, cx| editor.buffer().update(cx, update))
|
||||
}
|
||||
|
||||
// pub fn buffer_text(&self) -> String {
|
||||
// self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
|
||||
// }
|
||||
pub fn buffer_text(&mut self) -> String {
|
||||
self.multibuffer(|buffer, cx| buffer.snapshot(cx).text())
|
||||
}
|
||||
|
||||
// pub fn buffer<F, T>(&self, read: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&Buffer, &AppContext) -> T,
|
||||
// {
|
||||
// self.multibuffer(|multibuffer, cx| {
|
||||
// let buffer = multibuffer.as_singleton().unwrap().read(cx);
|
||||
// read(buffer, cx)
|
||||
// })
|
||||
// }
|
||||
pub fn buffer<F, T>(&mut self, read: F) -> T
|
||||
where
|
||||
F: FnOnce(&Buffer, &AppContext) -> T,
|
||||
{
|
||||
self.multibuffer(|multibuffer, cx| {
|
||||
let buffer = multibuffer.as_singleton().unwrap().read(cx);
|
||||
read(buffer, cx)
|
||||
})
|
||||
}
|
||||
|
||||
// pub fn update_buffer<F, T>(&mut self, update: F) -> T
|
||||
// where
|
||||
// F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T,
|
||||
// {
|
||||
// self.update_multibuffer(|multibuffer, cx| {
|
||||
// let buffer = multibuffer.as_singleton().unwrap();
|
||||
// buffer.update(cx, update)
|
||||
// })
|
||||
// }
|
||||
pub fn update_buffer<F, T>(&mut self, update: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Buffer, &mut ModelContext<Buffer>) -> T,
|
||||
{
|
||||
self.update_multibuffer(|multibuffer, cx| {
|
||||
let buffer = multibuffer.as_singleton().unwrap();
|
||||
buffer.update(cx, update)
|
||||
})
|
||||
}
|
||||
|
||||
// pub fn buffer_snapshot(&self) -> BufferSnapshot {
|
||||
// self.buffer(|buffer, _| buffer.snapshot())
|
||||
// }
|
||||
pub fn buffer_snapshot(&mut self) -> BufferSnapshot {
|
||||
self.buffer(|buffer, _| buffer.snapshot())
|
||||
}
|
||||
|
||||
// pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
|
||||
// let keystroke_under_test_handle =
|
||||
// self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
|
||||
// let keystroke = Keystroke::parse(keystroke_text).unwrap();
|
||||
pub fn add_assertion_context(&self, context: String) -> ContextHandle {
|
||||
self.assertion_cx.add_context(context)
|
||||
}
|
||||
|
||||
// self.cx.dispatch_keystroke(self.window, keystroke, false);
|
||||
pub fn assertion_context(&self) -> String {
|
||||
self.assertion_cx.context()
|
||||
}
|
||||
|
||||
// keystroke_under_test_handle
|
||||
// }
|
||||
pub fn simulate_keystroke(&mut self, keystroke_text: &str) -> ContextHandle {
|
||||
let keystroke_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystroke: {:?}", keystroke_text));
|
||||
let keystroke = Keystroke::parse(keystroke_text).unwrap();
|
||||
|
||||
// pub fn simulate_keystrokes<const COUNT: usize>(
|
||||
// &mut self,
|
||||
// keystroke_texts: [&str; COUNT],
|
||||
// ) -> ContextHandle {
|
||||
// let keystrokes_under_test_handle =
|
||||
// self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
|
||||
// for keystroke_text in keystroke_texts.into_iter() {
|
||||
// self.simulate_keystroke(keystroke_text);
|
||||
// }
|
||||
// // it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
|
||||
// // before returning.
|
||||
// // NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
|
||||
// // quickly races with async actions.
|
||||
// if let Foreground::Deterministic { cx_id: _, executor } = self.cx.foreground().as_ref() {
|
||||
// executor.run_until_parked();
|
||||
// } else {
|
||||
// unreachable!();
|
||||
// }
|
||||
self.cx.dispatch_keystroke(self.window, keystroke, false);
|
||||
|
||||
// keystrokes_under_test_handle
|
||||
// }
|
||||
keystroke_under_test_handle
|
||||
}
|
||||
|
||||
// pub fn ranges(&self, marked_text: &str) -> Vec<Range<usize>> {
|
||||
// let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
|
||||
// assert_eq!(self.buffer_text(), unmarked_text);
|
||||
// ranges
|
||||
// }
|
||||
pub fn simulate_keystrokes<const COUNT: usize>(
|
||||
&mut self,
|
||||
keystroke_texts: [&str; COUNT],
|
||||
) -> ContextHandle {
|
||||
let keystrokes_under_test_handle =
|
||||
self.add_assertion_context(format!("Simulated Keystrokes: {:?}", keystroke_texts));
|
||||
for keystroke_text in keystroke_texts.into_iter() {
|
||||
self.simulate_keystroke(keystroke_text);
|
||||
}
|
||||
// it is common for keyboard shortcuts to kick off async actions, so this ensures that they are complete
|
||||
// before returning.
|
||||
// NOTE: we don't do this in simulate_keystroke() because a possible cause of bugs is that typing too
|
||||
// quickly races with async actions.
|
||||
self.cx.background_executor.run_until_parked();
|
||||
|
||||
// pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
|
||||
// let ranges = self.ranges(marked_text);
|
||||
// let snapshot = self
|
||||
// .editor
|
||||
// .update(self.cx, |editor, cx| editor.snapshot(cx));
|
||||
// ranges[0].start.to_display_point(&snapshot)
|
||||
// }
|
||||
keystrokes_under_test_handle
|
||||
}
|
||||
|
||||
// // Returns anchors for the current buffer using `«` and `»`
|
||||
// pub fn text_anchor_range(&self, marked_text: &str) -> Range<language::Anchor> {
|
||||
// let ranges = self.ranges(marked_text);
|
||||
// let snapshot = self.buffer_snapshot();
|
||||
// snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
|
||||
// }
|
||||
pub fn ranges(&mut self, marked_text: &str) -> Vec<Range<usize>> {
|
||||
let (unmarked_text, ranges) = marked_text_ranges(marked_text, false);
|
||||
assert_eq!(self.buffer_text(), unmarked_text);
|
||||
ranges
|
||||
}
|
||||
|
||||
// pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
|
||||
// let diff_base = diff_base.map(String::from);
|
||||
// self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
|
||||
// }
|
||||
pub fn display_point(&mut self, marked_text: &str) -> DisplayPoint {
|
||||
let ranges = self.ranges(marked_text);
|
||||
let snapshot = self
|
||||
.editor
|
||||
.update(&mut self.cx, |editor, cx| editor.snapshot(cx));
|
||||
ranges[0].start.to_display_point(&snapshot)
|
||||
}
|
||||
|
||||
// /// Change the editor's text and selections using a string containing
|
||||
// /// embedded range markers that represent the ranges and directions of
|
||||
// /// each selection.
|
||||
// ///
|
||||
// /// Returns a context handle so that assertion failures can print what
|
||||
// /// editor state was needed to cause the failure.
|
||||
// ///
|
||||
// /// See the `util::test::marked_text_ranges` function for more information.
|
||||
// pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
|
||||
// let state_context = self.add_assertion_context(format!(
|
||||
// "Initial Editor State: \"{}\"",
|
||||
// marked_text.escape_debug().to_string()
|
||||
// ));
|
||||
// let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
|
||||
// self.editor.update(self.cx, |editor, cx| {
|
||||
// editor.set_text(unmarked_text, cx);
|
||||
// editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
// s.select_ranges(selection_ranges)
|
||||
// })
|
||||
// });
|
||||
// state_context
|
||||
// }
|
||||
// Returns anchors for the current buffer using `«` and `»`
|
||||
pub fn text_anchor_range(&mut self, marked_text: &str) -> Range<language::Anchor> {
|
||||
let ranges = self.ranges(marked_text);
|
||||
let snapshot = self.buffer_snapshot();
|
||||
snapshot.anchor_before(ranges[0].start)..snapshot.anchor_after(ranges[0].end)
|
||||
}
|
||||
|
||||
// /// Only change the editor's selections
|
||||
// pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
|
||||
// let state_context = self.add_assertion_context(format!(
|
||||
// "Initial Editor State: \"{}\"",
|
||||
// marked_text.escape_debug().to_string()
|
||||
// ));
|
||||
// let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
|
||||
// self.editor.update(self.cx, |editor, cx| {
|
||||
// assert_eq!(editor.text(cx), unmarked_text);
|
||||
// editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
// s.select_ranges(selection_ranges)
|
||||
// })
|
||||
// });
|
||||
// state_context
|
||||
// }
|
||||
pub fn set_diff_base(&mut self, diff_base: Option<&str>) {
|
||||
let diff_base = diff_base.map(String::from);
|
||||
self.update_buffer(|buffer, cx| buffer.set_diff_base(diff_base, cx));
|
||||
}
|
||||
|
||||
// /// Make an assertion about the editor's text and the ranges and directions
|
||||
// /// of its selections using a string containing embedded range markers.
|
||||
// ///
|
||||
// /// See the `util::test::marked_text_ranges` function for more information.
|
||||
// #[track_caller]
|
||||
// pub fn assert_editor_state(&mut self, marked_text: &str) {
|
||||
// let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
|
||||
// let buffer_text = self.buffer_text();
|
||||
/// Change the editor's text and selections using a string containing
|
||||
/// embedded range markers that represent the ranges and directions of
|
||||
/// each selection.
|
||||
///
|
||||
/// Returns a context handle so that assertion failures can print what
|
||||
/// editor state was needed to cause the failure.
|
||||
///
|
||||
/// See the `util::test::marked_text_ranges` function for more information.
|
||||
pub fn set_state(&mut self, marked_text: &str) -> ContextHandle {
|
||||
let state_context = self.add_assertion_context(format!(
|
||||
"Initial Editor State: \"{}\"",
|
||||
marked_text.escape_debug().to_string()
|
||||
));
|
||||
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
|
||||
self.editor.update(&mut self.cx, |editor, cx| {
|
||||
editor.set_text(unmarked_text, cx);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges(selection_ranges)
|
||||
})
|
||||
});
|
||||
state_context
|
||||
}
|
||||
|
||||
// if buffer_text != unmarked_text {
|
||||
// panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
|
||||
// }
|
||||
/// Only change the editor's selections
|
||||
pub fn set_selections_state(&mut self, marked_text: &str) -> ContextHandle {
|
||||
let state_context = self.add_assertion_context(format!(
|
||||
"Initial Editor State: \"{}\"",
|
||||
marked_text.escape_debug().to_string()
|
||||
));
|
||||
let (unmarked_text, selection_ranges) = marked_text_ranges(marked_text, true);
|
||||
self.editor.update(&mut self.cx, |editor, cx| {
|
||||
assert_eq!(editor.text(cx), unmarked_text);
|
||||
editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
|
||||
s.select_ranges(selection_ranges)
|
||||
})
|
||||
});
|
||||
state_context
|
||||
}
|
||||
|
||||
// self.assert_selections(expected_selections, marked_text.to_string())
|
||||
// }
|
||||
/// Make an assertion about the editor's text and the ranges and directions
|
||||
/// of its selections using a string containing embedded range markers.
|
||||
///
|
||||
/// See the `util::test::marked_text_ranges` function for more information.
|
||||
#[track_caller]
|
||||
pub fn assert_editor_state(&mut self, marked_text: &str) {
|
||||
let (unmarked_text, expected_selections) = marked_text_ranges(marked_text, true);
|
||||
let buffer_text = self.buffer_text();
|
||||
|
||||
// pub fn editor_state(&mut self) -> String {
|
||||
// generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
|
||||
// }
|
||||
if buffer_text != unmarked_text {
|
||||
panic!("Unmarked text doesn't match buffer text\nBuffer text: {buffer_text:?}\nUnmarked text: {unmarked_text:?}\nRaw buffer text\n{buffer_text}Raw unmarked text\n{unmarked_text}");
|
||||
}
|
||||
|
||||
// #[track_caller]
|
||||
// pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
|
||||
// let expected_ranges = self.ranges(marked_text);
|
||||
// let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
|
||||
// let snapshot = editor.snapshot(cx);
|
||||
// editor
|
||||
// .background_highlights
|
||||
// .get(&TypeId::of::<Tag>())
|
||||
// .map(|h| h.1.clone())
|
||||
// .unwrap_or_default()
|
||||
// .into_iter()
|
||||
// .map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||
// .collect()
|
||||
// });
|
||||
// assert_set_eq!(actual_ranges, expected_ranges);
|
||||
// }
|
||||
self.assert_selections(expected_selections, marked_text.to_string())
|
||||
}
|
||||
|
||||
// #[track_caller]
|
||||
// pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
|
||||
// let expected_ranges = self.ranges(marked_text);
|
||||
// let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
// let actual_ranges: Vec<Range<usize>> = snapshot
|
||||
// .text_highlight_ranges::<Tag>()
|
||||
// .map(|ranges| ranges.as_ref().clone().1)
|
||||
// .unwrap_or_default()
|
||||
// .into_iter()
|
||||
// .map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||
// .collect();
|
||||
// assert_set_eq!(actual_ranges, expected_ranges);
|
||||
// }
|
||||
pub fn editor_state(&mut self) -> String {
|
||||
generate_marked_text(self.buffer_text().as_str(), &self.editor_selections(), true)
|
||||
}
|
||||
|
||||
// #[track_caller]
|
||||
// pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
|
||||
// let expected_marked_text =
|
||||
// generate_marked_text(&self.buffer_text(), &expected_selections, true);
|
||||
// self.assert_selections(expected_selections, expected_marked_text)
|
||||
// }
|
||||
#[track_caller]
|
||||
pub fn assert_editor_background_highlights<Tag: 'static>(&mut self, marked_text: &str) {
|
||||
let expected_ranges = self.ranges(marked_text);
|
||||
let actual_ranges: Vec<Range<usize>> = self.update_editor(|editor, cx| {
|
||||
let snapshot = editor.snapshot(cx);
|
||||
editor
|
||||
.background_highlights
|
||||
.get(&TypeId::of::<Tag>())
|
||||
.map(|h| h.1.clone())
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||
.collect()
|
||||
});
|
||||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
}
|
||||
|
||||
// fn editor_selections(&self) -> Vec<Range<usize>> {
|
||||
// self.editor
|
||||
// .read_with(self.cx, |editor, cx| editor.selections.all::<usize>(cx))
|
||||
// .into_iter()
|
||||
// .map(|s| {
|
||||
// if s.reversed {
|
||||
// s.end..s.start
|
||||
// } else {
|
||||
// s.start..s.end
|
||||
// }
|
||||
// })
|
||||
// .collect::<Vec<_>>()
|
||||
// }
|
||||
#[track_caller]
|
||||
pub fn assert_editor_text_highlights<Tag: ?Sized + 'static>(&mut self, marked_text: &str) {
|
||||
let expected_ranges = self.ranges(marked_text);
|
||||
let snapshot = self.update_editor(|editor, cx| editor.snapshot(cx));
|
||||
let actual_ranges: Vec<Range<usize>> = snapshot
|
||||
.text_highlight_ranges::<Tag>()
|
||||
.map(|ranges| ranges.as_ref().clone().1)
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|range| range.to_offset(&snapshot.buffer_snapshot))
|
||||
.collect();
|
||||
assert_set_eq!(actual_ranges, expected_ranges);
|
||||
}
|
||||
|
||||
// #[track_caller]
|
||||
// fn assert_selections(
|
||||
// &mut self,
|
||||
// expected_selections: Vec<Range<usize>>,
|
||||
// expected_marked_text: String,
|
||||
// ) {
|
||||
// let actual_selections = self.editor_selections();
|
||||
// let actual_marked_text =
|
||||
// generate_marked_text(&self.buffer_text(), &actual_selections, true);
|
||||
// if expected_selections != actual_selections {
|
||||
// panic!(
|
||||
// indoc! {"
|
||||
#[track_caller]
|
||||
pub fn assert_editor_selections(&mut self, expected_selections: Vec<Range<usize>>) {
|
||||
let expected_marked_text =
|
||||
generate_marked_text(&self.buffer_text(), &expected_selections, true);
|
||||
self.assert_selections(expected_selections, expected_marked_text)
|
||||
}
|
||||
|
||||
// {}Editor has unexpected selections.
|
||||
#[track_caller]
|
||||
fn editor_selections(&mut self) -> Vec<Range<usize>> {
|
||||
self.editor
|
||||
.update(&mut self.cx, |editor, cx| {
|
||||
editor.selections.all::<usize>(cx)
|
||||
})
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
if s.reversed {
|
||||
s.end..s.start
|
||||
} else {
|
||||
s.start..s.end
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
// Expected selections:
|
||||
// {}
|
||||
#[track_caller]
|
||||
fn assert_selections(
|
||||
&mut self,
|
||||
expected_selections: Vec<Range<usize>>,
|
||||
expected_marked_text: String,
|
||||
) {
|
||||
let actual_selections = self.editor_selections();
|
||||
let actual_marked_text =
|
||||
generate_marked_text(&self.buffer_text(), &actual_selections, true);
|
||||
if expected_selections != actual_selections {
|
||||
panic!(
|
||||
indoc! {"
|
||||
|
||||
// Actual selections:
|
||||
// {}
|
||||
// "},
|
||||
// self.assertion_context(),
|
||||
// expected_marked_text,
|
||||
// actual_marked_text,
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// impl<'a> Deref for EditorTestContext<'a> {
|
||||
// type Target = gpui::TestAppContext;
|
||||
{}Editor has unexpected selections.
|
||||
|
||||
// fn deref(&self) -> &Self::Target {
|
||||
// self.cx
|
||||
// }
|
||||
// }
|
||||
Expected selections:
|
||||
{}
|
||||
|
||||
// impl<'a> DerefMut for EditorTestContext<'a> {
|
||||
// fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
// &mut self.cx
|
||||
// }
|
||||
// }
|
||||
Actual selections:
|
||||
{}
|
||||
"},
|
||||
self.assertion_context(),
|
||||
expected_marked_text,
|
||||
actual_marked_text,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for EditorTestContext<'a> {
|
||||
type Target = gpui::TestAppContext;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.cx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for EditorTestContext<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.cx
|
||||
}
|
||||
}
|
||||
|
||||
/// Tracks string context to be printed when assertions fail.
|
||||
/// Often this is done by storing a context string in the manager and returning the handle.
|
||||
#[derive(Clone)]
|
||||
pub struct AssertionContextManager {
|
||||
id: Arc<AtomicUsize>,
|
||||
contexts: Arc<RwLock<BTreeMap<usize, String>>>,
|
||||
}
|
||||
|
||||
impl AssertionContextManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: Arc::new(AtomicUsize::new(0)),
|
||||
contexts: Arc::new(RwLock::new(BTreeMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_context(&self, context: String) -> ContextHandle {
|
||||
let id = self.id.fetch_add(1, Ordering::Relaxed);
|
||||
let mut contexts = self.contexts.write();
|
||||
contexts.insert(id, context);
|
||||
ContextHandle {
|
||||
id,
|
||||
manager: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn context(&self) -> String {
|
||||
let contexts = self.contexts.read();
|
||||
format!("\n{}\n", contexts.values().join("\n"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to track the lifetime of a piece of context so that it can be provided when an assertion fails.
|
||||
/// For example, in the EditorTestContext, `set_state` returns a context handle so that if an assertion fails,
|
||||
/// the state that was set initially for the failure can be printed in the error message
|
||||
pub struct ContextHandle {
|
||||
id: usize,
|
||||
manager: AssertionContextManager,
|
||||
}
|
||||
|
||||
impl Drop for ContextHandle {
|
||||
fn drop(&mut self) {
|
||||
let mut contexts = self.manager.contexts.write();
|
||||
contexts.remove(&self.id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,8 +176,7 @@ macro_rules! actions {
|
|||
() => {};
|
||||
|
||||
( $name:ident ) => {
|
||||
#[gpui::register_action]
|
||||
#[derive(::std::clone::Clone, ::std::default::Default, ::std::fmt::Debug, ::std::cmp::PartialEq, $crate::serde::Deserialize)]
|
||||
#[gpui::action]
|
||||
pub struct $name;
|
||||
};
|
||||
|
||||
|
|
|
@ -1012,6 +1012,29 @@ impl Context for AppContext {
|
|||
let entity = self.entities.read(handle);
|
||||
read(entity, self)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let window = self
|
||||
.windows
|
||||
.get(window.id)
|
||||
.ok_or_else(|| anyhow!("window not found"))?
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
let root_view = window.root_view.clone().unwrap();
|
||||
let view = root_view
|
||||
.downcast::<T>()
|
||||
.map_err(|_| anyhow!("root view's type has changed"))?;
|
||||
|
||||
Ok(read(view, self))
|
||||
}
|
||||
}
|
||||
|
||||
/// These effects are processed at the end of each application update cycle.
|
||||
|
|
|
@ -66,6 +66,19 @@ impl Context for AsyncAppContext {
|
|||
let mut lock = app.borrow_mut();
|
||||
lock.update_window(window, f)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.upgrade().context("app was released")?;
|
||||
let lock = app.borrow();
|
||||
lock.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncAppContext {
|
||||
|
@ -250,6 +263,17 @@ impl Context for AsyncWindowContext {
|
|||
{
|
||||
self.app.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.app.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl VisualContext for AsyncWindowContext {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
AnyView, AnyWindowHandle, AppContext, AsyncAppContext, Context, Effect, Entity, EntityId,
|
||||
EventEmitter, Model, Subscription, Task, WeakModel, WindowContext,
|
||||
EventEmitter, Model, Subscription, Task, View, WeakModel, WindowContext, WindowHandle,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
|
@ -239,6 +239,17 @@ impl<'a, T> Context for ModelContext<'a, T> {
|
|||
{
|
||||
self.app.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn read_window<U, R>(
|
||||
&self,
|
||||
window: &WindowHandle<U>,
|
||||
read: impl FnOnce(View<U>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
U: 'static,
|
||||
{
|
||||
self.app.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Borrow<AppContext> for ModelContext<'_, T> {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::{
|
||||
AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor, Context,
|
||||
EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model, ModelContext,
|
||||
Render, Result, Task, TestDispatcher, TestPlatform, ViewContext, VisualContext, WindowContext,
|
||||
WindowHandle, WindowOptions,
|
||||
div, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext, BackgroundExecutor,
|
||||
Context, Div, EventEmitter, ForegroundExecutor, InputEvent, KeyDownEvent, Keystroke, Model,
|
||||
ModelContext, Render, Result, Task, TestDispatcher, TestPlatform, View, ViewContext,
|
||||
VisualContext, WindowContext, WindowHandle, WindowOptions,
|
||||
};
|
||||
use anyhow::{anyhow, bail};
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::{future::Future, rc::Rc, sync::Arc, time::Duration};
|
||||
use std::{future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TestAppContext {
|
||||
|
@ -58,6 +58,18 @@ impl Context for TestAppContext {
|
|||
let app = self.app.borrow();
|
||||
app.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
let app = self.app.borrow();
|
||||
app.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl TestAppContext {
|
||||
|
@ -93,8 +105,8 @@ impl TestAppContext {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn executor(&self) -> &BackgroundExecutor {
|
||||
&self.background_executor
|
||||
pub fn executor(&self) -> BackgroundExecutor {
|
||||
self.background_executor.clone()
|
||||
}
|
||||
|
||||
pub fn foreground_executor(&self) -> &ForegroundExecutor {
|
||||
|
@ -120,6 +132,26 @@ impl TestAppContext {
|
|||
cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window))
|
||||
}
|
||||
|
||||
pub fn add_empty_window(&mut self) -> AnyWindowHandle {
|
||||
let mut cx = self.app.borrow_mut();
|
||||
cx.open_window(WindowOptions::default(), |cx| {
|
||||
cx.build_view(|_| EmptyView {})
|
||||
})
|
||||
.any_handle
|
||||
}
|
||||
|
||||
pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, VisualTestContext)
|
||||
where
|
||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||
V: Render,
|
||||
{
|
||||
let mut cx = self.app.borrow_mut();
|
||||
let window = cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window));
|
||||
drop(cx);
|
||||
let view = window.root_view(self).unwrap();
|
||||
(view, VisualTestContext::from_window(*window.deref(), self))
|
||||
}
|
||||
|
||||
pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
|
||||
where
|
||||
Fut: Future<Output = R> + 'static,
|
||||
|
@ -146,6 +178,11 @@ impl TestAppContext {
|
|||
Some(read(lock.try_global()?, &lock))
|
||||
}
|
||||
|
||||
pub fn set_global<G: 'static>(&mut self, global: G) {
|
||||
let mut lock = self.app.borrow_mut();
|
||||
lock.set_global(global);
|
||||
}
|
||||
|
||||
pub fn update_global<G: 'static, R>(
|
||||
&mut self,
|
||||
update: impl FnOnce(&mut G, &mut AppContext) -> R,
|
||||
|
@ -259,3 +296,191 @@ impl<T: Send> Model<T> {
|
|||
.expect("model was dropped")
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> View<V> {
|
||||
pub fn condition<Evt>(
|
||||
&self,
|
||||
cx: &TestAppContext,
|
||||
mut predicate: impl FnMut(&V, &AppContext) -> bool,
|
||||
) -> impl Future<Output = ()>
|
||||
where
|
||||
Evt: 'static,
|
||||
V: EventEmitter<Evt>,
|
||||
{
|
||||
use postage::prelude::{Sink as _, Stream as _};
|
||||
|
||||
let (tx, mut rx) = postage::mpsc::channel(1024);
|
||||
let timeout_duration = Duration::from_millis(100); //todo!() cx.condition_duration();
|
||||
|
||||
let mut cx = cx.app.borrow_mut();
|
||||
let subscriptions = (
|
||||
cx.observe(self, {
|
||||
let mut tx = tx.clone();
|
||||
move |_, _| {
|
||||
tx.blocking_send(()).ok();
|
||||
}
|
||||
}),
|
||||
cx.subscribe(self, {
|
||||
let mut tx = tx.clone();
|
||||
move |_, _: &Evt, _| {
|
||||
tx.blocking_send(()).ok();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
let cx = cx.this.upgrade().unwrap();
|
||||
let handle = self.downgrade();
|
||||
|
||||
async move {
|
||||
crate::util::timeout(timeout_duration, async move {
|
||||
loop {
|
||||
{
|
||||
let cx = cx.borrow();
|
||||
let cx = &*cx;
|
||||
if predicate(
|
||||
handle
|
||||
.upgrade()
|
||||
.expect("view dropped with pending condition")
|
||||
.read(cx),
|
||||
cx,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// todo!(start_waiting)
|
||||
// cx.borrow().foreground_executor().start_waiting();
|
||||
rx.recv()
|
||||
.await
|
||||
.expect("view dropped with pending condition");
|
||||
// cx.borrow().foreground_executor().finish_waiting();
|
||||
}
|
||||
})
|
||||
.await
|
||||
.expect("condition timed out");
|
||||
drop(subscriptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use derive_more::{Deref, DerefMut};
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct VisualTestContext<'a> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
cx: &'a mut TestAppContext,
|
||||
window: AnyWindowHandle,
|
||||
}
|
||||
|
||||
impl<'a> VisualTestContext<'a> {
|
||||
pub fn from_window(window: AnyWindowHandle, cx: &'a mut TestAppContext) -> Self {
|
||||
Self { cx, window }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Context for VisualTestContext<'a> {
|
||||
type Result<T> = <TestAppContext as Context>::Result<T>;
|
||||
|
||||
fn build_model<T: 'static>(
|
||||
&mut self,
|
||||
build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
|
||||
) -> Self::Result<Model<T>> {
|
||||
self.cx.build_model(build_model)
|
||||
}
|
||||
|
||||
fn update_model<T, R>(
|
||||
&mut self,
|
||||
handle: &Model<T>,
|
||||
update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
|
||||
) -> Self::Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.update_model(handle, update)
|
||||
}
|
||||
|
||||
fn read_model<T, R>(
|
||||
&self,
|
||||
handle: &Model<T>,
|
||||
read: impl FnOnce(&T, &AppContext) -> R,
|
||||
) -> Self::Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
|
||||
{
|
||||
self.cx.update_window(window, f)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.cx.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VisualContext for VisualTestContext<'a> {
|
||||
fn build_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: 'static + Render,
|
||||
{
|
||||
self.window
|
||||
.update(self.cx, |_, cx| cx.build_view(build_view))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn update_view<V: 'static, R>(
|
||||
&mut self,
|
||||
view: &View<V>,
|
||||
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
|
||||
) -> Self::Result<R> {
|
||||
self.window
|
||||
.update(self.cx, |_, cx| cx.update_view(view, update))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn replace_root_view<V>(
|
||||
&mut self,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> Self::Result<View<V>>
|
||||
where
|
||||
V: Render,
|
||||
{
|
||||
self.window
|
||||
.update(self.cx, |_, cx| cx.replace_root_view(build_view))
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl AnyWindowHandle {
|
||||
pub fn build_view<V: Render + 'static>(
|
||||
&self,
|
||||
cx: &mut TestAppContext,
|
||||
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
|
||||
) -> View<V> {
|
||||
self.update(cx, |_, cx| cx.build_view(build_view)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EmptyView {}
|
||||
|
||||
impl Render for EmptyView {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> Self::Element {
|
||||
div()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -167,7 +167,7 @@ impl TryFrom<&'_ str> for Rgba {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Default, Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct Hsla {
|
||||
pub h: f32,
|
||||
|
@ -176,10 +176,63 @@ pub struct Hsla {
|
|||
pub a: f32,
|
||||
}
|
||||
|
||||
impl PartialEq for Hsla {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.h
|
||||
.total_cmp(&other.h)
|
||||
.then(self.s.total_cmp(&other.s))
|
||||
.then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a)))
|
||||
.is_eq()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Hsla {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// SAFETY: The total ordering relies on this always being Some()
|
||||
Some(
|
||||
self.h
|
||||
.total_cmp(&other.h)
|
||||
.then(self.s.total_cmp(&other.s))
|
||||
.then(self.l.total_cmp(&other.l).then(self.a.total_cmp(&other.a))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Hsla {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
// SAFETY: The partial comparison is a total comparison
|
||||
unsafe { self.partial_cmp(other).unwrap_unchecked() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Hsla {
|
||||
pub fn to_rgb(self) -> Rgba {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn red() -> Self {
|
||||
red()
|
||||
}
|
||||
|
||||
pub fn green() -> Self {
|
||||
green()
|
||||
}
|
||||
|
||||
pub fn blue() -> Self {
|
||||
blue()
|
||||
}
|
||||
|
||||
pub fn black() -> Self {
|
||||
black()
|
||||
}
|
||||
|
||||
pub fn white() -> Self {
|
||||
white()
|
||||
}
|
||||
|
||||
pub fn transparent_black() -> Self {
|
||||
transparent_black()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Hsla {}
|
||||
|
|
|
@ -81,6 +81,7 @@ impl<V: 'static> Element<V> for Text<V> {
|
|||
let text = self.text.clone();
|
||||
|
||||
let rem_size = cx.rem_size();
|
||||
|
||||
let layout_id = cx.request_measured_layout(Default::default(), rem_size, {
|
||||
let element_state = element_state.clone();
|
||||
move |known_dimensions, _| {
|
||||
|
@ -93,6 +94,10 @@ impl<V: 'static> Element<V> for Text<V> {
|
|||
)
|
||||
.log_err()
|
||||
else {
|
||||
element_state.lock().replace(TextElementState {
|
||||
lines: Default::default(),
|
||||
line_height,
|
||||
});
|
||||
return Size::default();
|
||||
};
|
||||
|
||||
|
@ -131,7 +136,8 @@ impl<V: 'static> Element<V> for Text<V> {
|
|||
let element_state = element_state.lock();
|
||||
let element_state = element_state
|
||||
.as_ref()
|
||||
.expect("measurement has not been performed");
|
||||
.ok_or_else(|| anyhow::anyhow!("measurement has not been performed on {}", &self.text))
|
||||
.unwrap();
|
||||
|
||||
let line_height = element_state.line_height;
|
||||
let mut line_origin = bounds.origin;
|
||||
|
|
|
@ -105,6 +105,14 @@ pub trait Context {
|
|||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
|
||||
where
|
||||
F: FnOnce(AnyView, &mut WindowContext<'_>) -> T;
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static;
|
||||
}
|
||||
|
||||
pub trait VisualContext: Context {
|
||||
|
|
|
@ -45,7 +45,7 @@ impl<V: 'static> ElementInputHandler<V> {
|
|||
/// containing view.
|
||||
pub fn new(element_bounds: Bounds<Pixels>, cx: &mut ViewContext<V>) -> Self {
|
||||
ElementInputHandler {
|
||||
view: cx.view(),
|
||||
view: cx.view().clone(),
|
||||
element_bounds,
|
||||
cx: cx.to_async(),
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{
|
||||
build_action_from_type, Action, Bounds, DispatchPhase, Element, FocusEvent, FocusHandle,
|
||||
FocusId, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent, Pixels,
|
||||
Style, StyleRefinement, ViewContext, WindowContext,
|
||||
FocusId, KeyBinding, KeyContext, KeyMatch, Keymap, Keystroke, KeystrokeMatcher, MouseDownEvent,
|
||||
Pixels, Style, StyleRefinement, ViewContext, WindowContext,
|
||||
};
|
||||
use collections::HashMap;
|
||||
use parking_lot::Mutex;
|
||||
|
@ -145,6 +145,15 @@ impl DispatchTree {
|
|||
actions
|
||||
}
|
||||
|
||||
pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
|
||||
self.keymap
|
||||
.lock()
|
||||
.bindings_for_action(action.type_id())
|
||||
.filter(|candidate| candidate.action.partial_eq(action))
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn dispatch_key(
|
||||
&mut self,
|
||||
keystroke: &Keystroke,
|
||||
|
|
|
@ -3,9 +3,19 @@ use anyhow::Result;
|
|||
use smallvec::SmallVec;
|
||||
|
||||
pub struct KeyBinding {
|
||||
action: Box<dyn Action>,
|
||||
pub(super) keystrokes: SmallVec<[Keystroke; 2]>,
|
||||
pub(super) context_predicate: Option<KeyBindingContextPredicate>,
|
||||
pub(crate) action: Box<dyn Action>,
|
||||
pub(crate) keystrokes: SmallVec<[Keystroke; 2]>,
|
||||
pub(crate) context_predicate: Option<KeyBindingContextPredicate>,
|
||||
}
|
||||
|
||||
impl Clone for KeyBinding {
|
||||
fn clone(&self) -> Self {
|
||||
KeyBinding {
|
||||
action: self.action.boxed_clone(),
|
||||
keystrokes: self.keystrokes.clone(),
|
||||
context_predicate: self.context_predicate.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyBinding {
|
||||
|
|
|
@ -8,7 +8,8 @@ use parking_lot::Mutex;
|
|||
|
||||
use crate::{
|
||||
px, AtlasKey, AtlasTextureId, AtlasTile, Pixels, PlatformAtlas, PlatformDisplay,
|
||||
PlatformWindow, Point, Scene, Size, TileId, WindowAppearance, WindowBounds, WindowOptions,
|
||||
PlatformInputHandler, PlatformWindow, Point, Scene, Size, TileId, WindowAppearance,
|
||||
WindowBounds, WindowOptions,
|
||||
};
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -23,6 +24,7 @@ pub struct TestWindow {
|
|||
bounds: WindowBounds,
|
||||
current_scene: Mutex<Option<Scene>>,
|
||||
display: Rc<dyn PlatformDisplay>,
|
||||
input_handler: Option<Box<dyn PlatformInputHandler>>,
|
||||
|
||||
handlers: Mutex<Handlers>,
|
||||
sprite_atlas: Arc<dyn PlatformAtlas>,
|
||||
|
@ -33,7 +35,7 @@ impl TestWindow {
|
|||
bounds: options.bounds,
|
||||
current_scene: Default::default(),
|
||||
display,
|
||||
|
||||
input_handler: None,
|
||||
sprite_atlas: Arc::new(TestAtlas::new()),
|
||||
handlers: Default::default(),
|
||||
}
|
||||
|
@ -77,8 +79,8 @@ impl PlatformWindow for TestWindow {
|
|||
todo!()
|
||||
}
|
||||
|
||||
fn set_input_handler(&mut self, _input_handler: Box<dyn crate::PlatformInputHandler>) {
|
||||
todo!()
|
||||
fn set_input_handler(&mut self, input_handler: Box<dyn crate::PlatformInputHandler>) {
|
||||
self.input_handler = Some(input_handler);
|
||||
}
|
||||
|
||||
fn prompt(
|
||||
|
|
|
@ -54,9 +54,9 @@ impl LineLayout {
|
|||
pub fn closest_index_for_x(&self, x: Pixels) -> usize {
|
||||
let mut prev_index = 0;
|
||||
let mut prev_x = px(0.);
|
||||
|
||||
for run in self.runs.iter() {
|
||||
for glyph in run.glyphs.iter() {
|
||||
glyph.index;
|
||||
if glyph.position.x >= x {
|
||||
if glyph.position.x - x < x - prev_x {
|
||||
return glyph.index;
|
||||
|
@ -68,7 +68,7 @@ impl LineLayout {
|
|||
prev_x = glyph.position.x;
|
||||
}
|
||||
}
|
||||
prev_index
|
||||
prev_index + 1
|
||||
}
|
||||
|
||||
pub fn x_for_index(&self, index: usize) -> Pixels {
|
||||
|
|
|
@ -1,16 +1,26 @@
|
|||
#[cfg(any(test, feature = "test-support"))]
|
||||
use std::time::Duration;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use futures::Future;
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
use smol::future::FutureExt;
|
||||
|
||||
pub use util::*;
|
||||
|
||||
// pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
|
||||
// where
|
||||
// F: Future<Output = T>,
|
||||
// {
|
||||
// let timer = async {
|
||||
// smol::Timer::after(timeout).await;
|
||||
// Err(())
|
||||
// };
|
||||
// let future = async move { Ok(f.await) };
|
||||
// timer.race(future).await
|
||||
// }
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
|
||||
where
|
||||
F: Future<Output = T>,
|
||||
{
|
||||
let timer = async {
|
||||
smol::Timer::after(timeout).await;
|
||||
Err(())
|
||||
};
|
||||
let future = async move { Ok(f.await) };
|
||||
timer.race(future).await
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);
|
||||
|
|
|
@ -3,15 +3,15 @@ use crate::{
|
|||
AsyncWindowContext, AvailableSpace, Bounds, BoxShadow, Context, Corners, CursorStyle,
|
||||
DevicePixels, DispatchNodeId, DispatchTree, DisplayId, Edges, Effect, Entity, EntityId,
|
||||
EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
|
||||
InputEvent, IsZero, KeyContext, KeyDownEvent, LayoutId, Model, ModelContext, Modifiers,
|
||||
MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
|
||||
PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite,
|
||||
PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels,
|
||||
SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet, Subscription,
|
||||
TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakView,
|
||||
WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, LayoutId, Model, ModelContext,
|
||||
Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path,
|
||||
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
|
||||
PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
|
||||
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
|
||||
Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
|
||||
WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use futures::{
|
||||
|
@ -1377,6 +1377,13 @@ impl<'a> WindowContext<'a> {
|
|||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
|
||||
self.window
|
||||
.current_frame
|
||||
.dispatch_tree
|
||||
.bindings_for_action(action)
|
||||
}
|
||||
}
|
||||
|
||||
impl Context for WindowContext<'_> {
|
||||
|
@ -1431,6 +1438,28 @@ impl Context for WindowContext<'_> {
|
|||
let entity = self.entities.read(handle);
|
||||
read(&*entity, &*self.app)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
if window.any_handle == self.window.handle {
|
||||
let root_view = self
|
||||
.window
|
||||
.root_view
|
||||
.clone()
|
||||
.unwrap()
|
||||
.downcast::<T>()
|
||||
.map_err(|_| anyhow!("the type of the window's root view has changed"))?;
|
||||
Ok(read(root_view, self))
|
||||
} else {
|
||||
self.app.read_window(window, read)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VisualContext for WindowContext<'_> {
|
||||
|
@ -1737,9 +1766,12 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||
}
|
||||
}
|
||||
|
||||
// todo!("change this to return a reference");
|
||||
pub fn view(&self) -> View<V> {
|
||||
self.view.clone()
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.view.entity_id()
|
||||
}
|
||||
|
||||
pub fn view(&self) -> &View<V> {
|
||||
self.view
|
||||
}
|
||||
|
||||
pub fn model(&self) -> Model<V> {
|
||||
|
@ -1762,7 +1794,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||
where
|
||||
V: 'static,
|
||||
{
|
||||
let view = self.view();
|
||||
let view = self.view().clone();
|
||||
self.window_cx.on_next_frame(move |cx| view.update(cx, f));
|
||||
}
|
||||
|
||||
|
@ -2094,7 +2126,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let handle = self.view();
|
||||
let handle = self.view().clone();
|
||||
self.window_cx.on_mouse_event(move |event, phase, cx| {
|
||||
handle.update(cx, |view, cx| {
|
||||
handler(view, event, phase, cx);
|
||||
|
@ -2106,7 +2138,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||
&mut self,
|
||||
handler: impl Fn(&mut V, &Event, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let handle = self.view();
|
||||
let handle = self.view().clone();
|
||||
self.window_cx.on_key_event(move |event, phase, cx| {
|
||||
handle.update(cx, |view, cx| {
|
||||
handler(view, event, phase, cx);
|
||||
|
@ -2119,7 +2151,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
|||
action_type: TypeId,
|
||||
handler: impl Fn(&mut V, &dyn Any, DispatchPhase, &mut ViewContext<V>) + 'static,
|
||||
) {
|
||||
let handle = self.view();
|
||||
let handle = self.view().clone();
|
||||
self.window_cx
|
||||
.on_action(action_type, move |action, phase, cx| {
|
||||
handle.update(cx, |view, cx| {
|
||||
|
@ -2194,6 +2226,17 @@ impl<V> Context for ViewContext<'_, V> {
|
|||
{
|
||||
self.window_cx.read_model(handle, read)
|
||||
}
|
||||
|
||||
fn read_window<T, R>(
|
||||
&self,
|
||||
window: &WindowHandle<T>,
|
||||
read: impl FnOnce(View<T>, &AppContext) -> R,
|
||||
) -> Result<R>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
self.window_cx.read_window(window, read)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> VisualContext for ViewContext<'_, V> {
|
||||
|
@ -2266,7 +2309,7 @@ impl<V: 'static + Render> WindowHandle<V> {
|
|||
}
|
||||
|
||||
pub fn update<C, R>(
|
||||
self,
|
||||
&self,
|
||||
cx: &mut C,
|
||||
update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
|
||||
) -> Result<R>
|
||||
|
@ -2280,6 +2323,36 @@ impl<V: 'static + Render> WindowHandle<V> {
|
|||
Ok(cx.update_view(&view, update))
|
||||
})?
|
||||
}
|
||||
|
||||
pub fn read<'a>(&self, cx: &'a AppContext) -> Result<&'a V> {
|
||||
let x = cx
|
||||
.windows
|
||||
.get(self.id)
|
||||
.and_then(|window| {
|
||||
window
|
||||
.as_ref()
|
||||
.and_then(|window| window.root_view.clone())
|
||||
.map(|root_view| root_view.downcast::<V>())
|
||||
})
|
||||
.ok_or_else(|| anyhow!("window not found"))?
|
||||
.map_err(|_| anyhow!("the type of the window's root view has changed"))?;
|
||||
|
||||
Ok(x.read(cx))
|
||||
}
|
||||
|
||||
pub fn read_with<C, R>(&self, cx: &C, read_with: impl FnOnce(&V, &AppContext) -> R) -> Result<R>
|
||||
where
|
||||
C: Context,
|
||||
{
|
||||
cx.read_window(self, |root_view, cx| read_with(root_view.read(cx), cx))
|
||||
}
|
||||
|
||||
pub fn root_view<C>(&self, cx: &C) -> Result<View<V>>
|
||||
where
|
||||
C: Context,
|
||||
{
|
||||
cx.read_window(self, |root_view, _cx| root_view.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> Copy for WindowHandle<V> {}
|
||||
|
@ -2345,6 +2418,18 @@ impl AnyWindowHandle {
|
|||
{
|
||||
cx.update_window(self, update)
|
||||
}
|
||||
|
||||
pub fn read<T, C, R>(self, cx: &C, read: impl FnOnce(View<T>, &AppContext) -> R) -> Result<R>
|
||||
where
|
||||
C: Context,
|
||||
T: 'static,
|
||||
{
|
||||
let view = self
|
||||
.downcast::<T>()
|
||||
.context("the type of the window's root view has changed")?;
|
||||
|
||||
cx.read_window(&view, read)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
|
|
@ -34,13 +34,21 @@ pub fn action(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
let visibility = input.vis;
|
||||
|
||||
let output = match input.data {
|
||||
syn::Data::Struct(ref struct_data) => {
|
||||
syn::Data::Struct(ref struct_data) => match &struct_data.fields {
|
||||
syn::Fields::Named(_) | syn::Fields::Unnamed(_) => {
|
||||
let fields = &struct_data.fields;
|
||||
quote! {
|
||||
#attributes
|
||||
#visibility struct #name #fields
|
||||
}
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
quote! {
|
||||
#attributes
|
||||
#visibility struct #name;
|
||||
}
|
||||
}
|
||||
},
|
||||
syn::Data::Enum(ref enum_data) => {
|
||||
let variants = &enum_data.variants;
|
||||
quote! {
|
||||
|
|
|
@ -1858,7 +1858,7 @@ mod tests {
|
|||
async fn test_first_line_pattern(cx: &mut TestAppContext) {
|
||||
let mut languages = LanguageRegistry::test();
|
||||
|
||||
languages.set_executor(cx.executor().clone());
|
||||
languages.set_executor(cx.executor());
|
||||
let languages = Arc::new(languages);
|
||||
languages.register(
|
||||
"/javascript",
|
||||
|
@ -1895,7 +1895,7 @@ mod tests {
|
|||
#[gpui::test(iterations = 10)]
|
||||
async fn test_language_loading(cx: &mut TestAppContext) {
|
||||
let mut languages = LanguageRegistry::test();
|
||||
languages.set_executor(cx.executor().clone());
|
||||
languages.set_executor(cx.executor());
|
||||
let languages = Arc::new(languages);
|
||||
languages.register(
|
||||
"/JSON",
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
use gpui::actions;
|
||||
|
||||
// todo!(remove this)
|
||||
// If the zed binary doesn't use anything in this crate, it will be optimized away
|
||||
// and the actions won't initialize. So we just provide an empty initialization function
|
||||
// to be called from main.
|
||||
//
|
||||
// These may provide relevant context:
|
||||
// https://github.com/rust-lang/rust/issues/47384
|
||||
// https://github.com/mmastrac/rust-ctor/issues/280
|
||||
pub fn unused() {}
|
||||
pub fn init() {}
|
||||
|
||||
actions!(
|
||||
Cancel,
|
||||
|
|
|
@ -497,7 +497,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_prettier_lookup_finds_nothing(cx: &mut gpui::TestAppContext) {
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
@ -573,7 +573,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_prettier_lookup_in_simple_npm_projects(cx: &mut gpui::TestAppContext) {
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
@ -638,7 +638,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_prettier_lookup_for_not_installed(cx: &mut gpui::TestAppContext) {
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
@ -731,7 +731,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_prettier_lookup_in_npm_workspaces(cx: &mut gpui::TestAppContext) {
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
@ -812,7 +812,7 @@ mod tests {
|
|||
async fn test_prettier_lookup_in_npm_workspaces_for_not_installed(
|
||||
cx: &mut gpui::TestAppContext,
|
||||
) {
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
|
|
@ -863,7 +863,7 @@ impl Project {
|
|||
cx: &mut gpui::TestAppContext,
|
||||
) -> Model<Project> {
|
||||
let mut languages = LanguageRegistry::test();
|
||||
languages.set_executor(cx.executor().clone());
|
||||
languages.set_executor(cx.executor());
|
||||
let http_client = util::http::FakeHttpClient::with_404_response();
|
||||
let client = cx.update(|cx| client::Client::new(http_client.clone(), cx));
|
||||
let user_store = cx.build_model(|cx| UserStore::new(client.clone(), http_client, cx));
|
||||
|
|
|
@ -89,7 +89,7 @@ async fn test_symlinks(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
json!({
|
||||
|
@ -189,7 +189,7 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
json!({
|
||||
|
@ -547,7 +547,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
json!({
|
||||
|
@ -734,7 +734,7 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -826,7 +826,7 @@ async fn test_single_file_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_hidden_worktrees_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/root",
|
||||
json!({
|
||||
|
@ -914,7 +914,7 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -1046,7 +1046,7 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
|
@ -1125,7 +1125,7 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "x" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
|
@ -1215,7 +1215,7 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
|
@ -1279,7 +1279,7 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
|
||||
.await;
|
||||
|
||||
|
@ -1401,7 +1401,7 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
"
|
||||
.unindent();
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
|
@ -1671,7 +1671,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
|
|||
"let three = 3;\n",
|
||||
);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
|
@ -1734,7 +1734,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "one two three" }))
|
||||
.await;
|
||||
|
||||
|
@ -1813,7 +1813,7 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
|
|||
"
|
||||
.unindent();
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -1959,7 +1959,7 @@ async fn test_edits_from_lsp2_with_edits_on_adjacent_lines(cx: &mut gpui::TestAp
|
|||
"
|
||||
.unindent();
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2067,7 +2067,7 @@ async fn test_invalid_edits_from_lsp2(cx: &mut gpui::TestAppContext) {
|
|||
"
|
||||
.unindent();
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2187,7 +2187,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
|||
);
|
||||
let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2299,7 +2299,7 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2396,7 +2396,7 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2451,7 +2451,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
|||
);
|
||||
let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2559,7 +2559,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_save_file(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2591,7 +2591,7 @@ async fn test_save_file(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2622,7 +2622,7 @@ async fn test_save_in_single_file_worktree(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_save_as(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({})).await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
|
@ -2830,7 +2830,7 @@ async fn test_rescan_and_remote_updates(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2881,7 +2881,7 @@ async fn test_buffer_identity_across_renames(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -2927,7 +2927,7 @@ async fn test_buffer_deduping(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_buffer_is_dirty(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3074,7 +3074,7 @@ 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.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3154,7 +3154,7 @@ async fn test_buffer_file_changes_on_disk(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3216,7 +3216,7 @@ async fn test_buffer_line_endings(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-dir",
|
||||
json!({
|
||||
|
@ -3479,7 +3479,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
|||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3596,7 +3596,7 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_search(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3655,7 +3655,7 @@ async fn test_search_with_inclusions(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3767,7 +3767,7 @@ async fn test_search_with_exclusions(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
@ -3878,7 +3878,7 @@ async fn test_search_with_exclusions_and_inclusions(cx: &mut gpui::TestAppContex
|
|||
|
||||
let search_query = "file";
|
||||
|
||||
let fs = FakeFs::new(cx.executor().clone());
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
json!({
|
||||
|
|
|
@ -577,18 +577,18 @@ mod tests {
|
|||
let client2 = Peer::new(0);
|
||||
|
||||
let (client1_to_server_conn, server_to_client_1_conn, _kill) =
|
||||
Connection::in_memory(cx.executor().clone());
|
||||
Connection::in_memory(cx.executor());
|
||||
let (client1_conn_id, io_task1, client1_incoming) =
|
||||
client1.add_test_connection(client1_to_server_conn, cx.executor().clone());
|
||||
client1.add_test_connection(client1_to_server_conn, cx.executor());
|
||||
let (_, io_task2, server_incoming1) =
|
||||
server.add_test_connection(server_to_client_1_conn, cx.executor().clone());
|
||||
server.add_test_connection(server_to_client_1_conn, cx.executor());
|
||||
|
||||
let (client2_to_server_conn, server_to_client_2_conn, _kill) =
|
||||
Connection::in_memory(cx.executor().clone());
|
||||
Connection::in_memory(cx.executor());
|
||||
let (client2_conn_id, io_task3, client2_incoming) =
|
||||
client2.add_test_connection(client2_to_server_conn, cx.executor().clone());
|
||||
client2.add_test_connection(client2_to_server_conn, cx.executor());
|
||||
let (_, io_task4, server_incoming2) =
|
||||
server.add_test_connection(server_to_client_2_conn, cx.executor().clone());
|
||||
server.add_test_connection(server_to_client_2_conn, cx.executor());
|
||||
|
||||
executor.spawn(io_task1).detach();
|
||||
executor.spawn(io_task2).detach();
|
||||
|
@ -763,16 +763,16 @@ mod tests {
|
|||
|
||||
#[gpui::test(iterations = 50)]
|
||||
async fn test_dropping_request_before_completion(cx: &mut TestAppContext) {
|
||||
let executor = cx.executor().clone();
|
||||
let executor = cx.executor();
|
||||
let server = Peer::new(0);
|
||||
let client = Peer::new(0);
|
||||
|
||||
let (client_to_server_conn, server_to_client_conn, _kill) =
|
||||
Connection::in_memory(cx.executor().clone());
|
||||
Connection::in_memory(cx.executor());
|
||||
let (client_to_server_conn_id, io_task1, mut client_incoming) =
|
||||
client.add_test_connection(client_to_server_conn, cx.executor().clone());
|
||||
client.add_test_connection(client_to_server_conn, cx.executor());
|
||||
let (server_to_client_conn_id, io_task2, mut server_incoming) =
|
||||
server.add_test_connection(server_to_client_conn, cx.executor().clone());
|
||||
server.add_test_connection(server_to_client_conn, cx.executor());
|
||||
|
||||
executor.spawn(io_task1).detach();
|
||||
executor.spawn(io_task2).detach();
|
||||
|
|
|
@ -48,7 +48,7 @@ fn main() {
|
|||
let args = Args::parse();
|
||||
|
||||
let story_selector = args.story.clone();
|
||||
let theme_name = args.theme.unwrap_or("Zed Pro Moonlight".to_string());
|
||||
let theme_name = args.theme.unwrap_or("One Dark".to_string());
|
||||
|
||||
let asset_source = Arc::new(Assets);
|
||||
gpui::App::production(asset_source).run(move |cx| {
|
||||
|
|
|
@ -1,261 +1,15 @@
|
|||
use gpui::{hsla, Hsla, Rgba};
|
||||
use gpui::{Hsla, Rgba};
|
||||
|
||||
use crate::colors::{StatusColors, SystemColors, ThemeColors};
|
||||
use crate::scale::{ColorScaleSet, ColorScales};
|
||||
use crate::syntax::SyntaxTheme;
|
||||
use crate::{ColorScale, PlayerColor, PlayerColors};
|
||||
use crate::ColorScale;
|
||||
use crate::{SystemColors, ThemeColors};
|
||||
|
||||
impl Default for PlayerColors {
|
||||
fn default() -> Self {
|
||||
Self(vec![
|
||||
PlayerColor {
|
||||
cursor: blue().dark().step_9(),
|
||||
background: blue().dark().step_5(),
|
||||
selection: blue().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: orange().dark().step_9(),
|
||||
background: orange().dark().step_5(),
|
||||
selection: orange().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: pink().dark().step_9(),
|
||||
background: pink().dark().step_5(),
|
||||
selection: pink().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: lime().dark().step_9(),
|
||||
background: lime().dark().step_5(),
|
||||
selection: lime().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: purple().dark().step_9(),
|
||||
background: purple().dark().step_5(),
|
||||
selection: purple().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: amber().dark().step_9(),
|
||||
background: amber().dark().step_5(),
|
||||
selection: amber().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: jade().dark().step_9(),
|
||||
background: jade().dark().step_5(),
|
||||
selection: jade().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: red().dark().step_9(),
|
||||
background: red().dark().step_5(),
|
||||
selection: red().dark().step_3(),
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerColors {
|
||||
pub fn default_light() -> Self {
|
||||
Self(vec![
|
||||
PlayerColor {
|
||||
cursor: blue().light().step_9(),
|
||||
background: blue().light().step_4(),
|
||||
selection: blue().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: orange().light().step_9(),
|
||||
background: orange().light().step_4(),
|
||||
selection: orange().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: pink().light().step_9(),
|
||||
background: pink().light().step_4(),
|
||||
selection: pink().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: lime().light().step_9(),
|
||||
background: lime().light().step_4(),
|
||||
selection: lime().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: purple().light().step_9(),
|
||||
background: purple().light().step_4(),
|
||||
selection: purple().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: amber().light().step_9(),
|
||||
background: amber().light().step_4(),
|
||||
selection: amber().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: jade().light().step_9(),
|
||||
background: jade().light().step_4(),
|
||||
selection: jade().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: red().light().step_9(),
|
||||
background: red().light().step_4(),
|
||||
selection: red().light().step_3(),
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
fn neutral() -> ColorScaleSet {
|
||||
pub(crate) fn neutral() -> ColorScaleSet {
|
||||
slate()
|
||||
}
|
||||
|
||||
impl Default for SystemColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transparent: hsla(0.0, 0.0, 0.0, 0.0),
|
||||
mac_os_traffic_light_red: hsla(0.0139, 0.79, 0.65, 1.0),
|
||||
mac_os_traffic_light_yellow: hsla(0.114, 0.88, 0.63, 1.0),
|
||||
mac_os_traffic_light_green: hsla(0.313, 0.49, 0.55, 1.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StatusColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
conflict: red().dark().step_9(),
|
||||
created: grass().dark().step_9(),
|
||||
deleted: red().dark().step_9(),
|
||||
error: red().dark().step_9(),
|
||||
hidden: neutral().dark().step_9(),
|
||||
ignored: neutral().dark().step_9(),
|
||||
info: blue().dark().step_9(),
|
||||
modified: yellow().dark().step_9(),
|
||||
renamed: blue().dark().step_9(),
|
||||
success: grass().dark().step_9(),
|
||||
warning: yellow().dark().step_9(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyntaxTheme {
|
||||
pub fn default_light() -> Self {
|
||||
Self {
|
||||
highlights: vec![
|
||||
("attribute".into(), cyan().light().step_11().into()),
|
||||
("boolean".into(), tomato().light().step_11().into()),
|
||||
("comment".into(), neutral().light().step_11().into()),
|
||||
("comment.doc".into(), iris().light().step_12().into()),
|
||||
("constant".into(), red().light().step_9().into()),
|
||||
("constructor".into(), red().light().step_9().into()),
|
||||
("embedded".into(), red().light().step_9().into()),
|
||||
("emphasis".into(), red().light().step_9().into()),
|
||||
("emphasis.strong".into(), red().light().step_9().into()),
|
||||
("enum".into(), red().light().step_9().into()),
|
||||
("function".into(), red().light().step_9().into()),
|
||||
("hint".into(), red().light().step_9().into()),
|
||||
("keyword".into(), orange().light().step_11().into()),
|
||||
("label".into(), red().light().step_9().into()),
|
||||
("link_text".into(), red().light().step_9().into()),
|
||||
("link_uri".into(), red().light().step_9().into()),
|
||||
("number".into(), red().light().step_9().into()),
|
||||
("operator".into(), red().light().step_9().into()),
|
||||
("predictive".into(), red().light().step_9().into()),
|
||||
("preproc".into(), red().light().step_9().into()),
|
||||
("primary".into(), red().light().step_9().into()),
|
||||
("property".into(), red().light().step_9().into()),
|
||||
("punctuation".into(), neutral().light().step_11().into()),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
neutral().light().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
neutral().light().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
blue().light().step_11().into(),
|
||||
),
|
||||
("punctuation.special".into(), red().light().step_9().into()),
|
||||
("string".into(), jade().light().step_11().into()),
|
||||
("string.escape".into(), red().light().step_9().into()),
|
||||
("string.regex".into(), tomato().light().step_11().into()),
|
||||
("string.special".into(), red().light().step_9().into()),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
red().light().step_9().into(),
|
||||
),
|
||||
("tag".into(), red().light().step_9().into()),
|
||||
("text.literal".into(), red().light().step_9().into()),
|
||||
("title".into(), red().light().step_9().into()),
|
||||
("type".into(), red().light().step_9().into()),
|
||||
("variable".into(), red().light().step_9().into()),
|
||||
("variable.special".into(), red().light().step_9().into()),
|
||||
("variant".into(), red().light().step_9().into()),
|
||||
],
|
||||
inlay_style: tomato().light().step_1().into(), // todo!("nate: use a proper style")
|
||||
suggestion_style: orange().light().step_1().into(), // todo!("nate: use proper style")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_dark() -> Self {
|
||||
Self {
|
||||
highlights: vec![
|
||||
("attribute".into(), tomato().dark().step_11().into()),
|
||||
("boolean".into(), tomato().dark().step_11().into()),
|
||||
("comment".into(), neutral().dark().step_11().into()),
|
||||
("comment.doc".into(), iris().dark().step_12().into()),
|
||||
("constant".into(), orange().dark().step_11().into()),
|
||||
("constructor".into(), gold().dark().step_11().into()),
|
||||
("embedded".into(), red().dark().step_11().into()),
|
||||
("emphasis".into(), red().dark().step_11().into()),
|
||||
("emphasis.strong".into(), red().dark().step_11().into()),
|
||||
("enum".into(), yellow().dark().step_11().into()),
|
||||
("function".into(), blue().dark().step_11().into()),
|
||||
("hint".into(), indigo().dark().step_11().into()),
|
||||
("keyword".into(), plum().dark().step_11().into()),
|
||||
("label".into(), red().dark().step_11().into()),
|
||||
("link_text".into(), red().dark().step_11().into()),
|
||||
("link_uri".into(), red().dark().step_11().into()),
|
||||
("number".into(), red().dark().step_11().into()),
|
||||
("operator".into(), red().dark().step_11().into()),
|
||||
("predictive".into(), red().dark().step_11().into()),
|
||||
("preproc".into(), red().dark().step_11().into()),
|
||||
("primary".into(), red().dark().step_11().into()),
|
||||
("property".into(), red().dark().step_11().into()),
|
||||
("punctuation".into(), neutral().dark().step_11().into()),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
neutral().dark().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
neutral().dark().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
blue().dark().step_11().into(),
|
||||
),
|
||||
("punctuation.special".into(), red().dark().step_11().into()),
|
||||
("string".into(), lime().dark().step_11().into()),
|
||||
("string.escape".into(), orange().dark().step_11().into()),
|
||||
("string.regex".into(), tomato().dark().step_11().into()),
|
||||
("string.special".into(), red().dark().step_11().into()),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
red().dark().step_11().into(),
|
||||
),
|
||||
("tag".into(), red().dark().step_11().into()),
|
||||
("text.literal".into(), purple().dark().step_11().into()),
|
||||
("title".into(), sky().dark().step_11().into()),
|
||||
("type".into(), mint().dark().step_11().into()),
|
||||
("variable".into(), red().dark().step_11().into()),
|
||||
("variable.special".into(), red().dark().step_11().into()),
|
||||
("variant".into(), red().dark().step_11().into()),
|
||||
],
|
||||
inlay_style: neutral().dark().step_11().into(), // todo!("nate: use a proper style")
|
||||
suggestion_style: orange().dark().step_11().into(), // todo!("nate: use a proper style")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ThemeColors {
|
||||
pub fn default_light() -> Self {
|
||||
pub fn light() -> Self {
|
||||
let system = SystemColors::default();
|
||||
|
||||
Self {
|
||||
|
@ -327,7 +81,7 @@ impl ThemeColors {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn default_dark() -> Self {
|
||||
pub fn dark() -> Self {
|
||||
let system = SystemColors::default();
|
||||
|
||||
Self {
|
||||
|
@ -470,7 +224,7 @@ pub fn default_color_scales() -> ColorScales {
|
|||
}
|
||||
}
|
||||
|
||||
fn gray() -> ColorScaleSet {
|
||||
pub(crate) fn gray() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Gray",
|
||||
light: [
|
||||
|
@ -534,7 +288,7 @@ fn gray() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn mauve() -> ColorScaleSet {
|
||||
pub(crate) fn mauve() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Mauve",
|
||||
light: [
|
||||
|
@ -598,7 +352,7 @@ fn mauve() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn slate() -> ColorScaleSet {
|
||||
pub(crate) fn slate() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Slate",
|
||||
light: [
|
||||
|
@ -662,7 +416,7 @@ fn slate() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn sage() -> ColorScaleSet {
|
||||
pub(crate) fn sage() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Sage",
|
||||
light: [
|
||||
|
@ -726,7 +480,7 @@ fn sage() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn olive() -> ColorScaleSet {
|
||||
pub(crate) fn olive() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Olive",
|
||||
light: [
|
||||
|
@ -790,7 +544,7 @@ fn olive() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn sand() -> ColorScaleSet {
|
||||
pub(crate) fn sand() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Sand",
|
||||
light: [
|
||||
|
@ -854,7 +608,7 @@ fn sand() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn gold() -> ColorScaleSet {
|
||||
pub(crate) fn gold() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Gold",
|
||||
light: [
|
||||
|
@ -918,7 +672,7 @@ fn gold() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn bronze() -> ColorScaleSet {
|
||||
pub(crate) fn bronze() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Bronze",
|
||||
light: [
|
||||
|
@ -982,7 +736,7 @@ fn bronze() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn brown() -> ColorScaleSet {
|
||||
pub(crate) fn brown() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Brown",
|
||||
light: [
|
||||
|
@ -1046,7 +800,7 @@ fn brown() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn yellow() -> ColorScaleSet {
|
||||
pub(crate) fn yellow() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Yellow",
|
||||
light: [
|
||||
|
@ -1110,7 +864,7 @@ fn yellow() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn amber() -> ColorScaleSet {
|
||||
pub(crate) fn amber() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Amber",
|
||||
light: [
|
||||
|
@ -1174,7 +928,7 @@ fn amber() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn orange() -> ColorScaleSet {
|
||||
pub(crate) fn orange() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Orange",
|
||||
light: [
|
||||
|
@ -1238,7 +992,7 @@ fn orange() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn tomato() -> ColorScaleSet {
|
||||
pub(crate) fn tomato() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Tomato",
|
||||
light: [
|
||||
|
@ -1302,7 +1056,7 @@ fn tomato() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn red() -> ColorScaleSet {
|
||||
pub(crate) fn red() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Red",
|
||||
light: [
|
||||
|
@ -1366,7 +1120,7 @@ fn red() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn ruby() -> ColorScaleSet {
|
||||
pub(crate) fn ruby() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Ruby",
|
||||
light: [
|
||||
|
@ -1430,7 +1184,7 @@ fn ruby() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn crimson() -> ColorScaleSet {
|
||||
pub(crate) fn crimson() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Crimson",
|
||||
light: [
|
||||
|
@ -1494,7 +1248,7 @@ fn crimson() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn pink() -> ColorScaleSet {
|
||||
pub(crate) fn pink() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Pink",
|
||||
light: [
|
||||
|
@ -1558,7 +1312,7 @@ fn pink() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn plum() -> ColorScaleSet {
|
||||
pub(crate) fn plum() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Plum",
|
||||
light: [
|
||||
|
@ -1622,7 +1376,7 @@ fn plum() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn purple() -> ColorScaleSet {
|
||||
pub(crate) fn purple() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Purple",
|
||||
light: [
|
||||
|
@ -1686,7 +1440,7 @@ fn purple() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn violet() -> ColorScaleSet {
|
||||
pub(crate) fn violet() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Violet",
|
||||
light: [
|
||||
|
@ -1750,7 +1504,7 @@ fn violet() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn iris() -> ColorScaleSet {
|
||||
pub(crate) fn iris() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Iris",
|
||||
light: [
|
||||
|
@ -1814,7 +1568,7 @@ fn iris() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn indigo() -> ColorScaleSet {
|
||||
pub(crate) fn indigo() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Indigo",
|
||||
light: [
|
||||
|
@ -1878,7 +1632,7 @@ fn indigo() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn blue() -> ColorScaleSet {
|
||||
pub(crate) fn blue() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Blue",
|
||||
light: [
|
||||
|
@ -1942,7 +1696,7 @@ fn blue() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn cyan() -> ColorScaleSet {
|
||||
pub(crate) fn cyan() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Cyan",
|
||||
light: [
|
||||
|
@ -2006,7 +1760,7 @@ fn cyan() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn teal() -> ColorScaleSet {
|
||||
pub(crate) fn teal() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Teal",
|
||||
light: [
|
||||
|
@ -2070,7 +1824,7 @@ fn teal() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn jade() -> ColorScaleSet {
|
||||
pub(crate) fn jade() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Jade",
|
||||
light: [
|
||||
|
@ -2134,7 +1888,7 @@ fn jade() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn green() -> ColorScaleSet {
|
||||
pub(crate) fn green() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Green",
|
||||
light: [
|
||||
|
@ -2198,7 +1952,7 @@ fn green() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn grass() -> ColorScaleSet {
|
||||
pub(crate) fn grass() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Grass",
|
||||
light: [
|
||||
|
@ -2262,7 +2016,7 @@ fn grass() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn lime() -> ColorScaleSet {
|
||||
pub(crate) fn lime() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Lime",
|
||||
light: [
|
||||
|
@ -2326,7 +2080,7 @@ fn lime() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn mint() -> ColorScaleSet {
|
||||
pub(crate) fn mint() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Mint",
|
||||
light: [
|
||||
|
@ -2390,7 +2144,7 @@ fn mint() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn sky() -> ColorScaleSet {
|
||||
pub(crate) fn sky() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Sky",
|
||||
light: [
|
||||
|
@ -2454,7 +2208,7 @@ fn sky() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn black() -> ColorScaleSet {
|
||||
pub(crate) fn black() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "Black",
|
||||
light: [
|
||||
|
@ -2518,7 +2272,7 @@ fn black() -> ColorScaleSet {
|
|||
.unwrap()
|
||||
}
|
||||
|
||||
fn white() -> ColorScaleSet {
|
||||
pub(crate) fn white() -> ColorScaleSet {
|
||||
StaticColorScaleSet {
|
||||
scale: "White",
|
||||
light: [
|
||||
|
|
|
@ -1,58 +1,56 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
colors::{StatusColors, SystemColors, ThemeColors, ThemeStyles},
|
||||
default_color_scales, Appearance, PlayerColors, SyntaxTheme, Theme, ThemeFamily,
|
||||
one_themes::{one_dark, one_family},
|
||||
Theme, ThemeFamily,
|
||||
};
|
||||
|
||||
fn zed_pro_daylight() -> Theme {
|
||||
Theme {
|
||||
id: "zed_pro_daylight".to_string(),
|
||||
name: "Zed Pro Daylight".into(),
|
||||
appearance: Appearance::Light,
|
||||
styles: ThemeStyles {
|
||||
system: SystemColors::default(),
|
||||
colors: ThemeColors::default_light(),
|
||||
status: StatusColors::default(),
|
||||
player: PlayerColors::default_light(),
|
||||
syntax: Arc::new(SyntaxTheme::default_light()),
|
||||
},
|
||||
}
|
||||
}
|
||||
// fn zed_pro_daylight() -> Theme {
|
||||
// Theme {
|
||||
// id: "zed_pro_daylight".to_string(),
|
||||
// name: "Zed Pro Daylight".into(),
|
||||
// appearance: Appearance::Light,
|
||||
// styles: ThemeStyles {
|
||||
// system: SystemColors::default(),
|
||||
// colors: ThemeColors::light(),
|
||||
// status: StatusColors::light(),
|
||||
// player: PlayerColors::light(),
|
||||
// syntax: Arc::new(SyntaxTheme::light()),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) fn zed_pro_moonlight() -> Theme {
|
||||
Theme {
|
||||
id: "zed_pro_moonlight".to_string(),
|
||||
name: "Zed Pro Moonlight".into(),
|
||||
appearance: Appearance::Dark,
|
||||
styles: ThemeStyles {
|
||||
system: SystemColors::default(),
|
||||
colors: ThemeColors::default_dark(),
|
||||
status: StatusColors::default(),
|
||||
player: PlayerColors::default(),
|
||||
syntax: Arc::new(SyntaxTheme::default_dark()),
|
||||
},
|
||||
}
|
||||
}
|
||||
// pub(crate) fn zed_pro_moonlight() -> Theme {
|
||||
// Theme {
|
||||
// id: "zed_pro_moonlight".to_string(),
|
||||
// name: "Zed Pro Moonlight".into(),
|
||||
// appearance: Appearance::Dark,
|
||||
// styles: ThemeStyles {
|
||||
// system: SystemColors::default(),
|
||||
// colors: ThemeColors::dark(),
|
||||
// status: StatusColors::dark(),
|
||||
// player: PlayerColors::dark(),
|
||||
// syntax: Arc::new(SyntaxTheme::dark()),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn zed_pro_family() -> ThemeFamily {
|
||||
ThemeFamily {
|
||||
id: "zed_pro".to_string(),
|
||||
name: "Zed Pro".into(),
|
||||
author: "Zed Team".into(),
|
||||
themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
|
||||
scales: default_color_scales(),
|
||||
}
|
||||
}
|
||||
// pub fn zed_pro_family() -> ThemeFamily {
|
||||
// ThemeFamily {
|
||||
// id: "zed_pro".to_string(),
|
||||
// name: "Zed Pro".into(),
|
||||
// author: "Zed Team".into(),
|
||||
// themes: vec![zed_pro_daylight(), zed_pro_moonlight()],
|
||||
// scales: default_color_scales(),
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Default for ThemeFamily {
|
||||
fn default() -> Self {
|
||||
zed_pro_family()
|
||||
one_family()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Theme {
|
||||
fn default() -> Self {
|
||||
zed_pro_daylight()
|
||||
one_dark()
|
||||
}
|
||||
}
|
||||
|
|
198
crates/theme2/src/one_themes.rs
Normal file
198
crates/theme2/src/one_themes.rs
Normal file
|
@ -0,0 +1,198 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use gpui::{hsla, FontStyle, FontWeight, HighlightStyle};
|
||||
|
||||
use crate::{
|
||||
default_color_scales, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
|
||||
ThemeColors, ThemeFamily, ThemeStyles,
|
||||
};
|
||||
|
||||
pub fn one_family() -> ThemeFamily {
|
||||
ThemeFamily {
|
||||
id: "one".to_string(),
|
||||
name: "One".into(),
|
||||
author: "".into(),
|
||||
themes: vec![one_dark()],
|
||||
scales: default_color_scales(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn one_dark() -> Theme {
|
||||
let bg = hsla(215. / 360., 12. / 100., 15. / 100., 1.);
|
||||
let editor = hsla(220. / 360., 12. / 100., 18. / 100., 1.);
|
||||
let elevated_surface = hsla(220. / 360., 12. / 100., 18. / 100., 1.);
|
||||
|
||||
let blue = hsla(207.8 / 360., 81. / 100., 66. / 100., 1.0);
|
||||
let gray = hsla(218.8 / 360., 10. / 100., 40. / 100., 1.0);
|
||||
let green = hsla(95. / 360., 38. / 100., 62. / 100., 1.0);
|
||||
let orange = hsla(29. / 360., 54. / 100., 61. / 100., 1.0);
|
||||
let purple = hsla(286. / 360., 51. / 100., 64. / 100., 1.0);
|
||||
let red = hsla(355. / 360., 65. / 100., 65. / 100., 1.0);
|
||||
let teal = hsla(187. / 360., 47. / 100., 55. / 100., 1.0);
|
||||
let yellow = hsla(39. / 360., 67. / 100., 69. / 100., 1.0);
|
||||
|
||||
Theme {
|
||||
id: "one_dark".to_string(),
|
||||
name: "One Dark".into(),
|
||||
appearance: Appearance::Dark,
|
||||
styles: ThemeStyles {
|
||||
system: SystemColors::default(),
|
||||
colors: ThemeColors {
|
||||
border: hsla(225. / 360., 13. / 100., 12. / 100., 1.),
|
||||
border_variant: hsla(228. / 360., 8. / 100., 25. / 100., 1.),
|
||||
border_focused: hsla(223. / 360., 78. / 100., 65. / 100., 1.),
|
||||
border_selected: hsla(222.6 / 360., 77.5 / 100., 65.1 / 100., 1.0),
|
||||
border_transparent: SystemColors::default().transparent,
|
||||
border_disabled: hsla(222.0 / 360., 11.6 / 100., 33.7 / 100., 1.0),
|
||||
elevated_surface_background: elevated_surface,
|
||||
surface_background: bg,
|
||||
background: bg,
|
||||
element_background: elevated_surface,
|
||||
element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
|
||||
element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
|
||||
element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
|
||||
element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
|
||||
drop_target_background: hsla(220.0 / 360., 8.3 / 100., 21.4 / 100., 1.0),
|
||||
ghost_element_background: SystemColors::default().transparent,
|
||||
ghost_element_hover: hsla(225.0 / 360., 11.8 / 100., 26.7 / 100., 1.0),
|
||||
ghost_element_active: hsla(220.0 / 360., 11.8 / 100., 20.0 / 100., 1.0),
|
||||
ghost_element_selected: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
|
||||
ghost_element_disabled: hsla(224.0 / 360., 11.3 / 100., 26.1 / 100., 1.0),
|
||||
text: hsla(222.9 / 360., 9.1 / 100., 84.9 / 100., 1.0),
|
||||
text_muted: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
|
||||
text_placeholder: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0),
|
||||
text_disabled: hsla(220.0 / 360., 6.6 / 100., 44.5 / 100., 1.0),
|
||||
text_accent: hsla(222.6 / 360., 77.5 / 100., 65.1 / 100., 1.0),
|
||||
icon: hsla(222.9 / 360., 9.9 / 100., 86.1 / 100., 1.0),
|
||||
icon_muted: hsla(220.0 / 360., 12.1 / 100., 66.1 / 100., 1.0),
|
||||
icon_disabled: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
|
||||
icon_placeholder: hsla(220.0 / 360., 6.4 / 100., 45.7 / 100., 1.0),
|
||||
icon_accent: blue.into(),
|
||||
status_bar_background: bg,
|
||||
title_bar_background: bg,
|
||||
toolbar_background: editor,
|
||||
tab_bar_background: bg,
|
||||
tab_inactive_background: bg,
|
||||
tab_active_background: editor,
|
||||
editor_background: editor,
|
||||
editor_gutter_background: editor,
|
||||
editor_subheader_background: bg,
|
||||
editor_active_line_background: hsla(222.9 / 360., 13.5 / 100., 20.4 / 100., 1.0),
|
||||
editor_highlighted_line_background: hsla(207.8 / 360., 81. / 100., 66. / 100., 0.1),
|
||||
editor_line_number: hsla(222.0 / 360., 11.5 / 100., 34.1 / 100., 1.0),
|
||||
editor_active_line_number: hsla(216.0 / 360., 5.9 / 100., 49.6 / 100., 1.0),
|
||||
editor_invisible: hsla(222.0 / 360., 11.5 / 100., 34.1 / 100., 1.0),
|
||||
editor_wrap_guide: gpui::black(),
|
||||
editor_active_wrap_guide: gpui::red(),
|
||||
editor_document_highlight_read_background: hsla(
|
||||
207.8 / 360.,
|
||||
81. / 100.,
|
||||
66. / 100.,
|
||||
0.2,
|
||||
),
|
||||
editor_document_highlight_write_background: gpui::red(),
|
||||
terminal_background: bg,
|
||||
// todo!("Use one colors for terminal")
|
||||
terminal_ansi_black: crate::black().dark().step_12(),
|
||||
terminal_ansi_red: crate::red().dark().step_11(),
|
||||
terminal_ansi_green: crate::green().dark().step_11(),
|
||||
terminal_ansi_yellow: crate::yellow().dark().step_11(),
|
||||
terminal_ansi_blue: crate::blue().dark().step_11(),
|
||||
terminal_ansi_magenta: crate::violet().dark().step_11(),
|
||||
terminal_ansi_cyan: crate::cyan().dark().step_11(),
|
||||
terminal_ansi_white: crate::neutral().dark().step_12(),
|
||||
terminal_ansi_bright_black: crate::black().dark().step_11(),
|
||||
terminal_ansi_bright_red: crate::red().dark().step_10(),
|
||||
terminal_ansi_bright_green: crate::green().dark().step_10(),
|
||||
terminal_ansi_bright_yellow: crate::yellow().dark().step_10(),
|
||||
terminal_ansi_bright_blue: crate::blue().dark().step_10(),
|
||||
terminal_ansi_bright_magenta: crate::violet().dark().step_10(),
|
||||
terminal_ansi_bright_cyan: crate::cyan().dark().step_10(),
|
||||
terminal_ansi_bright_white: crate::neutral().dark().step_11(),
|
||||
},
|
||||
status: StatusColors {
|
||||
conflict: yellow,
|
||||
created: green,
|
||||
deleted: red,
|
||||
error: red,
|
||||
hidden: gray,
|
||||
hint: blue,
|
||||
ignored: gray,
|
||||
info: blue,
|
||||
modified: yellow,
|
||||
predictive: gray,
|
||||
renamed: blue,
|
||||
success: green,
|
||||
unreachable: gray,
|
||||
warning: yellow,
|
||||
},
|
||||
player: PlayerColors::dark(),
|
||||
syntax: Arc::new(SyntaxTheme {
|
||||
highlights: vec![
|
||||
("attribute".into(), purple.into()),
|
||||
("boolean".into(), orange.into()),
|
||||
("comment".into(), gray.into()),
|
||||
("comment.doc".into(), gray.into()),
|
||||
("constant".into(), yellow.into()),
|
||||
("constructor".into(), blue.into()),
|
||||
("embedded".into(), HighlightStyle::default()),
|
||||
(
|
||||
"emphasis".into(),
|
||||
HighlightStyle {
|
||||
font_style: Some(FontStyle::Italic),
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
),
|
||||
(
|
||||
"emphasis.strong".into(),
|
||||
HighlightStyle {
|
||||
font_weight: Some(FontWeight::BOLD),
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
),
|
||||
("enum".into(), HighlightStyle::default()),
|
||||
("function".into(), blue.into()),
|
||||
("function.method".into(), blue.into()),
|
||||
("function.definition".into(), blue.into()),
|
||||
("hint".into(), blue.into()),
|
||||
("keyword".into(), purple.into()),
|
||||
("label".into(), HighlightStyle::default()),
|
||||
("link_text".into(), blue.into()),
|
||||
(
|
||||
"link_uri".into(),
|
||||
HighlightStyle {
|
||||
color: Some(teal.into()),
|
||||
font_style: Some(FontStyle::Italic),
|
||||
..HighlightStyle::default()
|
||||
},
|
||||
),
|
||||
("number".into(), orange.into()),
|
||||
("operator".into(), HighlightStyle::default()),
|
||||
("predictive".into(), HighlightStyle::default()),
|
||||
("preproc".into(), HighlightStyle::default()),
|
||||
("primary".into(), HighlightStyle::default()),
|
||||
("property".into(), red.into()),
|
||||
("punctuation".into(), HighlightStyle::default()),
|
||||
("punctuation.bracket".into(), HighlightStyle::default()),
|
||||
("punctuation.delimiter".into(), HighlightStyle::default()),
|
||||
("punctuation.list_marker".into(), HighlightStyle::default()),
|
||||
("punctuation.special".into(), HighlightStyle::default()),
|
||||
("string".into(), green.into()),
|
||||
("string.escape".into(), HighlightStyle::default()),
|
||||
("string.regex".into(), red.into()),
|
||||
("string.special".into(), HighlightStyle::default()),
|
||||
("string.special.symbol".into(), HighlightStyle::default()),
|
||||
("tag".into(), HighlightStyle::default()),
|
||||
("text.literal".into(), HighlightStyle::default()),
|
||||
("title".into(), HighlightStyle::default()),
|
||||
("type".into(), teal.into()),
|
||||
("variable".into(), HighlightStyle::default()),
|
||||
("variable.special".into(), red.into()),
|
||||
("variant".into(), HighlightStyle::default()),
|
||||
],
|
||||
inlay_style: HighlightStyle::default(),
|
||||
suggestion_style: HighlightStyle::default(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@ use gpui::{HighlightStyle, SharedString};
|
|||
use refineable::Refineable;
|
||||
|
||||
use crate::{
|
||||
zed_pro_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors, Theme,
|
||||
ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
||||
one_themes::one_family, Appearance, PlayerColors, StatusColors, SyntaxTheme, SystemColors,
|
||||
Theme, ThemeColors, ThemeFamily, ThemeStyles, UserTheme, UserThemeFamily,
|
||||
};
|
||||
|
||||
pub struct ThemeRegistry {
|
||||
|
@ -38,17 +38,17 @@ impl ThemeRegistry {
|
|||
fn insert_user_themes(&mut self, themes: impl IntoIterator<Item = UserTheme>) {
|
||||
self.insert_themes(themes.into_iter().map(|user_theme| {
|
||||
let mut theme_colors = match user_theme.appearance {
|
||||
Appearance::Light => ThemeColors::default_light(),
|
||||
Appearance::Dark => ThemeColors::default_dark(),
|
||||
Appearance::Light => ThemeColors::light(),
|
||||
Appearance::Dark => ThemeColors::dark(),
|
||||
};
|
||||
theme_colors.refine(&user_theme.styles.colors);
|
||||
|
||||
let mut status_colors = StatusColors::default();
|
||||
let mut status_colors = StatusColors::dark();
|
||||
status_colors.refine(&user_theme.styles.status);
|
||||
|
||||
let mut syntax_colors = match user_theme.appearance {
|
||||
Appearance::Light => SyntaxTheme::default_light(),
|
||||
Appearance::Dark => SyntaxTheme::default_dark(),
|
||||
Appearance::Light => SyntaxTheme::light(),
|
||||
Appearance::Dark => SyntaxTheme::dark(),
|
||||
};
|
||||
if let Some(user_syntax) = user_theme.styles.syntax {
|
||||
syntax_colors.highlights = user_syntax
|
||||
|
@ -76,7 +76,10 @@ impl ThemeRegistry {
|
|||
system: SystemColors::default(),
|
||||
colors: theme_colors,
|
||||
status: status_colors,
|
||||
player: PlayerColors::default(),
|
||||
player: match user_theme.appearance {
|
||||
Appearance::Light => PlayerColors::light(),
|
||||
Appearance::Dark => PlayerColors::dark(),
|
||||
},
|
||||
syntax: Arc::new(syntax_colors),
|
||||
},
|
||||
}
|
||||
|
@ -105,7 +108,7 @@ impl Default for ThemeRegistry {
|
|||
themes: HashMap::default(),
|
||||
};
|
||||
|
||||
this.insert_theme_families([zed_pro_family()]);
|
||||
this.insert_theme_families([one_family()]);
|
||||
|
||||
#[cfg(not(feature = "importing-themes"))]
|
||||
this.insert_user_theme_familes(crate::all_user_themes());
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::one_themes::one_dark;
|
||||
use crate::{Theme, ThemeRegistry};
|
||||
use anyhow::Result;
|
||||
use gpui::{px, AppContext, Font, FontFeatures, FontStyle, FontWeight, Pixels};
|
||||
|
@ -129,7 +130,7 @@ impl settings::Settings for ThemeSettings {
|
|||
buffer_line_height: defaults.buffer_line_height.unwrap(),
|
||||
active_theme: themes
|
||||
.get(defaults.theme.as_ref().unwrap())
|
||||
.or(themes.get("Zed Pro Moonlight"))
|
||||
.or(themes.get(&one_dark().name))
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
|
|
11
crates/theme2/src/styles.rs
Normal file
11
crates/theme2/src/styles.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
mod colors;
|
||||
mod players;
|
||||
mod status;
|
||||
mod syntax;
|
||||
mod system;
|
||||
|
||||
pub use colors::*;
|
||||
pub use players::*;
|
||||
pub use status::*;
|
||||
pub use syntax::*;
|
||||
pub use system::*;
|
|
@ -1,31 +1,8 @@
|
|||
use crate::{PlayerColors, SyntaxTheme};
|
||||
use gpui::Hsla;
|
||||
use refineable::Refineable;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SystemColors {
|
||||
pub transparent: Hsla,
|
||||
pub mac_os_traffic_light_red: Hsla,
|
||||
pub mac_os_traffic_light_yellow: Hsla,
|
||||
pub mac_os_traffic_light_green: Hsla,
|
||||
}
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(Debug, serde::Deserialize)]
|
||||
pub struct StatusColors {
|
||||
pub conflict: Hsla,
|
||||
pub created: Hsla,
|
||||
pub deleted: Hsla,
|
||||
pub error: Hsla,
|
||||
pub hidden: Hsla,
|
||||
pub ignored: Hsla,
|
||||
pub info: Hsla,
|
||||
pub modified: Hsla,
|
||||
pub renamed: Hsla,
|
||||
pub success: Hsla,
|
||||
pub warning: Hsla,
|
||||
}
|
||||
use crate::{PlayerColors, StatusColors, SyntaxTheme, SystemColors};
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(Debug, serde::Deserialize)]
|
||||
|
@ -259,7 +236,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn override_a_single_theme_color() {
|
||||
let mut colors = ThemeColors::default_light();
|
||||
let mut colors = ThemeColors::light();
|
||||
|
||||
let magenta: Hsla = gpui::rgb(0xff00ff);
|
||||
|
||||
|
@ -277,7 +254,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn override_multiple_theme_colors() {
|
||||
let mut colors = ThemeColors::default_light();
|
||||
let mut colors = ThemeColors::light();
|
||||
|
||||
let magenta: Hsla = gpui::rgb(0xff00ff);
|
||||
let green: Hsla = gpui::rgb(0x00ff00);
|
|
@ -16,6 +16,107 @@ pub struct PlayerColor {
|
|||
#[derive(Clone)]
|
||||
pub struct PlayerColors(pub Vec<PlayerColor>);
|
||||
|
||||
impl Default for PlayerColors {
|
||||
/// Don't use this!
|
||||
/// We have to have a default to be `[refineable::Refinable]`.
|
||||
/// todo!("Find a way to not need this for Refinable")
|
||||
fn default() -> Self {
|
||||
Self::dark()
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerColors {
|
||||
pub fn dark() -> Self {
|
||||
Self(vec![
|
||||
PlayerColor {
|
||||
cursor: blue().dark().step_9(),
|
||||
background: blue().dark().step_5(),
|
||||
selection: blue().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: orange().dark().step_9(),
|
||||
background: orange().dark().step_5(),
|
||||
selection: orange().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: pink().dark().step_9(),
|
||||
background: pink().dark().step_5(),
|
||||
selection: pink().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: lime().dark().step_9(),
|
||||
background: lime().dark().step_5(),
|
||||
selection: lime().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: purple().dark().step_9(),
|
||||
background: purple().dark().step_5(),
|
||||
selection: purple().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: amber().dark().step_9(),
|
||||
background: amber().dark().step_5(),
|
||||
selection: amber().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: jade().dark().step_9(),
|
||||
background: jade().dark().step_5(),
|
||||
selection: jade().dark().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: red().dark().step_9(),
|
||||
background: red().dark().step_5(),
|
||||
selection: red().dark().step_3(),
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
pub fn light() -> Self {
|
||||
Self(vec![
|
||||
PlayerColor {
|
||||
cursor: blue().light().step_9(),
|
||||
background: blue().light().step_4(),
|
||||
selection: blue().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: orange().light().step_9(),
|
||||
background: orange().light().step_4(),
|
||||
selection: orange().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: pink().light().step_9(),
|
||||
background: pink().light().step_4(),
|
||||
selection: pink().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: lime().light().step_9(),
|
||||
background: lime().light().step_4(),
|
||||
selection: lime().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: purple().light().step_9(),
|
||||
background: purple().light().step_4(),
|
||||
selection: purple().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: amber().light().step_9(),
|
||||
background: amber().light().step_4(),
|
||||
selection: amber().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: jade().light().step_9(),
|
||||
background: jade().light().step_4(),
|
||||
selection: jade().light().step_3(),
|
||||
},
|
||||
PlayerColor {
|
||||
cursor: red().light().step_9(),
|
||||
background: red().light().step_4(),
|
||||
selection: red().light().step_3(),
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
impl PlayerColors {
|
||||
pub fn local(&self) -> PlayerColor {
|
||||
// todo!("use a valid color");
|
||||
|
@ -36,6 +137,8 @@ impl PlayerColors {
|
|||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
use crate::{amber, blue, jade, lime, orange, pink, purple, red};
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
mod stories {
|
||||
use super::*;
|
134
crates/theme2/src/styles/status.rs
Normal file
134
crates/theme2/src/styles/status.rs
Normal file
|
@ -0,0 +1,134 @@
|
|||
use gpui::Hsla;
|
||||
use refineable::Refineable;
|
||||
|
||||
use crate::{blue, grass, neutral, red, yellow};
|
||||
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(Debug, serde::Deserialize)]
|
||||
pub struct StatusColors {
|
||||
/// Indicates some kind of conflict, like a file changed on disk while it was open, or
|
||||
/// merge conflicts in a Git repository.
|
||||
pub conflict: Hsla,
|
||||
|
||||
/// Indicates something new, like a new file added to a Git repository.
|
||||
pub created: Hsla,
|
||||
|
||||
/// Indicates that something no longer exists, like a deleted file.
|
||||
pub deleted: Hsla,
|
||||
|
||||
/// Indicates a system error, a failed operation or a diagnostic error.
|
||||
pub error: Hsla,
|
||||
|
||||
/// Represents a hidden status, such as a file being hidden in a file tree.
|
||||
pub hidden: Hsla,
|
||||
|
||||
/// Indicates a hint or some kind of additional information.
|
||||
pub hint: Hsla,
|
||||
|
||||
/// Indicates that something is deliberately ignored, such as a file or operation ignored by Git.
|
||||
pub ignored: Hsla,
|
||||
|
||||
/// Represents informational status updates or messages.
|
||||
pub info: Hsla,
|
||||
|
||||
/// Indicates a changed or altered status, like a file that has been edited.
|
||||
pub modified: Hsla,
|
||||
|
||||
/// Indicates something that is predicted, like automatic code completion, or generated code.
|
||||
pub predictive: Hsla,
|
||||
|
||||
/// Represents a renamed status, such as a file that has been renamed.
|
||||
pub renamed: Hsla,
|
||||
|
||||
/// Indicates a successful operation or task completion.
|
||||
pub success: Hsla,
|
||||
|
||||
/// Indicates some kind of unreachable status, like a block of code that can never be reached.
|
||||
pub unreachable: Hsla,
|
||||
|
||||
/// Represents a warning status, like an operation that is about to fail.
|
||||
pub warning: Hsla,
|
||||
}
|
||||
|
||||
impl Default for StatusColors {
|
||||
/// Don't use this!
|
||||
/// We have to have a default to be `[refineable::Refinable]`.
|
||||
/// todo!("Find a way to not need this for Refinable")
|
||||
fn default() -> Self {
|
||||
Self::dark()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DiagnosticColors {
|
||||
pub error: Hsla,
|
||||
pub warning: Hsla,
|
||||
pub info: Hsla,
|
||||
}
|
||||
|
||||
pub struct GitStatusColors {
|
||||
pub created: Hsla,
|
||||
pub deleted: Hsla,
|
||||
pub modified: Hsla,
|
||||
pub renamed: Hsla,
|
||||
pub conflict: Hsla,
|
||||
pub ignored: Hsla,
|
||||
}
|
||||
|
||||
impl StatusColors {
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
conflict: red().dark().step_9(),
|
||||
created: grass().dark().step_9(),
|
||||
deleted: red().dark().step_9(),
|
||||
error: red().dark().step_9(),
|
||||
hidden: neutral().dark().step_9(),
|
||||
hint: blue().dark().step_9(),
|
||||
ignored: neutral().dark().step_9(),
|
||||
info: blue().dark().step_9(),
|
||||
modified: yellow().dark().step_9(),
|
||||
predictive: neutral().dark_alpha().step_9(),
|
||||
renamed: blue().dark().step_9(),
|
||||
success: grass().dark().step_9(),
|
||||
unreachable: neutral().dark().step_10(),
|
||||
warning: yellow().dark().step_9(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
conflict: red().light().step_9(),
|
||||
created: grass().light().step_9(),
|
||||
deleted: red().light().step_9(),
|
||||
error: red().light().step_9(),
|
||||
hidden: neutral().light().step_9(),
|
||||
hint: blue().light().step_9(),
|
||||
ignored: neutral().light().step_9(),
|
||||
info: blue().light().step_9(),
|
||||
modified: yellow().light().step_9(),
|
||||
predictive: neutral().light_alpha().step_9(),
|
||||
renamed: blue().light().step_9(),
|
||||
success: grass().light().step_9(),
|
||||
unreachable: neutral().light().step_10(),
|
||||
warning: yellow().light().step_9(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn diagnostic(&self) -> DiagnosticColors {
|
||||
DiagnosticColors {
|
||||
error: self.error,
|
||||
warning: self.warning,
|
||||
info: self.info,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn git(&self) -> GitStatusColors {
|
||||
GitStatusColors {
|
||||
created: self.created,
|
||||
deleted: self.deleted,
|
||||
modified: self.modified,
|
||||
renamed: self.renamed,
|
||||
conflict: self.conflict,
|
||||
ignored: self.ignored,
|
||||
}
|
||||
}
|
||||
}
|
170
crates/theme2/src/styles/syntax.rs
Normal file
170
crates/theme2/src/styles/syntax.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
use gpui::{HighlightStyle, Hsla};
|
||||
|
||||
use crate::{
|
||||
blue, cyan, gold, indigo, iris, jade, lime, mint, neutral, orange, plum, purple, red, sky,
|
||||
tomato, yellow,
|
||||
};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SyntaxTheme {
|
||||
pub highlights: Vec<(String, HighlightStyle)>,
|
||||
// todo!("Remove this in favor of StatusColor.hint")
|
||||
// If this should be overridable we should move it to ThemeColors
|
||||
pub inlay_style: HighlightStyle,
|
||||
// todo!("Remove this in favor of StatusColor.prediction")
|
||||
// If this should be overridable we should move it to ThemeColors
|
||||
pub suggestion_style: HighlightStyle,
|
||||
}
|
||||
|
||||
impl SyntaxTheme {
|
||||
pub fn light() -> Self {
|
||||
Self {
|
||||
highlights: vec![
|
||||
("attribute".into(), cyan().light().step_11().into()),
|
||||
("boolean".into(), tomato().light().step_11().into()),
|
||||
("comment".into(), neutral().light().step_11().into()),
|
||||
("comment.doc".into(), iris().light().step_12().into()),
|
||||
("constant".into(), red().light().step_9().into()),
|
||||
("constructor".into(), red().light().step_9().into()),
|
||||
("embedded".into(), red().light().step_9().into()),
|
||||
("emphasis".into(), red().light().step_9().into()),
|
||||
("emphasis.strong".into(), red().light().step_9().into()),
|
||||
("enum".into(), red().light().step_9().into()),
|
||||
("function".into(), red().light().step_9().into()),
|
||||
("hint".into(), red().light().step_9().into()),
|
||||
("keyword".into(), orange().light().step_11().into()),
|
||||
("label".into(), red().light().step_9().into()),
|
||||
("link_text".into(), red().light().step_9().into()),
|
||||
("link_uri".into(), red().light().step_9().into()),
|
||||
("number".into(), red().light().step_9().into()),
|
||||
("operator".into(), red().light().step_9().into()),
|
||||
("predictive".into(), red().light().step_9().into()),
|
||||
("preproc".into(), red().light().step_9().into()),
|
||||
("primary".into(), red().light().step_9().into()),
|
||||
("property".into(), red().light().step_9().into()),
|
||||
("punctuation".into(), neutral().light().step_11().into()),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
neutral().light().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
neutral().light().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
blue().light().step_11().into(),
|
||||
),
|
||||
("punctuation.special".into(), red().light().step_9().into()),
|
||||
("string".into(), jade().light().step_11().into()),
|
||||
("string.escape".into(), red().light().step_9().into()),
|
||||
("string.regex".into(), tomato().light().step_11().into()),
|
||||
("string.special".into(), red().light().step_9().into()),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
red().light().step_9().into(),
|
||||
),
|
||||
("tag".into(), red().light().step_9().into()),
|
||||
("text.literal".into(), red().light().step_9().into()),
|
||||
("title".into(), red().light().step_9().into()),
|
||||
("type".into(), red().light().step_9().into()),
|
||||
("variable".into(), red().light().step_9().into()),
|
||||
("variable.special".into(), red().light().step_9().into()),
|
||||
("variant".into(), red().light().step_9().into()),
|
||||
],
|
||||
inlay_style: tomato().light().step_1().into(), // todo!("nate: use a proper style")
|
||||
suggestion_style: orange().light().step_1().into(), // todo!("nate: use proper style")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dark() -> Self {
|
||||
Self {
|
||||
highlights: vec![
|
||||
("attribute".into(), tomato().dark().step_11().into()),
|
||||
("boolean".into(), tomato().dark().step_11().into()),
|
||||
("comment".into(), neutral().dark().step_11().into()),
|
||||
("comment.doc".into(), iris().dark().step_12().into()),
|
||||
("constant".into(), orange().dark().step_11().into()),
|
||||
("constructor".into(), gold().dark().step_11().into()),
|
||||
("embedded".into(), red().dark().step_11().into()),
|
||||
("emphasis".into(), red().dark().step_11().into()),
|
||||
("emphasis.strong".into(), red().dark().step_11().into()),
|
||||
("enum".into(), yellow().dark().step_11().into()),
|
||||
("function".into(), blue().dark().step_11().into()),
|
||||
("hint".into(), indigo().dark().step_11().into()),
|
||||
("keyword".into(), plum().dark().step_11().into()),
|
||||
("label".into(), red().dark().step_11().into()),
|
||||
("link_text".into(), red().dark().step_11().into()),
|
||||
("link_uri".into(), red().dark().step_11().into()),
|
||||
("number".into(), red().dark().step_11().into()),
|
||||
("operator".into(), red().dark().step_11().into()),
|
||||
("predictive".into(), red().dark().step_11().into()),
|
||||
("preproc".into(), red().dark().step_11().into()),
|
||||
("primary".into(), red().dark().step_11().into()),
|
||||
("property".into(), red().dark().step_11().into()),
|
||||
("punctuation".into(), neutral().dark().step_11().into()),
|
||||
(
|
||||
"punctuation.bracket".into(),
|
||||
neutral().dark().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.delimiter".into(),
|
||||
neutral().dark().step_11().into(),
|
||||
),
|
||||
(
|
||||
"punctuation.list_marker".into(),
|
||||
blue().dark().step_11().into(),
|
||||
),
|
||||
("punctuation.special".into(), red().dark().step_11().into()),
|
||||
("string".into(), lime().dark().step_11().into()),
|
||||
("string.escape".into(), orange().dark().step_11().into()),
|
||||
("string.regex".into(), tomato().dark().step_11().into()),
|
||||
("string.special".into(), red().dark().step_11().into()),
|
||||
(
|
||||
"string.special.symbol".into(),
|
||||
red().dark().step_11().into(),
|
||||
),
|
||||
("tag".into(), red().dark().step_11().into()),
|
||||
("text.literal".into(), purple().dark().step_11().into()),
|
||||
("title".into(), sky().dark().step_11().into()),
|
||||
("type".into(), mint().dark().step_11().into()),
|
||||
("variable".into(), red().dark().step_11().into()),
|
||||
("variable.special".into(), red().dark().step_11().into()),
|
||||
("variant".into(), red().dark().step_11().into()),
|
||||
],
|
||||
inlay_style: neutral().dark().step_11().into(), // todo!("nate: use a proper style")
|
||||
suggestion_style: orange().dark().step_11().into(), // todo!("nate: use a proper style")
|
||||
}
|
||||
}
|
||||
|
||||
// TOOD: Get this working with `#[cfg(test)]`. Why isn't it?
|
||||
pub fn new_test(colors: impl IntoIterator<Item = (&'static str, Hsla)>) -> Self {
|
||||
SyntaxTheme {
|
||||
highlights: colors
|
||||
.into_iter()
|
||||
.map(|(key, color)| {
|
||||
(
|
||||
key.to_owned(),
|
||||
HighlightStyle {
|
||||
color: Some(color),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
inlay_style: HighlightStyle::default(),
|
||||
suggestion_style: HighlightStyle::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> HighlightStyle {
|
||||
self.highlights
|
||||
.iter()
|
||||
.find_map(|entry| if entry.0 == name { Some(entry.1) } else { None })
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn color(&self, name: &str) -> Hsla {
|
||||
self.get(name).color.unwrap_or_default()
|
||||
}
|
||||
}
|
20
crates/theme2/src/styles/system.rs
Normal file
20
crates/theme2/src/styles/system.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
use gpui::{hsla, Hsla};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SystemColors {
|
||||
pub transparent: Hsla,
|
||||
pub mac_os_traffic_light_red: Hsla,
|
||||
pub mac_os_traffic_light_yellow: Hsla,
|
||||
pub mac_os_traffic_light_green: Hsla,
|
||||
}
|
||||
|
||||
impl Default for SystemColors {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
transparent: hsla(0.0, 0.0, 0.0, 0.0),
|
||||
mac_os_traffic_light_red: hsla(0.0139, 0.79, 0.65, 1.0),
|
||||
mac_os_traffic_light_yellow: hsla(0.114, 0.88, 0.63, 1.0),
|
||||
mac_os_traffic_light_green: hsla(0.313, 0.49, 0.55, 1.0),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
use gpui::{HighlightStyle, Hsla};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SyntaxTheme {
|
||||
pub highlights: Vec<(String, HighlightStyle)>,
|
||||
pub inlay_style: HighlightStyle,
|
||||
pub suggestion_style: HighlightStyle,
|
||||
}
|
||||
|
||||
impl SyntaxTheme {
|
||||
// TOOD: Get this working with `#[cfg(test)]`. Why isn't it?
|
||||
pub fn new_test(colors: impl IntoIterator<Item = (&'static str, Hsla)>) -> Self {
|
||||
SyntaxTheme {
|
||||
highlights: colors
|
||||
.into_iter()
|
||||
.map(|(key, color)| {
|
||||
(
|
||||
key.to_owned(),
|
||||
HighlightStyle {
|
||||
color: Some(color),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
inlay_style: HighlightStyle::default(),
|
||||
suggestion_style: HighlightStyle::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> HighlightStyle {
|
||||
self.highlights
|
||||
.iter()
|
||||
.find_map(|entry| if entry.0 == name { Some(entry.1) } else { None })
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn color(&self, name: &str) -> Hsla {
|
||||
self.get(name).color.unwrap_or_default()
|
||||
}
|
||||
}
|
|
@ -1,11 +1,10 @@
|
|||
mod colors;
|
||||
mod default_colors;
|
||||
mod default_theme;
|
||||
mod players;
|
||||
mod one_themes;
|
||||
mod registry;
|
||||
mod scale;
|
||||
mod settings;
|
||||
mod syntax;
|
||||
mod styles;
|
||||
#[cfg(not(feature = "importing-themes"))]
|
||||
mod themes;
|
||||
mod user_theme;
|
||||
|
@ -13,14 +12,12 @@ mod user_theme;
|
|||
use std::sync::Arc;
|
||||
|
||||
use ::settings::Settings;
|
||||
pub use colors::*;
|
||||
pub use default_colors::*;
|
||||
pub use default_theme::*;
|
||||
pub use players::*;
|
||||
pub use registry::*;
|
||||
pub use scale::*;
|
||||
pub use settings::*;
|
||||
pub use syntax::*;
|
||||
pub use styles::*;
|
||||
#[cfg(not(feature = "importing-themes"))]
|
||||
pub use themes::*;
|
||||
pub use user_theme::*;
|
||||
|
|
35
crates/theme2/util/hex_to_hsla.py
Normal file
35
crates/theme2/util/hex_to_hsla.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import colorsys
|
||||
import sys
|
||||
|
||||
def hex_to_rgb(hex):
|
||||
hex = hex.lstrip('#')
|
||||
if len(hex) == 8: # 8 digit hex color
|
||||
r, g, b, a = (int(hex[i:i+2], 16) for i in (0, 2, 4, 6))
|
||||
return r, g, b, a / 255.0
|
||||
else: # 6 digit hex color
|
||||
return tuple(int(hex[i:i+2], 16) for i in (0, 2, 4)) + (1.0,)
|
||||
|
||||
def rgb_to_hsla(rgb):
|
||||
h, l, s = colorsys.rgb_to_hls(rgb[0]/255.0, rgb[1]/255.0, rgb[2]/255.0)
|
||||
a = rgb[3] # alpha value
|
||||
return (round(h * 360, 1), round(s * 100, 1), round(l * 100, 1), round(a, 3))
|
||||
|
||||
def hex_to_hsla(hex):
|
||||
return rgb_to_hsla(hex_to_rgb(hex))
|
||||
|
||||
if len(sys.argv) != 2:
|
||||
print("Usage: python util/hex_to_hsla.py <6 or 8 digit hex color or comma-separated list of colors>")
|
||||
else:
|
||||
input_arg = sys.argv[1]
|
||||
if ',' in input_arg: # comma-separated list of colors
|
||||
hex_colors = input_arg.split(',')
|
||||
hslas = [] # output array
|
||||
for hex_color in hex_colors:
|
||||
hex_color = hex_color.strip("'\" ")
|
||||
h, s, l, a = hex_to_hsla(hex_color)
|
||||
hslas.append(f"hsla({h} / 360., {s} / 100., {l} / 100., {a})")
|
||||
print(hslas)
|
||||
else: # single color
|
||||
hex_color = input_arg.strip("'\"")
|
||||
h, s, l, a = hex_to_hsla(hex_color)
|
||||
print(f"hsla({h} / 360., {s} / 100., {l} / 100., {a})")
|
|
@ -1,50 +1,42 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use gpui::Action;
|
||||
use strum::EnumIter;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Keybinding {
|
||||
pub struct KeyBinding {
|
||||
/// A keybinding consists of a key and a set of modifier keys.
|
||||
/// More then one keybinding produces a chord.
|
||||
///
|
||||
/// This should always contain at least one element.
|
||||
keybinding: Vec<(String, ModifierKeys)>,
|
||||
key_binding: gpui::KeyBinding,
|
||||
}
|
||||
|
||||
impl Keybinding {
|
||||
pub fn new(key: String, modifiers: ModifierKeys) -> Self {
|
||||
Self {
|
||||
keybinding: vec![(key, modifiers)],
|
||||
}
|
||||
impl KeyBinding {
|
||||
pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
|
||||
// todo! this last is arbitrary, we want to prefer users key bindings over defaults,
|
||||
// and vim over normal (in vim mode), etc.
|
||||
let key_binding = cx.bindings_for_action(action).last().cloned()?;
|
||||
Some(Self::new(key_binding))
|
||||
}
|
||||
|
||||
pub fn new_chord(
|
||||
first_note: (String, ModifierKeys),
|
||||
second_note: (String, ModifierKeys),
|
||||
) -> Self {
|
||||
Self {
|
||||
keybinding: vec![first_note, second_note],
|
||||
}
|
||||
pub fn new(key_binding: gpui::KeyBinding) -> Self {
|
||||
Self { key_binding }
|
||||
}
|
||||
|
||||
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> {
|
||||
div()
|
||||
.flex()
|
||||
.gap_2()
|
||||
.children(self.keybinding.iter().map(|(key, modifiers)| {
|
||||
.children(self.key_binding.keystrokes().iter().map(|keystroke| {
|
||||
div()
|
||||
.flex()
|
||||
.gap_1()
|
||||
.children(ModifierKey::iter().filter_map(|modifier| {
|
||||
if modifiers.0.contains(&modifier) {
|
||||
Some(Key::new(modifier.glyph().to_string()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}))
|
||||
.child(Key::new(key.clone()))
|
||||
.when(keystroke.modifiers.control, |el| el.child(Key::new("^")))
|
||||
.when(keystroke.modifiers.alt, |el| el.child(Key::new("⌥")))
|
||||
.when(keystroke.modifiers.command, |el| el.child(Key::new("⌘")))
|
||||
.when(keystroke.modifiers.shift, |el| el.child(Key::new("⇧")))
|
||||
.child(Key::new(keystroke.key.clone()))
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
@ -81,76 +73,6 @@ pub enum ModifierKey {
|
|||
Shift,
|
||||
}
|
||||
|
||||
impl ModifierKey {
|
||||
/// Returns the glyph for the [`ModifierKey`].
|
||||
pub fn glyph(&self) -> char {
|
||||
match self {
|
||||
Self::Control => '^',
|
||||
Self::Alt => '⌥',
|
||||
Self::Command => '⌘',
|
||||
Self::Shift => '⇧',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModifierKeys(HashSet<ModifierKey>);
|
||||
|
||||
impl ModifierKeys {
|
||||
pub fn new() -> Self {
|
||||
Self(HashSet::new())
|
||||
}
|
||||
|
||||
pub fn all() -> Self {
|
||||
Self(HashSet::from_iter(ModifierKey::iter()))
|
||||
}
|
||||
|
||||
pub fn add(mut self, modifier: ModifierKey) -> Self {
|
||||
self.0.insert(modifier);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn control(mut self, control: bool) -> Self {
|
||||
if control {
|
||||
self.0.insert(ModifierKey::Control);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Control);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn alt(mut self, alt: bool) -> Self {
|
||||
if alt {
|
||||
self.0.insert(ModifierKey::Alt);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Alt);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn command(mut self, command: bool) -> Self {
|
||||
if command {
|
||||
self.0.insert(ModifierKey::Command);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Command);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn shift(mut self, shift: bool) -> Self {
|
||||
if shift {
|
||||
self.0.insert(ModifierKey::Shift);
|
||||
} else {
|
||||
self.0.remove(&ModifierKey::Shift);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "stories")]
|
||||
pub use stories::*;
|
||||
|
||||
|
@ -158,29 +80,38 @@ pub use stories::*;
|
|||
mod stories {
|
||||
use super::*;
|
||||
use crate::Story;
|
||||
use gpui::{Div, Render};
|
||||
use gpui::{action, Div, Render};
|
||||
use itertools::Itertools;
|
||||
|
||||
pub struct KeybindingStory;
|
||||
|
||||
#[action]
|
||||
struct NoAction {}
|
||||
|
||||
pub fn binding(key: &str) -> gpui::KeyBinding {
|
||||
gpui::KeyBinding::new(key, NoAction {}, None)
|
||||
}
|
||||
|
||||
impl Render for KeybindingStory {
|
||||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let all_modifier_permutations = ModifierKey::iter().permutations(2);
|
||||
let all_modifier_permutations =
|
||||
["ctrl", "alt", "cmd", "shift"].into_iter().permutations(2);
|
||||
|
||||
Story::container(cx)
|
||||
.child(Story::title_for::<_, Keybinding>(cx))
|
||||
.child(Story::title_for::<_, KeyBinding>(cx))
|
||||
.child(Story::label(cx, "Single Key"))
|
||||
.child(Keybinding::new("Z".to_string(), ModifierKeys::new()))
|
||||
.child(KeyBinding::new(binding("Z")))
|
||||
.child(Story::label(cx, "Single Key with Modifier"))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
.gap_3()
|
||||
.children(ModifierKey::iter().map(|modifier| {
|
||||
Keybinding::new("C".to_string(), ModifierKeys::new().add(modifier))
|
||||
})),
|
||||
.child(KeyBinding::new(binding("ctrl-c")))
|
||||
.child(KeyBinding::new(binding("alt-c")))
|
||||
.child(KeyBinding::new(binding("cmd-c")))
|
||||
.child(KeyBinding::new(binding("shift-c"))),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with Modifier (Permuted)"))
|
||||
.child(
|
||||
|
@ -194,29 +125,17 @@ mod stories {
|
|||
.gap_4()
|
||||
.py_3()
|
||||
.children(chunk.map(|permutation| {
|
||||
let mut modifiers = ModifierKeys::new();
|
||||
|
||||
for modifier in permutation {
|
||||
modifiers = modifiers.add(modifier);
|
||||
}
|
||||
|
||||
Keybinding::new("X".to_string(), modifiers)
|
||||
KeyBinding::new(binding(&*(permutation.join("-") + "-x")))
|
||||
}))
|
||||
}),
|
||||
),
|
||||
)
|
||||
.child(Story::label(cx, "Single Key with All Modifiers"))
|
||||
.child(Keybinding::new("Z".to_string(), ModifierKeys::all()))
|
||||
.child(KeyBinding::new(binding("ctrl-alt-cmd-shift-z")))
|
||||
.child(Story::label(cx, "Chord"))
|
||||
.child(Keybinding::new_chord(
|
||||
("A".to_string(), ModifierKeys::new()),
|
||||
("Z".to_string(), ModifierKeys::new()),
|
||||
))
|
||||
.child(KeyBinding::new(binding("a z")))
|
||||
.child(Story::label(cx, "Chord with Modifier"))
|
||||
.child(Keybinding::new_chord(
|
||||
("A".to_string(), ModifierKeys::new().control(true)),
|
||||
("Z".to_string(), ModifierKeys::new().shift(true)),
|
||||
))
|
||||
.child(KeyBinding::new(binding("ctrl-a shift-z")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::prelude::*;
|
||||
use crate::{h_stack, v_stack, Keybinding, Label, LabelColor};
|
||||
use crate::{h_stack, v_stack, KeyBinding, Label, LabelColor};
|
||||
|
||||
#[derive(Component)]
|
||||
pub struct Palette {
|
||||
|
@ -108,7 +108,7 @@ impl Palette {
|
|||
pub struct PaletteItem {
|
||||
pub label: SharedString,
|
||||
pub sublabel: Option<SharedString>,
|
||||
pub keybinding: Option<Keybinding>,
|
||||
pub keybinding: Option<KeyBinding>,
|
||||
}
|
||||
|
||||
impl PaletteItem {
|
||||
|
@ -132,7 +132,7 @@ impl PaletteItem {
|
|||
|
||||
pub fn keybinding<K>(mut self, keybinding: K) -> Self
|
||||
where
|
||||
K: Into<Option<Keybinding>>,
|
||||
K: Into<Option<KeyBinding>>,
|
||||
{
|
||||
self.keybinding = keybinding.into();
|
||||
self
|
||||
|
@ -161,7 +161,7 @@ pub use stories::*;
|
|||
mod stories {
|
||||
use gpui::{Div, Render};
|
||||
|
||||
use crate::{ModifierKeys, Story};
|
||||
use crate::{binding, Story};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -181,46 +181,24 @@ mod stories {
|
|||
Palette::new("palette-2")
|
||||
.placeholder("Execute a command...")
|
||||
.items(vec![
|
||||
PaletteItem::new("theme selector: toggle").keybinding(
|
||||
Keybinding::new_chord(
|
||||
("k".to_string(), ModifierKeys::new().command(true)),
|
||||
("t".to_string(), ModifierKeys::new().command(true)),
|
||||
),
|
||||
),
|
||||
PaletteItem::new("assistant: inline assist").keybinding(
|
||||
Keybinding::new(
|
||||
"enter".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
),
|
||||
),
|
||||
PaletteItem::new("assistant: quote selection").keybinding(
|
||||
Keybinding::new(
|
||||
">".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
),
|
||||
),
|
||||
PaletteItem::new("assistant: toggle focus").keybinding(
|
||||
Keybinding::new(
|
||||
"?".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
),
|
||||
),
|
||||
PaletteItem::new("theme selector: toggle")
|
||||
.keybinding(KeyBinding::new(binding("cmd-k cmd-t"))),
|
||||
PaletteItem::new("assistant: inline assist")
|
||||
.keybinding(KeyBinding::new(binding("cmd-enter"))),
|
||||
PaletteItem::new("assistant: quote selection")
|
||||
.keybinding(KeyBinding::new(binding("cmd-<"))),
|
||||
PaletteItem::new("assistant: toggle focus")
|
||||
.keybinding(KeyBinding::new(binding("cmd-?"))),
|
||||
PaletteItem::new("auto update: check"),
|
||||
PaletteItem::new("auto update: view release notes"),
|
||||
PaletteItem::new("branches: open recent").keybinding(
|
||||
Keybinding::new(
|
||||
"b".to_string(),
|
||||
ModifierKeys::new().command(true).alt(true),
|
||||
),
|
||||
),
|
||||
PaletteItem::new("branches: open recent")
|
||||
.keybinding(KeyBinding::new(binding("cmd-alt-b"))),
|
||||
PaletteItem::new("chat panel: toggle focus"),
|
||||
PaletteItem::new("cli: install"),
|
||||
PaletteItem::new("client: sign in"),
|
||||
PaletteItem::new("client: sign out"),
|
||||
PaletteItem::new("editor: cancel").keybinding(Keybinding::new(
|
||||
"escape".to_string(),
|
||||
ModifierKeys::new(),
|
||||
)),
|
||||
PaletteItem::new("editor: cancel")
|
||||
.keybinding(KeyBinding::new(binding("escape"))),
|
||||
]),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use gpui::{div, Div, ParentElement, Render, SharedString, Styled, ViewContext};
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
use crate::StyledExt;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TextTooltip {
|
||||
title: SharedString,
|
||||
|
@ -16,16 +18,13 @@ impl Render for TextTooltip {
|
|||
type Element = Div<Self>;
|
||||
|
||||
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
|
||||
let theme = cx.theme();
|
||||
div()
|
||||
.bg(theme.colors().background)
|
||||
.rounded_lg()
|
||||
.border()
|
||||
.elevation_2(cx)
|
||||
.font("Zed Sans")
|
||||
.border_color(theme.colors().border)
|
||||
.text_color(theme.colors().text)
|
||||
.pl_2()
|
||||
.pr_2()
|
||||
.text_ui()
|
||||
.text_color(cx.theme().colors().text)
|
||||
.py_1()
|
||||
.px_2()
|
||||
.child(self.title.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ use gpui::{AppContext, ViewContext};
|
|||
use rand::Rng;
|
||||
use theme2::ActiveTheme;
|
||||
|
||||
use crate::HighlightedText;
|
||||
use crate::{binding, HighlightedText};
|
||||
use crate::{
|
||||
Buffer, BufferRow, BufferRows, Button, EditorPane, FileSystemStatus, GitStatus,
|
||||
HighlightedLine, Icon, Keybinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream,
|
||||
MicStatus, ModifierKeys, Notification, PaletteItem, Player, PlayerCallStatus,
|
||||
PlayerWithCallStatus, PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus,
|
||||
HighlightedLine, Icon, KeyBinding, Label, LabelColor, ListEntry, ListEntrySize, Livestream,
|
||||
MicStatus, Notification, PaletteItem, Player, PlayerCallStatus, PlayerWithCallStatus,
|
||||
PublicPlayer, ScreenShareStatus, Symbol, Tab, Toggle, VideoStatus,
|
||||
};
|
||||
use crate::{ListItem, NotificationAction};
|
||||
|
||||
|
@ -701,46 +701,16 @@ pub fn static_collab_panel_channels() -> Vec<ListItem> {
|
|||
|
||||
pub fn example_editor_actions() -> Vec<PaletteItem> {
|
||||
vec![
|
||||
PaletteItem::new("New File").keybinding(Keybinding::new(
|
||||
"N".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Open File").keybinding(Keybinding::new(
|
||||
"O".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Save File").keybinding(Keybinding::new(
|
||||
"S".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Cut").keybinding(Keybinding::new(
|
||||
"X".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Copy").keybinding(Keybinding::new(
|
||||
"C".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Paste").keybinding(Keybinding::new(
|
||||
"V".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Undo").keybinding(Keybinding::new(
|
||||
"Z".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Redo").keybinding(Keybinding::new(
|
||||
"Z".to_string(),
|
||||
ModifierKeys::new().command(true).shift(true),
|
||||
)),
|
||||
PaletteItem::new("Find").keybinding(Keybinding::new(
|
||||
"F".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("Replace").keybinding(Keybinding::new(
|
||||
"R".to_string(),
|
||||
ModifierKeys::new().command(true),
|
||||
)),
|
||||
PaletteItem::new("New File").keybinding(KeyBinding::new(binding("cmd-n"))),
|
||||
PaletteItem::new("Open File").keybinding(KeyBinding::new(binding("cmd-o"))),
|
||||
PaletteItem::new("Save File").keybinding(KeyBinding::new(binding("cmd-s"))),
|
||||
PaletteItem::new("Cut").keybinding(KeyBinding::new(binding("cmd-x"))),
|
||||
PaletteItem::new("Copy").keybinding(KeyBinding::new(binding("cmd-c"))),
|
||||
PaletteItem::new("Paste").keybinding(KeyBinding::new(binding("cmd-v"))),
|
||||
PaletteItem::new("Undo").keybinding(KeyBinding::new(binding("cmd-z"))),
|
||||
PaletteItem::new("Redo").keybinding(KeyBinding::new(binding("cmd-shift-z"))),
|
||||
PaletteItem::new("Find").keybinding(KeyBinding::new(binding("cmd-f"))),
|
||||
PaletteItem::new("Replace").keybinding(KeyBinding::new(binding("cmd-r"))),
|
||||
PaletteItem::new("Jump to Line"),
|
||||
PaletteItem::new("Select All"),
|
||||
PaletteItem::new("Deselect All"),
|
||||
|
|
|
@ -1401,20 +1401,32 @@ impl Pane {
|
|||
// .on_drop(|_view, state: View<DraggedTab>, cx| {
|
||||
// eprintln!("{:?}", state.read(cx));
|
||||
// })
|
||||
.px_2()
|
||||
.py_0p5()
|
||||
.flex()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
// todo!("Nate - I need to do some work to balance all the items in the tab once things stablize")
|
||||
.map(|this| {
|
||||
if close_right {
|
||||
this.pl_3().pr_1()
|
||||
} else {
|
||||
this.pr_1().pr_3()
|
||||
}
|
||||
})
|
||||
.py_1()
|
||||
.bg(tab_bg)
|
||||
.hover(|h| h.bg(tab_hover_bg))
|
||||
.active(|a| a.bg(tab_active_bg))
|
||||
.border_color(cx.theme().colors().border)
|
||||
.map(|this| match ix.cmp(&self.active_item_index) {
|
||||
cmp::Ordering::Less => this.border_l(),
|
||||
cmp::Ordering::Equal => this.border_r(),
|
||||
cmp::Ordering::Greater => this.border_l().border_r(),
|
||||
})
|
||||
// .hover(|h| h.bg(tab_hover_bg))
|
||||
// .active(|a| a.bg(tab_active_bg))
|
||||
.child(
|
||||
div()
|
||||
.px_1()
|
||||
.flex()
|
||||
.items_center()
|
||||
.gap_1p5()
|
||||
.gap_1()
|
||||
.text_color(text_color)
|
||||
.children(if item.has_conflict(cx) {
|
||||
Some(
|
||||
|
|
|
@ -1130,15 +1130,16 @@ impl Workspace {
|
|||
// }))
|
||||
// }
|
||||
|
||||
// pub fn prepare_to_close(
|
||||
// &mut self,
|
||||
// quitting: bool,
|
||||
// cx: &mut ViewContext<Self>,
|
||||
// ) -> Task<Result<bool>> {
|
||||
pub fn prepare_to_close(
|
||||
&mut self,
|
||||
quitting: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Task<Result<bool>> {
|
||||
//todo!(saveing)
|
||||
// let active_call = self.active_call().cloned();
|
||||
// let window = cx.window();
|
||||
|
||||
// cx.spawn(|this, mut cx| async move {
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
// let workspace_count = cx
|
||||
// .windows()
|
||||
// .into_iter()
|
||||
|
@ -1170,13 +1171,15 @@ impl Workspace {
|
|||
// }
|
||||
// }
|
||||
|
||||
// Ok(this
|
||||
Ok(
|
||||
false, // this
|
||||
// .update(&mut cx, |this, cx| {
|
||||
// this.save_all_internal(SaveIntent::Close, cx)
|
||||
// })?
|
||||
// .await?)
|
||||
// })
|
||||
// }
|
||||
// .await?
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// fn save_all(
|
||||
// &mut self,
|
||||
|
@ -4062,24 +4065,24 @@ impl ViewId {
|
|||
}
|
||||
}
|
||||
|
||||
// pub trait WorkspaceHandle {
|
||||
// fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
|
||||
// }
|
||||
pub trait WorkspaceHandle {
|
||||
fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath>;
|
||||
}
|
||||
|
||||
// impl WorkspaceHandle for View<Workspace> {
|
||||
// fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
|
||||
// self.read(cx)
|
||||
// .worktrees(cx)
|
||||
// .flat_map(|worktree| {
|
||||
// let worktree_id = worktree.read(cx).id();
|
||||
// worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
|
||||
// worktree_id,
|
||||
// path: f.path.clone(),
|
||||
// })
|
||||
// })
|
||||
// .collect::<Vec<_>>()
|
||||
// }
|
||||
// }
|
||||
impl WorkspaceHandle for View<Workspace> {
|
||||
fn file_project_paths(&self, cx: &AppContext) -> Vec<ProjectPath> {
|
||||
self.read(cx)
|
||||
.worktrees(cx)
|
||||
.flat_map(|worktree| {
|
||||
let worktree_id = worktree.read(cx).id();
|
||||
worktree.read(cx).files(true, 0).map(move |f| ProjectPath {
|
||||
worktree_id,
|
||||
path: f.path.clone(),
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
// impl std::fmt::Debug for OpenPaths {
|
||||
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
|
|
|
@ -9,6 +9,7 @@ use backtrace::Backtrace;
|
|||
use cli::FORCE_CLI_MODE_ENV_VAR_NAME;
|
||||
use client::UserStore;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use fs::RealFs;
|
||||
use futures::StreamExt;
|
||||
use gpui::{Action, App, AppContext, AsyncAppContext, Context, SemanticVersion, Task};
|
||||
|
@ -56,10 +57,7 @@ use zed2::{
|
|||
mod open_listener;
|
||||
|
||||
fn main() {
|
||||
//TODO!(figure out what the linker issues are here)
|
||||
// https://github.com/rust-lang/rust/issues/47384
|
||||
// https://github.com/mmastrac/rust-ctor/issues/280
|
||||
menu::unused();
|
||||
menu::init();
|
||||
let http = http::client();
|
||||
init_paths();
|
||||
init_logger();
|
||||
|
@ -357,8 +355,7 @@ async fn restore_or_create_workspace(app_state: &Arc<AppState>, mut cx: AsyncApp
|
|||
} else {
|
||||
cx.update(|cx| {
|
||||
workspace::open_new(app_state, cx, |workspace, cx| {
|
||||
// todo!(editor)
|
||||
// Editor::new_file(workspace, &Default::default(), cx)
|
||||
Editor::new_file(workspace, &Default::default(), cx)
|
||||
})
|
||||
.detach();
|
||||
})?;
|
||||
|
|
|
@ -56,7 +56,7 @@ pub fn initialize_workspace(
|
|||
) -> Task<Result<()>> {
|
||||
cx.spawn(|mut cx| async move {
|
||||
workspace_handle.update(&mut cx, |workspace, cx| {
|
||||
let workspace_handle = cx.view();
|
||||
let workspace_handle = cx.view().clone();
|
||||
cx.subscribe(&workspace_handle, {
|
||||
move |workspace, _, event, cx| {
|
||||
if let workspace::Event::PaneAdded(pane) = event {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue