Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support blink.nvim Autocomplete #770

Open
TZProgrammer opened this issue Nov 15, 2024 · 42 comments
Open

Support blink.nvim Autocomplete #770

TZProgrammer opened this issue Nov 15, 2024 · 42 comments

Comments

@TZProgrammer
Copy link

🚀 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

@NickvanDyke
Copy link

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

@codingluke
Copy link

codingluke commented Dec 4, 2024

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.

@NickvanDyke
Copy link

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

@sadtab
Copy link

sadtab commented Dec 7, 2024

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)

@Alan-TheGentleman
Copy link

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

@sadtab
Copy link

sadtab commented Dec 13, 2024

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

@paulwyszynski
Copy link

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.

bamdadfr added a commit to bamdadfr/config-nvim that referenced this issue Dec 16, 2024
@yuchenfei
Copy link

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",
          },
        },
      },
    },
  },
}

@Muizzyranking
Copy link

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

@sebszyller
Copy link

@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.

@yuchenfei
Copy link

@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.

@Muizzyranking
Copy link

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

@stefanboca
Copy link

stefanboca commented Dec 22, 2024

@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.

@shivayan-bora
Copy link

shivayan-bora commented Dec 23, 2024

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",
          },
        },
      },
    },
  },
}

@paulwyszynski
Copy link

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

@CaeChao
Copy link
Contributor

CaeChao commented Dec 23, 2024

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

@shivayan-bora
Copy link

shivayan-bora commented Dec 24, 2024

@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.

@Muizzyranking
Copy link

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

@brianrodri
Copy link

brianrodri commented Dec 24, 2024

@shivayan-bora I believe you also need version = "*" for the saghen/blink.compat plugin until #770 (comment) is released.

@CaeChao
Copy link
Contributor

CaeChao commented Dec 24, 2024

@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

@Alan-TheGentleman
Copy link

It's working perfectly for me with this configuration:

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

@Muizzyranking
Copy link

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

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

@celfred
Copy link

celfred commented Dec 27, 2024

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.

@Muizzyranking
Copy link

@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.

@celfred
Copy link

celfred commented Dec 27, 2024

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 !

@rbmarliere
Copy link

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

@celfred
Copy link

celfred commented Dec 27, 2024

@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…

@Muizzyranking
Copy link

@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.

@CaeChao
Copy link
Contributor

CaeChao commented Dec 28, 2024

@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

@Muizzyranking
Copy link

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

@celfred
Copy link

celfred commented Dec 28, 2024

@CaeChao @Muizzyranking Thanks a lot. Things are getting better (with a behavior a little different from what I used to have in case of completion suggestions).
I eventually added a blink.lua file in my plugins list contianing :

return {
  "saghen/blink.cmp",
  dependencies = {
    { "saghen/blink.compat", lazy = true, version = false },
  },
  opts = {
    sources = {
      -- LazyVim as custom option copmpat to pass in external sources with blink.compat
      compat = { "obsidian", "obsidian_new", "obsidian_tags" },
    },
  },
}

And I had to set nvim-cmp to true in my obsidian.lua file.

I have no error and completion works 😄

I'll enquire to try and understand how to 'modify' the suggestion list, but THANKS a lot for your help.

@Muizzyranking
Copy link

@CaeChao what changed?

@SimonYde
Copy link

SimonYde commented Jan 2, 2025

to avoid having to have cmp installed to register sources, one can simply do:

    package.loaded['nvim-cmp'] = package.loaded['blink.compat']

some time before loading this plugin. Then if using the nvim-cmp = true option will work and the completion sources can be added via blink.compat no problem.

@AquilesGomez
Copy link

Using the method outlined here
#770 (comment)

has it popup with a tag in the menu; but I'm unable to select it.

@LPRegen
Copy link

LPRegen commented Jan 6, 2025

At the moment using #770 (comment)

Thanks @CaeChao !

@ericsunplus
Copy link

Things basically work after using method suggested by @CaeChao .

There is one thing, I want to insert page link/tag link by using [[page_link#sec_link]]. But the completion selection is not triggered after I input [[ or #, I have continue to insert valid characters before the selection comes up. Anything I may missing? The following is my full config

-- blink.lua
return {
	"saghen/blink.cmp",
	version = "*",
	dependencies = {
		{ "L3MON4D3/LuaSnip", version = "v2.*" },
		{ "saghen/blink.compat", lazy = true, verson = false },
	},

	opts = {
		snippets = { preset = "luasnip" },
		keymap = { preset = "super-tab" },

		appearance = {
			use_nvim_cmp_as_default = true,
			nerd_font_variant = "normal",
		},

		sources = {
			default = { "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" },
			},

			cmdline = {},
		},

		signature = { enabled = true },
	},

	opts_extend = { "sources.default" },
}

@rbmarliere
Copy link

I have continue to insert valid characters before the selection comes up.

I think it might be due to completion.min_chars of obsidian.nvim

@Muizzyranking
Copy link

@ericsunplus i think you need to have a markdown lsp. My config is very similar to yours and it works for me.

@ericsunplus
Copy link

@ericsunplus i think you need to have a markdown lsp. My config is very similar to yours and it works for me.

Hi @Muizzyranking , I just install and configure the marksman LSP. Now [[ can trigger the selection, but # still not. Is this the same on your side? What is the markdown lsp you are using?

@ericsunplus
Copy link

I have continue to insert valid characters before the selection comes up.

I think it might be due to completion.min_chars of obsidian.nvim

Hi @rbmarliere , I try set min_chars to 1 or 0, both still not taking effect

@Muizzyranking
Copy link

@ericsunplus I generally don't expect completion from typing # because it's a header marker. I can't really help with that.

@ericsunplus
Copy link

@ericsunplus I generally don't expect completion from typing # because it's a header marker. I can't really help with that.

In the README doc of this obsidian plugin, it indicate that

Completion: Ultra-fast, asynchronous autocompletion for note references and tags via nvim-cmp (triggered by typing [[ for wiki links, [ for markdown links, or # for tags)

So # for tags is also something I want to achieve.

Thank you for the help anyway

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests