Skip to content

Introduce LspPlugin API#2739

Open
rchl wants to merge 98 commits intomainfrom
feat/new-plugin-api
Open

Introduce LspPlugin API#2739
rchl wants to merge 98 commits intomainfrom
feat/new-plugin-api

Conversation

@rchl
Copy link
Copy Markdown
Member

@rchl rchl commented Jan 27, 2026

"Introduce new plugin API" is a little over the top. I just wanted to jump-start it with one new method to allow for progressive enhancement and get early feedback.

The new handle_update_or_installation_async method combines needs_update_or_installation and install_or_update. Those were split because the code sets installing... status text if needs_update_or_installation returns true but that meant that various if checks had to be duplicated across those two and generally made the logic harder to read. With this one method, there is now a callable function passed that can be used to start the progress (perhaps unnecessary since user could use configuration.set_view_status but then that would likely lead to inconsistent status messages across packages).

The new method takes a dict with params. This is to allow adding new params without breaking API compatibility.

Most class methods are passed PluginContext now with configuration instance now to allow packages to get resolved configuration. Some packages currently read settings with sublime.load_settings but that has the problem of not considering project overrides.

Resolves #2039
Resolves #2491

@rchl rchl force-pushed the feat/new-plugin-api branch from c8f89be to f1b0dee Compare January 27, 2026 08:07
@rchl
Copy link
Copy Markdown
Member Author

rchl commented Jan 27, 2026

Actually just dumped some old code that I had in stash.

@jwortmann

This comment was marked as resolved.

@rchl rchl marked this pull request as draft January 30, 2026 08:42
@rchl rchl force-pushed the main branch 2 times, most recently from f806ca0 to 90081a4 Compare February 4, 2026 22:22
@rchl rchl changed the title Introduce new plugin API New Plugin API Feb 12, 2026
selector: str,
priority_selector: str | None = None,
schemes: list[str] | None = None,
command: list[str] | None = None,
Copy link
Copy Markdown
Member Author

@rchl rchl Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been thinking about case that tends to come up often - people want to use non-managed server binary.

Overriding command is one way to do it and it would work for simple cases but in more complex cases like LSP-clangd where there is a lot of arguments and some are even programmatically adjusted during start, it wouldn't work.

So my thought is that, given we use a variable like $server_binary, user should be able to override just the variables from the config file. It would need to override variables added through the additional_variables API.

Just a little concerned that it's not the simplest to explain to the user. Or at least will require a bunch of extra doc in every package but that's the best I can think of.

@rchl rchl changed the title New Plugin API Introduce LspPlugin API Apr 10, 2026
Comment on lines +319 to +320
@classmethod
def register(cls) -> None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could add the typing.final decorator here and also for unregister below, to make it clear that these methods shouldn't be overridden.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if that's necessary because there is nothing wrong with overriding it if super() is called also (which is something that one should always think of anyway in such cases).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But there should be no reason to override these methods, because the only place where they should get called from is in plugin_loaded and plugin_unloaded. Anything else that should be done at the same time could also be put into those ST API methods separately.

And since all other methods in LspPlugin are meant to be configurable (except for __init_subclass__, but that seems clear from the name), I would explicitly mark them as not intended to be overridden.

@jwortmann
Copy link
Copy Markdown
Member

@predragnikolic Do you maybe see anything else for the LspPlugin class in api.py that could be improved? Like the method names, or the parameter names and types, or is anything else missing?

I think it looks mostly good from my side now, but we should try to get it "perfect" now, so that we don't need any breaking changes or awkward additions later.

@predragnikolic
Copy link
Copy Markdown
Member

I will have some free time at the begging of next week. (Mon, Tue)

I'll try to find time to go through the changes and try to migrate some plugins to try out the new API (I will do it only locally).

Maybe I will return with some feedback.
Else if I cross that timeline I would suggest to not wait on me.
And maybe transition one the most complicated plugins first just to see if something seems like it needs additional tweaking.

@rchl
Copy link
Copy Markdown
Member Author

rchl commented Apr 11, 2026

I'm toying with the idea of making ClientConfig fully typed but discussing it here could get messy so created discussion for it - #2860

Feel free to chime in with opinions or better ideas.

@rchl
Copy link
Copy Markdown
Member Author

rchl commented Apr 12, 2026

I think I should also add a method for extending env to support this functionality from lsp_utils.

@rchl
Copy link
Copy Markdown
Member Author

rchl commented Apr 12, 2026

I should probably add something like below to be consistent but man I don't like it...

    @classmethod
    def env(cls, context: PluginContext) -> dict[str, str]:
        return context.configuration.env

Now in the override one has multiple options:

  • call super() to get the value, then update it and then return
  • update and return context.configuration.env
  • update context.configuration.env but return empty object
  • return new object with custom env.

I don't like it because it's so unclear how LSP handles the returned value. Does it merge or override existing values?
Of course all can be explained in the documentation of the method but it should be just clearer without reading docs.

If there would be a method that just has context as an argument, returns None and lets user do anything to the ClientConfig then at least it wouldn't leave any room for misunderstanding.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide Transport as parameter when invoking plugin.on_post_start Expose ClientConfig in more places in the Plugin API

3 participants