Skip to content
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

Add support for inline completions (WIP) #2552

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion ColorSchemes/Breakers.sublime-color-scheme
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
{
"scope": "meta.semantic-token",
"background": "#00000001"
}
},
{
"scope": "meta.inline-completion",
"foreground": "color(var(grey3) alpha(0.6))",
"font_style": "italic"
},
]
}
7 changes: 6 additions & 1 deletion ColorSchemes/Celeste.sublime-color-scheme
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
{
"scope": "meta.semantic-token",
"background": "#00000001"
}
},
{
"scope": "meta.inline-completion",
"foreground": "color(var(black) alpha(0.6))",
"font_style": "italic"
},
]
}
7 changes: 6 additions & 1 deletion ColorSchemes/Mariana.sublime-color-scheme
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
{
"scope": "meta.semantic-token",
"background": "#00000001"
}
},
{
"scope": "meta.inline-completion",
"foreground": "color(var(white3) alpha(0.6))",
"font_style": "italic"
},
]
}
7 changes: 6 additions & 1 deletion ColorSchemes/Monokai.sublime-color-scheme
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
{
"scope": "meta.semantic-token",
"background": "#00000001"
}
},
{
"scope": "meta.inline-completion",
"foreground": "color(var(white3) alpha(0.6))",
"font_style": "italic"
},
]
}
7 changes: 6 additions & 1 deletion ColorSchemes/Sixteen.sublime-color-scheme
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
{
"scope": "meta.semantic-token",
"background": "#00000001"
}
},
{
"scope": "meta.inline-completion",
"foreground": "color(var(grey5) alpha(0.6))",
"font_style": "italic"
},
]
}
22 changes: 17 additions & 5 deletions Default.sublime-keymap
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,26 @@
// "args": {"overlay": "command_palette", "text": "LSP: "}
// },
// Insert/Replace Completions
// {
// "keys": ["UNBOUND"],
// "command": "lsp_commit_completion_with_opposite_insert_mode",
// "context": [
// {"key": "lsp.session_with_capability", "operand": "completionProvider"},
// {"key": "auto_complete_visible"}
// ]
// },
// Insert Inline Completion
{
"keys": ["alt+enter"],
predragnikolic marked this conversation as resolved.
Show resolved Hide resolved
"command": "lsp_commit_completion_with_opposite_insert_mode",
"context": [
{"key": "lsp.session_with_capability", "operand": "completionProvider"},
{"key": "auto_complete_visible"}
]
"command": "lsp_commit_inline_completion",
"context": [{"key": "lsp.inline_completion_visible"}]
},
// Show next Inline Completion
// {
// "keys": ["UNBOUND"],
// "command": "lsp_next_inline_completion",
// "context": [{"key": "lsp.inline_completion_visible"}]
// },
// Save all open files that have a language server attached with lsp_save
// {
// "keys": ["UNBOUND"],
Expand Down
6 changes: 6 additions & 0 deletions boot.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
from .plugin.hover import LspToggleHoverPopupsCommand
from .plugin.inlay_hint import LspInlayHintClickCommand
from .plugin.inlay_hint import LspToggleInlayHintsCommand
from .plugin.inline_completion import LspCommitInlineCompletionCommand
from .plugin.inline_completion import LspInlineCompletionCommand
from .plugin.inline_completion import LspNextInlineCompletionCommand
from .plugin.panels import LspClearLogPanelCommand
from .plugin.panels import LspClearPanelCommand
from .plugin.panels import LspShowDiagnosticsPanelCommand
Expand Down Expand Up @@ -99,6 +102,7 @@
"LspCollapseTreeItemCommand",
"LspColorPresentationCommand",
"LspCommitCompletionWithOppositeInsertMode",
"LspCommitInlineCompletionCommand",
"LspCopyToClipboardFromBase64Command",
"LspDisableLanguageServerGloballyCommand",
"LspDisableLanguageServerInProjectCommand",
Expand All @@ -120,7 +124,9 @@
"LspHierarchyToggleCommand",
"LspHoverCommand",
"LspInlayHintClickCommand",
"LspInlineCompletionCommand",
"LspNextDiagnosticCommand",
"LspNextInlineCompletionCommand",
predragnikolic marked this conversation as resolved.
Show resolved Hide resolved
"LspOnDoubleClickCommand",
"LspOpenLinkCommand",
"LspOpenLocationCommand",
Expand Down
6 changes: 6 additions & 0 deletions docs/src/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,9 @@ The color scheme rule only works if the "background" color is (marginally) diffe
| ----- | ----------- |
| `markup.accent.codelens.lsp` | Accent color for code lens annotations |
| `markup.accent.codeaction.lsp` | Accent color for code action annotations |

