-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathplugin.py
More file actions
175 lines (139 loc) · 5.55 KB
/
Copy pathplugin.py
File metadata and controls
175 lines (139 loc) · 5.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
from __future__ import annotations
import os
import shutil
from typing import Any, TypedDict, cast
import sublime
from LSP.plugin import AbstractPlugin, LspTextCommand, Request, register_plugin, unregister_plugin
from LSP.plugin.core.views import (
extract_variables,
first_selection_region,
offset_to_point,
text_document_identifier,
text_document_position_params,
)
from LSP.protocol import Position, TextDocumentIdentifier
from typing_extensions import NotRequired
from .const import ARCH, PLATFORM, PLATFORM_ARCH, PLATFORM_ARCH_TO_TARBALL, PLUGIN_NAME, SERVER_VERSION
from .server import get_default_server_bin_path, get_plugin_storage_dir, get_server_dir, get_server_download_url
from .tarball import decompress, download
class TextDocumentBuildParams(TypedDict):
textDocument: TextDocumentIdentifier
position: NotRequired[Position]
def plugin_loaded() -> None:
register_plugin(LspTexLabPlugin)
def plugin_unloaded() -> None:
unregister_plugin(LspTexLabPlugin)
class LspTexLabPlugin(AbstractPlugin):
@classmethod
def name(cls) -> str:
return PLUGIN_NAME
@classmethod
def configuration(cls) -> tuple[sublime.Settings, str]:
name = cls.name()
basename = f"{name}.sublime-settings"
filepath = f"Packages/{name}/{basename}"
return sublime.load_settings(basename), filepath
@classmethod
def additional_variables(cls) -> dict[str, str]:
return {
"texlab_bin": get_default_server_bin_path()
if PLATFORM_ARCH in PLATFORM_ARCH_TO_TARBALL
else "texlab",
}
@classmethod
def needs_update_or_installation(cls) -> bool:
command = cast('list[str]', cls.configuration()[0].get("command"))
server_bin = command[0]
# only auto manage platforms which the official server supports
if PLATFORM_ARCH in PLATFORM_ARCH_TO_TARBALL and server_bin in {
"${texlab_bin}",
"$texlab_bin",
}:
variables = extract_variables(sublime.active_window())
variables.update(cls.additional_variables())
server_bin = sublime.expand_variables(server_bin, variables)
return not os.path.isfile(server_bin)
# for unofficial supported platforms, users have to compile texlab by themselves
# and adjust the "command" to use the executable
return False
@classmethod
def install_or_update(cls) -> None:
cls._cleanup_cache()
is_download_ok = cls._prepare_server_bin()
if not is_download_ok:
raise RuntimeError("Unable to download the server binary...")
@classmethod
def _prepare_server_bin(cls) -> bool:
"""Download the LSP server binary."""
server_dir = get_server_dir()
download_url = get_server_download_url(SERVER_VERSION, PLATFORM, ARCH)
if not download_url:
return False
tarball_name = download_url.split("/")[-1]
tarball_path = os.path.join(server_dir, tarball_name)
download(download_url, tarball_path)
decompress(tarball_path, server_dir)
return True
@classmethod
def _cleanup_cache(cls) -> None:
"""Clean up this plugin's cache directory."""
shutil.rmtree(get_plugin_storage_dir(), ignore_errors=True)
class LspTexlabForwardSearchCommand(LspTextCommand):
session_name = PLUGIN_NAME
def run(self, _: sublime.Edit) -> None:
session = self.session_by_name(PLUGIN_NAME)
if not session:
return
params = text_document_position_params(self.view, next(iter(self.view.sel())).a)
session.send_request(
Request("textDocument/forwardSearch", params),
self.on_response_async,
self.on_error_async,
)
def on_response_async(self, response: Any) -> None:
status = response["status"]
window = self.view.window()
if window is None:
return
if status == 0:
pass # success
elif status == 1:
window.status_message(PLUGIN_NAME + ": Previewer exited with errors")
elif status == 2:
window.status_message(
PLUGIN_NAME + ": Previewer failed to start or crashed"
)
elif status == 3:
window.status_message(PLUGIN_NAME + ": Previewer is not configured")
def on_error_async(self, error: Any) -> None:
pass
class LspTexlabBuildCommand(LspTextCommand):
session_name = PLUGIN_NAME
def run(self, _: sublime.Edit) -> None:
session = self.session_by_name(PLUGIN_NAME)
if not session:
return
params: TextDocumentBuildParams = {"textDocument": text_document_identifier(self.view)}
region = first_selection_region(self.view)
if region is not None:
params["position"] = offset_to_point(self.view, region.b).to_lsp()
session.send_request(
Request("textDocument/build", params),
self.on_response_async,
self.on_error_async,
)
def on_response_async(self, response: Any) -> None:
status = response["status"]
window = self.view.window()
if window is None:
return
if status == 0:
pass # success
elif status == 1:
window.status_message(PLUGIN_NAME + ": Build error")
elif status == 2:
window.status_message(PLUGIN_NAME + ": Build failure")
elif status == 3:
window.status_message(PLUGIN_NAME + ": Build cancelled")
def on_error_async(self, error: Any) -> None:
pass