style(clippy): auto-fix code style issues and unnecessary map_or allocations detected by cargo clippy

This commit is contained in:
Riz Ashraf committed 2026-09-27 22:38:08 +01:00
1 parent febd48c65d
commit 706e9ad172
9 files changed
+126 -69

No files matched your search

+5 -1
View File
@@ -8,7 +8,11 @@ pub static HEADLESS_PROC: LazyLock<Arc<std::sync::Mutex<Option<tokio::process::C
LazyLock::new(|| Arc::new(std::sync::Mutex::new(None))); LazyLock::new(|| Arc::new(std::sync::Mutex::new(None)));
pub async fn spawn_headless_nvim() -> Result<String, String> { pub async fn spawn_headless_nvim() -> Result<String, String> {
if std::process::Command::new("nvim").arg("--version").output().is_err() { if std::process::Command::new("nvim")
.arg("--version")
.output()
.is_err()
{
return Err("Error: Neovim is not installed or not in PATH. Cannot execute nvim tool. Fallback to basic terminal tools.".to_string()); return Err("Error: Neovim is not installed or not in PATH. Cannot execute nvim tool. Fallback to basic terminal tools.".to_string());
} }
+65 -42
View File
@@ -169,18 +169,22 @@ static NVIM_STATE: LazyLock<Arc<std::sync::Mutex<NvimState>>> =
LazyLock::new(|| Arc::new(std::sync::Mutex::new(NvimState::default()))); LazyLock::new(|| Arc::new(std::sync::Mutex::new(NvimState::default())));
fn handle_nvim_notification(params: &[rmpv::Value]) { fn handle_nvim_notification(params: &[rmpv::Value]) {
if params.is_empty() { return; } if params.is_empty() {
return;
}
if let rmpv::Value::String(event) = &params[0] { if let rmpv::Value::String(event) = &params[0] {
match event.as_str().unwrap_or("") { match event.as_str().unwrap_or("") {
"CursorMoved" => { "CursorMoved" => {
if params.len() > 1 if params.len() > 1
&& let rmpv::Value::Array(pos) = &params[1] && let rmpv::Value::Array(pos) = &params[1]
&& pos.len() >= 4 && pos.len() >= 4
&& let (rmpv::Value::Integer(row), rmpv::Value::Integer(col)) = (&pos[1], &pos[2]) { && let (rmpv::Value::Integer(row), rmpv::Value::Integer(col)) =
let mut state = NVIM_STATE.lock().unwrap_or_else(|e| e.into_inner()); (&pos[1], &pos[2])
state.cursor = format!("Line: {}, Column: {}", row, col); {
} let mut state = NVIM_STATE.lock().unwrap_or_else(|e| e.into_inner());
}, state.cursor = format!("Line: {}, Column: {}", row, col);
}
}
"TextChanged" => { "TextChanged" => {
tracing::debug!("Shadow buffer text changed event received"); tracing::debug!("Shadow buffer text changed event received");
} }
@@ -195,28 +199,32 @@ async fn get_nvim_connection() -> Result<mpsc::Sender<NvimRequest>, String> {
{ {
let mut conn_lock = NVIM_CONN.lock().unwrap_or_else(|e| e.into_inner()); let mut conn_lock = NVIM_CONN.lock().unwrap_or_else(|e| e.into_inner());
if let Some(sender) = conn_lock.as_ref() { if let Some(sender) = conn_lock.as_ref()
if !sender.is_closed() { && !sender.is_closed()
let current_socket = { {
let active_lock = ACTIVE_SOCKET.lock().unwrap_or_else(|e| e.into_inner()); let current_socket = {
active_lock.clone() let active_lock = ACTIVE_SOCKET.lock().unwrap_or_else(|e| e.into_inner());
}; active_lock.clone()
};
if let Some(ref interactive) = interactive_socket { if let Some(ref interactive) = interactive_socket {
if Some(interactive.clone()) != current_socket { if Some(interactive.clone()) != current_socket {
tracing::info!("Interactive Preemption: New Neovim instance detected at {}. Disconnecting from current.", interactive); tracing::info!(
should_kill_headless = true; "Interactive Preemption: New Neovim instance detected at {}. Disconnecting from current.",
*conn_lock = None; interactive
{ );
let mut active_lock2 = ACTIVE_SOCKET.lock().unwrap_or_else(|e| e.into_inner()); should_kill_headless = true;
*active_lock2 = None; *conn_lock = None;
} {
} else { let mut active_lock2 =
return Ok(sender.clone()); ACTIVE_SOCKET.lock().unwrap_or_else(|e| e.into_inner());
*active_lock2 = None;
} }
} else { } else {
return Ok(sender.clone()); return Ok(sender.clone());
} }
} else {
return Ok(sender.clone());
} }
} }
} }
@@ -238,7 +246,10 @@ async fn get_nvim_connection() -> Result<mpsc::Sender<NvimRequest>, String> {
spawn_headless_nvim().await? spawn_headless_nvim().await?
}; };
tracing::info!("Establishing new persistent connection to Neovim at {}", socket_path); tracing::info!(
"Establishing new persistent connection to Neovim at {}",
socket_path
);
#[cfg(windows)] #[cfg(windows)]
let stream = { let stream = {
@@ -308,12 +319,14 @@ async fn get_nvim_connection() -> Result<mpsc::Sender<NvimRequest>, String> {
if let Some((_, reply_sender)) = pending_clone2.remove(&msgid) { if let Some((_, reply_sender)) = pending_clone2.remove(&msgid) {
let _ = reply_sender.send(Ok(val)); let _ = reply_sender.send(Ok(val));
} }
} else if arr.len() >= 3 && arr[0] == rmpv::Value::Integer(2.into()) } else if arr.len() >= 3
&& arr[0] == rmpv::Value::Integer(2.into())
&& let rmpv::Value::String(method) = &arr[1] && let rmpv::Value::String(method) = &arr[1]
&& method.as_str().unwrap_or("") == "mcp_event" && method.as_str().unwrap_or("") == "mcp_event"
&& let rmpv::Value::Array(params) = &arr[2] { && let rmpv::Value::Array(params) = &arr[2]
handle_nvim_notification(params); {
} handle_nvim_notification(params);
}
} }
continue; continue;
} }
@@ -407,11 +420,13 @@ async fn get_nvim_connection() -> Result<mpsc::Sender<NvimRequest>, String> {
]), ]),
]); ]);
let (reply_tx, _reply_rx) = tokio::sync::oneshot::channel(); let (reply_tx, _reply_rx) = tokio::sync::oneshot::channel();
let _ = tx_clone.send(NvimRequest { let _ = tx_clone
msgid, .send(NvimRequest {
req, msgid,
reply: reply_tx, req,
}).await; reply: reply_tx,
})
.await;
tracing::info!("Injected bidirectional event tracking autocmds into Neovim"); tracing::info!("Injected bidirectional event tracking autocmds into Neovim");
}); });
@@ -781,7 +796,8 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
id, id,
result: Some(json!({"resources": []})), result: Some(json!({"resources": []})),
error: None, error: None,
}).await; })
.await;
} }
"resources/templates/list" => { "resources/templates/list" => {
send_response(JsonRpcResponse { send_response(JsonRpcResponse {
@@ -789,7 +805,8 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
id, id,
result: Some(json!({"resourceTemplates": []})), result: Some(json!({"resourceTemplates": []})),
error: None, error: None,
}).await; })
.await;
} }
"prompts/list" => { "prompts/list" => {
send_response(JsonRpcResponse { send_response(JsonRpcResponse {
@@ -797,7 +814,8 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
id, id,
result: Some(json!({"prompts": []})), result: Some(json!({"prompts": []})),
error: None, error: None,
}).await; })
.await;
} }
"tools/list" => { "tools/list" => {
@@ -1431,8 +1449,10 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
"nvim_read_file" => { "nvim_read_file" => {
if let Some(file) = args.get("file").and_then(|v| v.as_str()) { if let Some(file) = args.get("file").and_then(|v| v.as_str()) {
let start_line = args.get("start_line").and_then(|v| v.as_i64()).unwrap_or(1); let start_line =
let end_line = args.get("end_line").and_then(|v| v.as_i64()).unwrap_or(-1); args.get("start_line").and_then(|v| v.as_i64()).unwrap_or(1);
let end_line =
args.get("end_line").and_then(|v| v.as_i64()).unwrap_or(-1);
let escaped_file = file.replace('\\', "\\\\").replace('"', "\\\""); let escaped_file = file.replace('\\', "\\\\").replace('"', "\\\"");
let code = format!(" let code = format!("
@@ -1466,7 +1486,8 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
args.get("pattern").and_then(|v| v.as_str()), args.get("pattern").and_then(|v| v.as_str()),
) { ) {
let escaped_file = file.replace('\\', "\\\\").replace('"', "\\\""); let escaped_file = file.replace('\\', "\\\\").replace('"', "\\\"");
let escaped_pattern = pattern.replace('\\', "\\\\").replace('"', "\\\""); let escaped_pattern =
pattern.replace('\\', "\\\\").replace('"', "\\\"");
let code = format!(" let code = format!("
local file = \"{escaped_file}\" local file = \"{escaped_file}\"
local pattern = \"{escaped_pattern}\" local pattern = \"{escaped_pattern}\"
@@ -1545,7 +1566,9 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
let exe_path = std::env::current_exe() let exe_path = std::env::current_exe()
.map(|p| p.to_string_lossy().to_string()) .map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| "unknown".to_string()); .unwrap_or_else(|_| "unknown".to_string());
let socket = get_socket_path().await.unwrap_or_else(|e| format!("Error: {}", e)); let socket = get_socket_path()
.await
.unwrap_or_else(|e| format!("Error: {}", e));
let info = format!( let info = format!(
"Server: {}\nVersion: {}\nOS: {}\nExecutable: {}\nBackend Socket: {}", "Server: {}\nVersion: {}\nOS: {}\nExecutable: {}\nBackend Socket: {}",
app_name, app_version, os, exe_path, socket app_name, app_version, os, exe_path, socket
+2 -2
View File
@@ -17,10 +17,10 @@ pub async fn wait_for_event_handler(
loop { loop {
match rx.recv().await { match rx.recv().await {
Ok(event) => { Ok(event) => {
let topic_matches = topic.as_ref().map_or(true, |t| t == &event.topic); let topic_matches = topic.as_ref().is_none_or(|t| t == &event.topic);
let session_matches = session_id let session_matches = session_id
.as_ref() .as_ref()
.map_or(true, |s| Some(s) == event.session_id.as_ref()); .is_none_or(|s| Some(s) == event.session_id.as_ref());
if topic_matches && session_matches { if topic_matches && session_matches {
return axum::Json(event); return axum::Json(event);
+11 -11
View File
@@ -50,17 +50,17 @@ pub async fn nvim_telemetry_handler(
let _ = tx.try_send(msg_str.clone()); let _ = tx.try_send(msg_str.clone());
} }
if payload.event == "BufWritePost" { if payload.event == "BufWritePost"
if let Some(ref file_path) = payload.file { && let Some(ref file_path) = payload.file
let normalized_file = file_path.replace("\\", "/"); {
let topic = format!("nvim:save:{}", normalized_file); let normalized_file = file_path.replace("\\", "/");
let event = crate::state::GenericEvent { let topic = format!("nvim:save:{}", normalized_file);
topic, let event = crate::state::GenericEvent {
session_id: Some(payload.session_id.clone()), topic,
payload: serde_json::json!(&payload), session_id: Some(payload.session_id.clone()),
}; payload: serde_json::json!(&payload),
let _ = state.handler.state.event_bus_tx.send(event); };
} let _ = state.handler.state.event_bus_tx.send(event);
} }
axum::Json(serde_json::json!({"status": "ok"})) axum::Json(serde_json::json!({"status": "ok"}))
+2 -3
View File
@@ -274,10 +274,9 @@ impl McpTool for ReadPreferencesHandler {
} }
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = state state
.prefs .prefs
.read_with(|prefs| serde_json::to_string(prefs).map_err(|e| e.to_string())); .read_with(|prefs| serde_json::to_string(prefs).map_err(|e| e.to_string()))
data
} }
} }
+1 -1
View File
@@ -275,7 +275,7 @@ impl McpTool for ListContextWorkspacesHandler {
let data = state.context_workspaces.read_with(|ws| { let data = state.context_workspaces.read_with(|ws| {
let filtered: Vec<_> = ws let filtered: Vec<_> = ws
.iter() .iter()
.filter(|w| req.namespace.as_ref().map_or(true, |ns| &w.namespace == ns)) .filter(|w| req.namespace.as_ref().is_none_or(|ns| &w.namespace == ns))
.collect(); .collect();
serde_json::to_string(&filtered).map_err(|e| e.to_string()) serde_json::to_string(&filtered).map_err(|e| e.to_string())
})?; })?;
+26 -5
View File
@@ -587,8 +587,16 @@ mod tests {
}); });
let res_list = handler.handle_request(req_list_res).await.unwrap(); let res_list = handler.handle_request(req_list_res).await.unwrap();
let resources_arr = res_list["result"]["resources"].as_array().unwrap(); let resources_arr = res_list["result"]["resources"].as_array().unwrap();
assert!(resources_arr.iter().any(|r| r["uri"] == "memory://tasks/active")); assert!(
assert!(resources_arr.iter().any(|r| r["uri"] == "memory://pinned_files")); resources_arr
.iter()
.any(|r| r["uri"] == "memory://tasks/active")
);
assert!(
resources_arr
.iter()
.any(|r| r["uri"] == "memory://pinned_files")
);
// Test resources/read // Test resources/read
let req_read_res = json!({ let req_read_res = json!({
@@ -600,8 +608,16 @@ mod tests {
} }
}); });
let res_read = handler.handle_request(req_read_res).await.unwrap(); let res_read = handler.handle_request(req_read_res).await.unwrap();
assert_eq!(res_read["result"]["contents"][0]["uri"], "memory://tasks/active"); assert_eq!(
assert!(res_read["result"]["contents"][0]["text"].as_str().unwrap().contains("[]")); // Empty tasks res_read["result"]["contents"][0]["uri"],
"memory://tasks/active"
);
assert!(
res_read["result"]["contents"][0]["text"]
.as_str()
.unwrap()
.contains("[]")
); // Empty tasks
// Test prompts/list // Test prompts/list
let req_list_prompts = json!({ let req_list_prompts = json!({
@@ -627,7 +643,12 @@ mod tests {
let res_get = handler.handle_request(req_get_prompt).await.unwrap(); let res_get = handler.handle_request(req_get_prompt).await.unwrap();
let messages = res_get["result"]["messages"].as_array().unwrap(); let messages = res_get["result"]["messages"].as_array().unwrap();
assert_eq!(messages[0]["role"], "user"); assert_eq!(messages[0]["role"], "user");
assert!(messages[0]["content"]["text"].as_str().unwrap().contains("standup report")); assert!(
messages[0]["content"]["text"]
.as_str()
.unwrap()
.contains("standup report")
);
} }
#[tokio::test] #[tokio::test]
+3 -1
View File
@@ -28,7 +28,9 @@ impl<T: DeserializeOwned + Default + Serialize + Clone + Send + Sync + 'static>
let key_inner = key_clone.clone(); let key_inner = key_clone.clone();
let json_data = { let json_data = {
let lock = cache_clone.read().unwrap_or_else(|e| e.into_inner()); let lock = cache_clone.read().unwrap_or_else(|e| e.into_inner());
serde_json::to_vec(&*lock).map_err(|e| tracing::error!("Failed to serialize memory store: {}", e)).ok() serde_json::to_vec(&*lock)
.map_err(|e| tracing::error!("Failed to serialize memory store: {}", e))
.ok()
}; };
if let Some(json_data) = json_data { if let Some(json_data) = json_data {
+11 -3
View File
@@ -1,5 +1,5 @@
use serde_json::{Value, json}; use serde_json::{Value, json};
use std::io::{BufRead, BufReader, Write, Read}; use std::io::{BufRead, BufReader, Read, Write};
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
fn send_message(stdin: &mut std::process::ChildStdin, msg: Value) { fn send_message(stdin: &mut std::process::ChildStdin, msg: Value) {
@@ -161,7 +161,11 @@ fn test_mcp_initialization_and_tools_list() {
send_message(&mut stdin, call_req); send_message(&mut stdin, call_req);
let call_resp = read_message(&mut stdout).expect("Failed to read tools/call response"); let call_resp = read_message(&mut stdout).expect("Failed to read tools/call response");
println!("Response for {}: {}", tool_name, serde_json::to_string(&call_resp).unwrap()); println!(
"Response for {}: {}",
tool_name,
serde_json::to_string(&call_resp).unwrap()
);
assert_eq!(call_resp["jsonrpc"], "2.0"); assert_eq!(call_resp["jsonrpc"], "2.0");
assert_eq!(call_resp["id"], req_id); assert_eq!(call_resp["id"], req_id);
} }
@@ -202,5 +206,9 @@ fn test_mcp_initialization_and_tools_list() {
} }
let status = child.wait().expect("Failed to wait on child"); let status = child.wait().expect("Failed to wait on child");
assert!(status.success(), "Child process did not exit successfully. Stderr: {}", stderr_output); assert!(
status.success(),
"Child process did not exit successfully. Stderr: {}",
stderr_output
);
} }