Skip to content

Commit b5315f8

Browse files
committed
fix: prevent autosuggest hangs during path completion
Skip zage autosuggest for path-like tokens and give autosuggest requests a short default timeout so ZLE stays responsive while native path completion runs. Also move the Forgejo workflow under .gitea and trim duplicated agent guidance.
1 parent 77735ae commit b5315f8

5 files changed

Lines changed: 337 additions & 186 deletions

File tree

AGENTS.md

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -2,95 +2,16 @@
22

33
> **Important:** Prefer the `mise` tasks for installs, builds, tests, and formatting. Only use raw toolchain commands when no `mise` wrapper exists, and call that out explicitly.
44
>
5-
> **CRITICAL: Prefer the Rust LSP for Rust code navigation.** The Rust LSP is the primary tool for Rust files because it is accurate, fast, and type-aware. That said, other tools (rg/find/manual browsing) are still acceptable when they are faster for the task, the LSP is unavailable, or you're working outside Rust code. See the "Code Navigation (Use Rust LSP!)" section for detailed commands.
6-
>
7-
> **CRITICAL: Do NOT run git mutations without explicit approval from the user** Do NOT ever run git checkout/revert/restore/reset without EXPLICIT APPROVAL from the USER
8-
>
95
> **CRITICAL: DO NOT ASK KNOWABLE QUESTIONS** Do not ask the user for information that you can look up.
106
117

128
## Build, Test, and Development Commands
139
Always default to the `mise` tasks below; only run direct toolchain commands if no `mise` wrapper exists and note the deviation.
1410

15-
**For Rust code navigation and understanding, use the Rust LSP first.** For non-Rust code or quick searches, it is fine to use rg/find/manual browsing when it is more appropriate.
16-
1711
- `mise install`: Install pinned Rust, Bun, Wrangler, etc.
1812
- `mise build:debug`: Build Rust
1913
- `mise test`: All tests (Rust nextest + Workers via bun test).
2014

21-
## Code Navigation (Use Rust LSP!)
22-
23-
**IMPORTANT: Prefer the Rust LSP for Rust code navigation.** The Rust LSP should be your primary tool for:
24-
- Finding symbols and definitions
25-
- Navigating to references
26-
- Getting function signatures and documentation
27-
- Understanding code structure
28-
- Finding implementations and usages
29-
30-
**Prefer Rust LSP over:** grep/find/rg, manual file browsing, or any other navigation method **for Rust files**. Use other tools when they are faster for the task, the LSP is unavailable, or the code is not Rust.
31-
32-
### Rust LSP Commands Available
33-
34-
Use these `mcp__rust-lsp__*` tools for navigation:
35-
36-
```bash
37-
# Get file structure and symbols
38-
mcp__rust-lsp__outline <file_path>
39-
40-
# Search for symbols across the codebase
41-
mcp__rust-lsp__search <query>
42-
43-
# Find all references to a symbol
44-
mcp__rust-lsp__references <file_path> <line> <character>
45-
46-
# Get detailed info about a symbol at cursor position
47-
mcp__rust-lsp__inspect <file_path> <line> <character>
48-
49-
# Get code completions at a position
50-
mcp__rust-lsp__completion <file_path> <line> <character>
51-
52-
# Rename a symbol across the codebase
53-
mcp__rust-lsp__rename <file_path> <line> <character> <new_name>
54-
55-
# Get diagnostics (errors/warnings) for a file
56-
mcp__rust-lsp__diagnostics <file_path>
57-
```
58-
59-
### Navigation Examples
60-
61-
```bash
62-
# Find all search-related services
63-
mcp__rust-lsp__search "SearchService"
64-
65-
# Explore the main application structure
66-
mcp__rust-lsp__outline "crates/slipstreamd/src/lib.rs"
67-
68-
# Find all references to AppState
69-
mcp__rust-lsp__references "crates/slipstreamd/src/app.rs" 16 1
70-
71-
# Inspect a function to get its documentation
72-
mcp__rust-lsp__inspect "crates/embedding/src/lib.rs" 127 1
73-
74-
# Get completions for method calls
75-
mcp__rust-lsp__completion "crates/slipstreamd/src/routes.rs" 42 20
76-
```
77-
78-
### Why Use Rust LSP?
79-
80-
- **Accurate**: Understands Rust's type system and module resolution
81-
- **Fast**: Instant navigation without scanning files
82-
- **Context-aware**: Knows about imports, traits, generics
83-
- **Complete**: Shows parameters, return types, documentation
84-
- **IDE-quality**: Same experience as modern IDEs
85-
86-
**Remember: For Rust code, reach for the Rust LSP first; for everything else, use the best tool for the job.**
87-
88-
89-
> REMINDER:
90-
> ALWAYS get approval from the user for git checkout/reset/restore/revert/...
91-
> NEVER run destructive git commands without explicit approval
92-
93-
9415
## Code Style & Formatting
9516

