diff options
7 files changed, 274 insertions, 890 deletions
| diff --git a/continuedev/src/continuedev/core/lsp.py b/continuedev/src/continuedev/core/lsp.py index 86923fb7..181eea2e 100644 --- a/continuedev/src/continuedev/core/lsp.py +++ b/continuedev/src/continuedev/core/lsp.py @@ -1,282 +1,272 @@  import asyncio -import os -import socket -import subprocess  import threading  from typing import List, Optional +import aiohttp  from pydantic import BaseModel -from pylsp.python_lsp import PythonLSPServer, start_tcp_lang_server +from pylsp.python_lsp import PythonLSPServer, start_ws_lang_server -from ..libs.lspclient.json_rpc_endpoint import JsonRpcEndpoint -from ..libs.lspclient.lsp_client import LspClient -from ..libs.lspclient.lsp_endpoint import LspEndpoint -from ..libs.lspclient.lsp_structs import Position as LspPosition -from ..libs.lspclient.lsp_structs import SymbolInformation, TextDocumentIdentifier  from ..libs.util.logging import logger  from ..models.filesystem import RangeInFile  from ..models.main import Position, Range -class ReadPipe(threading.Thread): -    def __init__(self, pipe): -        threading.Thread.__init__(self) -        self.pipe = pipe - -    def run(self): -        line = self.pipe.readline().decode("utf-8") -        while line: -            print(line) -            line = self.pipe.readline().decode("utf-8") +def filepath_to_uri(filename: str) -> str: +    return f"file://{filename}" -class SocketFileWrapper: -    def __init__(self, sockfile): -        self.sockfile = sockfile +def uri_to_filepath(uri: str) -> str: +    if uri.startswith("file://"): +        return uri.lstrip("file://") +    else: +        return uri -    def write(self, data): -        if isinstance(data, bytes): -            data = data.decode("utf-8").replace("\r\n", "\n") -        return self.sockfile.write(data) -    def read(self, size=-1): -        data = self.sockfile.read(size) -        if isinstance(data, str): -            data = data.replace("\n", "\r\n").encode("utf-8") -        return data +PORT = 8099 -    def readline(self, size=-1): -        data = self.sockfile.readline(size) -        if isinstance(data, str): -            data = data.replace("\n", "\r\n").encode("utf-8") -        return data -    def flush(self): -        return self.sockfile.flush() +class LSPClient: +    def __init__(self, host: str, port: int, workspace_paths: List[str]): +        self.host = host +        self.port = port +        self.session = aiohttp.ClientSession() +        self.next_id = 0 +        self.workspace_paths = workspace_paths -    def close(self): -        return self.sockfile.close() +    async def connect(self): +        print("Connecting") +        self.ws = await self.session.ws_connect(f"ws://{self.host}:{self.port}/") +        print("Connected") +    async def send(self, data): +        await self.ws.send_json(data) -async def create_json_rpc_endpoint(use_subprocess: Optional[str] = None): -    if use_subprocess is None: -        try: -            threading.Thread( -                target=start_tcp_lang_server, -                args=("localhost", 8080, False, PythonLSPServer), -            ).start() -        except Exception as e: -            logger.warning("Could not start TCP server: %s", e) +    async def recv(self): +        return await self.ws.receive_json() -        await asyncio.sleep(2) +    async def close(self): +        await self.ws.close() +        await self.session.close() -        # Connect to the server -        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) -        s.connect(("localhost", 8080)) +    async def call_method(self, method_name, **kwargs): +        body = { +            "jsonrpc": "2.0", +            "id": self.next_id, +            "method": method_name, +            "params": kwargs, +        } +        self.next_id += 1 +        await self.send(body) +        response = await self.recv() +        return response -        # Create a file-like object from the socket -        sockfile = s.makefile("rw") -        wrapped_sockfile = SocketFileWrapper(sockfile) -        return JsonRpcEndpoint(wrapped_sockfile, wrapped_sockfile), None +    async def initialize(self): +        initialization_args = { +            "capabilities": { +                "textDocument": { +                    "codeAction": {"dynamicRegistration": True}, +                    "codeLens": {"dynamicRegistration": True}, +                    "colorProvider": {"dynamicRegistration": True}, +                    "completion": { +                        "completionItem": { +                            "commitCharactersSupport": True, +                            "documentationFormat": ["markdown", "plaintext"], +                            "snippetSupport": True, +                        }, +                        "completionItemKind": { +                            "valueSet": [ +                                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, +                            ] +                        }, +                        "contextSupport": True, +                        "dynamicRegistration": True, +                    }, +                    "definition": {"dynamicRegistration": True}, +                    "documentHighlight": {"dynamicRegistration": True}, +                    "documentLink": {"dynamicRegistration": True}, +                    "documentSymbol": { +                        "dynamicRegistration": True, +                        "symbolKind": { +                            "valueSet": [ +                                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, +                            ] +                        }, +                    }, +                    "formatting": {"dynamicRegistration": True}, +                    "hover": { +                        "contentFormat": ["markdown", "plaintext"], +                        "dynamicRegistration": True, +                    }, +                    "implementation": {"dynamicRegistration": True}, +                    "onTypeFormatting": {"dynamicRegistration": True}, +                    "publishDiagnostics": {"relatedInformation": True}, +                    "rangeFormatting": {"dynamicRegistration": True}, +                    "references": {"dynamicRegistration": True}, +                    "rename": {"dynamicRegistration": True}, +                    "signatureHelp": { +                        "dynamicRegistration": True, +                        "signatureInformation": { +                            "documentationFormat": ["markdown", "plaintext"] +                        }, +                    }, +                    "synchronization": { +                        "didSave": True, +                        "dynamicRegistration": True, +                        "willSave": True, +                        "willSaveWaitUntil": True, +                    }, +                    "typeDefinition": {"dynamicRegistration": True}, +                }, +                "workspace": { +                    "applyEdit": True, +                    "configuration": True, +                    "didChangeConfiguration": {"dynamicRegistration": True}, +                    "didChangeWatchedFiles": {"dynamicRegistration": True}, +                    "executeCommand": {"dynamicRegistration": True}, +                    "symbol": { +                        "dynamicRegistration": True, +                        "symbolKind": { +                            "valueSet": [ +                                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, +                            ] +                        }, +                    }, +                    "workspaceEdit": {"documentChanges": True}, +                    "workspaceFolders": True, +                }, +            }, +            "processId": 1234, +            "rootPath": None, +            "rootUri": filepath_to_uri(self.workspace_paths[0]), +            "initializationOptions": {}, +            "trace": "off", +            "workspaceFolders": [ +                { +                    "uri": filepath_to_uri(workspacePath), +                    "name": workspacePath.split("/")[-1], +                } +                for workspacePath in self.workspace_paths +            ], +        } +        return await self.call_method("initialize", **initialization_args) + +    async def goto_definition(self, filepath: str, position: Position): +        return await self.call_method( +            "textDocument/definition", +            textDocument={"uri": filepath_to_uri(filepath)}, +            position=position.dict(), +        ) -    else: -        pyls_cmd = use_subprocess.split() -        p = subprocess.Popen( -            pyls_cmd, -            stdin=subprocess.PIPE, -            stdout=subprocess.PIPE, -            stderr=subprocess.PIPE, +    async def document_symbol(self, filepath: str): +        return await self.call_method( +            "textDocument/documentSymbol", +            textDocument={"uri": filepath_to_uri(filepath)},          ) -        read_pipe = ReadPipe(p.stderr) -        read_pipe.start() -        return JsonRpcEndpoint(p.stdin, p.stdout), p -def filename_to_uri(filename: str) -> str: -    return f"file://{filename}" +async def start_language_server() -> threading.Thread: +    try: +        thread = threading.Thread( +            target=start_ws_lang_server, +            args=(PORT, False, PythonLSPServer), +        ) +        thread.daemon = True +        thread.start() +    except Exception as e: +        logger.warning("Could not start TCP server: %s", e) -def uri_to_filename(uri: str) -> str: -    if uri.startswith("file://"): -        return uri.lstrip("file://") -    else: -        return uri +    await asyncio.sleep(2) +    return thread -async def create_lsp_client(workspace_dir: str, use_subprocess: Optional[str] = None): -    json_rpc_endpoint, process = await create_json_rpc_endpoint( -        use_subprocess=use_subprocess -    ) -    lsp_endpoint = LspEndpoint(json_rpc_endpoint) -    lsp_client = LspClient(lsp_endpoint) -    capabilities = { -        "textDocument": { -            "codeAction": {"dynamicRegistration": True}, -            "codeLens": {"dynamicRegistration": True}, -            "colorProvider": {"dynamicRegistration": True}, -            "completion": { -                "completionItem": { -                    "commitCharactersSupport": True, -                    "documentationFormat": ["markdown", "plaintext"], -                    "snippetSupport": True, -                }, -                "completionItemKind": { -                    "valueSet": [ -                        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, -                    ] -                }, -                "contextSupport": True, -                "dynamicRegistration": True, -            }, -            "definition": {"dynamicRegistration": True}, -            "documentHighlight": {"dynamicRegistration": True}, -            "documentLink": {"dynamicRegistration": True}, -            "documentSymbol": { -                "dynamicRegistration": True, -                "symbolKind": { -                    "valueSet": [ -                        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, -                    ] -                }, -            }, -            "formatting": {"dynamicRegistration": True}, -            "hover": { -                "contentFormat": ["markdown", "plaintext"], -                "dynamicRegistration": True, -            }, -            "implementation": {"dynamicRegistration": True}, -            "onTypeFormatting": {"dynamicRegistration": True}, -            "publishDiagnostics": {"relatedInformation": True}, -            "rangeFormatting": {"dynamicRegistration": True}, -            "references": {"dynamicRegistration": True}, -            "rename": {"dynamicRegistration": True}, -            "signatureHelp": { -                "dynamicRegistration": True, -                "signatureInformation": { -                    "documentationFormat": ["markdown", "plaintext"] -                }, -            }, -            "synchronization": { -                "didSave": True, -                "dynamicRegistration": True, -                "willSave": True, -                "willSaveWaitUntil": True, -            }, -            "typeDefinition": {"dynamicRegistration": True}, -        }, -        "workspace": { -            "applyEdit": True, -            "configuration": True, -            "didChangeConfiguration": {"dynamicRegistration": True}, -            "didChangeWatchedFiles": {"dynamicRegistration": True}, -            "executeCommand": {"dynamicRegistration": True}, -            "symbol": { -                "dynamicRegistration": True, -                "symbolKind": { -                    "valueSet": [ -                        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, -                    ] -                }, -            }, -            "workspaceEdit": {"documentChanges": True}, -            "workspaceFolders": True, -        }, -    } -    root_uri = filename_to_uri(workspace_dir) -    dir_name = os.path.basename(workspace_dir) -    workspace_folders = [{"name": dir_name, "uri": root_uri}] -    lsp_client.initialize( -        None, -        None, -        root_uri, -        None, -        capabilities, -        "off", -        workspace_folders, -    ) -    lsp_client.initialized() -    return lsp_client, process + +class DocumentSymbol(BaseModel): +    name: str +    containerName: Optional[str] = None +    kind: int +    location: RangeInFile  class ContinueLSPClient(BaseModel):      workspace_dir: str -    lsp_client: LspClient = None -    use_subprocess: Optional[str] = None -    lsp_process: Optional[subprocess.Popen] = None + +    lsp_client: LSPClient = None +    lsp_thread: Optional[threading.Thread] = None      class Config:          arbitrary_types_allowed = True @@ -287,26 +277,26 @@ class ContinueLSPClient(BaseModel):          return original_dict      async def start(self): -        self.lsp_client, self.lsp_process = await create_lsp_client( -            self.workspace_dir, use_subprocess=self.use_subprocess -        ) +        self.lsp_thread = await start_language_server() +        self.lsp_client = LSPClient("localhost", PORT, [self.workspace_dir]) +        await self.lsp_client.connect() +        await self.lsp_client.initialize()      async def stop(self): -        self.lsp_client.shutdown() -        self.lsp_client.exit() -        if self.lsp_process is not None: -            self.lsp_process.terminate() -            self.lsp_process.wait() -            self.lsp_process = None - -    def goto_definition(self, position: Position, filename: str) -> List[RangeInFile]: -        response = self.lsp_client.definition( -            TextDocumentIdentifier(filename_to_uri(filename)), -            LspPosition(position.line, position.character), +        await self.lsp_client.close() +        if self.lsp_thread: +            self.lsp_thread.join() + +    async def goto_definition( +        self, position: Position, filename: str +    ) -> List[RangeInFile]: +        response = self.lsp_client.goto_definition( +            filename, +            position,          )          return [              RangeInFile( -                filepath=uri_to_filename(x.uri), +                filepath=uri_to_filepath(x.uri),                  range=Range.from_shorthand(                      x.range.start.line,                      x.range.start.character, @@ -317,9 +307,22 @@ class ContinueLSPClient(BaseModel):              for x in response          ] -    def get_symbols(self, filepath: str) -> List[SymbolInformation]: -        response = self.lsp_client.documentSymbol( -            TextDocumentIdentifier(filename_to_uri(filepath)) -        ) - -        return response +    async def document_symbol(self, filepath: str) -> List: +        response = await self.lsp_client.document_symbol(filepath) +        return [ +            DocumentSymbol( +                name=x["name"], +                containerName=x["containerName"], +                kind=x["kind"], +                location=RangeInFile( +                    filepath=uri_to_filepath(x["location"]["uri"]), +                    range=Range.from_shorthand( +                        x["location"]["range"]["start"]["line"], +                        x["location"]["range"]["start"]["character"], +                        x["location"]["range"]["end"]["line"], +                        x["location"]["range"]["end"]["character"], +                    ), +                ), +            ) +            for x in response["result"] +        ] diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py index 658848c8..de209114 100644 --- a/continuedev/src/continuedev/core/sdk.py +++ b/continuedev/src/continuedev/core/sdk.py @@ -2,6 +2,8 @@ import os  import traceback  from typing import Coroutine, List, Optional, Union +from ..libs.util.create_async_task import create_async_task +  from ..libs.llm import LLM  from ..libs.util.devdata import dev_data_logger  from ..libs.util.logging import logger @@ -103,9 +105,9 @@ class ContinueSDK(AbstractContinueSDK):                  logger.warning(f"Failed to start LSP client: {e}", exc_info=True)                  sdk.lsp = None -        # create_async_task( -        #     start_lsp(), on_error=lambda e: logger.error("Failed to setup LSP: %s", e) -        # ) +        create_async_task( +            start_lsp(), on_error=lambda e: logger.error("Failed to setup LSP: %s", e) +        )          # When the config is loaded, setup posthog logger          posthog_logger.setup(sdk.ide.unique_id, sdk.config.allow_anonymous_telemetry) diff --git a/continuedev/src/continuedev/libs/lspclient/json_rpc_endpoint.py b/continuedev/src/continuedev/libs/lspclient/json_rpc_endpoint.py deleted file mode 100644 index 80c51000..00000000 --- a/continuedev/src/continuedev/libs/lspclient/json_rpc_endpoint.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import print_function - -import json -import re -import threading - -JSON_RPC_REQ_FORMAT = "Content-Length: {json_string_len}\r\n\r\n{json_string}" -JSON_RPC_RES_REGEX = "Content-Length: ([0-9]*)\r\n" -# TODO: add content-type - - -class MyEncoder(json.JSONEncoder): -    """ -    Encodes an object in JSON -    """ - -    def default(self, o): -        return o.__dict__ - - -class JsonRpcEndpoint(object): -    """ -    Thread safe JSON RPC endpoint implementation. Responsible to recieve and send JSON RPC messages, as described in the -    protocol. More information can be found: https://www.jsonrpc.org/ -    """ - -    def __init__(self, stdin, stdout): -        self.stdin = stdin -        self.stdout = stdout -        self.read_lock = threading.Lock() -        self.write_lock = threading.Lock() - -    @staticmethod -    def __add_header(json_string): -        """ -        Adds a header for the given json string - -        :param str json_string: The string -        :return: the string with the header -        """ -        return JSON_RPC_REQ_FORMAT.format( -            json_string_len=len(json_string), json_string=json_string -        ) - -    def send_request(self, message): -        """ -        Sends the given message. - -        :param dict message: The message to send. -        """ -        json_string = json.dumps(message, cls=MyEncoder) -        # print("sending:", json_string) -        jsonrpc_req = self.__add_header(json_string) -        with self.write_lock: -            self.stdin.write(jsonrpc_req.encode()) -            self.stdin.flush() - -    def recv_response(self): -        """ -        Recives a message. - -         :return: a message -        """ -        with self.read_lock: -            line = self.stdout.readline() -            if not line: -                return None -            # print(line) -            line = line.decode() -            # TODO: handle content type as well. -            match = re.match(JSON_RPC_RES_REGEX, line) -            if match is None or not match.groups(): -                raise RuntimeError("Bad header: " + line) -            size = int(match.groups()[0]) -            line = self.stdout.readline() -            if not line: -                return None -            line = line.decode() -            # if line != "\r\n": -            #     raise RuntimeError("Bad header: missing newline") -            jsonrpc_res = self.stdout.read(size + 2) -            return json.loads(jsonrpc_res) diff --git a/continuedev/src/continuedev/libs/lspclient/lsp_client.py b/continuedev/src/continuedev/libs/lspclient/lsp_client.py deleted file mode 100644 index fe2db6ad..00000000 --- a/continuedev/src/continuedev/libs/lspclient/lsp_client.py +++ /dev/null @@ -1,150 +0,0 @@ -from .lsp_structs import Location, SignatureHelp, SymbolInformation - - -class LspClient(object): -    def __init__(self, lsp_endpoint): -        """ -        Constructs a new LspClient instance. - -        :param lsp_endpoint: TODO -        """ -        self.lsp_endpoint = lsp_endpoint - -    def initialize( -        self, -        processId, -        rootPath, -        rootUri, -        initializationOptions, -        capabilities, -        trace, -        workspaceFolders, -    ): -        """ -        The initialize request is sent as the first request from the client to the server. If the server receives a request or notification -        before the initialize request it should act as follows: - -        1. For a request the response should be an error with code: -32002. The message can be picked by the server. -        2. Notifications should be dropped, except for the exit notification. This will allow the exit of a server without an initialize request. - -        Until the server has responded to the initialize request with an InitializeResult, the client must not send any additional requests or -        notifications to the server. In addition the server is not allowed to send any requests or notifications to the client until it has responded -        with an InitializeResult, with the exception that during the initialize request the server is allowed to send the notifications window/showMessage, -        window/logMessage and telemetry/event as well as the window/showMessageRequest request to the client. - -        The initialize request may only be sent once. - -        :param int processId: The process Id of the parent process that started the server. Is null if the process has not been started by another process. -                                If the parent process is not alive then the server should exit (see exit notification) its process. -        :param str rootPath: The rootPath of the workspace. Is null if no folder is open. Deprecated in favour of rootUri. -        :param DocumentUri rootUri: The rootUri of the workspace. Is null if no folder is open. If both `rootPath` and `rootUri` are set -                                    `rootUri` wins. -        :param any initializationOptions: User provided initialization options. -        :param ClientCapabilities capabilities: The capabilities provided by the client (editor or tool). -        :param Trace trace: The initial trace setting. If omitted trace is disabled ('off'). -        :param list workspaceFolders: The workspace folders configured in the client when the server starts. This property is only available if the client supports workspace folders. -                                        It can be `null` if the client supports workspace folders but none are configured. -        """ -        self.lsp_endpoint.start() -        return self.lsp_endpoint.call_method( -            "initialize", -            processId=processId, -            rootPath=rootPath, -            rootUri=rootUri, -            initializationOptions=initializationOptions, -            capabilities=capabilities, -            trace=trace, -            workspaceFolders=workspaceFolders, -        ) - -    def initialized(self): -        """ -        The initialized notification is sent from the client to the server after the client received the result of the initialize request -        but before the client is sending any other request or notification to the server. The server can use the initialized notification -        for example to dynamically register capabilities. The initialized notification may only be sent once. -        """ -        self.lsp_endpoint.send_notification("initialized") - -    def shutdown(self): -        """ -        The initialized notification is sent from the client to the server after the client received the result of the initialize request -        but before the client is sending any other request or notification to the server. The server can use the initialized notification -        for example to dynamically register capabilities. The initialized notification may only be sent once. -        """ -        self.lsp_endpoint.stop() -        return self.lsp_endpoint.call_method("shutdown") - -    def exit(self): -        """ -        The initialized notification is sent from the client to the server after the client received the result of the initialize request -        but before the client is sending any other request or notification to the server. The server can use the initialized notification -        for example to dynamically register capabilities. The initialized notification may only be sent once. -        """ -        self.lsp_endpoint.send_notification("exit") - -    def didOpen(self, textDocument): -        """ -        The document open notification is sent from the client to the server to signal newly opened text documents. The document's truth is -        now managed by the client and the server must not try to read the document's truth using the document's uri. Open in this sense -        means it is managed by the client. It doesn't necessarily mean that its content is presented in an editor. An open notification must -        not be sent more than once without a corresponding close notification send before. This means open and close notification must be -        balanced and the max open count for a particular textDocument is one. Note that a server's ability to fulfill requests is independent -        of whether a text document is open or closed. - -        The DidOpenTextDocumentParams contain the language id the document is associated with. If the language Id of a document changes, the -        client needs to send a textDocument/didClose to the server followed by a textDocument/didOpen with the new language id if the server -        handles the new language id as well. - -        :param TextDocumentItem textDocument: The initial trace setting. If omitted trace is disabled ('off'). -        """ -        return self.lsp_endpoint.send_notification( -            "textDocument/didOpen", textDocument=textDocument -        ) - -    def documentSymbol(self, textDocument): -        """ -        The document symbol request is sent from the client to the server to return a flat list of all symbols found in a given text document. -        Neither the symbol's location range nor the symbol's container name should be used to infer a hierarchy. - -        :param TextDocumentItem textDocument: The text document. -        """ -        result_dict = self.lsp_endpoint.call_method( -            "textDocument/documentSymbol", textDocument=textDocument -        ) -        return [SymbolInformation(**sym) for sym in result_dict] - -    def definition(self, textDocument, position): -        """ -        The goto definition request is sent from the client to the server to resolve the definition location of a symbol at a given text document position. - -        :param TextDocumentItem textDocument: The text document. -        :param Position position: The position inside the text document.. -        """ -        result_dict = self.lsp_endpoint.call_method( -            "textDocument/definition", textDocument=textDocument, position=position -        ) -        return [Location(**l) for l in result_dict] - -    def typeDefinition(self, textDocument, position): -        """ -        The goto type definition request is sent from the client to the server to resolve the type definition location of a symbol at a given text document position. - -        :param TextDocumentItem textDocument: The text document. -        :param Position position: The position inside the text document.. -        """ -        result_dict = self.lsp_endpoint.call_method( -            "textDocument/definition", textDocument=textDocument, position=position -        ) -        return [Location(**l) for l in result_dict] - -    def signatureHelp(self, textDocument, position): -        """ -        The signature help request is sent from the client to the server to request signature information at a given cursor position. - -        :param TextDocumentItem textDocument: The text document. -        :param Position position: The position inside the text document.. -        """ -        result_dict = self.lsp_endpoint.call_method( -            "textDocument/signatureHelp", textDocument=textDocument, position=position -        ) -        return SignatureHelp(**result_dict) diff --git a/continuedev/src/continuedev/libs/lspclient/lsp_endpoint.py b/continuedev/src/continuedev/libs/lspclient/lsp_endpoint.py deleted file mode 100644 index 9b37a06d..00000000 --- a/continuedev/src/continuedev/libs/lspclient/lsp_endpoint.py +++ /dev/null @@ -1,73 +0,0 @@ -from __future__ import print_function - -import threading -import time - - -class LspEndpoint(threading.Thread): -    def __init__(self, json_rpc_endpoint, default_callback=print, callbacks={}): -        threading.Thread.__init__(self) -        self.json_rpc_endpoint = json_rpc_endpoint -        self.callbacks = callbacks -        self.default_callback = default_callback -        self.event_dict = {} -        self.response_dict = {} -        self.next_id = 0 -        # self.daemon = True -        self.shutdown_flag = False - -    def handle_result(self, jsonrpc_res): -        self.response_dict[jsonrpc_res["id"]] = jsonrpc_res -        cond = self.event_dict[jsonrpc_res["id"]] -        cond.acquire() -        cond.notify() -        cond.release() - -    def stop(self): -        self.shutdown_flag = True - -    def run(self): -        while not self.shutdown_flag: -            time.sleep(0.1) -            jsonrpc_message = self.json_rpc_endpoint.recv_response() - -            if jsonrpc_message is None: -                print("server quit") -                break - -            # print("recieved message:", jsonrpc_message) -            if "result" in jsonrpc_message or "error" in jsonrpc_message: -                self.handle_result(jsonrpc_message) -            elif "method" in jsonrpc_message: -                if jsonrpc_message["method"] in self.callbacks: -                    self.callbacks[jsonrpc_message["method"]](jsonrpc_message) -                else: -                    self.default_callback(jsonrpc_message) -            else: -                print("unknown jsonrpc message") -            # print(jsonrpc_message) - -    def send_message(self, method_name, params, id=None): -        message_dict = {} -        message_dict["jsonrpc"] = "2.0" -        if id is not None: -            message_dict["id"] = id -        message_dict["method"] = method_name -        message_dict["params"] = params -        self.json_rpc_endpoint.send_request(message_dict) - -    def call_method(self, method_name, **kwargs): -        current_id = self.next_id -        self.next_id += 1 -        cond = threading.Condition() -        self.event_dict[current_id] = cond -        cond.acquire() -        self.send_message(method_name, kwargs, current_id) -        cond.wait() -        cond.release() -        # TODO: check if error, and throw an exception -        response = self.response_dict[current_id] -        return response["result"] - -    def send_notification(self, method_name, **kwargs): -        self.send_message(method_name, kwargs) diff --git a/continuedev/src/continuedev/libs/lspclient/lsp_structs.py b/continuedev/src/continuedev/libs/lspclient/lsp_structs.py deleted file mode 100644 index 2f0940d4..00000000 --- a/continuedev/src/continuedev/libs/lspclient/lsp_structs.py +++ /dev/null @@ -1,316 +0,0 @@ -def to_type(o, new_type): -    ''' -    Helper funciton that receives an object or a dict and convert it to a new given type. - -    :param object|dict o: The object to convert -    :param Type new_type: The type to convert to. -    ''' -    if new_type == type(o): -        return o -    else: -        return new_type(**o) - - -class Position(object): -    def __init__(self, line, character): -        """ -        Constructs a new Position instance. - -        :param int line: Line position in a document (zero-based). -        :param int character: Character offset on a line in a document (zero-based). -        """ -        self.line = line -        self.character = character - - -class Range(object): -    def __init__(self, start, end): -        """ -        Constructs a new Range instance. - -        :param Position start: The range's start position. -        :param Position end: The range's end position. -        """ -        self.start = to_type(start, Position) -        self.end = to_type(end, Position) - - -class Location(object): -    """ -    Represents a location inside a resource, such as a line inside a text file. -    """ -    def __init__(self, uri, range): -        """ -        Constructs a new Range instance. - -        :param str uri: Resource file. -        :param Range range: The range inside the file -        """ -        self.uri = uri -        self.range = to_type(range, Range) - -  -class Diagnostic(object): -     def __init__(self, range, severity, code, source, message, relatedInformation): -        """ -        Constructs a new Diagnostic instance. -        :param Range range: The range at which the message applies.Resource file. -        :param int severity: The diagnostic's severity. Can be omitted. If omitted it is up to the -                                client to interpret diagnostics as error, warning, info or hint. -        :param str code: The diagnostic's code, which might appear in the user interface. -        :param str source: A human-readable string describing the source of this -                            diagnostic, e.g. 'typescript' or 'super lint'. -        :param str message: The diagnostic's message. -        :param list relatedInformation: An array of related diagnostic information, e.g. when symbol-names within    -                                        a scope collide all definitions can be marked via this property. -        """ -        self.range = range -        self.severity = severity -        self.code = code -        self.source = source -        self.message = message -        self.relatedInformation = relatedInformation - - -class DiagnosticSeverity(object): -    Error = 1 -    Warning = 2 # TODO: warning is known in python -    Information = 3 -    Hint = 4 - - -class DiagnosticRelatedInformation(object): -    def __init__(self, location, message): -        """ -        Constructs a new Diagnostic instance. -        :param Location location: The location of this related diagnostic information. -        :param str message: The message of this related diagnostic information. -        """ -        self.location = location -        self.message = message - -  -class Command(object): -     def __init__(self, title, command, arguments): -        """ -        Constructs a new Diagnostic instance. -        :param str title: Title of the command, like `save`. -        :param str command: The identifier of the actual command handler. -        :param list argusments: Arguments that the command handler should be invoked with. -        """ -        self.title = title -        self.command = command -        self.arguments = arguments - - -class TextDocumentItem(object): -    """ -    An item to transfer a text document from the client to the server. -    """ -    def __init__(self, uri, languageId, version, text): -        """ -        Constructs a new Diagnostic instance. -         -        :param DocumentUri uri: Title of the command, like `save`. -        :param str languageId: The identifier of the actual command handler. -        :param int version: Arguments that the command handler should be invoked with. -        :param str text: Arguments that the command handler should be invoked with. -        """ -        self.uri = uri -        self.languageId = languageId -        self.version = version -        self.text = text - - -class TextDocumentIdentifier(object): -    """ -    Text documents are identified using a URI. On the protocol level, URIs are passed as strings.  -    """ -    def __init__(self, uri): -        """ -        Constructs a new TextDocumentIdentifier instance. - -        :param DocumentUri uri: The text document's URI.        -        """ -        self.uri = uri - -class TextDocumentPositionParams(object): -    """ -    A parameter literal used in requests to pass a text document and a position inside that document. -    """ -    def __init__(self, textDocument, position): -        """ -        Constructs a new TextDocumentPositionParams instance. -         -        :param TextDocumentIdentifier textDocument: The text document. -        :param Position position: The position inside the text document. -        """ -        self.textDocument = textDocument -        self.position = position - - -class LANGUAGE_IDENTIFIER: -    BAT="bat" -    BIBTEX="bibtex" -    CLOJURE="clojure" -    COFFESCRIPT="coffeescript" -    C="c" -    CPP="cpp" -    CSHARP="csharp" -    CSS="css" -    DIFF="diff" -    DOCKERFILE="dockerfile" -    FSHARP="fsharp" -    GIT_COMMIT="git-commit" -    GIT_REBASE="git-rebase" -    GO="go" -    GROOVY="groovy" -    HANDLEBARS="handlebars" -    HTML="html" -    INI="ini" -    JAVA="java" -    JAVASCRIPT="javascript" -    JSON="json" -    LATEX="latex" -    LESS="less" -    LUA="lua" -    MAKEFILE="makefile" -    MARKDOWN="markdown" -    OBJECTIVE_C="objective-c" -    OBJECTIVE_CPP="objective-cpp" -    Perl="perl" -    PHP="php" -    POWERSHELL="powershell" -    PUG="jade" -    PYTHON="python" -    R="r" -    RAZOR="razor" -    RUBY="ruby" -    RUST="rust" -    SASS="sass" -    SCSS="scss" -    ShaderLab="shaderlab" -    SHELL_SCRIPT="shellscript" -    SQL="sql" -    SWIFT="swift" -    TYPE_SCRIPT="typescript" -    TEX="tex" -    VB="vb" -    XML="xml" -    XSL="xsl" -    YAML="yaml" - - -class SymbolKind(object): -    File = 1 -    Module = 2 -    Namespace = 3 -    Package = 4 -    Class = 5 -    Method = 6 -    Property = 7 -    Field = 8 -    Constructor = 9 -    Enum = 10 -    Interface = 11 -    Function = 12 -    Variable = 13 -    Constant = 14 -    String = 15 -    Number = 16 -    Boolean = 17 -    Array = 18 -    Object = 19 -    Key = 20 -    Null = 21 -    EnumMember = 22 -    Struct = 23 -    Event = 24 -    Operator = 25 -    TypeParameter = 26 - - -class SymbolInformation(object): -    """ -    Represents information about programming constructs like variables, classes, interfaces etc. -    """ -    def __init__(self, name, kind, location, containerName, deprecated=False): -        """ -        Constructs a new SymbolInformation instance. - -        :param str name: The name of this symbol. -        :param int kind: The kind of this symbol. -        :param bool Location: The location of this symbol. The location's range is used by a tool -                                to reveal the location in the editor. If the symbol is selected in the -                                tool the range's start information is used to position the cursor. So -                                the range usually spans more then the actual symbol's name and does -                                normally include things like visibility modifiers. - -                                The range doesn't have to denote a node range in the sense of a abstract -                                syntax tree. It can therefore not be used to re-construct a hierarchy of -                                the symbols. -        :param str containerName: The name of the symbol containing this symbol. This information is for -                                    user interface purposes (e.g. to render a qualifier in the user interface -                                    if necessary). It can't be used to re-infer a hierarchy for the document -                                    symbols. -        :param bool deprecated: Indicates if this symbol is deprecated. -        """ -        self.name = name -        self.kind = kind -        self.deprecated = deprecated -        self.location = to_type(location, Location) -        self.containerName = containerName - - -class ParameterInformation(object): -    """ -    Represents a parameter of a callable-signature. A parameter can -    have a label and a doc-comment. -    """ -    def __init__(self, label, documentation=""): -        """ -        Constructs a new ParameterInformation instance. - -        :param str label: The label of this parameter. Will be shown in the UI. -        :param str documentation: The human-readable doc-comment of this parameter. Will be shown in the UI but can be omitted. -        """ -        self.label = label -        self.documentation = documentation - - -class SignatureInformation(object): -    """ -    Represents the signature of something callable. A signature -    can have a label, like a function-name, a doc-comment, and -    a set of parameters. -    """ -    def __init__(self, label, documentation="", parameters=[]): -        """ -        Constructs a new SignatureInformation instance. - -        :param str label: The label of this signature. Will be shown in the UI. -        :param str documentation: The human-readable doc-comment of this signature. Will be shown in the UI but can be omitted. -        :param ParameterInformation[] parameters: The parameters of this signature. -        """ -        self.label = label -        self.documentation = documentation -        self.parameters = [to_type(parameter, ParameterInformation) for parameter in parameters] - - -class SignatureHelp(object): -    """ -    Signature help represents the signature of something -    callable. There can be multiple signature but only one -    active and only one active parameter. -    """ -    def __init__(self, signatures, activeSignature=0, activeParameter=0): -        """ -        Constructs a new SignatureHelp instance. - -        :param SignatureInformation[] signatures: One or more signatures. -        :param int activeSignature: -        :param int activeParameter: -        """ -        self.signatures = [to_type(signature, SignatureInformation) for signature in signatures] -        self.activeSignature = activeSignature -        self.activeParameter = activeParameter
\ No newline at end of file diff --git a/continuedev/src/continuedev/plugins/steps/on_traceback.py b/continuedev/src/continuedev/plugins/steps/on_traceback.py index 078309d0..3a96a8c7 100644 --- a/continuedev/src/continuedev/plugins/steps/on_traceback.py +++ b/continuedev/src/continuedev/plugins/steps/on_traceback.py @@ -175,7 +175,7 @@ async def get_function_body(frame: TracebackFrame, sdk: ContinueSDK) -> Optional      if sdk.lsp is None:          return None -    document_symbols = sdk.lsp.get_symbols(frame.filepath) +    document_symbols = await sdk.lsp.document_symbol(frame.filepath)      for symbol in document_symbols:          if symbol.name == frame.function:              r = symbol.location.range | 
