Skip to content

Commit

Permalink
✨ Support LSP
Browse files Browse the repository at this point in the history
  • Loading branch information
Freed-Wu committed Jun 30, 2023
1 parent ae6cdf2 commit 7bd1a73
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 15 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

<a name="next"></a>
## next

### Fixed

- 💚 Add if to skip AUR CI/CD to fasten [[66e3571](https://github.com/Freed-Wu/translate-shell/commit/66e3571ac6b77ea071e0b66c693a464427434583)]

### Miscellaneous

- 📝 Update scripts/generate-requirements.md.pl [[ae6cdf2](https://github.com/Freed-Wu/translate-shell/commit/ae6cdf217b86b1caad3f9c6550ee6165b71c05dc)]
- 🧑‍💻 Add skipcq [[f044b82](https://github.com/Freed-Wu/translate-shell/commit/f044b82476be07e1f289856f373eb4a49d920c1c)]


<a name="0.0.28"></a>
## 0.0.28 (2023-06-15)

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ $ xsel -o | trans --format json | jq -r '"《\(.results[].paraphrase)》的英
Just for Fun is 纯娱乐 in Chinese.
```

### Language server

- [x] document hover: display translated results
- [x] completions: complete translated words

### CI/CD

#### Github Action
Expand Down
77 changes: 77 additions & 0 deletions docs/resources/configure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Configure for Language Servers

## (Neo)[Vim](https://www.vim.org)

### [coc.nvim](https://github.com/neoclide/coc.nvim)

```json
{
"languageserver": {
"translate": {
"command": "trans",
"args": [
"--lsp"
],
"filetypes": [
"text"
]
}
}
}
```

### [vim-lsp](https://github.com/prabirshrestha/vim-lsp)

```vim
if executable('trans')
augroup lsp
autocmd!
autocmd User lsp_setup call lsp#register_server({
\ 'name': 'translate',
\ 'cmd': {server_info->['trans', '--lsp']},
\ 'whitelist': ['text'],
\ })
augroup END
endif
```

## [Neovim](https://neovim.io)

```lua
vim.api.nvim_create_autocmd({ "BufEnter" }, {
pattern = { "*.txt" },
callback = function()
vim.lsp.start({
name = "translate",
cmd = { "trans" "--lsp" }
})
end,
})
```

## [Emacs](https://www.gnu.org/software/emacs)

```elisp
(make-lsp-client :new-connection
(lsp-stdio-connection
`(,(executable-find "trans" "--lsp")))
:activation-fn (lsp-activate-on "*.txt")
:server-id "translate")))
```

## [Sublime](https://www.sublimetext.com)

```json
{
"clients": {
"translate": {
"command": [
"trans",
"--lsp"
],
"enabled": true,
"selector": "source.text"
}
}
}
```
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ file = "requirements/keyring.txt"
[tool.setuptools.dynamic.optional-dependencies.langdetect]
file = "requirements/langdetect.txt"

[tool.setuptools.dynamic.optional-dependencies.lsp]
file = "requirements/lsp.txt"

[tool.setuptools.dynamic.optional-dependencies.notification]
file = "requirements/notification.txt"

Expand Down
3 changes: 3 additions & 0 deletions requirements/lsp.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env -S pip install -r

pygls
11 changes: 10 additions & 1 deletion src/translate_shell/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ def get_parser() -> ArgumentParser:
parser.add_argument(
"-q", "--quiet", action="count", default=0, help="reduce logger level"
)
parser.add_argument(
"--lsp", action="store_true", help="start language server"
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"--no-clipboard",
Expand Down Expand Up @@ -172,7 +175,13 @@ def main() -> None | NoReturn:
except ImportError:
if not sys.stdin.isatty():
args.text = [sys.stdin.read()] + args.text
if args.text:

from translate_shell.ui import init

init(args)
if args.lsp:
from translate_shell.ui.server import run
elif args.text:
from translate_shell.ui.cli import run
else:
from translate_shell.ui.repl import run
Expand Down
34 changes: 25 additions & 9 deletions src/translate_shell/ui/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,11 @@ def init(args: Namespace) -> None:
if value is not None:
setattr(args, attr, value)
args.text = " ".join(args.text)
_readline = init_readline()
_readline.set_completer(args.complete)
if args.text:
_readline.add_history(args.text)
if not args.lsp:
_readline = init_readline()
_readline.set_completer(args.complete)
if args.text:
_readline.add_history(args.text)
args.last_text = ""
logging.root.level += 10 * (args.quiet - args.verbose)
# override default functions
Expand All @@ -132,14 +133,16 @@ def is_sub_thread() -> bool:
return main_thread().ident != get_ident()


def process(args: Namespace, is_repl: bool = False) -> None:
"""Process.
def get_processed_result_text(
args: Namespace, is_repl: bool = False
) -> tuple[str, str]:
"""Get processed result and processed text.
:param args:
:type args: Namespace
:param is_repl: If the input is REPL's stdin, it is ``True``.
:param is_repl:
:type is_repl: bool
:rtype: None
:rtype: tuple[str, str]
"""
(
text,
Expand All @@ -154,7 +157,7 @@ def process(args: Namespace, is_repl: bool = False) -> None:
is_repl,
)
if text == "" or (not is_repl and text == args.last_text):
return
return ("", "")
target_lang = args.target_lang
if target_lang == "auto":
target_lang = os.getenv("LANG", "zh_CN.UTF-8").split(".")[0]
Expand Down Expand Up @@ -185,6 +188,19 @@ def process(args: Namespace, is_repl: bool = False) -> None:
rst = args.process_output(translation)
if rst and args.notification and is_sub_thread():
args.notify(rst)
return rst, text


def process(args: Namespace, is_repl: bool = False) -> None:
"""Process. Get processed result and processed text, then print them.
:param args:
:type args: Namespace
:param is_repl: If the input is REPL's stdin, it is ``True``.
:type is_repl: bool
:rtype: None
"""
rst, text = get_processed_result_text(args, is_repl)
if rst:
if is_sub_thread():
os.kill(os.getpid(), signal.SIGINT)
Expand Down
3 changes: 1 addition & 2 deletions src/translate_shell/ui/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"""
from argparse import Namespace

from . import init, process
from . import process


def run(args: Namespace) -> None:
Expand All @@ -13,5 +13,4 @@ def run(args: Namespace) -> None:
:type args: Namespace
:rtype: None
"""
init(args)
process(args)
3 changes: 1 addition & 2 deletions src/translate_shell/ui/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from argparse import Namespace
from threading import Thread

from . import init, process
from . import process


def run(args: Namespace) -> None:
Expand All @@ -14,7 +14,6 @@ def run(args: Namespace) -> None:
:type args: Namespace
:rtype: None
"""
init(args)
if args.clipboard:
from .gui import run as _run

Expand Down
142 changes: 142 additions & 0 deletions src/translate_shell/ui/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
r"""Server
==========
"""
import re
from argparse import Namespace
from typing import Any, Tuple

from lsprotocol.types import (
TEXT_DOCUMENT_COMPLETION,
TEXT_DOCUMENT_HOVER,
CompletionItem,
CompletionItemKind,
CompletionList,
CompletionParams,
Hover,
MarkupContent,
MarkupKind,
Position,
Range,
TextDocumentPositionParams,
)
from pygls.server import LanguageServer

from .. import APPNAME, HISTORY_FILE, __version__
from . import get_processed_result_text


class TranslateShellLanguageServer(LanguageServer):
r"""Translate shell language server."""

def __init__(self, translate_args: Namespace, *args: Any) -> None:
"""Init.
:param self:
:param translate_args:
:type translate_args: Namespace
:param args:
:type args: Any
:rtype: None
"""
self.args = translate_args
super().__init__(*args)

@self.feature(TEXT_DOCUMENT_HOVER)
def hover(params: TextDocumentPositionParams) -> Hover | None:
r"""Hover the translated results.
:param params:
:type params: TextDocumentPositionParams
:rtype: Hover | None
"""
word = self._cursor_word(
params.text_document.uri, params.position, True
)
if not word:
return None
self.args.text = word[0]
# ignore processed text
doc = get_processed_result_text(self.args)[0]
if not doc:
return None
return Hover(
contents=MarkupContent(kind=MarkupKind.PlainText, value=doc),
range=word[1],
)

@self.feature(TEXT_DOCUMENT_COMPLETION)
def completions(params: CompletionParams) -> CompletionList:
r"""Completion history words.
:param params:
:type params: CompletionParams
:rtype: CompletionList
"""
word = self._cursor_word(
params.text_document.uri, params.position, False
)
token = "" if word is None else word[0]
items = [
CompletionItem(
label=x,
kind=CompletionItemKind.Constant,
insert_text=x,
)
for x in HISTORY_FILE.read_text().splitlines()
if x.startswith(token)
]
return CompletionList(is_incomplete=False, items=items)

def _cursor_line(self, uri: str, position: Position) -> str:
r"""Cursor line.
:param uri:
:type uri: str
:param position:
:type position: Position
:rtype: str
"""
doc = self.workspace.get_document(uri)
content = doc.source
line = content.split("\n")[position.line]
return str(line)

def _cursor_word(
self, uri: str, position: Position, include_all: bool = True
) -> Tuple[str, Range] | None:
r"""Cursor word.
:param uri:
:type uri: str
:param position:
:type position: Position
:param include_all:
:type include_all: bool
:rtype: Tuple[str, Range] | None
"""
line = self._cursor_line(uri, position)
cursor = position.character
for m in re.finditer(r"\w+", line):
end = m.end() if include_all else cursor
if m.start() <= cursor <= m.end():
word = (
line[m.start() : end],
Range(
start=Position(
line=position.line, character=m.start()
),
end=Position(line=position.line, character=end),
),
)
return word
return None


def run(args: Namespace) -> None:
"""Run.
:param args:
:type args: Namespace
:rtype: None
"""
TranslateShellLanguageServer(args, APPNAME, __version__).start_io()
1 change: 1 addition & 0 deletions tests/txt/options.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ options:
print some setting
-v, --verbose increase logger level
-q, --quiet reduce logger level
--lsp start language server
--no-clipboard disable clipboard
--clipboard enable clipboard (default)
--no-notification disable notification
Expand Down

0 comments on commit 7bd1a73

Please sign in to comment.