完美搞定 obsidian.nvim 作为知识库录入工具

安装配置环境

lazyvim + obsidian.nvim 具备了所有想要的功能

美中不足的是 obsidian.nvim 存在了一些问题, 我就做了一些修改, 使其更加完美.

lazyvim 安装完毕后, plugins 下添加 obsidian.lua 文件, 内容如下:

return {
  "bigzhu/obsidian.nvim",
  version = "*",
  lazy = false,
  dependencies = {
    "nvim-lua/plenary.nvim",
    -- https://www.lazyvim.org/extras/coding/nvim-cmp
    -- :LazyExtras 确保安装 coding.nvim-cmp, 这里添加无用
    "hrsh7th/nvim-cmp",
  },
  opts = {
    completion = {
      nvim_cmp = true,
      min_chars = 1,
    },

    preferred_link_style = "markdown",
    workspaces = {
      {
        name = "cheese",
        path = "~/Sync/home/cheese",
      },
    },
    -- Optional, customize how note IDs are generated given an optional title.
    ---@param title string|?
    ---@return string
    ---使用 title 作为 node 的文件名
    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 be 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
        -- vim.notify("first title : " .. title, vim.log.levels.INFO)
        -- 移除末尾的空白字符和不可见字符
        suffix = vim.fn.trim(title)
        -- :gsub(" ", "-")      -- 空格转连字符
        -- :gsub("[\n\r]+", "") -- 移除换行
        -- :gsub("[<>:\"/\\|?*]+", "-") -- 移除非法字符
      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
      -- vim.notify("Final suffix: " .. suffix, vim.log.levels.INFO)
      return suffix
    end,
    -- 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.
    ---@param url string
    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
      -- vim.cmd(':silent exec "!start ' .. url .. '"') -- Windows
      -- vim.ui.open(url) -- need Neovim 0.10.0+
    end,
    -- Optional, by default when you use `:ObsidianFollowLink` on a link to an image
    -- file it will be ignored but you can customize this behavior here.
    ---@param img string
    follow_img_func = function(img)
      -- Get the current buffer's directory as base path
      local current_file = vim.fn.expand("%:p:h")
      local abs_path = current_file .. "/" .. img
      abs_path = vim.fn.expand(abs_path)

      vim.fn.jobstart({ "open", abs_path }) -- Mac OS
      -- vim.notify("Opening image: " .. abs_path, vim.log.levels.INFO)
      -- vim.fn.jobstart({ "qlmanage", "-p", abs_path }) -- Mac OS quick look preview
      -- vim.fn.jobstart({ "kitty", "+kitten", "icat", abs_path })
      -- vim.fn.jobstart({"xdg-open", url})  -- linux
      -- vim.cmd(':silent exec "!start ' .. url .. '"') -- Windows
    end,
  },
}

修改和完善的点

新建时候乱码问题

obsidian.nvim 存在的一个致命问题是 create link 时候作者没有考虑对 multibyte character 的支持

比如你选中一段文字, 想要以此为标题来新建一个文章, 如果最后一个字符是 multibyte character, 比如中文或者日文, 那么你会发现建立的 link 中字符被切断了, 出现了乱码.

我 fork 修改了支持了 multibyte character, 也向作者提了 pull request, 但是作者似乎很久又没出现了, 先凑合用我的吧.

排序问题

查找排序问题, ObsidianQuickSwitch 原本默认是按文件名排序的, 我增加了 sort_by = "modified" 相关的功能, 作者也接受支持了, 而且还将其作为默认值, 还不错.

后续发现 ObsidianQuickSwitch 以后, 如果输入关键词查找, 过滤出来的结果肯定还是希望依然按照modified 排序, 但是作者没有支持这个功能, 我也提了 pull request, 作者消失没合并, 就只能用我的了.

链接问题

默认对链接的处理也很有问题, 基础的对新建, url, 图片等的操作处理都没默认弄好, 所以上面的配置文件我都做了处理.

  • 用标题做文件名应该是默认操作, 作者选择了用随机字符串, 这样和他 [ 来索引引用文章的功能其实是冲突了, 搞不懂他为何那么做. 所以配置了note_id_func, 让其好好用标题当文件名
  • follow_url_func 配置了用默认浏览器打开 url
  • follow_img_func 原本配置了预览, 发现容易让界面混乱, 所以改回用 mac 默认工具打开了.

链接引用问题

作者文档说的是支持 [ 来引用文章, 但是弄来弄去都发现无法激活.

preferred_link_style = "markdown" 等配置都加了也无效, 最后发现添加的 dependencies 并不会生效.

要进入 :LazyExtras 确保安装 coding.nvim-cmp

热键

obsidian.nvim 的mappings不生效, 而且还要考虑激活问题, 我是随时都会调用查询或者写文章的, 所以直接在 lazyvim keymaps.lua 添加热键绑定了:

  vim.keymap.set("n", "<C-f>", "<cmd>ObsidianQuickSwitch<CR>", { desc = "Search cheese by file name" })
  vim.keymap.set("n", "<C-j>", "<cmd>ObsidianSearch<CR>", { desc = "Search cheese by content" })
  vim.keymap.set("n", "<C-g>", "<cmd>ObsidianToday<CR>", { desc = "Cheese create a new daily note" })
  vim.keymap.set("n", "<C-n>", "<cmd>ObsidianNew<CR>", { desc = "Cheese to create a new note" })
  vim.keymap.set("n", "<C-t>", "o<CR><Esc><cmd>ObsidianTemplate main.md<CR>", { desc = "Using templates" })
  map("v", "<CR>", "<cmd>ObsidianLinkNew<cr>", { desc = "create link" })