aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoxie <moxie@3kgcat.fi>2026-03-15 09:54:31 +0200
committermoxie <moxie@3kgcat.fi>2026-03-15 10:05:20 +0200
commitde2df63dcbe44f07dc994e29d7d400c49c811080 (patch)
tree10ae2c8eee90c661792e71aa5bd35cd26f5407d4
parent49c1e9d1fc3d6bf8748756a8543d8c1b7287940f (diff)
refactor: simplify codebase and fix FileType autocommands
-rw-r--r--README.md159
-rw-r--r--doc/muwiki.txt606
-rw-r--r--doc/tags21
-rw-r--r--lua/muwiki/config.lua58
-rw-r--r--lua/muwiki/external.lua30
-rw-r--r--lua/muwiki/health.lua33
-rw-r--r--lua/muwiki/init.lua45
-rw-r--r--lua/muwiki/links.lua146
-rw-r--r--lua/muwiki/paths.lua43
-rw-r--r--lua/muwiki/utils.lua73
10 files changed, 390 insertions, 824 deletions
diff --git a/README.md b/README.md
index c43671a..dca5385 100644
--- a/README.md
+++ b/README.md
@@ -11,14 +11,24 @@ A lightweight wiki plugin for Neovim using standard markdown syntax.
- Standard markdown links `[text](url)`
- Multiple wiki directories
-- External link handlers
-- Link navigation with custom keymaps
+- Link navigation and xdg-open for web/file links
+- Extensible via autocmds (mkdir, templates, custom handlers)
+
+See `:help muwiki-autocmd-recipes` for implementation examples.
See `:help muwiki` for complete documentation and configuration options.
## Installation
-Using [lazy.nvim](https://github.com/folke/lazy.nvim):
+### Using vim.pack (Neovim 0.12+)
+
+```lua
+vim.pack.add({
+ { src = "https://git.3kgcat.fi/muwiki.nvim" }
+})
+```
+
+### Using lazy.nvim
```lua
{
@@ -27,62 +37,86 @@ Using [lazy.nvim](https://github.com/folke/lazy.nvim):
{ "<leader>ww", function() require("muwiki").open_index("default") end, desc = "Open wiki index" },
},
opts = {
- -- Wiki directories (REQUIRED - configure at least one)
dirs = {{ name = 'default', path = '~/wiki' }},
},
- -- autogroup only loads keymaps in wiki files
config = function(_, opts)
local muwiki = require('muwiki')
muwiki.setup(opts)
local group = vim.api.nvim_create_augroup("muwiki", { clear = true })
- -- Setup wiki keymaps for markdown files
vim.api.nvim_create_autocmd("FileType", {
group = group,
pattern = "markdown",
- callback = function(args)
- if not muwiki.wiki_root(args.buf) then return end
-
- local keymap_opts = { buffer = args.buf, silent = true, nowait = true }
+ callback = function(ev)
+ if not muwiki.wiki_root(ev.buf) then return end
+ local keymap_opts = { buffer = ev.buf, silent = true, nowait = true }
vim.keymap.set('n', '<CR>', muwiki.open_link, keymap_opts)
vim.keymap.set('n', '<Tab>', muwiki.next_link, keymap_opts)
vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, keymap_opts)
vim.keymap.set('v', '<CR>', muwiki.create_link, keymap_opts)
- vim.keymap.set('n', '<leader>oo', muwiki.open_link_with, keymap_opts)
end,
})
+ end,
- -- Auto-create parent directories when saving files in wiki
- vim.api.nvim_create_autocmd("BufWritePre", {
- group = group,
- callback = function(args)
- local wiki_root = muwiki.wiki_root(args.buf)
- if not wiki_root then return end
+}
+```
- local file = args.file
- if not vim.startswith(file, wiki_root) then return end
+## Quick Start
- local dir = vim.fn.fnamemodify(file, ":h")
- if vim.fn.isdirectory(dir) == 0 then
- vim.fn.mkdir(dir, "p")
- end
- end,
- })
+Add this to your config for basic wiki functionality:
+
+```lua
+local muwiki = require('muwiki')
+
+-- Configure wiki directories
+muwiki.setup({
+ dirs = {{ name = 'default', path = '~/wiki' }},
+})
+
+-- Quick access to wiki index
+vim.keymap.set('n', '<leader>ww', function()
+ muwiki.open_index("default")
+end, { desc = "Open wiki index" })
+
+-- Keymaps for wiki navigation (only active in wiki files)
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "markdown",
+ callback = function(args)
+ if not muwiki.wiki_root(args.buf) then return end
+
+ local opts = { buffer = args.buf, silent = true }
+ vim.keymap.set('n', '<CR>', muwiki.open_link, opts)
+ vim.keymap.set('n', '<Tab>', muwiki.next_link, opts)
+ vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, opts)
+ vim.keymap.set('v', '<CR>', muwiki.create_link, opts)
end,
-}
+})
+
+-- Auto-create directories when saving new files
+vim.api.nvim_create_autocmd("BufWritePre", {
+ pattern = "*.md",
+ callback = function(args)
+ local wiki_root = muwiki.wiki_root(args.buf)
+ if not wiki_root then return end
+ local dir = vim.fn.fnamemodify(args.file, ":h")
+ if vim.fn.isdirectory(dir) == 0 then
+ vim.fn.mkdir(dir, "p")
+ end
+ end,
+})
```
-**Available actions:**
+## Available Actions
-- `open_link()` - Open link under cursor
+- `open_link()` - Open link under cursor (wiki→nvim, web/files→xdg-open)
- `next_link()` - Jump to next markdown link
- `prev_link()` - Jump to previous markdown link
- `create_link()` - Create link from visual selection
-- `open_link_with()` - Open link with custom external handler
-
-See `:help muwiki-commands` for complete API documentation.
+- `get_link()` - Get link info at cursor
+- `open_with_menu(handlers)` - Open link with selectable handler
+- `open_index(name)` - Open wiki index file
## Link Format
@@ -93,69 +127,6 @@ See `:help muwiki-commands` for complete API documentation.
[Absolute path](file:///tmp/image.png)
```
-## External Handlers
-
-Define custom handlers for opening external URLs and files:
-
-```lua
-external_handlers = {
- -- Open HTTP/HTTPS URLs in Firefox
- {
- name = 'Firefox',
- cmd = 'firefox',
- pattern = '^https?://',
- },
- -- Open videos with mpv (local files and YouTube)
- {
- name = 'mpv',
- cmd = 'mpv',
- pattern = {
- '%.mp4$',
- '%.mkv$',
- '%.avi$',
- '%.webm$',
- 'youtube%.com',
- 'youtu%.be',
- },
- },
- -- Open images with swayimg
- {
- name = 'swayimg',
- cmd = 'swayimg',
- pattern = {
- '%.png$',
- '%.jpe?g$',
- '%.gif$',
- '%.webp$',
- '%.bmp$',
- },
- },
- -- Copy URL to clipboard using wl-copy
- {
- name = 'Copy URL',
- cmd = function(url)
- vim.system({ 'wl-copy', url }, { detach = true })
- vim.notify('URL copied to clipboard', vim.log.levels.INFO)
- end,
- pattern = '.*',
- },
- -- Fallback for any URL (Linux)
- {
- name = 'xdg-open',
- cmd = 'xdg-open',
- pattern = '.*',
- },
-}
-```
-
-**Handler properties:**
-
-- `name` - Display name in the handler menu
-- `cmd` - Command string or Lua function
-- `pattern` - Lua pattern(s) to match URLs (string or table of strings; optional, matches all if omitted)
-
-**Note:** Files with extensions in `text_extensions` will always open in Neovim, bypassing external handlers.
-
## Recommended Plugins
These plugins work well with muwiki.nvim:
diff --git a/doc/muwiki.txt b/doc/muwiki.txt
index 878095d..c22be0f 100644
--- a/doc/muwiki.txt
+++ b/doc/muwiki.txt
@@ -1,453 +1,233 @@
-*muwiki.txt* A lightweight wiki plugin for Neovim
+*muwiki.nvim* A lightweight wiki plugin for Neovim
-Author: muwiki.nvim contributors
-License: TBD
-
-CONTENTS *muwiki-contents*
-
-1. Introduction ........................... |muwiki-introduction|
-2. Installation ........................... |muwiki-installation|
-3. Configuration .......................... |muwiki-configuration|
-4. Commands and API ....................... |muwiki-commands|
-5. Keymaps ................................ |muwiki-keymaps|
-6. Link Format ............................ |muwiki-link-format|
-7. External Handlers ...................... |muwiki-external-handlers|
+==============================================================================
+CONTENTS *muwiki-contents*
+
+1. Introduction ............................................ |muwiki-intro|
+2. Requirements ............................................ |muwiki-requirements|
+3. Installation ............................................ |muwiki-installation|
+4. Configuration ........................................... |muwiki-configuration|
+5. Commands & Functions .................................... |muwiki-commands|
+6. Lua API ................................................. |muwiki-api|
+7. Autocommands & Recipes .................................. |muwiki-autocmd|
+ 7.1 Auto-create directories ............................. |muwiki-autocmd-mkdir|
+ 7.2 Add template for new files .......................... |muwiki-autocmd-template|
+ 7.3 Open with menu examples ............................. |muwiki-autocmd-open-with|
+8. Health Check ............................................ |muwiki-health|
==============================================================================
-1. INTRODUCTION *muwiki-introduction*
+1. INTRODUCTION *muwiki-intro*
-muwiki.nvim is a lightweight wiki plugin for Neovim that uses standard
-markdown syntax for creating and navigating wiki-style documentation.
+MuWiki is a lightweight wiki plugin for Neovim using standard markdown syntax.
+It provides:
-Features:~
- Standard markdown links `[text](url)`
-- Multiple wiki directories support
-- External link handlers for custom URL/file opening
-- Link navigation with custom keymaps
+- Multiple wiki directories
+- Link navigation and xdg-open for web/file links
+- Extensible via autocmds (mkdir, templates, custom handlers)
+
+==============================================================================
+2. REQUIREMENTS *muwiki-requirements*
-Requirements:~
- Neovim v0.10+
- Treesitter markdown parsers (`:TSInstall markdown markdown_inline`)
==============================================================================
-2. INSTALLATION *muwiki-installation*
+3. INSTALLATION *muwiki-installation*
-Using lazy.nvim:~
->lua
- {
- url = "https://git.3kgcat.fi/muwiki.nvim",
- keys = {
- { "<leader>ww", function() require("muwiki").open_index("default") end, desc = "Open wiki index" },
- },
- opts = {
- -- Wiki directories (REQUIRED - configure at least one)
- dirs = {{ name = 'default', path = '~/wiki' }},
- },
-
- -- autogroup only loads keymaps in wiki files
- config = function(_, opts)
- local muwiki = require('muwiki')
- muwiki.setup(opts)
-
- local group = vim.api.nvim_create_augroup("muwiki", { clear = true })
-
- -- Setup wiki keymaps for markdown files
- vim.api.nvim_create_autocmd("FileType", {
- group = group,
- pattern = "markdown",
- callback = function(args)
- if not muwiki.wiki_root(args.buf) then return end
-
- local keymap_opts = { buffer = args.buf, silent = true, nowait = true }
- vim.keymap.set('n', '<CR>', muwiki.open_link, keymap_opts)
- vim.keymap.set('n', '<Tab>', muwiki.next_link, keymap_opts)
- vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, keymap_opts)
- vim.keymap.set('v', '<CR>', muwiki.create_link, keymap_opts)
- end,
- })
-
- -- Auto-create parent directories when saving files in wiki
- vim.api.nvim_create_autocmd("BufWritePre", {
- group = group,
- callback = function(args)
- local wiki_root = muwiki.wiki_root(args.buf)
- if not wiki_root then return end
-
- local file = args.file
- if not vim.startswith(file, wiki_root) then return end
-
- local dir = vim.fn.fnamemodify(file, ":h")
- if vim.fn.isdirectory(dir) == 0 then
- vim.fn.mkdir(dir, "p")
- end
- end,
- })
- end,
- }
+Using vim.pack (Neovim 0.12+)~
+>
+ vim.pack.add({
+ { src = "https://git.3kgcat.fi/muwiki.nvim" }
+ })
<
-==============================================================================
-3. CONFIGURATION *muwiki-configuration*
-
-Configuration Options:~
-
- dirs List of wiki directories (required)
- Each entry has `name` and `path` fields
-
- index_file Name of the wiki index file
- Default: 'index.md'
-
- use_external_handlers Enable external URL/file handlers
- Default: false
-
- external_handlers List of external handler definitions
- See |muwiki-external-handlers|
- text_extensions Extensions to open in Neovim, bypassing
- external handlers. Only used when
- use_external_handlers is enabled.
- Even binary files like PNG can be added
- to force editor opening.
- Default: { 'md', 'txt' }
-
-Example:~
->lua
- {
- url = "https://git.3kgcat.fi/muwiki.nvim/",
- keys = {
- { "<leader>ww", function() require("muwiki").open_index("default") end, desc = "Open wiki index" },
- { "<leader>we", function() require("muwiki").open_index("test") end, desc = "Open test wiki index" },
- },
- opts = {
- dirs = {
- { name = 'default', path = '~/wiki' },
- { name = 'test', path = '~/wiki_test' },
- },
- index_file = 'index.md',
- text_extensions = { 'md', 'txt' },
- use_external_handlers = true,
- external_handlers = {
- {
- name = 'mpv',
- cmd = 'mpv',
- pattern = {
- '%.mp4$',
- '%.mkv$',
- '%.avi$',
- '%.webm$',
- 'youtube%.com',
- 'youtu%.be',
- },
- },
- {
- name = 'Copy URL',
- cmd = function(url)
- vim.system({ 'wl-copy', url }, { detach = true })
- vim.notify('URL copied to clipboard', vim.log.levels.INFO)
- end,
- pattern = '.*',
- },
- {
- name = 'xdg-open',
- cmd = 'xdg-open',
- pattern = '.*',
- },
- },
- },
-
- config = function(_, opts)
- local muwiki = require('muwiki')
- muwiki.setup(opts)
-
- local group = vim.api.nvim_create_augroup("muwiki", { clear = true })
-
- -- Setup wiki keymaps for markdown files
- vim.api.nvim_create_autocmd("FileType", {
- group = group,
- pattern = "markdown",
- callback = function(args)
- if not muwiki.wiki_root(args.buf) then return end
-
- local keymap_opts = { buffer = args.buf, silent = true, nowait = true }
- vim.keymap.set('n', '<CR>', muwiki.open_link, keymap_opts)
- vim.keymap.set('n', '<Tab>', muwiki.next_link, keymap_opts)
- vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, keymap_opts)
- vim.keymap.set('v', '<CR>', muwiki.create_link, keymap_opts)
- vim.keymap.set('n', '<leader>oo', muwiki.open_link_with, keymap_opts)
- end,
- })
-
- -- Auto-create parent directories when saving files in wiki
- vim.api.nvim_create_autocmd("BufWritePre", {
- group = group,
- callback = function(args)
- local wiki_root = muwiki.wiki_root(args.buf)
- if not wiki_root then return end
-
- local file = args.file
- if not vim.startswith(file, wiki_root) then return end
-
- local dir = vim.fn.fnamemodify(file, ":h")
- if vim.fn.isdirectory(dir) == 0 then
- vim.fn.mkdir(dir, "p")
- end
- end,
- })
- end,
- }
-<
-==============================================================================
-4. COMMANDS AND API *muwiki-commands*
-
-All functions are accessed through the main module: `require("muwiki")`
-
- *muwiki.open_index()*
->lua
- require("muwiki").open_index(name)
+Using lazy.nvim~
+>
+ {
+ url = "https://git.3kgcat.fi/muwiki.nvim",
+ keys = {
+ { "<leader>ww", function() require("muwiki").open_index("default") end, desc = "Open wiki index" },
+ },
+ opts = {
+ dirs = {{ name = 'default', path = '~/wiki' }},
+ },
+ }
<
-Open the index file of a wiki.
-
-Parameters:~
- {name} Wiki directory name as configured in `dirs`
-
-Example:~
->lua
- -- Open the default wiki index
- require("muwiki").open_index("default")
- -- Can be bound to a key
- vim.keymap.set('n', '<leader>ww', function()
- require("muwiki").open_index("default")
- end)
+==============================================================================
+4. CONFIGURATION *muwiki-configuration*
+
+The plugin is configured using the |muwiki.setup()| function.
+
+Configuration Options~
+
+ *muwiki-opt-dirs*
+dirs List of wiki directories.
+ Each entry is a table with:
+ - name: Wiki name for reference
+ - path: Absolute path to wiki directory
+ Default: nil
+
+ *muwiki-opt-index_file*
+index_file Name of the index file for each wiki.
+ Default: 'index.md'
+
+Example Configuration~
+>
+ local muwiki = require('muwiki')
+
+ muwiki.setup({
+ dirs = {
+ { name = 'default', path = '~/wiki' },
+ { name = 'work', path = '~/work/wiki' },
+ },
+ index_file = 'index.md',
+ })
<
- *muwiki.open_link()*
->lua
- require("muwiki").open_link()
-<
-Open the link under the cursor. Automatically detects link type:
-- Wiki links: Opens in Neovim (creates new page if doesn't exist)
-- Web links: Opens with xdg-open
-- File links: Opens with xdg-open or in Neovim (for text_extensions)
-
-Note: This function uses xdg-open directly and ignores custom
-external_handlers configuration. File existence is checked for file://
-links and an error is shown if the file is not found.
-Use |muwiki.open_link_with()| to utilize custom handlers.
-
- *muwiki.next_link()*
->lua
- require("muwiki").next_link()
+==============================================================================
+5. COMMANDS & FUNCTIONS *muwiki-commands*
+
+Keymaps should be configured by the user. Here are recommended keymaps:
+>
+ -- Quick access to wiki index
+ vim.keymap.set('n', '<leader>ww', function()
+ muwiki.open_index("default")
+ end, { desc = "Open wiki index" })
+
+ -- Keymaps for wiki navigation (only active in wiki files)
+ vim.api.nvim_create_autocmd("FileType", {
+ pattern = "markdown",
+ callback = function(args)
+ if not muwiki.wiki_root(args.buf) then return end
+
+ local opts = { buffer = args.buf, silent = true }
+ vim.keymap.set('n', '<CR>', muwiki.open_link, opts)
+ vim.keymap.set('n', '<Tab>', muwiki.next_link, opts)
+ vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, opts)
+ vim.keymap.set('v', '<CR>', muwiki.create_link, opts)
+ end,
+ })
<
-Jump to the next markdown link in the current buffer.
- *muwiki.prev_link()*
->lua
- require("muwiki").prev_link()
+Link Format~
+>
+ [Wiki page](page.md)
+ [Website](https://example.com)
+ [Relative path](file://../document.pdf)
+ [Absolute path](file:///tmp/image.png)
<
-Jump to the previous markdown link in the current buffer.
- *muwiki.open_link_with()*
->lua
- require("muwiki").open_link_with()
-<
-Open the link under cursor with a custom external handler.
-Shows a menu if multiple handlers match the URL.
-Uses custom external_handlers configuration.
-Only available for web and file links.
-
- *muwiki.create_link()*
->lua
- require("muwiki").create_link()
+==============================================================================
+6. LUA API *muwiki-api*
+
+muwiki.setup({opts}) *muwiki.setup()*
+ Configure the plugin. See |muwiki-configuration| for options.
+
+muwiki.open_link() *muwiki.open_link()*
+ Open the link under cursor.
+ - Wiki links: open in Neovim buffer
+ - Web links: open with xdg-open
+ - File links: open with xdg-open
+
+muwiki.next_link() *muwiki.next_link()*
+ Jump to the next markdown link in the buffer.
+
+muwiki.prev_link() *muwiki.prev_link()*
+ Jump to the previous markdown link in the buffer.
+
+muwiki.create_link() *muwiki.create_link()*
+ Create a link from visual selection. In visual mode, select text and
+call this function to convert it to a markdown link. The text will be
+normalized to create a filename (lowercase, spaces to underscores).
+
+muwiki.get_link() *muwiki.get_link()*
+ Get information about the link under cursor.
+ Returns a table with:
+ - text: Link display text
+ - target: Link destination
+ - type: Link type ('web', 'file', or 'wiki')
+
+muwiki.open_with_menu({handlers}, {link}) *muwiki.open_with_menu()*
+ Open link with a selectable handler from a menu.
+ {handlers} is a list of handler tables:
+>
+ {
+ name = "Handler Name",
+ cmd = "command" or function(url),
+ exts = {"pdf", "doc"} -- optional file extensions
+ }
<
-Create a markdown link from the visually selected text.
-Transforms the selected text into a link format `[text](normalized_text.md)`
-and opens the target file in a new buffer.
+ {link} is optional; if not provided, gets link under cursor.
-Usage:
-- Select text in visual mode (v or V)
-- Call this function
-- Selected text is replaced with a markdown link
-- Target file is opened in a new buffer (unsaved)
+muwiki.open_index({name}) *muwiki.open_index()*
+ Open the index file of a wiki by name.
+ {name} is the wiki name from configuration.
-Example:
-- Select: `My New Page`
-- Result: `[My New Page](my_new_page.md)`
-
-Note: Multi-line selections are not supported.
+muwiki.wiki_root({bufnr}) *muwiki.wiki_root()*
+ Get the wiki root directory for a buffer.
+ Returns the path or nil if buffer is not in a wiki.
==============================================================================
-5. KEYMAPS *muwiki-keymaps*
-
-muwiki.nvim provides actions that automatically check if the current buffer
-is within a configured wiki directory before executing.
-
-Keymap Setup:~
-Configure keymaps using an autocmd to ensure they only apply within wiki
-directories: >lua
- local muwiki = require('muwiki')
-
- local group = vim.api.nvim_create_augroup("muwiki", { clear = true })
-
- -- Setup wiki keymaps for markdown files
- vim.api.nvim_create_autocmd("FileType", {
- group = group,
- pattern = "markdown",
- callback = function(args)
- if not muwiki.wiki_root(args.buf) then return end
-
- local opts = { buffer = args.buf, silent = true, nowait = true }
- vim.keymap.set('n', '<CR>', muwiki.open_link, opts)
- vim.keymap.set('n', '<Tab>', muwiki.next_link, opts)
- vim.keymap.set('n', '<S-Tab>', muwiki.prev_link, opts)
- vim.keymap.set('v', '<CR>', muwiki.create_link, opts)
- vim.keymap.set('n', '<leader>oo', muwiki.open_link_with, opts)
- end,
- })
-
- -- Auto-create parent directories when saving files in wiki
- vim.api.nvim_create_autocmd("BufWritePre", {
- group = group,
- callback = function(args)
- local wiki_root = muwiki.wiki_root(args.buf)
- if not wiki_root then return end
-
- local file = args.file
- if not vim.startswith(file, wiki_root) then return end
-
- local dir = vim.fn.fnamemodify(file, ":h")
- if vim.fn.isdirectory(dir) == 0 then
- vim.fn.mkdir(dir, "p")
- end
- end,
- })
+7. AUTOCOMMANDS & RECIPES *muwiki-autocmd*
+
+Auto-create directories when saving~ *muwiki-autocmd-mkdir*
+>
+ vim.api.nvim_create_autocmd("BufWritePre", {
+ pattern = "*.md",
+ callback = function(args)
+ local wiki_root = muwiki.wiki_root(args.buf)
+ if not wiki_root then return end
+ local dir = vim.fn.fnamemodify(args.file, ":h")
+ if vim.fn.isdirectory(dir) == 0 then
+ vim.fn.mkdir(dir, "p")
+ end
+ end,
+ })
<
-Note: Actions check if the buffer is within a configured wiki directory and
-notify the user if not.
-
- *muwiki*
-Actions:~
-All user-facing actions are available directly through `require('muwiki')`:
-
- open_link() Open link under cursor
- next_link() Jump to next markdown link
- prev_link() Jump to previous markdown link
- open_link_with() Open link with custom external handler
- create_link() Create link from visual selection
-These functions check if the buffer is a wiki buffer before executing.
-
-Available API Functions:~
+Add template for new files~ *muwiki-autocmd-template*
+>
+ vim.api.nvim_create_autocmd("BufNewFile", {
+ pattern = "*.md",
+ callback = function(args)
+ local wiki_root = muwiki.wiki_root(args.buf)
+ if not wiki_root then return end
+
+ local filename = vim.fn.fnamemodify(args.file, ":t:r")
+ local template = string.format("# %%s\n\n", filename)
+ vim.api.nvim_buf_set_lines(args.buf, 0, 0, false, vim.split(template, "\n"))
+ end,
+ })
+<
- open_index(name) Open wiki index file
+Open with menu example~ *muwiki-autocmd-open-with*
+>
+ vim.keymap.set('n', '<leader>o', function()
+ muwiki.open_with_menu({
+ { name = "Zathura", cmd = "zathura", exts = {"pdf"} },
+ { name = "swayimg", cmd = "swayimg", exts = {"png", "jpg", "jpeg", "gif", "webp"} },
+ { name = "mpv", cmd = "mpv", exts = {"mp4", "mkv", "avi", "mov", "webm"} },
+ { name = "xdg-open", cmd = "xdg-open" },
+ })
+ end, { buffer = true, desc = "Open with..." })
+<
==============================================================================
-6. LINK FORMAT *muwiki-link-format*
+8. HEALTH CHECK *muwiki-health*
-muwiki.nvim uses standard markdown link syntax.
+Run |:checkhealth muwiki| to verify:
+- Wiki directories are configured and exist
+- Treesitter markdown parser is installed
->markdown
- [Wiki page](page.md)
- [Website](https://example.com)
- [Relative path](file://../document.pdf)
- [Absolute path](file:///tmp/image.png)
-<
==============================================================================
-7. EXTERNAL HANDLERS *muwiki-external-handlers*
-
-Define custom handlers for opening external URLs and files.
+RECOMMENDED PLUGINS
-Note: Custom handlers are only used by |muwiki.open_link_with()|.
-The default <CR> mapping uses |muwiki.open_link()| which uses xdg-open
-directly, ignoring custom handlers.
-
-Enable external handlers:~
->lua
- require("muwiki").setup({
- use_external_handlers = true,
- })
-<
-Handler Definition:~
-Each handler is a table with:
-
- name Display name in the handler menu (string)
- cmd Command string or Lua function (string|function)
- pattern Lua pattern(s) to match URLs (optional string or table of
- strings; if omitted, matches all URLs)
-
-Handler Examples:~
->lua
- external_handlers = {
- -- Open HTTP/HTTPS URLs in Firefox
- {
- name = 'Firefox',
- cmd = 'firefox',
- pattern = '^https?://',
- },
- -- Open videos with mpv using multiple patterns (table)
- {
- name = 'mpv',
- cmd = 'mpv',
- pattern = {
- '%.mp4$',
- '%.mkv$',
- '%.avi$',
- '%.webm$',
- 'youtube%.com',
- 'youtu%.be',
- },
- },
- -- Open images with swayimg using multiple patterns
- {
- name = 'swayimg',
- cmd = 'swayimg',
- pattern = {
- '%.png$',
- '%.jpe?g$',
- '%.gif$',
- '%.webp$',
- '%.bmp$',
- },
- },
- -- Copy URL to clipboard using Lua function
- {
- name = 'Copy URL',
- cmd = function(url)
- vim.system({ 'wl-copy', url }, { detach = true })
- vim.notify('URL copied to clipboard', vim.log.levels.INFO)
- end,
- pattern = '.*',
- },
- -- Fallback for any URL
- {
- name = 'xdg-open',
- cmd = 'xdg-open',
- pattern = '.*',
- },
- }
-<
-Interaction with text_extensions:~
-The |muwiki-configuration| option `text_extensions` overrides external
-handlers. Files with extensions listed in `text_extensions` will always
-open in Neovim, even when `use_external_handlers` is enabled. This is
-useful for forcing certain file types into the editor (e.g., adding
-'png' to edit images as text/hex).
-
-Example: Open PNG files in Neovim (as text/hex) instead of external viewer:~
->lua
- text_extensions = { 'md', 'txt', 'png' }
-<
-==============================================================================
-==============================================================================
-HEALTH CHECKING *muwiki-health*
+These plugins work well with muwiki.nvim:
-Run health check with:
->:checkhealth muwiki
-<
-The health check verifies:
-- Wiki directories are configured
-- Wiki directories exist and are accessible
-- Treesitter markdown parser is installed (required for link detection)
-- External handler commands are available (if configured)
+- render-markdown.nvim - Improve markdown rendering in Neovim
+- outline.nvim - Navigate document structure with symbols outline
==============================================================================
vim:tw=78:ts=8:ft=help:norl:
diff --git a/doc/tags b/doc/tags
index 300649b..6b9bc55 100644
--- a/doc/tags
+++ b/doc/tags
@@ -1,17 +1,24 @@
+muwiki-api muwiki.txt /*muwiki-api*
+muwiki-autocmd muwiki.txt /*muwiki-autocmd*
+muwiki-autocmd-mkdir muwiki.txt /*muwiki-autocmd-mkdir*
+muwiki-autocmd-open-with muwiki.txt /*muwiki-autocmd-open-with*
+muwiki-autocmd-template muwiki.txt /*muwiki-autocmd-template*
muwiki-commands muwiki.txt /*muwiki-commands*
muwiki-configuration muwiki.txt /*muwiki-configuration*
muwiki-contents muwiki.txt /*muwiki-contents*
-muwiki-external-handlers muwiki.txt /*muwiki-external-handlers*
muwiki-health muwiki.txt /*muwiki-health*
muwiki-installation muwiki.txt /*muwiki-installation*
-muwiki-introduction muwiki.txt /*muwiki-introduction*
-muwiki-keymaps muwiki.txt /*muwiki-keymaps*
-muwiki-link-format muwiki.txt /*muwiki-link-format*
+muwiki-intro muwiki.txt /*muwiki-intro*
+muwiki-opt-dirs muwiki.txt /*muwiki-opt-dirs*
+muwiki-opt-index_file muwiki.txt /*muwiki-opt-index_file*
+muwiki-requirements muwiki.txt /*muwiki-requirements*
+muwiki.create_link() muwiki.txt /*muwiki.create_link()*
+muwiki.get_link() muwiki.txt /*muwiki.get_link()*
muwiki.next_link() muwiki.txt /*muwiki.next_link()*
+muwiki.nvim muwiki.txt /*muwiki.nvim*
muwiki.open_index() muwiki.txt /*muwiki.open_index()*
muwiki.open_link() muwiki.txt /*muwiki.open_link()*
-muwiki.open_link_with() muwiki.txt /*muwiki.open_link_with()*
+muwiki.open_with_menu() muwiki.txt /*muwiki.open_with_menu()*
muwiki.prev_link() muwiki.txt /*muwiki.prev_link()*
muwiki.setup() muwiki.txt /*muwiki.setup()*
-muwiki.create_link() muwiki.txt /*muwiki.create_link()*
-muwiki.txt muwiki.txt /*muwiki.txt*
+muwiki.wiki_root() muwiki.txt /*muwiki.wiki_root()*
diff --git a/lua/muwiki/config.lua b/lua/muwiki/config.lua
index 1e04d4a..9d668b9 100644
--- a/lua/muwiki/config.lua
+++ b/lua/muwiki/config.lua
@@ -3,22 +3,8 @@ local M = {}
M.options = {
dirs = nil,
index_file = 'index.md',
- text_extensions = { 'md', 'txt' },
- use_external_handlers = false,
- external_handlers = {
- {
- name = 'xdg-open',
- cmd = 'xdg-open',
- pattern = '.*',
- },
- },
}
-local function dir_exists(path)
- local stat = vim.uv.fs_stat(path)
- return stat and stat.type == 'directory'
-end
-
function M.setup(opts)
opts = opts or {}
@@ -42,51 +28,11 @@ function M.setup(opts)
end
for _, dir in ipairs(M.options.dirs or {}) do
- if not dir_exists(dir.path) then
+ local stat = vim.uv.fs_stat(dir.path)
+ if not (stat and stat.type == 'directory') then
vim.notify('Wiki directory not found: ' .. dir.path, vim.log.levels.WARN)
end
end
end
--- Lookup wiki path by name (e.g., "default" -> "~/wiki/")
-function M.wiki_path(name)
- if not M.options.dirs or #M.options.dirs == 0 then
- vim.notify('MuWiki: No dirs configured. See :help muwiki-configuration', vim.log.levels.ERROR)
- return nil
- end
-
- if name then
- for _, dir in ipairs(M.options.dirs) do
- if dir.name == name then
- return dir.path
- end
- end
- vim.notify(string.format('Wiki "%s" not found, using default', name), vim.log.levels.WARN)
- end
-
- return M.options.dirs[1].path
-end
-
--- Find which wiki contains this buffer's file (cached per-buffer)
-function M.wiki_root(bufnr)
- bufnr = bufnr or 0
-
- if vim.b[bufnr].muwiki_root ~= nil then
- return vim.b[bufnr].muwiki_root or nil
- end
-
- local filepath = vim.api.nvim_buf_get_name(bufnr)
- local normalized = vim.fs.normalize(filepath)
-
- for _, dir in ipairs(M.options.dirs or {}) do
- if vim.startswith(normalized, dir.path) then
- vim.b[bufnr].muwiki_root = dir.path
- return dir.path
- end
- end
-
- vim.b[bufnr].muwiki_root = false
- return nil
-end
-
return M
diff --git a/lua/muwiki/external.lua b/lua/muwiki/external.lua
deleted file mode 100644
index 9ea83b5..0000000
--- a/lua/muwiki/external.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local config = require('muwiki.config')
-local paths = require('muwiki.paths')
-
-local M = {}
-
-function M.open(url)
- if type(url) ~= 'string' then
- vim.notify('Invalid URL type', vim.log.levels.ERROR)
- return false
- end
-
- local valid, err = paths.validate_url_scheme(url)
- if not valid then
- vim.notify(err, vim.log.levels.ERROR)
- return false
- end
-
- vim.system({ 'xdg-open', url }, { detach = true })
- return true
-end
-
-function M.execute(handler, url)
- if type(handler.cmd) == 'function' then
- handler.cmd(url)
- else
- vim.system({ handler.cmd, url }, { detach = true })
- end
-end
-
-return M
diff --git a/lua/muwiki/health.lua b/lua/muwiki/health.lua
index 22c9747..13c5523 100644
--- a/lua/muwiki/health.lua
+++ b/lua/muwiki/health.lua
@@ -1,15 +1,5 @@
-
-local utils = require('muwiki.utils')
-
local M = {}
-local function command_exists(cmd)
- if type(cmd) == 'function' then
- return true
- end
- return vim.fn.executable(cmd) == 1
-end
-
M.check = function()
vim.health.start('Wiki Setup')
@@ -26,7 +16,8 @@ M.check = function()
vim.health.info('Add to your config: dirs = {{name = "default", path = "~/wiki"}}')
else
for _, dir in ipairs(cfg.dirs) do
- if utils.dir_exists(dir.path) then
+ local stat = vim.uv.fs_stat(dir.path)
+ if stat and stat.type == 'directory' then
vim.health.ok(string.format("Wiki '%s': %s", dir.name, dir.path))
else
vim.health.warn(string.format("Wiki '%s': %s (not found)", dir.name, dir.path))
@@ -34,26 +25,6 @@ M.check = function()
end
end
- vim.health.start('External Handlers')
-
- if cfg.use_external_handlers then
- vim.health.ok('External handlers are enabled')
-
- for _, handler in ipairs(cfg.external_handlers) do
- if type(handler.cmd) == 'function' then
- vim.health.ok(string.format("Handler '%s': <Lua function>", handler.name))
- elseif command_exists(handler.cmd) then
- vim.health.ok(string.format("Handler '%s': %s", handler.name, handler.cmd))
- else
- vim.health.error(
- string.format("Handler '%s': command not found (%s)", handler.name, handler.cmd)
- )
- end
- end
- else
- vim.health.info('External handlers are disabled')
- end
-
vim.health.start('Treesitter (Required)')
local has_ts_parser = pcall(vim.treesitter.get_parser, 0, 'markdown')
diff --git a/lua/muwiki/init.lua b/lua/muwiki/init.lua
index a6997a7..f9d8f23 100644
--- a/lua/muwiki/init.lua
+++ b/lua/muwiki/init.lua
@@ -1,37 +1,16 @@
local M = {}
-local config = require('muwiki.config')
-
-M.setup = function(opts)
- config.setup(opts)
-end
-
-function M.open_link()
- require('muwiki.links').open_link()
-end
-
-function M.next_link()
- require('muwiki.links').next_link()
-end
-
-function M.prev_link()
- require('muwiki.links').prev_link()
-end
-
-function M.open_link_with()
- require('muwiki.links').open_link_with()
-end
-
-function M.create_link()
- require('muwiki.links').create_link()
-end
-
-function M.open_index(name)
- require('muwiki.utils').open_index(name)
-end
-
-function M.wiki_root(bufnr)
- return config.wiki_root(bufnr)
-end
+function M.setup(opts)
+ require('muwiki.config').setup(opts)
+end
+
+M.open_link = function() require('muwiki.links').open_link() end
+M.next_link = function() require('muwiki.links').jump_link('next') end
+M.prev_link = function() require('muwiki.links').jump_link('prev') end
+M.create_link = function() require('muwiki.links').create_link() end
+M.get_link = function() return require('muwiki.links').get_link() end
+M.open_with_menu = function(handlers, link) require('muwiki.links').open_with_menu(handlers, link) end
+M.open_index = function(name) require('muwiki.utils').open_index(name) end
+M.wiki_root = function(bufnr) return require('muwiki.utils').wiki_root(bufnr) end
return M
diff --git a/lua/muwiki/links.lua b/lua/muwiki/links.lua
index 55ce692..1a681fa 100644
--- a/lua/muwiki/links.lua
+++ b/lua/muwiki/links.lua
@@ -1,7 +1,4 @@
-local config = require('muwiki.config')
-local paths = require('muwiki.paths')
local utils = require('muwiki.utils')
-local external = require('muwiki.external')
local M = {}
@@ -15,6 +12,12 @@ local function get_link_type(target)
return 'wiki'
end
+local function resolve_file_url(url)
+ local path = url:gsub('^file://', '')
+ local resolved = utils.resolve(path, nil)
+ return 'file://' .. resolved
+end
+
function M.get_link()
local cursor = vim.api.nvim_win_get_cursor(0)
@@ -60,20 +63,8 @@ function M.get_link()
}
end
-local function resolve_file_url(url)
- local path = paths.strip_file_protocol(url)
- local path_type = paths.get_path_type(path)
-
- if path_type == 'relative' then
- local current_dir = vim.fs.dirname(vim.api.nvim_buf_get_name(0))
- path = vim.fs.joinpath(current_dir, path)
- end
-
- return 'file://' .. vim.fs.normalize(path)
-end
-
local function get_wiki_root_or_notify()
- local wiki_root = config.wiki_root(0)
+ local wiki_root = utils.wiki_root(0)
if not wiki_root then
vim.notify('Not in a wiki buffer', vim.log.levels.ERROR)
return nil
@@ -88,10 +79,9 @@ function M.open_link()
return
end
+ -- Web links - open with xdg-open
if link.type == 'web' then
- if config.options.use_external_handlers then
- external.open(link.target)
- end
+ vim.system({'xdg-open', link.target}, {detach = true})
return
end
@@ -100,107 +90,89 @@ function M.open_link()
return
end
+ -- File links - use xdg-open
if link.type == 'file' then
if vim.startswith(link.target, 'file://') then
- if config.options.use_external_handlers then
- local absolute_url = resolve_file_url(link.target)
- external.open(absolute_url)
- end
+ -- file:// URLs - open with xdg-open
+ vim.system({'xdg-open', resolve_file_url(link.target)}, {detach = true})
else
+ -- Local file paths - open with xdg-open
local ok, file_path = pcall(utils.resolve, link.target, wiki_root)
if not ok then
vim.notify(string.format('Cannot resolve path: %s', link.target), vim.log.levels.ERROR)
return
end
- if not utils.file_exists(file_path) then
- vim.notify(string.format('File not found: %s', file_path), vim.log.levels.ERROR)
- return
- end
-
- local ext = file_path:match('%.([^%.]+)$') or ''
- if not utils.is_text_file(ext) then
- if config.options.use_external_handlers then
- external.open(file_path)
- end
- return
- end
-
- utils.open_in_buffer(file_path)
+ vim.system({'xdg-open', file_path}, {detach = true})
end
return
end
- -- wiki link
+ -- Wiki links - open in buffer
local file_path = utils.resolve(link.target, wiki_root)
- utils.open_wiki_file(file_path)
+ utils.open_in_buffer(file_path)
end
-function M.open_link_with()
- local link = M.get_link()
+function M.open_with_menu(handlers, link)
+ -- Get link automatically if not provided
+ link = link or M.get_link()
if not link then
- vim.notify('No link found under cursor', vim.log.levels.WARN)
- return
- end
-
- if link.type == 'wiki' then
- vim.notify('Menu not available for wiki links', vim.log.levels.WARN)
+ vim.notify('No link under cursor', vim.log.levels.WARN)
return
end
- local url = link.target
- if link.type == 'file' then
- local wiki_root = get_wiki_root_or_notify()
- if not wiki_root then
- return
- end
- url = utils.resolve(url, wiki_root)
- end
-
- -- Find matching handlers for URL
- local function handler_matches(handler, url)
- local pattern = handler.pattern
- if pattern == nil then
- return true
- end
- if type(pattern) == 'string' then
- return url:match(pattern) ~= nil
- end
- for _, p in ipairs(pattern) do
- if url:match(p) then
- return true
- end
- end
- return false
- end
+ -- Get file extension
+ local ext = link.target:match('%.([^%.]+)$')
+ ext = ext and ext:lower() or nil
+ -- Filter handlers by extension
local matching_handlers = {}
- for _, handler in ipairs(config.options.external_handlers) do
- if handler_matches(handler, url) then
+ for _, handler in ipairs(handlers) do
+ -- Check if handler matches this extension
+ if handler.exts then
+ for _, handler_ext in ipairs(handler.exts) do
+ if handler_ext:lower() == ext then
+ table.insert(matching_handlers, handler)
+ break
+ end
+ end
+ else
+ -- No exts specified - matches all
table.insert(matching_handlers, handler)
end
end
if #matching_handlers == 0 then
- vim.notify('No handlers available for this URL', vim.log.levels.WARN)
+ vim.notify('No handlers for file type: ' .. (ext or 'unknown'), vim.log.levels.WARN)
return
end
- if #matching_handlers == 1 then
- external.execute(matching_handlers[1], url)
- return
+ -- Resolve the path
+ local url = link.target
+ if link.type == 'file' and not vim.startswith(url, 'file://') then
+ local wiki_root = get_wiki_root_or_notify()
+ if wiki_root then
+ url = utils.resolve(url, wiki_root)
+ end
end
+ -- Build menu items
local handler_names = {}
for _, handler in ipairs(matching_handlers) do
table.insert(handler_names, handler.name)
end
+ -- Show menu with link text as title
vim.ui.select(handler_names, {
- prompt = 'Open with:',
+ prompt = 'Open "' .. link.text .. '" with:',
}, function(choice, idx)
if choice and idx then
- external.execute(matching_handlers[idx], url)
+ local handler = matching_handlers[idx]
+ if type(handler.cmd) == 'function' then
+ handler.cmd(url)
+ else
+ vim.system({ handler.cmd, url }, { detach = true })
+ end
end
end)
end
@@ -254,17 +226,17 @@ function M.create_link()
vim.api.nvim_buf_set_text(0, start_row, start_col, end_row, end_col, { link_text })
vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes('<Esc>', true, false, true), 'n', false)
- local wiki_root = config.wiki_root(0)
+ local wiki_root = utils.wiki_root(0)
if not wiki_root then
vim.notify('Not in a wiki buffer', vim.log.levels.ERROR)
return
end
local target_path = utils.resolve(link_target, wiki_root)
- utils.open_wiki_file(target_path)
+ utils.open_in_buffer(target_path)
end
-local function jump_link(direction)
+function M.jump_link(direction)
local flags = direction == 'next' and 'w' or 'bw'
local msg = direction == 'next' and 'No more links' or 'No previous links'
@@ -273,12 +245,4 @@ local function jump_link(direction)
end
end
-function M.next_link()
- jump_link('next')
-end
-
-function M.prev_link()
- jump_link('prev')
-end
-
return M
diff --git a/lua/muwiki/paths.lua b/lua/muwiki/paths.lua
deleted file mode 100644
index c2f8fdb..0000000
--- a/lua/muwiki/paths.lua
+++ /dev/null
@@ -1,43 +0,0 @@
-local M = {}
-
-function M.get_path_type(path)
- if path:sub(1, 1) == '/' then
- return 'absolute'
- elseif path:sub(1, 1) == '~' then
- return 'home'
- else
- return 'relative'
- end
-end
-
-function M.resolve(filepath, current_file)
- local path_type = M.get_path_type(filepath)
-
- if path_type == 'relative' then
- local base = current_file and vim.fs.dirname(current_file)
- or vim.fs.dirname(vim.api.nvim_buf_get_name(0))
- filepath = vim.fs.joinpath(base, filepath)
- end
-
- return vim.fs.normalize(filepath)
-end
-
-function M.strip_file_protocol(url)
- return url:gsub('^file://', '')
-end
-
-local ALLOWED_SCHEMES = {
- http = true,
- https = true,
- file = true,
-}
-
-function M.validate_url_scheme(url)
- local scheme = url:match('^([a-zA-Z]+)://')
- if scheme and not ALLOWED_SCHEMES[scheme:lower()] then
- return false, string.format('URL scheme not allowed: %s', scheme)
- end
- return true, nil
-end
-
-return M
diff --git a/lua/muwiki/utils.lua b/lua/muwiki/utils.lua
index af22c8e..bc6094e 100644
--- a/lua/muwiki/utils.lua
+++ b/lua/muwiki/utils.lua
@@ -1,25 +1,54 @@
local config = require('muwiki.config')
-local paths = require('muwiki.paths')
local M = {}
-function M.file_exists(path)
- local stat = vim.uv.fs_stat(path)
- return stat and stat.type == 'file'
+function M.open_in_buffer(filepath)
+ vim.cmd.edit(vim.fn.fnameescape(filepath))
end
-function M.dir_exists(path)
- local stat = vim.uv.fs_stat(path)
- return stat and stat.type == 'directory'
+-- Lookup wiki path by name (e.g., "default" -> "~/wiki/")
+function M.wiki_path(name)
+ if not config.options.dirs or #config.options.dirs == 0 then
+ vim.notify('MuWiki: No dirs configured. See :help muwiki-configuration', vim.log.levels.ERROR)
+ return nil
+ end
+
+ if name then
+ for _, dir in ipairs(config.options.dirs) do
+ if dir.name == name then
+ return dir.path
+ end
+ end
+ vim.notify(string.format('Wiki "%s" not found, using default', name), vim.log.levels.WARN)
+ end
+
+ return config.options.dirs[1].path
end
-function M.open_in_buffer(filepath)
- local bufnr = vim.fn.bufnr(filepath, true)
- vim.api.nvim_win_set_buf(0, bufnr)
+-- Find which wiki contains this buffer's file (cached per-buffer)
+function M.wiki_root(bufnr)
+ bufnr = bufnr or 0
+
+ if vim.b[bufnr].muwiki_root ~= nil then
+ return vim.b[bufnr].muwiki_root or nil
+ end
+
+ local filepath = vim.api.nvim_buf_get_name(bufnr)
+ local normalized = vim.fs.normalize(filepath)
+
+ for _, dir in ipairs(config.options.dirs or {}) do
+ if vim.startswith(normalized, dir.path) then
+ vim.b[bufnr].muwiki_root = dir.path
+ return dir.path
+ end
+ end
+
+ vim.b[bufnr].muwiki_root = false
+ return nil
end
function M.open_index(name)
- local wiki_path = config.wiki_path(name)
+ local wiki_path = M.wiki_path(name)
if not wiki_path then
return
end
@@ -29,18 +58,14 @@ function M.open_index(name)
end
function M.resolve(filepath, wiki_root)
- local path = paths.strip_file_protocol(filepath)
- return paths.resolve(path, wiki_root)
-end
-
-function M.is_text_file(ext)
- local ext_lower = ext:lower()
- for _, text_ext in ipairs(config.options.text_extensions) do
- if ext_lower == text_ext then
- return true
- end
+ local path = filepath:gsub('^file://', '')
+ local path_type = path:sub(1, 1)
+ if path_type ~= '/' and path_type ~= '~' then
+ local base = wiki_root and vim.fs.dirname(wiki_root)
+ or vim.fs.dirname(vim.api.nvim_buf_get_name(0))
+ path = vim.fs.joinpath(base, path)
end
- return false
+ return vim.fs.normalize(path)
end
function M.normalize_filename(text)
@@ -50,8 +75,4 @@ function M.normalize_filename(text)
return normalized
end
-function M.open_wiki_file(filepath)
- M.open_in_buffer(filepath)
-end
-
return M