ci: add GitHub Actions workflows with semantic-release auto-versioning

chore: fix all 702 clippy warnings across codebase
- auto-fix 475 via cargo clippy --fix
- fix remaining 227 manually: uninlined_format_args, redundant_closure, match_same_arms,
  underscore_binding, format_push_string, items_after_statements, needless_pass_by_value,
  clone_on_copy, case_sensitive_extension, single_match/let-else, write_with_newline,
  and other clippy lints
This commit is contained in:
asepharyana
2026-07-13 08:12:12 +07:00
parent be921d6836
commit 29a9fae3f6
79 changed files with 826 additions and 904 deletions
+60 -63
View File
@@ -1,3 +1,5 @@
#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_precision_loss, clippy::cast_possible_wrap)]
use std::fmt::Write;
use serde_json::{json, Value};
use anyhow::{Result, anyhow};
@@ -59,7 +61,7 @@ impl Tool for LspConnect {
.unwrap_or_default();
let mut manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
manager.connect(name, command, &extra_args, language_id)?;
// Auto-register this server's known extensions so lsp_diagnostics /
@@ -79,8 +81,7 @@ impl Tool for LspConnect {
.unwrap_or_else(|_| "{}".to_string());
Ok(format!(
"Connected to LSP server '{}' (language: {})\nServer capabilities:\n{}",
name, language_id, caps_summary
"Connected to LSP server '{name}' (language: {language_id})\nServer capabilities:\n{caps_summary}"
))
}
}
@@ -132,15 +133,15 @@ impl Tool for LspDiagnostics {
let uri = path_to_lsp_uri(&abs_path.to_string_lossy());
let manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
let language_id = manager.get_language_id(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found. Use lsp_connect first.", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found. Use lsp_connect first."))?;
let client_arc = manager.get_client(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found"))?;
drop(manager);
let mut client = client_arc.lock()
.map_err(|e| anyhow!("LSP client lock error: {}", e))?;
.map_err(|e| anyhow!("LSP client lock error: {e}"))?;
match client.collect_diagnostics(&uri, &language_id, text) {
Ok(diags) => {
@@ -151,7 +152,7 @@ impl Tool for LspDiagnostics {
let mut output = String::from("Diagnostics:\n");
for d in &diags_array {
let range = d.get("range").and_then(|r| r.get("start"));
let severity = match d.get("severity").and_then(|s| s.as_i64()).unwrap_or(0) {
let severity = match d.get("severity").and_then(serde_json::Value::as_i64).unwrap_or(0) {
1 => "ERROR",
2 => "WARNING",
3 => "INFO",
@@ -159,13 +160,13 @@ impl Tool for LspDiagnostics {
_ => "NOTE",
};
let message = d.get("message").and_then(|m| m.as_str()).unwrap_or("?");
let line = range.and_then(|r| r.get("line")).and_then(|l| l.as_i64()).unwrap_or(0);
let col = range.and_then(|r| r.get("character")).and_then(|c| c.as_i64()).unwrap_or(0);
let line = range.and_then(|r| r.get("line")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let col = range.and_then(|r| r.get("character")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let code = d.get("code")
.and_then(|c| c.as_str().or_else(|| c.as_i64().map(|n| Box::leak(Box::new(n.to_string()))).map(|s| s.as_str())))
.unwrap_or("");
let code_str = if code.is_empty() { String::new() } else { format!(" [{}]", code) };
output.push_str(&format!(" {}:{}:{} - {}{}: {}\n", rel_path, line + 1, col, severity, code_str, message));
let code_str = if code.is_empty() { String::new() } else { format!(" [{code}]") };
writeln!(output, " {}:{}:{} - {}{}: {}", rel_path, line + 1, col, severity, code_str, message).unwrap();
}
Ok(output)
}
@@ -226,10 +227,10 @@ impl Tool for LspHover {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: path"))?;
let line = args.get("line")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: line"))? as u32;
let column = args.get("column")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: column"))? as u32;
let server_name = resolve_server_name(ctx, args, rel_path)?;
let server_name = server_name.as_str();
@@ -238,20 +239,20 @@ impl Tool for LspHover {
let uri = path_to_lsp_uri(&abs_path.to_string_lossy());
let file_content = std::fs::read_to_string(&abs_path)
.map_err(|e| anyhow!("failed to read file '{}': {}", rel_path, e))?;
.map_err(|e| anyhow!("failed to read file '{rel_path}': {e}"))?;
let manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
let language_id = manager.get_language_id(server_name)
.unwrap_or_else(|| {
args.get("language_id").and_then(|v| v.as_str()).unwrap_or("plaintext").to_string()
});
let client_arc = manager.get_client(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found. Use lsp_connect first.", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found. Use lsp_connect first."))?;
drop(manager);
let mut client = client_arc.lock()
.map_err(|e| anyhow!("LSP client lock error: {}", e))?;
.map_err(|e| anyhow!("LSP client lock error: {e}"))?;
client.did_open(&uri, &language_id, 1, &file_content)?;
let result = client.hover(&uri, line, column);
@@ -267,9 +268,9 @@ impl Tool for LspHover {
let mut output = String::new();
if let Some(range_val) = range {
if let Some(start) = range_val.get("start") {
let rl = start.get("line").and_then(|l| l.as_i64()).unwrap_or(0);
let rc = start.get("character").and_then(|c| c.as_i64()).unwrap_or(0);
output.push_str(&format!("Range: {}:{}\n", rl + 1, rc + 1));
let rl = start.get("line").and_then(serde_json::Value::as_i64).unwrap_or(0);
let rc = start.get("character").and_then(serde_json::Value::as_i64).unwrap_or(0);
writeln!(output, "Range: {}:{}", rl + 1, rc + 1).unwrap();
}
}
if let Some(contents_val) = contents {
@@ -292,7 +293,7 @@ fn format_hover_contents(contents: &Value) -> String {
}
Value::Object(map) => {
if let Some(kind) = map.get("kind").and_then(|k| k.as_str()) {
out.push_str(&format!("[{kind}] "));
write!(out, "[{kind}] ").unwrap();
}
if let Some(value) = map.get("value").and_then(|v| v.as_str()) {
out.push_str(value);
@@ -355,10 +356,10 @@ impl Tool for LspCompletion {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: path"))?;
let line = args.get("line")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: line"))? as u32;
let column = args.get("column")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: column"))? as u32;
let server_name = resolve_server_name(ctx, args, rel_path)?;
let server_name = server_name.as_str();
@@ -367,18 +368,18 @@ impl Tool for LspCompletion {
let uri = path_to_lsp_uri(&abs_path.to_string_lossy());
let file_content = std::fs::read_to_string(&abs_path)
.map_err(|e| anyhow!("failed to read file '{}': {}", rel_path, e))?;
.map_err(|e| anyhow!("failed to read file '{rel_path}': {e}"))?;
let manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
let language_id = manager.get_language_id(server_name)
.unwrap_or_else(|| "plaintext".to_string());
let client_arc = manager.get_client(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found. Use lsp_connect first.", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found. Use lsp_connect first."))?;
drop(manager);
let mut client = client_arc.lock()
.map_err(|e| anyhow!("LSP client lock error: {}", e))?;
.map_err(|e| anyhow!("LSP client lock error: {e}"))?;
client.did_open(&uri, &language_id, 1, &file_content)?;
let result = client.completion(&uri, line, column);
@@ -401,7 +402,7 @@ impl Tool for LspCompletion {
let mut output = format!("{} completion suggestions at {}:{}:\n", items.len(), line + 1, column + 1);
for (i, item) in items.iter().enumerate().take(50) {
let label = item.get("label").and_then(|l| l.as_str()).unwrap_or("?");
let kind = match item.get("kind").and_then(|k| k.as_i64()).unwrap_or(0) {
let kind = match item.get("kind").and_then(serde_json::Value::as_i64).unwrap_or(0) {
1 => "Text",
2 => "Method",
3 => "Function",
@@ -430,11 +431,11 @@ impl Tool for LspCompletion {
_ => "Other",
};
let detail = item.get("detail").and_then(|d| d.as_str()).unwrap_or("");
let detail_str = if detail.is_empty() { String::new() } else { format!(" - {}", detail) };
output.push_str(&format!(" {}. [{}] {}{}\n", i + 1, kind, label, detail_str));
let detail_str = if detail.is_empty() { String::new() } else { format!(" - {detail}") };
writeln!(output, " {}. [{}] {}{}", i + 1, kind, label, detail_str).unwrap();
}
if items.len() > 50 {
output.push_str(&format!(" ... and {} more\n", items.len() - 50));
writeln!(output, " ... and {} more", items.len() - 50).unwrap();
}
Ok(output)
}
@@ -485,10 +486,10 @@ impl Tool for LspDefinition {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: path"))?;
let line = args.get("line")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: line"))? as u32;
let column = args.get("column")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: column"))? as u32;
let server_name = resolve_server_name(ctx, args, rel_path)?;
let server_name = server_name.as_str();
@@ -497,18 +498,18 @@ impl Tool for LspDefinition {
let uri = path_to_lsp_uri(&abs_path.to_string_lossy());
let file_content = std::fs::read_to_string(&abs_path)
.map_err(|e| anyhow!("failed to read file '{}': {}", rel_path, e))?;
.map_err(|e| anyhow!("failed to read file '{rel_path}': {e}"))?;
let manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
let language_id = manager.get_language_id(server_name)
.unwrap_or_else(|| "plaintext".to_string());
let client_arc = manager.get_client(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found. Use lsp_connect first.", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found. Use lsp_connect first."))?;
drop(manager);
let mut client = client_arc.lock()
.map_err(|e| anyhow!("LSP client lock error: {}", e))?;
.map_err(|e| anyhow!("LSP client lock error: {e}"))?;
client.did_open(&uri, &language_id, 1, &file_content)?;
let result = client.goto_definition(&uri, line, column);
@@ -534,13 +535,13 @@ impl Tool for LspDefinition {
let target_uri = loc.get("uri").and_then(|u| u.as_str()).unwrap_or("?");
let target_range = loc.get("range").or_else(|| loc.get("targetRange"));
let target_start = target_range.and_then(|r| r.get("start"));
let tl = target_start.and_then(|s| s.get("line")).and_then(|l| l.as_i64()).unwrap_or(0);
let tc = target_start.and_then(|s| s.get("character")).and_then(|c| c.as_i64()).unwrap_or(0);
let tl = target_start.and_then(|s| s.get("line")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let tc = target_start.and_then(|s| s.get("character")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let path_str = target_uri.strip_prefix("file://").unwrap_or(target_uri);
output.push_str(&format!(" {}. {}:{}:{}\n", i + 1, path_str, tl + 1, tc + 1));
writeln!(output, " {}. {}:{}:{}", i + 1, path_str, tl + 1, tc + 1).unwrap();
}
if locations.len() > 10 {
output.push_str(&format!(" ... and {} more locations\n", locations.len() - 10));
writeln!(output, " ... and {} more", locations.len() - 10).unwrap();
}
Ok(output)
}
@@ -591,10 +592,10 @@ impl Tool for LspReferences {
.and_then(|v| v.as_str())
.ok_or_else(|| anyhow!("missing required argument: path"))?;
let line = args.get("line")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: line"))? as u32;
let column = args.get("column")
.and_then(|v| v.as_i64())
.and_then(serde_json::Value::as_i64)
.ok_or_else(|| anyhow!("missing required argument: column"))? as u32;
let server_name = resolve_server_name(ctx, args, rel_path)?;
let server_name = server_name.as_str();
@@ -603,18 +604,18 @@ impl Tool for LspReferences {
let uri = path_to_lsp_uri(&abs_path.to_string_lossy());
let file_content = std::fs::read_to_string(&abs_path)
.map_err(|e| anyhow!("failed to read file '{}': {}", rel_path, e))?;
.map_err(|e| anyhow!("failed to read file '{rel_path}': {e}"))?;
let manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
let language_id = manager.get_language_id(server_name)
.unwrap_or_else(|| "plaintext".to_string());
let client_arc = manager.get_client(server_name)
.ok_or_else(|| anyhow!("LSP server '{}' not found. Use lsp_connect first.", server_name))?;
.ok_or_else(|| anyhow!("LSP server '{server_name}' not found. Use lsp_connect first."))?;
drop(manager);
let mut client = client_arc.lock()
.map_err(|e| anyhow!("LSP client lock error: {}", e))?;
.map_err(|e| anyhow!("LSP client lock error: {e}"))?;
client.did_open(&uri, &language_id, 1, &file_content)?;
let result = client.references(&uri, line, column);
@@ -631,13 +632,13 @@ impl Tool for LspReferences {
for (i, loc) in locations.iter().enumerate().take(50) {
let target_uri = loc.get("uri").and_then(|u| u.as_str()).unwrap_or("?");
let range = loc.get("range").and_then(|r| r.get("start"));
let rl = range.and_then(|s| s.get("line")).and_then(|l| l.as_i64()).unwrap_or(0);
let rc = range.and_then(|s| s.get("character")).and_then(|c| c.as_i64()).unwrap_or(0);
let rl = range.and_then(|s| s.get("line")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let rc = range.and_then(|s| s.get("character")).and_then(serde_json::Value::as_i64).unwrap_or(0);
let path_str = target_uri.strip_prefix("file://").unwrap_or(target_uri);
output.push_str(&format!(" {}. {}:{}:{}\n", i + 1, path_str, rl + 1, rc + 1));
writeln!(output, " {}. {}:{}:{}", i + 1, path_str, rl + 1, rc + 1).unwrap();
}
if locations.len() > 50 {
output.push_str(&format!(" ... and {} more references\n", locations.len() - 50));
writeln!(output, " ... and {} more references", locations.len() - 50).unwrap();
}
Ok(output)
}
@@ -676,12 +677,12 @@ impl Tool for LspDisconnect {
.ok_or_else(|| anyhow!("missing required argument: name"))?;
let mut manager = ctx.lsp_manager.lock()
.map_err(|e| anyhow!("LSP manager lock error: {}", e))?;
.map_err(|e| anyhow!("LSP manager lock error: {e}"))?;
if manager.disconnect(name) {
Ok(format!("Disconnected from LSP server '{}'", name))
Ok(format!("Disconnected from LSP server '{name}'"))
} else {
Err(anyhow!("LSP server '{}' not found", name))
Err(anyhow!("LSP server '{name}' not found"))
}
}
}
@@ -718,7 +719,7 @@ fn known_extensions_for(language_id: &str) -> &[&'static str] {
/// connected server's language is known to use that extension.
fn auto_detect_server(ctx: &ToolCtx, path: &str) -> Option<String> {
let ext = std::path::Path::new(path).extension().and_then(|e| e.to_str())?;
let dot_ext = format!(".{}", ext);
let dot_ext = format!(".{ext}");
if let Ok(mgr) = ctx.lsp_manager.lock() {
for s in &mgr.servers {
let exts = known_extensions_for(&s.language_id);
@@ -754,15 +755,13 @@ fn resolve_server_name(ctx: &ToolCtx, args: &Value, path: &str) -> Result<String
let ext = std::path::Path::new(path)
.extension()
.and_then(|e| e.to_str())
.map(|e| format!(".{}", e))
.unwrap_or_else(|| "<none>".to_string());
.and_then(|e| e.to_str()).map_or_else(|| "<none>".to_string(), |e| format!(".{e}"));
let available = ctx.lsp_manager.lock().ok()
.map(|mgr| {
mgr.list_servers()
.iter()
.map(|(name, lang, _)| format!("{} ({})", name, lang))
.map(|(name, lang, _)| format!("{name} ({lang})"))
.collect::<Vec<_>>()
.join(", ")
})
@@ -770,8 +769,6 @@ fn resolve_server_name(ctx: &ToolCtx, args: &Value, path: &str) -> Result<String
let available = if available.is_empty() { "none".to_string() } else { available };
Err(anyhow!(
"LSP server not found for extension '{}'. Use lsp_connect to connect one. Available servers: {}",
ext,
available
"LSP server not found for extension '{ext}'. Use lsp_connect to connect one. Available servers: {available}"
))
}