Compare commits
1 Commits
diff-perf-
...
tag-stack-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b69a2ea200 |
@@ -839,7 +839,7 @@ ui_input = { codegen-units = 1 }
|
||||
zed_actions = { codegen-units = 1 }
|
||||
|
||||
[profile.release]
|
||||
debug = "full"
|
||||
debug = "limited"
|
||||
lto = "thin"
|
||||
codegen-units = 1
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@
|
||||
"ctrl-o": "pane::GoBack",
|
||||
"ctrl-i": "pane::GoForward",
|
||||
"ctrl-]": "editor::GoToDefinition",
|
||||
"ctrl-t": "pane::GoToOlderTag",
|
||||
"escape": "vim::SwitchToNormalMode",
|
||||
"ctrl-[": "vim::SwitchToNormalMode",
|
||||
"v": "vim::ToggleVisual",
|
||||
|
||||
@@ -2051,15 +2051,6 @@ impl AcpThreadView {
|
||||
.into_any(),
|
||||
};
|
||||
|
||||
let needs_confirmation = if let AgentThreadEntry::ToolCall(tool_call) = entry {
|
||||
matches!(
|
||||
tool_call.status,
|
||||
ToolCallStatus::WaitingForConfirmation { .. }
|
||||
)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let Some(thread) = self.thread() else {
|
||||
return primary;
|
||||
};
|
||||
@@ -2068,13 +2059,7 @@ impl AcpThreadView {
|
||||
v_flex()
|
||||
.w_full()
|
||||
.child(primary)
|
||||
.map(|this| {
|
||||
if needs_confirmation {
|
||||
this.child(self.render_generating(true))
|
||||
} else {
|
||||
this.child(self.render_thread_controls(&thread, cx))
|
||||
}
|
||||
})
|
||||
.child(self.render_thread_controls(&thread, cx))
|
||||
.when_some(
|
||||
self.thread_feedback.comments_editor.clone(),
|
||||
|this, editor| this.child(Self::render_feedback_feedback_editor(editor, cx)),
|
||||
@@ -4844,31 +4829,6 @@ impl AcpThreadView {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_generating(&self, confirmation: bool) -> impl IntoElement {
|
||||
h_flex()
|
||||
.id("generating-spinner")
|
||||
.py_2()
|
||||
.px(rems_from_px(22.))
|
||||
.map(|this| {
|
||||
if confirmation {
|
||||
this.gap_2()
|
||||
.child(
|
||||
h_flex()
|
||||
.w_2()
|
||||
.child(SpinnerLabel::sand().size(LabelSize::Small)),
|
||||
)
|
||||
.child(
|
||||
LoadingLabel::new("Waiting Confirmation")
|
||||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
} else {
|
||||
this.child(SpinnerLabel::new().size(LabelSize::Small))
|
||||
}
|
||||
})
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_thread_controls(
|
||||
&self,
|
||||
thread: &Entity<AcpThread>,
|
||||
@@ -4876,7 +4836,12 @@ impl AcpThreadView {
|
||||
) -> impl IntoElement {
|
||||
let is_generating = matches!(thread.read(cx).status(), ThreadStatus::Generating);
|
||||
if is_generating {
|
||||
return self.render_generating(false).into_any_element();
|
||||
return h_flex().id("thread-controls-container").child(
|
||||
div()
|
||||
.py_2()
|
||||
.px(rems_from_px(22.))
|
||||
.child(SpinnerLabel::new().size(LabelSize::Small)),
|
||||
);
|
||||
}
|
||||
|
||||
let open_as_markdown = IconButton::new("open-as-markdown", IconName::FileMarkdown)
|
||||
@@ -4964,10 +4929,7 @@ impl AcpThreadView {
|
||||
);
|
||||
}
|
||||
|
||||
container
|
||||
.child(open_as_markdown)
|
||||
.child(scroll_to_top)
|
||||
.into_any_element()
|
||||
container.child(open_as_markdown).child(scroll_to_top)
|
||||
}
|
||||
|
||||
fn render_feedback_feedback_editor(editor: Entity<Editor>, cx: &Context<Self>) -> Div {
|
||||
|
||||
@@ -1013,7 +1013,7 @@ impl AgentConfiguration {
|
||||
.child(Divider::horizontal().color(DividerColor::BorderFaded))
|
||||
.child(self.render_agent_server(
|
||||
AgentIcon::Name(IconName::AiOpenAi),
|
||||
"Codex CLI",
|
||||
"Codex",
|
||||
false,
|
||||
))
|
||||
.child(Divider::horizontal().color(DividerColor::BorderFaded))
|
||||
|
||||
@@ -25,6 +25,7 @@ use std::{
|
||||
any::{Any, TypeId},
|
||||
collections::hash_map::Entry,
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{CommonAnimationExt, IconButtonShape, KeyBinding, Tooltip, prelude::*, vertical_divider};
|
||||
@@ -516,12 +517,7 @@ impl Item for AgentDiffPane {
|
||||
.update(cx, |editor, cx| editor.deactivated(window, cx));
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -1880,12 +1880,7 @@ impl AgentPanel {
|
||||
{
|
||||
let focus_handle = focus_handle.clone();
|
||||
move |_window, cx| {
|
||||
Tooltip::for_action_in(
|
||||
"New Thread…",
|
||||
&ToggleNewThreadMenu,
|
||||
&focus_handle,
|
||||
cx,
|
||||
)
|
||||
Tooltip::for_action_in("New…", &ToggleNewThreadMenu, &focus_handle, cx)
|
||||
}
|
||||
},
|
||||
)
|
||||
@@ -1983,7 +1978,7 @@ impl AgentPanel {
|
||||
.separator()
|
||||
.header("External Agents")
|
||||
.item(
|
||||
ContextMenuEntry::new("New Claude Code")
|
||||
ContextMenuEntry::new("New Claude Code Thread")
|
||||
.icon(IconName::AiClaude)
|
||||
.disabled(is_via_collab)
|
||||
.icon_color(Color::Muted)
|
||||
@@ -2009,7 +2004,7 @@ impl AgentPanel {
|
||||
}),
|
||||
)
|
||||
.item(
|
||||
ContextMenuEntry::new("New Codex CLI")
|
||||
ContextMenuEntry::new("New Codex Thread")
|
||||
.icon(IconName::AiOpenAi)
|
||||
.disabled(is_via_collab)
|
||||
.icon_color(Color::Muted)
|
||||
@@ -2035,7 +2030,7 @@ impl AgentPanel {
|
||||
}),
|
||||
)
|
||||
.item(
|
||||
ContextMenuEntry::new("New Gemini CLI")
|
||||
ContextMenuEntry::new("New Gemini CLI Thread")
|
||||
.icon(IconName::AiGemini)
|
||||
.icon_color(Color::Muted)
|
||||
.disabled(is_via_collab)
|
||||
@@ -2079,7 +2074,7 @@ impl AgentPanel {
|
||||
for agent_name in agent_names {
|
||||
let icon_path = agent_server_store_read.agent_icon(&agent_name);
|
||||
let mut entry =
|
||||
ContextMenuEntry::new(format!("New {}", agent_name));
|
||||
ContextMenuEntry::new(format!("New {} Thread", agent_name));
|
||||
if let Some(icon_path) = icon_path {
|
||||
entry = entry.custom_icon_path(icon_path);
|
||||
} else {
|
||||
|
||||
@@ -2530,7 +2530,7 @@ impl Item for TextThreadEditor {
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn std::any::Any>,
|
||||
data: Rc<dyn std::any::Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
|
||||
@@ -18,6 +18,7 @@ use project::Project;
|
||||
use rpc::proto::ChannelVisibility;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::prelude::*;
|
||||
@@ -515,12 +516,7 @@ impl Item for ChannelView {
|
||||
})))
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
use collections::HashMap;
|
||||
use dap::StackFrameId;
|
||||
@@ -331,12 +334,7 @@ impl Item for StackTraceView {
|
||||
.update(cx, |editor, cx| editor.deactivated(window, cx));
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ use settings::Settings;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cmp::Ordering,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use text::{Anchor, BufferSnapshot, OffsetRangeExt};
|
||||
@@ -734,12 +735,7 @@ impl Item for BufferDiagnosticsEditor {
|
||||
self.multibuffer.read(cx).is_dirty(cx)
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ use std::{
|
||||
any::{Any, TypeId},
|
||||
cmp,
|
||||
ops::{Range, RangeInclusive},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
@@ -728,12 +729,7 @@ impl Item for ProjectDiagnosticsEditor {
|
||||
.update(cx, |editor, cx| editor.deactivated(window, cx));
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -7599,17 +7599,18 @@ impl Editor {
|
||||
)
|
||||
}
|
||||
|
||||
fn is_cmd_or_ctrl_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
|
||||
match EditorSettings::get_global(cx).multi_cursor_modifier {
|
||||
MultiCursorModifier::Alt => modifiers.secondary(),
|
||||
MultiCursorModifier::CmdOrCtrl => modifiers.alt,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_alt_pressed(modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
|
||||
match EditorSettings::get_global(cx).multi_cursor_modifier {
|
||||
MultiCursorModifier::Alt => modifiers.alt,
|
||||
MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
|
||||
fn multi_cursor_modifier(invert: bool, modifiers: &Modifiers, cx: &mut Context<Self>) -> bool {
|
||||
let multi_cursor_setting = EditorSettings::get_global(cx).multi_cursor_modifier;
|
||||
if invert {
|
||||
match multi_cursor_setting {
|
||||
MultiCursorModifier::Alt => modifiers.alt,
|
||||
MultiCursorModifier::CmdOrCtrl => modifiers.secondary(),
|
||||
}
|
||||
} else {
|
||||
match multi_cursor_setting {
|
||||
MultiCursorModifier::Alt => modifiers.secondary(),
|
||||
MultiCursorModifier::CmdOrCtrl => modifiers.alt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7618,9 +7619,9 @@ impl Editor {
|
||||
cx: &mut Context<Self>,
|
||||
) -> Option<ColumnarMode> {
|
||||
if modifiers.shift && modifiers.number_of_modifiers() == 2 {
|
||||
if Self::is_cmd_or_ctrl_pressed(modifiers, cx) {
|
||||
if Self::multi_cursor_modifier(false, modifiers, cx) {
|
||||
Some(ColumnarMode::FromMouse)
|
||||
} else if Self::is_alt_pressed(modifiers, cx) {
|
||||
} else if Self::multi_cursor_modifier(true, modifiers, cx) {
|
||||
Some(ColumnarMode::FromSelection)
|
||||
} else {
|
||||
None
|
||||
@@ -14037,6 +14038,27 @@ impl Editor {
|
||||
);
|
||||
}
|
||||
|
||||
fn finish_tag_jump(&mut self, point: Point, cx: &mut Context<Self>) {
|
||||
let Some(nav_history) = self.nav_history.as_mut() else {
|
||||
return;
|
||||
};
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let cursor_anchor = snapshot.anchor_after(point);
|
||||
let cursor_position = cursor_anchor.to_point(&snapshot);
|
||||
let scroll_state = self.scroll_manager.anchor();
|
||||
let scroll_top_row = scroll_state.top_row(&snapshot);
|
||||
dbg!("finish tag jump", cursor_position);
|
||||
nav_history.finish_tag_jump(
|
||||
Some(NavigationData {
|
||||
cursor_anchor,
|
||||
cursor_position,
|
||||
scroll_anchor: scroll_state,
|
||||
scroll_top_row,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
fn push_to_nav_history(
|
||||
&mut self,
|
||||
cursor_anchor: Anchor,
|
||||
@@ -16436,18 +16458,37 @@ impl Editor {
|
||||
let Some(provider) = self.semantics_provider.clone() else {
|
||||
return Task::ready(Ok(Navigated::No));
|
||||
};
|
||||
let head = self
|
||||
let cursor = self
|
||||
.selections
|
||||
.newest::<usize>(&self.display_snapshot(cx))
|
||||
.head();
|
||||
let buffer = self.buffer.read(cx);
|
||||
let Some((buffer, head)) = buffer.text_anchor_for_position(head, cx) else {
|
||||
let multi_buffer = self.buffer.read(cx);
|
||||
let Some((buffer, head)) = multi_buffer.text_anchor_for_position(cursor, cx) else {
|
||||
return Task::ready(Ok(Navigated::No));
|
||||
};
|
||||
let Some(definitions) = provider.definitions(&buffer, head, kind, cx) else {
|
||||
return Task::ready(Ok(Navigated::No));
|
||||
};
|
||||
|
||||
if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
let snapshot = self.buffer.read(cx).snapshot(cx);
|
||||
let cursor_anchor = snapshot.anchor_after(cursor);
|
||||
let cursor_position = snapshot.offset_to_point(cursor);
|
||||
let scroll_anchor = self.scroll_manager.anchor();
|
||||
let scroll_top_row = scroll_anchor.top_row(&snapshot);
|
||||
|
||||
dbg!("start tag jump", cursor_position);
|
||||
nav_history.start_tag_jump(
|
||||
Some(NavigationData {
|
||||
cursor_anchor,
|
||||
cursor_position,
|
||||
scroll_anchor,
|
||||
scroll_top_row,
|
||||
}),
|
||||
cx,
|
||||
);
|
||||
}
|
||||
|
||||
cx.spawn_in(window, async move |editor, cx| {
|
||||
let Some(definitions) = definitions.await? else {
|
||||
return Ok(Navigated::No);
|
||||
@@ -16692,6 +16733,7 @@ impl Editor {
|
||||
if !split
|
||||
&& Some(&target_buffer) == editor.buffer.read(cx).as_singleton().as_ref()
|
||||
{
|
||||
editor.finish_tag_jump(range.start, cx);
|
||||
editor.go_to_singleton_buffer_range(range, window, cx);
|
||||
} else {
|
||||
let pane = workspace.read(cx).active_pane().clone();
|
||||
@@ -16717,6 +16759,7 @@ impl Editor {
|
||||
// When selecting a definition in a different buffer, disable the nav history
|
||||
// to avoid creating a history entry at the previous cursor location.
|
||||
pane.update(cx, |pane, _| pane.disable_history());
|
||||
target_editor.finish_tag_jump(range.start, cx);
|
||||
target_editor.go_to_singleton_buffer_range(range, window, cx);
|
||||
pane.update(cx, |pane, _| pane.enable_history());
|
||||
});
|
||||
@@ -17038,6 +17081,7 @@ impl Editor {
|
||||
|
||||
multibuffer.with_title(title)
|
||||
});
|
||||
let first_range = ranges.first().cloned();
|
||||
let existing = workspace.active_pane().update(cx, |pane, cx| {
|
||||
pane.items()
|
||||
.filter_map(|item| item.downcast::<Editor>())
|
||||
@@ -17090,6 +17134,21 @@ impl Editor {
|
||||
});
|
||||
}
|
||||
});
|
||||
cx.defer({
|
||||
let editor = editor.clone();
|
||||
move |cx| {
|
||||
let Some(range) = first_range else { return };
|
||||
editor.update(cx, |editor, cx| {
|
||||
let point = editor
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.snapshot(cx)
|
||||
.summary_for_anchor(&range.start);
|
||||
|
||||
editor.finish_tag_jump(point, cx)
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
let item = Box::new(editor);
|
||||
let item_id = item.item_id();
|
||||
|
||||
@@ -911,7 +911,7 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
|
||||
invalid_anchor.text_anchor.buffer_id = BufferId::new(999).ok();
|
||||
let invalid_point = Point::new(9999, 0);
|
||||
editor.navigate(
|
||||
Box::new(NavigationData {
|
||||
Rc::new(NavigationData {
|
||||
cursor_anchor: invalid_anchor,
|
||||
cursor_position: invalid_point,
|
||||
scroll_anchor: ScrollAnchor {
|
||||
|
||||
@@ -820,7 +820,7 @@ impl EditorElement {
|
||||
editor.select(
|
||||
SelectPhase::Begin {
|
||||
position,
|
||||
add: Editor::is_alt_pressed(&modifiers, cx),
|
||||
add: Editor::multi_cursor_modifier(true, &modifiers, cx),
|
||||
click_count,
|
||||
},
|
||||
window,
|
||||
@@ -1004,7 +1004,7 @@ impl EditorElement {
|
||||
let text_hitbox = &position_map.text_hitbox;
|
||||
let pending_nonempty_selections = editor.has_pending_nonempty_selection();
|
||||
|
||||
let hovered_link_modifier = Editor::is_cmd_or_ctrl_pressed(&event.modifiers(), cx);
|
||||
let hovered_link_modifier = Editor::multi_cursor_modifier(false, &event.modifiers(), cx);
|
||||
|
||||
if let Some(mouse_position) = event.mouse_position()
|
||||
&& !pending_nonempty_selections
|
||||
|
||||
@@ -116,7 +116,7 @@ impl Editor {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let hovered_link_modifier = Editor::is_cmd_or_ctrl_pressed(&modifiers, cx);
|
||||
let hovered_link_modifier = Editor::multi_cursor_modifier(false, &modifiers, cx);
|
||||
if !hovered_link_modifier || self.has_pending_selection() {
|
||||
self.hide_hovered_link(cx);
|
||||
return;
|
||||
@@ -241,8 +241,21 @@ impl Editor {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let split = Self::is_alt_pressed(&modifiers, cx);
|
||||
let navigate_task = self.navigate_to_hover_links(None, links, split, window, cx);
|
||||
|
||||
// todo!()
|
||||
// if let Some(nav_history) = self.nav_history.as_mut() {
|
||||
// nav_history.start_tag_jump(
|
||||
// Some(NavigationData {
|
||||
// cursor_anchor,
|
||||
// cursor_position,
|
||||
// scroll_anchor: scroll_state,
|
||||
// scroll_top_row,
|
||||
// }),
|
||||
// cx,
|
||||
// );
|
||||
// }
|
||||
let navigate_task =
|
||||
self.navigate_to_hover_links(None, links, modifiers.alt, window, cx);
|
||||
self.select(SelectPhase::End, window, cx);
|
||||
return navigate_task;
|
||||
}
|
||||
@@ -261,8 +274,7 @@ impl Editor {
|
||||
);
|
||||
|
||||
let navigate_task = if point.as_valid().is_some() {
|
||||
let split = Self::is_alt_pressed(&modifiers, cx);
|
||||
match (modifiers.shift, split) {
|
||||
match (modifiers.shift, modifiers.alt) {
|
||||
(true, true) => {
|
||||
self.go_to_type_definition_split(&GoToTypeDefinitionSplit, window, cx)
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ use std::{
|
||||
iter,
|
||||
ops::Range,
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use text::{BufferId, BufferSnapshot, Selection};
|
||||
@@ -589,7 +590,7 @@ impl Item for Editor {
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn std::any::Any>,
|
||||
data: Rc<dyn std::any::Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
|
||||
@@ -17,6 +17,7 @@ use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Write as _,
|
||||
path::PathBuf,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{
|
||||
@@ -527,12 +528,7 @@ impl Item for CommitView {
|
||||
});
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ use std::{
|
||||
any::{Any, TypeId},
|
||||
path::PathBuf,
|
||||
pin::pin,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
@@ -301,12 +302,7 @@ impl Item for FileDiffView {
|
||||
});
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -32,9 +32,12 @@ use project::{
|
||||
},
|
||||
};
|
||||
use settings::{Settings, SettingsStore};
|
||||
use std::{any::{Any, TypeId}, time::Duration};
|
||||
use std::ops::Range;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
rc::Rc,
|
||||
};
|
||||
use theme::ActiveTheme;
|
||||
use ui::{KeyBinding, Tooltip, prelude::*, vertical_divider};
|
||||
use util::{ResultExt as _, rel_path::RelPath};
|
||||
@@ -590,10 +593,6 @@ impl ProjectDiff {
|
||||
.ok();
|
||||
})?;
|
||||
}
|
||||
cx.background_executor().timer(Duration::from_millis(5)).await;
|
||||
this.update(cx, |_, cx| {
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
this.update(cx, |this, cx| {
|
||||
this.pending_scroll.take();
|
||||
@@ -653,12 +652,7 @@ impl Item for ProjectDiff {
|
||||
.update(cx, |editor, cx| editor.deactivated(window, cx));
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use std::{
|
||||
cmp,
|
||||
ops::Range,
|
||||
pin::pin,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
@@ -362,12 +363,7 @@ impl Item for TextDiffView {
|
||||
});
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.diff_editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -26,8 +26,10 @@ pub struct KeyDownEvent {
|
||||
/// Whether the key is currently held down.
|
||||
pub is_held: bool,
|
||||
|
||||
/// Whether to prefer character input over keybindings for this keystroke.
|
||||
/// In some cases, like AltGr on Windows, modifiers are significant for character input.
|
||||
/// Whether the modifiers are excessive for producing this character.
|
||||
/// When false, the modifiers are essential for character input (e.g., AltGr),
|
||||
/// and character input should be prioritized over keybindings.
|
||||
/// When true, the modifiers are for keybindings (e.g., Ctrl+A).
|
||||
pub prefer_character_input: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -1387,8 +1387,6 @@ fn should_prefer_character_input(vkey: VIRTUAL_KEY, scan_code: u16) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Workaround for some bug that makes the compiler think keyboard_state is still zeroed out
|
||||
let keyboard_state = std::hint::black_box(keyboard_state);
|
||||
let ctrl_down = (keyboard_state[VK_CONTROL.0 as usize] & 0x80) != 0;
|
||||
let alt_down = (keyboard_state[VK_MENU.0 as usize] & 0x80) != 0;
|
||||
let win_down = (keyboard_state[VK_LWIN.0 as usize] & 0x80) != 0
|
||||
|
||||
@@ -969,7 +969,6 @@ impl Buffer {
|
||||
|
||||
/// Builds a [`Buffer`] with the given underlying [`TextBuffer`], diff base, [`File`] and [`Capability`].
|
||||
pub fn build(buffer: TextBuffer, file: Option<Arc<dyn File>>, capability: Capability) -> Self {
|
||||
log::info!("file: {:?}", file.as_ref().map(|f| f.path()));
|
||||
let saved_mtime = file.as_ref().and_then(|file| file.disk_state().mtime());
|
||||
let snapshot = buffer.snapshot();
|
||||
let syntax_map = Mutex::new(SyntaxMap::new(&snapshot));
|
||||
@@ -3367,19 +3366,7 @@ impl BufferSnapshot {
|
||||
pub fn syntax_layer_at<D: ToOffset>(&self, position: D) -> Option<SyntaxLayer<'_>> {
|
||||
let offset = position.to_offset(self);
|
||||
self.syntax_layers_for_range(offset..offset, false)
|
||||
.filter(|l| {
|
||||
if let Some(ranges) = l.included_sub_ranges {
|
||||
ranges.iter().any(|range| {
|
||||
let start = range.start.to_offset(self);
|
||||
start <= offset && {
|
||||
let end = range.end.to_offset(self);
|
||||
offset < end
|
||||
}
|
||||
})
|
||||
} else {
|
||||
l.node().start_byte() <= offset && l.node().end_byte() > offset
|
||||
}
|
||||
})
|
||||
.filter(|l| l.node().end_byte() > offset)
|
||||
.last()
|
||||
}
|
||||
|
||||
|
||||
@@ -2633,7 +2633,7 @@ fn test_language_scope_at_with_combined_injections(cx: &mut App) {
|
||||
buffer.set_language_registry(language_registry.clone());
|
||||
buffer.set_language(
|
||||
language_registry
|
||||
.language_for_name("HTML+ERB")
|
||||
.language_for_name("ERB")
|
||||
.now_or_never()
|
||||
.unwrap()
|
||||
.ok(),
|
||||
@@ -2753,50 +2753,6 @@ fn test_language_at_for_markdown_code_block(cx: &mut App) {
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_syntax_layer_at_for_injected_languages(cx: &mut App) {
|
||||
init_settings(cx, |_| {});
|
||||
|
||||
cx.new(|cx| {
|
||||
let text = r#"
|
||||
```html+erb
|
||||
<div>Hello</div>
|
||||
<%= link_to "Some", "https://zed.dev" %>
|
||||
```
|
||||
"#
|
||||
.unindent();
|
||||
|
||||
let language_registry = Arc::new(LanguageRegistry::test(cx.background_executor().clone()));
|
||||
language_registry.add(Arc::new(erb_lang()));
|
||||
language_registry.add(Arc::new(html_lang()));
|
||||
language_registry.add(Arc::new(ruby_lang()));
|
||||
|
||||
let mut buffer = Buffer::local(text, cx);
|
||||
buffer.set_language_registry(language_registry.clone());
|
||||
buffer.set_language(
|
||||
language_registry
|
||||
.language_for_name("HTML+ERB")
|
||||
.now_or_never()
|
||||
.unwrap()
|
||||
.ok(),
|
||||
cx,
|
||||
);
|
||||
|
||||
let snapshot = buffer.snapshot();
|
||||
|
||||
// Test points in the code line
|
||||
let html_point = Point::new(1, 4);
|
||||
let language = snapshot.language_at(html_point).unwrap();
|
||||
assert_eq!(language.name().as_ref(), "HTML");
|
||||
|
||||
let ruby_point = Point::new(2, 6);
|
||||
let language = snapshot.language_at(ruby_point).unwrap();
|
||||
assert_eq!(language.name().as_ref(), "Ruby");
|
||||
|
||||
buffer
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn test_serialization(cx: &mut gpui::App) {
|
||||
let mut now = Instant::now();
|
||||
@@ -3699,7 +3655,7 @@ fn html_lang() -> Language {
|
||||
fn erb_lang() -> Language {
|
||||
Language::new(
|
||||
LanguageConfig {
|
||||
name: "HTML+ERB".into(),
|
||||
name: "ERB".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["erb".to_string()],
|
||||
..Default::default()
|
||||
@@ -3717,15 +3673,15 @@ fn erb_lang() -> Language {
|
||||
.with_injection_query(
|
||||
r#"
|
||||
(
|
||||
(code) @content
|
||||
(#set! "language" "ruby")
|
||||
(#set! "combined")
|
||||
(code) @injection.content
|
||||
(#set! injection.language "ruby")
|
||||
(#set! injection.combined)
|
||||
)
|
||||
|
||||
(
|
||||
(content) @content
|
||||
(#set! "language" "html")
|
||||
(#set! "combined")
|
||||
(content) @injection.content
|
||||
(#set! injection.language "html")
|
||||
(#set! injection.combined)
|
||||
)
|
||||
"#,
|
||||
)
|
||||
|
||||
@@ -587,8 +587,6 @@ impl SyntaxSnapshot {
|
||||
let changed_ranges;
|
||||
|
||||
let mut included_ranges = step.included_ranges;
|
||||
let is_combined = matches!(step.mode, ParseMode::Combined { .. });
|
||||
|
||||
for range in &mut included_ranges {
|
||||
range.start_byte -= step_start_byte;
|
||||
range.end_byte -= step_start_byte;
|
||||
@@ -751,20 +749,16 @@ impl SyntaxSnapshot {
|
||||
);
|
||||
}
|
||||
|
||||
let included_sub_ranges: Option<Vec<Range<Anchor>>> = if is_combined {
|
||||
Some(
|
||||
let included_sub_ranges: Option<Vec<Range<Anchor>>> =
|
||||
(included_ranges.len() > 1).then_some(
|
||||
included_ranges
|
||||
.into_iter()
|
||||
.filter(|r| r.start_byte < r.end_byte)
|
||||
.map(|r| {
|
||||
text.anchor_before(r.start_byte + step_start_byte)
|
||||
..text.anchor_after(r.end_byte + step_start_byte)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
);
|
||||
SyntaxLayerContent::Parsed {
|
||||
tree,
|
||||
language,
|
||||
|
||||
@@ -538,27 +538,27 @@ impl OpenAiEventMapper {
|
||||
return events;
|
||||
};
|
||||
|
||||
if let Some(delta) = choice.delta.as_ref() {
|
||||
if let Some(content) = delta.content.clone() {
|
||||
if let Some(content) = choice.delta.content.clone() {
|
||||
if !content.is_empty() {
|
||||
events.push(Ok(LanguageModelCompletionEvent::Text(content)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tool_calls) = delta.tool_calls.as_ref() {
|
||||
for tool_call in tool_calls {
|
||||
let entry = self.tool_calls_by_index.entry(tool_call.index).or_default();
|
||||
if let Some(tool_calls) = choice.delta.tool_calls.as_ref() {
|
||||
for tool_call in tool_calls {
|
||||
let entry = self.tool_calls_by_index.entry(tool_call.index).or_default();
|
||||
|
||||
if let Some(tool_id) = tool_call.id.clone() {
|
||||
entry.id = tool_id;
|
||||
if let Some(tool_id) = tool_call.id.clone() {
|
||||
entry.id = tool_id;
|
||||
}
|
||||
|
||||
if let Some(function) = tool_call.function.as_ref() {
|
||||
if let Some(name) = function.name.clone() {
|
||||
entry.name = name;
|
||||
}
|
||||
|
||||
if let Some(function) = tool_call.function.as_ref() {
|
||||
if let Some(name) = function.name.clone() {
|
||||
entry.name = name;
|
||||
}
|
||||
|
||||
if let Some(arguments) = function.arguments.clone() {
|
||||
entry.arguments.push_str(&arguments);
|
||||
}
|
||||
if let Some(arguments) = function.arguments.clone() {
|
||||
entry.arguments.push_str(&arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ pub struct Usage {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct ChoiceDelta {
|
||||
pub index: u32,
|
||||
pub delta: Option<ResponseMessageDelta>,
|
||||
pub delta: ResponseMessageDelta,
|
||||
pub finish_reason: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -319,8 +319,6 @@ impl BranchDiff {
|
||||
});
|
||||
}
|
||||
});
|
||||
// let names: Vec<_> = output.iter().map(|o| o.repo_path.as_unix_str()).collect();
|
||||
// eprintln!("OUTPUT IS *********************************: {names:?}");
|
||||
output
|
||||
}
|
||||
|
||||
|
||||
@@ -12349,7 +12349,10 @@ impl LspStore {
|
||||
.update(cx, |buffer, _| buffer.wait_for_version(version))?
|
||||
.await?;
|
||||
lsp_store.update(cx, |lsp_store, cx| {
|
||||
let lsp_data = lsp_store.latest_lsp_data(&buffer, cx);
|
||||
let lsp_data = lsp_store
|
||||
.lsp_data
|
||||
.entry(buffer_id)
|
||||
.or_insert_with(|| BufferLspData::new(&buffer, cx));
|
||||
let chunks_queried_for = lsp_data
|
||||
.inlay_hints
|
||||
.applicable_chunks(&[range])
|
||||
|
||||
@@ -1070,21 +1070,14 @@ impl SshSocket {
|
||||
}
|
||||
|
||||
async fn shell(&self) -> String {
|
||||
let default_shell = "sh";
|
||||
match self
|
||||
.run_command(ShellKind::Posix, "sh", &["-c", "echo $SHELL"])
|
||||
.await
|
||||
{
|
||||
Ok(shell) => match shell.trim() {
|
||||
"" => {
|
||||
log::error!("$SHELL is not set, falling back to {default_shell}");
|
||||
default_shell.to_owned()
|
||||
}
|
||||
shell => shell.to_owned(),
|
||||
},
|
||||
Ok(shell) => shell.trim().to_owned(),
|
||||
Err(e) => {
|
||||
log::error!("Failed to get shell: {e}");
|
||||
default_shell.to_owned()
|
||||
"sh".to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ use std::{
|
||||
mem,
|
||||
ops::{Not, Range},
|
||||
pin::pin,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
use ui::{IconButtonShape, KeyBinding, Toggleable, Tooltip, prelude::*, utils::SearchInputWidth};
|
||||
@@ -633,12 +634,7 @@ impl Item for ProjectSearchView {
|
||||
});
|
||||
}
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
data: Box<dyn Any>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> bool {
|
||||
fn navigate(&mut self, data: Rc<dyn Any>, window: &mut Window, cx: &mut Context<Self>) -> bool {
|
||||
self.results_editor
|
||||
.update(cx, |editor, cx| editor.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::prelude::*;
|
||||
use gpui::{Animation, AnimationExt, FontWeight};
|
||||
use gpui::{Animation, AnimationExt, FontWeight, pulsating_between};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
@@ -84,29 +84,38 @@ impl RenderOnce for LoadingLabel {
|
||||
fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
|
||||
let text = self.text.clone();
|
||||
|
||||
self.base.color(Color::Muted).with_animations(
|
||||
"loading_label",
|
||||
vec![
|
||||
Animation::new(Duration::from_secs(1)),
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
],
|
||||
move |mut label, animation_ix, delta| {
|
||||
match animation_ix {
|
||||
0 => {
|
||||
let chars_to_show = (delta * text.len() as f32).ceil() as usize;
|
||||
let text = SharedString::from(text[0..chars_to_show].to_string());
|
||||
label.set_text(text);
|
||||
self.base
|
||||
.color(Color::Muted)
|
||||
.with_animations(
|
||||
"loading_label",
|
||||
vec![
|
||||
Animation::new(Duration::from_secs(1)),
|
||||
Animation::new(Duration::from_secs(1)).repeat(),
|
||||
],
|
||||
move |mut label, animation_ix, delta| {
|
||||
match animation_ix {
|
||||
0 => {
|
||||
let chars_to_show = (delta * text.len() as f32).ceil() as usize;
|
||||
let text = SharedString::from(text[0..chars_to_show].to_string());
|
||||
label.set_text(text);
|
||||
}
|
||||
1 => match delta {
|
||||
d if d < 0.25 => label.set_text(text.clone()),
|
||||
d if d < 0.5 => label.set_text(format!("{}.", text)),
|
||||
d if d < 0.75 => label.set_text(format!("{}..", text)),
|
||||
_ => label.set_text(format!("{}...", text)),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
1 => match delta {
|
||||
d if d < 0.25 => label.set_text(text.clone()),
|
||||
d if d < 0.5 => label.set_text(format!("{}.", text)),
|
||||
d if d < 0.75 => label.set_text(format!("{}..", text)),
|
||||
_ => label.set_text(format!("{}...", text)),
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
label
|
||||
},
|
||||
)
|
||||
label
|
||||
},
|
||||
)
|
||||
.with_animation(
|
||||
"pulsating-label",
|
||||
Animation::new(Duration::from_secs(2))
|
||||
.repeat()
|
||||
.with_easing(pulsating_between(0.6, 1.)),
|
||||
|label, delta| label.map_element(|label| label.alpha(delta)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ pub enum SpinnerVariant {
|
||||
#[default]
|
||||
Dots,
|
||||
DotsVariant,
|
||||
Sand,
|
||||
}
|
||||
|
||||
/// A spinner indication, based on the label component, that loops through
|
||||
@@ -42,11 +41,6 @@ impl SpinnerVariant {
|
||||
match self {
|
||||
SpinnerVariant::Dots => vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
||||
SpinnerVariant::DotsVariant => vec!["⣼", "⣹", "⢻", "⠿", "⡟", "⣏", "⣧", "⣶"],
|
||||
SpinnerVariant::Sand => vec![
|
||||
"⠁", "⠂", "⠄", "⡀", "⡈", "⡐", "⡠", "⣀", "⣁", "⣂", "⣄", "⣌", "⣔", "⣤", "⣥", "⣦",
|
||||
"⣮", "⣶", "⣷", "⣿", "⡿", "⠿", "⢟", "⠟", "⡛", "⠛", "⠫", "⢋", "⠋", "⠍", "⡉", "⠉",
|
||||
"⠑", "⠡", "⢁",
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +48,6 @@ impl SpinnerVariant {
|
||||
match self {
|
||||
SpinnerVariant::Dots => Duration::from_millis(1000),
|
||||
SpinnerVariant::DotsVariant => Duration::from_millis(1000),
|
||||
SpinnerVariant::Sand => Duration::from_millis(2000),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +55,6 @@ impl SpinnerVariant {
|
||||
match self {
|
||||
SpinnerVariant::Dots => "spinner_label_dots",
|
||||
SpinnerVariant::DotsVariant => "spinner_label_dots_variant",
|
||||
SpinnerVariant::Sand => "spinner_label_dots_variant_2",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,10 +83,6 @@ impl SpinnerLabel {
|
||||
pub fn dots_variant() -> Self {
|
||||
Self::with_variant(SpinnerVariant::DotsVariant)
|
||||
}
|
||||
|
||||
pub fn sand() -> Self {
|
||||
Self::with_variant(SpinnerVariant::Sand)
|
||||
}
|
||||
}
|
||||
|
||||
impl LabelCommon for SpinnerLabel {
|
||||
@@ -197,7 +185,6 @@ impl Component for SpinnerLabel {
|
||||
"Dots Variant",
|
||||
SpinnerLabel::dots_variant().into_any_element(),
|
||||
),
|
||||
single_example("Sand Variant", SpinnerLabel::sand().into_any_element()),
|
||||
];
|
||||
|
||||
Some(example_group(examples).vertical().into_any_element())
|
||||
|
||||
@@ -194,7 +194,7 @@ pub trait Item: Focusable + EventEmitter<Self::Event> + Render + Sized {
|
||||
fn discarded(&self, _project: Entity<Project>, _window: &mut Window, _cx: &mut Context<Self>) {}
|
||||
fn on_removed(&self, _cx: &App) {}
|
||||
fn workspace_deactivated(&mut self, _window: &mut Window, _: &mut Context<Self>) {}
|
||||
fn navigate(&mut self, _: Box<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
|
||||
fn navigate(&mut self, _: Rc<dyn Any>, _window: &mut Window, _: &mut Context<Self>) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
@@ -449,7 +449,7 @@ pub trait ItemHandle: 'static + Send {
|
||||
fn deactivated(&self, window: &mut Window, cx: &mut App);
|
||||
fn on_removed(&self, cx: &App);
|
||||
fn workspace_deactivated(&self, window: &mut Window, cx: &mut App);
|
||||
fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool;
|
||||
fn navigate(&self, data: Rc<dyn Any>, window: &mut Window, cx: &mut App) -> bool;
|
||||
fn item_id(&self) -> EntityId;
|
||||
fn to_any(&self) -> AnyView;
|
||||
fn is_dirty(&self, cx: &App) -> bool;
|
||||
@@ -900,7 +900,7 @@ impl<T: Item> ItemHandle for Entity<T> {
|
||||
self.update(cx, |this, cx| this.workspace_deactivated(window, cx));
|
||||
}
|
||||
|
||||
fn navigate(&self, data: Box<dyn Any>, window: &mut Window, cx: &mut App) -> bool {
|
||||
fn navigate(&self, data: Rc<dyn Any>, window: &mut Window, cx: &mut App) -> bool {
|
||||
self.update(cx, |this, cx| this.navigate(data, window, cx))
|
||||
}
|
||||
|
||||
@@ -1277,7 +1277,7 @@ pub mod test {
|
||||
InteractiveElement, IntoElement, Render, SharedString, Task, WeakEntity, Window,
|
||||
};
|
||||
use project::{Project, ProjectEntryId, ProjectPath, WorktreeId};
|
||||
use std::{any::Any, cell::Cell};
|
||||
use std::{any::Any, cell::Cell, rc::Rc};
|
||||
use util::rel_path::rel_path;
|
||||
|
||||
pub struct TestProjectItem {
|
||||
@@ -1510,11 +1510,14 @@ pub mod test {
|
||||
|
||||
fn navigate(
|
||||
&mut self,
|
||||
state: Box<dyn Any>,
|
||||
state: Rc<dyn Any>,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Self>,
|
||||
) -> bool {
|
||||
let state = *state.downcast::<String>().unwrap_or_default();
|
||||
let state = state
|
||||
.downcast_ref::<String>()
|
||||
.map(|s| s.to_string())
|
||||
.unwrap_or_default();
|
||||
if state != self.state {
|
||||
self.state = state;
|
||||
true
|
||||
|
||||
@@ -211,6 +211,10 @@ actions!(
|
||||
GoBack,
|
||||
/// Navigates forward in history.
|
||||
GoForward,
|
||||
/// Navigates back in the tag stack.
|
||||
GoToOlderTag,
|
||||
/// Navigates forward in the tag stack.
|
||||
GoToNewerTag,
|
||||
/// Joins this pane into the next pane.
|
||||
JoinIntoNext,
|
||||
/// Joins all panes into one.
|
||||
@@ -417,6 +421,9 @@ struct NavHistoryState {
|
||||
backward_stack: VecDeque<NavigationEntry>,
|
||||
forward_stack: VecDeque<NavigationEntry>,
|
||||
closed_stack: VecDeque<NavigationEntry>,
|
||||
tag_stack: VecDeque<(NavigationEntry, NavigationEntry)>,
|
||||
tag_stack_pos: usize,
|
||||
pending_tag_source: Option<NavigationEntry>,
|
||||
paths_by_item: HashMap<EntityId, (ProjectPath, Option<PathBuf>)>,
|
||||
pane: WeakEntity<Pane>,
|
||||
next_timestamp: Arc<AtomicUsize>,
|
||||
@@ -438,9 +445,10 @@ impl Default for NavigationMode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NavigationEntry {
|
||||
pub item: Arc<dyn WeakItemHandle>,
|
||||
pub data: Option<Box<dyn Any + Send>>,
|
||||
pub data: Option<Rc<dyn Any + Send>>,
|
||||
pub timestamp: usize,
|
||||
pub is_preview: bool,
|
||||
}
|
||||
@@ -513,6 +521,9 @@ impl Pane {
|
||||
backward_stack: Default::default(),
|
||||
forward_stack: Default::default(),
|
||||
closed_stack: Default::default(),
|
||||
tag_stack: Default::default(),
|
||||
tag_stack_pos: 0,
|
||||
pending_tag_source: None,
|
||||
paths_by_item: Default::default(),
|
||||
pane: handle,
|
||||
next_timestamp,
|
||||
@@ -850,6 +861,24 @@ impl Pane {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn go_to_older_tag(
|
||||
&mut self,
|
||||
_: &GoToOlderTag,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
let pane = cx.entity().downgrade();
|
||||
window.defer(cx, move |window, cx| {
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
.navigate_tag_history(pane, window, cx)
|
||||
.detach_and_log_err(cx)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn history_updated(&mut self, cx: &mut Context<Self>) {
|
||||
self.toolbar.update(cx, |_, cx| cx.notify());
|
||||
}
|
||||
@@ -3756,6 +3785,7 @@ impl Render for Pane {
|
||||
.on_action(cx.listener(Pane::toggle_zoom))
|
||||
.on_action(cx.listener(Self::navigate_backward))
|
||||
.on_action(cx.listener(Self::navigate_forward))
|
||||
.on_action(cx.listener(Self::go_to_older_tag))
|
||||
.on_action(
|
||||
cx.listener(|pane: &mut Pane, action: &ActivateItem, window, cx| {
|
||||
pane.activate_item(
|
||||
@@ -3996,8 +4026,40 @@ impl ItemNavHistory {
|
||||
self.history.pop(NavigationMode::GoingBack, cx)
|
||||
}
|
||||
|
||||
pub fn pop_forward(&mut self, cx: &mut App) -> Option<NavigationEntry> {
|
||||
self.history.pop(NavigationMode::GoingForward, cx)
|
||||
pub fn start_tag_jump<D>(&mut self, data: Option<D>, cx: &mut App)
|
||||
where
|
||||
D: 'static + Any + Send,
|
||||
{
|
||||
if self
|
||||
.item
|
||||
.upgrade()
|
||||
.is_some_and(|item| item.include_in_nav_history())
|
||||
{
|
||||
self.history.start_tag_jump(
|
||||
data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
self.item.clone(),
|
||||
self.is_preview,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish_tag_jump<D>(&mut self, data: Option<D>, cx: &mut App)
|
||||
where
|
||||
D: 'static + Any + Send,
|
||||
{
|
||||
if self
|
||||
.item
|
||||
.upgrade()
|
||||
.is_some_and(|item| item.include_in_nav_history())
|
||||
{
|
||||
self.history.finish_tag_jump(
|
||||
data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
self.item.clone(),
|
||||
self.is_preview,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4075,7 +4137,7 @@ impl NavHistory {
|
||||
}
|
||||
state.backward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
|
||||
data: data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
is_preview,
|
||||
});
|
||||
@@ -4087,7 +4149,7 @@ impl NavHistory {
|
||||
}
|
||||
state.forward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
|
||||
data: data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
is_preview,
|
||||
});
|
||||
@@ -4098,7 +4160,7 @@ impl NavHistory {
|
||||
}
|
||||
state.backward_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
|
||||
data: data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
is_preview,
|
||||
});
|
||||
@@ -4109,7 +4171,7 @@ impl NavHistory {
|
||||
}
|
||||
state.closed_stack.push_back(NavigationEntry {
|
||||
item,
|
||||
data: data.map(|data| Box::new(data) as Box<dyn Any + Send>),
|
||||
data: data.map(|data| Rc::new(data) as Rc<dyn Any + Send>),
|
||||
timestamp: state.next_timestamp.fetch_add(1, Ordering::SeqCst),
|
||||
is_preview,
|
||||
});
|
||||
@@ -4135,6 +4197,55 @@ impl NavHistory {
|
||||
pub fn path_for_item(&self, item_id: EntityId) -> Option<(ProjectPath, Option<PathBuf>)> {
|
||||
self.0.lock().paths_by_item.get(&item_id).cloned()
|
||||
}
|
||||
|
||||
pub fn start_tag_jump(
|
||||
&mut self,
|
||||
data: Option<Rc<dyn Any + Send>>,
|
||||
item: Arc<dyn WeakItemHandle>,
|
||||
is_preview: bool,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
self.0.lock().pending_tag_source.replace(NavigationEntry {
|
||||
item,
|
||||
data,
|
||||
timestamp: 0,
|
||||
is_preview,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn finish_tag_jump(
|
||||
&mut self,
|
||||
data: Option<Rc<dyn Any + Send>>,
|
||||
item: Arc<dyn WeakItemHandle>,
|
||||
is_preview: bool,
|
||||
_cx: &mut App,
|
||||
) {
|
||||
let mut state = self.0.lock();
|
||||
let Some(source) = state.pending_tag_source.take() else {
|
||||
debug_panic!("Finished tag jump without starting one?");
|
||||
return;
|
||||
};
|
||||
let dest = NavigationEntry {
|
||||
item,
|
||||
data,
|
||||
timestamp: 0,
|
||||
is_preview,
|
||||
};
|
||||
let truncate_to = state.tag_stack_pos;
|
||||
state.tag_stack.truncate(truncate_to);
|
||||
state.tag_stack.push_back((source, dest));
|
||||
state.tag_stack_pos += 1;
|
||||
}
|
||||
|
||||
pub fn tag_stack_back(&mut self) -> Option<NavigationEntry> {
|
||||
let mut state = self.0.lock();
|
||||
if state.tag_stack_pos > 0 {
|
||||
state.tag_stack_pos -= 1;
|
||||
Some(state.tag_stack[state.tag_stack_pos].0.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NavHistoryState {
|
||||
|
||||
@@ -1927,6 +1927,127 @@ impl Workspace {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn navigate_tag_history(
|
||||
&mut self,
|
||||
pane: WeakEntity<Pane>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Workspace>,
|
||||
) -> Task<Result<()>> {
|
||||
let to_load = if let Some(pane) = pane.upgrade() {
|
||||
pane.update(cx, |pane, cx| {
|
||||
window.focus(&pane.focus_handle(cx));
|
||||
loop {
|
||||
// Retrieve the weak item handle from the history.
|
||||
let entry = pane.nav_history_mut().tag_stack_back()?;
|
||||
|
||||
// If the item is still present in this pane, then activate it.
|
||||
if let Some(index) = entry
|
||||
.item
|
||||
.upgrade()
|
||||
.and_then(|v| pane.index_for_item(v.as_ref()))
|
||||
{
|
||||
let prev_active_item_index = pane.active_item_index();
|
||||
pane.activate_item(index, true, true, window, cx);
|
||||
|
||||
let mut navigated = prev_active_item_index != pane.active_item_index();
|
||||
if let Some(data) = entry.data {
|
||||
navigated |= pane.active_item()?.navigate(data, window, cx);
|
||||
}
|
||||
|
||||
if navigated {
|
||||
break None;
|
||||
}
|
||||
} else {
|
||||
// If the item is no longer present in this pane, then retrieve its
|
||||
// path info in order to reopen it.
|
||||
break pane
|
||||
.nav_history()
|
||||
.path_for_item(entry.item.id())
|
||||
.map(|(project_path, abs_path)| (project_path, abs_path, entry));
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some((project_path, abs_path, entry)) = to_load {
|
||||
// If the item was no longer present, then load it again from its previous path, first try the local path
|
||||
let open_by_project_path = self.load_path(project_path.clone(), window, cx);
|
||||
|
||||
cx.spawn_in(window, async move |workspace, cx| {
|
||||
let open_by_project_path = open_by_project_path.await;
|
||||
let mut navigated = false;
|
||||
match open_by_project_path
|
||||
.with_context(|| format!("Navigating to {project_path:?}"))
|
||||
{
|
||||
Ok((project_entry_id, build_item)) => {
|
||||
let prev_active_item_id = pane.update(cx, |pane, _| {
|
||||
pane.active_item().map(|p| p.item_id())
|
||||
})?;
|
||||
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
let item = pane.open_item(
|
||||
project_entry_id,
|
||||
project_path,
|
||||
true,
|
||||
entry.is_preview,
|
||||
true,
|
||||
None,
|
||||
window, cx,
|
||||
build_item,
|
||||
);
|
||||
navigated |= Some(item.item_id()) != prev_active_item_id;
|
||||
if let Some(data) = entry.data {
|
||||
navigated |= item.navigate(data, window, cx);
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Err(open_by_project_path_e) => {
|
||||
// Fall back to opening by abs path, in case an external file was opened and closed,
|
||||
// and its worktree is now dropped
|
||||
if let Some(abs_path) = abs_path {
|
||||
let prev_active_item_id = pane.update(cx, |pane, _| {
|
||||
pane.active_item().map(|p| p.item_id())
|
||||
})?;
|
||||
let open_by_abs_path = workspace.update_in(cx, |workspace, window, cx| {
|
||||
workspace.open_abs_path(abs_path.clone(), OpenOptions { visible: Some(OpenVisible::None), ..Default::default() }, window, cx)
|
||||
})?;
|
||||
match open_by_abs_path
|
||||
.await
|
||||
.with_context(|| format!("Navigating to {abs_path:?}"))
|
||||
{
|
||||
Ok(item) => {
|
||||
pane.update_in(cx, |pane, window, cx| {
|
||||
navigated |= Some(item.item_id()) != prev_active_item_id;
|
||||
if let Some(data) = entry.data {
|
||||
navigated |= item.navigate(data, window, cx);
|
||||
}
|
||||
})?;
|
||||
}
|
||||
Err(open_by_abs_path_e) => {
|
||||
log::error!("Failed to navigate history: {open_by_project_path_e:#} and {open_by_abs_path_e:#}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !navigated {
|
||||
workspace
|
||||
.update_in(cx, |workspace, window, cx| {
|
||||
Self::navigate_tag_history(workspace, pane, window, cx)
|
||||
})?
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
} else {
|
||||
Task::ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn navigate_history(
|
||||
&mut self,
|
||||
pane: WeakEntity<Pane>,
|
||||
|
||||
@@ -17,7 +17,6 @@ To debug code written in a specific language, Zed needs to find a debug adapter
|
||||
- [C](./languages/c.md#debugging) (built-in)
|
||||
- [C++](./languages/cpp.md#debugging) (built-in)
|
||||
- [Go](./languages/go.md#debugging) (built-in)
|
||||
- [Java](./languages/java.md#debugging) (provided by extension)
|
||||
- [JavaScript](./languages/javascript.md#debugging) (built-in)
|
||||
- [PHP](./languages/php.md#debugging) (built-in)
|
||||
- [Python](./languages/python.md#debugging) (built-in)
|
||||
|
||||
@@ -19,149 +19,150 @@ Or manually download and install [OpenJDK 23](https://jdk.java.net/23/).
|
||||
|
||||
## Extension Install
|
||||
|
||||
You can install by opening {#action zed::Extensions}({#kb zed::Extensions}) and searching for `java`.
|
||||
You can install either by opening {#action zed::Extensions}({#kb zed::Extensions}) and searching for `java`.
|
||||
|
||||
## Quick start and configuration
|
||||
## Settings / Initialization Options
|
||||
|
||||
For the majority of users, Java support should work out of the box.
|
||||
The extension will automatically download the language server, see: [Manual JDTLS Install](#manual-jdts-install) below if you'd prefer to manage that yourself.
|
||||
|
||||
- It is generally recommended to open projects with the Zed-project root at the Java project root folder (where you would commonly have your `pom.xml` or `build.gradle` file).
|
||||
For available `initialization_options` please see the [Initialize Request section of the Eclipse.jdt.ls Wiki](https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request).
|
||||
|
||||
- By default the extension will download and run the latest official version of JDTLS for you, but this requires Java version 21 to be available on your system via either the `$JAVA_HOME` environment variable or as a `java(.exe)` executable on your `$PATH`. If your project requires a lower Java version in the environment, you can specify a different JDK to use for running JDTLS via the `java_home` configuration option.
|
||||
You can add these customizations to your Zed Settings by launching {#action zed::OpenSettings}({#kb zed::OpenSettings}) or by using a `.zed/setting.json` inside your project.
|
||||
|
||||
- You can provide a **custom launch script for JDTLS**, by adding an executable named `jdtls` (or `jdtls.bat` on Windows) to your `$PATH` environment variable. If this is present, the extension will skip downloading and launching a managed instance and use the one from the environment.
|
||||
### Zed Java Settings
|
||||
|
||||
- To support [Lombok](https://projectlombok.org/), the lombok-jar must be downloaded and registered as a Java-Agent when launching JDTLS. By default the extension automatically takes care of that, but in case you don't want that you can set the `lombok_support` configuration-option to `false`.
|
||||
|
||||
Here is a common `settings.json` including the above mentioned configurations:
|
||||
|
||||
```jsonc
|
||||
```json [settings]
|
||||
{
|
||||
"lsp": {
|
||||
"jdtls": {
|
||||
"settings": {
|
||||
"java_home": "/path/to/your/JDK21+",
|
||||
"lombok_support": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
"initialization_options": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Debugging
|
||||
## Example Configs
|
||||
|
||||
Debug support is enabled via our [Fork of Java Debug](https://github.com/zed-industries/java-debug), which the extension will automatically download and start for you. Please refer to the [Debugger Documentation](https://zed.dev/docs/debugger#getting-started) for general information about how debugging works in Zed.
|
||||
### JDTLS Binary
|
||||
|
||||
To get started with Java, click the `edit debug.json` button in the Debug menu, and replace the contents of the file with the following:
|
||||
By default, zed will look in your `PATH` for a `jdtls` binary, if you wish to specify an explicit binary you can do so via settings:
|
||||
|
||||
```jsonc
|
||||
[
|
||||
{
|
||||
"adapter": "Java",
|
||||
"request": "launch",
|
||||
"label": "Launch Debugger",
|
||||
// if your project has multiple entry points, specify the one to use:
|
||||
// "mainClass": "com.myorganization.myproject.MyMainClass",
|
||||
//
|
||||
// this effectively sets a breakpoint at your program entry:
|
||||
"stopOnEntry": true,
|
||||
// the working directory for the debug process
|
||||
"cwd": "$ZED_WORKTREE_ROOT",
|
||||
},
|
||||
]
|
||||
```json [settings]
|
||||
"lsp": {
|
||||
"jdtls": {
|
||||
"binary": {
|
||||
"path": "/path/to/java/bin/jdtls",
|
||||
// "arguments": [],
|
||||
// "env": {},
|
||||
"ignore_system_version": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You should then be able to start a new Debug Session with the "Launch Debugger" scenario from the debug menu.
|
||||
### Zed Java Initialization Options
|
||||
|
||||
## Launch Scripts (aka Tasks) in Windows
|
||||
There are also many more options you can pass directly to the language server, for example:
|
||||
|
||||
This extension provides tasks for running your application and tests from within Zed via little play buttons next to tests/entry points. However, due to current limitations of Zed's extension interface, we can not provide scripts that will work across Maven and Gradle on both Windows and Unix-compatible systems, so out of the box the launch scripts only work on Mac and Linux.
|
||||
|
||||
There is a fairly straightforward fix that you can apply to make it work on Windows by supplying your own task scripts. Please see [this Issue](https://github.com/zed-extensions/java/issues/94) for information on how to do that and read the [Tasks section in Zeds documentation](https://zed.dev/docs/tasks) for more information.
|
||||
|
||||
## Advanced Configuration/JDTLS initialization Options
|
||||
|
||||
JDTLS provides many configuration options that can be passed via the `initialize` LSP-request. The extension will pass the JSON-object from `lsp.jdtls.settings.initialization_options` in your settings on to JDTLS. Please refer to the [JDTLS Configuration Wiki Page](https://github.com/eclipse-jdtls/eclipse.jdt.ls/wiki/Running-the-JAVA-LS-server-from-the-command-line#initialize-request) for the available options and values. Below is an example `settings.json` that would pass on the example configuration from the above wiki page to JDTLS:
|
||||
|
||||
```jsonc
|
||||
```json [settings]
|
||||
{
|
||||
"lsp": {
|
||||
"jdtls": {
|
||||
"settings": {
|
||||
// this will be sent to JDTLS as initializationOptions:
|
||||
"initialization_options": {
|
||||
"bundles": [],
|
||||
// use this if your zed project root folder is not the same as the java project root:
|
||||
"workspaceFolders": ["file:///home/snjeza/Project"],
|
||||
"settings": {
|
||||
"java": {
|
||||
"home": "/usr/local/jdk-9.0.1",
|
||||
"errors": {
|
||||
"incompleteClasspath": {
|
||||
"severity": "warning",
|
||||
},
|
||||
},
|
||||
"configuration": {
|
||||
"updateBuildConfiguration": "interactive",
|
||||
"maven": {
|
||||
"userSettings": null,
|
||||
},
|
||||
},
|
||||
"import": {
|
||||
"gradle": {
|
||||
"enabled": true,
|
||||
},
|
||||
"maven": {
|
||||
"enabled": true,
|
||||
},
|
||||
"exclusions": [
|
||||
"**/node_modules/**",
|
||||
"**/.metadata/**",
|
||||
"**/archetype-resources/**",
|
||||
"**/META-INF/maven/**",
|
||||
"/**/test/**",
|
||||
],
|
||||
},
|
||||
"referencesCodeLens": {
|
||||
"enabled": false,
|
||||
},
|
||||
"signatureHelp": {
|
||||
"enabled": false,
|
||||
},
|
||||
"implementationCodeLens": "all",
|
||||
"format": {
|
||||
"enabled": true,
|
||||
},
|
||||
"saveActions": {
|
||||
"organizeImports": false,
|
||||
},
|
||||
"contentProvider": {
|
||||
"preferred": null,
|
||||
},
|
||||
"autobuild": {
|
||||
"enabled": false,
|
||||
},
|
||||
"completion": {
|
||||
"favoriteStaticMembers": [
|
||||
"org.junit.Assert.*",
|
||||
"org.junit.Assume.*",
|
||||
"org.junit.jupiter.api.Assertions.*",
|
||||
"org.junit.jupiter.api.Assumptions.*",
|
||||
"org.junit.jupiter.api.DynamicContainer.*",
|
||||
"org.junit.jupiter.api.DynamicTest.*",
|
||||
],
|
||||
"importOrder": ["java", "javax", "com", "org"],
|
||||
},
|
||||
"initialization_options": {
|
||||
"bundles": [],
|
||||
"workspaceFolders": ["file:///home/snjeza/Project"],
|
||||
"settings": {
|
||||
"java": {
|
||||
"home": "/usr/local/jdk-9.0.1",
|
||||
"errors": {
|
||||
"incompleteClasspath": {
|
||||
"severity": "warning"
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"configuration": {
|
||||
"updateBuildConfiguration": "interactive",
|
||||
"maven": {
|
||||
"userSettings": null
|
||||
}
|
||||
},
|
||||
"trace": {
|
||||
"server": "verbose"
|
||||
},
|
||||
"import": {
|
||||
"gradle": {
|
||||
"enabled": true
|
||||
},
|
||||
"maven": {
|
||||
"enabled": true
|
||||
},
|
||||
"exclusions": [
|
||||
"**/node_modules/**",
|
||||
"**/.metadata/**",
|
||||
"**/archetype-resources/**",
|
||||
"**/META-INF/maven/**",
|
||||
"/**/test/**"
|
||||
]
|
||||
},
|
||||
"jdt": {
|
||||
"ls": {
|
||||
"lombokSupport": {
|
||||
"enabled": false // Set this to true to enable lombok support
|
||||
}
|
||||
}
|
||||
},
|
||||
"referencesCodeLens": {
|
||||
"enabled": false
|
||||
},
|
||||
"signatureHelp": {
|
||||
"enabled": false
|
||||
},
|
||||
"implementationsCodeLens": {
|
||||
"enabled": false
|
||||
},
|
||||
"format": {
|
||||
"enabled": true
|
||||
},
|
||||
"saveActions": {
|
||||
"organizeImports": false
|
||||
},
|
||||
"contentProvider": {
|
||||
"preferred": null
|
||||
},
|
||||
"autobuild": {
|
||||
"enabled": false
|
||||
},
|
||||
"completion": {
|
||||
"favoriteStaticMembers": [
|
||||
"org.junit.Assert.*",
|
||||
"org.junit.Assume.*",
|
||||
"org.junit.jupiter.api.Assertions.*",
|
||||
"org.junit.jupiter.api.Assumptions.*",
|
||||
"org.junit.jupiter.api.DynamicContainer.*",
|
||||
"org.junit.jupiter.api.DynamicTest.*"
|
||||
],
|
||||
"importOrder": ["java", "javax", "com", "org"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Manual JDTLS Install
|
||||
|
||||
If you prefer, you can install JDTLS yourself and the extension can be configured to use that instead.
|
||||
|
||||
- macOS: `brew install jdtls`
|
||||
- Arch: [`jdtls` from AUR](https://aur.archlinux.org/packages/jdtls)
|
||||
|
||||
Or manually download install:
|
||||
|
||||
- [JDTLS Milestone Builds](http://download.eclipse.org/jdtls/milestones/) (updated every two weeks)
|
||||
- [JDTLS Snapshot Builds](https://download.eclipse.org/jdtls/snapshots/) (frequent updates)
|
||||
|
||||
## See also
|
||||
|
||||
[Zed Java Repo](https://github.com/zed-extensions/java)
|
||||
[Eclipse JDTLS Repo](https://github.com/eclipse-jdtls/eclipse.jdt.ls)
|
||||
- [Zed Java Repo](https://github.com/zed-extensions/java)
|
||||
- [Zed Java Issues](https://github.com/zed-extensions/java/issues)
|
||||
|
||||
@@ -71,18 +71,6 @@ To switch to `ruby-lsp`, add the following to your `settings.json`:
|
||||
"languages": {
|
||||
"Ruby": {
|
||||
"language_servers": ["ruby-lsp", "!solargraph", "!rubocop", "..."]
|
||||
},
|
||||
// Enable herb and ruby-lsp for *.html.erb files
|
||||
"HTML+ERB": {
|
||||
"language_servers": ["herb", "ruby-lsp", "..."]
|
||||
},
|
||||
// Enable ruby-lsp for *.js.erb files
|
||||
"JS+ERB": {
|
||||
"language_servers": ["ruby-lsp", "..."]
|
||||
},
|
||||
// Enable ruby-lsp for *.yaml.erb files
|
||||
"YAML+ERB": {
|
||||
"language_servers": ["ruby-lsp", "..."]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user