Skip to content

Commit 83ff7b8

Browse files
committed
feat: add pygls initial implementaiton
1 parent 89265cd commit 83ff7b8

File tree

3 files changed

+511
-0
lines changed

3 files changed

+511
-0
lines changed

pylsp/server/__init__.py

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
import logging
5+
import os
6+
import typing as typ
7+
8+
from lsprotocol import types as lsptyp
9+
from pygls import server
10+
11+
from pylsp import PYLSP, __version__
12+
from pylsp._utils import flatten, is_process_alive
13+
from pylsp.server.protocol import LangageServerProtocol
14+
15+
if typ.TYPE_CHECKING:
16+
from pylsp.server.workspace import Workspace
17+
18+
logger = logging.getLogger(__name__)
19+
20+
MAX_WORKERS = 64
21+
PARENT_PROCESS_WATCH_INTERVAL = 10 # 10 s
22+
23+
CONFIG_FILES = ("pycodestyle.cfg", "setup.cfg", "tox.ini", ".flake8")
24+
25+
26+
class LanguageServer(server.LanguageServer):
27+
"""Python Language Server."""
28+
29+
lsp: LangageServerProtocol
30+
31+
@property
32+
def workspace(self) -> Workspace:
33+
"""Returns in-memory workspace."""
34+
return self.lsp.workspace
35+
36+
def check_parent_process(self):
37+
"""Check if the parent process is still alive."""
38+
async def watch_parent_process():
39+
ppid = os.getppid()
40+
while True:
41+
if self._stop_event is not None and self._stop_event.is_set():
42+
break
43+
if not is_process_alive(ppid):
44+
self.shutdown()
45+
break
46+
await asyncio.sleep(PARENT_PROCESS_WATCH_INTERVAL)
47+
asyncio.create_task(watch_parent_process())
48+
49+
50+
LSP_SERVER = LanguageServer(
51+
name=PYLSP,
52+
version=__version__,
53+
max_workers=MAX_WORKERS,
54+
protocol_cls=LangageServerProtocol,
55+
text_document_sync_kind=lsptyp.TextDocumentSyncKind.Incremental,
56+
notebook_document_sync=lsptyp.NotebookDocumentSyncOptions(
57+
notebook_selector=[
58+
lsptyp.NotebookDocumentSyncOptionsNotebookSelectorType2(
59+
cells=[
60+
lsptyp.NotebookDocumentSyncOptionsNotebookSelectorType2CellsType(
61+
language="python"
62+
),
63+
],
64+
),
65+
],
66+
),
67+
)
68+
69+
70+
@LSP_SERVER.feature(lsptyp.INITIALIZE)
71+
async def initialize(ls: LanguageServer, params: lsptyp.InitializeParams):
72+
"""Handle the initialization request."""
73+
# Call the initialization hook
74+
await ls.lsp.call_hook("pylsp_initialize")
75+
76+
@LSP_SERVER.feature(lsptyp.INITIALIZED)
77+
async def initialized(ls: LanguageServer, params: lsptyp.InitializedParams):
78+
"""Handle the initialized notification."""
79+
# Call the initialized hook
80+
await ls.lsp.call_hook("pylsp_initialized")
81+
82+
@LSP_SERVER.feature(lsptyp.WORKSPACE_DID_CHANGE_CONFIGURATION)
83+
async def workspace_did_change_configuration(ls: LanguageServer, params: lsptyp.WorkspaceConfigurationParams):
84+
"""Handle the workspace did change configuration notification."""
85+
for config_item in params.items:
86+
ls.workspace.config.update({config_item.scope_uri: config_item.section})
87+
# TODO: Check configuration update is valid and supports this type of update
88+
await ls.lsp.call_hook("pylsp_workspace_configuration_changed")
89+
90+
@LSP_SERVER.feature(lsptyp.WORKSPACE_DID_CHANGE_WATCHED_FILES)
91+
def workspace_did_change_watched_files(ls: LanguageServer, params: lsptyp.DidChangeWatchedFilesParams):
92+
"""Handle the workspace did change watched files notification."""
93+
for change in params.changes:
94+
if change.uri.endswith(CONFIG_FILES):
95+
ls.workspace.config.settings.cache_clear()
96+
break
97+
98+
# TODO: check if necessary to link files not handled by textDocument/Open
99+
100+
@LSP_SERVER.feature(lsptyp.WORKSPACE_EXECUTE_COMMAND)
101+
async def workspace_execute_command(ls: LanguageServer, params: lsptyp.ExecuteCommandParams):
102+
"""Handle the workspace execute command request."""
103+
# Call the execute command hook
104+
await ls.lsp.call_hook("pylsp_execute_command", command=params.command, arguments=params.arguments, work_done_token=params.work_done_token)
105+
106+
@LSP_SERVER.feature(lsptyp.NOTEBOOK_DOCUMENT_DID_OPEN)
107+
async def notebook_document_did_open(ls: LanguageServer, params: lsptyp.DidOpenNotebookDocumentParams):
108+
"""Handle the notebook document did open notification."""
109+
await ls.lsp.lint_notebook_document(params.notebook_document.uri)
110+
111+
@LSP_SERVER.feature(lsptyp.NOTEBOOK_DOCUMENT_DID_CHANGE)
112+
async def notebook_document_did_change(ls: LanguageServer, params: lsptyp.DidChangeNotebookDocumentParams):
113+
"""Handle the notebook document did change notification."""
114+
await ls.lsp.lint_notebook_document(params.notebook_document.uri)
115+
116+
@LSP_SERVER.feature(lsptyp.NOTEBOOK_DOCUMENT_DID_SAVE)
117+
async def notebook_document_did_save(ls: LanguageServer, params: lsptyp.DidSaveNotebookDocumentParams):
118+
"""Handle the notebook document did save notification."""
119+
await ls.lsp.lint_notebook_document(params.notebook_document.uri)
120+
await ls.workspace.save(params.notebook_document.uri)
121+
122+
@LSP_SERVER.feature(lsptyp.NOTEBOOK_DOCUMENT_DID_CLOSE)
123+
async def notebook_document_did_close(ls: LanguageServer, params: lsptyp.DidCloseNotebookDocumentParams):
124+
"""Handle the notebook document did close notification."""
125+
await ls.lsp.cancel_tasks(params.notebook_document.uri)
126+
127+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DID_OPEN)
128+
async def text_document_did_open(ls: LanguageServer, params: lsptyp.DidOpenTextDocumentParams):
129+
"""Handle the text document did open notification."""
130+
await ls.lsp.lint_text_document(params.text_document.uri)
131+
132+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DID_CHANGE)
133+
async def text_document_did_change(ls: LanguageServer, params: lsptyp.DidChangeTextDocumentParams):
134+
"""Handle the text document did change notification."""
135+
await ls.lsp.lint_text_document(params.text_document.uri)
136+
137+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DID_SAVE)
138+
async def text_document_did_save(ls: LanguageServer, params: lsptyp.DidSaveTextDocumentParams):
139+
"""Handle the text document did save notification."""
140+
await ls.lsp.lint_text_document(params.text_document.uri)
141+
await ls.workspace.save(params.text_document.uri)
142+
143+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DID_CLOSE)
144+
async def text_document_did_close(ls: LanguageServer, params: lsptyp.DidCloseTextDocumentParams):
145+
"""Handle the text document did close notification."""
146+
await ls.lsp.cancel_tasks(params.text_document.uri)
147+
148+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_CODE_ACTION)
149+
async def text_document_code_action(ls: LanguageServer, params: lsptyp.CodeActionParams) -> typ.List[lsptyp.Command | lsptyp.CodeAction] | None:
150+
"""Handle the text document code action request."""
151+
actions: typ.List[lsptyp.Command | lsptyp.CodeAction] | None = flatten(await ls.lsp.call_hook(
152+
"pylsp_code_action",
153+
params.text_document.uri,
154+
range=params.range,
155+
context=params.context,
156+
work_done_token=params.work_done_token,
157+
))
158+
return actions
159+
160+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_CODE_LENS)
161+
async def text_document_code_lens(ls: LanguageServer, params: lsptyp.CodeLensParams) -> typ.List[lsptyp.CodeLens] | None:
162+
"""Handle the text document code lens request."""
163+
lenses: typ.List[lsptyp.CodeLens] | None = flatten(await ls.lsp.call_hook(
164+
"pylsp_code_lens",
165+
params.text_document.uri,
166+
work_done_token=params.work_done_token,
167+
))
168+
return lenses
169+
170+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_COMPLETION)
171+
async def text_document_completion(ls: LanguageServer, params: lsptyp.CompletionParams) -> typ.List[lsptyp.CompletionItem] | None:
172+
"""Handle the text document completion request."""
173+
completions: typ.List[lsptyp.CompletionItem] | None = flatten(await ls.lsp.call_hook(
174+
"pylsp_completion",
175+
params.text_document.uri,
176+
position=params.position,
177+
context=params.context,
178+
work_done_token=params.work_done_token,
179+
))
180+
return completions
181+
182+
@LSP_SERVER.feature(lsptyp.COMPLETION_ITEM_RESOLVE)
183+
async def completion_item_resolve(ls: LanguageServer, params: lsptyp.CompletionItem) -> lsptyp.CompletionItem | None:
184+
"""Handle the completion item resolve request."""
185+
item: lsptyp.CompletionItem | None = await ls.lsp.call_hook(
186+
"pylsp_completion_item_resolve",
187+
(params.data or {}).get("doc_uri"),
188+
completion_item=params,
189+
)
190+
return item
191+
192+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DEFINITION)
193+
async def text_document_definition(ls: LanguageServer, params: lsptyp.DefinitionParams) -> lsptyp.Location | None:
194+
"""Handle the text document definition request."""
195+
location: lsptyp.Location | None = await ls.lsp.call_hook(
196+
"pylsp_definitions",
197+
params.text_document.uri,
198+
position=params.position,
199+
work_done_token=params.work_done_token,
200+
)
201+
return location
202+
203+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_TYPE_DEFINITION)
204+
async def text_document_type_definition(ls: LanguageServer, params: lsptyp.TypeDefinitionParams) -> lsptyp.Location | None:
205+
"""Handle the text document type definition request."""
206+
location: lsptyp.Location | None = await ls.lsp.call_hook(
207+
"pylsp_type_definition",
208+
params.text_document.uri,
209+
position=params.position,
210+
work_done_token=params.work_done_token,
211+
)
212+
return location
213+
214+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DOCUMENT_HIGHLIGHT)
215+
async def text_document_document_highlight(ls: LanguageServer, params: lsptyp.DocumentHighlightParams) -> typ.List[lsptyp.DocumentHighlight] | None:
216+
"""Handle the text document document highlight request."""
217+
highlights: typ.List[lsptyp.DocumentHighlight] | None = flatten(await ls.lsp.call_hook(
218+
"pylsp_document_highlight",
219+
params.text_document.uri,
220+
position=params.position,
221+
work_done_token=params.work_done_token,
222+
))
223+
return highlights
224+
225+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_HOVER)
226+
async def text_document_hover(ls: LanguageServer, params: lsptyp.HoverParams) -> lsptyp.Hover | None:
227+
"""Handle the text document hover request."""
228+
hover: lsptyp.Hover = await ls.lsp.call_hook(
229+
"pylsp_hover",
230+
params.text_document.uri,
231+
position=params.position,
232+
work_done_token=params.work_done_token,
233+
)
234+
return hover
235+
236+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
237+
async def text_document_document_symbol(ls: LanguageServer, params: lsptyp.DocumentSymbolParams) -> typ.List[lsptyp.DocumentSymbol] | None:
238+
"""Handle the text document document symbol request."""
239+
symbols: typ.List[lsptyp.DocumentSymbol] | None = flatten(await ls.lsp.call_hook(
240+
"pylsp_document_symbols",
241+
params.text_document.uri,
242+
work_done_token=params.work_done_token,
243+
))
244+
return symbols
245+
246+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_FORMATTING)
247+
async def text_document_formatting(ls: LanguageServer, params: lsptyp.DocumentFormattingParams) -> typ.List[lsptyp.TextEdit] | None:
248+
"""Handle the text document formatting request."""
249+
edits: typ.List[lsptyp.TextEdit] | None = flatten(await ls.lsp.call_hook(
250+
"pylsp_format_document",
251+
params.text_document.uri,
252+
options=params.options,
253+
))
254+
return edits
255+
256+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_RANGE_FORMATTING)
257+
async def text_document_range_formatting(ls: LanguageServer, params: lsptyp.DocumentRangeFormattingParams) -> typ.List[lsptyp.TextEdit] | None:
258+
"""Handle the text document range formatting request."""
259+
edits: typ.List[lsptyp.TextEdit] | None = flatten(await ls.lsp.call_hook(
260+
"pylsp_format_range",
261+
params.text_document.uri,
262+
range=params.range,
263+
options=params.options,
264+
))
265+
return edits
266+
267+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_FOLDING_RANGE)
268+
async def text_document_folding_range(ls: LanguageServer, params: lsptyp.FoldingRangeParams) -> typ.List[lsptyp.FoldingRange] | None:
269+
"""Handle the text document folding range request."""
270+
ranges: typ.List[lsptyp.FoldingRange] | None = flatten(await ls.lsp.call_hook(
271+
"pylsp_folding_range",
272+
params.text_document.uri,
273+
work_done_token=params.work_done_token,
274+
))
275+
return ranges
276+
277+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_REFERENCES)
278+
async def text_document_references(ls: LanguageServer, params: lsptyp.ReferenceParams) -> typ.List[lsptyp.Location] | None:
279+
"""Handle the text document references request."""
280+
locations: typ.List[lsptyp.Location] | None = flatten(await ls.lsp.call_hook(
281+
"pylsp_references",
282+
params.text_document.uri,
283+
position=params.position,
284+
context=params.context,
285+
))
286+
return locations
287+
288+
@LSP_SERVER.feature(lsptyp.TEXT_DOCUMENT_SIGNATURE_HELP)
289+
async def text_document_signature_help(ls: LanguageServer, params: lsptyp.SignatureHelpParams) -> lsptyp.SignatureHelp | None:
290+
"""Handle the text document signature help request."""
291+
signature_help: lsptyp.SignatureHelp | None = await ls.lsp.call_hook(
292+
"pylsp_signature_help",
293+
params.text_document.uri,
294+
position=params.position,
295+
work_done_token=params.work_done_token,
296+
)
297+
return signature_help
298+

0 commit comments

Comments
 (0)