Wire up completion keyboard navigation and add AGENTS.md
- Tab/Enter confirms the selected completion item - Up/Down arrows navigate the completion menu - Escape dismisses the menu (via existing dismiss_lsp_overlays) - Editor intercepts keys via completion_intercept_keys flag when menu is showing - Add AGENTS.md with LLM-powered predictive autocomplete idea Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a75bc99852
commit
d421b9583f
@@ -0,0 +1,28 @@
|
||||
# Galaxy AI Agents - Ideas & Future Work
|
||||
|
||||
## LLM-Powered Predictive Autocomplete
|
||||
|
||||
**Idea:** As the user types in the code editor, stream the current context (surrounding code, file structure, recent edits) to an LLM and predict what they're about to write — offering inline ghost-text completions similar to GitHub Copilot.
|
||||
|
||||
**Scope options:**
|
||||
- By line (predict the rest of the current line)
|
||||
- By function (predict the full function body)
|
||||
- By class/module (predict structural code)
|
||||
|
||||
**Challenges:**
|
||||
- Latency: can't hit the LLM on every keystroke. Need aggressive debouncing (500ms+), speculative pre-fetching, and streaming partial results.
|
||||
- Cost: high token volume. May need a small/fast model (Haiku) for inline suggestions with a larger model for multi-line predictions.
|
||||
- Context window: need to efficiently pack relevant context (current file, imports, related types, recent edits) without blowing the token budget.
|
||||
- Cancellation: must cancel in-flight requests when the user keeps typing past the prediction point.
|
||||
- UX: ghost text rendering, Tab to accept, partial accept (word-by-word), dismiss on divergence.
|
||||
|
||||
**Possible approaches:**
|
||||
- Debounce + streaming: wait 500ms after last keystroke, stream tokens as they arrive, render as ghost text
|
||||
- Predictive pre-fetch: on function signature completion or newline, proactively request the likely next block
|
||||
- Local model: run a small code model locally for instant line completions, use cloud model for multi-line
|
||||
- Hybrid: use LSP completions for symbol-level, LLM for line/block-level predictions
|
||||
|
||||
**Integration points in Galaxy:**
|
||||
- `app/src/code/completion.rs` — extend the completion state machine with an LLM provider
|
||||
- `crates/ai/` — existing Bedrock/LLM infrastructure can be reused
|
||||
- Editor decoration system — for rendering ghost text (similar to inlay hints)
|
||||
@@ -141,6 +141,14 @@ impl LocalCodeEditorView {
|
||||
FeatureFlag::LspCompletion.is_enabled()
|
||||
}
|
||||
|
||||
/// Sync the editor's key interception flag with the completion state.
|
||||
pub(super) fn sync_completion_intercept(&self, ctx: &mut ViewContext<Self>) {
|
||||
let should_intercept = self.completion_state.is_showing();
|
||||
self.editor.update(ctx, |editor, _ctx| {
|
||||
editor.completion_intercept_keys = should_intercept;
|
||||
});
|
||||
}
|
||||
|
||||
/// Handle a user typing event — potentially trigger completion.
|
||||
pub(super) fn on_content_changed_for_completion(&mut self, ctx: &mut ViewContext<Self>) {
|
||||
if !Self::is_completion_enabled() {
|
||||
@@ -270,6 +278,7 @@ impl LocalCodeEditorView {
|
||||
Ok(Some(result)) if !result.items.is_empty() => result,
|
||||
_ => {
|
||||
self.completion_state = CompletionState::Idle;
|
||||
self.sync_completion_intercept(ctx);
|
||||
ctx.notify();
|
||||
return;
|
||||
}
|
||||
@@ -288,6 +297,7 @@ impl LocalCodeEditorView {
|
||||
let query = self.get_completion_filter_query(trigger_offset, ctx);
|
||||
self.completion_state.filter(&query);
|
||||
|
||||
self.sync_completion_intercept(ctx);
|
||||
ctx.notify();
|
||||
}
|
||||
|
||||
@@ -332,6 +342,7 @@ impl LocalCodeEditorView {
|
||||
};
|
||||
|
||||
self.completion_state = CompletionState::Idle;
|
||||
self.sync_completion_intercept(ctx);
|
||||
|
||||
self.editor.update(ctx, |editor, ctx| {
|
||||
let edit_range = if let Some(range) = text_edit_range {
|
||||
|
||||
@@ -153,6 +153,12 @@ pub enum CodeEditorEvent {
|
||||
VimGotoDefinition,
|
||||
VimFindReferences,
|
||||
VimShowHover,
|
||||
/// Emitted when Up is pressed and completion_intercept_keys is active.
|
||||
CompletionNavigateUp,
|
||||
/// Emitted when Down is pressed and completion_intercept_keys is active.
|
||||
CompletionNavigateDown,
|
||||
/// Emitted when Tab/Enter is pressed and completion_intercept_keys is active.
|
||||
CompletionConfirm,
|
||||
}
|
||||
|
||||
/// Store all states related to displaying the editor content.
|
||||
@@ -284,6 +290,9 @@ pub struct CodeEditorView {
|
||||
/// The offset where find references card is anchored (if showing).
|
||||
find_references_anchor_offset: Option<CharOffset>,
|
||||
window_id: WindowId,
|
||||
/// When true, Up/Down/Tab/Enter are redirected to completion events instead of
|
||||
/// normal editor behavior. Set by the parent when a completion menu is visible.
|
||||
pub completion_intercept_keys: bool,
|
||||
}
|
||||
|
||||
impl CodeEditorView {
|
||||
@@ -427,6 +436,7 @@ impl CodeEditorView {
|
||||
show_find_references_provider: render_options.show_find_references_provider,
|
||||
find_references_anchor_offset: None,
|
||||
window_id: ctx.window_id(),
|
||||
completion_intercept_keys: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -805,6 +805,28 @@ impl TypedActionView for CodeEditorView {
|
||||
return;
|
||||
}
|
||||
|
||||
// When completion menu is active, redirect navigation/confirm keys to the parent.
|
||||
if self.completion_intercept_keys {
|
||||
match action {
|
||||
MoveUp => {
|
||||
ctx.emit(super::CodeEditorEvent::CompletionNavigateUp);
|
||||
return;
|
||||
}
|
||||
MoveDown => {
|
||||
ctx.emit(super::CodeEditorEvent::CompletionNavigateDown);
|
||||
return;
|
||||
}
|
||||
Tab | Enter => {
|
||||
ctx.emit(super::CodeEditorEvent::CompletionConfirm);
|
||||
return;
|
||||
}
|
||||
Escape => {
|
||||
// Let escape fall through to normal handling (which emits EscapePressed)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
match action {
|
||||
UserTyped(content) => self.user_insert(content, ctx),
|
||||
VimUserTyped(content) => {
|
||||
|
||||
@@ -455,6 +455,17 @@ impl LocalCodeEditorView {
|
||||
CodeEditorEvent::DelayedRenderingFlushed => {
|
||||
ctx.emit(LocalCodeEditorEvent::DelayedRenderingFlushed);
|
||||
}
|
||||
CodeEditorEvent::CompletionNavigateUp => {
|
||||
me.completion_state.move_selection(-1);
|
||||
ctx.notify();
|
||||
}
|
||||
CodeEditorEvent::CompletionNavigateDown => {
|
||||
me.completion_state.move_selection(1);
|
||||
ctx.notify();
|
||||
}
|
||||
CodeEditorEvent::CompletionConfirm => {
|
||||
me.confirm_completion(ctx);
|
||||
}
|
||||
CodeEditorEvent::VimGotoDefinition
|
||||
| CodeEditorEvent::VimFindReferences
|
||||
| CodeEditorEvent::VimShowHover => {
|
||||
@@ -1934,6 +1945,9 @@ impl LocalCodeEditorView {
|
||||
let had_refs = self.close_find_references_card(ctx);
|
||||
let had_hover = self.lsp_hover_state.clear();
|
||||
let had_completion = self.completion_state.dismiss();
|
||||
if had_completion {
|
||||
self.sync_completion_intercept(ctx);
|
||||
}
|
||||
had_refs || had_hover || had_completion
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user