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

Support blink.nvim Autocomplete

Open TZProgrammer opened this issue 1 year ago • 52 comments
trafficstars

🚀 The feature, motivation and pitch

Currently obsidian.nvim does not work with blink.nvim, only with nvim-cmp. However, as blink is gaining popularity and becoming the default for many, compatibility between blink and obsidian would be very welcome.

Alternatives

No response

Additional context

No response

TZProgrammer avatar Nov 15 '24 23:11 TZProgrammer

https://github.com/benlubas/cmp2lsp may do the trick in the meantime? I don't know what obsidian.nvim's source name would be though, nor if it's guaranteed to be exposed

NickvanDyke avatar Nov 27 '24 05:11 NickvanDyke

Especially, now that lazyvim has an extra for blink.nvim, I guess more and more will switch. There is also https://github.com/Saghen/blink.compat. It would be great to have an example in the docs how to configure it properly. 🤗

Edit: I managed to get the sources to blink. however, there is always an error as obsidian.nvim wants to register itself to nvim_cmp.

return {
  { "saghen/blink.compat" },
  {
    "saghen/blink.cmp",
    dependencies = {
      { "epwalsh/obsidian.nvim" },
    },
    opts_extend = { "sources.completion.enabled_providers" },
    ---@module 'blink.cmp'
    ---@type blink.cmp.Config
    opts = {
      sources = {
        completion = {
          enabled_providers = {
            "lsp",
            "path",
            "snippets",
            "buffer",
            "obsidian",
            "obsidian_new",
            "obsidian_tags",
          },
        },
        providers = {
          obsidian = {
            name = "obsidian",
            module = "blink.compat.source",
          },
          obsidian_new = {
            name = "obsidian_new",
            module = "blink.compat.source",
          },
          obsidian_tags = {
            name = "obsidian_tags",
            module = "blink.compat.source",
          },
        },
      },
    },
  },
}

The error:

Error 08:40:08 msg_show.lua_error E5108: Error executing lua: vim/_editor.lua:0: nvim_exec2()..BufEnter Autocommands for "*.md": Vim(append):Error executing lua callback: ...ocal/share/nvim/lazy/obsidian.nvim/lua/obsidian/init.lua:151: attempt to call field 'get_config' (a nil value)

I guess https://github.com/epwalsh/obsidian.nvim/blob/main/lua/obsidian/init.lua#L143-L158 this block should be guarded.

codingluke avatar Dec 04 '24 07:12 codingluke

I got autocomplete for links at least (not sure what else obsidian.nvim might provide) using the marksman language server

NickvanDyke avatar Dec 04 '24 15:12 NickvanDyke

The permanent solution would be a LSP, not support for any given completion, cpm and blinks comes and goes. For example Neorg has its own mini LSP, you wont need to configure cmp for it

https://github.com/nvim-neorg/neorg/issues/1603#issuecomment-2480788053

sadtab avatar Dec 07 '24 20:12 sadtab

LazyVim last update removed nvim-cmp in favor of blink-cmd. It would be great to see Obsidian.nvim support it !

Alan-TheGentleman avatar Dec 13 '24 18:12 Alan-TheGentleman

The permanent solution would be a LSP, not support for any given completion, cpm and blinks comes and goes. For example Neorg has its own mini LSP, you wont need to configure cmp for it

nvim-neorg/neorg#1603 (comment)

Actually I take this back, I say obsidian notes are markdowns anyway, and we already have markdown LSP, so we don't need to reinvent the wheel, the mentioned LSP already supports document linking auto complete, tag auto complete, insert TOC, tree sitter highlights are also available, other functionality like toggle checkboxs etc. actually I disabled the obsidian plugin entirely and I could do almost my daily workflow on my vaults.

In my humble opinion, the easiest, still most maintainable and extendable breakthrough, is to keep obsidian plugin, only related to obsidian specific functionalities like managing the vaults, referencing the tags/files (BY USING THE MD LSP, again not reinventing the wheel), front matter, opening the MD in obsidian etc.

Managing the highlights, auto complete anything, toggle the checkbox etc are done more maturely in markdown lsp

sadtab avatar Dec 13 '24 20:12 sadtab

LazyVim last update removed nvim-cmp in favor of blink-cmd. It would be great to see Obsidian.nvim support it !

Yep, would be awesome! My workaround is to enable nvim-cmp again in the extras.

