obsidian.nvim icon indicating copy to clipboard operation
obsidian.nvim copied to clipboard

Enable users to create custom commands opening new notes with templates

Open escapingSamsara opened this issue 1 year ago • 6 comments

🚀 The feature, motivation and pitch

I wanted to create a note template which acts as my journaling template, since the command available for a daily note is ObsidianToday, which is the only command that creates a note with todays date as title, i was wondering if it is possible to create commands like lets call it "OpenNewJournalEntry", that offers the same feature as ObsidianToday, but enables adding of custom tags, and a custom template

Alternatives

I was able to create a command in lua as keymap, but it doesnt work with obsidian.nvim together (especially opening the note like ObsidianNew or ObsidianToday does, in the obsidian vault working directory), this is the function i came up with, but i would love a connection with obsidian.nvim, to open it up in the correct folder after creating it.

 vim.api.nvim_create_user_command("OpenDailyNote", function()
        local date_format = "%Y-%m-%d"
        local obsidian_vault_dir = "~/Documents/second-brain/"
        local daily_folder = obsidian_vault_dir .. "NOTES/JOURNAL"
        local template_path = obsidian_vault_dir .. "Templates/JournalTemplate.md"
        local date = os.date(date_format)
        local note_path = vim.fn.expand(daily_folder .. "/" .. date .. ".md")
        local id = tostring(os.time())
        if vim.fn.filereadable(note_path) == 0 then
          vim.fn.mkdir(vim.fn.expand(daily_folder), "p")
          local front_matter = table.concat({
            "---",
            "id: " .. id,
            "title: " .. date,
            "tags: [journaling]",
            "---",
            "",
          }, "\n")
          local file = io.open(note_path, "w")
          file:write(front_matter)
          if template_path and #template_path > 0 then
            local template = io.open(template_path, "r")
            if template then
              local content = template:read("*all")
              file:write(content)
              template:close()
            end
          end
          file:close()
        end
      end, {}),

Additional context

No response

escapingSamsara avatar Feb 20 '24 23:02 escapingSamsara

Hey @escapingSamsara, I would like to expand the template functionality at some point to also handle frontmatter, but in the meantime you could probably accomplish this elegantly using obsidian.nvim's Lua API. As an example, I have similar use case where I programmatically create notes corresponding to research papers. This the command I wrote to do that:

vim.api.nvim_create_user_command("NewPaper", function(ev)
  local obsidian = require "obsidian"

  ---@type obsidian.Client
  local client = obsidian.get_client()
  local corpus_id = assert(tonumber(ev.args))

  -- Get paper metadata.
  local data = get_paper_metadata(corpus_id)
  local note_title = data.title .. " (paper)"

  -- Create new note and add metadata.
  local note = client:new_note(note_title)
  note:add_tag "paper"
  note:add_alias(data.title .. " (paper)")
  note:add_field("corpus_id", data.corpus_id)
  note:add_field("url", data.url)
  note:add_field("tldr", data.tldr.text)
  note:save()

  -- Insert link to the note.
  insert_text(client:format_link(note, { label = note_title }))
end, { nargs = 1 })



------ Util functions ------

local get_paper_metadata = function(corpus_id)
  local curl = require "plenary.curl"

  local response = curl.get {
    url = string.format("https://api.semanticscholar.org/graph/v1/paper/CorpusId:%s?fields=tldr,title,url", corpus_id),
    accept = "application/json",
  }
  assert(response.status == 200)
  local data = vim.json.decode(response.body)
  data.corpus_id = corpus_id
  return data
end

local insert_text = function(text)
  local curpos = vim.fn.getcurpos()
  local line_num, line_col = curpos[2], curpos[3]

  -- Convert text to lines table so we can handle multi-line strings.
  local lines = {}
  for line in text:gmatch "[^\r\n]+" do
    lines[#lines + 1] = line
  end

  vim.api.nvim_buf_set_text(0, line_num - 1, line_col, line_num - 1, line_col, lines)
end

epwalsh avatar Feb 21 '24 00:02 epwalsh

hey @epwalsh thanks so much for your quick response ! that surely would be awesome !

I was tweaking around a little bit more and used your provided snippet as template (thanks for sharing it!)

i implemented this now in my keymaps.lua file. the function gets called and a new note gets created in my obsidian-vault directory. whats weird tho (and i hope you might know whats going on here) is, i created a Template/JournalTemplate.md file... when i use the cmd "JournalEntry" now, a new note gets created in my vault exactly how i want it, BUT:

  1. it doesnt redirect me (focus) to the obsidian-vault and the note, it just creates it
  2. and secondly, whats absolutely something i cant explain (although something i actually would want), it uses the template file... but why ? it isnt declared anywhere in the snippet.

here is the function i came up with:

---------------------------------------------------------
vim.api.nvim_create_user_command("JournalEntry", function()
  local date_format = "%Y-%m-%d"
  local obsidian = require("obsidian")
  local date = os.date(date_format)
  ---@type obsidian.Client
  local client = obsidian.get_client()
  --get metadata
  local note_title = tostring(date)

  --create new note and add metadata
  local note = client:new_note(note_title)
  note:add_tag("journaling")
  note:add_alias(note_title .. " journal entry")
  note:save()
end, {})
--------------------------------------------------------------------

do you know why it uses the template stored in my templates dir and also if there is a way to open up the note like it does with the built-in functions like "ObsidianNew" when using the cmd anywhere in a neovim file/directory

escapingSamsara avatar Feb 21 '24 11:02 escapingSamsara

@escapingSamsara I just added a method on main for easily opening a note in a buffer. Update to the latest commit and then add this to the end of your command:

client:open_note(note)

something i cant explain (although something i actually would want), it uses the template file... but why ? it isnt declared anywhere in the snippet.

I'm guessing you have the template set to daily_notes.template in your obsidian.nvim config?

epwalsh avatar Feb 21 '24 16:02 epwalsh

hi !

adding : client:open_note(note) works perfectly ! thanks for adding that in :)

i do have my daily_notes template set to this, but i did not assume that the command uses the ObsidianToday functionality under the hood, which is used by daily_notes i thought. i was thinking the created command would use an empty default note

would you mind explaining why this is different ?

escapingSamsara avatar Feb 21 '24 17:02 escapingSamsara

It recognizes that the note title is in your specified date format for daily notes, so treats it as a daily.

epwalsh avatar Feb 21 '24 19:02 epwalsh

It recognizes that the note title is in your specified date format for daily notes, so treats it as a daily.

alright !! that makes a ton of sense, thanks for the explanation :) i am quite new to lua so i was unsure what might have caused this .

thanks again for the help and the little push for the client command. i really appreciate it. and also: thanks so much for providing such an awesome plugin !

escapingSamsara avatar Feb 21 '24 19:02 escapingSamsara