### Inline Completions

| scope | description |
| ----- | ----------- |
| `meta.inline-completion.lsp` | Style for inline completions |
6 changes: 6 additions & 0 deletions docs/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,12 @@ Inlay hints are disabled by default and can be enabled with the `"show_inlay_hin

!!! info "Some servers require additional settings to be enabled in order to show inlay hints."

## Inline Completions

Inline completions are typically provided by an AI code assistant.
They can span multiple lines and are rendered directly in the source code as grayed out text ("ghost text").
Currently inline completions are only requested when you manually trigger auto-completions (<kbd>Ctrl</kbd> + <kbd>Space</kbd>).

jwortmann marked this conversation as resolved.
Show resolved Hide resolved
## Server Commands

In Sublime Text you can bind any runnable command to a key or add it to various UI elements. Commands in Sublime Text are normally supplied by plugins or packages written in Python. A language server may provide a runnable command as well. These kinds of commands are wrapped in an `lsp_execute` Sublime command that you can bind to a key.
Expand Down
63 changes: 63 additions & 0 deletions docs/src/language_servers.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,69 @@ If there are no setup steps for a language server on this page, but a [language
!!! info "For legacy ST3 docs, see [lsp.readthedocs.io](https://lsp.readthedocs.io)."


## Universal

### Tabby

[Tabby](https://tabby.tabbyml.com/) is a self-hosted AI coding assistant which can provide inline completions for [various programming languages](https://tabby.tabbyml.com/docs/references/programming-languages/).

In order to use Tabby you need a sufficiently fast GPU; the CPU version which can also be downloaded from the GitHub releases page is much too slow and it will result in timeouts for the completion requests.
Alternatively, Tabby can be setup on a separate server with capable hardware; see the [Configuration docs](https://tabby.tabbyml.com/docs/extensions/configurations/) for the required configuration details.
The following steps describe a local installation on a Windows PC with compatible Nvidia GPU. More installation methods and the steps for other operation systems are listed in the [Tabby docs](https://tabby.tabbyml.com/docs/quick-start/installation/docker/).

1. Download and install the CUDA Toolkit from <https://developer.nvidia.com/cuda-downloads>

2. Download and extract a CUDA version of Tabby from the [GitHub releases page](https://github.com/TabbyML/tabby/releases) (click on "Assets"); e.g. `tabby_x86_64-windows-msvc-cuda122.zip`

jwortmann marked this conversation as resolved.
Show resolved Hide resolved
3. Install the `tabby-agent` language server via npm (requires NodeJS):

```sh
npm install -g tabby-agent
```

4. Open `Preferences > Package Settings > LSP > Settings` and add the `"tabby"` client configuration to the `"clients"`:

```jsonc
{
"clients": {
"tabby": {
"enabled": true,
"command": ["tabby-agent", "--stdio"],
"selector": "source.js | source.python | source.rust", // replace with your relevant filetype(s)
"disabled_capabilities": {
"completionProvider": true
}
},
}
}
```

5. Download a completion model (see <https://tabby.tabbyml.com/docs/models/> for available model files and GPU requirements):

```sh
tabby download --model StarCoder-1B
```

6. If necessary, edit the configuration file under `~/.tabby-client/agent/config.toml`, which is generated automatically on the first start of tabby-agent.
For example, to disable anonymous usage tracking add

```toml
[anonymousUsageTracking]
disable = true
```

7. Manually start the Tabby backend:

```sh
tabby serve --model StarCoder-1B --no-webserver
```

The language server communicates with this backend, i.e. it needs to be running in order for `tabby-agent` to work.

8. Now you can open a file in Sublime Text and start coding.
Inline completions are requested when you manually trigger auto-complete via <kbd>Ctrl</kbd> + <kbd>Space</kbd>.


## Angular

Follow installation instructions on [LSP-angular](https://github.com/sublimelsp/LSP-angular).
Expand Down
10 changes: 10 additions & 0 deletions plugin/core/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
from enum import IntEnum, IntFlag
from typing import Any, Callable, Generator, List, Protocol, TypeVar
from typing import cast
from typing import TYPE_CHECKING
from typing_extensions import TypeAlias, TypeGuard
from weakref import WeakSet
import functools
Expand All @@ -122,6 +123,11 @@
import sublime
import weakref


if TYPE_CHECKING:
from ..inline_completion import InlineCompletionData


InitCallback: TypeAlias = Callable[['Session', bool], None]
T = TypeVar('T')

Expand Down Expand Up @@ -325,6 +331,9 @@ def get_initialize_params(variables: dict[str, str], workspace_folders: list[Wor
"itemDefaults": ["editRange", "insertTextFormat", "data"]
}
},
"inlineCompletion": {
"dynamicRegistration": True
},
"signatureHelp": {
"dynamicRegistration": True,
"contextSupport": True,
Expand Down Expand Up @@ -701,6 +710,7 @@ class AbstractViewListener(metaclass=ABCMeta):

view = cast(sublime.View, None)
hover_provider_count = 0
inline_completion = cast('InlineCompletionData', None)

@abstractmethod
def session_async(self, capability: str, point: int | None = None) -> Session | None:
Expand Down
8 changes: 8 additions & 0 deletions plugin/documents.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from .core.windows import WindowManager
from .folding_range import folding_range_to_range
from .hover import code_actions_content
from .inline_completion import InlineCompletionData
from .session_buffer import SessionBuffer
from .session_view import SessionView
from functools import partial
Expand Down Expand Up @@ -197,6 +198,7 @@ def on_change() -> None:
self._stored_selection: list[sublime.Region] = []
self._should_format_on_paste = False
self.hover_provider_count = 0
self.inline_completion = InlineCompletionData(self.view, 'lsp_inline_completion')
self._setup()

def __del__(self) -> None:
Expand Down Expand Up @@ -226,6 +228,7 @@ def _cleanup(self) -> None:
self._stored_selection = []
self.view.erase_status(AbstractViewListener.TOTAL_ERRORS_AND_WARNINGS_STATUS_KEY)
self._clear_highlight_regions()
self.inline_completion.clear_async()
self._clear_session_views_async()

def _reset(self) -> None:
Expand Down Expand Up @@ -418,6 +421,7 @@ def on_selection_modified_async(self) -> None:
if not self._is_in_higlighted_region(first_region.b):
self._clear_highlight_regions()
self._clear_code_actions_annotation()
self.inline_completion.clear_async()
if userprefs().document_highlight_style or userprefs().show_code_actions:
self._when_selection_remains_stable_async(
self._on_selection_modified_debounced_async, first_region, after_ms=self.debounce_time)
Expand Down Expand Up @@ -511,6 +515,8 @@ def on_query_context(self, key: str, operator: int, operand: Any, match_all: boo
if not session_view:
return not operand
return operand == bool(session_view.session_buffer.get_document_link_at_point(self.view, position))
elif key == 'lsp.inline_completion_visible' and operator == sublime.QueryOperator.EQUAL:
return operand == self.inline_completion.visible
return None

@requires_session
Expand Down Expand Up @@ -560,6 +566,7 @@ def _on_hover_gutter_async(self, point: int) -> None:
def on_text_command(self, command_name: str, args: dict | None) -> tuple[str, dict] | None:
if command_name == "auto_complete":
self._auto_complete_triggered_manually = True
self.view.run_command('lsp_inline_completion')
elif command_name == "show_scope_name" and userprefs().semantic_highlighting:
session = self.session_async("semanticTokensProvider")
if session:
Expand Down Expand Up @@ -990,6 +997,7 @@ def _on_view_updated_async(self) -> None:
if first_region is None:
return
self._clear_highlight_regions()
self.inline_completion.clear_async()
if userprefs().document_highlight_style:
self._when_selection_remains_stable_async(
self._do_highlights_async, first_region, after_ms=self.debounce_time)
Expand Down
Loading