paulwyszynski avatar Dec 15 '24 15:12 paulwyszynski

return {
  {
    "epwalsh/obsidian.nvim",
    dependencies = {
      "nvim-lua/plenary.nvim",
    },
    opts = {
      completion = {
        nvim_cmp = false,  -- disable!
      },
    },
    config = function(_, opts)
      require("obsidian").setup(opts)

      -- HACK: fix error, disable completion.nvim_cmp option, manually register sources
      local cmp = require("cmp")
      cmp.register_source("obsidian", require("cmp_obsidian").new())
      cmp.register_source("obsidian_new", require("cmp_obsidian_new").new())
      cmp.register_source("obsidian_tags", require("cmp_obsidian_tags").new())
    end,
  },
  {
    "saghen/blink.cmp",
    dependencies = { "saghen/blink.compat" },
    opts = {
      sources = {
        default = { "obsidian", "obsidian_new", "obsidian_tags" },
        providers = {
          obsidian = {
            name = "obsidian",
            module = "blink.compat.source",
          },
          obsidian_new = {
            name = "obsidian_new",
            module = "blink.compat.source",
          },
          obsidian_tags = {
            name = "obsidian_tags",
            module = "blink.compat.source",
          },
        },
      },
    },
  },
}

yuchenfei avatar Dec 20 '24 16:12 yuchenfei

@yuchenfei doesn't this still require nvim cmp to be installed?

Muizzyranking avatar Dec 21 '24 06:12 Muizzyranking

@yuchenfei doesn't this still require nvim cmp to be installed?

It does. You're registering obsidian sources with cmp and then using them in blink. It's a workaround not a replacement right now.

sebszyller avatar Dec 21 '24 07:12 sebszyller

@yuchenfei doesn't this still require nvim cmp to be installed?

Yes, it still requires nvim-cmp, but it only loads when completing in Obsidian notes. This is a workaround configuration to make it work for now.

yuchenfei avatar Dec 21 '24 07:12 yuchenfei

Oh, thank you. I initially did that without registering the source. I'll do this.

Muizzyranking avatar Dec 22 '24 11:12 Muizzyranking

@yuchenfei @sebszyller @Muizzyranking author of blink.compat here. FYI, when using blink.compat, nvim-cmp should not be installed. blink.compat mirrors the cmp api, including the cmp.register_source function you are using. I'll take a look at adding the cmp.get_config function as well so that this works out of the box.

stefanboca avatar Dec 22 '24 19:12 stefanboca

I have been using the same above change mentioned by @yuchenfei and I am not sure if I am doing something wrong. I keep getting this error: failed to get completions with error: ...share/nvim/lazy/blink.compat/lua/blink/compat/source.lua:87: attempt to index local 'item' (a boolean value)

Here's my obsidian lua configuration:

return {
  {
    "epwalsh/obsidian.nvim",
    version = "*",
    lazy = false,
    ft = "markdown",
    dependencies = {
      "nvim-lua/plenary.nvim",
    },
    opts = {
      workspaces = {
        {
          name = "wiki",
          path = "/Users/shivayanbora/PKM/My-Wiki-Vault",
        },
      },
      new_notes_location = "notes_subdir",
      notes_subdir = "0_inbox",
      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, customize how note IDs are generated given an optional title.
      ---@param title string|?
      ---@return string
      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
          -- 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, 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,
      templates = {
        folder = "templates",
        date_format = "%Y-%m-%d",
        time_format = "%H:%M:%S",
      },
      completion = {
        nvim_cmp = false,
        min_chars = 2,
      },
      ui = {
        enable = false,
      },
    },
    config = function(_, opts)
      require("obsidian").setup(opts)
      local cmp = require("cmp")
      cmp.register_source("obsidian", require("cmp_obsidian").new())
      cmp.register_source("obsidian_new", require("cmp_obsidian_new").new())
      cmp.register_source("obsidian_tags", require("cmp_obsidian_tags").new())
    end,
  },
  {
    "saghen/blink.cmp",
    dependencies = { "saghen/blink.compat" },
    opts = {
      sources = {
        default = { "obsidian", "obsidian_new", "obsidian_tags" },
        providers = {
          obsidian = {
            name = "obsidian",
            module = "blink.compat.source",
          },
          obsidian_new = {
            name = "obsidian_new",
            module = "blink.compat.source",
          },
          obsidian_tags = {
            name = "obsidian_tags",
            module = "blink.compat.source",
          },
        },
      },
    },
  },
}

