perf: fix memory leaks, optimize tracing logs, fix networking eof fragmentation bug in nvim-core and stub, and deduplicate json-rpc handlers

This commit is contained in:
Riz Ashraf committed 2026-09-22 04:31:50 +01:00
1 parent ce88bef45e
commit f76d8ccce8
23 files changed
+1373 -803

No files matched your search

+263
View File
@@ -0,0 +1,263 @@
use crate::models::*;
use crate::router::McpTool;
use crate::state::MemoryState;
use crate::tools::*;
use async_trait::async_trait;
use serde_json::Value;
use std::collections::HashSet;
use std::sync::Arc;
pub struct AddStickyNoteHandler;
#[async_trait]
impl McpTool for AddStickyNoteHandler {
fn name(&self) -> &'static str {
"add_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<AddStickyNoteTool>("add_sticky_note", "Execute add_sticky_note")
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: AddStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.sticky.modify(|notes| {
notes.push(StickyNote {
timestamp: crate::handlers::utils::now_secs(),
content: req.content,
});
});
Ok("Sticky note added.".to_string())
}
}
pub struct ReadStickyNotesHandler;
#[async_trait]
impl McpTool for ReadStickyNotesHandler {
fn name(&self) -> &'static str {
"read_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadStickyNotesTool>(
"read_sticky_notes",
"Execute read_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = state
.sticky
.read_with(|s| serde_json::to_string(s).unwrap_or_else(|_| "[]".to_string()));
Ok(data.to_string())
}
}
pub struct DeleteStickyNoteHandler;
#[async_trait]
impl McpTool for DeleteStickyNoteHandler {
fn name(&self) -> &'static str {
"delete_sticky_note"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<DeleteStickyNoteTool>(
"delete_sticky_note",
"Execute delete_sticky_note",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: DeleteStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
state.sticky.modify(|notes| {
if req.index > 0 && req.index <= notes.len() {
notes.remove(req.index - 1);
success = true;
}
});
if success {
Ok("Sticky note deleted.".to_string())
} else {
Err("Invalid sticky note index.".to_string())
}
}
}
pub struct ClearStickyNotesHandler;
#[async_trait]
impl McpTool for ClearStickyNotesHandler {
fn name(&self) -> &'static str {
"clear_sticky_notes"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearStickyNotesTool>(
"clear_sticky_notes",
"Execute clear_sticky_notes",
)
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
state.sticky.modify(|notes| {
notes.clear();
});
Ok("All sticky notes cleared.".to_string())
}
}
pub struct LeaveHandoffMemoHandler;
#[async_trait]
impl McpTool for LeaveHandoffMemoHandler {
fn name(&self) -> &'static str {
"leave_handoff_memo"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<LeaveHandoffMemoTool>(
"leave_handoff_memo",
"Execute leave_handoff_memo",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: LeaveHandoffMemoTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
author: "agy".to_string(),
content: req.content,
namespace: req.namespace,
timestamp: crate::handlers::utils::now_secs(),
})
});
Ok("Handoff memo left".to_string())
}
}
pub struct ReadHandoffMemosHandler;
#[async_trait]
impl McpTool for ReadHandoffMemosHandler {
fn name(&self) -> &'static str {
"read_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ReadHandoffMemosTool>(
"read_handoff_memos",
"Execute read_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.handoff_memos.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| {
if let Some(ns) = &req.namespace {
&i.namespace == ns
} else {
true
}
})
.collect();
serde_json::to_string(&filtered).unwrap_or_default()
});
Ok(data.to_string())
}
}
pub struct ClearHandoffMemosHandler;
#[async_trait]
impl McpTool for ClearHandoffMemosHandler {
fn name(&self) -> &'static str {
"clear_handoff_memos"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<ClearHandoffMemosTool>(
"clear_handoff_memos",
"Execute clear_handoff_memos",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: ClearHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let ids: HashSet<_> = req.ids.into_iter().collect();
state
.handoff_memos
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
Ok("Handoff memos cleared".to_string())
}
}
pub struct AddSessionSummaryHandler;
#[async_trait]
impl McpTool for AddSessionSummaryHandler {
fn name(&self) -> &'static str {
"add_session_summary"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<AddSessionSummaryTool>(
"add_session_summary",
"Execute add_session_summary",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: AddSessionSummaryTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.session_summaries.modify(|summaries| {
summaries.push(crate::models::SessionSummary {
summary: req.summary,
namespace: req.namespace,
timestamp: crate::handlers::utils::now_secs(),
})
});
Ok("Session summary added".to_string())
}
}
pub struct GenerateStandupReportHandler;
#[async_trait]
impl McpTool for GenerateStandupReportHandler {
fn name(&self) -> &'static str {
"generate_standup_report"
}
fn schema(&self) -> Value {
crate::mcp::tool_def::<GenerateStandupReportTool>(
"generate_standup_report",
"Execute generate_standup_report",
)
}
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: GenerateStandupReportTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let cutoff =
crate::handlers::utils::now_secs().saturating_sub(req.hours_lookback * 3600);
let report_str = state.tasks.read_with(|items| {
state.ledger.read_with(|changes| {
state.session_summaries.read_with(|summaries| {
let filtered_tasks: Vec<_> = items.iter().filter(|t| t.updated_at >= cutoff).collect();
let filtered_changes: Vec<_> = changes.iter().filter(|c| c.timestamp >= cutoff).collect();
let filtered_summaries: Vec<_> = summaries.iter().filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff).collect();
let report = serde_json::json!({ "tasks_updated": filtered_tasks, "code_changes": filtered_changes, "session_summaries": filtered_summaries });
report.to_string()
})
})
});
Ok(report_str)
}
}