epwalsh / obsidian.nvim

Obsidian 🀝 Neovim
Apache License 2.0
3.93k stars 180 forks source link

"Failed to resolve ... relative to vault root" on autocomplete #629

Open kamransoomro84 opened 3 months ago

kamransoomro84 commented 3 months ago

πŸ› Describe the bug

I have just installed this plugin in using lazy. It seems to be working otherwise but whenever I trigger autocomplete with [[ followed by at least two characters, I get the following error:

Error detected while processing TextChangedI Autocommands for "*":
Error executing lua callback: ...al/share/nvim/lazy/obsidian.nvim/lua/obsidian/client.lua:249: failed to resolve '~/Library/Mobile Documents/icloud~md~obsidian/Documents/Second Brain.md' relative to vault root '~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain'

As far as I can tell, autocomplete still works correctly, but I just keep getting this error whenever I trigger it.

Config

return {
  "epwalsh/obsidian.nvim",
  version = "*",  -- recommended, use latest release instead of latest commit
  lazy = true,
  ft = "markdown",
  -- Replace the above line with this if you only want to load obsidian.nvim for markdown files in your vault:
  -- event = {
  --   -- If you want to use the home shortcut '~' here you need to call 'vim.fn.expand'.
  --   -- E.g. "BufReadPre " .. vim.fn.expand "~" .. "/my-vault/**.md"
  --   "BufReadPre path/to/my-vault/**.md",
  --   "BufNewFile path/to/my-vault/**.md",
  -- },
  dependencies = {
    -- Required.
    "nvim-lua/plenary.nvim",

    -- see below for full list of optional dependencies πŸ‘‡
  },
  opts = {
    workspaces = {
      {
        name = "Second Brain",
        path = "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain",
      },
      {
        name = "JD Index",
        path = "~/Mobile Documents/iCloud~md~obsidian/Documents/JD Index",
      },
    },

    -- see below for full list of options πŸ‘‡
    -- Optional, completion of wiki links, local markdown links, and tags using nvim-cmp.
    completion = {
      -- Set to false to disable completion.
      nvim_cmp = true,
      -- Trigger completion at 2 chars.
      min_chars = 2,
    },

    -- Optional, configure key mappings. These are the defaults. If you don't want to set any keymappings this
    -- way then set 'mappings = {}'.
    mappings = {
      -- Overrides the 'gf' mapping to work on markdown/wiki links within your vault.
      ["gf"] = {
        action = function()
          return require("obsidian").util.gf_passthrough()
        end,
        opts = { noremap = false, expr = true, buffer = true },
      },
      -- Toggle check-boxes.
      ["<leader>ch"] = {
        action = function()
          return require("obsidian").util.toggle_checkbox()
        end,
        opts = { buffer = true },
      },
      -- Smart action depending on context, either follow link or toggle checkbox.
      ["<cr>"] = {
        action = function()
          return require("obsidian").util.smart_action()
        end,
        opts = { buffer = true, expr = true },
      },
      ["<leader>oo"] = {
        action = ':ObsidianOpen<CR>',
      },
      ["<leader>ob"] = {
        action = ":ObsidianBacklinks<CR>",
      },
      ["<leader>os"] = {
        action = ":ObsidianSearch<CR>",
      },
    },

    -- Where to put new notes. 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 = "notes_subdir",

    -- Optional, customize how note file names are generated given the ID, target directory, and title.
    ---@param spec { id: string, dir: obsidian.Path, title: string|? }
    ---@return string|obsidian.Path The full path to the new note.
    note_path_func = function(spec)
      -- This is equivalent to the default behavior.
      local path = spec.dir -- / tostring(spec.id)
      return path:with_suffix(".md")
    end,

    -- Optional, customize how wiki links are formatted. You can set this to one of:
    --  * "use_alias_only", e.g. '[[Foo Bar]]'
    --  * "prepend_note_id", e.g. '[[foo-bar|Foo Bar]]'
    --  * "prepend_note_path", e.g. '[[foo-bar.md|Foo Bar]]'
    --  * "use_path_only", e.g. '[[foo-bar.md]]'
    -- Or you can set it to a function that takes a table of options and returns a string, like this:
    wiki_link_func = function(opts)
      return require("obsidian.util").wiki_link_id_prefix(opts)
    end,

    -- Optional, customize how markdown links are formatted.
    markdown_link_func = function(opts)
      return require("obsidian.util").markdown_link(opts)
    end,

    -- Either 'wiki' or 'markdown'.
    preferred_link_style = "wiki",

    -- Optional, customize the default name or prefix when pasting images via `:ObsidianPasteImg`.
    ---@return string
    image_name_func = function()
      -- Prefix image names with timestamp.
      return string.format("%s-", os.time())
    end,

    -- Optional, boolean or a function that takes a filename and returns a boolean.
    -- `true` indicates that you don't want obsidian.nvim to manage frontmatter.
    disable_frontmatter = false,

    -- Optional, alternatively you can customize the frontmatter data.
    ---@return table
    note_frontmatter_func = function(note)
      -- Add the title of the note as an alias.
      if note.title then
        note:add_alias(note.title)
      end

      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 not vim.tbl_isempty(note.metadata) then
        for k, v in pairs(note.metadata) do
          out[k] = v
        end
      end

      return out
    end,

    -- Optional, for templates (see below).
    templates = {
      folder = "Templates",
      date_format = "%Y-%m-%d",
      time_format = "%H:%M",
      -- A map for custom variables, the key should be the variable and the value a function
      substitutions = {},
    },

    -- 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
    end,

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

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

    picker = {
      -- Set your preferred picker. Can be one of 'telescope.nvim', 'fzf-lua', or 'mini.pick'.
      name = "telescope.nvim",
      -- Optional, configure key mappings for the picker. These are the defaults.
      -- Not all pickers support all mappings.
      mappings = {
        -- Create a new note from your query.
        new = "<C-x>",
        -- Insert a link to the selected note.
        insert_link = "<C-l>",
      },
    },

    -- Optional, sort search results by "path", "modified", "accessed", or "created".
    -- The recommend value is "modified" and `true` for `sort_reversed`, which means, for example,
    -- that `:ObsidianQuickSwitch` will show the notes sorted by latest modified time
    sort_by = "modified",
    sort_reversed = true,

    -- Set the maximum number of lines to read from notes on disk when performing certain searches.
    search_max_lines = 1000,

    -- Optional, determines how certain commands open notes. The valid options are:
    -- 1. "current" (the default) - to always open in the current window
    -- 2. "vsplit" - to open in a vertical split if there's not already a vertical split
    -- 3. "hsplit" - to open in a horizontal split if there's not already a horizontal split
    open_notes_in = "current",

    -- Optional, define your own callbacks to further customize behavior.
    callbacks = {
      -- Runs at the end of `require("obsidian").setup()`.
      ---@param client obsidian.Client
      post_setup = function(client) end,

      -- Runs anytime you enter the buffer for a note.
      ---@param client obsidian.Client
      ---@param note obsidian.Note
      enter_note = function(client, note) end,

      -- Runs anytime you leave the buffer for a note.
      ---@param client obsidian.Client
      ---@param note obsidian.Note
      leave_note = function(client, note) end,

      -- Runs right before writing the buffer for a note.
      ---@param client obsidian.Client
      ---@param note obsidian.Note
      pre_write_note = function(client, note) end,

      -- Runs anytime the workspace is set/changed.
      ---@param client obsidian.Client
      ---@param workspace obsidian.Workspace
      post_set_workspace = function(client, workspace) end,
    },

    -- Optional, configure additional syntax highlighting / extmarks.
    -- This requires you have `conceallevel` set to 1 or 2. See `:help conceallevel` for more details.
    ui = {
      enable = true,  -- set to false to disable all additional syntax features
      update_debounce = 200,  -- update delay after a text change (in milliseconds)
      max_file_length = 5000,  -- disable UI features for files with more than this many lines
      -- Define how various check-boxes are displayed
      checkboxes = {
        -- NOTE: the 'char' value has to be a single character, and the highlight groups are defined below.
        [" "] = { char = "σ°„±", hl_group = "ObsidianTodo" },
        ["x"] = { char = "ο…Š", hl_group = "ObsidianDone" },
        [">"] = { char = "ο„Έ", hl_group = "ObsidianRightArrow" },
        ["~"] = { char = "σ°°±", hl_group = "ObsidianTilde" },
        ["!"] = { char = "", hl_group = "ObsidianImportant" },
        -- Replace the above with this if you don't have a patched font:
        -- [" "] = { char = "☐", hl_group = "ObsidianTodo" },
        -- ["x"] = { char = "βœ”", hl_group = "ObsidianDone" },

        -- You can also add more custom ones...
      },
      -- Use bullet marks for non-checkbox lists.
      bullets = { char = "β€’", hl_group = "ObsidianBullet" },
      external_link_icon = { char = "ο‚Ž", hl_group = "ObsidianExtLinkIcon" },
      -- Replace the above with this if you don't have a patched font:
      -- external_link_icon = { char = "", hl_group = "ObsidianExtLinkIcon" },
      reference_text = { hl_group = "ObsidianRefText" },
      highlight_text = { hl_group = "ObsidianHighlightText" },
      tags = { hl_group = "ObsidianTag" },
      block_ids = { hl_group = "ObsidianBlockID" },
      hl_groups = {
        -- The options are passed directly to `vim.api.nvim_set_hl()`. See `:help nvim_set_hl`.
        ObsidianTodo = { bold = true, fg = "#f78c6c" },
        ObsidianDone = { bold = true, fg = "#89ddff" },
        ObsidianRightArrow = { bold = true, fg = "#f78c6c" },
        ObsidianTilde = { bold = true, fg = "#ff5370" },
        ObsidianImportant = { bold = true, fg = "#d73128" },
        ObsidianBullet = { bold = true, fg = "#89ddff" },
        ObsidianRefText = { underline = true, fg = "#c792ea" },
        ObsidianExtLinkIcon = { fg = "#c792ea" },
        ObsidianTag = { italic = true, fg = "#89ddff" },
        ObsidianBlockID = { italic = true, fg = "#89ddff" },
        ObsidianHighlightText = { bg = "#75662e" },
      },
    },

    -- Specify how to handle attachments.
    attachments = {
      -- The default folder to place images in via `:ObsidianPasteImg`.
      -- If this is a relative path it will be interpreted as relative to the vault root.
      -- You can always override this per image by passing a full path to the command instead of just a filename.
      img_folder = "attachments",  -- This is the default
      -- A function that determines the text to insert in the note when pasting an image.
      -- It takes two arguments, the `obsidian.Client` and an `obsidian.Path` to the image file.
      -- This is the default implementation.
      ---@param client obsidian.Client
      ---@param path obsidian.Path the absolute path to the image file
      ---@return string
      img_text_func = function(client, path)
        path = client:vault_relative_path(path) or path
        return string.format("![%s](%s)", path.name, path)
      end,
    }
  },
}

Environment

nvim --version:

NVIM v0.10.0
Build type: Release
LuaJIT 2.1.1713773202

nvim --headless -c 'lua require("obsidian").info()' -c q

Obsidian.nvim v3.7.14 (c6bd6d93e4724ac2dc0cae73ebe1d568bf406537)
Status:
  β€’ buffer directory: nil
  β€’ working directory: ~
Workspaces:
  βœ“ active workspace: Workspace(name='Second Brain', path='~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain', root='~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain')
  βœ— inactive workspace: Workspace(name='JD Index', path='~/Library/Mobile Documents/iCloud~md~obsidian/Documents/JD Index', root='~/Library/Mobile Documents/iCloud~md~obsidian/Documents/JD Index')
Dependencies:
  βœ“ plenary.nvim: a3e3bc82a3f95c5ed0d7201546d5d2c19b20d683
  βœ“ nvim-cmp: a110e12d0b58eefcf5b771f533fc2cf3050680ac
  βœ“ telescope.nvim: a0bbec21143c7bc5f8bb02e0005fa0b982edc026
Integrations:
  βœ“ picker: TelescopePicker()
  βœ“ completion: enabled (nvim-cmp) βœ— refs, βœ— tags, βœ— new
    all sources:
      β€’ nvim_lsp
      β€’ luasnip
Tools:
  βœ“ rg: ripgrep 14.1.0
Environment:
  β€’ operating system: Darwin
Config:
  β€’ notes_subdir: nil⏎        
epwalsh commented 3 months ago

Hey @kamransoomro84 are there more lines than that to the traceback?

kamransoomro84 commented 3 months ago

Here is the complete output:

Error detected while processing TextChangedI Autocommands for "*":
Error executing lua callback: ... al/share/nvim/lazy/obsidian.nvim/lua/obsidian/client.lua:249: failed to resolve '~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain.md' relative t
o vault root '~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Second Brain'
stack traceback:
[c]: in function 'error'
... al/share/nvim/lazy/obsidian.nvim/lua/obsidian/client.lua:249: in function 'vault_relative_path'
... al/share/nvim/lazy/obsidian.nvim/lua/obsidian/client.lua:2006: in function 'format_link'
... l/share/nvim/lazy/obsidian.nvim/lua/cmp_obsidian_new.lua:110: in function 'complete'
... oomro/. local/share/nvim/lazy/nvim-cmp/lua/cmp/source.lua:326: in function 'complete'
... -soomro/.local/share/nvim/lazy/nvim-cmp/lua/cmp/core.lua:299: in function 'complete'
... -soomro/.local/share/nvim/lazy/nvim-cmp/lua/cmp/core.lua:169: in function 'callback'
... -soomro/ local/share/nvim/lazy/nvim-cmp/lua/cmp/core.lua:229: in function 'autoindent'
... -soomro/.local/share/nvim/lazy/nvim-cmp/lua/cmp/core.lua:161: in function 'on_change'
... -soomro/.local/share/nvim/lazy/nvim-cmp/lua/cmp/init.lua:340: in function 'callback'
... local/share/nvim/lazy/nvim-cmp/lua/cmp/utils/autocmd.lua:49: in function 'emit'
... local/share/nvim/lazy/nvim-cmp/lua/cmp/utils/autocd.lua:23: in function β€Ή ... local/share/nvim/lazy/nvim-cmp/lua/cmp/utils/autocmd.lua:22>
epwalsh commented 3 months ago

Thanks @kamransoomro84, I think the issue stems from your note_path_func, which is ignoring the note ID and just appending ".md" to the parent directory name:

    -- Optional, customize how note file names are generated given the ID, target directory, and title.
    ---@param spec { id: string, dir: obsidian.Path, title: string|? }
    ---@return string|obsidian.Path The full path to the new note.
    note_path_func = function(spec)
      -- This is equivalent to the default behavior.
      local path = spec.dir -- / tostring(spec.id)
      return path:with_suffix(".md")
    end,
kamransoomro84 commented 3 months ago

Thanks. What does this mean? How do I fix it?

epwalsh commented 3 months ago

Try changing your note_path_func to:

    -- Optional, customize how note file names are generated given the ID, target directory, and title.
    ---@param spec { id: string, dir: obsidian.Path, title: string|? }
    ---@return string|obsidian.Path The full path to the new note.
    note_path_func = function(spec)
      -- This is equivalent to the default behavior.
      local path = spec.dir / tostring(spec.id)
      return path:with_suffix(".md")
    end,
kamransoomro84 commented 3 months ago

Just for context, I removed that because I don't want IDs in my file names.

kamransoomro84 commented 3 months ago

Just thinking about this more, the reason I don't want IDs is because I assumed these are Zettelkasten IDs, which I don't want. Is that not the case, and if so, how do I not use them?