shivayan-bora avatar Dec 23 '24 09:12 shivayan-bora

@stefanboca could you post a config here to make it work with obsidian or maybe mention it in the blink readme? 😬

paulwyszynski avatar Dec 23 '24 12:12 paulwyszynski

I have been using the same above change mentioned by @yuchenfei and I am not sure if I am doing something wrong. I keep getting this error: failed to get completions with error: ...share/nvim/lazy/blink.compat/lua/blink/compat/source.lua:87: attempt to index local 'item' (a boolean value)

Just switch the blink.compat to main branch

CaeChao avatar Dec 23 '24 17:12 CaeChao

@CaeChao Am I doing this correctly? Sorry I am quite new to Neovim.

return {
  {
    "epwalsh/obsidian.nvim",
    version = "*",
    lazy = false,
    ft = "markdown",
    dependencies = {
      "nvim-lua/plenary.nvim",
    },
    opts = {
      workspaces = {
        {
          name = "wiki",
          path = "/Users/shivayanbora/PKM/My-Wiki-Vault",
        },
      },
      new_notes_location = "notes_subdir",
      notes_subdir = "0_inbox",
      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, customize how note IDs are generated given an optional title.
      ---@param title string|?
      ---@return string
      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
          -- 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, 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,
      templates = {
        folder = "templates",
        date_format = "%Y-%m-%d",
        time_format = "%H:%M:%S",
      },
      completion = {
        nvim_cmp = false,
        min_chars = 2,
      },
      ui = {
        enable = false,
      },
    },
    config = function(_, opts)
      require("obsidian").setup(opts)
      local cmp = require("cmp")
      cmp.register_source("obsidian", require("cmp_obsidian").new())
      cmp.register_source("obsidian_new", require("cmp_obsidian_new").new())
      cmp.register_source("obsidian_tags", require("cmp_obsidian_tags").new())
    end,
  },
  {
    "saghen/blink.cmp",
    dependencies = {
      { "saghen/blink.compat", branch = "main" },
    },
    opts = {
      sources = {
        default = { "obsidian", "obsidian_new", "obsidian_tags" },
        providers = {
          obsidian = {
            name = "obsidian",
            module = "blink.compat.source",
          },
          obsidian_new = {
            name = "obsidian_new",
            module = "blink.compat.source",
          },
          obsidian_tags = {
            name = "obsidian_tags",
            module = "blink.compat.source",
          },
        },
      },
    },
  },
}

If yes, I am still getting the error: failed to get completions with error: ...share/nvim/lazy/blink.compat/lua/blink/compat/source.lua:87: attempt to index local 'item' (a boolean value)

Just for context: image

UPDATE:

I deleted ~/.local/share/nvim and ~/.local/state/nvim and reinstalled everything. Still the same issue.

shivayan-bora avatar Dec 24 '24 06:12 shivayan-bora

@shivayan-bora remove the part where you registered the source with CMP, blink compat's author said it wasn't necessary.

Muizzyranking avatar Dec 24 '24 06:12 Muizzyranking

@shivayan-bora I believe you also need version = "*" for the saghen/blink.compat plugin until https://github.com/epwalsh/obsidian.nvim/issues/770#issuecomment-2558570918 is released.

brianrodri avatar Dec 24 '24 15:12 brianrodri

@shivayan-bora my guess is that you are using LazyVim, so you need to put version = false inside { "saghen/blink.compat", lazy = true, version = false } to override the default setting from LazyVIm or set vim.g.lazyvim_blink_main = true in your options.lua file or you can just wait for the next release....

@Muizzyranking I think you still need the registered part above to make it work, blink.compat just mirror the CMP module, obsidian need to call the api in order to register the sources

CaeChao avatar Dec 24 '24 18:12 CaeChao

It's working perfectly for me with this configuration:

https://github.com/Gentleman-Programming/Gentleman.Dots/blob/main/GentlemanNvim/nvim/lua/plugins/obsidian.lua

Alan-TheGentleman avatar Dec 24 '24 19:12 Alan-TheGentleman

@CaeChao blink.compat author said it wasn't neccessary.

@Alan-TheGentleman this is very similar to what I have too.

