Ejemplo n.º 1
0
    def __init__(self, read_stream, write_stream, max_workers=MAX_WORKERS):
        from robocode_ls_core.lsp import LSPMessages

        self._config: IConfig = self._create_config()
        self._workspace: Optional[IWorkspace] = None
        self.root_uri = None
        self.watching_thread = None
        self.uri_workspace_mapper = {}

        self._jsonrpc_stream_reader = JsonRpcStreamReader(read_stream)
        self._jsonrpc_stream_writer = JsonRpcStreamWriter(write_stream)
        self._endpoint = Endpoint(self,
                                  self._jsonrpc_stream_writer.write,
                                  max_workers=max_workers)
        self._lsp_messages = LSPMessages(self._endpoint)

        self._shutdown = False
Ejemplo n.º 2
0
def communicate_lang_server(
    write_to, read_from, language_server_client_class=None, kwargs={}
):
    if language_server_client_class is None:
        from robocode_ls_core.unittest_tools.language_server_client import (
            _LanguageServerClient,
        )

        language_server_client_class = _LanguageServerClient

    from robocode_ls_core.jsonrpc.streams import JsonRpcStreamWriter
    from robocode_ls_core.jsonrpc.streams import JsonRpcStreamReader

    w = JsonRpcStreamWriter(write_to, sort_keys=True)
    r = JsonRpcStreamReader(read_from)

    language_server = language_server_client_class(w, r, **kwargs)
    try:
        yield language_server
    finally:
        if language_server.require_exit_messages:
            language_server.shutdown()
            language_server.exit()
Ejemplo n.º 3
0
    def _get_server_api(self):
        with self._server_lock:
            server_process = self._server_process

            if server_process is not None:
                # If someone killed it, dispose of internal references
                # and create a new process.
                if not is_process_alive(server_process.pid):
                    server_process = None
                    self._dispose_server_process()

            if server_process is None:
                try:
                    from robotframework_ls.options import Setup
                    from robotframework_ls.server_api.client import (
                        RobotFrameworkApiClient,
                    )
                    from robotframework_ls.server_api.server__main__ import (
                        start_server_process,
                    )
                    from robocode_ls_core.jsonrpc.streams import (
                        JsonRpcStreamWriter,
                        JsonRpcStreamReader,
                    )

                    args = []
                    if Setup.options.verbose:
                        args.append("-" + "v" * int(Setup.options.verbose))
                    if Setup.options.log_file:
                        log_id = _next_id()
                        # i.e.: use a log id in case we create more than one in the
                        # same session.
                        if log_id == 0:
                            args.append(
                                "--log-file="
                                + Setup.options.log_file
                                + self._log_extension
                            )
                        else:
                            args.append(
                                "--log-file="
                                + Setup.options.log_file
                                + (".%s" % (log_id,))
                                + self._log_extension
                            )

                    python_exe = self._get_python_executable()
                    environ = self._get_environ()

                    self._used_python_executable = python_exe
                    self._used_environ = environ

                    server_process = start_server_process(
                        args=args, python_exe=python_exe, env=environ
                    )

                    self._server_process = server_process

                    write_to = server_process.stdin
                    read_from = server_process.stdout
                    w = JsonRpcStreamWriter(write_to, sort_keys=True)
                    r = JsonRpcStreamReader(read_from)

                    api = self._server_api = RobotFrameworkApiClient(
                        w, r, server_process
                    )
                    api.initialize(
                        process_id=os.getpid(),
                        root_uri=self.workspace.root_uri,
                        workspace_folders=list(
                            {"uri": folder.uri, "name": folder.name}
                            for folder in list(self.workspace.folders.values())
                        ),
                    )

                    config = self._config
                    if config is not None:
                        api.forward(
                            "workspace/didChangeConfiguration",
                            {"settings": config.get_full_settings()},
                        )

                    # Open existing documents in the API.
                    for document in self.workspace.iter_documents():
                        try:
                            source = document.source
                        except Exception:
                            source = None

                        api.forward(
                            "textDocument/didOpen",
                            {
                                "textDocument": {
                                    "uri": document.uri,
                                    "version": document.version,
                                    "text": source,
                                }
                            },
                        )

                except Exception as e:
                    if server_process is None:
                        log.exception(
                            "Error starting robotframework server api (server_process=None)."
                        )
                    else:
                        exitcode = server_process.poll()
                        if exitcode is not None:
                            # Note: only read() if the process exited.
                            log.exception(
                                "Error starting robotframework server api. Exit code: %s Base exception: %s. Stderr: %s"
                                % (exitcode, e, server_process.stderr.read())
                            )
                        else:
                            log.exception(
                                "Error (%s) starting robotframework server api (still running). Base exception: %s."
                                % (exitcode, e)
                            )
                    self._dispose_server_process()
                finally:
                    if server_process is not None:
                        log.debug(
                            "Server api (%s) created pid: %s"
                            % (self, server_process.pid)
                        )

        return self._server_api
