• Stars
    star
    3,037
  • Rank 14,853 (Top 0.3 %)
  • Language
    Lua
  • License
    Apache License 2.0
  • Created over 2 years ago
  • Updated 6 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Obsidian 🤝 Neovim

obsidian.nvim

A Neovim plugin for writing and navigating an Obsidian vault, written in Lua.

Built for people who love the concept of Obsidian -- a simple, markdown-based notes app -- but love Neovim too much to stand typing characters into anything else.

This plugin is not meant to replace Obsidian, but to complement it. My personal workflow involves writing Obsidian notes in Neovim using this plugin, while viewing and reading them using the Obsidian app. That said, this plugin stands on its own as well. You don't necessarily need to use it alongside the Obsidian app.

Table of contents

Features

  • ▶️ Autocompletion for note references via nvim-cmp (triggered by typing [[)
  • 🏃 Optional passthrough for gf to enable Obsidian links without interfering with existing functionality
  • 💅 Additional markdown syntax highlighting and concealing for references

Commands

  • :ObsidianOpen to open a note in the Obsidian app. This command has one optional argument: the ID, path, or alias of the note to open. If not given, the note corresponding to the current buffer is opened.
  • :ObsidianNew to create a new note. This command has one optional argument: the title of the new note.
  • :ObsidianQuickSwitch to quickly switch to another notes in your vault, searching by its name using fzf.vim, fzf-lua or telescope.nvim.
  • :ObsidianFollowLink to follow a note reference under the cursor.
  • :ObsidianBacklinks for getting a location list of references to the current buffer.
  • :ObsidianToday to create a new daily note.
  • :ObsidianYesterday to open (eventually creating) the daily note for the previous working day.
  • :ObsidianTemplate to insert a template from the templates folder, selecting from a list using telescope.nvim or one of the fzf alternatives. See "using templates" for more information.
  • :ObsidianSearch to search for notes in your vault using ripgrep with fzf.vim, fzf-lua or telescope.nvim. This command has one optional argument: a search query to start with.
  • :ObsidianLink to link an in-line visual selection of text to a note. This command has one optional argument: the ID, path, or alias of the note to link to. If not given, the selected text will be used to find the note with a matching ID, path, or alias.
  • :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.

Demo

general_demo.mp4

Setup

Requirements

  • NeoVim >= 0.8.0 (this plugin uses vim.fs which was only added in 0.8).
  • If you want completion and search features (recommended) you'll also need ripgrep to be installed and on your $PATH. See ripgrep#installation for install options.

Search functionality (e.g. via the :ObsidianSearch and :ObsidianQuickSwitch commands) also requires telescope.nvim or one of the fzf alternatives (see plugin dependencies below).

Install and configure

To configure obsidian.nvim you just need to call require("obsidian").setup({ ... }) with the desired options. Here are some examples using different plugin managers. The full set of plugin dependencies and configuration options are listed below.

Using lazy.nvim

return {
  "epwalsh/obsidian.nvim",
  lazy = true,
  event = { "BufReadPre path/to/my-vault/**.md" },
  -- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand':
  -- event = { "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/**.md" },
  dependencies = {
    -- Required.
    "nvim-lua/plenary.nvim",

    -- see below for full list of optional dependencies 👇
  },
  opts = {
    dir = "~/my-vault",  -- no need to call 'vim.fn.expand' here

    -- see below for full list of options 👇
  },
}

Using packer.nvim

use({
  "epwalsh/obsidian.nvim",
  requires = {
    -- Required.
    "nvim-lua/plenary.nvim",

    -- see below for full list of optional dependencies 👇
  },
  config = function()
    require("obsidian").setup({
      dir = "~/my-vault",

      -- see below for full list of options 👇
    })
  end,
})

Plugin dependencies

The only required plugin dependency is plenary.nvim, but there are a number of optional dependencies that enhance the obsidian.nvim experience:

If you choose to use any of these you should include them in the "dependencies" or "requires" field of the obsidian.nvim plugin spec for your package manager.

Configuration options

This is a complete list of all of the options that can be passed to require("obsidian").setup():

{
  -- Required, the path to your vault directory.
  dir = "~/my-vault",

  -- Optional, if you keep notes in a specific subdirectory of your vault.
  notes_subdir = "notes",

  -- Optional, set the log level for obsidian.nvim. This is an integer corresponding to one of the log
  -- levels defined by "vim.log.levels.*" or nil, which is equivalent to DEBUG (1).
  log_level = vim.log.levels.DEBUG,

  daily_notes = {
    -- Optional, if you keep daily notes in a separate directory.
    folder = "notes/dailies",
    -- Optional, if you want to change the date format for daily notes.
    date_format = "%Y-%m-%d"
  },

  -- Optional, completion.
  completion = {
    -- If using nvim-cmp, otherwise set to false
    nvim_cmp = true,
    -- Trigger completion at 2 chars
    min_chars = 2,
    -- Where to put new notes created from completion. Valid options are
    --  * "current_dir" - put new notes in same directory as the current buffer.
    --  * "notes_subdir" - put new notes in the default notes subdirectory.
    new_notes_location = "current_dir",

    -- Whether to add the output of the node_id_func to new notes in autocompletion.
    -- E.g. "[[Foo" completes to "[[foo|Foo]]" assuming "foo" is the ID of the note.
    prepend_note_id = true
  },

  -- Optional, key mappings.
  mappings = {
    -- Overrides the 'gf' mapping to work on markdown/wiki links within your vault.
    ["gf"] = require("obsidian.mapping").gf_passthrough(),
  },

  -- Optional, customize how names/IDs for new notes are created.
  note_id_func = function(title)
    -- Create note IDs in a Zettelkasten format with a timestamp and a suffix.
    -- In this case a note with the title 'My new note' will given an ID that looks
    -- like '1657296016-my-new-note', and therefore the file name '1657296016-my-new-note.md'
    local suffix = ""
    if title ~= nil then
      -- If title is given, transform it into valid file name.
      suffix = title:gsub(" ", "-"):gsub("[^A-Za-z0-9-]", ""):lower()
    else
      -- If title is nil, just add 4 random uppercase letters to the suffix.
      for _ = 1, 4 do
        suffix = suffix .. string.char(math.random(65, 90))
      end
    end
    return tostring(os.time()) .. "-" .. suffix
  end,

  -- Optional, set to true if you don't want obsidian.nvim to manage frontmatter.
  disable_frontmatter = false,

  -- Optional, alternatively you can customize the frontmatter data.
  note_frontmatter_func = function(note)
    -- This is equivalent to the default frontmatter function.
    local out = { id = note.id, aliases = note.aliases, tags = note.tags }
    -- `note.metadata` contains any manually added fields in the frontmatter.
    -- So here we just make sure those fields are kept in the frontmatter.
    if note.metadata ~= nil and require("obsidian").util.table_length(note.metadata) > 0 then
      for k, v in pairs(note.metadata) do
        out[k] = v
      end
    end
    return out
  end,

  -- Optional, for templates (see below).
  templates = {
    subdir = "templates",
    date_format = "%Y-%m-%d-%a",
    time_format = "%H:%M",
  },

  -- Optional, by default when you use `:ObsidianFollowLink` on a link to an external
  -- URL it will be ignored but you can customize this behavior here.
  follow_url_func = function(url)
    -- Open the URL in the default web browser.
    vim.fn.jobstart({"open", url})  -- Mac OS
    -- vim.fn.jobstart({"xdg-open", url})  -- linux
  end,

  -- Optional, set to true if you use the Obsidian Advanced URI plugin.
  -- https://github.com/Vinzent03/obsidian-advanced-uri
  use_advanced_uri = true,

  -- Optional, set to true to force ':ObsidianOpen' to bring the app to the foreground.
  open_app_foreground = false,

  -- Optional, by default commands like `:ObsidianSearch` will attempt to use
  -- telescope.nvim, fzf-lua, and fzf.nvim (in that order), and use the
  -- first one they find. By setting this option to your preferred
  -- finder you can attempt it first. Note that if the specified finder
  -- is not installed, or if it the command does not support it, the
  -- remaining finders will be attempted in the original order.
  finder = "telescope.nvim",

  -- Optional, determines whether to open notes in a horizontal split, a vertical split,
  -- or replacing the current buffer (default)
  -- Accepted values are "current", "hsplit" and "vsplit"
  open_notes_in = "current"
}

Notes on configuration

Completion

obsidian.nvim will set itself up as an nvim-cmp source automatically when you enter a markdown buffer within your vault directory, you do not need to specify this plugin as a cmp source manually.

Syntax highlighting

If you use vim-markdown you'll probably want to disable its frontmatter syntax highlighting (vim.g.vim_markdown_frontmatter = 1) which I've found doesn't work very well.

If you're using nvim-treesitter and not vim-markdown, you'll probably want to enable additional_vim_regex_highlighting for markdown to benefit from obsidian.nvim's extra syntax improvements:

require("nvim-treesitter.configs").setup({
  ensure_installed = { "markdown", "markdown_inline", ... },
  highlight = {
    enable = true,
    additional_vim_regex_highlighting = { "markdown" },
  },
})

Note naming and location

The notes_subdir and note_id_func options are not mutually exclusive. You can use them both. For example, using a combination of both of the above settings, a new note called "My new note" will assigned a path like notes/1657296016-my-new-note.md.

gf passthrough

If you want the gf passthrough functionality but you've already overridden the gf keybinding, just change your gf mapping definition to something like this:

vim.keymap.set("n", "gf", function()
  if require("obsidian").util.cursor_on_markdown_link() then
    return "<cmd>ObsidianFollowLink<CR>"
  else
    return "gf"
  end
end, { noremap = false, expr = true })

Then make sure to comment out the gf keybinding in your obsidian.nvim config:

mappings = {
  -- ["gf"] = require("obsidian.mapping").gf_passthrough(),
},

Or alternatively you could map obsidian.nvim's follow functionality to a different key:

mappings = {
  ["fo"] = require("obsidian.mapping").gf_passthrough(),
},

Using templates

To insert a template, run the command :ObsidianTemplate. This will open 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

{
  -- other fields ...

  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:

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

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

# Configuring Neovim

Date created: 2023-03-01-Wed

above the cursor position.

Known Issues

Configuring vault directory behind a link

If you are having issues with commands like ObsidianOpen, ensure that your vault is configured to use an absolute path rather than a link. If you must use a link in your configuration, make sure that the name of the vault is present in the file path of the link. For example:

Vault: ~/path/to/vault/parent/obsidian/
Link: ~/obsidian OR ~/parent

Contributing

Please read the CONTRIBUTING guide before submitting a pull request.

More Repositories

1

pomo.nvim

🆕 ⏱️ A simple, customizable pomodoro timer for Neovim, written in Lua, with nvim-notify, lualine, and telescope integrations
Lua
127
star
2

nlp-models

NLP research experiments, built on PyTorch within the AllenNLP framework.
Python
90
star
3

pytorch-crf

🔥 A PyTorch implementation of a Bi-LSTM CRF with character-level features
Python
64
star
4

rust-dl-webserver

🦀 Example of serving deep learning models in Rust with batched prediction
Rust
33
star
5

rust-cached-path

🦀 Rust utility for accessing both local and remote files through a unified interface
Rust
27
star
6

batched-fn

🦀 Rust server plugin for deploying deep learning models with batched prediction
Rust
17
star
7

dotfiles

Personal dotfiles (alacritty, tmux, neovim, fish)
Lua
16
star
8

python-registrable

Python module for registering and instantiating classes by name
Python
13
star
9

docker-celery

🐳 A Docker image for a simple Python Celery application
Python
10
star
10

allennlp-manager

[WORK IN PROGRESS] Your manager for AllenNLP experiments
Python
7
star
11

CSA

C++ header-only library for Coupled Simulated Annealing
C++
6
star
12

nlpete

Simple, self-contained, PyTorch NLP models.
Python
4
star
13

docker-shiny

🐳 A Docker image for an R Shiny server with username + password authentication 🔐
HTML
4
star
14

allennlp-issue-4428

Python
1
star
15

check-links

A command-line utility for finding unreachable links in your crate's documentation
Rust
1
star
16

allennlp-hacks-project

1
star
17

NvimAutoheader

Automatic header management for Neovim (no longer maintained)
Python
1
star