diff --git a/nvim-core/src/lib.rs b/nvim-core/src/lib.rs index 2631d66..5864603 100644 --- a/nvim-core/src/lib.rs +++ b/nvim-core/src/lib.rs @@ -510,6 +510,31 @@ async fn set_nvim_diagnostics(line: i64, message: &str) -> Result<(), String> { Ok(()) } +async fn set_nvim_extmark(line: i64, text: &str, hl_group: &str) -> Result<(), String> { + let escaped_text = text.replace('\\', "\\\\").replace('"', "\\\""); + let lua_script = format!( + r#" + local ns = vim.api.nvim_create_namespace("gemini_extmarks") + local opts = {{ + virt_text = {{{{"{escaped_text}", "{hl_group}"}}}}, + virt_text_pos = "eol", + }} + vim.api.nvim_buf_set_extmark(0, ns, {line} - 1, 0, opts) + "# + ); + + call_nvim_method( + "nvim_exec_lua", + vec![ + rmpv::Value::String(lua_script.into()), + rmpv::Value::Array(vec![]), + ], + ) + .await?; + + Ok(()) +} + fn rmpv_to_json(val: &rmpv::Value) -> serde_json::Value { match val { rmpv::Value::Nil => serde_json::Value::Null, @@ -731,6 +756,19 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) { "required": ["line", "message"] } }, + { + "name": "nvim_set_extmark", + "description": "Add virtual text (ghost text) to a specific line in the buffer to give the user rich UI feedback", + "inputSchema": { + "type": "object", + "properties": { + "line": { "type": "integer" }, + "text": { "type": "string" }, + "highlight_group": { "type": "string", "description": "Optional. E.g., 'DiagnosticVirtualTextInfo', 'Comment'" } + }, + "required": ["line", "text"] + } + }, { "name": "nvim_list_buffers", "description": "List all open buffers in Neovim", @@ -952,6 +990,28 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) { send_error(id, -32602, "Missing 'line' or 'message'").await; } } + "nvim_set_extmark" => { + if let (Some(line), Some(text)) = ( + args.get("line").and_then(serde_json::Value::as_i64), + args.get("text").and_then(|v| v.as_str()), + ) { + let hl_group = args + .get("highlight_group") + .and_then(|v| v.as_str()) + .unwrap_or("Comment"); + match set_nvim_extmark(line, text, hl_group).await { + Ok(()) => { + send_text_result!( + id.clone(), + format!("Successfully set extmark on line {}", line) + ); + } + Err(e) => send_error(id, -32603, &e).await, + } + } else { + send_error(id, -32602, "Missing 'line' or 'text'").await; + } + } "nvim_list_buffers" => { let lua_code = r" local bufs = vim.api.nvim_list_bufs()