feat: expand Galaxy agent and remote tooling
Add Wormhole remote helpers, provider and agent improvements, filesystem diagnostics, model metadata support, and schema-aware settings IntelliSense.
This commit is contained in:
+42
-18
@@ -187,6 +187,13 @@ fn fuzzy_match(target: &str, query: &str) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn completion_documentation(item: &CompletionItem) -> Option<String> {
|
||||
match item.documentation.as_ref()? {
|
||||
lsp_types::Documentation::String(documentation) => Some(documentation.clone()),
|
||||
lsp_types::Documentation::MarkupContent(documentation) => Some(documentation.value.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
impl LocalCodeEditorView {
|
||||
pub(super) fn is_completion_enabled() -> bool {
|
||||
FeatureFlag::LspCompletion.is_enabled()
|
||||
@@ -254,12 +261,13 @@ impl LocalCodeEditorView {
|
||||
};
|
||||
|
||||
if let Some(trigger) = trigger {
|
||||
self.request_completion(cursor_offset, trigger, ctx);
|
||||
self.request_completion(cursor_offset, cursor_offset, trigger, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn request_completion(
|
||||
&mut self,
|
||||
request_offset: CharOffset,
|
||||
trigger_offset: CharOffset,
|
||||
trigger: CompletionTrigger,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
@@ -279,7 +287,7 @@ impl LocalCodeEditorView {
|
||||
let lsp_position = self
|
||||
.editor()
|
||||
.as_ref(ctx)
|
||||
.offset_to_lsp_position(trigger_offset, ctx);
|
||||
.offset_to_lsp_position(request_offset, ctx);
|
||||
|
||||
let future =
|
||||
match lsp_server
|
||||
@@ -318,7 +326,7 @@ impl LocalCodeEditorView {
|
||||
}
|
||||
|
||||
let word_start = self.find_word_start(offset, ctx);
|
||||
self.request_completion(word_start, CompletionTrigger::Invoked, ctx);
|
||||
self.request_completion(offset, word_start, CompletionTrigger::Invoked, ctx);
|
||||
}
|
||||
|
||||
/// Find the start of the current identifier word by walking backwards from `offset`.
|
||||
@@ -440,7 +448,7 @@ impl LocalCodeEditorView {
|
||||
|
||||
/// Resolve documentation for the currently selected completion item.
|
||||
pub(super) fn resolve_selected_completion_docs(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
let raw_item = match &self.completion_state {
|
||||
let (item_index, raw_item) = match &self.completion_state {
|
||||
CompletionState::Showing {
|
||||
items,
|
||||
filtered_indices,
|
||||
@@ -458,14 +466,22 @@ impl LocalCodeEditorView {
|
||||
{
|
||||
return;
|
||||
}
|
||||
items[item_idx].raw_item.clone()
|
||||
(item_idx, items[item_idx].raw_item.clone())
|
||||
}
|
||||
_ => return,
|
||||
};
|
||||
|
||||
if let Some(documentation) = completion_documentation(&raw_item) {
|
||||
self.set_resolved_completion_docs(item_index, documentation, ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
let Some(lsp_server) = &self.lsp_server else {
|
||||
return;
|
||||
};
|
||||
if !lsp_server.as_ref(ctx).supports_completion_resolve() {
|
||||
return;
|
||||
}
|
||||
|
||||
let future = match lsp_server.as_ref(ctx).completion_resolve(raw_item) {
|
||||
Ok(future) => future,
|
||||
@@ -473,8 +489,8 @@ impl LocalCodeEditorView {
|
||||
};
|
||||
|
||||
let abort_handle = ctx
|
||||
.spawn(future, |me, result, ctx| {
|
||||
me.handle_completion_resolve_response(result, ctx);
|
||||
.spawn(future, move |me, result, ctx| {
|
||||
me.handle_completion_resolve_response(item_index, result, ctx);
|
||||
})
|
||||
.abort_handle();
|
||||
|
||||
@@ -492,6 +508,7 @@ impl LocalCodeEditorView {
|
||||
|
||||
fn handle_completion_resolve_response(
|
||||
&mut self,
|
||||
item_index: usize,
|
||||
result: anyhow::Result<CompletionItem>,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
@@ -500,22 +517,29 @@ impl LocalCodeEditorView {
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let doc_string = match resolved_item.documentation {
|
||||
Some(lsp_types::Documentation::String(s)) => s,
|
||||
Some(lsp_types::Documentation::MarkupContent(m)) => m.value,
|
||||
None => return,
|
||||
let Some(documentation) = completion_documentation(&resolved_item) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if doc_string.trim().is_empty() {
|
||||
self.set_resolved_completion_docs(item_index, documentation, ctx);
|
||||
}
|
||||
|
||||
fn set_resolved_completion_docs(
|
||||
&mut self,
|
||||
item_index: usize,
|
||||
documentation: String,
|
||||
ctx: &mut ViewContext<Self>,
|
||||
) {
|
||||
if documentation.trim().is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let formatted = match markdown_parser::parse_markdown(&doc_string) {
|
||||
let formatted = match markdown_parser::parse_markdown(&documentation) {
|
||||
Ok(text) => text,
|
||||
Err(_) => {
|
||||
use markdown_parser::{FormattedTextFragment, FormattedTextLine};
|
||||
FormattedText::new([FormattedTextLine::Line(vec![
|
||||
FormattedTextFragment::plain_text(doc_string),
|
||||
FormattedTextFragment::plain_text(documentation),
|
||||
])])
|
||||
}
|
||||
};
|
||||
@@ -529,9 +553,9 @@ impl LocalCodeEditorView {
|
||||
} = &mut self.completion_state
|
||||
{
|
||||
*resolve_abort_handle = None;
|
||||
if let Some(&item_idx) = filtered_indices.get(*selected_index) {
|
||||
if filtered_indices.get(*selected_index) == Some(&item_index) {
|
||||
*resolved_docs = Some(ResolvedDocumentation {
|
||||
item_index: item_idx,
|
||||
item_index,
|
||||
text: formatted,
|
||||
scroll_state: ClippedScrollStateHandle::default(),
|
||||
});
|
||||
@@ -552,7 +576,7 @@ impl LocalCodeEditorView {
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually trigger completion (Ctrl+Alt+Space).
|
||||
/// Manually trigger completion (Ctrl+Space in the code editor).
|
||||
pub(super) fn trigger_completion_manually(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
if !Self::is_completion_enabled() {
|
||||
return;
|
||||
@@ -562,7 +586,7 @@ impl LocalCodeEditorView {
|
||||
}
|
||||
let cursor_offset = self.editor().as_ref(ctx).cursor_head_offset(ctx);
|
||||
let word_start = self.find_word_start(cursor_offset, ctx);
|
||||
self.request_completion(word_start, CompletionTrigger::Invoked, ctx);
|
||||
self.request_completion(cursor_offset, word_start, CompletionTrigger::Invoked, ctx);
|
||||
}
|
||||
|
||||
pub(super) fn render_completion_menu(&self, app: &AppContext) -> Option<Box<dyn Element>> {
|
||||
|
||||
@@ -158,6 +158,8 @@ pub enum CodeEditorEvent {
|
||||
CompletionNavigateDown,
|
||||
/// Emitted when Tab/Enter is pressed and completion_intercept_keys is active.
|
||||
CompletionConfirm,
|
||||
/// Emitted when the manual completion keybinding is pressed.
|
||||
CompletionTrigger,
|
||||
}
|
||||
|
||||
/// Store all states related to displaying the editor content.
|
||||
|
||||
@@ -78,6 +78,11 @@ pub fn init(app: &mut AppContext) {
|
||||
CodeEditorViewAction::VimShiftEnter,
|
||||
text_entry.clone() & id!("Vim"),
|
||||
),
|
||||
FixedBinding::new(
|
||||
"ctrl-space",
|
||||
CodeEditorViewAction::TriggerCompletion,
|
||||
editable_state.clone(),
|
||||
),
|
||||
FixedBinding::new(
|
||||
"backspace",
|
||||
CodeEditorViewAction::Backspace,
|
||||
@@ -711,6 +716,7 @@ pub enum CodeEditorViewAction {
|
||||
ShiftTab,
|
||||
ShowFindBar,
|
||||
ShowGoToLine,
|
||||
TriggerCompletion,
|
||||
Escape,
|
||||
VimEnter,
|
||||
VimTab,
|
||||
@@ -795,6 +801,7 @@ impl CodeEditorViewAction {
|
||||
| Self::Copy
|
||||
| Self::ShowFindBar
|
||||
| Self::ShowGoToLine
|
||||
| Self::TriggerCompletion
|
||||
| Self::Escape
|
||||
| Self::HiddenSectionExpansion { .. }
|
||||
| Self::AddDiffHunkContext { .. }
|
||||
@@ -1064,6 +1071,7 @@ impl TypedActionView for CodeEditorView {
|
||||
|
||||
ShowFindBar => self.show_find_bar(ctx),
|
||||
ShowGoToLine => self.show_goto_line(ctx),
|
||||
TriggerCompletion => ctx.emit(CodeEditorEvent::CompletionTrigger),
|
||||
Escape => self.escape(ctx),
|
||||
HiddenSectionExpansion {
|
||||
line_range,
|
||||
|
||||
@@ -2965,7 +2965,7 @@ impl View for FileTreeView {
|
||||
if let CodingPanelEnablementState::RemoteSession { has_remote_server } = self.enablement
|
||||
{
|
||||
// When the session has a remote server connection (Auto SSH
|
||||
// Warpification / mode 1), show a loading state — the server
|
||||
// Wormholing / mode 1), show a loading state — the server
|
||||
// may push repo metadata momentarily. For other SSH modes
|
||||
// (tmux, subshell) no data will arrive, so show the disabled
|
||||
// error instead.
|
||||
|
||||
@@ -1562,13 +1562,23 @@ impl GlobalBufferModel {
|
||||
.flatten();
|
||||
|
||||
// If we have a previous version that wasn't synced, we need to do a full sync.
|
||||
let needs_full_sync = previous_version.is_some_and(|prev| {
|
||||
last_synced.is_none() || last_synced.is_some_and(|synced| synced < prev)
|
||||
});
|
||||
let server_requires_full_sync = lsp_server.as_ref(ctx).requires_full_document_sync();
|
||||
let needs_full_sync = server_requires_full_sync
|
||||
|| previous_version.is_some_and(|prev| {
|
||||
last_synced.is_none() || last_synced.is_some_and(|synced| synced < prev)
|
||||
});
|
||||
|
||||
let deltas_len = deltas.len();
|
||||
|
||||
if needs_full_sync {
|
||||
if server_requires_full_sync {
|
||||
lsp_server.as_ref(ctx).log_to_server_log(
|
||||
LspServerLogLevel::Debug,
|
||||
format!(
|
||||
"didChange -> server: REQUIRED full-sync file={} send_version={current_version} deltas={deltas_len}",
|
||||
path.display()
|
||||
),
|
||||
);
|
||||
} else if needs_full_sync {
|
||||
lsp_server.as_ref(ctx).log_to_server_log(
|
||||
LspServerLogLevel::Info,
|
||||
format!(
|
||||
|
||||
@@ -114,11 +114,6 @@ pub fn init(app: &mut AppContext) {
|
||||
LocalCodeEditorAction::StartRename,
|
||||
id!("LocalCodeEditorView"),
|
||||
),
|
||||
FixedBinding::new(
|
||||
"ctrl-alt-space",
|
||||
LocalCodeEditorAction::TriggerCompletion,
|
||||
id!("LocalCodeEditorView"),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -216,8 +211,6 @@ pub enum LocalCodeEditorAction {
|
||||
OpenCodeActions,
|
||||
/// Start LSP rename at cursor (F2).
|
||||
StartRename,
|
||||
/// Manually trigger completion (Ctrl+Alt+Space).
|
||||
TriggerCompletion,
|
||||
/// Hover over a completion item by display index.
|
||||
CompletionHoverItem(usize),
|
||||
/// Confirm completion via mouse click.
|
||||
@@ -510,6 +503,9 @@ impl LocalCodeEditorView {
|
||||
CodeEditorEvent::CompletionConfirm => {
|
||||
me.confirm_completion(ctx);
|
||||
}
|
||||
CodeEditorEvent::CompletionTrigger => {
|
||||
me.trigger_completion_manually(ctx);
|
||||
}
|
||||
CodeEditorEvent::VimGotoDefinition
|
||||
| CodeEditorEvent::VimFindReferences
|
||||
| CodeEditorEvent::VimShowHover => {
|
||||
@@ -995,9 +991,12 @@ impl LocalCodeEditorView {
|
||||
// If the LSP is not registered, try to start it via PersistedWorkspace.
|
||||
#[cfg(feature = "local_fs")]
|
||||
{
|
||||
use crate::ai::persisted_workspace::LspTask;
|
||||
PersistedWorkspace::handle(ctx).update(ctx, |workspace, ctx| {
|
||||
workspace.execute_lsp_task(LspTask::Spawn { file_path: path }, ctx);
|
||||
if path == crate::settings::user_preferences_toml_file_path() {
|
||||
workspace.ensure_settings_toml_lsp(path, ctx);
|
||||
} else {
|
||||
workspace.execute_lsp_task(LspTask::Spawn { file_path: path }, ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -2487,9 +2486,6 @@ impl TypedActionView for LocalCodeEditorView {
|
||||
LocalCodeEditorAction::StartRename => {
|
||||
self.start_rename(ctx);
|
||||
}
|
||||
LocalCodeEditorAction::TriggerCompletion => {
|
||||
self.trigger_completion_manually(ctx);
|
||||
}
|
||||
LocalCodeEditorAction::CompletionHoverItem(display_index) => {
|
||||
self.handle_completion_hover_item(*display_index, ctx);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user