Ejemplo n.º 4
0
class PythonLanguageServer(MethodDispatcher):
    """ Implementation of the Microsoft VSCode Language Server Protocol
    https://github.com/Microsoft/language-server-protocol/blob/master/versions/protocol-1-x.md
    
    Based on: https://github.com/palantir/python-language-server/blob/develop/pyls/python_ls.py
    """
    def __init__(self, read_stream, write_stream, max_workers=MAX_WORKERS):
        from robocode_ls_core.lsp import LSPMessages

        self._config: IConfig = self._create_config()
        self._workspace: Optional[IWorkspace] = None
        self.root_uri = None
        self.watching_thread = None
        self.uri_workspace_mapper = {}

        self._jsonrpc_stream_reader = JsonRpcStreamReader(read_stream)
        self._jsonrpc_stream_writer = JsonRpcStreamWriter(write_stream)
        self._endpoint = Endpoint(self,
                                  self._jsonrpc_stream_writer.write,
                                  max_workers=max_workers)
        self._lsp_messages = LSPMessages(self._endpoint)

        self._shutdown = False

    @property
    def workspace(self) -> Optional[IWorkspace]:
        return self._workspace

    @workspace.setter
    def workspace(self, workspace: IWorkspace) -> None:
        self._workspace = workspace
        self._on_workspace_set(workspace)

    def _on_workspace_set(self, workspace: IWorkspace):
        pass

    @property  # i.e.: read-only
    def config(self) -> IConfig:
        return self._config

    def start(self):
        """Entry point for the server."""
        self._jsonrpc_stream_reader.listen(self._endpoint.consume)

    def m_shutdown(self, **_kwargs):
        self._shutdown = True

    def m_exit(self, **_kwargs):
        self._endpoint.shutdown()
        # If there's someone reading, we could deadlock here.
        self._jsonrpc_stream_reader.close()
        self._jsonrpc_stream_writer.close()

    def capabilities(self):
        return {}  # Subclasses should override for capabilities.

    def m_initialize(
        self,
        processId=None,
        rootUri=None,
        rootPath=None,
        initializationOptions=None,
        workspaceFolders=None,
        **_kwargs,
    ) -> dict:
        from robocode_ls_core.basic import exit_when_pid_exists
        from robocode_ls_core.lsp import WorkspaceFolder

        log.debug(
            "Language server initialized with:\n    processId: %s\n    rootUri: %s\n    rootPath: %s\n    initializationOptions: %s\n    workspaceFolders: %s",
            processId,
            rootUri,
            rootPath,
            initializationOptions,
            workspaceFolders,
        )
        if rootUri is None:
            rootUri = uris.from_fs_path(
                rootPath) if rootPath is not None else ""

        self.root_uri = rootUri
        if workspaceFolders:
            workspaceFolders = [WorkspaceFolder(**w) for w in workspaceFolders]

        self.workspace = self._create_workspace(rootUri, workspaceFolders
                                                or [])

        if processId not in (None, -1, 0):
            exit_when_pid_exists(processId)

        # Get our capabilities
        return {"capabilities": self.capabilities()}

    def _create_config(self) -> IConfig:
        raise NotImplementedError(f"Not implemented in: {self.__class__}")

    def _create_workspace(self, root_uri, workspace_folders) -> IWorkspace:
        from robocode_ls_core.workspace import Workspace

        return Workspace(root_uri, workspace_folders)

    def m_initialized(self, **_kwargs):
        pass

    def lint(self, doc_uri, is_saved):
        raise NotImplementedError(
            "Subclasses must override (current class: %s)." %
            (self.__class__, ))

    def m_text_document__did_close(self, textDocument=None, **_kwargs) -> None:
        ws = self.workspace
        if ws is not None:
            ws.remove_document(textDocument["uri"])

    def m_text_document__did_open(self, textDocument=None, **_kwargs) -> None:
        from robocode_ls_core.lsp import TextDocumentItem

        ws = self.workspace
        if ws is not None:
            ws.put_document(TextDocumentItem(**textDocument))
        self.lint(textDocument["uri"], is_saved=True)

    def m_text_document__did_change(self,
                                    contentChanges=None,
                                    textDocument=None,
                                    **_kwargs):
        from robocode_ls_core.lsp import TextDocumentItem
        from robocode_ls_core.lsp import TextDocumentContentChangeEvent

        if contentChanges:
            text_document_item = TextDocumentItem(**textDocument)
            for change in contentChanges:
                try:
                    range = change.get("range", None)
                    range_length = change.get("rangeLength", 0)
                    text = change.get("text", "")
                    self.workspace.update_document(
                        text_document_item,
                        TextDocumentContentChangeEvent(
                            range=range, rangeLength=range_length, text=text),
                    )
                except:
                    log.exception(
                        "Error updating document: %s with changes: %s" %
                        (textDocument, contentChanges))
        self.lint(textDocument["uri"], is_saved=False)

    def m_text_document__did_save(self, textDocument=None, **_kwargs):
        self.lint(textDocument["uri"], is_saved=True)

    def m_workspace__did_change_configuration(self, settings=None) -> None:
        self.config.update(settings or {})

    def m_workspace__did_change_workspace_folders(self, event=None):
        """Adds/Removes folders from the workspace."""
        from robocode_ls_core.lsp import WorkspaceFolder

        log.info(f"Workspace folders changed: {event}")

        added_folders = []
        removed_folders = []
        if event:
            added_folders = event.get("added", [])
            removed_folders = event.get("removed", [])

        for f_add in added_folders:
            self.workspace.add_folder(WorkspaceFolder(**f_add))

        for f_remove in removed_folders:
            self.workspace.remove_folder(f_remove["uri"])

    def m_workspace__did_change_watched_files(self, changes=None, **_kwargs):
        pass
Ejemplo n.º 5
0
def writer(wfile):
    return JsonRpcStreamWriter(wfile, sort_keys=True)