Skip to content

Overlays, example overlay plugins #3733

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

glupi-borna
Copy link
Contributor

Implemented more or less the way I described here.

Some notes:

  • I've cut things down and made them as simple as possible on the go side. This naturally pushes a bit of complexity to the plugin side of things, but I think that the compromise is decent and allows plugin authors a lot of flexibility in how they want to do things.
  • I have created two example plugins and added them alongside the default plugins, but I don't think they should become default by any means - it just seemed like the cleanest way to include some examples in this draft PR. The plugins show how to implement an autocomplete overlay that shows up next to the cursor, and how to implement a search/palette type of widget. Hopefully the code for these is clear enough.
  • Proper event handling in plugin code is not the simplest affair to figure out, so the examples include what I thought was a nice way to handle events in an immediate-mode fashion.
  • Creating a more involved widget (e.g. an input box) is a bit complex, as there are a lot of events to handle, and a lot of behaviors to implement. A very barebones example of this can be found in the quickmenu plugin.

I'm hoping plugin authors can also weigh in and give some feedback!

@Andriamanitra
Copy link
Contributor

Thanks for taking the initiative on this one!

I tried it with a tiny test plugin that draws an overlay and allows moving it around with arrow keys1 and it seems to work pretty well. I like the API for registering an overlay and drawing, it's easy enough to get started quickly. I will definitely use this in µlsp for showing hover information and menus for taking code actions / jumping to results of find symbol if/when this gets merged. Event handling seems to be a bit clunky when you want to do more advanced stuff like type into the overlay – in that use case it would be nice to have something that works like a hidden bufpane which handles all events while the overlay is visible (so you don't need to handle and ignore all possible events to emulate capturing focus).

Some criticism:

  • overlay.DrawText vertical clipping seems to be off by one
  • The cursor is drawn on top of overlays (screenshot)
  • I don't like the overlay.StringToStyle API that takes an arbitrary string that needs to be formatted just right.
  • As you mentioned in the comment overlay.BufPaneScreenRect is unnecessary, bp:GetView() already exists. overlay.BufPaneScreenLoc could maybe also just be a method on BufPane?

Footnotes

  1. https://gist.github.com/Andriamanitra/2989976f8dc11fd2cc46f81eba6604d2

@cutelisp
Copy link
Contributor

cutelisp commented May 5, 2025

I am trying to make my first plugin, and I had in my plans to create a simple settings menu for my plugin.
I gave it a try using overlay, and some of "quickmenu" plugin logic cutesettings

It seemed to work, although when triggering actions the bp does not show its "real state"
If you run my example you can see the cursor and the statusline aren't synced.
(Maybe this is just a bad approach by me on lua side)

To create text boxes maybe would be nice to add a softwrap flag?

@glupi-borna
Copy link
Contributor Author

@Andriamanitra

hidden bufpane which handles all events while the overlay is visible

Literally reading my mind. I actually started working on something like this, but ended up scrapping it because it was getting a bit complicated, and I didn't want to make this PR too big to get accepted. But hopefully we can get something like it implemented in the future, it would be very nice for more advanced use cases (like @usfbih8u mentioned in the tooltip issue).

overlay.DrawText vertical clipping seems to be off by one

Embarrassing! I'll have to get that fixed.

The cursor is drawn on top of overlays

Yeah, I noticed this too - not sure what to do about it, though. There is an old PR (#2632) that adds a hidecursor option, which looks like it could work. If that got merged, maybe we could simply toggle that when the overlay is created/destroyed? On the other hand, looking at it now, hidecursor is a local-only option, which won't work for plugins that are not bound to a buffer (i.e. a floating keybind cheatsheet, etc).

It might also be okay to let the plugin optionally control where the cursor is drawn? Dunno. Could use some suggestions here.

the overlay.StringToStyle API

Yep, it's kinda bad - the saving grace is that the syntax is the same as micro's theme syntax, so it's not completely arbitrary, but I agree that it is weird. GetColor is a bit better, and apparently it also falls back to StringToStyle, so maybe StringToStyle can get canned. We could also bikeshed a completely different API, if anyone feels like it. :)

overlay.BufPaneScreenLoc could maybe also just be a method on BufPane

Nice, I just tried that and it's much better. Just bp:ScreenLoc(loc) and you're off to the races. The only thing that's keeping me from outright removing both BufPaneScreenLoc and BufPaneScreenRect is for their discoverability in help plugins. But honestly, I think that the help files could be improved a lot in general, especially for onboarding people to plugin development.

https://gist.github.com/Andriamanitra/2989976f8dc11fd2cc46f81eba6604d2

We are this close to having micro be a game engine, just like emacs :D

@cutelisp

If you run my example you can see the cursor and the statusline aren't synced

I didn't have time to check it out yet, but at a glance, I don't see anything obviously wrong with your code. One thing that comes to mind is that, since overlays are rendered after everything else, any changes you make to micro's state won't be reflected until the next screen update. You can force a screen update by calling overlay.Redraw(), but be careful not to call it every frame (nothing would break, but it would be wasteful, since it would cause micro to render as often as possible).

To create text boxes maybe would be nice to add a softwrap flag?

Yes! Actually, I initially implemented DrawText with softwrap, but then I realized that clipping is probably a better behavior for general plugin needs (i.e. scrolling containers). I'm open to adding a utility function for softwrapping/wordwrapping a string to some width, but probably in a different PR!

@cutelisp cutelisp mentioned this pull request May 8, 2025
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.

3 participants