nvim + pi / bi-directional communication
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214import { BinaryReader } from "./binary";import type { ExtmarkRecord, OptionalField, Pos, UndoEntry, UndoFile, UndoHeader, VisualInfo,} from "./types";
const MAGIC = Buffer.from([ 0x56, 0x69, 0x6d, 0x9f, 0x55, 0x6e, 0x44, 0x6f, 0xe5,]);const VERSION = 3;const UF_HEADER_MAGIC = 0x5fd0;const UF_HEADER_END_MAGIC = 0xe7aa;const UF_ENTRY_MAGIC = 0xf518;const UF_ENTRY_END_MAGIC = 0x3581;const NMARKS = 26;const EXTMARK_STRUCT_SIZE = 48;
function readOptionalFields(reader: BinaryReader): OptionalField[] { const fields: OptionalField[] = [];
while (true) { const length = reader.readUInt8(); if (length === 0) return fields;
const what = reader.readUInt8(); fields.push({ what, data: reader.readBuffer(length) }); }}
function readPos(reader: BinaryReader): Pos { return { lnum: reader.readInt32BE(), col: reader.readInt32BE(), coladd: reader.readInt32BE(), };}
function readVisual(reader: BinaryReader): VisualInfo { return { start: readPos(reader), end: readPos(reader), mode: reader.readInt32BE(), curswant: reader.readInt32BE(), };}
function readEntry(reader: BinaryReader): UndoEntry { const top = reader.readInt32BE(); const bot = reader.readInt32BE(); const lcount = reader.readInt32BE(); const size = reader.readInt32BE();
if (size < 0) { throw new Error(`Invalid undo entry size: ${size}`); }
const lines: Buffer[] = []; for (let i = 0; i < size; i++) { const lineLength = reader.readInt32BE(); if (lineLength < 0) { throw new Error(`Invalid undo line length: ${lineLength}`); } lines.push(reader.readBuffer(lineLength)); }
return { top, bot, lcount, lines };}
function readEntries(reader: BinaryReader): UndoEntry[] { const entries: UndoEntry[] = [];
while (true) { const marker = reader.readUInt16BE(); if (marker === UF_ENTRY_END_MAGIC) return entries; if (marker !== UF_ENTRY_MAGIC) { throw new Error(`Invalid undo entry marker: 0x${marker.toString(16)}`); } entries.push(readEntry(reader)); }}
function readExtmarks(reader: BinaryReader): ExtmarkRecord[] { const extmarks: ExtmarkRecord[] = [];
while (true) { const marker = reader.readUInt16BE(); if (marker === UF_ENTRY_END_MAGIC) return extmarks; if (marker !== UF_ENTRY_MAGIC) { throw new Error(`Invalid extmark marker: 0x${marker.toString(16)}`); }
const type = reader.readInt32BE(); if (type !== 0 && type !== 1) { throw new Error(`Unsupported extmark undo object type: ${type}`); } extmarks.push({ type, data: reader.readBuffer(EXTMARK_STRUCT_SIZE) }); }}
function readHeader(reader: BinaryReader): UndoHeader { const nextSeq = reader.readInt32BE(); const prevSeq = reader.readInt32BE(); const altNextSeq = reader.readInt32BE(); const altPrevSeq = reader.readInt32BE(); const seq = reader.readInt32BE(); const cursor = readPos(reader); const cursorVcol = reader.readInt32BE(); const flags = reader.readUInt16BE(); const namedMarks = Array.from({ length: NMARKS }, () => readPos(reader)); const visual = readVisual(reader); const time = reader.readBigUInt64BE(); const optionalFields = readOptionalFields(reader); const entries = readEntries(reader); const extmarks = readExtmarks(reader);
return { nextSeq, prevSeq, altNextSeq, altPrevSeq, seq, cursor, cursorVcol, flags, namedMarks, visual, time, optionalFields, entries, extmarks, };}
export function parseUndofile(buffer: Buffer): UndoFile { const reader = new BinaryReader(buffer); const magic = reader.readBuffer(MAGIC.length); if (!magic.equals(MAGIC)) { throw new Error("Not a Neovim undofile."); }
const version = reader.readUInt16BE(); if (version !== VERSION) { throw new Error(`Unsupported Neovim undofile version: ${version}`); }
const hash = reader.readBuffer(32); const lineCount = reader.readInt32BE(); const uLineLength = reader.readInt32BE(); if (uLineLength < 0) { throw new Error(`Invalid U-line length: ${uLineLength}`); } const uLine = reader.readBuffer(uLineLength); const uLineLnum = reader.readInt32BE(); const uLineColnr = reader.readInt32BE(); const oldHeadSeq = reader.readInt32BE(); const newHeadSeq = reader.readInt32BE(); const curHeadSeq = reader.readInt32BE(); const numHead = reader.readInt32BE(); const seqLast = reader.readInt32BE(); const seqCur = reader.readInt32BE(); const timeCur = reader.readBigUInt64BE(); const optionalFields = readOptionalFields(reader);
const headers: UndoHeader[] = []; while (true) { const marker = reader.readUInt16BE(); if (marker === UF_HEADER_END_MAGIC) break; if (marker !== UF_HEADER_MAGIC) { throw new Error(`Invalid undo header marker: 0x${marker.toString(16)}`); } headers.push(readHeader(reader)); }
if (!reader.eof) { throw new Error("Trailing data after undofile end marker."); } if (headers.length !== numHead) { throw new Error( `Header count mismatch: expected ${numHead}, read ${headers.length}.`, ); }
return { hash, lineCount, uLine, uLineLnum, uLineColnr, oldHeadSeq, newHeadSeq, curHeadSeq, seqLast, seqCur, timeCur, optionalFields, headers, };}
export const UND_FILE_CONSTANTS = { MAGIC, VERSION, UF_HEADER_MAGIC, UF_HEADER_END_MAGIC, UF_ENTRY_MAGIC, UF_ENTRY_END_MAGIC, NMARKS,} as const;