Skip to content

Conversation

@usfbih8u
Copy link
Contributor

@usfbih8u usfbih8u commented Feb 4, 2025

It's a simple implementation; it does not support placeholders and only correctly indents the text.

@Andriamanitra
Copy link
Owner

To be honest I prefer not even displaying the snippet completions until placeholders are supported.

@usfbih8u
Copy link
Contributor Author

usfbih8u commented Feb 4, 2025

Most of the snippets are not useful until the placeholder situation is addressed.

I primarily implemented this to test how my tooltip module handles newlines from snippets (which I will probably publish this week).

I need to see how the snippets work. I don't even know if this type of snippet is standardized across all LSPs or not.

@usfbih8u
Copy link
Contributor Author

usfbih8u commented Feb 6, 2025

https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax

@Andriamanitra
Copy link
Owner

Yeah it's not a simple thing to implement. 😅 I'm really not sure how to make the tab stops work in micro (at least without some ugly hacks to get around autocomplete) which is why I didn't even bother trying.

When a completion with a placeholder is inserted, a "search" its executed, and
the placeholders are highlighted. You can keep cycling between suggestions with
`Tab` and `Backtab` as usual.

When a completion with placeholders is selected, you can cycle between the
placeholders with `FindNext` and `FindPrevious`.
@usfbih8u
Copy link
Contributor Author

usfbih8u commented Feb 8, 2025

I implemented partial support for placeholders. Long story short, I use regex and a "find region" to mark the placeholders in the completion. You can cycle forward and backward through the completions with Tab, as usual, and then use FindNext and FindPrevious to move between placeholders.

I only tested it with luals. Let me know what you think.

P.S. If you don't mind, could you test my plugin? It creates a tooltip for the suggestions. Maybe you'll like it.

@Andriamanitra
Copy link
Owner

Andriamanitra commented Feb 8, 2025

I implemented partial support for placeholders. Long story short, I use regex and a "find region" to mark the placeholders in the completion. You can cycle forward and backward through the completions with Tab, as usual, and then use FindNext and FindPrevious to move between placeholders.

I only tested it with luals. Let me know what you think.

I tested with luals by typing "func<Tab>" to trigger the function snippet. but it doesn't seem to work properly, I'm unable to cycle between the tab stops. I actually couldn't figure out how to get snippet completions from any other language server I tried 😅

P.S. If you don't mind, could you test my plugin? It creates a tooltip for the suggestions. Maybe you'll like it.

I tried to install it but when starting micro there was an error from this line:

TooltipModule = require('micro-autocomplete-tooltip.tooltip')

with the following error message:

Plugin autocomplete_tooltip: autocomplete_tooltip:23: module micro-autocomplete-tooltip.tooltip not found:
	no field package.preload['micro-autocomplete-tooltip.tooltip']
	stat /tmp/tmp.oXQxPk3fqB/plug/micro-autocomplete-tooltip/tooltip.lua: no such file or directory
	stat ./micro-autocomplete-tooltip/tooltip.lua: no such file or directory
	stat /usr/local/share/lua/5.1/micro-autocomplete-tooltip/tooltip.lua: no such file or directory
	stat /usr/local/share/lua/5.1/micro-autocomplete-tooltip/tooltip/init.lua: no such file or directory, 
stack traceback:
	[G]: in function 'require'
	autocomplete_tooltip:23: in main chunk
	[G]: ?

@usfbih8u
Copy link
Contributor Author

usfbih8u commented Feb 8, 2025

Sorry, I did not mention that you have to use lsp autocomplete. You are using the settings.tabAutocomplete option, right? I forgot about that option (put it to false if it does not work). 😅

The error message is because it cannot find the module.

  • Are you on Windows?
  • If you are on Linux, are you installing it in a non-standard location or at least not in your normal config directory? The function require needs to search in the package.path, similar to how $PATH works with binaries. I assume you are using a temporary directory or something similar?

This line in the plugin is responsible for adding the path for the plugin to the package.path:

package.path = config.ConfigDir .. "/plug/?.lua;" .. package.path

