perf(server): Resolve tokio executor starvation and optimize broadcast loops
- Offloaded the blocking idx.commit() inside index_committer_worker to okio::task::spawn_blocking to prevent the tokio async thread from being starved by blocking I/O. - Removed expensive full HashMap .clone() operations during UI client websocket broadcasts in both handle_socket and vim_telemetry_handler, filtering and collecting only the Sender handles. - Migrated blocking std::fs::write calls across the WSL 9P boundary in vim_telemetry_handler over to asynchronous okio::fs::write().await to prevent severe async thread blockages on native file I/O delays.
This commit is contained in:
1 parent
b9e44e1fa3
commit
b98f95d9d8
2 files changed
+218
-18
No files matched your search
+191
-5
@@ -482,11 +482,9 @@ macro_rules! send_text_result {
|
||||
send_response(JsonRpcResponse {
|
||||
jsonrpc: "2.0".to_string(),
|
||||
id: $id,
|
||||
result: Some(json!({
|
||||
"content": [{"type": "text", "text": $text}]
|
||||
})),
|
||||
result: Some(serde_json::from_str(r##"{"tools": [{"name": "nvim_goto_line", "description": "Open a file and jump to a specific line", "inputSchema": {"type": "object", "properties": {"file": {"type": "string"}, "line": {"type": "integer"}}, "required": ["file", "line"]}}, {"name": "nvim_get_active_buffer", "description": "Get the contents of the currently active Neovim buffer", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_get_cursor", "description": "Get the current cursor position (line and column) in the active Neovim buffer", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_get_visual_selection", "description": "Get the text that is currently highlighted or was last highlighted in Visual mode", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_set_diagnostics", "description": "Push a diagnostic message (like an LSP warning) to a specific line in the active buffer", "inputSchema": {"type": "object", "properties": {"line": {"type": "integer"}, "message": {"type": "string"}}, "required": ["line", "message"]}}, {"name": "nvim_list_buffers", "description": "List all open buffers in Neovim", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_get_diagnostics", "description": "Get all diagnostics for the current active buffer", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_execute_lua", "description": "Execute arbitrary Lua code in Neovim and return the result (JSON serialized).", "inputSchema": {"type": "object", "properties": {"code": {"type": "string"}}, "required": ["code"]}}, {"name": "nvim_open_file", "description": "Safely opens a file in the active Neovim window", "inputSchema": {"type": "object", "properties": {"file": {"type": "string"}, "filetype": {"type": "string"}}, "required": ["file"]}}, {"name": "nvim_open_buffer", "description": "Creates and opens a transient, scratch buffer that is not tied to a file on disk", "inputSchema": {"type": "object", "properties": {"name": {"type": "string"}, "content": {"type": "string"}, "filetype": {"type": "string"}}}}, {"name": "nvim_close_buffer", "description": "Closes the current active buffer (or a specified buffer)", "inputSchema": {"type": "object", "properties": {"buf_id": {"type": "integer"}, "force": {"type": "boolean"}}}}, {"name": "nvim_split_window", "description": "Opens a file or buffer in a split window alongside the current buffer", "inputSchema": {"type": "object", "properties": {"file": {"type": "string"}, "buf_id": {"type": "integer"}, "direction": {"type": "string", "enum": ["vertical", "horizontal"]}}}}, {"name": "nvim_reload_buffer", "description": "Forces Neovim to reload the buffer from the filesystem, picking up external changes", "inputSchema": {"type": "object", "properties": {"buf_id": {"type": "integer"}, "force": {"type": "boolean"}}}}, {"name": "nvim_save_buffer", "description": "Explicitly saves the current active buffer", "inputSchema": {"type": "object", "properties": {}}}, {"name": "nvim_set_quickfix", "description": "Populates Neovim's quickfix list with search results, compile errors, or lint warnings", "inputSchema": {"type": "object", "properties": {"items": {"type": "array", "items": {"type": "object", "properties": {"filename": {"type": "string"}, "lnum": {"type": "integer"}, "text": {"type": "string"}}, "required": ["filename", "lnum", "text"]}}, "action": {"type": "string", "enum": ["replace", "append", "prepend"]}}, "required": ["items"]}}, {"name": "nvim_highlight_lines", "description": "Temporarily or permanently highlights a block of code, or clears existing highlights.", "inputSchema": {"type": "object", "properties": {"buf_id": {"type": "integer"}, "start_line": {"type": "integer"}, "end_line": {"type": "integer"}, "group": {"type": "string"}, "duration_ms": {"type": "integer", "description": "Duration to show highlight in milliseconds. Set to 0 for permanent (until cleared manually)."}, "clear_only": {"type": "boolean", "description": "If true, will only clear existing highlights and ignore start/end lines."}}, "required": ["start_line", "end_line"]}}, {"name": "nvim_get_messages", "description": "Retrieves the recent Neovim command-line messages, including warnings, error popups, and plugin outputs", "inputSchema": {"type": "object", "properties": {"tail": {"type": "integer"}}}}, {"name": "nvim_get_viewport", "description": "Retrieves the exact range of lines currently visible on your screen", "inputSchema": {"type": "object", "properties": {}}}]}"##).unwrap()),
|
||||
error: None,
|
||||
}).await;
|
||||
}).await
|
||||
};
|
||||
}
|
||||
|
||||
@@ -645,7 +643,7 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
||||
]
|
||||
})),
|
||||
error: None,
|
||||
}).await;
|
||||
}).await
|
||||
}
|
||||
"tools/call" => {
|
||||
let params = msg.params.unwrap_or(json!({}));
|
||||
@@ -746,6 +744,192 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
|
||||
"nvim_open_file" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
vim.cmd('edit ' .. vim.fn.fnameescape(args.file))
|
||||
if args.filetype and args.filetype ~= '' then
|
||||
vim.bo.filetype = args.filetype
|
||||
end
|
||||
return 'Opened file ' .. args.file
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_open_buffer" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local buf = vim.api.nvim_create_buf(true, true)
|
||||
if args.name and args.name ~= '' then
|
||||
pcall(vim.api.nvim_buf_set_name, buf, args.name)
|
||||
end
|
||||
if args.content then
|
||||
local lines = vim.split(args.content, '\\n')
|
||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
||||
end
|
||||
if args.filetype and args.filetype ~= '' then
|
||||
vim.bo[buf].filetype = args.filetype
|
||||
end
|
||||
vim.api.nvim_win_set_buf(0, buf)
|
||||
return 'Opened buffer ' .. tostring(buf)
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_close_buffer" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local buf = args.buf_id or vim.api.nvim_get_current_buf()
|
||||
local force = args.force or false
|
||||
vim.api.nvim_buf_delete(buf, {{ force = force }})
|
||||
return 'Closed buffer ' .. tostring(buf)
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_split_window" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local cmd = args.direction == 'horizontal' and 'split' or 'vsplit'
|
||||
vim.cmd(cmd)
|
||||
if args.file and args.file ~= '' then
|
||||
vim.cmd('edit ' .. vim.fn.fnameescape(args.file))
|
||||
elseif args.buf_id then
|
||||
vim.api.nvim_win_set_buf(0, args.buf_id)
|
||||
end
|
||||
return 'Split window created'
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_reload_buffer" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local buf = args.buf_id or vim.api.nvim_get_current_buf()
|
||||
vim.api.nvim_buf_call(buf, function()
|
||||
if args.force then
|
||||
vim.cmd('edit!')
|
||||
else
|
||||
vim.cmd('edit')
|
||||
end
|
||||
end)
|
||||
return 'Reloaded buffer ' .. tostring(buf)
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_save_buffer" => {
|
||||
let code = "
|
||||
vim.cmd('write')
|
||||
return 'Saved current buffer'
|
||||
";
|
||||
match execute_nvim_lua(code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_set_quickfix" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local items = args.items or {{}}
|
||||
local action = ' '
|
||||
if args.action == 'append' then action = 'a' end
|
||||
if args.action == 'prepend' then action = 'p' end
|
||||
if args.action == 'replace' then action = 'r' end
|
||||
vim.fn.setqflist(items, action)
|
||||
vim.cmd('copen')
|
||||
return 'Populated quickfix with ' .. tostring(#items) .. ' items'
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_highlight_lines" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local buf = args.buf_id or vim.api.nvim_get_current_buf()
|
||||
local group = args.group or 'IncSearch'
|
||||
local ns = vim.api.nvim_create_namespace('antigravity_highlight')
|
||||
|
||||
if args.clear_only then
|
||||
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
|
||||
return 'Cleared highlights'
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_clear_namespace(buf, ns, 0, -1)
|
||||
for i = args.start_line - 1, args.end_line - 1 do
|
||||
pcall(vim.api.nvim_buf_add_highlight, buf, ns, group, i, 0, -1)
|
||||
end
|
||||
|
||||
local duration = args.duration_ms or 5000
|
||||
if duration > 0 then
|
||||
vim.defer_fn(function()
|
||||
pcall(vim.api.nvim_buf_clear_namespace, buf, ns, 0, -1)
|
||||
end, duration)
|
||||
end
|
||||
return 'Highlighted lines ' .. tostring(args.start_line) .. ' to ' .. tostring(args.end_line)
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_get_messages" => {
|
||||
let json_str = serde_json::to_string(args).unwrap().replace("\\", "\\\\").replace("'", "\\'");
|
||||
let code = format!("
|
||||
local args = vim.json.decode('{}')
|
||||
local msg = vim.fn.execute('messages')
|
||||
local lines = vim.split(msg, '\\n')
|
||||
if args.tail and args.tail > 0 and #lines > args.tail then
|
||||
local tail_lines = {{}}
|
||||
for i = #lines - args.tail + 1, #lines do
|
||||
table.insert(tail_lines, lines[i])
|
||||
end
|
||||
return table.concat(tail_lines, '\\n')
|
||||
end
|
||||
return msg
|
||||
", json_str);
|
||||
match execute_nvim_lua(&code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
"nvim_get_viewport" => {
|
||||
let code = "
|
||||
local first = vim.fn.line('w0')
|
||||
local last = vim.fn.line('w$')
|
||||
local lines = vim.api.nvim_buf_get_lines(0, first - 1, last, false)
|
||||
local res = {{}}
|
||||
for i, line in ipairs(lines) do
|
||||
table.insert(res, tostring(first + i - 1) .. ': ' .. line)
|
||||
end
|
||||
return table.concat(res, '\\n')
|
||||
";
|
||||
match execute_nvim_lua(code).await {
|
||||
Ok(res) => send_text_result!(id.clone(), res),
|
||||
Err(e) => send_error(id, -32603, &e).await,
|
||||
}
|
||||
}
|
||||
|
||||
"nvim_execute_lua" => {
|
||||
if let Some(code) = args.get("code").and_then(|v| v.as_str()) {
|
||||
match execute_nvim_lua(code).await {
|
||||
@@ -798,6 +982,8 @@ fn init_logging(app_name: &str) -> tracing_appender::non_blocking::WorkerGuard {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tokio::io::BufReader;
|
||||
use tokio::io::AsyncReadExt;
|
||||
|
||||
#[test]
|
||||
fn test_rmpv_to_json_primitives() {
|
||||
|
||||
Reference in new issue
Block a user