Sublime Forum

How to implement multiline code suggestions in Sublime Text?

#1

Hi there, I want to implement AI code suggestions for Refact because our clients ask for it.

The behavior I’m looking for:

  1. Complete the current line until the end, using a grey (or ghost, or inline) text. Tab accepts it, Esc hides.

  2. Write several lines of grey text when the current line is empty, most likely after the user hits Enter.

What I found so far:

  • I can easily implement a LSP server, add items to the completions popup.

  • The “mini_auto_complete” option disables popup, writes grey text instead. Popup is still available using Option+Space.

It’s almost a usable solution, but there is no way to show multiline grey text suggestions. Here is what is shows if I set “label” to “line1\nline2\nline3”:

And when I press Tab, it accepts the suggestion correctly.

Possible solutions I can think of:

  1. Make “mini_auto_complete” show multiline suggestions as well,

  2. Make popup show a multiline label, such as function body, might be bigger than the popup itself,

  3. Write a plugin that will produce a custom popup, but popups can be distracting, and it’s not clear which popup to show at any given point.

I personally like option (1) the most, any thoughts?

0 Likes

#2

There is no good solution at this point in ST imho.

https://github.com/TerminalFi/LSP-copilot eventually chose to go the popup way.
image

@timfjord even tried to use a phantom to mimic “ghost text” but it doesn’t result in good UX. https://github.com/sublimehq/sublime_text/issues/5536


  1. Make “mini_auto_complete” show multiline suggestions as well,

mini_auto_complete is an hidden experimental feature which has various issues (?). Plus, doesn’t it work with multiline completion?

  1. Write a plugin that will produce a custom popup, but popups can be distracting, and it’s not clear which popup to show at any given point.

This is the LSP-copilot way. I actually think this is okay or the current best choice.

Another though: use a dedicate view/panel rather than popup.

2 Likes

#3

Hey @jfcherng maybe you can give me a hint with this:

sublime-multiline-insert

When inserting a multiline suggestion, unfortunately the editor uses auto indent and messes up the inserted text. I tried CompletionItem(…, insert_text_mode=InsertTextMode.AsIs) but it didn’t help.

Can you point me to the code in Sublime that handles this? I’d like to find out what flags it looks at, if at all.

Another (smaller) problem is code completion triggering. It does not trigger on backspace, which is bad but bearable, but it doesn’t trigger on Enter! That is a common use case: write one line, let the AI model to write 5 more. It’s still a minor problem IMO, because triggering the completion manually still finishes the job. The auto intent issue is worse, makes multiline suggestions unusable :neutral_face:

My work-in-progress is at https://github.com/smallcloudai/refact-lsp if you want to take a look.

0 Likes

#4

Suppose you are using the LSP plugin as Sublime’s client.

It seems that it doesn’t claim supporting InsertTextMode.AsIshttps://github.com/sublimelsp/LSP/blob/1ac2b3e46cc4e9f0cc1e837e0eaad559c92158ed/plugin/core/sessions.py#L289-L291

0 Likes

#5

Ah I see. It’s the LSP plugin doing this. Now at least I know where to look at, thanks!

0 Likes

#6

I wonder whether this addition in the LSP package would do it (for non-snippet completions):

--- LSP/plugin/completion.py
+++ LSP/plugin/completion.py
@@ -11,6 +11,7 @@ from .core.protocol import CompletionParams
 from .core.protocol import Error
 from .core.protocol import InsertReplaceEdit
 from .core.protocol import InsertTextFormat
+from .core.protocol import InsertTextMode
 from .core.protocol import MarkupContent, MarkedString, MarkupKind
 from .core.protocol import Range
 from .core.protocol import Request
@@ -353,8 +354,17 @@ class LspSelectCompletionCommand(LspTextCommand):
             new_text = new_text.replace("\r", "")
         if item.get("insertTextFormat", InsertTextFormat.PlainText) == InsertTextFormat.Snippet:
             self.view.run_command("insert_snippet", {"contents": new_text})
-        else:
+        elif item.get("insertTextMode", InsertTextMode.AdjustIndentation) == InsertTextMode.AdjustIndentation:
             self.view.run_command("insert", {"characters": new_text})
+        else:  # InsertTextMode.AsIs
+            for region in reversed(self.view.sel()):
+                pt = region.b
+                if not text_edit and \
+                        bool(self.view.classify(region.b) & (sublime.CLASS_WORD_START | sublime.CLASS_WORD_END | 512)):
+                    replacement_region = self.view.word(region.b)
+                    pt = replacement_region.a
+                    self.view.erase(edit, replacement_region)
+                self.view.insert(edit, pt, new_text)
         # todo: this should all run from the worker thread
         session = self.session_by_name(session_name, 'completionProvider.resolveProvider')
         additional_text_edits = item.get('additionalTextEdits')

Do you plan to support the new Inline Completion Request which was recently added to the LSP specs?

1 Like

#7

Thank you @jwortmann that’s very interesting. You’ve answered a week ago and I’m only seeing it now, I need to turn on notifications in this forum.

Can you help us to add InlineCompletion from 3.18 to Sublime and the LSP plugin? Or can you recommend us someone who can help?

(I’ll try you patch later ~today)

0 Likes