Skip to content

pysan3/neorg-templates

Repository files navigation

Neorg Templates

* This README is generated from ./README.norg.

Demo

templates-demo

  1. :Neorg templates fload journal (fload = force load: Overwrite buffer without asking)

  2. AUTHOR, TODAY... will be automatically filled.

  3. {TITLE_INPUT} is an input_node with {TITLE} being the default placeholder text.

  • Changes the title.
  1. Cursor at {WEATHER}.
  • Choosing between options provided from LuaSnip's choice_node.
  1. Cursor ends up at {CURSOR} position.

* {CURSOR} is a magic keyword, which specifies the last position of the cursor. More about magic keywords in Magic Keywords.

* You can use the same keyword in multiple places to insert the same content. Not possible in bare LuaSnip.

Usage

Template Norg Files

So, the plugin works like this. First you create a template file with placeholders for dynamic values.

~/.config/nvim/templates/norg/journal.norg

@document.meta
title: {TITLE_INPUT}
description:
authors: {AUTHOR}
categories:
created: {TODAY}
updated: {TODAY}
version: 1.0.0
@end

* {TITLE_INPUT}
  Weather: {WEATHER}
  {{:{YESTERDAY}:}}[Yesterday] - {{:{TOMORROW}:}}[Tomorrow]

** Daily Review
   - {CURSOR}

When you load this plugin, you have the command: :Neorg templates load journal.

This overwrites the current buffer and substitutes it with the content the template file. This behavior can be customized. More in Subcommands.

Autofill with LuaSnip

But do you see the {AUTHOR} placeholder in the template file?

Yes, it automatically substitutes those placeholders with the power of LuaSnip!

And since it is a snippet, you can also use input_node, choice_node and all other useful nodes inside your template file! I've also added some useful snippets by default so you can use them out of the box.

Installation

-- neorg.lua
local M = {
  "nvim-neorg/neorg",
  ft = "norg",
  dependencies = {
    { "pysan3/neorg-templates", dependencies = { "L3MON4D3/LuaSnip" } }, -- ADD THIS LINE
  },
}

Configuration

-- See {*** Options} for more options
M.config = function ()
  require("neorg").setup({
    load = {
      ["external.templates"] = {
        -- templates_dir = vim.fn.stdpath("config") .. "/templates/norg",
        -- default_subcommand = "add", -- or "fload", "load"
        -- keywords = { -- Add your own keywords.
        --   EXAMPLE_KEYWORD = function ()
        --     return require("luasnip").insert_node(1, "default text blah blah")
        --   end,
        -- },
        -- snippets_overwrite = {},
      }
    }
  })
end

Options

Find details here: module.config.public

templates_dir

  • string | string[]

Path to the directories where the template files are stored.

  • Default: vim.fn.stdpath("config") .. "/templates/norg"

    • Most likely: ~/.config/nvim/templates/norg
  • Only looks for 1 depth.

  • You may also provide multiple paths to directories with a table.

    • templates_dir = { "~/temp_dir1/", "~/temp_dir2/" }

default_subcommand

  • string

Default action to take when only filename is provided.

More details are explained in Subcommands

keywords

  • {KEY: <snippet_node>} | {KEY: fun(...) -> <snippet_node>}

Define snippets to be called in placeholders. Kyes are advised to be ALL_CAPITALIZED.

  • Examples are provided in ./lua/neorg/modules/external/templates/default_snippets.lua

  • KEY should be the name of the placeholder

  • Value should be a snippet node or a function that returns a snippet node.

    • For example TITLE_INPUT needs to run M.file_title() right before the expansion. Therefore, it is defined with a function.
  • First argument of nodes (pos) should always be 1.

    • e.g. i(<pos>, "default text"), c(<pos>, { t("choice1"), t("choice2") })

    • The order of jumping is dynamically calculated after loading the template, so you cannot specify with integer here.

snippets_overwrite

  • table<string, any>

Overwrite any field of ./lua/neorg/modules/external/templates/default_snippets.lua.

{
  snippets_overwrite = {
    date_format = [[%a, %d %b]] -- Wed, 1 Nov (norg date format)
  }
}

Subcommands

All command in this plugin takes the format of :Neorg templates <subcmd> <templ_name>.

  • <subcmd>: Sub command. Basically defines how to load the template file.

  • <templ_name>: Name of template file to load.

    • If you want to load <config.templates_dir>/journal.norg, call with journal.

default_subcommand Option

Read default_subcommand for more information. If you ommit <subcmd> and call this plugin with :Neorg templates <templ_name>, the behavior depends on this config option.

You can choose from the functions below.

add

Add (append) template file content to the current cursor position.

fload

Force-load template file. Overwrites content of current file and replace it with LuaSnip.

load

Load. Similar to fload but asks for confirmation before deleting the buffer content.

Magic Keywords

Magic keywords take the format of {CURSOR}, same as a placeholder, but has a special meaning.

{CURSOR}

The cursor position when the snippet ends.

  • Same as i(0)

  • If {CURSOR} is not found, the cursor will be at the end of the file.

{METADATA}

Placeholder for metadata. Generated by core.norg.esupports.metagen.

  • Metadata will be simply substituted with this keyword.

  • You cannot edit or control the output with this plugin.

  • This keyword MUST BE at the first line of the template file.

  • In order to :Neorg inject-metadata blog-post, use {METADATA:blog-post}

Tips and Tricks

Ask for a Snippet

If you are not familiar with LuaSnip, post what kind of snippet you want! Maybe someone can help you out.

Post it in the Discussions with the category `✂️ Ask for a Snippet `.

Autoload with :Neorg journal

nvim-neorg/neorg#714 (comment)

vim.api.nvim_create_autocmd("BufNewFile", {
  command = "Neorg templates load journal",
  pattern = { neorg_dir .. "journal/*.norg" },
})

Builtin Snippets

I will not list all builtin snippets but some useful ones that might need a bit of explanation. Please read default_snippets.lua for the whole list.

TITLE, TITLE_INPUT

Inserts the file name. TITLE_INPUT would be an insert node with default text being TITLE.

TODAY, YESTERDAY, TOMORROW + _OF_FILENAME, _OF_FILETREE

TODAY_OF_FILENAME will insert the date by parsing the filename. OF_FILENAME is useful when journal.strategy == "flat". So, if the filename is 2023-01-01.norg, YESTERDAY_OF_FILENAME => 2022-12-31.

Similarly, TODAY_OF_FILETREE is useful when journal.strategy == "nested". Ex: if the filename is /path/to/2023/01/01.norg, YESTERDAY_OF_FILETREE => 2022-12-31.

For more fine control, read 2. Build you own snippet key.

Useful Templates

More examples welcome! Create a PR to update the README.

Contribution

LICENSE

All files in this repository without annotation are licensed under the GPL-3.0 license as detailed in LICENSE.

FAQs

Q. Do you plan to support other snippet engines like Ultisnip?

No. This plugin heavily depends on luasnip and I do not have the time and interest to support other ones.

There is an open issue discussing for a pure lua solution using autocmds.

There are other neovim template plugins (not just for norg) that do not depend on luasnip. Here are some examples.

Q. I want to use this plugin for other filetypes!

Most of the code related to luasnip and template loading mechanism is written inside snippet_handler.lua which does not rely on neorg at all. The entry point is mostly load_template_at_curpos(content, fs_name).

For usage example, read the implementation of subcommands here.

You are free to fork / copy-paste this code as long as you respect the LICENSE. I just don't have the motivation to compete against other template plugins listed above.

Q. Where can I contact you?

I'll be at neorg's discord server with the username @pysan3, so feel free to ping me there.

Q. Where can I donate?

Thank you very much but I'd be more than happy with a star for this repo. Buy yourself a cup of coffee and have a nice day 😉