-
Notifications
You must be signed in to change notification settings - Fork 5
/
plugin.py
168 lines (145 loc) · 7.11 KB
/
plugin.py
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
from LSP.plugin.core.typing import Any, Callable, Dict, Literal, Optional, Set
from LSP.plugin import uri_to_filename
from LSP.plugin import WorkspaceFolder
from lsp_utils import notification_handler
from lsp_utils import NpmClientHandler
from lsp_utils import request_handler
import os
import re
import sublime
import webbrowser
def plugin_loaded() -> None:
LspEslintPlugin.setup()
def plugin_unloaded() -> None:
LspEslintPlugin.cleanup()
class LspEslintPlugin(NpmClientHandler):
package_name = __package__
server_directory = 'language-server'
server_binary_path = os.path.join(server_directory, 'out', 'eslintServer.js')
skip_npm_install = True
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self._probe_failed = set() # type: Set[str]
@classmethod
def required_node_version(cls) -> str:
return '>=16.20.2'
@notification_handler('eslint/status')
def handle_status(self, params: Any) -> None:
pass
@notification_handler('eslint/exitCalled')
def handle_exit_called(self, params: Any) -> None:
pass
@notification_handler('eslint/showOutputChannel')
def handle_show_output_channel(self, params: Any) -> None:
sublime.active_window().run_command('lsp_toggle_server_panel')
@request_handler('eslint/openDoc')
def handle_open_doc(self, params: Any, respond: Callable[[Any], None]) -> None:
webbrowser.open(params['url'])
respond({})
@request_handler('eslint/probeFailed')
def handle_probe_failed(self, params: Any, respond: Callable[[Any], None]) -> None:
self._probe_failed.add(params['textDocument']['uri'])
respond(None)
@request_handler('eslint/noConfig')
def handle_no_config(self, params: Any, respond: Callable[[Any], None]) -> None:
# TODO: Show better notification that no eslint configuration was found.
print('LSP-eslint: Could not find eslint configuration ({}) for {}'.format(
params['message'], params['document']['uri']))
respond(None)
@request_handler('eslint/noLibrary')
def handle_no_library(self, params: Any, respond: Callable[[Any], None]) -> None:
# TODO: Show better notification that no eslint library was found.
print('LSP-eslint: Failed resolving eslint library for {}'.format(params['source']['uri']))
respond(None)
def on_workspace_configuration(self, params: Dict[str, Any], configuration: Dict) -> Dict:
session = self.weaksession()
if session:
scope_uri = params.get('scopeUri')
if scope_uri:
workspace_folder = None # type: Optional[WorkspaceFolder]
for folder in session.get_workspace_folders():
if folder.includes_uri(scope_uri):
workspace_folder = folder
break
if workspace_folder:
configuration['workspaceFolder'] = workspace_folder.to_lsp()
self.resolve_working_directory(configuration, scope_uri, workspace_folder)
buf = session.get_session_buffer_for_uri_async(scope_uri)
if buf:
configuration['validate'] = self.compute_validate(
buf.get_language_id() or '', scope_uri, configuration)
else:
configuration['validate'] = 'on'
del configuration['probe']
return configuration
def resolve_working_directory(self, configuration: Dict, scope_uri: str,
workspace_folder: Optional[WorkspaceFolder]) -> None:
working_directories = configuration.get('workingDirectories', None)
if isinstance(working_directories, list):
working_directory = None
workspace_folder_path = workspace_folder.path if workspace_folder else None
for entry in working_directories:
directory = None
no_cwd = False
if isinstance(entry, str):
directory = entry
elif self.is_working_directory_item(entry, 'directory'):
directory = entry['directory']
if isinstance(entry.get('!cwd', None), bool):
no_cwd = entry['!cwd']
elif self.is_working_directory_item(entry, 'pattern'):
print('LSP-eslint: workingDirectories configuration that uses "pattern" is not supported')
continue
elif self.is_mode_item(entry):
working_directory = entry
continue
directory_value = None
if directory:
file_path = uri_to_filename(scope_uri)
if directory:
directory = self.to_os_path(directory)
if not os.path.isabs(directory) and workspace_folder_path:
# Trailing '' will add trailing slash if missing.
directory = os.path.join(workspace_folder_path, directory, '')
if file_path.startswith(directory):
directory_value = directory
if directory_value:
if not working_directory or self.is_mode_item(working_directory):
working_directory = {'directory': directory_value, '!cwd': no_cwd}
else:
if len(working_directory['directory']) < len(directory_value):
working_directory['directory'] = directory_value
working_directory['!cwd'] = no_cwd
configuration['workingDirectory'] = working_directory
configuration.pop('workingDirectories', None)
def is_working_directory_item(self, item: Any, configuration_key: str) -> bool:
if isinstance(item, dict):
value = item.get(configuration_key, None)
not_cwd = item.get('!cwd', None)
return isinstance(value, str) and (isinstance(not_cwd, bool) or not_cwd is None)
return False
def is_mode_item(self, item: Any) -> bool:
if isinstance(item, dict):
mode = item.get('mode', None)
return isinstance(mode, str) and mode in ('auto', 'location')
return False
def to_os_path(self, path: str) -> str:
if sublime.platform == 'windows':
path = re.sub(r'^\/(\w)\/', r'\1:\\', path)
return os.path.normpath(path)
def compute_validate(
self, language_id: str, scope_uri: str, config: Dict[str, Any]
) -> Literal['off', 'on', 'probe']:
validate = config.get('validate')
if isinstance(validate, list):
for validate_langugage_id in validate:
if validate_langugage_id == language_id:
return 'on'
if scope_uri in self._probe_failed:
return 'off'
probe = config.get('probe')
if isinstance(probe, list):
for probe_language_id in probe:
if probe_language_id == language_id:
return 'probe'
return 'off'