def lsp_initialize(self, params: InitializeParams) -> InitializeResult: """Method that initializes language server. It will compute and return server capabilities based on registered features. """ logger.info('Language server initialized %s', params) self._server.process_id = params.process_id # Initialize server capabilities self.client_capabilities = params.capabilities self.server_capabilities = ServerCapabilitiesBuilder( self.client_capabilities, {**self.fm.features, **self.fm.builtin_features}.keys(), self.fm.feature_options, list(self.fm.commands.keys()), self._server.sync_kind, ).build() logger.debug('Server capabilities: %s', self.server_capabilities.dict()) root_path = params.root_path root_uri = params.root_uri or from_fs_path(root_path) # Initialize the workspace workspace_folders = params.workspace_folders or [] self.workspace = Workspace(root_uri, self._server.sync_kind, workspace_folders) self.trace = Trace.Off return InitializeResult(capabilities=self.server_capabilities)
class LanguageServerProtocol(JsonRPCProtocol, metaclass=LSPMeta): """A class that represents language server protocol. It contains implementations for generic LSP features. Attributes: workspace(Workspace): In memory workspace """ def __init__(self, server): super().__init__(server) self.workspace = None self.trace = None from pygls.progress import Progress self.progress = Progress(self) self._register_builtin_features() def _register_builtin_features(self): """Registers generic LSP features from this class.""" for name in dir(self): attr = getattr(self, name) if callable(attr) and hasattr(attr, 'method_name'): self.fm.add_builtin_feature(attr.method_name, attr) def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> ApplyWorkspaceEditResponse: """Sends apply edit request to the client.""" return self.send_request(WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label)) @lsp_method(EXIT) def lsp_exit(self, *args) -> None: """Stops the server process.""" self.transport.close() sys.exit(0 if self._shutdown else 1) @lsp_method(INITIALIZE) def lsp_initialize(self, params: InitializeParams) -> InitializeResult: """Method that initializes language server. It will compute and return server capabilities based on registered features. """ logger.info('Language server initialized %s', params) self._server.process_id = params.process_id # Initialize server capabilities self.client_capabilities = params.capabilities self.server_capabilities = ServerCapabilitiesBuilder( self.client_capabilities, {**self.fm.features, **self.fm.builtin_features}.keys(), self.fm.feature_options, list(self.fm.commands.keys()), self._server.sync_kind, ).build() logger.debug('Server capabilities: %s', self.server_capabilities.dict()) root_path = params.root_path root_uri = params.root_uri or from_fs_path(root_path) # Initialize the workspace workspace_folders = params.workspace_folders or [] self.workspace = Workspace(root_uri, self._server.sync_kind, workspace_folders) self.trace = Trace.Off return InitializeResult(capabilities=self.server_capabilities) @lsp_method(INITIALIZED) def lsp_initialized(self, *args) -> None: """Notification received when client and server are connected.""" pass @lsp_method(SHUTDOWN) def lsp_shutdown(self, *args) -> None: """Request from client which asks server to shutdown.""" for future in self._client_request_futures.values(): future.cancel() for future in self._server_request_futures.values(): future.cancel() self._shutdown = True return None @lsp_method(TEXT_DOCUMENT_DID_CHANGE) def lsp_text_document__did_change(self, params: DidChangeTextDocumentParams) -> None: """Updates document's content. (Incremental(from server capabilities); not configurable for now) """ for change in params.content_changes: self.workspace.update_document(params.text_document, change) @lsp_method(TEXT_DOCUMENT_DID_CLOSE) def lsp_text_document__did_close(self, params: DidCloseTextDocumentParams) -> None: """Removes document from workspace.""" self.workspace.remove_document(params.text_document.uri) @lsp_method(TEXT_DOCUMENT_DID_OPEN) def lsp_text_document__did_open(self, params: DidOpenTextDocumentParams) -> None: """Puts document to the workspace.""" self.workspace.put_document(params.text_document) @lsp_method(SET_TRACE_NOTIFICATION) def lsp_set_trace(self, params: SetTraceParams) -> None: """Changes server trace value.""" self.trace = params.value @lsp_method(WORKSPACE_DID_CHANGE_WORKSPACE_FOLDERS) def lsp_workspace__did_change_workspace_folders( self, params: DidChangeWorkspaceFoldersParams) -> None: """Adds/Removes folders from the workspace.""" logger.info('Workspace folders changed: %s', params) added_folders = params.event.added or [] removed_folders = params.event.removed or [] for f_add, f_remove in zip_longest(added_folders, removed_folders): if f_add: self.workspace.add_folder(f_add) if f_remove: self.workspace.remove_folder(f_remove.uri) @lsp_method(WORKSPACE_EXECUTE_COMMAND) def lsp_workspace__execute_command(self, params: ExecuteCommandParams, msg_id: str) -> None: """Executes commands with passed arguments and returns a value.""" cmd_handler = self.fm.commands[params.command] self._execute_request(msg_id, cmd_handler, params.arguments) def get_configuration(self, params: ConfigurationParams, callback: Optional[ConfigCallbackType] = None) -> Future: """Sends configuration request to the client. Args: params(ConfigurationParams): ConfigurationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WORKSPACE_CONFIGURATION, params, callback) def get_configuration_async(self, params: ConfigurationParams) -> asyncio.Future: """Calls `get_configuration` method but designed to use with coroutines Args: params(ConfigurationParams): ConfigurationParams from lsp specs Returns: asyncio.Future that can be awaited """ return asyncio.wrap_future(self.get_configuration(params)) def log_trace(self, message: str, verbose: Optional[str] = None) -> None: """Sends trace notification to the client.""" if self.trace == Trace.Off: return params = LogTraceParams(message=message) if verbose and self.trace == Trace.Verbose: params.verbose = verbose self.notify(LOG_TRACE_NOTIFICATION, params) def publish_diagnostics(self, doc_uri: str, diagnostics: List[Diagnostic]) -> None: """Sends diagnostic notification to the client.""" self.notify(TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, PublishDiagnosticsParams(uri=doc_uri, diagnostics=diagnostics)) def register_capability(self, params: RegistrationParams, callback: Optional[Callable[[], None]] = None) -> Future: """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_REGISTER_CAPABILITY, params, callback) def register_capability_async(self, params: RegistrationParams) -> asyncio.Future: """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.register_capability(params, None)) def semantic_tokens_refresh(self, callback: Optional[Callable[[], None]] = None) -> Future: """Requesting a refresh of all semantic tokens. Args: callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WORKSPACE_SEMANTIC_TOKENS_REFRESH, callback=callback) def semantic_tokens_refresh_async(self) -> asyncio.Future: """Requesting a refresh of all semantic tokens. Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.semantic_tokens_refresh(None)) def show_document(self, params: ShowDocumentParams, callback: Optional[ShowDocumentCallbackType] = None) -> Future: """Display a particular document in the user interface. Args: params(ShowDocumentParams): ShowDocumentParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WINDOW_SHOW_DOCUMENT, params, callback) def show_document_async(self, params: ShowDocumentParams) -> asyncio.Future: """Display a particular document in the user interface. Args: params(ShowDocumentParams): ShowDocumentParams from lsp specs Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.show_document(params, None)) def show_message(self, message, msg_type=MessageType.Info): """Sends message to the client to display message.""" self.notify(WINDOW_SHOW_MESSAGE, ShowMessageParams(type=msg_type, message=message)) def show_message_log(self, message, msg_type=MessageType.Log): """Sends message to the client's output channel.""" self.notify(WINDOW_LOG_MESSAGE, LogMessageParams(type=msg_type, message=message)) def unregister_capability(self, params: UnregistrationParams, callback: Optional[Callable[[], None]] = None) -> Future: """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_UNREGISTER_CAPABILITY, params, callback) def unregister_capability_async(self, params: UnregistrationParams) -> asyncio.Future: """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.unregister_capability(params, None))
class LanguageServerProtocol(JsonRPCProtocol, metaclass=LSPMeta): """A class that represents language server protocol. It contains implementations for generic LSP features. Attributes: workspace(Workspace): In memory workspace """ def __init__(self, server): super().__init__(server) self.workspace = None self._register_builtin_features() def _register_builtin_features(self): """Registers generic LSP features from this class.""" for name in dir(self): attr = getattr(self, name) if callable(attr) and name.startswith('bf_'): lsp_name = to_lsp_name(name[3:]) self.fm.add_builtin_feature(lsp_name, attr) def apply_edit(self, edit: WorkspaceEdit, label: str = None) -> \ ApplyWorkspaceEditResponse: """Sends apply edit request to the client.""" return self.send_request( WORKSPACE_APPLY_EDIT, ApplyWorkspaceEditParams(edit=edit, label=label)) def bf_exit(self, *args): """Stops the server process.""" self.transport.close() sys.exit(0 if self._shutdown else 1) def bf_initialize(self, params: InitializeParams): """Method that initializes language server. It will compute and return server capabilities based on registered features. """ logger.info('Language server initialized %s', params) self._server.process_id = params.process_id # Initialize server capabilities self.client_capabilities = params.capabilities self.server_capabilities = ServerCapabilitiesBuilder( self.client_capabilities, self.fm.features.keys(), self.fm.feature_options, list(self.fm.commands.keys()), self._server.sync_kind, ).build() logger.debug('Server capabilities: %s', self.server_capabilities.dict()) root_path = params.root_path root_uri = params.root_uri or from_fs_path(root_path) # Initialize the workspace workspace_folders = params.workspace_folders or [] self.workspace = Workspace(root_uri, self._server.sync_kind, workspace_folders) return InitializeResult(capabilities=self.server_capabilities) def bf_initialized(self, *args): """Notification received when client and server are connected.""" pass def bf_shutdown(self, *args): """Request from client which asks server to shutdown.""" for future in self._client_request_futures.values(): future.cancel() for future in self._server_request_futures.values(): future.cancel() self._shutdown = True return None def bf_text_document__did_change(self, params: DidChangeTextDocumentParams): """Updates document's content. (Incremental(from server capabilities); not configurable for now) """ for change in params.content_changes: self.workspace.update_document(params.text_document, change) def bf_text_document__did_close(self, params: DidCloseTextDocumentParams): """Removes document from workspace.""" self.workspace.remove_document(params.text_document.uri) def bf_text_document__did_open(self, params: DidOpenTextDocumentParams): """Puts document to the workspace.""" self.workspace.put_document(params.text_document) def bf_workspace__did_change_workspace_folders( self, params: DidChangeWorkspaceFoldersParams): """Adds/Removes folders from the workspace.""" logger.info('Workspace folders changed: %s', params) added_folders = params.event.added or [] removed_folders = params.event.removed or [] for f_add, f_remove in zip_longest(added_folders, removed_folders): if f_add: self.workspace.add_folder(f_add) if f_remove: self.workspace.remove_folder(f_remove.uri) def bf_workspace__execute_command(self, params: ExecuteCommandParams, msg_id): """Executes commands with passed arguments and returns a value.""" cmd_handler = self.fm.commands[params.command] self._execute_request(msg_id, cmd_handler, params.arguments) def get_configuration(self, params, callback): """Sends configuration request to the client. Args: params(dict): ConfigurationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(WORKSPACE_CONFIGURATION, params, callback) def get_configuration_async(self, params): """Calls `get_configuration` method but designed to use with coroutines Args: params(dict): ConfigurationParams from lsp specs Returns: asyncio.Future that can be awaited """ return asyncio.wrap_future(self.get_configuration(params, None)) def publish_diagnostics(self, doc_uri: str, diagnostics: List[Diagnostic]): """Sends diagnostic notification to the client.""" self.notify( TEXT_DOCUMENT_PUBLISH_DIAGNOSTICS, PublishDiagnosticsParams(uri=doc_uri, diagnostics=diagnostics)) def register_capability(self, params: RegistrationParams, callback): """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_REGISTER_CAPABILITY, params, callback) def register_capability_async(self, params: RegistrationParams): """Register a new capability on the client. Args: params(RegistrationParams): RegistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.register_capability(params, None)) def show_message(self, message, msg_type=MessageType.Info): """Sends message to the client to display message.""" self.notify(WINDOW_SHOW_MESSAGE, ShowMessageParams(type=msg_type, message=message)) def show_message_log(self, message, msg_type=MessageType.Log): """Sends message to the client's output channel.""" self.notify(WINDOW_LOG_MESSAGE, LogMessageParams(type=msg_type, message=message)) def unregister_capability(self, params: UnregistrationParams, callback): """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: concurrent.futures.Future object that will be resolved once a response has been received """ return self.send_request(CLIENT_UNREGISTER_CAPABILITY, params, callback) def unregister_capability_async(self, params: UnregistrationParams): """Unregister a new capability on the client. Args: params(UnregistrationParams): UnregistrationParams from lsp specs callback(callable): Callabe which will be called after response from the client is received Returns: asyncio.Future object that will be resolved once a response has been received """ return asyncio.wrap_future(self.unregister_capability(params, None))