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

Better document extension points #251

Open
danielo515 opened this issue Apr 6, 2022 · 17 comments
Open

Better document extension points #251

danielo515 opened this issue Apr 6, 2022 · 17 comments
Labels
enhancement New feature or request

Comments

@danielo515
Copy link
Contributor

Feature Description

Hello, first, thanks for this wonderful neovim preset. It looks outstanding and really lowers the entry barrier.
However, when it's time to extend and customise it beyond the most basic (keymaps and just adding more basic plugins) it is not easy to understand. For example, each plugin documentation has it's own set of assumptions about how is your environment and the knowledge you have about it (basically your personal configuration), but in the case of lunar most people will have no idea if they can just follow the plugin instructions or they have to hook that into some lunar vim startup function.
I will list the things that I think are worth documenting, but probably there are more

Describe the alternatives you have considered

Right now the only alternative is reading the source-code, but that is not very comfortable because you have to navigate the repository, have a good understanding of lua and a good understanding of neovim apis and vim in general. Those are too high of a entry barrier.

Support information

Some of the things I think are worth documenting are:

  • Startup order: what files are loaded first? When does the user config kick in?
  • What is available at certain point? For example, can I require a plugin inside the config after the declaration of plugins? Or should I defer it to a
  • Available setup-hooks/callback. For example, there is lvim.builtin.telescope.on_config_done that is called once the telescope config is complete and you can use that to add telescope extensions. I think there is the same callback available for other plugins, so it will be awesome to document that
  • How to extend specific plugin configurations? The number of built-in plugins is very nice, and sometimes you just want to change one little detail, but how to do this is not clear. Should I override the entire plugin configuration? Can I just "extend" it? Is there any user entrypoint that will get merged with the defaults?
@danielo515 danielo515 added the enhancement New feature or request label Apr 6, 2022
@abzcoding
Copy link
Member

abzcoding commented Apr 6, 2022

first off i do agree we should have a better documentation, but sadly we don't have enough manpower to do so, so feel free to contribute


  1. Startup order

here is the detailed order -> https://www.lunarvim.org/dev/02-under-the-hood.html


  1. What is available at a certain point

right now you can require them inside config because we have minimal lazy loading, though i would suggest using this syntax to be on the safe side

  local status_ok, sample_plugin = pcall(require, "sample_plugin")
  if not status_ok then
    return
  end
  
  -- do stuff with `sample_plugin`

and also you can require them inside on_config_done


  1. Available setup-hooks/callback

pretty much all plugins have this, and they all work the same way, they provide you with an object so you can do anything you want with them and override lunarvim settings
and we are planning to enable users to change the load event of plugins as well ( e.g. InsertEnter ... )


  1. How to extend specific plugin configurations

