diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index dbfbcc03ab..b517f9aebb 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -2,7 +2,7 @@ "*": { "ctrl-alt-cmd-f": "workspace::FollowNextCollaborator", "cmd-s": "workspace::Save", - "cmd-alt-i": "workspace::DebugElements", + "cmd-alt-i": "zed::DebugElements", "cmd-k cmd-left": "workspace::ActivatePreviousPane", "cmd-k cmd-right": "workspace::ActivateNextPane", "cmd-=": "zed::IncreaseBufferFontSize", diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index ea8150da45..c15c20c775 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -8,13 +8,15 @@ use editor::{ highlight_diagnostic_message, Editor, ExcerptId, MultiBuffer, ToOffset, }; use gpui::{ - actions, elements::*, fonts::TextStyle, AnyViewHandle, AppContext, Entity, ModelHandle, - MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, + actions, elements::*, fonts::TextStyle, serde_json, AnyViewHandle, AppContext, Entity, + ModelHandle, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, + WeakViewHandle, }; use language::{ Bias, Buffer, Diagnostic, DiagnosticEntry, DiagnosticSeverity, Point, Selection, SelectionGoal, }; use project::{DiagnosticSummary, Project, ProjectPath}; +use serde_json::json; use settings::Settings; use std::{ any::{Any, TypeId}, @@ -90,6 +92,31 @@ impl View for ProjectDiagnosticsEditor { cx.focus(&self.editor); } } + + fn debug_json(&self, cx: &AppContext) -> serde_json::Value { + let project = self.project.read(cx); + json!({ + "project": json!({ + "language_servers": project.language_server_statuses().collect::>(), + "summary": project.diagnostic_summary(cx), + }), + "summary": self.summary, + "paths_to_update": self.paths_to_update.iter().map(|path| + path.path.to_string_lossy() + ).collect::>(), + "paths_states": self.path_states.iter().map(|state| + json!({ + "path": state.path.path.to_string_lossy(), + "groups": state.diagnostic_groups.iter().map(|group| + json!({ + "block_count": group.blocks.len(), + "excerpt_count": group.excerpts.len(), + }) + ).collect::>(), + }) + ).collect::>(), + }) + } } impl ProjectDiagnosticsEditor { diff --git a/crates/diagnostics/src/items.rs b/crates/diagnostics/src/items.rs index 0f00b6dd7e..2f9a228256 100644 --- a/crates/diagnostics/src/items.rs +++ b/crates/diagnostics/src/items.rs @@ -1,6 +1,7 @@ use crate::render_summary; use gpui::{ - elements::*, platform::CursorStyle, Entity, ModelHandle, RenderContext, View, ViewContext, + elements::*, platform::CursorStyle, serde_json, Entity, ModelHandle, RenderContext, View, + ViewContext, }; use project::Project; use settings::Settings; @@ -67,6 +68,10 @@ impl View for DiagnosticSummary { .on_click(|cx| cx.dispatch_action(crate::Deploy)) .boxed() } + + fn debug_json(&self, _: &gpui::AppContext) -> serde_json::Value { + serde_json::json!({ "summary": self.summary }) + } } impl StatusItemView for DiagnosticSummary { diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 28d794729f..bc398754a7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1008,7 +1008,7 @@ impl Editor { if project.read(cx).is_remote() { cx.propagate_action(); } else if let Some(buffer) = project - .update(cx, |project, cx| project.create_buffer(cx)) + .update(cx, |project, cx| project.create_buffer("", None, cx)) .log_err() { workspace.add_item( diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f0d33252e4..eb97b85c67 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -62,6 +62,9 @@ pub trait View: Entity + Sized { cx.set.insert(Self::ui_name().into()); cx } + fn debug_json(&self, _: &AppContext) -> serde_json::Value { + serde_json::Value::Null + } } pub trait ReadModel { @@ -2277,6 +2280,12 @@ pub struct AppContext { } impl AppContext { + pub(crate) fn root_view(&self, window_id: usize) -> Option { + self.windows + .get(&window_id) + .map(|window| window.root_view.clone()) + } + pub fn root_view_id(&self, window_id: usize) -> Option { self.windows .get(&window_id) @@ -2590,6 +2599,7 @@ pub trait AnyView { fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); fn keymap_context(&self, cx: &AppContext) -> keymap::Context; + fn debug_json(&self, cx: &AppContext) -> serde_json::Value; } impl AnyView for T @@ -2653,6 +2663,10 @@ where fn keymap_context(&self, cx: &AppContext) -> keymap::Context { View::keymap_context(self, cx) } + + fn debug_json(&self, cx: &AppContext) -> serde_json::Value { + View::debug_json(self, cx) + } } pub struct ModelContext<'a, T: ?Sized> { @@ -3927,6 +3941,12 @@ impl AnyViewHandle { pub fn view_type(&self) -> TypeId { self.view_type } + + pub fn debug_json(&self, cx: &AppContext) -> serde_json::Value { + cx.views + .get(&(self.window_id, self.view_id)) + .map_or_else(|| serde_json::Value::Null, |view| view.debug_json(cx)) + } } impl Clone for AnyViewHandle { diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index ee840bd1e7..4585e321c4 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -209,15 +209,18 @@ impl Presenter { } pub fn debug_elements(&self, cx: &AppContext) -> Option { - cx.root_view_id(self.window_id) - .and_then(|root_view_id| self.rendered_views.get(&root_view_id)) - .map(|root_element| { - root_element.debug(&DebugContext { - rendered_views: &self.rendered_views, - font_cache: &self.font_cache, - app: cx, + let view = cx.root_view(self.window_id)?; + Some(json!({ + "root_view": view.debug_json(cx), + "root_element": self.rendered_views.get(&view.id()) + .map(|root_element| { + root_element.debug(&DebugContext { + rendered_views: &self.rendered_views, + font_cache: &self.font_cache, + app: cx, + }) }) - }) + })) } } @@ -554,6 +557,7 @@ impl Element for ChildView { "type": "ChildView", "view_id": self.view.id(), "bounds": bounds.to_json(), + "view": self.view.debug_json(cx.app), "child": if let Some(view) = cx.rendered_views.get(&self.view.id()) { view.debug(cx) } else { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 409e49e9fb..1bc2d84109 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,6 +28,7 @@ use parking_lot::Mutex; use postage::watch; use rand::prelude::*; use search::SearchQuery; +use serde::Serialize; use settings::Settings; use sha2::{Digest, Sha256}; use similar::{ChangeTag, TextDiff}; @@ -132,16 +133,18 @@ pub enum Event { CollaboratorLeft(PeerId), } +#[derive(Serialize)] pub struct LanguageServerStatus { pub name: String, pub pending_work: BTreeMap, - pending_diagnostic_updates: isize, + pub pending_diagnostic_updates: isize, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize)] pub struct LanguageServerProgress { pub message: Option, pub percentage: Option, + #[serde(skip_serializing)] pub last_update_at: Instant, } @@ -151,7 +154,7 @@ pub struct ProjectPath { pub path: Arc, } -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Serialize)] pub struct DiagnosticSummary { pub error_count: usize, pub warning_count: usize, @@ -467,7 +470,6 @@ impl Project { .and_then(|buffer| buffer.upgrade(cx)) } - #[cfg(any(test, feature = "test-support"))] pub fn languages(&self) -> &Arc { &self.languages } @@ -813,13 +815,19 @@ impl Project { !self.is_local() } - pub fn create_buffer(&mut self, cx: &mut ModelContext) -> Result> { + pub fn create_buffer( + &mut self, + text: &str, + language: Option>, + cx: &mut ModelContext, + ) -> Result> { if self.is_remote() { return Err(anyhow!("creating buffers as a guest is not supported yet")); } let buffer = cx.add_model(|cx| { - Buffer::new(self.replica_id(), "", cx).with_language(language::PLAIN_TEXT.clone(), cx) + Buffer::new(self.replica_id(), text, cx) + .with_language(language.unwrap_or(language::PLAIN_TEXT.clone()), cx) }); self.register_buffer(&buffer, cx)?; Ok(buffer) @@ -6581,7 +6589,9 @@ mod tests { .unwrap(); let worktree_id = worktree.read_with(cx, |worktree, _| worktree.id()); - let buffer = project.update(cx, |project, cx| project.create_buffer(cx).unwrap()); + let buffer = project.update(cx, |project, cx| { + project.create_buffer("", None, cx).unwrap() + }); buffer.update(cx, |buffer, cx| { buffer.edit([0..0], "abc", cx); assert!(buffer.is_dirty()); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d63626e1d3..c73a6bab5b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -18,11 +18,11 @@ use gpui::{ elements::*, geometry::{rect::RectF, vector::vec2f, PathBuilder}, impl_internal_actions, - json::{self, to_string_pretty, ToJson}, + json::{self, ToJson}, platform::{CursorStyle, WindowOptions}, - AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, ClipboardItem, Entity, - ImageData, ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, - View, ViewContext, ViewHandle, WeakViewHandle, + AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Border, Entity, ImageData, + ModelHandle, MutableAppContext, PathPromptOptions, PromptLevel, RenderContext, Task, View, + ViewContext, ViewHandle, WeakViewHandle, }; use language::LanguageRegistry; use log::error; @@ -75,7 +75,6 @@ actions!( ToggleShare, Unfollow, Save, - DebugElements, ActivatePreviousPane, ActivateNextPane, FollowNextCollaborator, @@ -133,7 +132,6 @@ pub fn init(client: &Arc, cx: &mut MutableAppContext) { workspace.save_active_item(cx).detach_and_log_err(cx); }, ); - cx.add_action(Workspace::debug_elements); cx.add_action(Workspace::toggle_sidebar_item); cx.add_action(Workspace::toggle_sidebar_item_focus); cx.add_action(|workspace: &mut Workspace, _: &ActivatePreviousPane, cx| { @@ -1053,22 +1051,6 @@ impl Workspace { cx.notify(); } - pub fn debug_elements(&mut self, _: &DebugElements, cx: &mut ViewContext) { - match to_string_pretty(&cx.debug_elements()) { - Ok(json) => { - let kib = json.len() as f32 / 1024.; - cx.as_mut().write_to_clipboard(ClipboardItem::new(json)); - log::info!( - "copied {:.1} KiB of element debug JSON to the clipboard", - kib - ); - } - Err(error) => { - log::error!("error debugging elements: {}", error); - } - }; - } - fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { let pane = cx.add_view(|cx| Pane::new(cx)); let pane_id = pane.id(); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 7b1ff803f5..e9ed886557 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -10,6 +10,7 @@ pub use client; pub use contacts_panel; use contacts_panel::ContactsPanel; pub use editor; +use editor::Editor; use gpui::{ actions, geometry::vector::vec2f, @@ -22,8 +23,10 @@ use project::Project; pub use project::{self, fs}; use project_panel::ProjectPanel; use search::{BufferSearchBar, ProjectSearchBar}; +use serde_json::to_string_pretty; use settings::Settings; use std::{path::PathBuf, sync::Arc}; +use util::ResultExt; pub use workspace; use workspace::{AppState, Workspace, WorkspaceParams}; @@ -32,6 +35,7 @@ actions!( [ About, Quit, + DebugElements, OpenSettings, IncreaseBufferFontSize, DecreaseBufferFontSize @@ -100,6 +104,28 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { .detach_and_log_err(cx); } }); + cx.add_action( + |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + let content = to_string_pretty(&cx.debug_elements()).unwrap(); + let project = workspace.project().clone(); + let json_language = project.read(cx).languages().get_language("JSON").unwrap(); + if project.read(cx).is_remote() { + cx.propagate_action(); + } else if let Some(buffer) = project + .update(cx, |project, cx| { + project.create_buffer(&content, Some(json_language), cx) + }) + .log_err() + { + workspace.add_item( + Box::new( + cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)), + ), + cx, + ); + } + }, + ); workspace::lsp_status::init(cx);