Added
vim.obsidian.keymap— global (non-editor) keymaps from Lua — define key bindings for non-editor contexts (graph view, canvas, PDF viewer, file explorer) using a Neovim-style APIvim.obsidian.keymap.set(lhs, rhs, opts?)— create a global keymap with:obcommand <id>or:<ex-command>as RHSvim.obsidian.keymap.del(lhs)— remove a global keymapdescoption auto-creates a label in the global which-key popup- Lua global keymaps override vimrc
gmapon conflict (last-write-wins) - Survives settings changes and feature reloads via
luaGlobalMapspersistence arrays - Plugin:
src/lua/api.ts(LuaGlobalKeymaptype,onGlobalKeymap/onGlobalKeymapDelcallbacks)
vim.obsidian.whichkey— which-key labels from Lua — set group and command labels for the which-key popupvim.obsidian.whichkey.set_group(key, label, opts?)— name a which-key group by prefixvim.obsidian.whichkey.set_label(key, label, opts?)— label an individual which-key bindingcontextoption defaults to"editor"; use{ context = "global" }for non-editor which-key overlay- Previously only available via vimrc
whichkeygroup/whichkeylabeland Settings UI - Plugin:
src/lua/api.ts(onWhichKeyGroupLabel/onWhichKeyCommandLabelcallbacks)
vim.opt.guicursor— cursor shapes from Lua — set per-mode cursor shapes withoutvim.cmdpassthroughvim.opt.guicursor = "n:block,i:bar,v:block,r:underline,o:underline"— mode codes:n,i,v,r,o,a(all); shapes:block,bar,underline,hollow- Write-only (reading returns nil); invalid strings log a warning
- Previously only available via vimrc
set guicursor=...
- Lua standard library utilities (
vim.tbl_*,vim.split,vim.inspect,vim.json) — 22 Neovim-compatible utility functions for table manipulation, string operations, debugging, and JSON serialization- Table utilities (12):
vim.tbl_deep_extend,vim.tbl_extend,vim.tbl_contains(with predicate support),vim.tbl_keys,vim.tbl_values,vim.tbl_map,vim.tbl_filter,vim.tbl_count,vim.tbl_isempty,vim.tbl_get,vim.list_extend,vim.deepcopy - String utilities (6):
vim.split(with{plain, trimempty}options),vim.trim,vim.startswith,vim.endswith,vim.pesc,vim.stricmp vim.inspect(value)— human-readable table/value serialization for debugging init.lua configsvim.json.encode(value)/vim.json.decode(str)— JSON serialization bridged to JavaScript'sJSON.stringify/JSON.parse- Plugin:
src/lua/stdlib.ts
- Table utilities (12):
- Async primitives (
vim.schedule,vim.defer_fn,vim.uvtimers) — Neovim-compatible async APIs for deferred execution and timer managementvim.schedule(fn)— defer function to next event loop iteration (useful for breaking recursive autocmd loops)vim.schedule_wrap(fn)— returns a function that wrapsfnwithvim.schedule, passing all argumentsvim.defer_fn(fn, timeout)— defer function bytimeoutmilliseconds, returns cancellable handle withstop()/close()/is_closing()vim.uv.new_timer()— create timer withstart(delay, repeat, callback),stop(),close(),is_closing(),is_active()vim.uv.hrtime()— high-resolution time in nanosecondsvim.uv.now()— current time in millisecondsvim.loopalias forvim.uv(Neovim backward compatibility)- All timers cleaned up on plugin unload (no leaked timeouts)
- Plugin:
src/lua/timers.ts
- Buffer-local keymaps (
vim.keymap.set({ buffer = 0 })) — keymaps scoped to specific files, automatically swapped on editor/tab switchvim.keymap.set("n", "gd", handler, { buffer = 0 })— keymap active only in the current filevim.api.nvim_buf_set_keymap(0, mode, lhs, rhs, opts)/nvim_buf_del_keymap(0, mode, lhs)— low-level buffer keymap APIs- Combined with
BufEnterautocmd for per-filetype keymaps (e.g., markdown-only bindings) - Buffer identity uses vault-relative file path; only
buffer = 0(current file) is supported - Plugin:
src/lua/buffer.ts(BufferKeymapManager)
- Buffer content APIs (
nvim_buf_get_lines,nvim_buf_set_lines) — read and modify editor content from Lua callbacksvim.api.nvim_buf_get_lines(0, start, end, strict_indexing)— 0-based, end-exclusive,-1for EOFvim.api.nvim_buf_set_lines(0, start, end, strict_indexing, replacement)— empty table deletes linesvim.api.nvim_get_current_buf()— returns0(current buffer)vim.api.nvim_buf_get_name(0)— vault-relative file pathvim.api.nvim_buf_line_count(0)— total line countstrict_indexing = trueerrors on out-of-bounds;falseclamps silently
- 4 new autocmd events —
CursorMoved,CursorHold,BufWritePre,BufWritePost(total: 12 events)CursorMoved— fires after cursor moves (throttled viavim-command-doneevent)CursorHold— fires after cursor is idle forupdatetimems (default 4000, configurable viavim.opt.updatetime)BufWritePre/BufWritePost— fire before/after:w,:wq,:x,:wall,:updatewith vault-relative glob pattern supportupdatetimeoption added toKNOWN_SET_OPTIONSfor vimrc andvim.optconfiguration
vim.obsidiannamespace (vim.obalias) — Obsidian-specific APIs that don't exist in Neovimvim.obsidian.vault_name(),vim.obsidian.app_version(),vim.obsidian.plugin_version()vim.obsidian.run_command(id)— execute any Obsidian command by IDvim.obsidian.list_commands()— table of{id, name}for all available commandsvim.obsidian.open_file(path)— open a vault filevim.obsidian.current_file()— table{path, name, extension, basename}or nilvim.obsidian.vault_path()— vault absolute path (desktop only)
- Sandboxed
vim.env— environment variable proxy with curated values and user-defined storagevim.env.HOME(vault path),vim.env.VIM("motions"),vim.env.TERM("obsidian"),vim.env.OBSIDIAN_VERSION- Custom variables:
vim.env.MY_VAR = "value"— stored in memory, not inprocess.env - Unknown keys return nil
vim.api.nvim_set_hl— highlight group → CSS bridge — customize plugin styling from Lua using Neovim's highlight APIvim.api.nvim_set_hl(0, "EasyMotionTarget", { fg = "#ff5555", bold = true })— change EasyMotion label colorsvim.api.nvim_set_hl(0, "StatusLineNormal", { bg = "#282a36" })— change status bar mode colors- 13 plugin-defined highlight groups:
EasyMotionTarget,EasyMotionShade,HintTarget,StatusLineNormal/Insert/Visual/Replace/VLine/VBlock/Command/Search/Select/VReplace - User-defined groups generate
.vim-hl-GroupNameCSS classes - Supports:
fg,bg,sp,bold,italic,underline,undercurl,strikethrough,reverse,blend,link(group inheritance),default(don't override),update(merge) vim.api.nvim_get_hl(0, { name = "group" })— query highlight attrsvim.api.nvim_create_namespace(name)— returns0(only global namespace supported)- Plugin:
src/lua/highlight.ts(HighlightManager)
- Enhanced
vim.notifywith log levels —vim.notify(msg, level)routes messages by severityvim.log.levels:TRACE(0),DEBUG(1),INFO(2),WARN(3),ERROR(4),OFF(5)ERROR/WARN→ Obsidian Notice + console;INFO→ Notice;DEBUG/TRACE→ console.debug onlyvim.notify_once(msg, level)— deduplicates by message content
Fixed
- Space-leader global keymaps not matching keyboard input —
replaceLeaderKeyconverted<leader>to raw" "(space character), butnormalizeKeyEventinGlobalKeyHandlerconverted spacebar to"<Space>". The key sequences never matched inGlobalMappingRegistry.resolve(). Fixed by addingnormalizeKeyString()to convert raw special characters to angle-bracket notation (" "→"<Space>") before storing keys in the registry. Affects both vimrcgmapand Luavim.obsidian.keymap.setwith space leader.- Plugin:
src/workspace/global-mapping-registry.ts(normalizeKeyString),src/main.ts(applyGlobalMaps,rebuildGlobalWhichKey)
- Plugin:
vim.g.mode_prompt_*reads returned nil after write — the__newindexhandler for mode_prompt keys calledonSettingOverridebut did not store the value in theglobalsMap. The__indexhandler's fallback toglobals.get(key)returnedundefined. Fixed by also storing inglobalson write.- Plugin:
src/lua/api.ts(vim.g__newindexhandler)
- Plugin:
Changed
vim.apiexpanded from 6 to 16 functions —nvim_set_hl,nvim_get_hl,nvim_create_namespace,nvim_buf_get_lines,nvim_buf_set_lines,nvim_get_current_buf,nvim_buf_get_name,nvim_buf_line_count,nvim_buf_set_keymap,nvim_buf_del_keymapadded alongside existing autocmd/augroup/user command functions- Autocmd events expanded from 8 to 12 — added
CursorMoved,CursorHold,BufWritePre,BufWritePost vim.obsidiannamespace expanded — addedkeymapandwhichkeysub-namespaces for global keymaps and which-key labels.vim.obalias includes the new sub-namespaces.
Documentation
docs/configuration/lua-config.md: comprehensive Lua API reference expansion — added vim.opt table with defaults and valid ranges, keymapping mode reference, autocmd event data reference (per-eventev.datafields), highlight group CSS variable mapping, Lua sandbox reference (available/unavailable libraries, instruction limits),vim.fn.has()completeness statement, mode prompt customization section, global keymaps section (vim.obsidian.keymap), which-key labels section (vim.obsidian.whichkey),vim.opt.guicursoroption; fixedbufferoption row (was "Not supported", now correctly documentsbuffer = 0/true); fixedos/debuglibrary availability claims (not loaded by plugin); addedvim.stricmp,vim.env.MYVIMRC,underdouble/underdotted/underdashedhighlight attributes, TextYankPostregnamefield, highlight group case-sensitivity callout, buffer-local keymap accumulation warning, underline style limitation calloutdocs/configuration/which-key.md: added Lua examples for group labels (vim.obsidian.whichkey.set_group) and global which-key labelsdocs/configuration/cursor-shapes.md: addedvim.opt.guicursorLua section, removed "not supported" workaround notedocs/configuration/status-bar.md: expanded Lua mode prompt examples to all 11 modesdocs/features/ex-commands.md: added Lua example for custom commands vianvim_create_user_commandKNOWN_LIMITATIONS.md: updated supported APIs list (addedvim.obsidian.keymap,vim.obsidian.whichkey,vim.opt.guicursor), correctedos/debuglibrary availability (not loaded by plugin sandbox)AGENTS.md: clarified fengari fork vs plugin library loading distinction (fork keepsos/debug, plugin does not load them)README.md: updated tagline and Lua configuration feature bullet with expanded API surface
Full Changelog: 0.35.0...0.36.0