From de2df63dcbe44f07dc994e29d7d400c49c811080 Mon Sep 17 00:00:00 2001 From: moxie Date: Sun, 15 Mar 2026 09:54:31 +0200 Subject: refactor: simplify codebase and fix FileType autocommands --- README.md | 159 ++++++------- doc/muwiki.txt | 606 +++++++++++++++--------------------------------- doc/tags | 21 +- lua/muwiki/config.lua | 58 +---- lua/muwiki/external.lua | 30 --- lua/muwiki/health.lua | 33 +-- lua/muwiki/init.lua | 45 +--- lua/muwiki/links.lua | 146 +++++------- lua/muwiki/paths.lua | 43 ---- lua/muwiki/utils.lua | 73 +++--- 10 files changed, 390 insertions(+), 824 deletions(-) delete mode 100644 lua/muwiki/external.lua delete mode 100644 lua/muwiki/paths.lua 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): { "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', '', muwiki.open_link, keymap_opts) vim.keymap.set('n', '', muwiki.next_link, keymap_opts) vim.keymap.set('n', '', muwiki.prev_link, keymap_opts) vim.keymap.set('v', '', muwiki.create_link, keymap_opts) - vim.keymap.set('n', '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', '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', '', muwiki.open_link, opts) + vim.keymap.set('n', '', muwiki.next_link, opts) + vim.keymap.set('n', '', muwiki.prev_link, opts) + vim.keymap.set('v', '', 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 = { - { "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', '', muwiki.open_link, keymap_opts) - vim.keymap.set('n', '', muwiki.next_link, keymap_opts) - vim.keymap.set('n', '', muwiki.prev_link, keymap_opts) - vim.keymap.set('v', '', 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 = { - { "ww", function() require("muwiki").open_index("default") end, desc = "Open wiki index" }, - { "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', '', muwiki.open_link, keymap_opts) - vim.keymap.set('n', '', muwiki.next_link, keymap_opts) - vim.keymap.set('n', '', muwiki.prev_link, keymap_opts) - vim.keymap.set('v', '', muwiki.create_link, keymap_opts) - vim.keymap.set('n', '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 = { + { "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', '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', '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', '', muwiki.open_link, opts) + vim.keymap.set('n', '', muwiki.next_link, opts) + vim.keymap.set('n', '', muwiki.prev_link, opts) + vim.keymap.set('v', '', 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', '', muwiki.open_link, opts) - vim.keymap.set('n', '', muwiki.next_link, opts) - vim.keymap.set('n', '', muwiki.prev_link, opts) - vim.keymap.set('v', '', muwiki.create_link, opts) - vim.keymap.set('n', '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', '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 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': ", 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('', 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 -- cgit v1.2.3