Skip to content

Commit df746ad

Browse files
committed
fix(lsp): filter inlay hints by requested range, improve error messages
Filter inlay hints by the range requested by the editor to fix partial hint rendering in Zed. Show actionable GITHUB_TOKEN error when GitHub API rate limit is hit without authentication.
1 parent 2620531 commit df746ad

File tree

4 files changed

+70
-27
lines changed

4 files changed

+70
-27
lines changed

README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,19 @@ export GITHUB_TOKEN=$(gh auth token)
260260
export GITHUB_TOKEN=ghp_...
261261
```
262262

263+
For **Zed**, launch with the token so the LSP process inherits it:
264+
265+
```bash
266+
# bash / zsh
267+
alias zed='GITHUB_TOKEN="$(gh auth token)" command zed'
268+
269+
# fish
270+
alias zed='env GITHUB_TOKEN=(gh auth token) command zed'
271+
```
272+
273+
> [!TIP]
274+
> Add the alias to your shell profile (`~/.zshrc`, `~/.bashrc`, `~/.config/fish/config.fish`) for persistence.
275+
263276
## Development
264277

265278
> [!IMPORTANT]

crates/deps-lsp/src/document/lifecycle.rs

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ struct FetchResult {
6262
versions: HashMap<String, String>,
6363
/// Number of packages that failed to fetch (timeout or error)
6464
failed_count: usize,
65+
/// First actionable error message (shown to user via `window/showMessage`)
66+
first_error: Option<String>,
6567
}
6668

6769
/// Fetches latest versions for multiple packages in parallel with progress reporting.
@@ -104,13 +106,15 @@ async fn fetch_latest_versions_parallel(
104106

105107
let fetched = Arc::new(std::sync::atomic::AtomicUsize::new(0));
106108
let failed = Arc::new(std::sync::atomic::AtomicUsize::new(0));
109+
let first_error: Arc<std::sync::Mutex<Option<String>>> = Arc::new(std::sync::Mutex::new(None));
107110
let timeout = Duration::from_secs(timeout_secs);
108111

109112
let results: Vec<_> = stream::iter(package_names)
110113
.map(|name| {
111114
let registry = Arc::clone(&registry);
112115
let fetched = Arc::clone(&fetched);
113116
let failed = Arc::clone(&failed);
117+
let first_error = Arc::clone(&first_error);
114118
let progress_sender = progress_sender.clone();
115119
async move {
116120
let result =
@@ -128,6 +132,10 @@ async fn fetch_latest_versions_parallel(
128132
Ok(Err(e)) => {
129133
tracing::warn!(package = %name, error = %e, "fetch failed");
130134
failed.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
135+
let mut fe = first_error.lock().unwrap_or_else(|p| p.into_inner());
136+
if fe.is_none() {
137+
*fe = Some(e.to_string());
138+
}
131139
None
132140
}
133141
Err(_) => {
@@ -152,6 +160,7 @@ async fn fetch_latest_versions_parallel(
152160
FetchResult {
153161
versions: results.into_iter().flatten().collect(),
154162
failed_count: failed.load(std::sync::atomic::Ordering::Relaxed),
163+
first_error: first_error.lock().unwrap_or_else(|p| p.into_inner()).take(),
155164
}
156165
}
157166

@@ -298,14 +307,16 @@ pub async fn handle_document_open(
298307

299308
// Notify user about failed packages
300309
if fetch_result.failed_count > 0 {
301-
client_clone
302-
.show_message(
303-
tower_lsp_server::ls_types::MessageType::WARNING,
304-
format!(
305-
"deps-lsp: {} package(s) failed to fetch (timeout or network error)",
306-
fetch_result.failed_count
307-
),
310+
let message = if let Some(err) = &fetch_result.first_error {
311+
format!("deps-lsp: {err}")
312+
} else {
313+
format!(
314+
"deps-lsp: {} package(s) failed to fetch (timeout or network error)",
315+
fetch_result.failed_count
308316
)
317+
};
318+
client_clone
319+
.show_message(MessageType::WARNING, message)
309320
.await;
310321
}
311322

@@ -505,14 +516,16 @@ pub async fn handle_document_change(
505516

506517
// Notify user about failed packages
507518
if fetch_result.failed_count > 0 {
508-
client_clone
509-
.show_message(
510-
tower_lsp_server::ls_types::MessageType::WARNING,
511-
format!(
512-
"deps-lsp: {} package(s) failed to fetch (timeout or network error)",
513-
fetch_result.failed_count
514-
),
519+
let message = if let Some(err) = &fetch_result.first_error {
520+
format!("deps-lsp: {err}")
521+
} else {
522+
format!(
523+
"deps-lsp: {} package(s) failed to fetch (timeout or network error)",
524+
fetch_result.failed_count
515525
)
526+
};
527+
client_clone
528+
.show_message(MessageType::WARNING, message)
516529
.await;
517530
}
518531

crates/deps-lsp/src/server.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -378,17 +378,21 @@ impl LanguageServer for Backend {
378378
async fn inlay_hint(&self, params: InlayHintParams) -> Result<Option<Vec<InlayHint>>> {
379379
// Clone config before async call to release lock early
380380
let inlay_config = { self.config.read().await.inlay_hints.clone() };
381+
let range = params.range;
381382

382-
Ok(Some(
383-
inlay_hints::handle_inlay_hints(
384-
Arc::clone(&self.state),
385-
params,
386-
&inlay_config,
387-
self.client.clone(),
388-
Arc::clone(&self.config),
389-
)
390-
.await,
391-
))
383+
let hints: Vec<_> = inlay_hints::handle_inlay_hints(
384+
Arc::clone(&self.state),
385+
params,
386+
&inlay_config,
387+
self.client.clone(),
388+
Arc::clone(&self.config),
389+
)
390+
.await
391+
.into_iter()
392+
.filter(|h| h.position.line >= range.start.line && h.position.line <= range.end.line)
393+
.collect();
394+
395+
Ok(Some(hints))
392396
}
393397

394398
async fn code_action(

crates/deps-swift/src/registry.rs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ impl std::error::Error for InvalidOwnerRepo {}
4343
pub struct SwiftRegistry {
4444
cache: Arc<HttpCache>,
4545
auth_headers: Vec<(reqwest::header::HeaderName, String)>,
46+
has_token: bool,
4647
}
4748

4849
impl SwiftRegistry {
@@ -51,8 +52,9 @@ impl SwiftRegistry {
5152
/// Reads `GITHUB_TOKEN` from environment for authenticated requests
5253
/// (5000 req/h vs 60 req/h unauthenticated).
5354
pub fn new(cache: Arc<HttpCache>) -> Self {
54-
let auth_headers = std::env::var("GITHUB_TOKEN")
55-
.ok()
55+
let token = std::env::var("GITHUB_TOKEN").ok().filter(|t| !t.is_empty());
56+
let has_token = token.is_some();
57+
let auth_headers = token
5658
.map(|token| {
5759
tracing::info!("GITHUB_TOKEN detected, using authenticated GitHub API requests");
5860
vec![(reqwest::header::AUTHORIZATION, format!("Bearer {token}"))]
@@ -62,6 +64,7 @@ impl SwiftRegistry {
6264
Self {
6365
cache,
6466
auth_headers,
67+
has_token,
6568
}
6669
}
6770

@@ -81,7 +84,17 @@ impl SwiftRegistry {
8184
let data = self
8285
.cache
8386
.get_cached_with_headers(&url, &self.headers())
84-
.await?;
87+
.await
88+
.map_err(|e| {
89+
if !self.has_token && e.to_string().contains("HTTP 403") {
90+
SwiftError::GitHubApiError {
91+
status: 403,
92+
message: "GitHub API rate limit exceeded. Set GITHUB_TOKEN to increase the limit (5000 req/h). Run: export GITHUB_TOKEN=$(gh auth token)".into(),
93+
}.into()
94+
} else {
95+
e
96+
}
97+
})?;
8598
parse_tags_response(&data)
8699
}
87100

0 commit comments

Comments
 (0)