just add the settings you like( and we don't have), for example in bufferline options, we don't have mode but you can easily add it using

lvim.builtin.bufferline.options.mode = "buffers", -- set to "tabs" to only show tabpages instead

basically, just extend the settings or even completely override them


let me know if you need extra info

@kylo252
Copy link
Contributor

kylo252 commented Apr 7, 2022

Another relevant issue to this is that the site only targets the stable branch, this makes it harder to update information when a PR is done.

Also related https://github.com/LunarVim/LunarVim/projects/1#card-67485925

@danielo515
Copy link
Contributor Author

first off i do agree we should have a better documentation, but sadly we don't have enough manpower to do so, so feel free to contribute

I will be happy to contribute, but I want to first get to an agreement of what is needed so I don't try to do useless PRs.

Here are some things that, even after reading your answer and what you suggested, are still not clear to me.

right now you can require them inside config because we have minimal lazy loading, though i would suggest using this syntax to be on the safe side

It have happened to me several times that I could not load a plugin from my configuration. At first I thought it was because I have configured the plugins before trying to require them, but even when I moved the plugin require to the config function of packer I was not able to require them. Explaining at what part of your local config you can add configs that rely on plugin imports will be a nice thing to clarify.
Even using the format you suggested does not help, because the result will be that you don't have an error, but you will not load your configurations either.

pretty much all plugins have this, and they all work the same way, they provide you with an object so you can do anything you want with them and override lunarvim settings and we are planning to enable users to change the load event of plugins as well ( e.g. InsertEnter ... )

This is another thing that may look obvious when you have familiarity with the code-base, but that it is not that simple From the outside. "Pretty much all plugins" means that I have to go to the code, see if such a reference exist and it's exact naming, because I may become with a different naming idea than yours. Having a section of "config-hooks" or "plugin-configuration" with all the available ones specifically listed will be a nice thing to have. Also, I still don't know what the thing you get is exactly. Is the instance of the plugin? Is the configuration object? Is it equivalent to do require"plugin-name"? Does it change from plugin to plugin?

just add the settings you like( and we don't have), for example in bufferline options, we don't have mode but you can easily add it using

lvim.builtin.bufferline.options.mode = "buffers", -- set to "tabs" to only show tabpages instead

basically, just extend the settings or even completely override them

Again, having specific instructions, ideally per plugin, about how to extend lvim settings will be amazing. To me it is still not clear when this configurations are applied: Do they happen after my entire config.lua it's sourced? If that is the case, that means that I can not do anything that relies on those configs inside my config.lua unless is a function that is lazy loaded.
Another thing that is not clear is what extend mean. If you are a seasoned lua developer you probably have your own ideas, but as a newbie is hard to figure it out, specially because it is not clear the way configs are applied by lunar vim. What does extend mean exactly? Do I need to call vim.tbl_deep_extend ? Or is lunar vim just applying whatever I put on the global configuration with some kind of internal default config? As I said, this may look like silly questions once you read the code but ideally you should not be reading the code.

To give you an example on this last point, this is something I tried to configure telescope:

lvim.builtin.telescope.pickers = {
	{
		lsp_workspace_symbols = {
			attach_mappings = function(_, map)
				local onSelect = function(prompt_bufnr)
					local selection = require("telescope.actions.state").get_selected_entry()
					print(selection)
				end
				map("i", "<CR>", onSelect)
			end,
				mappings = {
					i = {
						["<cr>"] = function(prompt_bufnr)
							local selection = require("telescope.actions.state").get_selected_entry()
							print(vim.inspect(selection))
						end,
					},
				},
		},
	},
}

However, it is not working and I have no idea why. Is this the supposed way to extend telescope? Should I be doing something different?
However, If I move that very same piece of code to the callback function of telescope like this:

lvim.builtin.telescope.on_config_done = function(tele)
	tele.load_extension("frecency")
	tele.load_extension("command_palette")
	tele.load_extension("notify")
	tele.load_extension("file_browser")
	local opts = {
		pickers = {
			lsp_workspace_symbols = {
				mappings = {
					i = {
						["<cr>"] = function(prompt_bufnr)
							local selection = require("telescope.actions.state").get_selected_entry()
							print(vim.inspect(selection))
						end,
					},
				},
			},
		},
	}

    notify(vim.inspect(opts))
    require("telescope").setup(opts)
end

Then it is loaded into telescope and kind of works. Does not work as I wanted because the which key function is not triggering with this mappings, however if I manually run :Telescope lsp_workspace_symbols it works as I expect. So advanced configurations are not trivial.

@danielo515
Copy link
Contributor Author

To give you another good example most users will have to face: adding snippets.
This is a trivial task in almost all editors, but when it's time to do it on lunar-vim it's very confusing for most people which is made obvious by reading how many questions about this are on the chat about this.
Of course it is not a matter of replicating the docs of luasnip, but, specially becasue lunar-vim uses a quite old version of it, some guidance should be added.
Things like the following will be nice to have them documented:

  • How to create snippets local to your configuration. Reading luasnip seems that by just putting a snippets folder in any place inside your runtime path is enough. But (there is a but) you need to call some function to lazy load them. Right now, this is not obvious: does lunar-vim call this function for me? Should I add it to my config? The answer is both. Lunar vim is calling the lazy load for VSCode like extensions, but it is not calling the one required to load the snipmate extensions, so you need to add require("luasnip.loaders.from_snipmate").lazy_load() to your config. As I said, once you figure it out, it's easy, but having to discover it yourself (and repeat this process for every new user) it's hard.
  • Recommended places to put your snippets and the folder structure. Luasnip is non opinionated on this, but that doesn't mean that lunar could not give what can be considered best practices for having snippets being loaded per file type etc.
  • How to install visual studio code extension snippets. This is quite easy, just add the plugin to packer (I think) but will be nice to have it.

@abzcoding
Copy link
Member

I will be happy to contribute, but I want to first get to an agreement of what is needed so I don't try to do useless PRs.

thank you for taking time and trying to improve lunarvim <3

It have happened to me several times that I could not load a plugin from my configuration. At first I thought it was because I have configured the plugins before trying to require them, but even when I moved the plugin require to the config function of packer I was not able to require them. Explaining at what part of your local config you can add configs that rely on plugin imports will be a nice thing to clarify.

can you give a sample? ( to clarify this part for me )

This is another thing that may look obvious when you have familiarity with the code-base, but that it is not that simple From the outside. "Pretty much all plugins" means that I have to go to the code, see if such a reference exist and it's exact naming, because I may become with a different naming idea than yours.

you don't need to read the code, the auto-completion should be enough

  1. alpha doesn't have on_config_done

Screen Shot 2022-04-09 at 1 19 48 AM

2. telescope does

Screen Shot 2022-04-09 at 1 20 02 AM

Having a section of "config-hooks" or "plugin-configuration" with all the available ones specifically listed will be a nice thing to have

that is a good idea, we should add somewhere here probably

Also, I still don't know what the thing you get is exactly. Is the instance of the plugin? Is the configuration object? Is it equivalent to do require"plugin-name"? Does it change from plugin to plugin?

it is equivalent to do require"plugin-name"

Again, having specific instructions, ideally per plugin, about how to extend lvim settings will be amazing. To me it is still not clear when this configurations are applied: Do they happen after my entire config.lua it's sourced? If that is the case, that means that I can not do anything that relies on those configs inside my config.lua unless is a function that is lazy l

Screen Shot 2022-04-09 at 1 25 19 AM

To give you an example on this last point, this is something I tried to configure telescope:

pickers are under lvim.builtin.telescope.defaults.pickers you forgot the defaults, this should be visible via auto-complete
Screen Shot 2022-04-09 at 1 35 29 AM
Screen Shot 2022-04-09 at 1 35 20 AM

What does extend mean exactly? Do I need to call vim.tbl_deep_extend ? Or is lunar vim just applying whatever I put on the global configuration with some kind of internal default config

this is sadly inconsistent, for example in keybindings you can override the entire config ( we don't extend, we just override)
but for example in plugin config, we do sth like this

  lvim.builtin.telescope = vim.tbl_extend("force", lvim.builtin.telescope, {

we should address this 🤞

but, specially becasue lunar-vim uses a quite old version of it, some guidance should be added.

what do you mean an old version?

@abzcoding
Copy link
Member

also please feel free to do any PR that would improve our documentation, we do welcome any PR that would help users an easier transition into lunarvim, also tyvm for trying to help us improve lunarvim ❤️

@danielo515
Copy link
Contributor Author

can you give a sample? ( to clarify this part for me )

So, for example. I have a configuration for neoclip plugin. I want this plugin to self-register it's own which key configs, but at the time of calling the config function, which-key is never available. here is a slimmed down version of this scenario:

local log = require("lvim.core.log")
local M = {}

M.config = function()
	local status_ok, neoclip = pcall(require, "neoclip")
	if not status_ok then
		log:error("Could not load neoclip config")
		return
	end

	neoclip.setup({
		history = 50,
		enable_persistent_history = true,
		db_path = vim.fn.stdpath("data") .. "/neoclip.sqlite3",
	})
	local function clip()
		local opts = {  bla bla }
		local dropdown = require("telescope.themes").get_dropdown(opts)
		require("telescope").extensions.neoclip.default(dropdown)
	end
	local whk_status, whk = pcall(require, "which-key")
	if not whk_status then
		log:error("Could not load which-key")
		return
	end
	whk.register({
		["<leader>y"] = { clip, "neoclip: open yank history" },
	})
end

return M

Then, on plugins.lua, which is required from config.lua

	{
		"AckslD/nvim-neoclip.lua",
		requires = { "tami5/sqlite.lua" },
		config = function()
			require("user.neoclip").config()
		end,
	},

Most of the times I see the warning about which-key not being available. But maybe this is an issue related to how packer loads plugins and not something lunar-vim specific? At this point, I am a bit confused about how and when plugins are loaded and if I'm supposed to being able to require them on my config files. The graph you have linked twice makes things even worse, because it specifically states that user config is loaded before plugins are setup? So now I'm wondering how it is possible that any of my configs works since many of them load some plugins almost at the root of the config file.

you don't need to read the code, the auto-completion should be enough

That is nice once you realise such kind of functions exist, but I didn't saw them until guess what, I read the code 😛
Having a dedicated section about configuring plugins on the docs, mentioning things like this on_config_done callbacks will be awesome.

pickers are under lvim.builtin.telescope.defaults.pickers you forgot the defaults, this should be visible via auto-complete

See how it is confusing? I did it correctly. Pickers are not,or at least should not, be under defaults key. They are just like extensions, and the telescope documentation states them at the root of the config. In fact it works perfectly fine for me doing it inside the setup complete callback:

lvim.builtin.telescope.on_config_done = function(tele)
	tele.load_extension("frecency")
	tele.load_extension("command_palette")
	tele.load_extension("notify")
	tele.load_extension("file_browser")
	tele.load_extension("lazygit")
	tele.load_extension("packer")
	local opts = {
		pickers = {
			lsp_workspace_symbols = {
				mappings = {
					i = {
						["<cr>"] = function(prompt_bufnr)
							local selection = require("telescope.actions.state").get_selected_entry()
							print(vim.inspect(selection))
						end,
					},
				},
			},
		},
	}

	tele.setup(opts)
end

this is sadly inconsistent, for example in keybindings you can override the entire config ( we don't extend, we just override) but for example in plugin config, we do sth like this

While I understand the reasoning (if the user wants to completely wipe out lunar defaults) I think it should not be this implicit. Maybe providing configuration functions to add your own configs with clear options about merge/replace will be a more clear API?

  lvim.builtin.telescope = vim.tbl_extend("force", lvim.builtin.telescope, {

Is the bit you wrote like that in the actual code? If I get it correctly, tbl_extend gives precedence to the right, meaning you will be overriding any configuration provided by the user in case of conflict.

what do you mean an old version?

Forget about it, I was confusing by reading an old comment on the chat about the luasnips version and the lack of certain method.

@abzcoding
Copy link
Member

abzcoding commented Apr 11, 2022

Most of the time I see the warning about which-key not being available. But maybe this is an issue related to how packer loads plugins and not something lunar-vim specific?

ignore the warning, does the <leader>y command work? cause i have the exact settings and the key registers everytime
Screen Shot 2022-04-11 at 1 45 33 PM

because it specifically states that user config is loaded before plugins are setup? So now I'm wondering how it is possible that any of my configs works since many of them load some plugins almost at the root of the config file

packer first reads all of your configs to be able to create a compiled version of it under ~/.config/lvim/plugin/packer_compiled.lua then we create a cached version of it under ~/.cache/nvim/luacache, that is why all of your config works

Having a dedicated section about configuring plugins on the docs, and mentioning things like this on_config_done callbacks will be awesome.

sounds good to me 👍

See how it is confusing? I did it correctly. Pickers are not,or at least should not, be under defaults key. They are just like extensions, and the telescope documentation states them

you are right, I think that is a bug on our side, we should move out pickers from defaults as we did with extensions
https://github.com/LunarVim/LunarVim/blob/36361e1107d80e2eb2d8d90afebcff40f1198764/lua/lvim/core/telescope.lua#L73-L80

Is the bit you wrote like that in the actual code? If I get it correctly, tbl_extend gives precedence to the right, meaning you will be overriding any configuration provided by the user in case of conflict.

that part is under config which is called way before user config, so it will not override user config, the setup part which is called after using config will respect your config 😅
the reason being -> -- Define this minimal config so that it's available if telescope is not yet available.


anyway, I think we have a couple of steps to take:

  1. move out pickers from defaults
  2. create a section in lunarvim.org explaining on_config_done and a simple explanation of how things work
  3. any more suggestions that you have

@danielo515
Copy link
Contributor Author

that sounds like a plan, I'll be drafting some PRs whenever I can.
As an additional note, here is another pattern that I tried that didn't worked because how lunar internals are:

local M = {}

M.plugin = {
	"ThePrimeagen/harpoon",
	requires = "nvim-lua/plenary.nvim",
	after = "telescope.nvim",
	config = function()
		require("telescope").load_extension("harpoon")
		-- set harpoon keymaps
		lvim.keys.normal_mode["tu"] = "<cmd>lua require('harpoon.term').gotoTerminal(1)<CR>"
		lvim.keys.normal_mode["te"] = "<cmd>lua require('harpoon.term').gotoTerminal(2)<CR>"
		lvim.keys.normal_mode["cu"] = "<cmd>lua require('harpoon.term').sendCommand(1, 1)<CR>"
		lvim.keys.normal_mode["ce"] = "<cmd>lua require('harpoon.term').sendCommand(1, 2)<CR>"
		lvim.builtin.which_key.mappings["a"] = { "<cmd>lua require('harpoon.mark').add_file()<CR>", "ÔóÑ Add Mark" }
		lvim.builtin.which_key.mappings["<leader>"] = {
			"<cmd>Telescope harpoon marks<CR>",
			"ÔÄ¢ Harpoon",
		}

		local whk_status, whk = pcall(require, "which-key")
		if not whk_status then
			return
		end
		whk.register({
			["<leader>1"] = { "<CMD>lua require('harpoon.ui').nav_file(1)<CR>", "Ô¢£ goto1" },
			["<leader>2"] = { "<CMD>lua require('harpoon.ui').nav_file(2)<CR>", " goto2" },
			["<leader>3"] = { "<CMD>lua require('harpoon.ui').nav_file(3)<CR>", " goto3" },
			["<leader>4"] = { "<CMD>lua require('harpoon.ui').nav_file(4)<CR>", " goto4" },
		})
	end,
}

return M

The leader mappings worked as expected, but the normal mappings for lunar vim didn't. Why? Well, my guess is that which key register works at any time, while registering keymaps for lunar vim only works during startup, and you can't add more once all plugins and everything has finished loading.

@abzcoding
Copy link
Member

while registering keymaps for lunar vim only works during startup, and you can't add more once all plugins and everything has finished loading

yes 😅
using this function

https://github.com/LunarVim/LunarVim/blob/b4d5f093a5607d3f14cbf90aebfb25036c3ee272/lua/lvim/keymappings.lua#L144-L160

@seanrmurphy
Copy link

I'm also tying to add neoclip to my telescope config - I'm not familiar with lua and my understanding of the way that Lunarvim all fits together is less than comprehensive. (I'd also like to add telescope-github).

I tried adding the following to lvim.plugins

  {
    "AckslD/nvim-neoclip.lua",
    requires = {
      -- you'll need at least one of these
      { 'nvim-telescope/telescope.nvim' },
      -- {'ibhagwan/fzf-lua'},
    },
    config = function()
      require('neoclip').setup()
    end,
  },

I can see that Lunarvim modifies the Packer config to load this module. It is not, however, registered with Telescope - I am not able to run

:Telescope neoclip

If I do this, however

:lua require('telescope').load_extension('neoclip')

I am then able to use neoclip in Telescope (but I'm not sure if it's really configured correctly).

From the above discussion, I'm not clear on the best approach - do I need to modify lvim.builtin.telescope.extensions - is there a way to extend this, rather than overwrite this?

@danielo515
Copy link
Contributor Author

@seanrmurphy I was about to open a PR to explain this in more detail, because this is one of the problems I struggle with when I started. I think this is what can be considered an actionable item, no @abzcoding ? 😄
My recommendation, and what I will be adding to the docs under the section of Extra Plugins > Telescope extensions (here) is:

Add your extension as usual to the list of plugins. There are several ways to register extensions within telescope, but generally the safer is explained below.
Create the callback function that will be called when telescope has finished loading. Then register your extension within that callback to make sure telescope is available and can register extensions like this:

lvim.builtin.telescope.on_config_done = function(telescope)
  telescope.load_extension "frecency"
  telescope.load_extension "neoclip"
  ...
end

@seanrmurphy
Copy link

Thanks for that @danielo515 - so, to give a concrete example, this is the content that I have in my config.lua which seems to work fine - it is preceded by some over stuff (languages installed, theme to use etc).

<SNIP>...

lvim.plugins = {
  -- { "fatih/vim-go" },
  { "WhoIsSethDaniel/goldsmith.nvim" },
  { "nvim-treesitter/nvim-treesitter-textobjects" },
  { "preservim/vim-markdown" },
  {
    "tzachar/cmp-tabnine",
    run = "./install.sh",
    requires = "hrsh7th/nvim-cmp",
    config = function()
      local tabnine = require "cmp_tabnine.config"
      tabnine:setup {
        max_lines = 1000,
        max_num_results = 10,
        sort = true,
      }
    end,
    opt = true,
    event = "InsertEnter",
  },
  {
    "AckslD/nvim-neoclip.lua",
    requires = {
      -- you'll need at least one of these
      { 'nvim-telescope/telescope.nvim' },
      -- {'ibhagwan/fzf-lua'},
    },
    config = function()
      require('neoclip').setup({
        keys = {
          telescope = {
            i = {
              select = '<cr>',
              paste = '<c-p>',
              paste_behind = '<c-k>',
              replay = '<c-q>', -- replay a macro
              delete = '<c-d>', -- delete an entry
              custom = {},
            },
            n = {
              select = '<cr>',
              paste = 'p',
              paste_behind = 'P',
              replay = 'q',
              delete = 'd',
              custom = {},
            }
          }
        }
      })
    end,
  },


}

lvim.builtin.telescope.on_config_done = function(telescope)
  telescope.load_extension "neoclip"
end

@danielo515
Copy link
Contributor Author

danielo515 commented May 30, 2022 via email

@BartOtten
Copy link

The instructions do not seem to work reliably with telescope-project. Have pasted the plugin snippet and added project in the callback, ran :PackerSync a few times to be sure but it rarely works (sometimes is does….)

@kylo252 kylo252 transferred this issue from LunarVim/LunarVim Oct 11, 2022
@LostNeophyte LostNeophyte mentioned this issue Jan 16, 2023
9 tasks
@baipaiha
Copy link

Thanks for lunarvim work !
Since documentation need a lot of man power, maybe a listing of user configuration repository could help ?
Currently, i use https://github.com/ChristianChiarulli/lvim as configuration example,

@adamwojt
Copy link

The extension points are pain for core plugins. I can't seem to find a way to do it for LuaSnip. The only way is to have dirty git source with custom patches or completely override config for it.
I just wanted to add require("luasnip").config.setup({store_selection_keys="<Tab>"}) so that I can use selection snippets.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

7 participants