mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-18 18:08:07 +03:00
Start work on a language server log view
This commit is contained in:
parent
2dd4920625
commit
a280a93cd8
21
Cargo.lock
generated
21
Cargo.lock
generated
@ -3612,6 +3612,26 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lsp_log"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"collections",
|
||||||
|
"editor",
|
||||||
|
"futures 0.3.25",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"lsp",
|
||||||
|
"project",
|
||||||
|
"serde",
|
||||||
|
"settings",
|
||||||
|
"theme",
|
||||||
|
"unindent",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mach"
|
name = "mach"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -8571,6 +8591,7 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
|
"lsp_log",
|
||||||
"node_runtime",
|
"node_runtime",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"outline",
|
"outline",
|
||||||
|
@ -35,6 +35,7 @@ members = [
|
|||||||
"crates/live_kit_client",
|
"crates/live_kit_client",
|
||||||
"crates/live_kit_server",
|
"crates/live_kit_server",
|
||||||
"crates/lsp",
|
"crates/lsp",
|
||||||
|
"crates/lsp_log",
|
||||||
"crates/media",
|
"crates/media",
|
||||||
"crates/menu",
|
"crates/menu",
|
||||||
"crates/node_runtime",
|
"crates/node_runtime",
|
||||||
|
@ -511,6 +511,7 @@ pub struct Editor {
|
|||||||
workspace_id: Option<WorkspaceId>,
|
workspace_id: Option<WorkspaceId>,
|
||||||
keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
|
keymap_context_layers: BTreeMap<TypeId, KeymapContext>,
|
||||||
input_enabled: bool,
|
input_enabled: bool,
|
||||||
|
read_only: bool,
|
||||||
leader_replica_id: Option<u16>,
|
leader_replica_id: Option<u16>,
|
||||||
remote_id: Option<ViewId>,
|
remote_id: Option<ViewId>,
|
||||||
hover_state: HoverState,
|
hover_state: HoverState,
|
||||||
@ -1283,6 +1284,7 @@ impl Editor {
|
|||||||
workspace_id: None,
|
workspace_id: None,
|
||||||
keymap_context_layers: Default::default(),
|
keymap_context_layers: Default::default(),
|
||||||
input_enabled: true,
|
input_enabled: true,
|
||||||
|
read_only: false,
|
||||||
leader_replica_id: None,
|
leader_replica_id: None,
|
||||||
remote_id: None,
|
remote_id: None,
|
||||||
hover_state: Default::default(),
|
hover_state: Default::default(),
|
||||||
@ -1425,6 +1427,10 @@ impl Editor {
|
|||||||
self.input_enabled = input_enabled;
|
self.input_enabled = input_enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_read_only(&mut self, read_only: bool) {
|
||||||
|
self.read_only = read_only;
|
||||||
|
}
|
||||||
|
|
||||||
fn selections_did_change(
|
fn selections_did_change(
|
||||||
&mut self,
|
&mut self,
|
||||||
local: bool,
|
local: bool,
|
||||||
@ -1533,6 +1539,10 @@ impl Editor {
|
|||||||
S: ToOffset,
|
S: ToOffset,
|
||||||
T: Into<Arc<str>>,
|
T: Into<Arc<str>>,
|
||||||
{
|
{
|
||||||
|
if self.read_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.buffer
|
self.buffer
|
||||||
.update(cx, |buffer, cx| buffer.edit(edits, None, cx));
|
.update(cx, |buffer, cx| buffer.edit(edits, None, cx));
|
||||||
}
|
}
|
||||||
@ -1543,6 +1553,10 @@ impl Editor {
|
|||||||
S: ToOffset,
|
S: ToOffset,
|
||||||
T: Into<Arc<str>>,
|
T: Into<Arc<str>>,
|
||||||
{
|
{
|
||||||
|
if self.read_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.buffer.update(cx, |buffer, cx| {
|
self.buffer.update(cx, |buffer, cx| {
|
||||||
buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
|
buffer.edit(edits, Some(AutoindentMode::EachLine), cx)
|
||||||
});
|
});
|
||||||
@ -1897,6 +1911,9 @@ impl Editor {
|
|||||||
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
|
pub fn handle_input(&mut self, text: &str, cx: &mut ViewContext<Self>) {
|
||||||
let text: Arc<str> = text.into();
|
let text: Arc<str> = text.into();
|
||||||
|
|
||||||
|
if self.read_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if !self.input_enabled {
|
if !self.input_enabled {
|
||||||
cx.emit(Event::InputIgnored { text });
|
cx.emit(Event::InputIgnored { text });
|
||||||
return;
|
return;
|
||||||
@ -2282,6 +2299,10 @@ impl Editor {
|
|||||||
autoindent_mode: Option<AutoindentMode>,
|
autoindent_mode: Option<AutoindentMode>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
|
if self.read_only {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let text: Arc<str> = text.into();
|
let text: Arc<str> = text.into();
|
||||||
self.transact(cx, |this, cx| {
|
self.transact(cx, |this, cx| {
|
||||||
let old_selections = this.selections.all_adjusted(cx);
|
let old_selections = this.selections.all_adjusted(cx);
|
||||||
|
29
crates/lsp_log/Cargo.toml
Normal file
29
crates/lsp_log/Cargo.toml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
[package]
|
||||||
|
name = "lsp_log"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/lsp_log.rs"
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
collections = { path = "../collections" }
|
||||||
|
editor = { path = "../editor" }
|
||||||
|
settings = { path = "../settings" }
|
||||||
|
theme = { path = "../theme" }
|
||||||
|
language = { path = "../language" }
|
||||||
|
project = { path = "../project" }
|
||||||
|
workspace = { path = "../workspace" }
|
||||||
|
gpui = { path = "../gpui" }
|
||||||
|
util = { path = "../util" }
|
||||||
|
lsp = { path = "../lsp" }
|
||||||
|
futures = { workspace = true }
|
||||||
|
serde = { workspace = true }
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gpui = { path = "../gpui", features = ["test-support"] }
|
||||||
|
util = { path = "../util", features = ["test-support"] }
|
||||||
|
unindent = "0.1.7"
|
374
crates/lsp_log/src/lsp_log.rs
Normal file
374
crates/lsp_log/src/lsp_log.rs
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
use collections::HashMap;
|
||||||
|
use editor::Editor;
|
||||||
|
use futures::{channel::mpsc, StreamExt};
|
||||||
|
use gpui::{
|
||||||
|
actions,
|
||||||
|
elements::{
|
||||||
|
AnchorCorner, ChildView, Empty, Flex, Label, MouseEventHandler, Overlay, OverlayFitMode,
|
||||||
|
ParentElement, Stack,
|
||||||
|
},
|
||||||
|
impl_internal_actions,
|
||||||
|
platform::MouseButton,
|
||||||
|
AppContext, Element, ElementBox, Entity, ModelHandle, RenderContext, View, ViewContext,
|
||||||
|
ViewHandle,
|
||||||
|
};
|
||||||
|
use language::{Buffer, LanguageServerId, LanguageServerName};
|
||||||
|
use project::{Project, WorktreeId};
|
||||||
|
use settings::Settings;
|
||||||
|
use std::{borrow::Cow, sync::Arc};
|
||||||
|
use theme::Theme;
|
||||||
|
use workspace::{
|
||||||
|
item::{Item, ItemHandle},
|
||||||
|
ToolbarItemLocation, ToolbarItemView, Workspace,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SEND_LINE: &str = "// Send:\n";
|
||||||
|
const RECEIVE_LINE: &str = "// Receive:\n";
|
||||||
|
|
||||||
|
pub struct LspLogView {
|
||||||
|
enabled_logs: HashMap<LanguageServerId, LogState>,
|
||||||
|
current_server_id: Option<LanguageServerId>,
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
io_tx: mpsc::UnboundedSender<(LanguageServerId, bool, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LspLogToolbarItemView {
|
||||||
|
log_view: Option<ViewHandle<LspLogView>>,
|
||||||
|
menu_open: bool,
|
||||||
|
project: ModelHandle<Project>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LogState {
|
||||||
|
buffer: ModelHandle<Buffer>,
|
||||||
|
editor: ViewHandle<Editor>,
|
||||||
|
last_message_kind: Option<MessageKind>,
|
||||||
|
_subscription: lsp::Subscription,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
enum MessageKind {
|
||||||
|
Send,
|
||||||
|
Receive,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct ActivateLog {
|
||||||
|
server_id: LanguageServerId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
struct ToggleMenu;
|
||||||
|
|
||||||
|
impl_internal_actions!(log, [ActivateLog, ToggleMenu]);
|
||||||
|
actions!(log, [OpenLanguageServerLogs]);
|
||||||
|
|
||||||
|
pub fn init(cx: &mut AppContext) {
|
||||||
|
cx.add_action(LspLogView::deploy);
|
||||||
|
cx.add_action(LspLogToolbarItemView::toggle_menu);
|
||||||
|
cx.add_action(LspLogToolbarItemView::activate_log_for_server);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LspLogView {
|
||||||
|
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let (io_tx, mut io_rx) = mpsc::unbounded();
|
||||||
|
let this = Self {
|
||||||
|
enabled_logs: HashMap::default(),
|
||||||
|
current_server_id: None,
|
||||||
|
io_tx,
|
||||||
|
project,
|
||||||
|
};
|
||||||
|
cx.spawn_weak(|this, mut cx| async move {
|
||||||
|
while let Some((language_server_id, is_output, mut message)) = io_rx.next().await {
|
||||||
|
if let Some(this) = this.upgrade(&cx) {
|
||||||
|
this.update(&mut cx, |this, cx| {
|
||||||
|
message.push('\n');
|
||||||
|
this.on_io(language_server_id, is_output, &message, cx);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
this
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deploy(
|
||||||
|
workspace: &mut Workspace,
|
||||||
|
_: &OpenLanguageServerLogs,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) {
|
||||||
|
let project = workspace.project().read(cx);
|
||||||
|
if project.is_remote() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let log_view = cx.add_view(|cx| Self::new(workspace.project().clone(), cx));
|
||||||
|
workspace.add_item(Box::new(log_view), cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate_log(&mut self, action: &ActivateLog, cx: &mut ViewContext<Self>) {
|
||||||
|
self.enable_logs_for_language_server(action.server_id, cx);
|
||||||
|
self.current_server_id = Some(action.server_id);
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_io(
|
||||||
|
&mut self,
|
||||||
|
language_server_id: LanguageServerId,
|
||||||
|
is_received: bool,
|
||||||
|
message: &str,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
if let Some(state) = self.enabled_logs.get_mut(&language_server_id) {
|
||||||
|
state.buffer.update(cx, |buffer, cx| {
|
||||||
|
let kind = if is_received {
|
||||||
|
MessageKind::Receive
|
||||||
|
} else {
|
||||||
|
MessageKind::Send
|
||||||
|
};
|
||||||
|
if state.last_message_kind != Some(kind) {
|
||||||
|
let len = buffer.len();
|
||||||
|
let line = match kind {
|
||||||
|
MessageKind::Send => SEND_LINE,
|
||||||
|
MessageKind::Receive => RECEIVE_LINE,
|
||||||
|
};
|
||||||
|
buffer.edit([(len..len, line)], None, cx);
|
||||||
|
state.last_message_kind = Some(kind);
|
||||||
|
}
|
||||||
|
let len = buffer.len();
|
||||||
|
buffer.edit([(len..len, message)], None, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enable_logs_for_language_server(
|
||||||
|
&mut self,
|
||||||
|
server_id: LanguageServerId,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) {
|
||||||
|
if let Some(server) = self.project.read(cx).language_server_for_id(server_id) {
|
||||||
|
self.enabled_logs.entry(server_id).or_insert_with(|| {
|
||||||
|
let project = self.project.read(cx);
|
||||||
|
let io_tx = self.io_tx.clone();
|
||||||
|
let language = project.languages().language_for_name("JSON");
|
||||||
|
let buffer = cx.add_model(|cx| Buffer::new(0, "", cx));
|
||||||
|
cx.spawn({
|
||||||
|
let buffer = buffer.clone();
|
||||||
|
|_, mut cx| async move {
|
||||||
|
let language = language.await.ok();
|
||||||
|
buffer.update(&mut cx, |buffer, cx| {
|
||||||
|
buffer.set_language(language, cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
|
let editor = cx.add_view(|cx| {
|
||||||
|
let mut editor =
|
||||||
|
Editor::for_buffer(buffer.clone(), Some(self.project.clone()), cx);
|
||||||
|
editor.set_read_only(true);
|
||||||
|
editor
|
||||||
|
});
|
||||||
|
|
||||||
|
LogState {
|
||||||
|
buffer,
|
||||||
|
editor,
|
||||||
|
last_message_kind: None,
|
||||||
|
_subscription: server.on_io(move |is_received, json| {
|
||||||
|
io_tx
|
||||||
|
.unbounded_send((server_id, is_received, json.to_string()))
|
||||||
|
.ok();
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for LspLogView {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"LspLogView"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox {
|
||||||
|
if let Some(id) = self.current_server_id {
|
||||||
|
if let Some(log) = self.enabled_logs.get_mut(&id) {
|
||||||
|
return ChildView::new(&log.editor, cx).boxed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Empty::new().boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Item for LspLogView {
|
||||||
|
fn tab_content(&self, _: Option<usize>, style: &theme::Tab, _: &AppContext) -> ElementBox {
|
||||||
|
Label::new("Logs", style.label.clone()).boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolbarItemView for LspLogToolbarItemView {
|
||||||
|
fn set_active_pane_item(
|
||||||
|
&mut self,
|
||||||
|
active_pane_item: Option<&dyn ItemHandle>,
|
||||||
|
cx: &mut ViewContext<Self>,
|
||||||
|
) -> workspace::ToolbarItemLocation {
|
||||||
|
self.menu_open = false;
|
||||||
|
if let Some(item) = active_pane_item {
|
||||||
|
if let Some(log_view) = item.downcast::<LspLogView>() {
|
||||||
|
self.log_view = Some(log_view.clone());
|
||||||
|
return ToolbarItemLocation::PrimaryLeft {
|
||||||
|
flex: Some((1., false)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.log_view = None;
|
||||||
|
ToolbarItemLocation::Hidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for LspLogToolbarItemView {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"LspLogView"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox {
|
||||||
|
let theme = cx.global::<Settings>().theme.clone();
|
||||||
|
let Some(log_view) = self.log_view.as_ref() else { return Empty::new().boxed() };
|
||||||
|
let project = self.project.read(cx);
|
||||||
|
let mut language_servers = project.language_servers().collect::<Vec<_>>();
|
||||||
|
language_servers.sort_by_key(|a| a.0);
|
||||||
|
|
||||||
|
let current_server_id = log_view.read(cx).current_server_id;
|
||||||
|
let current_server = current_server_id.and_then(|current_server_id| {
|
||||||
|
if let Ok(ix) = language_servers.binary_search_by_key(¤t_server_id, |e| e.0) {
|
||||||
|
Some(language_servers[ix].clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Stack::new()
|
||||||
|
.with_child(Self::render_language_server_menu_header(
|
||||||
|
current_server,
|
||||||
|
&self.project,
|
||||||
|
&theme,
|
||||||
|
cx,
|
||||||
|
))
|
||||||
|
.with_children(if self.menu_open {
|
||||||
|
Some(
|
||||||
|
Overlay::new(
|
||||||
|
Flex::column()
|
||||||
|
.with_children(language_servers.into_iter().filter_map(
|
||||||
|
|(id, name, worktree_id)| {
|
||||||
|
Self::render_language_server_menu_item(
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
worktree_id,
|
||||||
|
&self.project,
|
||||||
|
&theme,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.contained()
|
||||||
|
.with_style(theme.contacts_popover.container)
|
||||||
|
.constrained()
|
||||||
|
.with_width(200.)
|
||||||
|
.with_height(400.)
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
.with_fit_mode(OverlayFitMode::SwitchAnchor)
|
||||||
|
.with_anchor_corner(AnchorCorner::TopRight)
|
||||||
|
.with_z_index(999)
|
||||||
|
.aligned()
|
||||||
|
.bottom()
|
||||||
|
.right()
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LspLogToolbarItemView {
|
||||||
|
pub fn new(project: ModelHandle<Project>) -> Self {
|
||||||
|
Self {
|
||||||
|
menu_open: false,
|
||||||
|
log_view: None,
|
||||||
|
project,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toggle_menu(&mut self, _: &ToggleMenu, cx: &mut ViewContext<Self>) {
|
||||||
|
self.menu_open = !self.menu_open;
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn activate_log_for_server(&mut self, action: &ActivateLog, cx: &mut ViewContext<Self>) {
|
||||||
|
if let Some(log_view) = &self.log_view {
|
||||||
|
log_view.update(cx, |log_view, cx| {
|
||||||
|
log_view.activate_log(action, cx);
|
||||||
|
});
|
||||||
|
self.menu_open = false;
|
||||||
|
}
|
||||||
|
cx.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_language_server_menu_header(
|
||||||
|
current_server: Option<(LanguageServerId, LanguageServerName, WorktreeId)>,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
|
theme: &Arc<Theme>,
|
||||||
|
cx: &mut RenderContext<Self>,
|
||||||
|
) -> ElementBox {
|
||||||
|
MouseEventHandler::<ToggleMenu>::new(0, cx, move |state, cx| {
|
||||||
|
let project = project.read(cx);
|
||||||
|
let label: Cow<str> = current_server
|
||||||
|
.and_then(|(_, server_name, worktree_id)| {
|
||||||
|
let worktree = project.worktree_for_id(worktree_id, cx)?;
|
||||||
|
let worktree = &worktree.read(cx);
|
||||||
|
Some(format!("{} - ({})", server_name.0, worktree.root_name()).into())
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| "No server selected".into());
|
||||||
|
Label::new(label, theme.context_menu.item.default.label.clone()).boxed()
|
||||||
|
})
|
||||||
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(ToggleMenu);
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_language_server_menu_item(
|
||||||
|
id: LanguageServerId,
|
||||||
|
name: LanguageServerName,
|
||||||
|
worktree_id: WorktreeId,
|
||||||
|
project: &ModelHandle<Project>,
|
||||||
|
theme: &Arc<Theme>,
|
||||||
|
cx: &mut RenderContext<Self>,
|
||||||
|
) -> Option<ElementBox> {
|
||||||
|
let project = project.read(cx);
|
||||||
|
let worktree = project.worktree_for_id(worktree_id, cx)?;
|
||||||
|
let worktree = &worktree.read(cx);
|
||||||
|
if !worktree.is_visible() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let label = format!("{} - ({})", name.0, worktree.root_name());
|
||||||
|
|
||||||
|
Some(
|
||||||
|
MouseEventHandler::<ActivateLog>::new(id.0, cx, move |state, cx| {
|
||||||
|
Label::new(label, theme.context_menu.item.default.label.clone()).boxed()
|
||||||
|
})
|
||||||
|
.on_click(MouseButton::Left, move |_, cx| {
|
||||||
|
cx.dispatch_action(ActivateLog { server_id: id })
|
||||||
|
})
|
||||||
|
.boxed(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for LspLogView {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for LspLogToolbarItemView {
|
||||||
|
type Event = ();
|
||||||
|
}
|
@ -185,6 +185,8 @@ pub struct Collaborator {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
|
LanguageServerAdded(LanguageServerId),
|
||||||
|
LanguageServerRemoved(LanguageServerId),
|
||||||
ActiveEntryChanged(Option<ProjectEntryId>),
|
ActiveEntryChanged(Option<ProjectEntryId>),
|
||||||
WorktreeAdded,
|
WorktreeAdded,
|
||||||
WorktreeRemoved(WorktreeId),
|
WorktreeRemoved(WorktreeId),
|
||||||
@ -1869,7 +1871,7 @@ impl Project {
|
|||||||
let next_snapshot = buffer.text_snapshot();
|
let next_snapshot = buffer.text_snapshot();
|
||||||
|
|
||||||
let language_servers: Vec<_> = self
|
let language_servers: Vec<_> = self
|
||||||
.language_servers_iter_for_buffer(buffer, cx)
|
.language_servers_for_buffer(buffer, cx)
|
||||||
.map(|i| i.1.clone())
|
.map(|i| i.1.clone())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@ -6279,7 +6281,25 @@ impl Project {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn language_servers_iter_for_buffer(
|
pub fn language_servers(
|
||||||
|
&self,
|
||||||
|
) -> impl '_ + Iterator<Item = (LanguageServerId, LanguageServerName, WorktreeId)> {
|
||||||
|
self.language_server_ids
|
||||||
|
.iter()
|
||||||
|
.map(|((worktree_id, server_name), server_id)| {
|
||||||
|
(*server_id, server_name.clone(), *worktree_id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
|
||||||
|
if let LanguageServerState::Running { server, .. } = self.language_servers.get(&id)? {
|
||||||
|
Some(server.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn language_servers_for_buffer(
|
||||||
&self,
|
&self,
|
||||||
buffer: &Buffer,
|
buffer: &Buffer,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
@ -6299,20 +6319,12 @@ impl Project {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn language_servers_for_buffer(
|
|
||||||
&self,
|
|
||||||
buffer: &Buffer,
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> Vec<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
|
|
||||||
self.language_servers_iter_for_buffer(buffer, cx).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn primary_language_servers_for_buffer(
|
fn primary_language_servers_for_buffer(
|
||||||
&self,
|
&self,
|
||||||
buffer: &Buffer,
|
buffer: &Buffer,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
|
) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
|
||||||
self.language_servers_iter_for_buffer(buffer, cx).next()
|
self.language_servers_for_buffer(buffer, cx).next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn language_server_for_buffer(
|
fn language_server_for_buffer(
|
||||||
@ -6321,7 +6333,7 @@ impl Project {
|
|||||||
server_id: LanguageServerId,
|
server_id: LanguageServerId,
|
||||||
cx: &AppContext,
|
cx: &AppContext,
|
||||||
) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
|
) -> Option<(&Arc<CachedLspAdapter>, &Arc<LanguageServer>)> {
|
||||||
self.language_servers_iter_for_buffer(buffer, cx)
|
self.language_servers_for_buffer(buffer, cx)
|
||||||
.find(|(_, s)| s.server_id() == server_id)
|
.find(|(_, s)| s.server_id() == server_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ journal = { path = "../journal" }
|
|||||||
language = { path = "../language" }
|
language = { path = "../language" }
|
||||||
language_selector = { path = "../language_selector" }
|
language_selector = { path = "../language_selector" }
|
||||||
lsp = { path = "../lsp" }
|
lsp = { path = "../lsp" }
|
||||||
|
lsp_log = { path = "../lsp_log" }
|
||||||
node_runtime = { path = "../node_runtime" }
|
node_runtime = { path = "../node_runtime" }
|
||||||
outline = { path = "../outline" }
|
outline = { path = "../outline" }
|
||||||
plugin_runtime = { path = "../plugin_runtime" }
|
plugin_runtime = { path = "../plugin_runtime" }
|
||||||
|
@ -262,6 +262,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
|
|||||||
);
|
);
|
||||||
activity_indicator::init(cx);
|
activity_indicator::init(cx);
|
||||||
copilot_button::init(cx);
|
copilot_button::init(cx);
|
||||||
|
lsp_log::init(cx);
|
||||||
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
call::init(app_state.client.clone(), app_state.user_store.clone(), cx);
|
||||||
settings::KeymapFileContent::load_defaults(cx);
|
settings::KeymapFileContent::load_defaults(cx);
|
||||||
}
|
}
|
||||||
@ -273,7 +274,7 @@ pub fn initialize_workspace(
|
|||||||
) {
|
) {
|
||||||
let workspace_handle = cx.handle();
|
let workspace_handle = cx.handle();
|
||||||
cx.subscribe(&workspace_handle, {
|
cx.subscribe(&workspace_handle, {
|
||||||
move |_, _, event, cx| {
|
move |workspace, _, event, cx| {
|
||||||
if let workspace::Event::PaneAdded(pane) = event {
|
if let workspace::Event::PaneAdded(pane) = event {
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
pane.toolbar().update(cx, |toolbar, cx| {
|
pane.toolbar().update(cx, |toolbar, cx| {
|
||||||
@ -287,6 +288,10 @@ pub fn initialize_workspace(
|
|||||||
toolbar.add_item(submit_feedback_button, cx);
|
toolbar.add_item(submit_feedback_button, cx);
|
||||||
let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
|
let feedback_info_text = cx.add_view(|_| FeedbackInfoText::new());
|
||||||
toolbar.add_item(feedback_info_text, cx);
|
toolbar.add_item(feedback_info_text, cx);
|
||||||
|
let lsp_log_item = cx.add_view(|_| {
|
||||||
|
lsp_log::LspLogToolbarItemView::new(workspace.project().clone())
|
||||||
|
});
|
||||||
|
toolbar.add_item(lsp_log_item, cx);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user