Skip to content

[RFC] Add a plugin namespace#1334

Open
fischerling wants to merge 2 commits intomartanne:masterfrom
fischerling:plugin-namespace
Open

[RFC] Add a plugin namespace#1334
fischerling wants to merge 2 commits intomartanne:masterfrom
fischerling:plugin-namespace

Conversation

@fischerling
Copy link
Copy Markdown
Collaborator

@fischerling fischerling commented Apr 10, 2026

Multiple times we needed a way for plugins to communicate with each other.
Currently the user has to do the plumbing in their visrc.lua file.

This proposal simply adds a global table vis.plugins as central namespace
for all plugins to expose their APIs.

Each plugin would insert its own table vis.plugins['plugin-name'].

This allows plugins to be easily extended by other plugins without
duplicating a lot of user configuration code.

Usecases:

  • The status line could be extendable
  • The word-completion could be extended
  • The lspc configuration could be changed by an editorconf plugin
  • ...

Questions

  • Do we want this in general?
    • In my opinion it is really simple but powerful, therefore a good addition.
  • Should the plugin namespace be userdata exposed by C to prevent it from be deleted vis.plugins=nil?
  • Should the table be read-only and use registration functions to add new plugin subtables?

Related

Fixes #1332.

Currently plugins have no way to communicate with each other without
the user doing the plumbing in their visrc.lua file.
By introducing a global namespace vis.plugins each plugin can expose
its API or Configuration in its own namespace vis.plugins['plugin-name'].

This allows plugins to expose hooks for others to insert data, export
utility functions or build extendable "base" plugins.
External completions sources may insert a completion hook to
insert their completions into the complete-word plugin.
A hook is inserted by adding it to the table:
vis.plugin['complete-word'].completion_hooks

Each hook is called with the word prefix to complete and MUST
return a table containing the completion candidates.

	table.insert(vis.plugins["complete-word"].candidate_hooks,
	function(prefix)
		return {prefix.."foo"}
	end)
@TwoF1nger
Copy link
Copy Markdown

Multiple times we needed a way for plugins to communicate with each other.
Currently the user has to do the plumbing in their visrc.lua file.

Is there an example of this plumbing that I could take a look at?

I'm asking because a few of my plugins do customize each other via hooks, but no action from the user is necessary. The user doesn't even need to know about it.

The thing is, my hooks are just functions in the global namespace. On one hand, this runs the risk that another plugin, or the user, defines a symbol with that same name. On the other hand, this keeps everything working even if the user renames the plugins to their liking. Which is not possible if you use per-plugin tables.

Another thing - if per-plugin tables are what you want, can't you use package.loaded['name'] instead, since it already exists. The same API exposed by returning a module table can be accessed directly by other plugins, without any plumbing by the user (IIUC what you mean by plumbing).

@fischerling
Copy link
Copy Markdown
Collaborator Author

Multiple times we needed a way for plugins to communicate with each other.
Currently the user has to do the plumbing in their visrc.lua file.

Is there an example of this plumbing that I could take a look at?

My visrc.lua has no example because the plugins I use are not integrated.
For example lspc exposes its select implementation via the returned module table.
If a second plugin should use this function, without using the global namespace the user has to register the select implementation for use by the second plugin.

local lspc = require('vis-lspc')
local plugin2 = require("plugin2")
plugin2.use_select_impl(lspc.select)

I'm asking because a few of my plugins do customize each other via hooks, but no action from the user is necessary. The user doesn't even need to know about it.

The thing is, my hooks are just functions in the global namespace. On one hand, this runs the risk that another plugin, or the user, defines a symbol with that same name. On the other hand, this keeps everything working even if the user renames the plugins to their liking. Which is not possible if you use per-plugin tables.

In general I dislike using globals, but for our use case the "C approach" (globals with a safe prefix (vis_plugin_foo)) should be totally fine.

Using a per plugin table under a namespace controlled by the plugin is what I propose.
Plugin foo would register itself with the chosen name foo.
Therefore, it does not matter how the user is requiring the plugin.
This eliminates the use of globals and has the same effect as your approach.

Another thing - if per-plugin tables are what you want, can't you use package.loaded['name'] instead, since it already exists. The same API exposed by returning a module table can be accessed directly by other plugins, without any plumbing by the user (IIUC what you mean by plumbing).

I did not know about package.loaded indeed this is similar to the more narrow suggested vis.plugins.
However, it has the disadvantage that the names are not stable.
If I require vis-lspc as lspc how should another plugin know the local name I used to require vis-lspc?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request]: Extend Ctrl+N completions

2 participants