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 languagesKeymapsCustomize 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: