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:
2026-08-23 13:55:47 -05:00
parent f17642fc62
commit 7c106eecd5
147 changed files with 2208 additions and 1514 deletions
+42 -18
View File
@@ -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>> {
+2
View File
@@ -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.
+8
View File
@@ -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,
+1 -1
View File
@@ -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.
+14 -4
View File
@@ -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!(
+8 -12
View File
@@ -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);
}