The require function expects to find the module inside the plug directory within a config directory. You can change the directory with micro -config-dir if you do not want to place it in your normal .config/micro/plug directory. I suspect that is the problem. Did you install it in a different location? Did you use the bash script?

I tried it myself; I cloned the repo into a fake directory and used micro -config-dir ../../fake_micro_config/, and it worked as well.

Using this setup, my config.ConfigDir is ../../fake_micro_config/, so if the plugin is located in ../../fake_micro_config/plug/micro-autocomplete-tooltip, it should work.

@usfbih8u
Copy link
Contributor Author

usfbih8u commented Feb 8, 2025

I tried tabAutocomplete = true, and it works as well.

Further tests:

  1. I created another fake config directory (so it is a "fresh" instance).
  2. I copied the mlsp plugin there, with this branch selected (the config.lua has tabAutocomplete = true).
  3. I ran micro -config-dir ../../../fake2_micro/ main.lua.
  4. I executed lsp start.
  5. I typed func<Tab>, and it worked.

This was the completion:

function $1($2)
	$0
end

I also tried fo<Tab> and could cycle through different "for" snippets.

I do not know what is going on.

@Andriamanitra
Copy link
Owner

I also get the completion

function $1($2)
    $0
end

but after accepting the snippet pressing Tab should take the cursor to $1, then $2, and finally $0 (or end of snippet if $0 wasn't present). That's the part I can't get working.


The error with your plugin was that the require() assumed that it had been cloned into $MICRO_CONFIG_DIR/plug/micro-autocomplete-tooltip, using the same name for the directory as the name of the repository, but I had given it a shorter name (git clone https://github.com/usfbih8u/micro-autocomplete-tooltip $MICRO_CONFIG_DIR/plug/tooltips). I got it working now.

I didn't know something like this was even possible in micro so that's very cool. I would love to have tooltips like this for hoverAction. And although I prefer completions to stay out of my way for the most part, a vertical list would open up room to include extra information along with the completions (eg. type signatures). I wonder if there's a way to implement this without triggering micro's own autocomplete at all, so you wouldn't get the usual autocomplete menu in the statusline in addition to the tooltip?

@usfbih8u
Copy link
Contributor Author

but after accepting the snippet pressing Tab should take the cursor to $1, then $2, and finally $0 (or end of snippet if $0 wasn't present). That's the part I can't get working.

From my previous message:

"and then use FindNext and FindPrevious to move between placeholders."

The use of tabstops, as you describe, could be achieved, but there are problems.

Micro cannot jump between searches other than sequentially, so the placeholders must be in order. Micro only searches for the next match with the last regex/literal; the highlighter gives the false illusion of a list of matches. The jump to the end of the snippet would require adding more state to the plugin; not sure if it’s worth it.

You could always cycle over all the matches, detecting all of them, create a sorter, then append the end of the snippet to that list, and fake iterate over them...(?)

When you "accept" the snippet, the suggestions are reset inside Micro, and then you listen to Autocomplete() and onAnyEvent() (this detects a lot of internal things, and it’s problematic; I will change it in the future), maybe use onBeforeText() and all the tab and split changes, etc. There are a lot of "ifs" on top of the previous constraints.

I think that using the default search functions is the best option until Micro provides at least a real list of matches. I think there should be a good reason to do that here instead of in Micro (primarily, not knowing Go, like me). And users are familiar with that.

The error with your plugin was that the require() assumed that it had been cloned into $MICRO_CONFIG_DIR/plug/micro-autocomplete-tooltip, using the same name for the directory as the name of the repository, but I had given it a shorter name (git clone https://github.com/usfbih8u/micro-autocomplete-tooltip $MICRO_CONFIG_DIR/plug/tooltips).

Yes, it's on purpose. If you use only the file name and that file exists in one of the paths inside package.path, you will load that one. This happened to me with my other plugin that uses the tooltip module. Now, the plugin directory name is used as a prefix to avoid name collisions.

I didn't know something like this was even possible in micro so that's very cool. I would love to have tooltips like this for hoverAction.

I already implemented a plugin to display the diagnostic messages (and other gutter messages) in a tooltip. The inconvenience is that I have to format the text into lines in order to have a real background (Micro did not consider buffers on top of each other; in fact, the mouse does not work very well—something with "layers" probably). You have to pad the lines with spaces to make it look like a box, and that has its limitations regarding highlighting/syntax.

And although I prefer completions to stay out of my way for the most part, a vertical list would open up room to include extra information along with the completions (eg. type signatures).

I was playing with that when you merged your last PR, using the labels as suggestions, adding the kinds, etc.

I wonder if there's a way to implement this without triggering micro's own autocomplete at all, so you wouldn't get the usual autocomplete menu in the statusline in addition to the tooltip?

The answer is yes, but it comes with trade-offs.

In the case of my plugin, it depends on the suggestions and completions of the buffer. So, in order to display that info, you will also affect the status line suggestions.

That being said, as I mentioned before, the plugin has to format each suggestion to pad the text like a box, so it is completely possible to change that text without changing the status line at all.

tooltip:

for i = ...    Snippet
format         Variable
formatAction   Function

statusline:

for i = ...  format formatAction

But you cannot communicate that extra information to the plugin. There are options:

a. Detecting at runtime if my plugin is installed; if it is, you add that information formatted to the status line (my plugin will pad it), not caring at all how the status line looks. If it’s not installed, you leave the current implementation. The status line will look like this:

for i = ...    Snippet format         Variable formatAction   Function

b. Add my plugin to mlsp with modifications; do not use Micro's suggestions to avoid affecting the status line (and conflicting with my plugin). Create the "buffer-text-box" and then use Autocomplete, onAnyEvent, etc., to handle all of that (on top of the implementation of the tab stops).

@usfbih8u
Copy link
Contributor Author

I found a bug. When the autocompletion is direct (only 1 match), the placeholder search does not work. I have to see how it works internally.

This can be reproduced with the example you used: func<Tab> (the last time I had more results and it did not happen).

function $1($2)
	$0
end

With fo<Tab>, it works as expected; all placeholders are highlighted and FindNext & FindPrev are working.

@usfbih8u
Copy link
Contributor Author

a vertical list would open up room to include extra information along with the completions (eg. type signatures). I wonder if there's a way to implement this without triggering micro's own autocomplete at all, so you wouldn't get the usual autocomplete menu in the statusline in addition to the tooltip?

Yes, it is possible, and it is done. I was able to get the native micro autocompletions without displaying anything in the statusline (it does not work for InfoBar because it does not trigger actions).

I am currently testing the triggers for completion and signature (also the manual call of signatureHelp; this uses my tooltip module) with it. The base functionality is done, but it needs to be polished.

This new plugin will not autocomplete directly and could be called externally to set its suggestions from other plugins. This is primarily to improve the synergy with mlsp.

The API is:

---@param bufpane `BufPane` where the tooltip is created
---@param suggestions `string[]` is used internally to match the output of `GetWord()`.
---@param completions `string[]` 
---@param displayedSuggestions `string[]` is the suggestion displayed and can contain more information on it.
---@param filetype `string` tooltip's buffer syntax to being able to have external customization (this is not done yet but should be trivial).
function ShowSuggestions(bufpane, suggestions, completions, displayedSuggestions, filetype)

The plugin keeps this information until the tooltip is closed.

Example (| simulates where the cursor is), suggestions from mlsp are:

1. `f|or .. ipairs` => displayed as `🗒️ for .. ipairs [Snippet] `
2. `f|or .. pairs`  => displayed as `🗒️ for .. pairs  [Snippet] `
3. `f|mt`           => displayed as `📦 fmt           [Package] `
4. `f|ormat`        => displayed as `💲 fmt           [Variable]`

So the text inserted is matched against the suggestion, but the displayed text is what the tooltip's buffer contains. This list changes while you type; then you use ENTER to insert the completion.

The important thing is to make suggestions and displayedSuggestions similar, so the user understands against what is matching the text.

If you type 'o', the list will be modified and only display 1 and 2; if you remove the 'o' and type 'm', the other 2 will be displayed. The tooltip is closed if the trigger location (initial location of the active cursor) is surpassed to its left or if you insert something that does not match anything. Enter is used to enter the completion.
I need to see what problems I encounter while using it, but it looks promising.

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.

2 participants