Added
vim.regex()— ECMAScript regular expressions in Lua —vim.regex(pattern, flags?)creates a regex object exposingmatch_str,match_line,match_pos,replace, andtestmethods. Uses JavaScript'sRegExpengine (not Vim regex syntax). Returns 0-based byte offsets matching Neovim'svim.regex()convention. Invalid patterns raise a Lua error catchable withpcall.- Plugin:
src/lua/regex.ts(new),src/lua/api.ts(registration viainjectRegex)
- Plugin:
- Fengari fork:
__gcmetamethods via FinalizationRegistry —__gcmetamethods on userdata are now invoked when the userdata becomes unreachable from JavaScript. Registration happens atlua_setmetatabletime (only when the metatable contains__gc). Finalizers are drained at three points: outermostluaD_pcallreturn,collectgarbage("collect"), andlua_close. Errors in__gcare silently swallowed (PUC-Rio semantics). Finalization order is unspecified. Tables with__gcare not finalized (userdata only). Environments withoutFinalizationRegistrygracefully degrade (no registration, no errors).- Fork:
~/Repos/fengari/src/lstate.js(finalizer infrastructure onglobal_State,drainFinalizers,lua_closedrain + unregister),~/Repos/fengari/src/lapi.js(lua_setmetatablesplitLUA_TUSERDATA/LUA_TTABLE, FR registration),~/Repos/fengari/src/ldo.js(drain point inluaD_pcall),~/Repos/fengari/src/lbaselib.js(collectgarbage("collect")drain integration)
- Fork:
- Fengari fork:
collectgarbage()no longer crashes — all 8collectgarbagemodes now return safe values instead of throwingluaL_error("lua_gc not implemented")."count"returns0, 0(no memory tracking)."collect"drains the__gcfinalizer queue."isrunning"returnsfalse. All other modes return0. Previously, any Lua code callingcollectgarbage()crashed the entire init sequence.- Fork:
~/Repos/fengari/src/lbaselib.js
- Fork:
- Native JS error propagation via
lua_atnativeerror— the plugin now installs alua_atnativeerrorhandler that converts native JS errors (TypeError, RangeError, etc.) to extractable Lua strings. Previously, native JS errors thrown inside fengari C functions were pushed aslightuserdataand lost —lua_tolstringreturnednull, producing generic "Unknown Lua error" messages. The handler extractsError.message(orString(e)for non-Error values) and pushes it as a Lua string. Covers all threads including coroutines (handler is onglobal_State).- Plugin:
src/lua/engine.ts(lua_atnativeerrorhandler increateSandboxedState),src/lua/types.d.ts(lua_touserdata,lua_atnativeerror,lua_pushintegertype declarations)
- Plugin:
Changed
- Fengari fork:
sprintf-jsreplaced with custom formatter — thesprintf-jsnpm dependency (sole runtime dependency) has been replaced with a purpose-builtluaSprintffunction in the fork'ssrc/lstrlib.js. The fork now ships with zero runtime dependencies. Output is byte-identical to the previous implementation for all standard format patterns.- Fork:
~/Repos/fengari/src/lstrlib.js,~/Repos/fengari/package.json,~/Repos/fengari/DIFFERENCES.md
- Fork:
- Fengari fork: integers widened from 32-bit to 53-bit —
math.maxintegeris now9007199254740991(2^53 - 1). Arithmetic operations use full 53-bitNumberprecision.string.packsize("j")returns 8 (was 4).tonumber("1099511627776")now returns the integer (wasnil). Bitwise operations remain 32-bit (JavaScript platform limitation). See~/Repos/fengari/DIFFERENCES.md§ "Integer widening" for the full change list and remaining limitations.- Fork:
~/Repos/fengari/src/luaconf.js,~/Repos/fengari/src/llimits.js,~/Repos/fengari/src/lvm.js,~/Repos/fengari/src/lobject.js,~/Repos/fengari/src/lstrlib.js,~/Repos/fengari/src/ltable.js,~/Repos/fengari/src/lapi.js,~/Repos/fengari/src/ldo.js,~/Repos/fengari/src/lmathlib.js,~/Repos/fengari/src/lbaselib.js
- Fork:
- Coroutine↔Promise bridge for async Lua execution — Lua callbacks (keymap functions, autocmd handlers, timer callbacks, user commands) can now call async APIs that yield the coroutine and resume when the Promise resolves. The bridge uses fengari's
lua_yieldkcontinuations with aCoroutineRunnermanaging thread lifecycle, instruction hooks, timeouts (10s), and concurrency limits (16 concurrent operations).pcallcorrectly catches async errors across yield/resume boundaries.- Plugin:
src/lua/coroutine-runner.ts(new:CoroutineRunner+AsyncRegistry),src/lua/engine.ts(evalLuaAsync,INSTRUCTION_LIMITexport),src/lua/types.d.ts(7 new fengari type declarations:lua_newthread,lua_resume,lua_yieldk,lua_status,lua_xmove,lua_isyieldable,LUA_YIELD)
- Plugin:
vim.ob.fs.read(path)andvim.ob.fs.readlines(path)— read vault files from Lua.readreturns a string,readlinesreturns a table of lines. Both yield internally via the coroutine bridge. Errors are catchable withpcall. Works in keymap callbacks, autocmd handlers, timer callbacks, and user commands. Also works at top level ininit.lua. Blocked in snippetf()/d()nodes (raises "async APIs cannot be called from snippet nodes").- Plugin:
src/lua/obsidian-api.ts(read/readlinesC-functions),src/lua/loader.ts(fsReadcallback viaadapter.read+readExternalFilefor absolute paths),src/lua/api.ts(fsReadonVimApiCallbacks)
- Plugin:
require()for multi-file Lua configs —require('mymodule')loadslua/mymodule.luafrom the vault root. Dot-separated names resolve to subdirectories (require('utils.strings')→lua/utils/strings.lua). Modules are cached inpackage.loaded. Circular requires detected via sentinel. Security: path traversal (..), absolute paths, and backslash paths are rejected.- Plugin:
src/lua/package.ts(new:packagetable, sandboxedload(), Lua-implementedrequire()),src/lua/engine.ts(loadkept in disabled list with re-enable note)
- Plugin:
load(chunk)re-enabled with sandboxing —load()compiles a string chunk and returns the compiled function (ornil+ error).dofileandloadfileremain disabled. The instruction count hook applies to loaded code.- Plugin:
src/lua/package.ts(injectSandboxedLoad)
- Plugin:
evalLuaAsyncfor async init.lua execution — top-levelinit.luacode can now call async APIs likevim.ob.fs.read. The init.lua chunk runs inside a coroutine viaevalLuaAsync, which compiles on the main state and delegates toinvokeAsyncCapable.autocmdManager.activate()fires only after all yields complete.- Plugin:
src/lua/engine.ts(evalLuaAsync),src/lua/loader.ts(evalLua→await evalLuaAsync)
- Plugin:
- Callback sites refactored for async capability — all 4 Lua callback invocation sites now use
CoroutineRunner.invokeAsyncCapablewhen a runner is available, with fallback to the originallua_pcallpath when not. Existing sync callbacks work identically.- Plugin:
src/lua/api.ts(keymap, user command, autocmd callbacks),src/lua/timers.ts(invokeLuaCallback+ 5 call sites)
- Plugin:
- Snippet async guard —
f()andd()snippet node evaluations are wrapped withrunner.setAsyncBlocked(true/false)to prevent async API calls during snippet expansion.- Plugin:
src/snippets/dynamic-bridge.ts(guards inrecomputeIfNeededandexpandDynamicSnippet)
- Plugin:
Tests
- 11 tests in
~/Repos/fengari/test/collectgarbage.test.js: all 8 modes return safe values, pcall succeeds, invalid mode errors - 7 tests in
~/Repos/fengari/test/atnativeerror.test.js: TypeError/RangeError extraction, string/number throws, pure Lua error unaffected, handler covers coroutine threads, without-handler baseline - 10 tests in
~/Repos/fengari/test/gc-finalizers.test.js: userdata__gcdrain, no-overhead without__gc, tables not registered, error swallowing, metatable nil/change unregister, recursive drain guard,lua_closedrain, post-close guard, no-FR graceful degradation - 8 tests in
~/Repos/fengari/test/53bit-integers.test.js: sprintf replacement (format specifiers, flags, hex float), 53-bit integer constants/boundaries, wide arithmetic, string parsing/formatting, table keying, pack/unpack with SZINT=8, 32-bit bitwise verification, wide for-loop - 9 unit tests in
test/unit/lua/regex.test.ts: constructor validation,match_str(offsets + nil),match_linealias,match_posfrom offset,replacewith captures + global flag,testboolean, flags (case-insensitive), invalid pattern error, missing pattern error - 8 spike tests in
~/Repos/fengari/test/coroutine-promise-bridge.test.js:lua_yieldkcontinuations,lua_isyieldable,pcallacross yield, instruction hooks, error propagation, sequential yields, Lua-level vs C-level coroutines - 11 unit tests in
test/unit/lua/coroutine-runner.test.ts: sync path, yield/resume, rejected Promise, pcall error catch, instruction limit, timeout, concurrency limit, destroyAll, sequential async, snippet guard, thread-targeted hooks - 7 unit tests in
test/unit/lua/eval-lua-async.test.ts: sync code, syntax errors, top-level async, sequential async, pcall at top level, side effects across yield, instruction limit - 6 unit tests in
test/unit/lua/fs-read.test.ts: file read, pcall error catch, empty file, readlines, sequential reads, snippet guard - 10 unit tests in
test/unit/lua/package-require.test.ts: module loading, caching, subdirectory resolution, circular require, missing module, path traversal, syntax error, runtime error, load() compilation, load() error - 22 e2e tests in
test/specs/lua-require.e2e.ts: functional behavior (7), error handling (4), sandbox security (11)
Documentation
CHANGELOG.md: Added fengari fork improvements (sprintf, 53-bit integers,__gc,collectgarbage,atnativeerror),vim.regex(), coroutine bridge, async Lua APIs, require(), load(), evalLuaAsync entriesKNOWN_LIMITATIONS.md: 32-bit integer limitation → Implemented (widened to 53-bit), hrtime overflow claim corrected; JS RegExp item 5 → Implemented; sprintf item 7 → Implemented (zero deps); vault file reading → Implemented; coroutine bridge item 1 → Implemented (Phases 1–3); require() item 2 → Implemented; load() item 6 → Implemented;__gcitem 4 → Implemented (userdata via FinalizationRegistry); error message quality item 8 → Implemented (atnativeerror handler);collectgarbageitem 10 → Implemented (safe no-ops); fengari improvement opportunities priority table updated (9/10 implemented, only weak tables remaining)README.md: Updated Lua configuration feature bullet withvim.regex(), async file reading, multi-file configs, and__gcuserdata finalizationCONTRIBUTING.md: Addedregex.ts,coroutine-runner.tsandpackage.tsto codebase structureAGENTS.md: Updated fengari fork section — sprintf-js removed (zero deps), 53-bit integers,vim.regex(), async bridge, require(), load(),__gcvia FinalizationRegistry,collectgarbagesafe no-ops, native error propagation viaatnativeerrordocs/configuration/lua-config.md: Addedvim.regex()API reference section,vim.ob.fs.read/readlinesto fs table,require()andload()sections,collectgarbagebehavior, updated unsupported APIs list~/Repos/fengari/DIFFERENCES.md: Updated behavioral differences table (collectgarbage,__gc), updated inherited limitations (collectgarbage and__gcaddressed)~/Repos/fengari/DIFFERENCES.md: Added "Integer widening" section, sprintf replacement documentation, updated behavioral differences table, updated files modified list
Full Changelog: 0.65.0...0.66.0