Muizzyranking avatar Dec 24 '24 22:12 Muizzyranking

Well, I've tried to follow all pieces of advice, but it doesn't work on my side… No more completion with my LazyVim config. I had to set nvim-cpm to false :(

I guess I'll wait for the next obsidian.nvim version and I hope it will be able to auto-detect the config (for newbies like me !) when nvim-cmp is set to true.

celfred avatar Dec 27 '24 12:12 celfred

@celfred can you share a snippet of what you did? Because for LazyVim, there is a custom spec to pass in custom sources as a list so you won't have to add the module, look at this extra for example. I am not with my pc at the moment but if I see your config, I could help you write LazyVim compatible config.

Muizzyranking avatar Dec 27 '24 13:12 Muizzyranking

Thanks @Muizzyranking for this quick reply. I'm not sure how your link can help me… sorry…

What I've done so far :

  1. I added this at the end of my 'opts= {} ' in my obsidian.nvim file (in my plugins directory) :
    ` config = function(_, opts) require("obsidian").setup(opts)

    -- HACK: fix error, disable completion.nvim_cmp option, manually register sources local cmp = require("cmp") cmp.register_source("obsidian", require("cmp_obsidian").new()) cmp.register_source("obsidian_new", require("cmp_obsidian_new").new()) cmp.register_source("obsidian_tags", require("cmp_obsidian_tags").new()) end, `

  2. I added this right after my 'opts={}' block : { "saghen/blink.cmp", dependencies = { "saghen/blink.compat" }, opts = { sources = { default = { "obsidian", "obsidian_new", "obsidian_tags" }, providers = { obsidian = { name = "obsidian", module = "blink.compat.source", }, obsidian_new = { name = "obsidian_new", module = "blink.compat.source", }, obsidian_tags = { name = "obsidian_tags", module = "blink.compat.source", }, }, }, }, },

  3. I added this line in my 'config > options.lua' file : vim.g.lazyvim_blink_main = true

Don't know if that explains anything… But thanks for your time !

celfred avatar Dec 27 '24 13:12 celfred

@celfred As a workaround I suggest you use marskman LSP.

rbmarliere avatar Dec 27 '24 13:12 rbmarliere

@celfred As a workaround I suggest you use marskman LSP.

Well, I've read that suggestion too earlier, but I had no idea how to set up marksman LSP instead of what I'm using right now…

celfred avatar Dec 27 '24 13:12 celfred

@celfred I just got hold of my PC, first of all remove the hack to register source in obsidian with cmp, blink compat has it covered

for blink you can do this

{
    "saghen/blink.cmp",
    dependencies = {
      { "epwalsh/obsidian.nvim", "saghen/blink.compat" },
    },
    opts = {
      sources = {
        -- LazyVim as custom option copmpat to pass in external sources with blink.compat
        compat = { "obsidian", "obsidian_new", "obsidian_tags" },
        -- Optional to set kind name., not really neccesary.
        -- providers = {
        --   obsidian = {
        --     kind = "Obsidian",
        --     async = true,
        --   },
        --   obsidian_new = {
        --     kind = "Obsidian",
        --     async = true,
        --   },
        --   obsidian_tags = {
        --     kind = "Obsidian",
        --     async = true,
        --   },
        -- },
      },
    },
  }

To setup marksman lsp in lazyvim, go to your lazy.lua file and add this to the spec table.

{ import = "lazyvim.plugins.extras.lang.markdown" },

I will suggest you read LazyVim docs on how to extend/override plugins opts and importing extras. You might also need to peek the source code to get a better understanding.

Muizzyranking avatar Dec 27 '24 23:12 Muizzyranking

@Muizzyranking Your solution is basically ignoring the Obsidian source and replace it with Makrsman lsp as source. The author of blink.compat said you should not install nvim-cmp instead of ignoring the register part.

The main branch of blink.compat has already implemented a workaround for obsidian Just switch the blink.compat version to main like this

{
   "saghen/blink.cmp",
   dependencies = {
     { "saghen/blink.compat", lazy = true, version = false }, },
   },
...
}

and put the nvim_cmp back to true and it will work fine now

CaeChao avatar Dec 28 '24 00:12 CaeChao

@CaeChao putting nvim_cmp to true would cause some errors, set it to false and blink.compat would deal with registering the sources.

Muizzyranking avatar Dec 28 '24 06:12 Muizzyranking