Skip to content

bug fixes #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 29 additions & 29 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mcp-neovim-server",
"version": "0.5.2",
"version": "0.5.3",
"description": "An MCP server for neovim",
"type": "module",
"bin": {
Expand All @@ -26,13 +26,13 @@
},
"homepage": "https://github.com/bigcodegen/mcp-neovim-server#readme",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.11.0",
"@modelcontextprotocol/sdk": "^1.13.2",
"neovim": "^5.3.0",
"ts-node": "^10.9.2",
"zod": "^3.25.64"
"zod": "^3.25.67"
},
"devDependencies": {
"@types/node": "^22.15.3",
"@types/node": "^24.0.6",
"typescript": "^5.8.3"
}
}
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { z } from "zod";
const server = new McpServer(
{
name: "mcp-neovim-server",
version: "0.5.2"
version: "0.5.3"
}
);

Expand Down Expand Up @@ -244,7 +244,7 @@ server.tool(
"vim_register",
"Manage Neovim register contents",
{
register: z.string().regex(/^[a-z\"]$/).describe("Register name - a lowercase letter [a-z] or double-quote [\"] for the unnamed register"),
register: z.string().regex(/^[a-z"]$/).describe("Register name - a lowercase letter [a-z] or double-quote [\"] for the unnamed register"),
content: z.string().describe("The text content to store in the specified register")
},
async ({ register, content }) => {
Expand Down
95 changes: 80 additions & 15 deletions src/neovim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,25 +213,32 @@ export class NeovimManager {
const tabpage = await nvim.tabpage;
const currentTab = await tabpage.number;

// Get marks (a-z)
// Get marks (a-z) - only include set marks
const marks: { [key: string]: [number, number] } = {};
for (const mark of 'abcdefghijklmnopqrstuvwxyz') {
try {
const pos = await nvim.eval(`getpos("'${mark}")`) as [number, number, number, number];
marks[mark] = [pos[1], pos[2]];
// Only include marks that are actually set (not at position 0,0)
if (pos[1] > 0 && pos[2] > 0) {
marks[mark] = [pos[1], pos[2]];
}
} catch (e) {
// Mark not set
}
}

// Get registers (a-z, ", 0-9)
// Get registers (a-z, ", 0-9) - only include non-empty registers
const registers: { [key: string]: string } = {};
const registerNames = [...'abcdefghijklmnopqrstuvwxyz', '"', ...Array(10).keys()];
for (const reg of registerNames) {
try {
registers[reg] = String(await nvim.eval(`getreg('${reg}')`));
const content = String(await nvim.eval(`getreg('${reg}')`));
// Only include registers that have content
if (content && content.trim().length > 0) {
registers[String(reg)] = content;
}
} catch (e) {
// Register empty
// Register empty or error
}
}

Expand All @@ -243,8 +250,8 @@ export class NeovimManager {
let pluginInfo = '';

try {
// Get LSP clients if available
const lspClients = await nvim.eval('luaeval("vim.lsp.get_active_clients()")');
// Get LSP clients if available (use new API for Neovim >=0.10)
const lspClients = await nvim.eval('luaeval("vim.lsp.get_clients()")');
if (Array.isArray(lspClients) && lspClients.length > 0) {
const clientNames = lspClients.map((client: any) => client.name || 'unknown').join(', ');
lspInfo = `Active LSP clients: ${clientNames}`;
Expand Down Expand Up @@ -288,14 +295,72 @@ export class NeovimManager {
};

if (mode.mode.startsWith('v')) {
const start = await nvim.eval(`getpos("'<")`) as [number, number, number, number];
const end = await nvim.eval(`getpos("'>")`) as [number, number, number, number];
const lines = await buffer.getLines({
start: start[1] - 1,
end: end[1],
strictIndexing: true
});
neovimStatus.visualSelection = lines.join('\n');
try {
// Use a more reliable method to get the visual selection
// This Lua code gets the actual selected text
const visualText = await nvim.lua(`
local mode = vim.fn.visualmode()
if mode == '' then
return ''
end

-- Save current register content
local save_reg = vim.fn.getreg('"')
local save_regtype = vim.fn.getregtype('"')

-- Yank the visual selection to unnamed register
vim.cmd('normal! "vy')

-- Get the yanked text
local selected_text = vim.fn.getreg('"')

-- Restore the register
vim.fn.setreg('"', save_reg, save_regtype)

return selected_text
`);

neovimStatus.visualSelection = String(visualText || '');
} catch (e) {
// Fallback method using getpos and getline
try {
const start = await nvim.eval(`getpos("'<")`) as [number, number, number, number];
const end = await nvim.eval(`getpos("'>")`) as [number, number, number, number];

if (start[1] === end[1]) {
// Single line selection
const line = await nvim.eval(`getline(${start[1]})`) as string;
const startCol = start[2] - 1; // Convert to 0-based
const endCol = end[2]; // Keep 1-based for substring end
neovimStatus.visualSelection = line.substring(startCol, endCol);
} else {
// Multi-line selection
const lines = await nvim.eval(`getline(${start[1]}, ${end[1]})`) as string[];
if (lines && lines.length > 0) {
const result = [];
const startCol = start[2] - 1;
const endCol = end[2];

// First line: from start column to end
result.push(lines[0].substring(startCol));

// Middle lines: complete lines
for (let i = 1; i < lines.length - 1; i++) {
result.push(lines[i]);
}

// Last line: from beginning to end column
if (lines.length > 1) {
result.push(lines[lines.length - 1].substring(0, endCol));
}

neovimStatus.visualSelection = result.join('\n');
}
}
} catch (e2) {
neovimStatus.visualSelection = '';
}
}
}

return neovimStatus;
Expand Down