jakewvincent/mkdnflow.nvim

github github
note-taking
stars 777
issues 37
subscribers 6
forks 41
CREATED

UPDATED


Contents

  1. ๐Ÿš€ Introduction
  2. โœจ Features
    1. ๐Ÿงญ Navigation
    2. ๐Ÿ”— Link and reference handling
    3. ๐Ÿ“Š Table support
    4. ๐Ÿ“ List support
    5. โœ… To-do list support
    6. ๐Ÿ“ File management
    7. ๐Ÿช— Folding
    8. ๐Ÿ”ฎ Completion
    9. ๐Ÿงฉ YAML block parsing
    10. ๐Ÿ–Œ๏ธ Visual enhancements
  3. ๐Ÿ’พ Installation
  4. โš™๏ธ Configuration
    1. โšก Quick start
    2. ๐Ÿ”ง Advanced configuration and sample recipes
  5. ๐Ÿ› ๏ธ Commands & mappings
  6. ๐Ÿ“š API
    1. Initialization
    2. Link management
    3. Link and path handling
    4. Buffer navigation
    5. Cursor movement
    6. Cursor-aware manipulations
    7. List management
    8. To-do list management
    9. Table management
    10. Folds
    11. Yaml blocks
    12. Bibliography
    13. Statusline components
  7. ๐Ÿค Contributing
  8. ๐Ÿ”ข Version information
  9. ๐Ÿ”— Related projects
    1. Competition
    2. Complementary plugins

๐Ÿš€ Introduction

Mkdnflow is designed for the fluent navigation and management of markdown documents and document collections (notebooks, wikis, etc). It features numerous convenience functions that make it easier to work within raw markdown documents or document collections: link and reference handling (๐Ÿ”— Link and reference handling), navigation (๐Ÿงญ Navigation), table support (๐Ÿ“Š Table support), list (๐Ÿ“ List support) and to-do list (โœ… To-do list support) support, file management (๐Ÿ“ File management), section folding (๐Ÿช— Folding), and more. Use it for notetaking, personal knowledge management, static website building, and more. Most features are highly tweakable (โš™๏ธ Configuration).

โœจ Features

๐Ÿงญ Navigation

Within-buffer navigation

  • Jump to links
  • Jump to section headings

Within-notebook navigation

  • Link following
    • Open markdown and other text filetypes in the current window
    • Open other filetypes and URLs with your system's default application
  • Browser-like 'Back' and 'Forward' functionality
  • Table of contents window

