Skip to content

Instantly share code, notes, and snippets.

@aiya000
Created October 26, 2025 20:43
Show Gist options
  • Save aiya000/62c4ac1f06cea97353ba9fdeacfb4b38 to your computer and use it in GitHub Desktop.
Save aiya000/62c4ac1f06cea97353ba9fdeacfb4b38 to your computer and use it in GitHub Desktop.
#!/usr/bin/env -S deno run --allow-read --allow-write
/**
* fix-neovim-shada.ts
*
* ## What this script does
*
* Fixes corrupted Neovim shada files by removing problematic newline characters (0x0a, 0x0d).
* These newline characters can appear in command history or search history entries,
* causing plugins like cmdpalette.nvim to crash with errors like:
* "Error executing Lua callback: 'replacement string' item contains newlines"
*
* This script scans the shada file byte-by-byte and removes all newline (0x0a) and
* carriage return (0x0d) characters, while preserving all other data including
* command history, search history, marks, registers, and other shada contents.
*
* ## Usage
*
* 1. Close Neovim completely
* 2. Run this script:
*
* deno run --allow-read --allow-write ~/.dotfiles/bin/fix-neovim-shada.ts \
* ~/.local/state/nvim/shada/main.shada \
* ~/.local/state/nvim/shada/main.shada.fixed
*
* 3. Replace the original file:
*
* mv ~/.local/state/nvim/shada/main.shada.fixed ~/.local/state/nvim/shada/main.shada
*
* 4. Start Neovim again
*
* ## Example
*
* # Backup first (recommended)
* cp ~/.local/state/nvim/shada/main.shada ~/tmp/main.shada.backup
*
* # Fix the shada file
* deno run --allow-read --allow-write ~/.dotfiles/bin/fix-neovim-shada.ts \
* ~/.local/state/nvim/shada/main.shada \
* ~/.local/state/nvim/shada/main.shada.fixed
*
* # Replace if the fix looks good
* mv ~/.local/state/nvim/shada/main.shada.fixed ~/.local/state/nvim/shada/main.shada
*/
async function fixShada(inputPath: string, outputPath: string): Promise<void> {
console.log(`Reading shada file: ${inputPath}`);
const data = await Deno.readFile(inputPath);
console.log(`File size: ${data.length} bytes`);
// Fix approach: scan and remove problematic newlines byte-by-byte
const fixedData: number[] = [];
let fixedCount = 0;
for (let i = 0; i < data.length; i++) {
const byte = data[i];
// Skip newlines (0x0a) and carriage returns (0x0d)
if (byte === 0x0a || byte === 0x0d) {
console.log(` Removing byte 0x${byte.toString(16).padStart(2, '0')} at offset ${i} (${i.toString(16)})`);
fixedCount++;
continue;
}
fixedData.push(byte);
}
if (fixedCount > 0) {
console.log(`\nRemoved ${fixedCount} problematic bytes`);
const outputData = new Uint8Array(fixedData);
await Deno.writeFile(outputPath, outputData);
console.log(`Fixed shada written to: ${outputPath}`);
console.log(`New file size: ${outputData.length} bytes (was ${data.length})`);
} else {
console.log('\nNo problematic bytes found!');
await Deno.writeFile(outputPath, data);
}
}
// Main
if (import.meta.main) {
const args = Deno.args;
if (args.length < 2) {
console.error("Usage: deno run --allow-read --allow-write fix_shada.ts <input_shada> <output_shada>");
console.error("Example: deno run --allow-read --allow-write fix_shada.ts bad.shada fixed.shada");
Deno.exit(1);
}
const [inputPath, outputPath] = args;
try {
await fixShada(inputPath, outputPath);
} catch (error) {
console.error("Error:", error);
Deno.exit(1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment