Skip to content

Commit

Permalink
Added support for templates (#98)
Browse files Browse the repository at this point in the history
* added template insert feature

* added support for {{time}} and some error checking

* updated CHANGELOG with templates

* updated README.md with templates

* finalised template command

* satisfying the linter

* made note title detection more robust

* Added support for fzf-lua in the ObsidianTemplate command

* Templates supports fzf fallback

* Updated CHANGELOG and README to include templates

typo in readme

* Update README.md

Co-authored-by: Pete <epwalsh10@gmail.com>

* Update README.md

Co-authored-by: Pete <epwalsh10@gmail.com>

* Update README.md

Co-authored-by: Pete <epwalsh10@gmail.com>

* template {{title}} uses Note class for file name

Co-authored-by: Pete <epwalsh10@gmail.com>

* Update README.md

Co-authored-by: Pete <epwalsh10@gmail.com>

* insertion position of templates behaves like Obsidian.md

* style

---------

Co-authored-by: Pete <epwalsh10@gmail.com>
  • Loading branch information
s-cassidy and epwalsh authored Mar 13, 2023
1 parent 5ecf680 commit 247e00d
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased


### Added

- Added `:ObsidianTemplate` to insert a template, configurable using a `templates` table passed to `setup()`.

## [v1.8.0](https://github.com/epwalsh/obsidian.nvim/releases/tag/v1.8.0) - 2023-02-16

### Changed
Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Built for people who love the concept of Obsidian -- a simple, markdown-based no
- `:ObsidianLinkNew` to create a new note and link it to an in-line visual selection of text.
This command has one optional argument: the title of the new note. If not given, the selected text will be used as the title.
- `:ObsidianFollowLink` to follow a note reference under the cursor.
- `:ObsidianTemplate` to insert a template from the templates folder, selecting from a list using [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) or one of the `fzf` alternatives.

## Setup

Expand Down Expand Up @@ -168,6 +169,39 @@ require("obsidian").setup({
})
```

#### Templates support

To insert a template, run the command `:ObsidianTemplate`. This will open [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) or one of the `fzf` alternatives and allow you to select a template from the templates folder. Select a template and hit `<CR>` to insert. Substitution of `{{date}}`, `{{time}}`, and `{{title}}` is supported.

For example, with the following configuration

```lua
require("obsidian").setup({
dir = "~/my-vault",
templates = {
subdir = "my-templates-folder",
date_format = "%Y-%m-%d-%a",
time_format = "%H:%M"
}
})
```

and the file `~/my-vault/my-templates-folder/note template.md`:

```markdown
# {{title}}
Date created: {{date}}
```

creating the note `Configuring Neovim.md` and executing `:ObsidianTemplate` will insert
```markdown
# Configuring Neovim

Date created: 2023-03-01-Wed
```

above the cursor position.

#### Using nvim-treesitter

If you're using [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter/blob/master/README.md) and not [vim-markdown](https://github.com/preservim/vim-markdown), you'll probably want to enable `additional_vim_regex_highlighting` for markdown to benefit from Obsidian.nvim's extra syntax improvements:
Expand Down
116 changes: 116 additions & 0 deletions lua/obsidian/command.lua
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,121 @@ command.search = function(client, data)
end
end

--- Insert a template
---
---@param client obsidian.Client
---@param data table
command.insert_template = function(client, data)
if not client.opts.templates.subdir then
echo.err "No templates folder defined in setup()"
return
end

local templates_dir = Path:new(client.dir) / client.opts.templates.subdir
if not templates_dir:is_dir() then
echo.err(string.format("%s is not a valid directory for templates", templates_dir))
return
end

-- We need to get these upfront otherwise
-- Telescope hijacks the current window
local buf = vim.api.nvim_win_get_buf(0)
local win = vim.api.nvim_get_current_win()
local row, col = unpack(vim.api.nvim_win_get_cursor(win))

local apply_template = function(name)
local template_path = Path:new(templates_dir / name)
local date_format = client.opts.templates.date_format or "%Y-%m-%d"
local time_format = client.opts.templates.time_format or "%H:%M"
local date = tostring(os.date(date_format))
local time = tostring(os.date(time_format))
local title = Note.from_buffer(buf, client.dir):display_name()

local insert_lines = {}
local template_file = io.open(tostring(template_path), "r")
if template_file then
local lines = template_file:lines()
for line in lines do
line = string.gsub(line, "{{date}}", date)
line = string.gsub(line, "{{time}}", time)
line = string.gsub(line, "{{title}}", title)
table.insert(insert_lines, line)
end
template_file:close()
table.insert(insert_lines, "")
end

vim.api.nvim_buf_set_text(buf, row - 1, col, row - 1, col, insert_lines)
local new_row, _ = unpack(vim.api.nvim_win_get_cursor(win))
vim.api.nvim_win_set_cursor(0, { new_row, 0 })
end

-- try with telescope.nvim
local has_telescope, _ = pcall(require, "telescope.builtin")
if has_telescope then
local choose_template = function()
local opts = {
cwd = tostring(templates_dir),
attach_mappings = function(_, map)
map({ "i", "n" }, "<CR>", function(prompt_bufnr)
local template = require("telescope.actions.state").get_selected_entry()
require("telescope.actions").close(prompt_bufnr)
apply_template(template[1])
end)
return true
end,
}
require("telescope.builtin").find_files(opts)
end
choose_template()
return
end

-- try with fzf-lua
local has_fzf_lua, fzf_lua = pcall(require, "fzf-lua")
if has_fzf_lua then
local cmd = vim.tbl_flatten { util.FIND_CMD, { ".", "-name", "'*.md'" } }
cmd = util.table_params_to_str(cmd)
fzf_lua.files {
cmd = cmd,
cwd = tostring(templates_dir),
file_icons = false,
actions = {
["default"] = function(entry)
-- for some reason fzf-lua passes the filename with 6 characters
-- at the start that appear on screen as 2 whitespace characters
-- so we need to start on the 7th character
local template = entry[1]:sub(7)
apply_template(template)
end,
},
}
return
end

-- try with fzf
local has_fzf, _ = pcall(function()
vim.api.nvim_create_user_command("ApplyTemplate", function(path)
-- remove escaped whitespace and extract the file name
local file_path = string.gsub(path.args, "\\ ", " ")
local template = vim.fs.basename(file_path)
apply_template(template)
vim.api.nvim_del_user_command "ApplyTemplate"
end, { nargs = 1, bang = true })

local base_cmd = vim.tbl_flatten { util.FIND_CMD, { tostring(templates_dir), "-name", "'*.md'" } }
base_cmd = util.table_params_to_str(base_cmd)
local fzf_options = { source = base_cmd, sink = "ApplyTemplate" }
vim.api.nvim_call_function("fzf#run", {
vim.api.nvim_call_function("fzf#wrap", { fzf_options }),
})
end)

if not has_fzf then
echo.err "Either telescope.nvim or fzf.vim is required for :ObsidianTemplate command"
end
end

---Quick switch to an obsidian note
---
---@param client obsidian.Client
Expand Down Expand Up @@ -412,6 +527,7 @@ end

local commands = {
ObsidianCheck = { func = command.check, opts = { nargs = 0 } },
ObsidianTemplate = { func = command.insert_template, opts = { nargs = "?" } },
ObsidianToday = { func = command.today, opts = { nargs = 0 } },
ObsidianYesterday = { func = command.yesterday, opts = { nargs = 0 } },
ObsidianOpen = { func = command.open, opts = { nargs = "?" }, complete = command.complete_args },
Expand Down
5 changes: 5 additions & 0 deletions lua/obsidian/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ local config = {}
---@class obsidian.config.ClientOpts
---@field dir string
---@field notes_subdir string|?
---@field templates table|?
---@field templates.subdir string
---@field templates.date_format string
---@field templates.time_format string
---@field note_id_func function|?
---@field note_frontmatter_func function|?
---@field disable_frontmatter boolean|?
Expand All @@ -19,6 +23,7 @@ config.ClientOpts.default = function()
return {
dir = vim.fs.normalize "./",
notes_subdir = nil,
templates = nil,
note_id_func = nil,
note_frontmatter_func = nil,
disable_frontmatter = false,
Expand Down

0 comments on commit 247e00d

Please sign in to comment.