9617
- Refactor, don't keep adding to the technical debt

src/cli/suggest.rs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use crate::server::{self, Request, Response};
1111
use crate::shell_history::{get_hostname, normalize_shellname};
1212
use crate::tokenize::tokenize;
1313

14+
const DEFAULT_AUTOSUGGEST_TIMEOUT_MS: u64 = 150;
15+
1416
pub async fn run(backend: BackendRef<'_>, args: SuggestArgs) -> Result<()> {
1517
let SuggestArgs {
1618
count,
@@ -68,9 +70,7 @@ pub async fn run(backend: BackendRef<'_>, args: SuggestArgs) -> Result<()> {
6870
let prefix = current_line.as_ref().filter(|value| !value.is_empty());
6971
let server_suggestions = match &backend {
7072
BackendRef::Server => {
71-
let timeout_ms = timeout
72-
.map(|duration| Duration::from(duration).as_millis())
73-
.map(|millis| u64::try_from(millis).unwrap_or(u64::MAX));
73+
let timeout_ms = resolve_timeout_ms(timeout, autosuggest);
7474
let request = Request::Suggest {
7575
current_line: prefix.cloned(),
7676
working_directory: cwd.clone(),
@@ -237,6 +237,13 @@ pub async fn run(backend: BackendRef<'_>, args: SuggestArgs) -> Result<()> {
237237
Ok(())
238238
}
239239

240+
fn resolve_timeout_ms(timeout: Option<humantime::Duration>, autosuggest: bool) -> Option<u64> {
241+
timeout
242+
.map(|duration| Duration::from(duration).as_millis())
243+
.map(|millis| u64::try_from(millis).unwrap_or(u64::MAX))
244+
.or_else(|| autosuggest.then_some(DEFAULT_AUTOSUGGEST_TIMEOUT_MS))
245+
}
246+
240247
fn format_zsh_item(word: &str, desc: Option<&str>) -> String {
241248
let mut escaped = String::new();
242249
for ch in word.chars() {
@@ -414,3 +421,32 @@ fn format_suggestion_debug(suggestion: &Suggestion, always_show_score: bool) ->
414421
suggestion.breakdown.online_model
415422
)
416423
}
424+
425+
#[cfg(test)]
426+
mod tests {
427+
use super::*;
428+
429+
#[test]
430+
fn autosuggest_uses_short_default_timeout() {
431+
assert_eq!(
432+
resolve_timeout_ms(None, true),
433+
Some(DEFAULT_AUTOSUGGEST_TIMEOUT_MS)
434+
);
435+
}
436+
437+
#[test]
438+
fn explicit_timeout_overrides_autosuggest_default() {
439+
assert_eq!(
440+
resolve_timeout_ms(
441+
Some(humantime::Duration::from(Duration::from_secs(2))),
442+
true
443+
),
444+
Some(2_000)
445+
);
446+
}
447+
448+
#[test]
449+
fn non_autosuggest_keeps_existing_default() {
450+
assert_eq!(resolve_timeout_ms(None, false), None);
451+
}
452+
}

0 commit comments

Comments
 (0)