From 817644eb20dbf7e004962954e3a5385a75529484 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 7 Jun 2023 16:55:29 -0700 Subject: [PATCH] Style new lsp log menu, add a test for it --- Cargo.lock | 2 + crates/lsp_log/Cargo.toml | 2 + crates/lsp_log/src/lsp_log.rs | 94 +++++++++++++++++----------- crates/lsp_log/src/lsp_log_tests.rs | 97 +++++++++++++++++++++++++++++ crates/theme/src/theme.rs | 10 +++ styles/src/styleTree/app.ts | 2 + styles/src/styleTree/lspLogMenu.ts | 41 ++++++++++++ 7 files changed, 210 insertions(+), 38 deletions(-) create mode 100644 crates/lsp_log/src/lsp_log_tests.rs create mode 100644 styles/src/styleTree/lspLogMenu.ts diff --git a/Cargo.lock b/Cargo.lock index c6d09871039c07d6f6e44dfb6b2ab20b6455ec69..c6d6fbb944904a779a731c90eabd33a263464465 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3763,8 +3763,10 @@ name = "lsp_log" version = "0.1.0" dependencies = [ "anyhow", + "client", "collections", "editor", + "env_logger 0.9.3", "futures 0.3.28", "gpui", "language", diff --git a/crates/lsp_log/Cargo.toml b/crates/lsp_log/Cargo.toml index 6f47057b442df907c3dcc42fd78d8f104301f24b..46f6006a23efde3569311da8f936c65e40ea9a93 100644 --- a/crates/lsp_log/Cargo.toml +++ b/crates/lsp_log/Cargo.toml @@ -24,7 +24,9 @@ serde.workspace = true anyhow.workspace = true [dev-dependencies] +client = { path = "../client", features = ["test-support"] } editor = { path = "../editor", features = ["test-support"] } gpui = { path = "../gpui", features = ["test-support"] } util = { path = "../util", features = ["test-support"] } +env_logger.workspace = true unindent.workspace = true diff --git a/crates/lsp_log/src/lsp_log.rs b/crates/lsp_log/src/lsp_log.rs index 2e54f4726d987a4a7c3fd2f0015a8c054bc89462..c7a95cab1d8f277c73fc5f1a889562691aeff482 100644 --- a/crates/lsp_log/src/lsp_log.rs +++ b/crates/lsp_log/src/lsp_log.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +mod lsp_log_tests; + use collections::HashMap; use editor::Editor; use futures::{channel::mpsc, StreamExt}; @@ -521,7 +524,7 @@ impl View for LspLogToolbarItemView { ) })) .contained() - .with_style(theme.context_menu.container) + .with_style(theme.lsp_log_menu.container) .constrained() .with_width(400.) .with_height(400.) @@ -548,6 +551,9 @@ impl View for LspLogToolbarItemView { } } +const RPC_MESSAGES: &str = "RPC Messages"; +const SERVER_LOGS: &str = "Server Logs"; + impl LspLogToolbarItemView { pub fn new() -> Self { Self { @@ -605,18 +611,25 @@ impl LspLogToolbarItemView { let label: Cow = current_server .and_then(|row| { let worktree = row.worktree.read(cx); - Some(format!("{} - ({})", row.server_name.0, worktree.root_name()).into()) + Some( + format!( + "{} ({}) - {}", + row.server_name.0, + worktree.root_name(), + if row.rpc_trace_selected { + RPC_MESSAGES + } else { + SERVER_LOGS + }, + ) + .into(), + ) }) .unwrap_or_else(|| "No server selected".into()); - Label::new( - label, - theme - .context_menu - .item - .style_for(state, false) - .label - .clone(), - ) + let style = theme.lsp_log_menu.header.style_for(state, false); + Label::new(label, style.text.clone()) + .contained() + .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, view, cx| { @@ -628,7 +641,7 @@ impl LspLogToolbarItemView { id: LanguageServerId, name: LanguageServerName, worktree: ModelHandle, - logging_enabled: bool, + rpc_trace_enabled: bool, logs_selected: bool, rpc_trace_selected: bool, theme: &Arc, @@ -637,23 +650,25 @@ impl LspLogToolbarItemView { enum ActivateLog {} enum ActivateRpcTrace {} - let header = format!("{} - ({})", name.0, worktree.read(cx).root_name()); - - let item_style = &theme.context_menu.item.default; Flex::column() - .with_child( - Label::new(header, item_style.label.clone()) - .aligned() - .left(), - ) + .with_child({ + let style = &theme.lsp_log_menu.server; + Label::new( + format!("{} ({})", name.0, worktree.read(cx).root_name()), + style.text.clone(), + ) + .contained() + .with_style(style.container) + .aligned() + .left() + }) .with_child( MouseEventHandler::::new(id.0, cx, move |state, _| { - let item_style = &theme.context_menu.item.style_for(state, logs_selected); - Label::new("logs", item_style.label.clone()) - .aligned() - .left() + let style = theme.lsp_log_menu.item.style_for(state, logs_selected); + Flex::row() + .with_child(Label::new(SERVER_LOGS, style.text.clone()).aligned().left()) .contained() - .with_style(item_style.container) + .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, view, cx| { @@ -662,26 +677,29 @@ impl LspLogToolbarItemView { ) .with_child( MouseEventHandler::::new(id.0, cx, move |state, cx| { - let item_style = &theme.context_menu.item.style_for(state, rpc_trace_selected); + let style = theme.lsp_log_menu.item.style_for(state, rpc_trace_selected); Flex::row() - .with_child(ui::checkbox_with_label::( - Empty::new(), - &theme.welcome.checkbox, - logging_enabled, - id.0, - cx, - move |this, enabled, cx| { - this.toggle_logging_for_server(id, enabled, cx); - }, - )) .with_child( - Label::new("rpc trace", item_style.label.clone()) + Label::new(RPC_MESSAGES, style.text.clone()) .aligned() .left(), ) + .with_child( + ui::checkbox_with_label::( + Empty::new(), + &theme.welcome.checkbox, + rpc_trace_enabled, + id.0, + cx, + move |this, enabled, cx| { + this.toggle_logging_for_server(id, enabled, cx); + }, + ) + .flex_float(), + ) .align_children_center() .contained() - .with_style(item_style.container) + .with_style(style.container) }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, move |_, view, cx| { diff --git a/crates/lsp_log/src/lsp_log_tests.rs b/crates/lsp_log/src/lsp_log_tests.rs new file mode 100644 index 0000000000000000000000000000000000000000..4be0db456cb663d2f708bd0132877f3a9a0ffc20 --- /dev/null +++ b/crates/lsp_log/src/lsp_log_tests.rs @@ -0,0 +1,97 @@ +use super::*; +use gpui::{serde_json::json, TestAppContext}; +use language::{tree_sitter_rust, FakeLspAdapter, Language, LanguageConfig}; +use project::FakeFs; +use settings::SettingsStore; + +#[gpui::test] +async fn test_lsp_logs(cx: &mut TestAppContext) { + if std::env::var("RUST_LOG").is_ok() { + env_logger::init(); + } + + init_test(cx); + + let mut rust_language = Language::new( + LanguageConfig { + name: "Rust".into(), + path_suffixes: vec!["rs".to_string()], + ..Default::default() + }, + Some(tree_sitter_rust::language()), + ); + let mut fake_rust_servers = rust_language + .set_fake_lsp_adapter(Arc::new(FakeLspAdapter { + name: "the-rust-language-server", + ..Default::default() + })) + .await; + + let fs = FakeFs::new(cx.background()); + fs.insert_tree( + "/the-root", + json!({ + "test.rs": "", + "package.json": "", + }), + ) + .await; + let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await; + project.update(cx, |project, _| { + project.languages().add(Arc::new(rust_language)); + }); + + let log_store = cx.add_model(|cx| LogStore::new(cx)); + log_store.update(cx, |store, cx| store.add_project(&project, cx)); + + let _rust_buffer = project + .update(cx, |project, cx| { + project.open_local_buffer("/the-root/test.rs", cx) + }) + .await + .unwrap(); + + let mut language_server = fake_rust_servers.next().await.unwrap(); + language_server + .receive_notification::() + .await; + + let (_, log_view) = cx.add_window(|cx| LspLogView::new(project.clone(), log_store.clone(), cx)); + + language_server.notify::(lsp::LogMessageParams { + message: "hello from the server".into(), + typ: lsp::MessageType::INFO, + }); + cx.foreground().run_until_parked(); + + log_view.read_with(cx, |view, cx| { + assert_eq!( + view.menu_items(cx).unwrap(), + &[LogMenuItem { + server_id: language_server.server.server_id(), + server_name: LanguageServerName("the-rust-language-server".into()), + worktree: project.read(cx).worktrees(cx).next().unwrap(), + rpc_trace_enabled: false, + rpc_trace_selected: false, + logs_selected: true, + }] + ); + assert_eq!( + view.editor.as_ref().unwrap().read(cx).text(cx), + "hello from the server\n" + ); + }); +} + +fn init_test(cx: &mut gpui::TestAppContext) { + cx.foreground().forbid_parking(); + + cx.update(|cx| { + cx.set_global(SettingsStore::test(cx)); + theme::init((), cx); + language::init(cx); + client::init_settings(cx); + Project::init_settings(cx); + editor::init_settings(cx); + }); +} diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index f7df63ca099d9a50cf39c565d9cb658aafe098a1..164a8cc1b21554710e4b54a73172080dc9c27c88 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -44,6 +44,7 @@ pub struct Theme { pub context_menu: ContextMenu, pub contacts_popover: ContactsPopover, pub contact_list: ContactList, + pub lsp_log_menu: LspLogMenu, pub copilot: Copilot, pub contact_finder: ContactFinder, pub project_panel: ProjectPanel, @@ -244,6 +245,15 @@ pub struct ContactFinder { pub disabled_contact_button: IconButton, } +#[derive(Deserialize, Default)] +pub struct LspLogMenu { + #[serde(flatten)] + pub container: ContainerStyle, + pub header: Interactive, + pub server: ContainedText, + pub item: Interactive, +} + #[derive(Clone, Deserialize, Default)] pub struct TabBar { #[serde(flatten)] diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index a9700a8d9994f0b8f63b74862b8db26c873a37da..4c85d2109f9fa450b3bfc38fa9b1dda442bed5b6 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -17,6 +17,7 @@ import projectSharedNotification from "./projectSharedNotification" import tooltip from "./tooltip" import terminal from "./terminal" import contactList from "./contactList" +import lspLogMenu from "./lspLogMenu" import incomingCallNotification from "./incomingCallNotification" import { ColorScheme } from "../themes/common/colorScheme" import feedback from "./feedback" @@ -45,6 +46,7 @@ export default function app(colorScheme: ColorScheme): Object { contactsPopover: contactsPopover(colorScheme), contactFinder: contactFinder(colorScheme), contactList: contactList(colorScheme), + lspLogMenu: lspLogMenu(colorScheme), search: search(colorScheme), sharedScreen: sharedScreen(colorScheme), updateNotification: updateNotification(colorScheme), diff --git a/styles/src/styleTree/lspLogMenu.ts b/styles/src/styleTree/lspLogMenu.ts new file mode 100644 index 0000000000000000000000000000000000000000..de4555e629e7372c8db6ef865b52336c229a0b46 --- /dev/null +++ b/styles/src/styleTree/lspLogMenu.ts @@ -0,0 +1,41 @@ +import { ColorScheme } from "../themes/common/colorScheme" +import { background, border, text } from "./components" + +export default function contactsPanel(colorScheme: ColorScheme) { + let layer = colorScheme.middle + + return { + background: background(layer), + border: border(layer), + shadow: colorScheme.popoverShadow, + header: { + ...text(layer, "sans", { size: "sm" }), + padding: { left: 8, right: 8, top: 2, bottom: 2 }, + cornerRadius: 6, + background: background(layer, "on"), + border: border(layer, "on", { overlay: true }), + hover: { + background: background(layer, "hovered"), + ...text(layer, "sans", "hovered", { size: "sm" }), + } + }, + server: { + ...text(layer, "sans", { size: "sm" }), + padding: { left: 8, right: 8, top: 8, bottom: 8 }, + }, + item: { + ...text(layer, "sans", { size: "sm" }), + padding: { left: 18, right: 18, top: 2, bottom: 2 }, + hover: { + background: background(layer, "hovered"), + ...text(layer, "sans", "hovered", { size: "sm" }), + }, + active: { + background: background(layer, "active"), + }, + activeHover: { + background: background(layer, "active"), + }, + }, + } +}