summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--continuedev/src/continuedev/core/lsp.py533
-rw-r--r--continuedev/src/continuedev/core/sdk.py8
-rw-r--r--continuedev/src/continuedev/libs/lspclient/json_rpc_endpoint.py82
-rw-r--r--continuedev/src/continuedev/libs/lspclient/lsp_client.py150
-rw-r--r--continuedev/src/continuedev/libs/lspclient/lsp_endpoint.py73
-rw-r--r--continuedev/src/continuedev/libs/lspclient/lsp_structs.py316
-rw-r--r--continuedev/src/continuedev/plugins/steps/on_traceback.py2
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