diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index bb9d25b4e7..63a97d2dc2 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -45,7 +45,7 @@ jobs:
run: |
# sudo apt-get install clangd-12
# sudo update-alternatives --install /usr/bin/clangd clangd /usr/bin/clangd-12 100
- pip install epc six basedpyright sexpdata watchdog
+ pip install epc six basedpyright sexpdata watchdog packaging
go install golang.org/x/tools/gopls@latest
curl https://raw.githubusercontent.com/eruizc-dev/jdtls-launcher/master/install.sh | bash
if: matrix.os == 'ubuntu-latest'
@@ -53,7 +53,7 @@ jobs:
- name: Install Dependencies (Windows)
run: |
# choco install llvm
- python -m pip install epc six basedpyright sexpdata watchdog
+ python -m pip install epc six basedpyright sexpdata watchdog packaging
go install golang.org/x/tools/gopls@latest
if: matrix.os == 'windows-latest'
diff --git a/README.md b/README.md
index e5e7a41de3..3604a1a58d 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,11 @@
- Installation •
- Support languages •
+ Installation •
+ Support languages •
Keymaps •
Customize options •
- Join development
+ Join development
@@ -32,7 +32,7 @@ Advantages of lsp-bridge:
## Installation
1. Install Emacs 28 or higher version
-2. Install Python dependencies: `pip3 install epc orjson sexpdata six setuptools paramiko rapidfuzz watchdog` (orjson is optional, orjson is based on Rust, providing faster JSON parsing performance)
+2. Install Python dependencies: `pip3 install epc orjson sexpdata six setuptools paramiko rapidfuzz watchdog packaging` (orjson is optional, orjson is based on Rust, providing faster JSON parsing performance)
3. Install Elisp dependencies: [markdown-mode](https://github.com/jrblevin/markdown-mode), [yasnippet](https://github.com/joaotavora/yasnippet)
4. Download this repository using git clone, and replace the load-path path in the configuration below.
@@ -268,8 +268,8 @@ lsp-bridge provides support for more than two language servers for many language
- `lsp-bridge-csharp-lsp-server`: C# language server, you can choose `omnisharp-mono`, `omnisharp-dotnet` or `csharp-ls`, note that you need to give **execute permissions** to the OmniSharp file
- `lsp-bridge-python-multi-lsp-server`: Python multi-language servers, you can choose `basedpyright_ruff`, `pyright_ruff`, `jedi_ruff`, `python-ms_ruff`, `pylsp_ruff`
- `lsp-bridge-nix-lsp-server`: Nix language server, you can choose `rnix-lsp`, `nixd` or `nil`
-- `lsp-bridge-markdown-lsp-server`: Markdown language server, you can choose `vale-ls` or `marksman`
-- `lsp-bridge-lua-lsp-server`: Lua language server, you can choose `sumneko` or `lua-lsp`
+- `lsp-bridge-markdown-lsp-server`: Markdown language server, you can choose `vale-ls` or `marksman`
+- `lsp-bridge-lua-lsp-server`: Lua language server, you can choose `sumneko` or `lua-lsp`
- `lsp-bridge-verilog-lsp-server`: Verilog language server, you can choose `verible`, or `svls`
- `lsp-bridge-xml-lsp-server`: XML language server, you can choose `lemminx`, or `camells`
@@ -393,7 +393,7 @@ If your language supports mixed multi-language servers, it is recommended to che
| Futhark | [futhark-lsp](https://futhark-lang.org) | |
| Fuzion | [fuzion-lsp-server](https://github.com/tokiwa-software/fuzion-lsp-server) | |
| F# | [fsautocomplete](https://github.com/fsharp/FsAutoComplete) | |
-| Gleam | [gleam lsp](https://gleam.run/news/v0.21-introducing-the-gleam-language-server/) |
+| Gleam | [gleam lsp](https://gleam.run/news/v0.21-introducing-the-gleam-language-server/) |
| GLSL | [glsl-language-server](https://github.com/svenstaro/glsl-language-server) | |
| Go | [gopls](https://github.com/golang/tools/tree/master/gopls) | Make sure install [go-mode](https://github.com/dominikh/go-mode.el) and `gopls` in PATH, please do `ln -s ~/go/bin/gopls ~/.local/bin`, and do `go mod init` first |
| GraphQL | [graphql-lsp](https://github.com/graphql/graphiql/tree/main/packages/graphql-language-service-cli) | |
diff --git a/core/tabnine.py b/core/tabnine.py
old mode 100644
new mode 100755
index 7963569755..e01ad54bad
--- a/core/tabnine.py
+++ b/core/tabnine.py
@@ -2,20 +2,20 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2022 Andy Stewart
-#
+#
# Author: Andy Stewart
# Maintainer:
-#
+#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
-#
+#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
-#
+#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
@@ -27,14 +27,9 @@
from core.utils import *
from subprocess import PIPE
-from sys import stderr,version_info
-
-if version_info[1] < 12 :
- from distutils.version import StrictVersion
- version_function = StrictVersion
-else:
- from pkg_resources import parse_version
- version_function = parse_version
+from sys import stderr
+from packaging.version import Version
+version_function = Version
TABNINE_PROTOCOL_VERSION = "1.0.14"
TABNINE_EXECUTABLE = "TabNine.exe" if get_os_name() == "windows" else "TabNine"
@@ -45,20 +40,20 @@ class TabNine:
def __init__(self):
self.process = None
self.path = None
-
+
self.receiver = None
self.sender = None
self.dispatcher = None
-
+
self.try_completion_timer = None
-
+
[self.tabnine_binaries_folder] = get_emacs_vars(["tabnine-bridge-binaries-folder"])
-
+
def complete(self, before, after, filename, region_includes_beginning, region_includes_end, max_num_results):
if self.is_tabnine_exist() and isinstance(filename, str):
if self.try_completion_timer is not None and self.try_completion_timer.is_alive():
self.try_completion_timer.cancel()
-
+
self.message = {
"version": TABNINE_PROTOCOL_VERSION,
"request": {
@@ -75,10 +70,10 @@ def complete(self, before, after, filename, region_includes_beginning, region_in
self.try_completion_timer = threading.Timer(0.5, self.do_complete)
self.try_completion_timer.start()
-
+
def do_complete(self):
self.sender.send_request(self.message) # type: ignore
-
+
def get_tabnine_path(self):
if os.path.exists(self.tabnine_binaries_folder):
try:
@@ -96,46 +91,46 @@ def get_tabnine_path(self):
return None
return None
-
+
def is_tabnine_exist(self):
if self.path is None:
self.path = self.get_tabnine_path()
-
+
if isinstance(self.path, str) and os.path.exists(self.path):
if self.process is None:
self.process = subprocess.Popen(
- [self.path, "--client", "emacs"],
- bufsize=DEFAULT_BUFFER_SIZE,
- stdin=PIPE,
- stdout=PIPE,
+ [self.path, "--client", "emacs"],
+ bufsize=DEFAULT_BUFFER_SIZE,
+ stdin=PIPE,
+ stdout=PIPE,
stderr=stderr)
-
+
self.receiver = TabNineReceiver(self.process)
self.receiver.start()
-
+
self.sender = TabNineSender(self.process)
self.sender.start()
-
+
self.dispatcher = threading.Thread(target=self.message_dispatcher)
self.dispatcher.start()
-
+
log_time("Start TabNine server ({})".format(self.path))
-
+
return self.process is not None
else:
return False
-
+
def message_dispatcher(self):
try:
while True:
message = self.receiver.get_message() # type: ignore
-
+
completion_candidates = []
-
+
if "results" in message:
for result in message["results"]:
label = result["new_prefix"]
-
+
candidate = {
"key": label,
"icon": "tabnine",
@@ -146,27 +141,27 @@ def message_dispatcher(self):
"new_suffix": result["new_suffix"],
"old_suffix": result["old_suffix"]
}
-
+
completion_candidates.append(candidate)
-
+
completion_candidates = sorted(completion_candidates, key=lambda candidate: candidate["annotation"], reverse=True)
-
+
eval_in_emacs("lsp-bridge-search-backend--record-items", "tabnine", completion_candidates)
except:
logger.error(traceback.format_exc())
-
+
class TabNineSender(MessageSender):
-
+
def send_message(self, message):
data = json.dumps(message) + "\n"
-
+
self.process.stdin.write(data.encode("utf-8")) # type: ignore
self.process.stdin.flush() # type: ignore
-
+
log_time("Send TabNine complete request for project {}".format(message["request"]["Autocomplete"]["filename"]))
-
+
logger.debug(json.dumps(message, indent=3))
-
+
def run(self):
try:
while self.process.poll() is None:
@@ -174,9 +169,9 @@ def run(self):
self.send_message(message)
except:
logger.error(traceback.format_exc())
-
+
class TabNineReceiver(MessageReceiver):
-
+
def run(self):
try:
while self.process.poll() is None: