Added
- Lua configuration support (
.obsidian.init.lua) — optional Neovim-style Lua configuration using a sandboxed Fengari Lua 5.3 runtime. Provides conditional logic, function-based keymaps, and familiarvim.keymap.set/vim.optsyntax. Disabled by default — enable in Settings → Vim Motions → Vimrc & key bindings → Enable Lua configuration. (#46)vim.opt.<name> = value/vim.o.<name>— set any plugin option (backed by the sameKNOWN_SET_OPTIONSmap as vimrcsetcommands)vim.g.mapleader/vim.g.<name>— set leader key and user variablesvim.keymap.set(mode, lhs, rhs, opts)— key mappings with string or function RHS,descfor which-key labels,noremap/remapcontrol, multi-mode supportvim.keymap.del(mode, lhs)— remove mappingsvim.cmd(string)— execute ex commands (deferred until first editor focus)vim.vault_name()— returns the current vault name for per-vault conditional configvim.notify(msg)— show an Obsidian notification from Luaprint(...)— outputs to developer console- Sandbox: 6 defense layers — selective library loading (no
io/os/debug/package), dangerous globals stripped (load/dofile/loadfile), nofengari-interop, instruction-count timeout vialua_sethook(1M instruction limit), custom environment table - Hybrid loading: settings and keymaps load immediately without an active editor;
vim.cmd()calls are queued and executed on first editor focus - Override hierarchy: init.lua loads after vimrc — Lua values override vimrc on conflict
- Settings:
configModedropdown (Lua + Vimrc / Lua only / Vimrc only / Settings only),luaConfigPath(custom file path) - Bundle impact: +238KB minified / +79KB gzipped (Fengari runtime)
- Plugin:
src/lua/engine.ts(sandbox + timeout),src/lua/api.ts(vim.* bridge),src/lua/loader.ts(hybrid file loading),src/lua/types.ts(Fengari type declarations) - 12 Neovim golden comparison test cases (
lua-keymapssuite), 17 e2e integration tests, 4 known deviations registered
vim.fn.*Neovim function subset — 27 functions from Neovim'svim.fnnamespace, scoped for Obsidian's vault-centric environment- Config/detection (13):
has,expand,fnamemodify,exists,localtime,strftime,filereadable,isdirectory,glob,mode,line,col,getline - String manipulation (14):
tolower,toupper,trim,strlen,strwidth,stridx,strridx,strpart,substitute,nr2char,char2nr,split,join vim.fn.has(feature)— platform detection with 12 features:mac,linux,win32,unix,mobile,desktop,ios,android,obsidian,obsidian-X.Y,nvim(0),vim(0)vim.fn.expand('%')— vault-relative file path with modifiers (:t,:e,:r,:h,:p)vim.fn.fnamemodify(path, mods)— general-purpose path modifier with chainable modifiers (:t:r)vim.fn.filereadable(path)/vim.fn.isdirectory(path)— vault-scoped, path traversal blockedvim.fn.glob(pattern)— vault-scoped file matchingvim.fn.line('.')/vim.fn.col('.')/vim.fn.getline('.')— context-aware: return cursor position in function callbacks, return 0 at config-load timevim.fn.strftime(fmt)— full C89 strftime implementation (src/lua/strftime.ts)- Unsupported
vim.fn.*functions produce a helpful error listing available functions vim.fn.hostname()/vim.fn.getenv()intentionally skipped (system fingerprinting concern)- Plugin:
src/lua/fn.ts(VimFnCallbacks, function registry,__indexdispatch),src/lua/strftime.ts(pure strftime utility)
- Config/detection (13):
vim.api.nvim_create_user_command: define custom ex commands from Lua- String RHS:
vim.api.nvim_create_user_command("W", "w", {}): simple aliases - Function RHS:
vim.api.nvim_create_user_command("Today", function(opts) ... end, {}): Lua callback withopts.args vim.apichanged from error stub to partial namespace: unsupportedvim.api.*functions give a helpful error listingnvim_create_user_commandas available- Registered commands are immediately usable from the
:ex command line
- String RHS:
nvim_create_autocmd/nvim_create_augroup: Neovim-compatible autocommand system with 8 events- Events:
InsertEnter,InsertLeave,ModeChanged,BufEnter,BufLeave,FocusGained,FocusLost,TextYankPost - Augroups with
{ clear = true }for safe config reloads nvim_del_autocmd,nvim_del_augroup_by_name,nvim_clear_autocmdsfor management- ModeChanged supports
"old:new"pattern with*wildcard - BufEnter/BufLeave support vault-relative path glob patterns
- TextYankPost provides structured data: operator, regcontents, regtype, visual
- Non-nested guard prevents infinite autocmd loops
- Reentrancy protection: settings changes from callbacks defer reloadFeatures()
- Plugin:
src/lua/autocmd.ts(AutocmdManager class) - Fork:
vim-yanksignal added to yank/delete/change operators invim.js - 16 unit tests, 2 e2e tests
- Events:
- Unit test infrastructure — Vitest test runner for the Lua config modules
- 49 unit tests across 6 files (smoke, sandbox, timeout, api, fn, strftime)
- Runs in 250ms without Obsidian or browser
npm run test:unit/npm run test:unit:watchscripts- Obsidian module mocked via
test/unit/__mocks__/obsidian.ts - CI:
.github/workflows/lint.ymlnow runs unit tests on every push across all branches
Changed
- Consolidated configuration settings — replaced two independent toggles (
enableVimrc+enableLuaConfig) with a single Configuration mode dropdown (configMode):- Lua + Vimrc (default): both loaded, Lua overrides vimrc on conflict
- Lua only: only init.lua loaded
- Vimrc only: only .obsidian.vimrc loaded
- Settings only: neither config file loaded
- Notification logic consolidated: in Lua + Vimrc mode, only notifies when NEITHER file is found (no spam about missing vimrc when only using Lua, or vice versa)
- Automatic migration from old boolean settings on first load
- Custom path fields (init.lua path, vimrc path) remain independent and disable based on active mode
Documentation
docs/configuration/lua-config.md: full Lua configuration reference with supported APIs, allvim.optoptions,vim.fn.*function tables (has features, expand modifiers, fnamemodify modifiers, exists expressions), mapping examples, conditional config examples, loading order, unsupported API documentationdocs/configuration/settings.md: updated withconfigModedropdown replacing old toggles, added Lua column to all settings tablesdocs/configuration/index.md: reordered — Lua configuration presented as primary method, vimrc as alternativedocs/configuration/vimrc.md: added tip pointing to Lua configuration for advanced use casesdocs/configuration/which-key.md: added Luadescoption integration for which-key labelsdocs/configuration/cursor-shapes.md: addedvim.cmdworkaround note for guicursordocs/configuration/status-bar.md: added Lua equivalents for status bar settingsdocs/features/quality-of-life.md: added Lua examples alongside vimrcdocs/features/workspace-navigation.md: added Lua examples alongside vimrcdocs/getting-started/quickstart.md: reordered — Lua shown as recommended configuration pathdocs/reference/known-limitations.md: Lua configuration section with supported/unsupported APIs, hybrid loading, vim.fn subset, bundle sizeKNOWN_LIMITATIONS.md: Lua configuration section with full details
Full Changelog: 0.33.0...0.34.0