11# Copyright (c) Microsoft Corporation. All rights reserved.
22# Licensed under the MIT License.
33"""Implementation of tool support over LSP."""
4+
45from __future__ import annotations
56
67import copy
1516from typing import Any , Optional , Sequence
1617
1718
19+ def _lsp_feature_safe_handle (func ):
20+ """Decorator to wrap LSP handlers in a try except block to catch all
21+ exceptions and log them instead of crashing the server."""
22+
23+ def wrapper (* args , ** kwargs ):
24+ try :
25+ return func (* args , ** kwargs )
26+ except Exception : # pylint: disable=broad-except
27+ log_error (
28+ f"Exception handling request { func .__name__ } :\r \n { traceback .format_exc (chain = True )} "
29+ )
30+
31+ return wrapper
32+
33+
1834# **********************************************************
1935# Update sys.path before importing any bundled libraries.
2036# **********************************************************
37+
38+
2139def update_sys_path (path_to_add : str , strategy : str ) -> None :
2240 """Add given path to `sys.path`."""
2341 if path_to_add not in sys .path and os .path .isdir (path_to_add ):
@@ -107,6 +125,7 @@ def update_sys_path(path_to_add: str, strategy: str) -> None:
107125
108126
109127@LSP_SERVER .feature (lsp .TEXT_DOCUMENT_DID_OPEN )
128+ @_lsp_feature_safe_handle
110129def did_open (params : lsp .DidOpenTextDocumentParams ) -> None :
111130 """LSP handler for textDocument/didOpen request."""
112131 document = LSP_SERVER .workspace .get_text_document (params .text_document .uri )
@@ -117,6 +136,7 @@ def did_open(params: lsp.DidOpenTextDocumentParams) -> None:
117136
118137
119138@LSP_SERVER .feature (lsp .TEXT_DOCUMENT_DID_SAVE )
139+ @_lsp_feature_safe_handle
120140def did_save (params : lsp .DidSaveTextDocumentParams ) -> None :
121141 """LSP handler for textDocument/didSave request."""
122142 document = LSP_SERVER .workspace .get_text_document (params .text_document .uri )
@@ -127,6 +147,7 @@ def did_save(params: lsp.DidSaveTextDocumentParams) -> None:
127147
128148
129149@LSP_SERVER .feature (lsp .TEXT_DOCUMENT_DID_CLOSE )
150+ @_lsp_feature_safe_handle
130151def did_close (params : lsp .DidCloseTextDocumentParams ) -> None :
131152 """LSP handler for textDocument/didClose request."""
132153 document = LSP_SERVER .workspace .get_text_document (params .text_document .uri )
@@ -137,6 +158,7 @@ def did_close(params: lsp.DidCloseTextDocumentParams) -> None:
137158
138159
139160@LSP_SERVER .feature (lsp .NOTEBOOK_DOCUMENT_DID_OPEN )
161+ @_lsp_feature_safe_handle
140162def notebook_did_open (params : lsp .DidOpenNotebookDocumentParams ) -> None :
141163 """LSP handler for notebookDocument/didOpen request."""
142164 nb = LSP_SERVER .workspace .get_notebook_document (
@@ -155,6 +177,7 @@ def notebook_did_open(params: lsp.DidOpenNotebookDocumentParams) -> None:
155177
156178
157179@LSP_SERVER .feature (lsp .NOTEBOOK_DOCUMENT_DID_CHANGE )
180+ @_lsp_feature_safe_handle
158181def notebook_did_change (params : lsp .DidChangeNotebookDocumentParams ) -> None :
159182 """LSP handler for notebookDocument/didChange request."""
160183 nb = LSP_SERVER .workspace .get_notebook_document (
@@ -167,9 +190,7 @@ def notebook_did_change(params: lsp.DidChangeNotebookDocumentParams) -> None:
167190 # Re-lint cells whose text content changed.
168191 if change .cells and change .cells .text_content :
169192 for text_change in change .cells .text_content :
170- document = LSP_SERVER .workspace .get_text_document (
171- text_change .document .uri
172- )
193+ document = LSP_SERVER .workspace .get_text_document (text_change .document .uri )
173194 diagnostics : list [lsp .Diagnostic ] = _linting_helper (document )
174195 LSP_SERVER .text_document_publish_diagnostics (
175196 lsp .PublishDiagnosticsParams (uri = document .uri , diagnostics = diagnostics )
@@ -200,6 +221,7 @@ def notebook_did_change(params: lsp.DidChangeNotebookDocumentParams) -> None:
200221
201222
202223@LSP_SERVER .feature (lsp .NOTEBOOK_DOCUMENT_DID_SAVE )
224+ @_lsp_feature_safe_handle
203225def notebook_did_save (params : lsp .DidSaveNotebookDocumentParams ) -> None :
204226 """LSP handler for notebookDocument/didSave request."""
205227 nb = LSP_SERVER .workspace .get_notebook_document (
@@ -218,6 +240,7 @@ def notebook_did_save(params: lsp.DidSaveNotebookDocumentParams) -> None:
218240
219241
220242@LSP_SERVER .feature (lsp .NOTEBOOK_DOCUMENT_DID_CLOSE )
243+ @_lsp_feature_safe_handle
221244def notebook_did_close (params : lsp .DidCloseNotebookDocumentParams ) -> None :
222245 """LSP handler for notebookDocument/didClose request."""
223246 for cell_doc in params .cell_text_documents :
@@ -324,6 +347,7 @@ def _get_severity(*_codes: list[str]) -> lsp.DiagnosticSeverity:
324347
325348
326349@LSP_SERVER .feature (lsp .TEXT_DOCUMENT_FORMATTING )
350+ @_lsp_feature_safe_handle
327351def formatting (params : lsp .DocumentFormattingParams ) -> list [lsp .TextEdit ] | None :
328352 """LSP handler for textDocument/formatting request."""
329353 # If your tool is a formatter you can use this handler to provide
@@ -388,6 +412,7 @@ def _match_line_endings(document: workspace.TextDocument, text: str) -> str:
388412# Required Language Server Initialization and Exit handlers.
389413# **********************************************************
390414@LSP_SERVER .feature (lsp .INITIALIZE )
415+ @_lsp_feature_safe_handle
391416def initialize (params : lsp .InitializeParams ) -> None :
392417 """LSP handler for initialize request."""
393418 log_to_output (f"CWD Server: { os .getcwd ()} " )
@@ -408,12 +433,14 @@ def initialize(params: lsp.InitializeParams) -> None:
408433
409434
410435@LSP_SERVER .feature (lsp .EXIT )
436+ @_lsp_feature_safe_handle
411437def on_exit (_params : Optional [Any ] = None ) -> None :
412438 """Handle clean up on exit."""
413439 jsonrpc .shutdown_json_rpc ()
414440
415441
416442@LSP_SERVER .feature (lsp .SHUTDOWN )
443+ @_lsp_feature_safe_handle
417444def on_shutdown (_params : Optional [Any ] = None ) -> None :
418445 """Handle clean up on shutdown."""
419446 jsonrpc .shutdown_json_rpc ()
0 commit comments