๐Ÿ”— Link and reference handling

  • Link creation from a visual selection or the word under the cursor
  • Link destruction
  • Follow links to local paths and other Markdown files
  • Follow external links (open using default application)
  • Follow .bib-based references
    • Open url or doi field in the default browser
    • Open documents specified in file field
  • Implicit filetype extensions
  • Support for various link types
    • Standard Markdown links ([my page](my_page.md))
    • Wiki links (direct [[my page]] or piped [[my_page.md|my page]])
    • Automatic links (<https://my.page>)
    • Reference-style links ([my page][1] with [1]: my_page.md)
    • Image links (![alt text](image.png)) โ€” opened in system viewer
    • Citations (@citekey or Pandoc-style [@citekey])

๐Ÿ“Š Table support

  • Table creation
  • Table extension (add rows and columns)
  • Table formatting
  • Pandoc grid table support
  • Column alignment (left, right, center)
  • Paste delimited data as a table
  • Import delimited file into a new table

๐Ÿ“ List support

  • Automatic list extension
  • Sensible auto-indentation and auto-dedentation
  • Ordered list number updating

โœ… To-do list support

  • Toggle to-do item status
  • Status propagation
  • To-do list sorting
  • Create to-do items from plain ordered or unordered list items
  • Customizable highlighting for to-do status markers and content

๐Ÿ“ File management

  • Simultaneous link and file renaming
  • As-needed directory creation

๐Ÿช— Folding

  • Section folding and fold toggling
  • Helpful indicators for folded section contents
    • Section heading level
    • Counts of Markdown objects (tables, lists, code blocks, etc.)
    • Line and word counts
  • YAML block folding

๐Ÿ”ฎ Completion

  • Path completion
  • Completion of bibliography items

๐Ÿงฉ YAML block parsing

  • Specify a bibliography file in YAML front matter

๐Ÿ–Œ๏ธ Visual enhancements

  • Conceal markdown and wiki link syntax
  • Extended link highlighting
    • Automatic links
    • Wiki links

๐Ÿ’พ Installation

Requirements:

  • Linux, macOS, or Windows
  • Neovim >= 0.9.5 (tested on 0.9.5, 0.10.x, and stable)

Install Mkdnflow using your preferred package manager for Neovim. Once installed, Mkdnflow is configured and initialized using a setup function.

require('lazy').setup({
    -- Your other plugins
    {
        'jakewvincent/mkdnflow.nvim',
        ft = { 'markdown', 'rmd' },  -- Add custom filetypes here if configured
        config = function()
            require('mkdnflow').setup({
                -- Your config
            })
        end
    }
    -- Your other plugins
})
" Vim-Plug
Plug 'jakewvincent/mkdnflow.nvim'

" Include the setup function somewhere else in your init.vim file, or the
" plugin won't activate itself:
lua << EOF
require('mkdnflow').setup({
    -- Config goes here; leave blank for defaults
})
EOF

โš™๏ธ Configuration

โšก Quick start

Mkdnflow is configured and initialized using a setup function. To use the default settings, pass no arguments or an empty table to the setup function:

{
    'jakewvincent/mkdnflow.nvim',
    config = function()
        require('mkdnflow').setup({})
    end
}

๐Ÿ”ง Advanced configuration and sample recipes

Most features are highly configurable. Study the default config first and read the documentation for the configuration options below or in the help files.

{
    modules = {
        bib = true,
        buffers = true,
        conceal = true,
        cursor = true,
        folds = true,
        foldtext = true,
        links = true,
        lists = true,
        maps = true,
        paths = true,
        tables = true,
        to_do = true,
        yaml = false,
        cmp = false,
    },
    create_dirs = true,
    silent = false,
    wrap = false,
    path_resolution = {
        primary = 'first',
        fallback = 'current',
        root_marker = false,
        sync_cwd = false,
        update_on_navigate = true,
    },
    filetypes = {
        markdown = true,
        rmd = true,
    },
    foldtext = {
        object_count = true,
        object_count_icon_set = 'emoji',
        object_count_opts = function()
            return require('mkdnflow').foldtext.default_count_opts
        end,
        line_count = true,
        line_percentage = true,
        word_count = false,
        title_transformer = function()
            return require('mkdnflow').foldtext.default_title_transformer
        end,
        fill_chars = {
            left_edge = 'โขพโฃฟโฃฟ',
            right_edge = 'โฃฟโฃฟโกท',
            item_separator = ' ยท ',
            section_separator = ' โฃนโฃฟโฃ ',
            left_inside = ' โฃน',
            right_inside = 'โฃ ',
            middle = 'โฃฟ',
        },
    },
    bib = {
        default_path = nil,
        find_in_root = true,
    },
    cursor = {
        jump_patterns = nil,
    },
    links = {
        style = 'markdown',
        compact = false,
        conceal = false,
        search_range = 0,
        implicit_extension = nil,
        transform_on_follow = false,
        transform_on_create = function(text)
            text = text:gsub('[ /]', '-')
            text = text:lower()
            text = os.date('%Y-%m-%d_') .. text
            return text
        end,
        auto_create = true,
        on_create_new = false,
    },
    new_file_template = {
        enabled = false,
        placeholders = {
            before = {
                title = 'link_title',
                date = 'os_date',
            },
            after = {},
        },
        template = '# {{ title }}',
    },
    to_do = {
        highlight = false,
        statuses = {
            {
                name = 'not_started',
                symbol = ' ',
                colors = {
                    marker = { link = 'Conceal' },
                    content = { link = 'Conceal' },
                },
                sort = { section = 2, position = 'top' },
                skip_on_toggle = false,
                propagate = {
                    up = function(host_list) ... end,
                    down = function(child_list) ... end,
                },
            },
            {
                name = 'in_progress',
                symbol = '-',
                colors = {
                    marker = { link = 'WarningMsg' },
                    content = { bold = true },
                },
                sort = { section = 1, position = 'bottom' },
                skip_on_toggle = false,
                propagate = {
                    up = function(host_list) ... end,
                    down = function(child_list) end,
                },
            },
            {
                name = 'complete',
                symbol = { 'X', 'x' },
                colors = {
                    marker = { link = 'String' },
                    content = { link = 'Conceal' },
                },
                sort = { section = 3, position = 'top' },
                skip_on_toggle = false,
                propagate = {
                    up = function(host_list) ... end,
                    down = function(child_list) ... end,
                },
            },
        },
        status_propagation = {
            up = true,
            down = true,
        },
        sort = {
            on_status_change = false,
            recursive = false,
            cursor_behavior = {
                track = true,
            },
        },
    },
    tables = {
        type = 'pipe',
        trim_whitespace = true,
        format_on_move = true,
        auto_extend_rows = false,
        auto_extend_cols = false,
        style = {
            cell_padding = 1,
            separator_padding = 1,
            outer_pipes = true,
            apply_alignment = true,
        },
    },
    yaml = {
        bib = { override = false },
    },
    mappings = {
        MkdnEnter = { { 'n', 'v' }, '<CR>' },
        MkdnGoBack = { 'n', '<BS>' },
        MkdnGoForward = { 'n', '<Del>' },
        MkdnMoveSource = { 'n', '<F2>' },
        MkdnNextLink = { 'n', '<Tab>' },
        MkdnPrevLink = { 'n', '<S-Tab>' },
        MkdnFollowLink = false,
        MkdnDestroyLink = { 'n', '<M-CR>' },
        MkdnTagSpan = { 'v', '<M-CR>' },
        MkdnYankAnchorLink = { 'n', 'yaa' },
        MkdnYankFileAnchorLink = { 'n', 'yfa' },
        MkdnNextHeading = { 'n', ']]' },
        MkdnPrevHeading = { 'n', '[[' },
        MkdnIncreaseHeading = { { 'n', 'v' }, '+' },
        MkdnDecreaseHeading = { { 'n', 'v' }, '-' },
        MkdnIncreaseHeadingOp = { { 'n', 'v' }, 'g+' },
        MkdnDecreaseHeadingOp = { { 'n', 'v' }, 'g-' },
        MkdnToggleToDo = { { 'n', 'v' }, '<C-Space>' },
        MkdnNewListItem = false,
        MkdnNewListItemBelowInsert = { 'n', 'o' },
        MkdnNewListItemAboveInsert = { 'n', 'O' },
        MkdnExtendList = false,
        MkdnUpdateNumbering = { 'n', '<leader>nn' },
        MkdnTableNextCell = { 'i', '<Tab>' },
        MkdnTablePrevCell = { 'i', '<S-Tab>' },
        MkdnTableNextRow = false,
        MkdnTablePrevRow = { 'i', '<M-CR>' },
        MkdnTableNewRowBelow = { 'n', '<leader>ir' },
        MkdnTableNewRowAbove = { 'n', '<leader>iR' },
        MkdnTableNewColAfter = { 'n', '<leader>ic' },
        MkdnTableNewColBefore = { 'n', '<leader>iC' },
        MkdnTableDeleteRow = { 'n', '<leader>dr' },
        MkdnTableDeleteCol = { 'n', '<leader>dc' },
        MkdnFoldSection = { 'n', '<leader>f' },
        MkdnUnfoldSection = { 'n', '<leader>F' },
        MkdnTab = false,
        MkdnSTab = false,
        MkdnCreateLink = false,
        MkdnCreateLinkFromClipboard = { { 'n', 'v' }, '<leader>p' },
    },
}

๐ŸŽจ Configuration options

modules
require('mkdnflow').setup({
    modules = {
        bib = true,
        buffers = true,
        conceal = true,
        cursor = true,
        folds = true,
        foldtext = true,
        links = true,
        lists = true,
        maps = true,
        paths = true,
        tables = true,
        to_do = true,
        yaml = false,
        cmp = false,
    }
})
Option Type Description
modules.bib boolean true (default): bib module is enabled (required for parsing .bib files and following citations).false: Disable bib module functionality.
modules.buffers boolean true (default): buffers module is enabled (required for backward and forward navigation through buffers).false: Disable buffers module functionality.
modules.conceal boolean true (default): conceal module is enabled (required if you wish to enable link concealing. This does not automatically enable conceal behavior; see links.conceal.)false: Disable conceal module functionality.
modules.cursor boolean true (default): cursor module is enabled (required for cursor movements: jumping to links, headings, etc.).false: Disable cursor module functionality.
modules.folds boolean true (default): folds module is enabled (required for section folding).false: Disable folds module functionality.
modules.foldtext boolean true (default): foldtext module is enabled (required for prettified foldtext).false: Disable foldtext module functionality.
modules.links boolean true (default): links module is enabled (required for creating, destroying, and following links).false: Disable links module functionality.
modules.lists boolean true (default): lists module is enabled (required for working in and manipulating lists, etc.).false: Disable lists module functionality.
modules.to_do boolean true (default): to_do module is enabled (required for manipulating to-do statuses/lists, toggling to-do items, to-do list sorting, etc.)false: Disable to_do module functionality.
modules.paths boolean true (default): paths module is enabled (required for link interpretation, link following, etc.).false: Disable paths module functionality.
modules.tables boolean true (default): tables module is enabled (required for table management, navigation, formatting, etc.).false: Disable tables module functionality.
modules.yaml boolean true: yaml module is enabled (required for parsing yaml headers).false (default): Disable yaml module functionality.
modules.cmp boolean true: cmp module is enabled (required if you wish to enable completion for nvim-cmp).false (default): Disable cmp module functionality.
create_dirs
require('mkdnflow').setup({
    create_dirs = true,
})
Option Type Description
create_dirs boolean true (default): Directories referenced in a link will be (recursively) created if they do not exist.false: No action will be taken when directories referenced in a link do not exist. Neovim will open a new file, but you will get an error when you attempt to write the file.
path_resolution
require('mkdnflow').setup({
    path_resolution = {
        primary = 'first',
        fallback = 'current',
        root_marker = false,
        sync_cwd = false,
        update_on_navigate = false,
    },
})
Option Type Description
path_resolution.primary string 'first' (default): Links will be interpreted relative to the first-opened file (when the current instance of Neovim was started).'current': Links will always be interpreted relative to the current file.'root': Links will be always interpreted relative to the root directory of the current notebook (requires path_resolution.root_marker to be specified).Previously named perspective.priority.
path_resolution.fallback string 'first': (see above)'current' (default): (see above)'root': (see above)
path_resolution.root_marker string | boolean false (default): The plugin does not look for the notebook root.string: The name of a file (not a full path) by which a notebook's root directory can be identified. For instance, '.root' or 'index.md'.Previously named perspective.root_tell.
path_resolution.sync_cwd boolean true: Changes in path resolution will be reflected in the nvim working directory. (In other words, the working directory will sync with the plugin's path resolution.) This helps ensure (at least) that path completions (if using a completion plugin with support for paths) will be accurate and usable.false (default): Neovim's working directory will not be affected by Mkdnflow.Previously named perspective.nvim_wd_heel.
path_resolution.update_on_navigate boolean true: Path resolution will be updated when following a link to a file in a separate notebook/wiki (or navigating backwards to a file in another notebook/wiki).false (default): Path resolution will be not updated when following a link to a file in a separate notebook/wiki. (Links in the file in the separate notebook/wiki will be interpreted relative to the original notebook/wiki.)Previously named perspective.update.
filetypes
require('mkdnflow').setup({
    filetypes = {
        markdown = true,
        rmd = true,
    },
})
Option Type Description
filetypes.markdown boolean true (default): The plugin activates for files with the markdown filetype (includes .md, .markdown, .mkd, .mkdn, .mdwn, .mdown extensions).false: The plugin does not activate for markdown files.
filetypes.rmd boolean true (default): The plugin activates for files with the rmd filetype (.rmd extension).false: The plugin does not activate for R Markdown files.
filetypes.<name> boolean | string Custom filetype/extension configuration:- true: If Neovim recognizes this as an extension (e.g., md), the detected filetype is used. Otherwise, the extension is auto-registered as its own filetype (e.g., wiki = true registers .wiki files as filetype wiki).- 'filetype' (string): Register this extension as the specified filetype. Example: txt = 'markdown' makes .txt files activate mkdnflow and be treated as markdown.- false: Disable for this filetype/extension.Note: Old extension-based configs (e.g., md = true) are automatically migrated to filetype-based configs (e.g., markdown = true).Examples:lua<br>filetypes = {<br> markdown = true, -- Standard (default)<br> rmd = true, -- Standard (default)<br> wiki = true, -- Auto-register .wiki as 'wiki' filetype<br> txt = 'markdown', -- Treat .txt files as markdown<br>}<br>

[!NOTE] Mkdnflow activates based on Neovim's filetype detection, not file extensions. This means files with modelines like vim: ft=markdown will activate the plugin regardless of their extension.

Lazy loading note: If you use a plugin manager with lazy loading (e.g., ft = { 'markdown' }) and configure custom extensions like wiki = true, you must add those filetypes to your lazy loading configuration (e.g., ft = { 'markdown', 'wiki' }), since the plugin won't load until it sees a matching filetype.

wrap
require('mkdnflow').setup({
    wrap = false,
})
Option Type Description
wrap boolean true: When jumping to next/previous links or headings, the cursor will continue searching at the beginning/end of the file.false (default): When jumping to next/previous links or headings, the cursor will stop searching at the end/beginning of the file.
bib
require('mkdnflow').setup({
    bib = {
        default_path = nil,
        find_in_root = true,
    },
})
Option Type Description
bib.default_path string | nil nil (default): No default/fallback bib file will be used to search for citation keys.string: A path to a default .bib file to look for citation keys in when attempting to follow a reference. The path need not be in the root directory of the notebook.
bib.find_in_root boolean true (default): When path_resolution.primary is also set to root (and a root directory was found), the plugin will search for bib files to reference in the notebook's top-level directory. If bib.default_path is also specified, the default path will be added to the list of bib files found in the top-level directory so that it will also be searched.false: The notebook's root directory will not be searched for bib files.
silent
require('mkdnflow').setup({
    silent = false,
})
Option Type Description
silent boolean true: The plugin will not display any messages in the console except compatibility warnings related to your config.false (default): The plugin will display messages to the console.
cursor
require('mkdnflow').setup({
    cursor = {
        jump_patterns = nil,
    },
})
Option Type Description
cursor.jump_patterns table | nil nil (default): The default jump patterns for the configured link style are used (markdown-style links by default).table: A table of custom Lua regex patterns.{} (empty table): Disable link jumping without disabling the cursor module.
cursor.yank_register string '"' (default): Anchor links are yanked to the unnamed register.'+': Yank to the system clipboard.'<any register>': Yank to any valid Vim register (e.g. 'a', '*', '0').
links
require('mkdnflow').setup({
    links = {
        style = 'markdown',
        compact = false,
        conceal = false,
        search_range = 0,
        implicit_extension = nil,
        transform_on_follow = false,
        transform_on_create = function(text)
            text = text:gsub(" ", "-")
            text = text:lower()
            text = os.date('%Y-%m-%d_') .. text
            return(text)
        end,
        auto_create = true,
        on_create_new = false,
    },
})
Option Type Description
links.style string 'markdown' (default): Links will be expected in the standard markdown format: [<title>](<source>)'wiki': Links will be expected in the unofficial wiki-link style, specifically the title-after-pipe format: [[<source>|<title>]].
links.compact boolean true: Wiki-style links will be created with the source and name being the same (e.g. [[Link]] will display as "Link" and go to a file named "Link.md").false (default): Wiki-style links will be created with separate name and source (e.g. [[link-to-source|Link]] will display as "Link" and go to a file named "link-to-source.md").Previously named links.name_is_source.
links.conceal boolean true: Link sources and delimiters will be concealed (depending on which link style is selected).false (default): Link sources and delimiters will not be concealed by mkdnflow.
links.search_range integer When following or jumping to links, consider n lines before and after a given line (useful if you ever permit links to be interrupted by a hard line break). Default: 0.Previously named links.context.
links.implicit_extension string A string that instructs the plugin (a) how to interpret links to files that do not have an extension, and (b) how to create new links from the word under cursor or text selection.nil (default): Extensions will be explicit when a link is created and must be explicit in any notebook link.'<any extension>' (e.g. 'md'): Links without an extension (e.g. [Homepage](index)) will be interpreted with the implicit extension (e.g. index.md), and new links will be created without an extension.
links.transform_on_create fun(string): string | boolean false: No transformations are applied to the text to be turned into the name of the link source/path.fun(string): string (default): A function that transforms the text to be inserted as the source/path of a link when a link is created. Anchor links are not currently customizable. For an example, see the sample recipes beneath this table.Previously named links.transform_explicit.
links.transform_on_follow fun(string): string | boolean false (default): Do not perform any transformations on the link's source when following.fun(string): string: A function that transforms the path of a link immediately before interpretation. It does not transform the actual text in the buffer but can be used to modify link interpretation. For an example, see the sample recipe below.Previously named links.transform_implicit.
links.auto_create boolean true (default): Try to create a link from the word under the cursor if there is no link under the cursor to follow.false: Do nothing if trying to follow a link and a link can't be found under the cursor.Previously named links.create_on_follow_failure.
links.on_create_new false | fun(string, string|nil): string|nil A callback invoked when following a link to a file that does not yet exist,allowing file creation to be delegated to an external tool (e.g. zk,Obsidian CLI, a custom script). This callback is only invoked when the targetfile does not yet exist. Following a link to an existing file bypasses thiscallback entirely.The function receives two arguments: the full resolved path (with extension)that mkdnflow would create, and the link's display text (which may be nil).It should return a string (file path for mkdnflow to open) or nil (if thecallback handled everything). If a path is returned and the file exists there,mkdnflow opens it directly (skipping template injection). If the file does notexist at the returned path, mkdnflow runs its normal creation flow.false (default): Use mkdnflow's built-in file creation.
require('mkdnflow').setup({
    links = {
        -- If you want all link paths to be explicitly prefixed with the year
        -- and for the path to be converted to uppercase:
        transform_on_create = function(input)
            return(string.upper(os.date('%Y-')..input))
        end,
        -- Link paths that match a date pattern can be opened in a `journals`
        -- subdirectory of your notebook, and all others can be opened in a
        -- `pages` subdirectory:
        transform_on_follow = function(input)
            if input:match('%d%d%d%d%-%d%d%-%d%d') then
                return('journals/'..input)
            else
                return('pages/'..input)
            end
        end
    }
})

Delegate new-file creation to zk:

require('mkdnflow').setup({
    links = {
        on_create_new = function(path, title)
            -- Let zk create the note with its own templates and ID scheme.
            -- `zk new` prints the created path to stdout.
            local dir = vim.fn.fnamemodify(path, ':h')
            local cmd = { 'zk', 'new', '--no-input', '--print-path', dir }
            if title then
                table.insert(cmd, '--title')
                table.insert(cmd, title)
            end
            local result = vim.fn.system(cmd)
            local new_path = vim.trim(result)
            if vim.v.shell_error ~= 0 then
                vim.api.nvim_echo(
                    {{ 'zk new failed: ' .. result, 'ErrorMsg' }},
                    true, {}
                )
                return nil
            end
            return new_path  -- mkdnflow opens the zk-created file
        end,
    },
})
new_file_template
require('mkdnflow').setup({
    new_file_template = {
        enabled = false,
        placeholders = {
            before = { title = 'link_title', date = 'os_date' },
            after = {},
        },
        template = '# {{ title }}',
    },
})
Option Type Description
new_file_template.enabled boolean true: Use the new-file template when opening a new file by following a link.false (default): Don't use the new-file template when opening a new file by following a link.Previously named new_file_template.use_template.
new_file_template.placeholders.before table<string, string|fun(): string> A table whose keys are placeholder names mapped either to a function (to be evaluated immediately before the buffer is opened in the current window) or to one of a limited set of recognized strings:'link_title': The title of the link that was followed to get to the just-opened file.'os_date': The current date, according to the OS.Default: { title = 'link_title', date = 'os_date' }
new_file_template.placeholders.after table<string, string|fun(): string> A table whose keys are placeholder names mapped either to a function (to be evaluated immediately after the buffer is opened in the current window) or to one of a limited set of recognized strings (see above). Default: {}
new_file_template.template string A string, optionally containing placeholder names, that will be inserted into a new file. Default: '# {{ title }}'
to_do
require('mkdnflow').setup({
    to_do = {
        highlight = false,
        status_propagation = { up = true, down = true },
        sort = {
            on_status_change = false,
            recursive = false,
            cursor_behavior = { track = true },
        },
        statuses = { ... },  -- See full default in docs
    },
})
Option Type Description
to_do.highlight boolean true: Apply highlighting to to-do status markers and/or content (as defined in to_do.statuses[*].highlight).false (default): Do not apply any highlighting.
to_do.status_propagation.up boolean true (default): Update ancestor statuses (recursively) when a descendant status is changed. Updated according to logic provided in to_do.statuses[*].propagate.up.false: Ancestor statuses are not affected by descendant status changes.
to_do.status_propagation.down boolean true (default): Update descendant statuses (recursively) when an ancestor's status is changed. Updated according to logic provided in to_do.statuses[*].propagate.down.false: Descendant statuses are not affected by ancestor status changes.
to_do.sort_on_status_change boolean true: Sort a to-do list when an item's status is changed.false (default): Leave all to-do items in their current position when an item's status is changed.Note: This will not apply if the to-do item's status is changed manually (i.e. by typing or pasting in the status marker).
to_do.sort.recursive boolean true: sort_on_status_change applies recursively, sorting the host list of each successive parent until the root of the list is reached.false (default): sort_on_status_change only applies at the current to-do list level (not to the host list of the parent to-do item).
to_do.sort.cursor_behavior.track boolean true (default): Move the cursor so that it remains on the same to-do item, even after a to-do list sort relocates the item.false: The cursor remains on its current line number, even if the to-do item is relocated by sorting.
to_do.statuses table (array-like) A list of tables, each of which represents a to-do status. See options in the following rows. An arbitrary number of to-do status tables can be provided. See default statuses in the settings table.
to_do.statuses[*].name string The designated name of the to-do status.
to_do.statuses[*].marker string | table The marker symbol to use for the status. The marker's string width must be 1.When provided as a string (e.g. ' '), the string is used as both the recognized and written marker.When provided as a table (e.g. { 'X', 'x' }), the first element is the primary marker โ€” the one written to the buffer when the status is set. Any additional elements are legacy markers that will be recognized as belonging to this status when read from a file, but will be replaced with the primary marker on the next toggle. This is useful for accepting alternate symbols from other tools or conventions without losing compatibility.
to_do.statuses[*].highlight.marker table (highlight definition) A table of highlight definitions to apply to a status marker, including brackets. See the {val} parameter of :h nvim_set_hl for possible options.
to_do.statuses[*].highlight.content table (highlight definition) A table of highlight definitions to apply to the to-do item content (everything following the status marker). See the {val} parameter of :h nvim_set_hl for possible options.
to_do.statuses[*].skip_on_toggle boolean true: When toggling/rotating a to-do item's status, skip this status in the rotation.false: Leave the status in the rotation.Note: This setting is useful if there is a status marker that you never want to manually set and only want to apply when automatically updating ancestors or descendants.Previously named to_do.statuses[*].exclude_from_rotation.
to_do.statuses[*].sort.section integer The integer should represent the linear section of the list in which items of this status should be placed when sorted. A section refers to a segment of a to-do list. If you want items with the 'in_progress' status to be first in the list, you would set this option to 1 for the status.Note: Sections are not visually delineated in any way other than items with the same section number occurring on adjacent lines in the list.
to_do.statuses[*].sort.position string Where in its assigned section a to-do item should be placed:'top': Place a sorted item at the top of its corresponding section.'bottom': Place a sorted item at the bottom of its corresponding section.'relative': Maintain the current relative order of the sorted item whose status was just changed (vs. other list items).
to_do.statuses[*].propagate.up fun(to_do_list): string | nil A function that accepts a to-do list instance and returns a valid to-do status name. The list passed in is the list that hosts the to-do item whose status was just changed. The return value should be the desired value of the parent. Return nil to leave the parent's status as is.
to_do.statuses[*].propagate.down fun(to_do_list): string[] A function that accepts a to-do list instance and returns a list of valid to-do status names. The list passed in will be the child list of the to-do item whose status was just changed. Return nil or an empty table to leave the children's status as is.

[!WARNING] The following to-do configuration options are deprecated. Please use the to_do.statuses table instead. Continued support for these options is temporarily provided by a compatibility layer that will be removed in the near future.

  • to_do.symbols - A list of markers representing to-do completion statuses
  • to_do.not_started - Which marker represents a not-yet-started to-do
  • to_do.in_progress - Which marker represents an in-progress to-do
  • to_do.complete - Which marker represents a complete to-do
  • to_do.update_parents - Whether parent to-dos' statuses should be updated
foldtext
require('mkdnflow').setup({
    foldtext = {
        object_count = true,
        object_count_icon_set = 'emoji',
        object_count_opts = function()
            return require('mkdnflow').foldtext.default_count_opts
        end,
        line_count = true,
        line_percentage = true,
        word_count = false,
        title_transformer = function()
            return require('mkdnflow').foldtext.default_title_transformer
        end,
        fill_chars = {
            left_edge = 'โขพโฃฟโฃฟ',
            right_edge = 'โฃฟโฃฟโกท',
            item_separator = ' ยท ',
            section_separator = ' โฃนโฃฟโฃ ',
            left_inside = ' โฃน',
            right_inside = 'โฃ ',
            middle = 'โฃฟ',
        },
    },
})
Option Type Description
foldtext.object_count boolean true (default): Show a count of all objects inside of a folded section.false: Do not show a count of any objects inside of a folded section.
foldtext.object_count_icon_set string | table 'emoji' (default): Use pre-defined emojis as icons for counted objects.'plain': Use pre-defined plaintext UTF-8 characters as icons for counted objects.'nerdfont': Use pre-defined nerdfont characters as icons for counted objects. Requires a nerdfont.table<string, string>: Use custom mapping of object names to icons.
foldtext.object_count_opts fun(): table<string, table> A function that returns the options table defining the final attributes of the objects to be counted, including icons and counting methods. The pre-defined object types are tbl, ul, ol, todo, img, fncblk, sec, par, and link.
foldtext.line_count boolean true (default): Show a count of the lines contained in the folded section.false: Don't show a line count.
foldtext.line_percentage boolean true (default): Show the percentage of document (buffer) lines contained in the folded section.false: Don't show the percentage.
foldtext.word_count boolean true: Show a count of the paragraph words in the folded section, ignoring words inside of other objects.false (default): Don't show a word count.
foldtext.title_transformer fun(): fun(string): string A function that returns another function. The inner function accepts a string (the section heading text) and returns a potentially modified string.
foldtext.fill_chars.left_edge string The character(s) to use at the very left edge of the foldtext. Default: 'โขพโฃฟโฃฟ'.
foldtext.fill_chars.right_edge string The character(s) to use at the very right edge of the foldtext. Default: 'โฃฟโฃฟโกท'
foldtext.fill_chars.item_separator string The character(s) used to separate the items within a section. Default: ' ยท '
foldtext.fill_chars.section_separator string The character(s) used to separate adjacent sections. Default: ' โฃนโฃฟโฃ '
foldtext.fill_chars.left_inside string The character(s) used at the internal left edge of fill characters. Default: ' โฃน'
foldtext.fill_chars.right_inside string The character(s) used at the internal right edge of fill characters. Default: 'โฃ '
foldtext.fill_chars.middle string The character used to fill empty space in the foldtext line. Default: 'โฃฟ'
-- SAMPLE FOLDTEXT CONFIGURATION RECIPE WITH COMMENTS
require('mkdnflow').setup({
    foldtext = {
        title_transformer = function()
            local function my_title_transformer(text)
                local updated_title = text:gsub('%b{}', '')
                updated_title = updated_title:gsub('^%s*', '')
                updated_title = updated_title:gsub('%s*$', '')
                updated_title = updated_title:gsub('^######', 'โ–‘โ–‘โ–‘โ–‘โ–‘โ–“')
                updated_title = updated_title:gsub('^#####', 'โ–‘โ–‘โ–‘โ–‘โ–“โ–“')
                updated_title = updated_title:gsub('^####', 'โ–‘โ–‘โ–‘โ–“โ–“โ–“')
                updated_title = updated_title:gsub('^###', 'โ–‘โ–‘โ–“โ–“โ–“โ–“')
                updated_title = updated_title:gsub('^##', 'โ–‘โ–“โ–“โ–“โ–“โ–“')
                updated_title = updated_title:gsub('^#', 'โ–“โ–“โ–“โ–“โ–“โ–“')
                return updated_title
            end
            return my_title_transformer
        end,
        object_count_icon_set = 'nerdfont',
        object_count_opts = function()
            local opts = {
                link = false,
                blockquote = {
                    icon = ' ',
                    count_method = {
                        pattern = { '^>.+$' },
                        tally = 'blocks',
                    }
                },
                fncblk = { icon = ' ' }
            }
            return opts
        end,
        line_count = false,
        word_count = true,
        fill_chars = {
            left_edge = 'โ•พโ”€๐Ÿ–ฟ โ”€',
            right_edge = 'โ”€โ”€โ•ผ',
            item_separator = ' ยท ',
            section_separator = ' // ',
            left_inside = ' โ”',
            right_inside = 'โ”ฅ ',
            middle = 'โ”€',
        },
    },
})

The above recipe will produce foldtext like the following (for an h3-level section heading called My section):

tables
require('mkdnflow').setup({
    tables = {
        type = 'pipe',
        trim_whitespace = true,
        format_on_move = true,
        auto_extend_rows = false,
        auto_extend_cols = false,
        style = {
            cell_padding = 1,
            separator_padding = 1,
            outer_pipes = true,
            apply_alignment = true,
        },
    },
})
Option Type Description
tables.type string 'pipe' (default): New tables are created in pipe format (| cell | cell | with | --- | --- | separator).'grid': New tables are created in pandoc grid format with +---+ border lines between rows and +===+ header separators. Grid tables support native multiline cells.Regardless of this setting, existing tables are auto-detected and handled in their native format when formatting, navigating, or editing.
tables.trim_whitespace boolean true (default): Trim extra whitespace from the end of a table cell when a table is formatted.false: Leave whitespace at the end of a table cell when formatting.
tables.format_on_move boolean true (default): Format the table each time the cursor is moved to the next/previous cell/row using the plugin's API.false: Don't format the table when the cursor is moved.
tables.auto_extend_rows boolean true: Add another row when attempting to jump to the next row and the row doesn't exist.false (default): Leave the table when attempting to jump to the next row and the row doesn't exist.
tables.auto_extend_cols boolean true: Add another column when attempting to jump to the next column and the column doesn't exist.false (default): Go to the first cell of the next row when attempting to jump to the next column and the column doesn't exist.
tables.style.cell_padding integer 1 (default): Use one space as padding at the beginning and end of each cell.<n>: Use <n> spaces as cell padding.
tables.style.separator_padding integer 1 (default): Use one space as padding in the separator row.<n>: Use <n> spaces as padding in the separator row.
tables.style.outer_pipes boolean true (default): Include outer pipes when formatting a table.false: Do not use outer pipes when formatting a table.
tables.style.apply_alignment boolean true (default): Apply the cell alignment indicated in the separator row when formatting the table.false: Always visually left-align cell contents when formatting a table.Previously named tables.style.mimic_alignment.
yaml
require('mkdnflow').setup({
    yaml = {
        bib = { override = false },
    },
})
Option Type Description
yaml.bib.override boolean true: A bib path specified in a markdown file's yaml header should be the only source considered for bib references in that file.false (default): All known bib paths will be considered, whether specified in the yaml header or in your configuration settings.
mappings
require('mkdnflow').setup({
    mappings = {
        MkdnEnter = { { 'n', 'v' }, '<CR>' },
        MkdnGoBack = { 'n', '<BS>' },
        MkdnGoForward = { 'n', '<Del>' },
        MkdnMoveSource = { 'n', '<F2>' },
        MkdnNextLink = { 'n', '<Tab>' },
        MkdnPrevLink = { 'n', '<S-Tab>' },
        MkdnFollowLink = false,
        MkdnDestroyLink = { 'n', '<M-CR>' },
        MkdnTagSpan = { 'v', '<M-CR>' },
        MkdnYankAnchorLink = { 'n', 'yaa' },
        MkdnYankFileAnchorLink = { 'n', 'yfa' },
        MkdnNextHeading = { 'n', ']]' },
        MkdnPrevHeading = { 'n', '[[' },
        MkdnIncreaseHeading = { { 'n', 'v' }, '+' },
        MkdnDecreaseHeading = { { 'n', 'v' }, '-' },
        MkdnIncreaseHeadingOp = { { 'n', 'v' }, 'g+' },
        MkdnDecreaseHeadingOp = { { 'n', 'v' }, 'g-' },
        MkdnToggleToDo = { { 'n', 'v' }, '<C-Space>' },
        MkdnNewListItem = false,
        MkdnNewListItemBelowInsert = { 'n', 'o' },
        MkdnNewListItemAboveInsert = { 'n', 'O' },
        MkdnExtendList = false,
        MkdnUpdateNumbering = { 'n', '<leader>nn' },
        MkdnTableNextCell = { 'i', '<Tab>' },
        MkdnTablePrevCell = { 'i', '<S-Tab>' },
        MkdnTableNextRow = false,
        MkdnTablePrevRow = { 'i', '<M-CR>' },
        MkdnTableNewRowBelow = { 'n', '<leader>ir' },
        MkdnTableNewRowAbove = { 'n', '<leader>iR' },
        MkdnTableNewColAfter = { 'n', '<leader>ic' },
        MkdnTableNewColBefore = { 'n', '<leader>iC' },
        MkdnTableDeleteRow = { 'n', '<leader>dr' },
        MkdnTableDeleteCol = { 'n', '<leader>dc' },
        MkdnTableAlignLeft = { 'n', '<leader>al' },
        MkdnTableAlignRight = { 'n', '<leader>ar' },
        MkdnTableAlignCenter = { 'n', '<leader>ac' },
        MkdnTableAlignDefault = { 'n', '<leader>ax' },
        MkdnFoldSection = { 'n', '<leader>f' },
        MkdnUnfoldSection = { 'n', '<leader>F' },
        MkdnTab = false,
        MkdnSTab = false,
        MkdnCreateLink = false,
        MkdnCreateLinkFromClipboard = { { 'n', 'v' }, '<leader>p' },
    },
})

See descriptions of commands and mappings below.

Note: <command> should be the name of a command defined in mkdnflow.nvim/plugin/mkdnflow.lua (see :h Mkdnflow-commands for a list).

Option Type Description
mappings.<command> [string|string[], string] The first item is a string or an array of strings representing the mode(s) that the mapping should apply in ('n', 'v', etc.). The second item is a string representing the mapping (in the expected format for vim).

๐Ÿ”ฎ Completion setup

To enable completion via cmp using the provided source, add mkdnflow as a source in your cmp setup function. You may also want to modify the formatting to see which completions are coming from Mkdnflow:

cmp.setup({
    -- Add 'mkdnflow' as a completion source
    sources = cmp.config.sources({
        { name = 'mkdnflow' },
    }),
    -- Completion source attribution
    formatting = {
        format = function(entry, vim_item)
            vim_item.menu = ({
                -- Other attributions
                mkdnflow = '[Mkdnflow]',
            })[entry.source_name]
            return vim_item
        end
    }
})

[!WARNING] There may be some compatibility issues with the completion module and links.transform_on_create/links.transform_on_follow functions.

If you have some transform_on_create option for links to organizing in folders then the folder name will be inserted accordingly. Some transformations may not work as expected in completions.

To prevent this, make sure you write sensible transformation functions, preferably using it for folder organization.

๐Ÿ› ๏ธ Commands & mappings

Below are descriptions of the user commands defined by Mkdnflow. For the default mappings to these commands, see the mappings = ... section of Configuration options.

Command Default mapping Description
MkdnEnter -- Triggers a wrapper function which will (a) infer your editor mode, and then if in normal or visual mode, either follow a link, create a new link from the word under the cursor or visual selection, or fold a section (if cursor is on a section heading); if in insert mode, it will create a new list item (if cursor is in a list), go to the next row in a table (if cursor is in a table), or behave normally (if cursor is not in a list or a table). In visual mode, if the selection overlaps a citation (@citekey or [@citekey]), a markdown link is created from the citekey instead of following the citation.Note: There is no insert-mode mapping for this command by default since some may find its effects intrusive. To enable the insert-mode functionality, add to the mappings table: MkdnEnter = {{'i', 'n', 'v'}, '<CR>'}.
MkdnNextLink { 'n', '<Tab>' } Move cursor to the beginning of the next link (if there is a next link).
MkdnPrevLink { 'n', '<S-Tab>' } Move the cursor to the beginning of the previous link (if there is one).
MkdnNextHeading { 'n', ']]' } Move the cursor to the beginning of the next heading (if there is one).
MkdnPrevHeading { 'n', '[[' } Move the cursor to the beginning of the previous heading (if there is one).
MkdnGoBack { 'n', '<BS>' } Open the historically last-active buffer in the current window.Note: The back-end function for :MkdnGoBack (require('mkdnflow').buffers.goBack()) returns a boolean indicating the success of goBack(). This may be useful if you wish to remap <BS> such that when goBack() is unsuccessful, another function is performed.
MkdnGoForward { 'n', '<Del>' } Open the buffer that was historically navigated away from in the current window.
MkdnCreateLink -- Create a link from the word under the cursor (in normal mode) or from the visual selection (in visual mode).
MkdnCreateLinkFromClipboard { { 'n', 'v' }, '<leader>p' } Create a link, using the content from the system clipboard (e.g. a URL) as the source and the word under cursor or visual selection as the link text.
MkdnFollowLink -- Open the link under the cursor, creating missing directories if desired, or if there is no link under the cursor, make a link from the word under the cursor. Image links (![alt](path)) are opened in the system's default viewer.
MkdnDestroyLink { 'n', '<M-CR>' } Destroy the link under the cursor, replacing it with just the text from the link name.
MkdnTagSpan { 'v', '<M-CR>' } Tag a visually-selected span of text with an ID, allowing it to be linked to with an anchor link.
MkdnMoveSource { 'n', '<F2>' } Open a dialog where you can provide a new source for a link and the plugin will rename and move the associated file on the backend (and rename the link source).
MkdnYankAnchorLink { 'n', 'yaa' } Yank a formatted anchor link (if cursor is currently on a line with a heading).
MkdnYankFileAnchorLink { 'n', 'yfa' } Yank a formatted anchor link with the filename included before the anchor (if cursor is currently on a line with a heading).
MkdnIncreaseHeading { { 'n', 'v' }, '+' } Increase heading importance (remove hashes). Supports visual selection to change multiple headings at once. Visual mode supports dot-repeat (like Vim's < and >).
MkdnDecreaseHeading { { 'n', 'v' }, '-' } Decrease heading importance (add hashes). Supports visual selection to change multiple headings at once. Visual mode supports dot-repeat (like Vim's < and >).
MkdnIncreaseHeadingOp { { 'n', 'v' }, 'g+' } Operator version of MkdnIncreaseHeading. In normal mode, use with a motion (e.g., g+} to increase headings to next paragraph). In visual mode, operates on selection. Supports dot-repeat.
MkdnDecreaseHeadingOp { { 'n', 'v' }, 'g-' } Operator version of MkdnDecreaseHeading. In normal mode, use with a motion (e.g., g-} to decrease headings to next paragraph). In visual mode, operates on selection. Supports dot-repeat.
MkdnToggleToDo { { 'n', 'v' }, '<C-Space>' } Toggle to-do list item's completion status or convert a list item into a to-do list item.
MkdnSortToDoList -- Sort the to-do list at the cursor position by status. Items are grouped by their status's sort.section value and positioned according to sort.position.
MkdnUpdateNumbering { 'n', '<leader>nn' } Update numbering for all siblings of the list item of the current line.
MkdnNewListItem -- Add a new ordered list item, unordered list item, or (uncompleted) to-do list item.
MkdnNewListItemBelowInsert { 'n', 'o' } Add a new list item below the current line and begin insert mode. Add a new line and enter insert mode when the cursor is not in a list.
MkdnNewListItemAboveInsert { 'n', 'O' } Add a new list item above the current line and begin insert mode. Add a new line and enter insert mode when the cursor is not in a list.
MkdnExtendList -- Like above, but the cursor stays on the current line (new list items of the same type are added below).
MkdnTable ncol nrow (noh) -- Make a table of ncol columns and nrow rows. Pass noh as a third argument to exclude table headers.
MkdnTableFormat -- Format a table under the cursor.
MkdnTableNextCell { 'i', '<Tab>' } Move the cursor to the beginning of the next cell in the table, jumping to the next row if needed.
MkdnTablePrevCell { 'i', '<S-Tab>' } Move the cursor to the beginning of the previous cell in the table, jumping to the previous row if needed.
MkdnTableCellNewLine { 'i', '<S-CR>' } Insert a new line within the current table cell. In grid tables, this adds a new content line within the current row. In pipe tables, this inserts a <br> tag at the cursor position.
MkdnTableNextRow -- Move the cursor to the beginning of the same cell in the next row of the table.
MkdnTablePrevRow { 'i', '<M-CR>' } Move the cursor to the beginning of the same cell in the previous row of the table.
MkdnTableNewRowBelow { 'n', '<leader>ir' } Add a new row below the row the cursor is currently in.
MkdnTableNewRowAbove { 'n', '<leader>iR' } Add a new row above the row the cursor is currently in.
MkdnTableNewColAfter { 'n', '<leader>ic' } Add a new column following the column the cursor is currently in.
MkdnTableNewColBefore { 'n', '<leader>iC' } Add a new column before the column the cursor is currently in.
MkdnTableDeleteRow { 'n', '<leader>dr' } Delete the row the cursor is currently in. Does nothing if cursor is on the separator row.
MkdnTableDeleteCol { 'n', '<leader>dc' } Delete the column the cursor is currently in. Does nothing if the table has only one column.
MkdnTableAlignLeft { 'n', '<leader>al' } Set the alignment of the current table column to left. Updates the separator row and reformats the table.
MkdnTableAlignRight { 'n', '<leader>ar' } Set the alignment of the current table column to right. Updates the separator row and reformats the table.
MkdnTableAlignCenter { 'n', '<leader>ac' } Set the alignment of the current table column to center. Updates the separator row and reformats the table.
MkdnTableAlignDefault { 'n', '<leader>ax' } Remove alignment from the current table column, returning it to the default. Updates the separator row and reformats the table.
MkdnTab -- Wrapper function which will jump to the next cell in a table (if cursor is in a table) or indent an (empty) list item (if cursor is in a list item).
MkdnSTab -- Wrapper function which will jump to the previous cell in a table (if cursor is in a table) or de-indent an (empty) list item (if cursor is in a list item).
MkdnFoldSection { 'n', '<leader>f' } Fold the section the cursor is currently on/in.
MkdnUnfoldSection { 'n', '<leader>F' } Unfold the folded section the cursor is currently on.
Mkdnflow -- Manually start Mkdnflow.

[!TIP] If you are attempting to (re)map <CR> in insert mode but can't get it to work, try inspecting your current insert mode mappings and seeing if anything is overriding your mapping. Possible candidates are completion plugins and auto-pair plugins.

If using nvim-cmp, consider using the mapping with a fallback. If using an autopair plugin that automatically maps <CR> (e.g. nvim-autopairs), see if it provides a way to disable its <CR> mapping.

๐Ÿ“š API

Mkdnflow provides a range of Lua functions that can be called directly to manipulate markdown files, navigate through buffers, manage links, and more. Below are the primary functions available:

  1. Initialization (Initialization)
  2. Link management (Link management)
  3. Link & path handling (Link and path handling)
  4. Buffer navigation (Buffer navigation)
  5. Cursor movement (Cursor movement)
  6. Cursor-aware manipulations (Cursor-aware manipulations)
  7. List management (List management)
  8. To-do list management (To-do list management)
  9. Table management (Table management)
  10. Folds (Folds)
  11. Yaml blocks (Yaml blocks)
  12. Bibliography (Bibliography)
  13. Statusline components (Statusline components)

Initialization

require('mkdnflow').setup(config)

Initializes the plugin with the provided configuration. See Advanced configuration and sample recipes. If called with an empty table, the default configuration is used.

  • Parameters:
    • config: (table) Configuration table containing various settings such as filetypes, modules, mappings, and more.

require('mkdnflow').forceStart(opts)

Activates the plugin if it has not already been activated. This is called automatically when a buffer with a matching filetype is opened, but can be triggered manually via the :Mkdnflow command.

  • Parameters:
    • opts: (table) Table of options.
      • opts[1]: (string) Pass 'silent' to suppress the startup message.

Link management

require('mkdnflow').links.createLink(args)

Creates a markdown link from the word under the cursor or visual selection.

  • Parameters:
    • args: (table) Arguments to customize link creation.
      • from_clipboard: (boolean) If true, use the system clipboard content as the link source.

require('mkdnflow').links.followLink(args)

Follows the link under the cursor, opening the corresponding file, URL, or directory. Image links are opened in the system's default image viewer.

  • Parameters:
    • args: (table) Arguments for following the link.
      • path: (string|nil) The path/source to follow. If nil, a path from a link under the cursor will be used.
      • anchor: (string|nil) An anchor, either one in the current buffer (in which case path will be nil), or one in the file referred to in path.
      • range: (boolean|nil) Whether a link should be created from a visual selection range. This is only relevant if create_on_follow_failure is true, there is no link under the cursor, and there is currently a visual selection that needs to be made into a link.

require('mkdnflow').links.destroyLink()

Destroys the link under the cursor, replacing it with plain text.

require('mkdnflow').links.tagSpan()

Tags a visual selection as a span, useful for adding attributes to specific text segments.

require('mkdnflow').links.getLinkUnderCursor(col)

Returns the link under the cursor at the specified column.

  • Parameters:
    • col: (number|nil) The column position to check for a link. The current cursor position is used if this is not specified.

require('mkdnflow').links.getLinkPart(link_table, part)

Retrieves a specific part of a link, such as the source or the text.

  • Parameters:
    • link_table: (table) The table containing link details, as provided by require('mkdnflow').links.getLinkUnderCursor().
    • part: (string|nil) The part of the link to retrieve (one of 'source', 'name', or 'anchor'). Default: 'source'.

require('mkdnflow').links.getBracketedSpanPart(part)

Retrieves a specific part of a bracketed span.

  • Parameters:
    • part: (string|nil) The part of the span to retrieve (one of 'text' or 'attr'). Default: 'attr'.

require('mkdnflow').links.hasUrl(string, to_return, col)

Checks if a given string contains a URL and optionally returns the URL.

  • Parameters:
    • string: (string) The string to check for a URL.
    • to_return: (string) The part to return (e.g., "url").
    • col: (number) The column position to check.

require('mkdnflow').links.transformPath(text)

Transforms the given text according to the default or user-supplied explicit transformation function.

  • Parameters:
    • text: (string) The text to transform.

require('mkdnflow').links.formatLink(text, source, part)

Creates a formatted link with whatever is provided.

  • Parameters:
    • text: (string) The link text.
    • source: (string) The link source.
    • part: (integer|nil) The specific part of the link to return.
      • nil: () Return the entire link.
      • 1: () Return the text part of the link.
      • 2: () Return the source part of the link.

Link and path handling

require('mkdnflow').paths.moveSource()

Moves the source file of a link to a new location, updating the link accordingly.

require('mkdnflow').paths.handlePath(path, anchor)

Handles all 'following' behavior for a given path, potentially opening it or performing other actions based on the type.

  • Parameters:
    • path: (string) The path to handle.
    • anchor: (string|nil) Optional anchor within the path.

require('mkdnflow').paths.formatTemplate(timing, template)

Formats the new file template based on the specified timing (before or after buffer creation). If this is called once with 'before' timing, the output can be captured and passed back in with 'after' timing to perform different substitutions before and after a new buffer is opened.

  • Parameters:
    • timing: (string) "before" or "after" specifying when to perform the formatting.
      • 'before': () Perform the template formatting before the new buffer is opened.
      • 'after': () Perform the template formatting after the new buffer is opened.
    • template: (string|nil) The template to format. If not provided, the default new file template is used.

require('mkdnflow').paths.updateDirs()

Updates the working directory after switching notebooks or notebook folders (if nvim_wd_heel is true).

require('mkdnflow').paths.pathType(path, anchor)

Determines the type of the given path (file, directory, URL, etc.).

  • Parameters:
    • path: (string) The path to check.
    • anchor: (string|nil) Optional anchor within the path.

require('mkdnflow').paths.transformPath(path)

Transforms the given path based on the plugin's configuration and transformations.

  • Parameters:
    • path: (string) The path to transform.

Buffer navigation

require('mkdnflow').buffers.goBack()

Navigates to the previously opened buffer.

require('mkdnflow').buffers.goForward()

Navigates to the next buffer in the history.

Cursor movement

require('mkdnflow').cursor.goTo(pattern, reverse)

Moves the cursor to the next or previous occurrence of the specified pattern.

  • Parameters:
    • pattern: (string|table) The Lua regex pattern(s) to search for.
    • reverse: (boolean) If true, search backward.
require('mkdnflow').cursor.goTo("%[.*%](.*)", false) -- Go to next markdown link

require('mkdnflow').cursor.toNextLink()

Moves the cursor to the next link in the file.

require('mkdnflow').cursor.toPrevLink()

Moves the cursor to the previous link in the file.

require('mkdnflow').cursor.toHeading(anchor_text, reverse)

Moves the cursor to the specified heading.

  • Parameters:
    • anchor_text: (string|nil) The text of the heading to move to, transformed in the way that is expected for an anchor link to a heading. If nil, the function will go to the next closest heading.
    • reverse: (boolean) If true, search backward.

require('mkdnflow').cursor.toId(id, starting_row)

Moves the cursor to the specified ID in the file.

  • Parameters:
    • id: (string) The Pandoc-style ID attribute (in a tagged span) to move to.
    • starting_row: (number|nil) The row to start the search from. If not provided, the cursor row will be used.

Cursor-aware manipulations

require('mkdnflow').cursor.changeHeadingLevel(change)

Increases or decreases the importance of the heading under the cursor by adjusting the number of hash symbols.

  • Parameters:
    • change: (string) "increase" to decrease hash symbols (increasing importance), "decrease" to add hash symbols, decreasing importance.

require('mkdnflow').cursor.yankAsAnchorLink(full_path)

Yanks the current line as an anchor link, optionally including the full file path depending on the value of the argument.

  • Parameters:
    • full_path: (boolean) If true, includes the full file path.

List management

require('mkdnflow').lists.newListItem({ carry, above, cursor_moves, mode_after, alt })

Inserts a new list item with various customization options such as whether to carry content from the current line, position the new item above or below, and the editor mode after insertion.

  • Parameters:
    • carry: (boolean) Whether to carry content following the cursor on the current line into the new line/list item.
    • above: (boolean) Whether to insert the new item above the current line.
    • cursor_moves: (boolean) Whether the cursor should move to the new line.
    • mode_after: (string) The mode to enter after insertion ("i" for insert, "n" for normal).
    • alt: (string) Which key(s) to feed if this is called while the cursor is not on a line with a list item. Must be a valid string for the first argument of vim.api.nvim_feedkeys.

require('mkdnflow').lists.hasListType(line)

Checks if the given line is part of a list.

  • Parameters:
    • line: (string) The (content of the) line to check. If not provided, the current cursor line will be used.

require('mkdnflow').lists.toggleToDo(opts)

Toggles (rotates) the status of a to-do list item based on the provided options.

  • Parameters:
    • opts: (table) Options for toggling.

require('mkdnflow').lists.updateNumbering(opts, offset)

Updates the numbering of the list items in the current list.

  • Parameters:
    • opts: (table) Options for updating numbering.
      • opts[1]: (integer) The number to start the current ordered list with.
    • offset: (number) The offset to start numbering from. Defaults to 0 if not provided.

[!WARNING] require('mkdnflow').lists.toggleToDo(opts) is deprecated. For convenience, it is now a wrapper function that calls its replacement, require('mkdnflow').to_do.toggle_to_do(opts). See require('mkdnflow').to_do.toggle_to_do() for details.

To-do list management

require('mkdnflow').to_do.toggle_to_do()

Toggle (rotate) to-do statuses for a to-do item under the cursor.

require('mkdnflow').to_do.get_to_do_item(line_nr)

Retrieves a to-do item from the specified line number.

  • Parameters:
    • line_nr: (number) The line number to retrieve the to-do item from. If not provided, defaults to the cursor line number.

require('mkdnflow').to_do.get_to_do_list(line_nr)

Retrieves the entire to-do list of which the specified line number is an item/member.

  • Parameters:
    • line_nr: (number) The line number to retrieve the to-do list from. If not provided, defaults to the cursor line number.

require('mkdnflow').to_do.hl.init()

Initializes highlighting for to-do items. If highlighting is enabled in your configuration, you should never need to use this.

Table management

require('mkdnflow').tables.formatTable()

Formats the current table, ensuring proper alignment and spacing.

require('mkdnflow').tables.addRow(offset)

Adds a new row to the table at the specified offset.

  • Parameters:
    • offset: (number) The position (relative to the current cursor row) in which to insert the new row. Defaults to 0, in which case a new row is added beneath the current cursor row. An offset of -1 will result in a row being inserted above the current cursor row; an offset of 1 will result in a row being inserted after the row following the current cursor row; etc.

require('mkdnflow').tables.addCol(offset)

Adds a new column to the table at the specified offset.

  • Parameters:
    • offset: (number) The position (relative to the table column the cursor is currently in) to insert the new column. Defaults to 0, in which case a new column is added after the current cursor table column. An offset of -1 will result in a column being inserted before the current cursor table column; an offset of 1 will result in a column being inserted after the column following the current cursor table column; etc.

require('mkdnflow').tables.newTable(opts)

Creates a new table with the specified options.

  • Parameters:
    • opts: (table) Options for the new table (number of columns and rows).
      • opts[1]: (integer) The number of columns the table should have
      • opts[2]: (integer) The number of rows the table should have (excluding the header row)
      • opts[3]: (string) Whether to include a header for the table or not ('noh' or 'noheader': Don't include a header row; nil: Include a header)

require('mkdnflow').tables.alignCol(alignment)

Sets the alignment of the table column under the cursor and reformats the table. Works with both pipe tables and Pandoc grid tables.

  • Parameters:
    • alignment: (string) The alignment to set for the column. One of 'left', 'right', 'center', or 'default'.

require('mkdnflow').tables.isPartOfTable(text, linenr)

Guesses as to whether the specified text is part of a table.

  • Parameters:
    • text: (string) The content to check for table membership.
    • linenr: (number) The line number corresponding to the text passed in.

require('mkdnflow').tables.moveToCell(row_offset, cell_offset)

Moves the cursor to the specified cell in the table.

  • Parameters:
    • row_offset: (number) The difference between the current row and the target row. 0, for instance, will target the current row.
    • cell_offset: (number) The difference between the current table column and the target table column. 0, for instance, will target the current column.

Folds

require('mkdnflow').folds.getHeadingLevel(line)

Gets the heading level of the specified line.

  • Parameters:
    • line: (string) The line content to get the heading level for. Required.

require('mkdnflow').folds.foldSection()

Folds the current section based on markdown headings.

require('mkdnflow').folds.unfoldSection()

Unfolds the current section.

Yaml blocks

require('mkdnflow').yaml.hasYaml()

Checks if the current buffer contains a YAML header block.

require('mkdnflow').yaml.ingestYamlBlock(start, finish)

Parses and ingests a YAML block from the specified range.

  • Parameters:
    • start: (number) The starting line number.
    • finish: (number) The ending line number.

Bibliography

require('mkdnflow').bib.handleCitation(citation)

Handles a citation, potentially linking to a bibliography entry or external source.

  • Parameters:
    • citation: (string) The citation key to handle. Required.

Statusline components

The following functions are designed for use in statusline, winbar, or tabline components. They return information about the plugin's current state and are safe to call frequently (e.g. on every statusline redraw).

require('mkdnflow').getNotebook()

Returns information about the current notebook (root directory), or nil if no root is active (e.g. when path_resolution.primary is not 'root', or no root marker was found). Designed for use in statusline, winbar, or tabline components.

Example: lualine component

require('lualine').setup({
    sections = {
        lualine_x = {
            {
                function()
                    local nb = require('mkdnflow').getNotebook()
                    return nb and nb.name or ''
                end,
                cond = function()
                    return require('mkdnflow').getNotebook() ~= nil
                end,
            },
        },
    },
})

Example: custom statusline

vim.o.statusline = '%f %m%=%{%v:lua.require("mkdnflow").getNotebook() '
    .. '~= nil and " " .. require("mkdnflow").getNotebook().name or ""%}'

๐Ÿค Contributing

See CONTRIBUTING.md

๐Ÿ”ข Version information

Mkdnflow uses Semantic Versioning. Version numbers follow the format MAJOR.MINOR.PATCH:

  • MAJOR: Incompatible API or configuration changes
  • MINOR: New functionality in a backward-compatible manner
  • PATCH: Backward-compatible bug fixes

For a detailed history of changes, see CHANGELOG.md.

๐Ÿ”— Related projects

Competition

Complementary plugins