From df8593b7aa91f7b48d7dc240cd285df52d49b02c Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Fri, 14 Feb 2025 09:44:38 -0300 Subject: [PATCH 01/18] feat: add support for C# language server and update related configurations --- .../languages/FoundRelationshipScope.py | 12 +-- blarify/code_hierarchy/languages/__init__.py | 1 + .../languages/csharp_definitions.py | 74 +++++++++++++++++++ blarify/code_references/implemented_lsp.py | 1 + blarify/code_references/lsp_caller.py | 4 +- blarify/code_references/lsp_helper.py | 5 ++ blarify/db_managers/neo4j_manager.py | 4 +- blarify/main.py | 63 +++++++++------- .../project_files_iterator.py | 7 +- blarify/project_graph_creator.py | 7 ++ poetry.lock | 24 +++++- pyproject.toml | 1 + 12 files changed, 164 insertions(+), 39 deletions(-) create mode 100644 blarify/code_hierarchy/languages/csharp_definitions.py diff --git a/blarify/code_hierarchy/languages/FoundRelationshipScope.py b/blarify/code_hierarchy/languages/FoundRelationshipScope.py index 1a33ddb7..806242a4 100644 --- a/blarify/code_hierarchy/languages/FoundRelationshipScope.py +++ b/blarify/code_hierarchy/languages/FoundRelationshipScope.py @@ -1,13 +1,15 @@ from dataclasses import dataclass -from tree_sitter import Node -from blarify.graph.relationship.relationship_type import RelationshipType -from typing import Optional +from typing import Optional, TYPE_CHECKING + +if TYPE_CHECKING: + from tree_sitter import Node + from blarify.graph.relationship.relationship_type import RelationshipType @dataclass class FoundRelationshipScope: - node_in_scope: Optional[Node] - relationship_type: RelationshipType + node_in_scope: Optional["Node"] + relationship_type: "RelationshipType" def __str__(self) -> str: return f"FoundRelationshipScope({self.node_in_scope}, {self.relationship_type})" diff --git a/blarify/code_hierarchy/languages/__init__.py b/blarify/code_hierarchy/languages/__init__.py index 95207dd3..0691030b 100644 --- a/blarify/code_hierarchy/languages/__init__.py +++ b/blarify/code_hierarchy/languages/__init__.py @@ -4,3 +4,4 @@ from .typescript_definitions import TypescriptDefinitions from .ruby_definitions import RubyDefinitions from .fallback_definitions import FallbackDefinitions +from .csharp_definitions import CsharpDefinitions diff --git a/blarify/code_hierarchy/languages/csharp_definitions.py b/blarify/code_hierarchy/languages/csharp_definitions.py new file mode 100644 index 00000000..a66d47a2 --- /dev/null +++ b/blarify/code_hierarchy/languages/csharp_definitions.py @@ -0,0 +1,74 @@ +from blarify.code_hierarchy.languages.FoundRelationshipScope import FoundRelationshipScope +from .language_definitions import LanguageDefinitions +from blarify.graph.relationship import RelationshipType + +import tree_sitter_c_sharp as tscsharp +from tree_sitter import Language, Parser + +from typing import Optional, Set, Dict + +from blarify.graph.node import NodeLabels +from tree_sitter import Node +from blarify.graph.node import Node as GraphNode + + +class CsharpDefinitions(LanguageDefinitions): + def get_language_name() -> str: + return "csharp" + + def get_parsers_for_extensions() -> Dict[str, Parser]: + return { + ".cs": Parser(Language(tscsharp.language())), + } + + def should_create_node(node: Node) -> bool: + return LanguageDefinitions._should_create_node_base_implementation( + node, + [ + " method_declaration", + "class_declaration", + "interface_declaration", + "constructor_declaration", + "record_declaration", + ], + ) + + def get_identifier_node(node: Node) -> Node: + return LanguageDefinitions._get_identifier_node_base_implementation(node) + + def get_body_node(node: Node) -> Node: + return LanguageDefinitions._get_body_node_base_implementation(node) + + def get_relationship_type(node: GraphNode, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + return CsharpDefinitions._find_relationship_type( + node_label=node.label, + node_in_point_reference=node_in_point_reference, + ) + + def get_node_label_from_type(type: str) -> NodeLabels: + return { + "class_declaration": NodeLabels.CLASS, + "method_declaration": NodeLabels.FUNCTION, + "interface_declaration": NodeLabels.CLASS, + "constructor_declaration": NodeLabels.FUNCTION, + "record_declaration": NodeLabels.CLASS, + }[type] + + def get_language_file_extensions() -> Set[str]: + return {".cs"} + + def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + relationship_types = CsharpDefinitions._get_relationship_types_by_label() + relevant_relationship_types = relationship_types.get(node_label, {}) + + return LanguageDefinitions._traverse_and_find_relationships( + node_in_point_reference, relevant_relationship_types + ) + + def _get_relationship_types_by_label() -> dict[str, RelationshipType]: + return { + NodeLabels.CLASS: {}, + NodeLabels.FUNCTION: { + "invocation_expression": RelationshipType.CALLS, + }, + } diff --git a/blarify/code_references/implemented_lsp.py b/blarify/code_references/implemented_lsp.py index 9c0c901d..487e780d 100644 --- a/blarify/code_references/implemented_lsp.py +++ b/blarify/code_references/implemented_lsp.py @@ -5,3 +5,4 @@ class ImplementedLsp(Enum): JEDI_LANGUAGE_SERVER = "jedi-language-server" TYPESCRIPT_LANGUAGE_SERVER = "typescript-language-server" SOLARGRAPH = "solargraph" + CSHARP_LS = "csharp-ls" diff --git a/blarify/code_references/lsp_caller.py b/blarify/code_references/lsp_caller.py index ebd67f66..1e7cf0ed 100644 --- a/blarify/code_references/lsp_caller.py +++ b/blarify/code_references/lsp_caller.py @@ -243,8 +243,8 @@ def map_reference(self, reference: dict) -> dict: } def log(self, message: dict) -> None: - if self.log: - self.pretty_print(message) + # if self.log: + self.pretty_print(message) def pretty_print(self, message: dict) -> None: ## print formatted json diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index f9af6db1..30fc5eea 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -1,4 +1,5 @@ from typing import Optional + from .lsp_caller import LspCaller from .types.Reference import Reference from blarify.graph.node import DefinitionNode @@ -9,6 +10,7 @@ RubyDefinitions, TypescriptDefinitions, LanguageDefinitions, + CsharpDefinitions, ) from collections import ChainMap @@ -38,6 +40,7 @@ def _create_extensions_to_lsp_servers(self): self._create_extension_to_lsp_servers(JavascriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), self._create_extension_to_lsp_servers(TypescriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), self._create_extension_to_lsp_servers(RubyDefinitions, ImplementedLsp.SOLARGRAPH), + self._create_extension_to_lsp_servers(CsharpDefinitions, ImplementedLsp.CSHARP_LS), ) def _create_extension_to_lsp_servers(self, language_definitions: LanguageDefinitions, lsp_server: ImplementedLsp): @@ -68,8 +71,10 @@ def _read_file(self, file_path: str) -> str: def get_paths_where_node_is_referenced(self, node: DefinitionNode) -> list[Reference]: lsp_caller = self.get_lsp_caller_for_extension(node.extension) references = lsp_caller.get_references(node.path, node.definition_range.start_dict) + if not references: return [] + return [Reference(reference) for reference in references] def get_definition_path_for_reference(self, reference: Reference) -> str: diff --git a/blarify/db_managers/neo4j_manager.py b/blarify/db_managers/neo4j_manager.py index 85771345..405b9058 100644 --- a/blarify/db_managers/neo4j_manager.py +++ b/blarify/db_managers/neo4j_manager.py @@ -91,12 +91,12 @@ def close(self): def create_nodes(self, nodeList: List[Any]): # Function to create nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_nodes_txn, nodeList, 10, repoId=self.repoId, entityId=self.entityId) + session.write_transaction(self._create_nodes_txn, nodeList, 100, repoId=self.repoId, entityId=self.entityId) def create_edges(self, edgesList: List[Any]): # Function to create edges between nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_edges_txn, edgesList, 10, entityId=self.entityId) + session.write_transaction(self._create_edges_txn, edgesList, 100, entityId=self.entityId) def format_query(self, query: str): # Function to format the query to be used in the fulltext index diff --git a/blarify/main.py b/blarify/main.py index fa8d359f..cf3b2c7e 100644 --- a/blarify/main.py +++ b/blarify/main.py @@ -18,8 +18,7 @@ def main(root_path: str = None, blarignore_path: str = None): lsp_query_helper.start() project_files_iterator = ProjectFilesIterator( - root_path=root_path, - blarignore_path=blarignore_path, + root_path=root_path, blarignore_path=blarignore_path, extensions_to_skip=[".json", ".xml"] ) ProjectFileStats(project_files_iterator).print(limit=10) @@ -151,6 +150,9 @@ def main_diff_with_previous( nodes = graph.get_nodes_as_objects() print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + + # batch create nodes and relationships + graph_manager.save_graph(nodes, relationships) graph_manager.close() lsp_query_helper.shutdown_exit_close() @@ -160,7 +162,7 @@ def main_diff_with_previous( dotenv.load_dotenv() root_path = os.getenv("ROOT_PATH") blarignore_path = os.getenv("BLARIGNORE_PATH") - # main(root_path=root_path, blarignore_path=blarignore_path) + main(root_path=root_path, blarignore_path=blarignore_path) # main_diff( # file_diffs=[ # FileDiff( @@ -186,32 +188,39 @@ def main_diff_with_previous( print("Updating") # main_update( # updated_files=[ - # UpdatedFile( - # path="file:///home/juan/devel/blar/git-webhook-tester/app/test/main.py", - # ), + # # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/schemas.py"), + # # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/models.py"), + # ], + # root_uri=root_path, + # blarignore_path=blarignore_path, + # ) + # main_update( + # updated_files=[ + # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/schemas.py"), + # UpdatedFile("file:///temp/repos/development/main/0/encuadrado-web/encuadrado-web/models.py"), # ], # root_uri=root_path, # blarignore_path=blarignore_path, # ) - main_diff_with_previous( - file_diffs=[ - FileDiff( - path="file:///home/juan/devel/blar/blar-qa/blar/agents/tasks.py", - diff_text="diff+++", - change_type=ChangeType.MODIFIED, - ), - ], - root_uri=root_path, - blarignore_path=blarignore_path, - previous_node_states=[ - PreviousNodeState( - "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_task", - open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), - ), - PreviousNodeState( - "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_taski", - open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), - ), - ], - ) + # main_diff_with_previous( + # file_diffs=[ + # FileDiff( + # path="file:///home/juan/devel/blar/blar-qa/blar/agents/tasks.py", + # diff_text="diff+++", + # change_type=ChangeType.MODIFIED, + # ), + # ], + # root_uri=root_path, + # blarignore_path=blarignore_path, + # previous_node_states=[ + # PreviousNodeState( + # "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_task", + # open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), + # ), + # PreviousNodeState( + # "/dev/MAIN/blar-qa/blar/agents/tasks.py.execute_pr_report_agent_taski", + # open("/home/juan/devel/blar/lsp-poc/blarify/example", "r").read(), + # ), + # ], + # ) diff --git a/blarify/project_file_explorer/project_files_iterator.py b/blarify/project_file_explorer/project_files_iterator.py index 43999bbb..83c7fd75 100644 --- a/blarify/project_file_explorer/project_files_iterator.py +++ b/blarify/project_file_explorer/project_files_iterator.py @@ -8,6 +8,7 @@ class ProjectFilesIterator: root_path: str paths_to_skip: List[str] names_to_skip: List[str] + extensions_to_skip: List[str] max_file_size_mb: int def __init__( @@ -15,12 +16,14 @@ def __init__( root_path: str, paths_to_skip: List[str] = None, names_to_skip: List[str] = None, + extensions_to_skip: List[str] = None, blarignore_path: str = None, max_file_size_mb: int = 10, ): self.paths_to_skip = paths_to_skip or [] self.root_path = root_path self.names_to_skip = names_to_skip or [] + self.extensions_to_skip = extensions_to_skip or [] self.max_file_size_mb = max_file_size_mb if blarignore_path: @@ -83,7 +86,9 @@ def _should_skip(self, path: str) -> bool: is_file_size_too_big = os.path.getsize(path) > self._mb_to_bytes(self.max_file_size_mb) - return is_basename_in_names_to_skip or is_path_in_paths_to_skip or is_file_size_too_big + is_extension_to_skip = any(path.endswith(extension) for extension in self.extensions_to_skip) + + return is_basename_in_names_to_skip or is_path_in_paths_to_skip or is_file_size_too_big or is_extension_to_skip def _mb_to_bytes(self, mb: int) -> int: return 1024 * 1024 * mb diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index b9e83039..c9b9843d 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -11,6 +11,7 @@ TypescriptDefinitions, FallbackDefinitions, RubyDefinitions, + CsharpDefinitions, ) from typing import List, TYPE_CHECKING from blarify.logger import Logger @@ -35,6 +36,7 @@ class ProjectGraphCreator: ".ts": TypescriptDefinitions, ".tsx": TypescriptDefinitions, ".rb": RubyDefinitions, + ".cs": CsharpDefinitions, } def __init__( @@ -171,6 +173,8 @@ def create_relationship_from_references(self, file_nodes: List["Node"]) -> None: if node.label == NodeLabels.FILE: continue + print(f"Processing node {node.name} of type {node.label}") + tree_sitter_helper = self._get_tree_sitter_for_file_extension(node.extension) references_relationships.extend( self.create_node_relationships(node=node, tree_sitter_helper=tree_sitter_helper) @@ -192,8 +196,11 @@ def _log_if_multiple_of_x(self, index: int, x: int, text: str) -> None: def create_node_relationships(self, node: "Node", tree_sitter_helper: TreeSitterHelper) -> List["Relationship"]: references = self.lsp_query_helper.get_paths_where_node_is_referenced(node) + print(f"References found: {len(references)}") relationships = RelationshipCreator.create_relationships_from_paths_where_node_is_referenced( references=references, node=node, graph=self.graph, tree_sitter_helper=tree_sitter_helper ) + print(f"Relationships created: {len(relationships)}") + return relationships diff --git a/poetry.lock b/poetry.lock index 19e57967..0ee56d10 100644 --- a/poetry.lock +++ b/poetry.lock @@ -111,6 +111,26 @@ files = [ docs = ["sphinx (>=7.3,<8.0)", "sphinx-book-theme"] tests = ["tree-sitter-html (>=0.23.0)", "tree-sitter-javascript (>=0.23.0)", "tree-sitter-json (>=0.23.0)", "tree-sitter-python (>=0.23.0)", "tree-sitter-rust (>=0.23.0)"] +[[package]] +name = "tree-sitter-c-sharp" +version = "0.23.1" +description = "C# grammar for tree-sitter" +optional = false +python-versions = ">=3.9" +files = [ + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:986e93d845a438ec3c4416401aa98e6a6f6631d644bbbc2e43fcb915c51d255d"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8024e466b2f5611c6dc90321f232d8584893c7fb88b75e4a831992f877616d2"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:7f9bf876866835492281d336b9e1f9626ab668737f74e914c31d285261507da7"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_amd64.whl", hash = "sha256:ae9a9e859e8f44e2b07578d44f9a220d3fa25b688966708af6aa55d42abeebb3"}, + {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-win_arm64.whl", hash = "sha256:c81548347a93347be4f48cb63ec7d60ef4b0efa91313330e69641e49aa5a08c5"}, + {file = "tree_sitter_c_sharp-0.23.1.tar.gz", hash = "sha256:322e2cfd3a547a840375276b2aea3335fa6458aeac082f6c60fec3f745c967eb"}, +] + +[package.extras] +core = ["tree-sitter (>=0.22,<1.0)"] + [[package]] name = "tree-sitter-javascript" version = "0.23.1" @@ -288,5 +308,5 @@ files = [ [metadata] lock-version = "2.0" -python-versions = ">=3.10,<=3.12.8" -content-hash = "a9c7603534dd4253b0674e07c5f0d98a7c52061374857fcef1b0317a762a2f31" +python-versions = ">=3.10,<=3.13" +content-hash = "39f2f6fcb74f8c3af214f2298baae627244be2c5b393953f63725fc9ce0b3cef" diff --git a/pyproject.toml b/pyproject.toml index 2af8e133..25890f22 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,6 +20,7 @@ tree-sitter-python = "^0.23.2" tree-sitter-ruby = "^0.23.0" tree-sitter-javascript = "^0.23.0" tree-sitter-typescript = "^0.23.2" +tree-sitter-c-sharp = "^0.23.1" [build-system] From e5dc41a1f68d1397e9e29cae1f2209910dffd219 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:44:00 -0300 Subject: [PATCH 02/18] refactor: remove unused ImplementedLsp class and clean up imports; update Python version constraint to 3.14; add get_relative_path_from_uri method --- .../languages/csharp_definitions.py | 10 +- blarify/code_references/__init__.py | 2 - blarify/code_references/implemented_lsp.py | 8 - blarify/code_references/lsp_helper.py | 148 ++++-- blarify/project_graph_creator.py | 14 +- blarify/utils/path_calculator.py | 7 + poetry.lock | 461 +++++++++++++++++- pyproject.toml | 3 +- 8 files changed, 575 insertions(+), 78 deletions(-) delete mode 100644 blarify/code_references/implemented_lsp.py diff --git a/blarify/code_hierarchy/languages/csharp_definitions.py b/blarify/code_hierarchy/languages/csharp_definitions.py index a66d47a2..84c0d1b8 100644 --- a/blarify/code_hierarchy/languages/csharp_definitions.py +++ b/blarify/code_hierarchy/languages/csharp_definitions.py @@ -25,7 +25,7 @@ def should_create_node(node: Node) -> bool: return LanguageDefinitions._should_create_node_base_implementation( node, [ - " method_declaration", + "method_declaration", "class_declaration", "interface_declaration", "constructor_declaration", @@ -67,7 +67,13 @@ def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> O def _get_relationship_types_by_label() -> dict[str, RelationshipType]: return { - NodeLabels.CLASS: {}, + NodeLabels.CLASS: { + "object_creation_expression": RelationshipType.INSTANTIATES, + "using_directive": RelationshipType.IMPORTS, + "variable_declaration": RelationshipType.TYPES, + "parameter": RelationshipType.TYPES, + "base_list": RelationshipType.INHERITS, + }, NodeLabels.FUNCTION: { "invocation_expression": RelationshipType.CALLS, }, diff --git a/blarify/code_references/__init__.py b/blarify/code_references/__init__.py index 59a1ccec..48d85c0a 100644 --- a/blarify/code_references/__init__.py +++ b/blarify/code_references/__init__.py @@ -1,3 +1 @@ -from .lsp_caller import LspCaller from .lsp_helper import LspQueryHelper, FileExtensionNotSupported -from .implemented_lsp import ImplementedLsp diff --git a/blarify/code_references/implemented_lsp.py b/blarify/code_references/implemented_lsp.py deleted file mode 100644 index 487e780d..00000000 --- a/blarify/code_references/implemented_lsp.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import Enum - - -class ImplementedLsp(Enum): - JEDI_LANGUAGE_SERVER = "jedi-language-server" - TYPESCRIPT_LANGUAGE_SERVER = "typescript-language-server" - SOLARGRAPH = "solargraph" - CSHARP_LS = "csharp-ls" diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 30fc5eea..88f47427 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -1,9 +1,11 @@ from typing import Optional -from .lsp_caller import LspCaller +from multilspy import SyncLanguageServer + +from blarify.utils.path_calculator import PathCalculator + from .types.Reference import Reference from blarify.graph.node import DefinitionNode -from .implemented_lsp import ImplementedLsp from blarify.code_hierarchy.languages import ( PythonDefinitions, JavascriptDefinitions, @@ -12,7 +14,9 @@ LanguageDefinitions, CsharpDefinitions, ) -from collections import ChainMap + +from multilspy.multilspy_config import MultilspyConfig +from multilspy.multilspy_logger import MultilspyLogger class FileExtensionNotSupported(Exception): @@ -21,67 +25,115 @@ class FileExtensionNotSupported(Exception): class LspQueryHelper: root_uri: str - lsp_callers: dict[ImplementedLsp, LspCaller] + language_to_lsp_server: dict[str, SyncLanguageServer] + + LSP_USAGES = 0 def __init__(self, root_uri: str, host: Optional[str] = None, port: Optional[int] = None): self.root_uri = root_uri - self.lsp_callers = self._create_lsp_callers(host=host, port=port) - self.extension_to_lsp_server = self._create_extensions_to_lsp_servers() - - def _create_lsp_callers(self, host: str, port: int) -> dict[ImplementedLsp, LspCaller]: - return { - lsp_server: LspCaller(root_uri=self.root_uri, lsp_server_name=lsp_server.value, host=host, port=port) - for lsp_server in ImplementedLsp - } - - def _create_extensions_to_lsp_servers(self): - return ChainMap( - self._create_extension_to_lsp_servers(PythonDefinitions, ImplementedLsp.JEDI_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(JavascriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(TypescriptDefinitions, ImplementedLsp.TYPESCRIPT_LANGUAGE_SERVER), - self._create_extension_to_lsp_servers(RubyDefinitions, ImplementedLsp.SOLARGRAPH), - self._create_extension_to_lsp_servers(CsharpDefinitions, ImplementedLsp.CSHARP_LS), - ) - - def _create_extension_to_lsp_servers(self, language_definitions: LanguageDefinitions, lsp_server: ImplementedLsp): - return {extension: lsp_server for extension in language_definitions.get_language_file_extensions()} + self.entered_lsp_servers = {} + self.language_to_lsp_server = {} + + def _get_language_definition_for_extension(self, extension: str) -> LanguageDefinitions: + if extension in PythonDefinitions.get_language_file_extensions(): + return PythonDefinitions + elif extension in JavascriptDefinitions.get_language_file_extensions(): + return JavascriptDefinitions + elif extension in TypescriptDefinitions.get_language_file_extensions(): + return TypescriptDefinitions + elif extension in RubyDefinitions.get_language_file_extensions(): + return RubyDefinitions + elif extension in CsharpDefinitions.get_language_file_extensions(): + return CsharpDefinitions + else: + raise FileExtensionNotSupported(f'File extension "{extension}" is not supported)') + + def _create_lsp_server(self, language_definitions: LanguageDefinitions): + language = language_definitions.get_language_name() + + config = MultilspyConfig.from_dict({"code_language": language}) + + logger = MultilspyLogger() + lsp = SyncLanguageServer.create(config, logger, PathCalculator.uri_to_path(self.root_uri)) + return lsp def start(self) -> None: - for lsp_caller in self.lsp_callers.values(): - lsp_caller.connect() - lsp_caller.initialize() + """ + DEPRECATED, LSP servers are started on demand + """ + + def get_or_create_multi_lsp(self, extension): + language_definitions = self._get_language_definition_for_extension(extension) + language = language_definitions.get_language_name() + + if language in self.language_to_lsp_server: + return self.language_to_lsp_server[language] + else: + new_lsp = self._create_lsp_server(language_definitions) + self.language_to_lsp_server[language] = new_lsp + self._initialize_lsp_server(language, new_lsp) + return new_lsp + + def _initialize_lsp_server(self, language, lsp): + context = lsp.start_server() + context.__enter__() + self.entered_lsp_servers[language] = context def initialize_directory(self, file) -> None: - lsp_caller = self.get_lsp_caller_for_extension(file.extension) - lsp_caller.did_open(document_uri=file.uri_path, text=self._read_file(file.path), extension=file.extension) - - def get_lsp_caller_for_extension(self, extension: str) -> LspCaller: - try: - return self.lsp_callers[self.extension_to_lsp_server[extension]] - except KeyError: - raise FileExtensionNotSupported(f'File extension "{extension}" is not supported') - - def _read_file(self, file_path: str) -> str: - try: - with open(file_path, "r") as file: - return file.read() - except UnicodeDecodeError: - return "" + """ + DEPRECATED, LSP servers are started on demand + """ def get_paths_where_node_is_referenced(self, node: DefinitionNode) -> list[Reference]: - lsp_caller = self.get_lsp_caller_for_extension(node.extension) - references = lsp_caller.get_references(node.path, node.definition_range.start_dict) + server = self.get_or_create_multi_lsp(node.extension) + references = self.request_references_with_fallback(node, server) if not references: return [] return [Reference(reference) for reference in references] + def request_references_with_fallback(self, node, lsp): + try: + references = lsp.request_references( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), + line=node.definition_range.start_dict["line"], + column=node.definition_range.start_dict["character"], + ) + except TimeoutError: + self.restart_lsp_for_extension(node) + lsp = self.multi_lsp_callers[node.extension] + references = lsp.request_references( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), + line=node.definition_range.start_dict["line"], + column=node.definition_range.start_dict["character"], + ) + + return references + + def restart_lsp_for_extension(self, node): + language_definitions = self._get_language_definition_for_extension(node.extension) + + new_lsp = self._create_lsp_server(language_definitions) + + print("Restarting LSP server") + try: + context = new_lsp.start_server() + context.__enter__() + except ConnectionResetError: + print("Connection reset error") + + self.entered_lsp_servers[node.extension] = context + def get_definition_path_for_reference(self, reference: Reference) -> str: - lsp_caller = self.get_lsp_caller_for_extension(".py") - definition = lsp_caller.get_definition(reference.uri, reference.start_dict) + lsp_caller = self.get_or_create_multi_lsp(".py") + definition = lsp_caller.request_definition( + file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=reference.uri), + line=reference.range.start.line, + column=reference.range.start.character, + ) return definition["uri"] if definition else "" def shutdown_exit_close(self) -> None: - for lsp_caller in self.lsp_callers.values(): - lsp_caller.shutdown_exit_close() + for lsp in self.entered_lsp_servers.values(): + lsp.__exit__(None, None, None) diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index c9b9843d..046922f0 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -58,7 +58,7 @@ def build(self) -> Graph: # TODO: Implement a better way to wait for the lsp to finish Logger.log("Waiting for LSP to finish") - time.sleep(15) + # time.sleep(15) Logger.log("LSP finished") self.create_relationships_from_references_for_files() @@ -173,8 +173,6 @@ def create_relationship_from_references(self, file_nodes: List["Node"]) -> None: if node.label == NodeLabels.FILE: continue - print(f"Processing node {node.name} of type {node.label}") - tree_sitter_helper = self._get_tree_sitter_for_file_extension(node.extension) references_relationships.extend( self.create_node_relationships(node=node, tree_sitter_helper=tree_sitter_helper) @@ -194,13 +192,15 @@ def _log_if_multiple_of_x(self, index: int, x: int, text: str) -> None: if index % x == 0: Logger.log(text) - def create_node_relationships(self, node: "Node", tree_sitter_helper: TreeSitterHelper) -> List["Relationship"]: + def create_node_relationships( + self, + node: "Node", + tree_sitter_helper: TreeSitterHelper, + ) -> List["Relationship"]: references = self.lsp_query_helper.get_paths_where_node_is_referenced(node) - print(f"References found: {len(references)}") + relationships = RelationshipCreator.create_relationships_from_paths_where_node_is_referenced( references=references, node=node, graph=self.graph, tree_sitter_helper=tree_sitter_helper ) - print(f"Relationships created: {len(relationships)}") - return relationships diff --git a/blarify/utils/path_calculator.py b/blarify/utils/path_calculator.py index aa6ed707..c814bb90 100644 --- a/blarify/utils/path_calculator.py +++ b/blarify/utils/path_calculator.py @@ -23,3 +23,10 @@ def compute_relative_path_with_prefix(pure_path: str, root_path: str) -> str: @staticmethod def get_parent_folder_path(file_path): return "/".join(file_path.split("/")[:-1]) + + @staticmethod + def get_relative_path_from_uri(root_uri: str, uri: str) -> str: + root_path = PathCalculator.uri_to_path(root_uri) + path = PathCalculator.uri_to_path(uri) + + return os.path.relpath(path, root_path) diff --git a/poetry.lock b/poetry.lock index 0ee56d10..563b56e0 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. [[package]] name = "asyncio" @@ -6,6 +6,7 @@ version = "3.4.3" description = "reference implementation of PEP 3156" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "asyncio-3.4.3-cp33-none-win32.whl", hash = "sha256:b62c9157d36187eca799c378e572c969f0da87cd5fc42ca372d92cdb06e7e1de"}, {file = "asyncio-3.4.3-cp33-none-win_amd64.whl", hash = "sha256:c46a87b48213d7464f22d9a497b9eef8c1928b68320a2fa94240f969f6fec08c"}, @@ -13,15 +14,292 @@ files = [ {file = "asyncio-3.4.3.tar.gz", hash = "sha256:83360ff8bc97980e4ff25c964c7bd3923d333d177aa4f7fb736b019f26c7cb41"}, ] +[[package]] +name = "attrs" +version = "25.1.0" +description = "Classes Without Boilerplate" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, + {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, +] + +[package.extras] +benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""] + +[[package]] +name = "cattrs" +version = "24.1.2" +description = "Composable complex class support for attrs and dataclasses." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "cattrs-24.1.2-py3-none-any.whl", hash = "sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0"}, + {file = "cattrs-24.1.2.tar.gz", hash = "sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85"}, +] + +[package.dependencies] +attrs = ">=23.1.0" +exceptiongroup = {version = ">=1.1.1", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4.1.0,<4.6.3 || >4.6.3", markers = "python_version < \"3.11\""} + +[package.extras] +bson = ["pymongo (>=4.4.0)"] +cbor2 = ["cbor2 (>=5.4.6)"] +msgpack = ["msgpack (>=1.0.5)"] +msgspec = ["msgspec (>=0.18.5) ; implementation_name == \"cpython\""] +orjson = ["orjson (>=3.9.2) ; implementation_name == \"cpython\""] +pyyaml = ["pyyaml (>=6.0)"] +tomlkit = ["tomlkit (>=0.11.8)"] +ujson = ["ujson (>=5.7.0)"] + +[[package]] +name = "certifi" +version = "2025.1.31" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, + {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, + {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, + {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, + {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, + {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, + {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, + {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, + {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, + {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, + {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, +] + +[[package]] +name = "docstring-to-markdown" +version = "0.15" +description = "On the fly conversion of Python docstrings to markdown" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "docstring-to-markdown-0.15.tar.gz", hash = "sha256:e146114d9c50c181b1d25505054a8d0f7a476837f0da2c19f07e06eaed52b73d"}, + {file = "docstring_to_markdown-0.15-py3-none-any.whl", hash = "sha256:27afb3faedba81e34c33521c32bbd258d7fbb79eedf7d29bc4e81080e854aec0"}, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +markers = "python_version < \"3.11\"" +files = [ + {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, + {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, +] + +[package.extras] +test = ["pytest (>=6)"] + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "jedi" +version = "0.19.2" +description = "An autocompletion tool for Python that can be used for text editors." +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"}, + {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"}, +] + +[package.dependencies] +parso = ">=0.8.4,<0.9.0" + +[package.extras] +docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"] + +[[package]] +name = "jedi-language-server" +version = "0.41.1" +description = "A language server for Jedi!" +optional = false +python-versions = ">=3.8,<4.0" +groups = ["main"] +files = [ + {file = "jedi_language_server-0.41.1-py3-none-any.whl", hash = "sha256:ca9b3e7f48b70f0988d85ffde4f01dd1ab94c8e0f69e8c6424e6657117b44f91"}, + {file = "jedi_language_server-0.41.1.tar.gz", hash = "sha256:3f15ca5cc28e728564f7d63583e171b418025582447ce023512e3f2b2d71ebae"}, +] + +[package.dependencies] +cattrs = ">=23.1.2" +docstring-to-markdown = "==0.*" +jedi = ">=0.19.0,<0.20.0" +lsprotocol = ">=2022.0.0a9" +pygls = ">=1.0.1,<2.0.0" + +[[package]] +name = "lsprotocol" +version = "2023.0.1" +description = "Python implementation of the Language Server Protocol." +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "lsprotocol-2023.0.1-py3-none-any.whl", hash = "sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2"}, + {file = "lsprotocol-2023.0.1.tar.gz", hash = "sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d"}, +] + +[package.dependencies] +attrs = ">=21.3.0" +cattrs = "!=23.2.1" + +[[package]] +name = "multilspy" +version = "0.0.12" +description = "A language-agnostic LSP client in Python, with a library interface. Intended to be used to build applications around language servers. Currently multilspy supports language servers for Python, Rust, Java, Go, JavaScript, Ruby and C#. Originally appeared as part of Monitor-Guided Decoding (https://github.com/microsoft/monitors4codegen)" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "multilspy-0.0.12-py3-none-any.whl", hash = "sha256:e06f7ef7c388793eaaa2d4a6bb91dd9ef3efc78090b34ca1d2343b52062c57fc"}, + {file = "multilspy-0.0.12.tar.gz", hash = "sha256:d6166302d15cda6d1c6820e9d0aac0a30382cb76dfc5fa2d1cee9f873289eb9c"}, +] + +[package.dependencies] +jedi-language-server = "0.41.1" +pydantic = ">=1.10.5,<2" +requests = "2.32.3" + [[package]] name = "neo4j" -version = "5.27.0" +version = "5.28.1" description = "Neo4j Bolt driver for Python" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ - {file = "neo4j-5.27.0-py3-none-any.whl", hash = "sha256:929c14b9e5341267324eca170b39d1798b032bffacc26a0529eacaf678ae483f"}, - {file = "neo4j-5.27.0.tar.gz", hash = "sha256:f82ee807cd15b178898d83f41a66372e11719a25dd487fd7bea48fd4b7323765"}, + {file = "neo4j-5.28.1-py3-none-any.whl", hash = "sha256:6755ef9e5f4e14b403aef1138fb6315b120631a0075c138b5ddb2a06b87b09fd"}, + {file = "neo4j-5.28.1.tar.gz", hash = "sha256:ae8e37a1d895099062c75bc359b2cce62099baac7be768d0eba7180c1298e214"}, ] [package.dependencies] @@ -32,12 +310,115 @@ numpy = ["numpy (>=1.7.0,<3.0.0)"] pandas = ["numpy (>=1.7.0,<3.0.0)", "pandas (>=1.1.0,<3.0.0)"] pyarrow = ["pyarrow (>=1.0.0)"] +[[package]] +name = "parso" +version = "0.8.4" +description = "A Python Parser" +optional = false +python-versions = ">=3.6" +groups = ["main"] +files = [ + {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, + {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, +] + +[package.extras] +qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] +testing = ["docopt", "pytest"] + +[[package]] +name = "pydantic" +version = "1.10.21" +description = "Data validation and settings management using python type hints" +optional = false +python-versions = ">=3.7" +groups = ["main"] +files = [ + {file = "pydantic-1.10.21-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:245e486e0fec53ec2366df9cf1cba36e0bbf066af7cd9c974bbbd9ba10e1e586"}, + {file = "pydantic-1.10.21-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6c54f8d4c151c1de784c5b93dfbb872067e3414619e10e21e695f7bb84d1d1fd"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b64708009cfabd9c2211295144ff455ec7ceb4c4fb45a07a804309598f36187"}, + {file = "pydantic-1.10.21-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a148410fa0e971ba333358d11a6dea7b48e063de127c2b09ece9d1c1137dde4"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:36ceadef055af06e7756eb4b871cdc9e5a27bdc06a45c820cd94b443de019bbf"}, + {file = "pydantic-1.10.21-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0501e1d12df6ab1211b8cad52d2f7b2cd81f8e8e776d39aa5e71e2998d0379f"}, + {file = "pydantic-1.10.21-cp310-cp310-win_amd64.whl", hash = "sha256:c261127c275d7bce50b26b26c7d8427dcb5c4803e840e913f8d9df3f99dca55f"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b6350b68566bb6b164fb06a3772e878887f3c857c46c0c534788081cb48adf4"}, + {file = "pydantic-1.10.21-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:935b19fdcde236f4fbf691959fa5c3e2b6951fff132964e869e57c70f2ad1ba3"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b6a04efdcd25486b27f24c1648d5adc1633ad8b4506d0e96e5367f075ed2e0b"}, + {file = "pydantic-1.10.21-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c1ba253eb5af8d89864073e6ce8e6c8dec5f49920cff61f38f5c3383e38b1c9f"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:57f0101e6c97b411f287a0b7cf5ebc4e5d3b18254bf926f45a11615d29475793"}, + {file = "pydantic-1.10.21-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:90e85834f0370d737c77a386ce505c21b06bfe7086c1c568b70e15a568d9670d"}, + {file = "pydantic-1.10.21-cp311-cp311-win_amd64.whl", hash = "sha256:6a497bc66b3374b7d105763d1d3de76d949287bf28969bff4656206ab8a53aa9"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2ed4a5f13cf160d64aa331ab9017af81f3481cd9fd0e49f1d707b57fe1b9f3ae"}, + {file = "pydantic-1.10.21-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3b7693bb6ed3fbe250e222f9415abb73111bb09b73ab90d2d4d53f6390e0ccc1"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:185d5f1dff1fead51766da9b2de4f3dc3b8fca39e59383c273f34a6ae254e3e2"}, + {file = "pydantic-1.10.21-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38e6d35cf7cd1727822c79e324fa0677e1a08c88a34f56695101f5ad4d5e20e5"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:1d7c332685eafacb64a1a7645b409a166eb7537f23142d26895746f628a3149b"}, + {file = "pydantic-1.10.21-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c9b782db6f993a36092480eeaab8ba0609f786041b01f39c7c52252bda6d85f"}, + {file = "pydantic-1.10.21-cp312-cp312-win_amd64.whl", hash = "sha256:7ce64d23d4e71d9698492479505674c5c5b92cda02b07c91dfc13633b2eef805"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0067935d35044950be781933ab91b9a708eaff124bf860fa2f70aeb1c4be7212"}, + {file = "pydantic-1.10.21-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5e8148c2ce4894ce7e5a4925d9d3fdce429fb0e821b5a8783573f3611933a251"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4973232c98b9b44c78b1233693e5e1938add5af18042f031737e1214455f9b8"}, + {file = "pydantic-1.10.21-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:662bf5ce3c9b1cef32a32a2f4debe00d2f4839fefbebe1d6956e681122a9c839"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:98737c3ab5a2f8a85f2326eebcd214510f898881a290a7939a45ec294743c875"}, + {file = "pydantic-1.10.21-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0bb58bbe65a43483d49f66b6c8474424d551a3fbe8a7796c42da314bac712738"}, + {file = "pydantic-1.10.21-cp313-cp313-win_amd64.whl", hash = "sha256:e622314542fb48542c09c7bd1ac51d71c5632dd3c92dc82ede6da233f55f4848"}, + {file = "pydantic-1.10.21-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d356aa5b18ef5a24d8081f5c5beb67c0a2a6ff2a953ee38d65a2aa96526b274f"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08caa8c0468172d27c669abfe9e7d96a8b1655ec0833753e117061febaaadef5"}, + {file = "pydantic-1.10.21-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c677aa39ec737fec932feb68e4a2abe142682f2885558402602cd9746a1c92e8"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:79577cc045d3442c4e845df53df9f9202546e2ba54954c057d253fc17cd16cb1"}, + {file = "pydantic-1.10.21-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:b6b73ab347284719f818acb14f7cd80696c6fdf1bd34feee1955d7a72d2e64ce"}, + {file = "pydantic-1.10.21-cp37-cp37m-win_amd64.whl", hash = "sha256:46cffa24891b06269e12f7e1ec50b73f0c9ab4ce71c2caa4ccf1fb36845e1ff7"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:298d6f765e3c9825dfa78f24c1efd29af91c3ab1b763e1fd26ae4d9e1749e5c8"}, + {file = "pydantic-1.10.21-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f2f4a2305f15eff68f874766d982114ac89468f1c2c0b97640e719cf1a078374"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35b263b60c519354afb3a60107d20470dd5250b3ce54c08753f6975c406d949b"}, + {file = "pydantic-1.10.21-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e23a97a6c2f2db88995496db9387cd1727acdacc85835ba8619dce826c0b11a6"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:3c96fed246ccc1acb2df032ff642459e4ae18b315ecbab4d95c95cfa292e8517"}, + {file = "pydantic-1.10.21-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:b92893ebefc0151474f682e7debb6ab38552ce56a90e39a8834734c81f37c8a9"}, + {file = "pydantic-1.10.21-cp38-cp38-win_amd64.whl", hash = "sha256:b8460bc256bf0de821839aea6794bb38a4c0fbd48f949ea51093f6edce0be459"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d387940f0f1a0adb3c44481aa379122d06df8486cc8f652a7b3b0caf08435f7"}, + {file = "pydantic-1.10.21-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:266ecfc384861d7b0b9c214788ddff75a2ea123aa756bcca6b2a1175edeca0fe"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61da798c05a06a362a2f8c5e3ff0341743e2818d0f530eaac0d6898f1b187f1f"}, + {file = "pydantic-1.10.21-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a621742da75ce272d64ea57bd7651ee2a115fa67c0f11d66d9dcfc18c2f1b106"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9e3e4000cd54ef455694b8be9111ea20f66a686fc155feda1ecacf2322b115da"}, + {file = "pydantic-1.10.21-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f198c8206640f4c0ef5a76b779241efb1380a300d88b1bce9bfe95a6362e674d"}, + {file = "pydantic-1.10.21-cp39-cp39-win_amd64.whl", hash = "sha256:e7f0cda108b36a30c8fc882e4fc5b7eec8ef584aa43aa43694c6a7b274fb2b56"}, + {file = "pydantic-1.10.21-py3-none-any.whl", hash = "sha256:db70c920cba9d05c69ad4a9e7f8e9e83011abb2c6490e561de9ae24aee44925c"}, + {file = "pydantic-1.10.21.tar.gz", hash = "sha256:64b48e2b609a6c22178a56c408ee1215a7206077ecb8a193e2fda31858b2362a"}, +] + +[package.dependencies] +typing-extensions = ">=4.2.0" + +[package.extras] +dotenv = ["python-dotenv (>=0.10.4)"] +email = ["email-validator (>=1.0.3)"] + +[[package]] +name = "pygls" +version = "1.3.1" +description = "A pythonic generic language server (pronounced like 'pie glass')" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "pygls-1.3.1-py3-none-any.whl", hash = "sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e"}, + {file = "pygls-1.3.1.tar.gz", hash = "sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018"}, +] + +[package.dependencies] +cattrs = ">=23.1.2" +lsprotocol = "2023.0.1" + +[package.extras] +ws = ["websockets (>=11.0.3)"] + [[package]] name = "python-dotenv" version = "1.0.1" description = "Read key-value pairs from a .env file and set them as environment variables" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, @@ -48,21 +429,45 @@ cli = ["click (>=5.0)"] [[package]] name = "pytz" -version = "2024.2" +version = "2025.1" description = "World timezone definitions, modern and historical" optional = false python-versions = "*" +groups = ["main"] files = [ - {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, - {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, + {file = "pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57"}, + {file = "pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e"}, ] +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + [[package]] name = "tree-sitter" version = "0.23.2" description = "Python bindings to the Tree-sitter parsing library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree-sitter-0.23.2.tar.gz", hash = "sha256:66bae8dd47f1fed7bdef816115146d3a41c39b5c482d7bad36d9ba1def088450"}, {file = "tree_sitter-0.23.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3a937f5d8727bc1c74c4bf2a9d1c25ace049e8628273016ad0d45914ae904e10"}, @@ -117,6 +522,7 @@ version = "0.23.1" description = "C# grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2b612a6e5bd17bb7fa2aab4bb6fc1fba45c94f09cb034ab332e45603b86e32fd"}, {file = "tree_sitter_c_sharp-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a8b98f62bc53efcd4d971151950c9b9cd5cbe3bacdb0cd69fdccac63350d83e"}, @@ -137,6 +543,7 @@ version = "0.23.1" description = "JavaScript grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6ca583dad4bd79d3053c310b9f7208cd597fd85f9947e4ab2294658bb5c11e35"}, {file = "tree_sitter_javascript-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:94100e491a6a247aa4d14caf61230c171b6376c863039b6d9cd71255c2d815ec"}, @@ -157,6 +564,7 @@ version = "0.23.6" description = "Python grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:28fbec8f74eeb2b30292d97715e60fac9ccf8a8091ce19b9d93e9b580ed280fb"}, {file = "tree_sitter_python-0.23.6-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:680b710051b144fedf61c95197db0094f2245e82551bf7f0c501356333571f7a"}, @@ -177,6 +585,7 @@ version = "0.23.1" description = "Ruby grammar for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:39f391322d2210843f07081182dbf00f8f69cfbfa4687b9575cac6d324bae443"}, {file = "tree_sitter_ruby-0.23.1-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:aa4ee7433bd42fac22e2dad4a3c0f332292ecf482e610316828c711a0bb7f794"}, @@ -197,6 +606,7 @@ version = "0.23.2" description = "TypeScript and TSX grammars for tree-sitter" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:3cd752d70d8e5371fdac6a9a4df9d8924b63b6998d268586f7d374c9fba2a478"}, {file = "tree_sitter_typescript-0.23.2-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:c7cc1b0ff5d91bac863b0e38b1578d5505e718156c9db577c8baea2557f66de8"}, @@ -211,12 +621,43 @@ files = [ [package.extras] core = ["tree-sitter (>=0.23,<1.0)"] +[[package]] +name = "typing-extensions" +version = "4.12.2" +description = "Backported and Experimental Type Hints for Python 3.8+" +optional = false +python-versions = ">=3.8" +groups = ["main"] +files = [ + {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, + {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, + {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + [[package]] name = "websockets" version = "13.1" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"}, {file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"}, @@ -307,6 +748,6 @@ files = [ ] [metadata] -lock-version = "2.0" -python-versions = ">=3.10,<=3.13" -content-hash = "39f2f6fcb74f8c3af214f2298baae627244be2c5b393953f63725fc9ce0b3cef" +lock-version = "2.1" +python-versions = ">=3.10,<=3.14" +content-hash = "f3755f83e1487be50653affb8c47df88edda0236e448710e3584767225ffaf77" diff --git a/pyproject.toml b/pyproject.toml index 25890f22..b60c188e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ license = "MIT" packages = [{include = "blarify"}] [tool.poetry.dependencies] -python = ">=3.10,<=3.13" +python = ">=3.10,<=3.14" asyncio = "^3.4.3" websockets = "^13.1" neo4j = "^5.25.0" @@ -21,6 +21,7 @@ tree-sitter-ruby = "^0.23.0" tree-sitter-javascript = "^0.23.0" tree-sitter-typescript = "^0.23.2" tree-sitter-c-sharp = "^0.23.1" +multilspy = "^0.0.12" [build-system] From 29b241656c786f69cdeaa7605586be3efdb90303 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:44:38 -0300 Subject: [PATCH 03/18] refactor: remove LspCaller class and associated code --- blarify/code_references/lsp_caller.py | 251 -------------------------- 1 file changed, 251 deletions(-) delete mode 100644 blarify/code_references/lsp_caller.py diff --git a/blarify/code_references/lsp_caller.py b/blarify/code_references/lsp_caller.py deleted file mode 100644 index 1e7cf0ed..00000000 --- a/blarify/code_references/lsp_caller.py +++ /dev/null @@ -1,251 +0,0 @@ -import time -from websockets import ConnectionClosedError -import websockets.sync.client as ws -import json -from blarify.logger import Logger -import logging - -logger = logging.getLogger(__name__) - -LANGUAGES_ID_MAP = { - ".tsx": "typescriptreact", - ".ts": "typescript", - ".jsx": "javascriptreact", - ".js": "javascript", -} - - -class LspCaller: - root_uri: str - host: str - port: int - websocket: ws.ClientConnection - unmatched_responses: dict - lsp_server_name: str - connection_retries: int - - def __init__( - self, root_uri: str, host="localhost", port=5000, log=False, lsp_server_name="", connection_retries: int = 3 - ): - self.host = host or "localhost" - self.port = port or 5000 - self.root_uri = root_uri - self.websocket = None - self.unmatched_responses = {} - self.lsp_server_name = lsp_server_name - self.connection_retries = connection_retries - - self._id = 0 - - @property - def id(self) -> int: - self._id += 1 - return self._id - - def connect(self, retries: int = 0) -> None: - uri = f"ws://{self.host}:{self.port}" - uri += self._get_query_params() - try: - self.websocket = ws.connect(uri) - except ConnectionRefusedError: - logger.info(f"Connection refused to {uri}") - logger.info(f"Retrying {retries}/{self.connection_retries}") - if retries < self.connection_retries: - time.sleep(1) - self.connect(retries + 1) - - def _get_query_params(self) -> str: - if self.lsp_server_name: - return f"?name={self.lsp_server_name}" - return "" - - def initialize(self) -> None: - initialize_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "initialize", - "params": { - "processId": None, - "rootUri": self.root_uri, - "capabilities": {}, - "initializationOptions": { - "completion": {"disableSnippets": False, "resolveEagerly": False, "ignorePatterns": []}, - "diagnostics": {"enable": False, "didOpen": True, "didChange": False, "didSave": False}, - "hover": { - "enable": False, - }, - }, - }, - } - self.send_request(initialize_request) - - def send_request(self, request: dict) -> dict: - request_id = request["id"] - - # Send the request - self.websocket.send(json.dumps(request)) - - return self.get_response(request_id, request) - - def send_notification(self, notification: dict) -> None: - self.websocket.send(json.dumps(notification)) - - def get_response(self, request_id: str, request: dict) -> dict: - # Check the response cache first - if request_id in self.unmatched_responses: - return self.unmatched_responses.pop(request_id) - - retries = 0 - while retries <= self.connection_retries: - try: - response = self.websocket.recv() - response = json.loads(response) - - if response.get("method") == "window/logMessage": - self.log(response) - - response_id = response.get("id") - - if response_id == request_id: - return response - else: - self.unmatched_responses[response_id] = response - except ConnectionClosedError: - retries += 1 - logger.info(f"Connection lost. Retrying ({retries}/{self.connection_retries})...") - self.connect() - self.websocket.send(json.dumps(request)) - if retries > self.connection_retries: - raise ConnectionClosedError("Failed to reconnect after maximum retries.") - - def get_document_symbols(self, document_uri: str) -> dict: - document_symbol_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/documentSymbol", - "params": {"textDocument": {"uri": document_uri}}, - } - return self.send_request(document_symbol_request).get("result") - - def get_definition(self, document_uri: str, position: dict) -> dict: - definition_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/definition", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - - result = self.send_request(definition_request).get("result") - if result: - return result[0] - return None - - def get_declaration(self, document_uri: str, position: dict) -> dict: - declaration_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/declaration", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - result = self.send_request(declaration_request).get("result") - if result: - return result[0] - return None - - def get_references(self, document_uri: str, position: dict) -> dict: - reference_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/references", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - "context": {"includeDeclaration": False}, - "workDoneToken": 1, - }, - } - return self.send_request(reference_request).get("result") - - def get_selection_range(self, document_uri: str, position: dict) -> dict: - selection_range_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/selectionRange", - "params": { - "textDocument": {"uri": document_uri}, - "position": position, - }, - } - return self.send_request(selection_range_request).get("result") - - def get_document_link(self, document_uri: str) -> dict: - document_link_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "textDocument/documentLink", - "params": {"textDocument": {"uri": document_uri}}, - } - return self.send_request(document_link_request).get("result") - - def did_open(self, document_uri: str, text: str, extension: str) -> None: - language_id = LANGUAGES_ID_MAP.get(extension, "javascript") - did_open_notification = { - "jsonrpc": "2.0", - "id": 400, - "method": "textDocument/didOpen", - "params": { - "textDocument": { - "uri": document_uri, - "languageId": language_id, - "version": 1, - "text": text, - }, - }, - } - self.send_notification(did_open_notification) - - def shutdown_exit_close(self) -> None: - try: - self.shutdown() - self.exit() - self.close() - except ConnectionClosedError: - pass - except Exception as e: - logger.info(f"Error closing connection {e}") - - def shutdown(self) -> None: - shutdown_request = { - "jsonrpc": "2.0", - "id": self.id, - "method": "shutdown", - "params": None, - } - self.send_request(shutdown_request) - - def exit(self) -> None: - exit_request = {"jsonrpc": "2.0", "method": "exit"} - self.websocket.send(json.dumps(exit_request)) - - def close(self) -> None: - self.websocket.close() - - def map_reference(self, reference: dict) -> dict: - return { - "uri": reference["uri"], - "range": reference["range"], - } - - def log(self, message: dict) -> None: - # if self.log: - self.pretty_print(message) - - def pretty_print(self, message: dict) -> None: - ## print formatted json - Logger.log(json.dumps(message, indent=2)) From f7fc44d2aae16a09c0423cd333703936c0a9ecf7 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 15:46:04 -0300 Subject: [PATCH 04/18] refactor: rename methods in LspQueryHelper class for consistency and clarity --- blarify/code_references/lsp_helper.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 88f47427..58a1c5a0 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -62,7 +62,7 @@ def start(self) -> None: DEPRECATED, LSP servers are started on demand """ - def get_or_create_multi_lsp(self, extension): + def _get_or_create_multi_lsp(self, extension): language_definitions = self._get_language_definition_for_extension(extension) language = language_definitions.get_language_name() @@ -85,15 +85,15 @@ def initialize_directory(self, file) -> None: """ def get_paths_where_node_is_referenced(self, node: DefinitionNode) -> list[Reference]: - server = self.get_or_create_multi_lsp(node.extension) - references = self.request_references_with_fallback(node, server) + server = self._get_or_create_multi_lsp(node.extension) + references = self._request_references_with_fallback(node, server) if not references: return [] return [Reference(reference) for reference in references] - def request_references_with_fallback(self, node, lsp): + def _request_references_with_fallback(self, node, lsp): try: references = lsp.request_references( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), @@ -101,7 +101,7 @@ def request_references_with_fallback(self, node, lsp): column=node.definition_range.start_dict["character"], ) except TimeoutError: - self.restart_lsp_for_extension(node) + self._restart_lsp_for_extension(node) lsp = self.multi_lsp_callers[node.extension] references = lsp.request_references( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), @@ -111,7 +111,7 @@ def request_references_with_fallback(self, node, lsp): return references - def restart_lsp_for_extension(self, node): + def _restart_lsp_for_extension(self, node): language_definitions = self._get_language_definition_for_extension(node.extension) new_lsp = self._create_lsp_server(language_definitions) @@ -126,7 +126,7 @@ def restart_lsp_for_extension(self, node): self.entered_lsp_servers[node.extension] = context def get_definition_path_for_reference(self, reference: Reference) -> str: - lsp_caller = self.get_or_create_multi_lsp(".py") + lsp_caller = self._get_or_create_multi_lsp(".py") definition = lsp_caller.request_definition( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=reference.uri), line=reference.range.start.line, From 8523e45ff60d4e8434ca550027031e2af7b8f1db Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 16:14:34 -0300 Subject: [PATCH 05/18] feat: add logging for node processing in ProjectGraphCreator --- blarify/project_graph_creator.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index 046922f0..3cdea0b5 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -173,6 +173,8 @@ def create_relationship_from_references(self, file_nodes: List["Node"]) -> None: if node.label == NodeLabels.FILE: continue + print(f"Processing node {node.name}") + tree_sitter_helper = self._get_tree_sitter_for_file_extension(node.extension) references_relationships.extend( self.create_node_relationships(node=node, tree_sitter_helper=tree_sitter_helper) From abae83cb995243803c3505eb9f51e060f6e064ec Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 16:48:06 -0300 Subject: [PATCH 06/18] chore: update dependencies to use git source for multilspy and clean up poetry.lock --- poetry.lock | 14 +++++++++----- pyproject.toml | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/poetry.lock b/poetry.lock index 563b56e0..0dc1aa6a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -280,16 +280,20 @@ description = "A language-agnostic LSP client in Python, with a library interfac optional = false python-versions = ">=3.7" groups = ["main"] -files = [ - {file = "multilspy-0.0.12-py3-none-any.whl", hash = "sha256:e06f7ef7c388793eaaa2d4a6bb91dd9ef3efc78090b34ca1d2343b52062c57fc"}, - {file = "multilspy-0.0.12.tar.gz", hash = "sha256:d6166302d15cda6d1c6820e9d0aac0a30382cb76dfc5fa2d1cee9f873289eb9c"}, -] +files = [] +develop = false [package.dependencies] jedi-language-server = "0.41.1" pydantic = ">=1.10.5,<2" requests = "2.32.3" +[package.source] +type = "git" +url = "https://github.com/blarApp/multilspy.git" +reference = "HEAD" +resolved_reference = "c1f05ad2f083e6d8130ff4e19e0b4fbcacaef28a" + [[package]] name = "neo4j" version = "5.28.1" @@ -750,4 +754,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<=3.14" -content-hash = "f3755f83e1487be50653affb8c47df88edda0236e448710e3584767225ffaf77" +content-hash = "ffea608a9e38e11827e1c64114a8422f07f0052f8fe316f508f9596d552e07bf" diff --git a/pyproject.toml b/pyproject.toml index b60c188e..fd72b63a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,7 +21,7 @@ tree-sitter-ruby = "^0.23.0" tree-sitter-javascript = "^0.23.0" tree-sitter-typescript = "^0.23.2" tree-sitter-c-sharp = "^0.23.1" -multilspy = "^0.0.12" +multilspy = {git = "https://github.com/blarApp/multilspy.git"} [build-system] From 1674e37fffc9d1443d2294eaa575b851a68308ba Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:03:28 -0300 Subject: [PATCH 07/18] fix: handle multiple definitions in get_definition_path_for_reference method --- blarify/code_references/lsp_helper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 58a1c5a0..7ca6e4e5 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -127,12 +127,16 @@ def _restart_lsp_for_extension(self, node): def get_definition_path_for_reference(self, reference: Reference) -> str: lsp_caller = self._get_or_create_multi_lsp(".py") - definition = lsp_caller.request_definition( + definitions = lsp_caller.request_definition( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=reference.uri), line=reference.range.start.line, column=reference.range.start.character, ) - return definition["uri"] if definition else "" + + if not definitions: + return "" + + return definitions[0].uri def shutdown_exit_close(self) -> None: for lsp in self.entered_lsp_servers.values(): From d9ac77087f25be45dcad8ac3303e942778a6a2c9 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Tue, 18 Feb 2025 17:12:06 -0300 Subject: [PATCH 08/18] fix: update return statement to access uri key in definitions --- blarify/code_references/lsp_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 7ca6e4e5..1a51e65a 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -136,7 +136,7 @@ def get_definition_path_for_reference(self, reference: Reference) -> str: if not definitions: return "" - return definitions[0].uri + return definitions[0]["uri"] def shutdown_exit_close(self) -> None: for lsp in self.entered_lsp_servers.values(): From c98b6787e664c858cb9c9647a306802603ca0d13 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:35:39 -0300 Subject: [PATCH 09/18] refactor: remove unused methods and streamline Neo4jManager class --- blarify/db_managers/neo4j_manager.py | 244 --------------------------- 1 file changed, 244 deletions(-) diff --git a/blarify/db_managers/neo4j_manager.py b/blarify/db_managers/neo4j_manager.py index 405b9058..c973444d 100644 --- a/blarify/db_managers/neo4j_manager.py +++ b/blarify/db_managers/neo4j_manager.py @@ -32,58 +32,13 @@ def __init__(self, repoId: str = None, entityId: str = None, max_connections: in else: raise e - if create_index: - self.create_indexes_and_constraints() self.repoId = repoId if repoId is not None else "default_repo" self.entityId = entityId if entityId is not None else "default_user" - def create_indexes_and_constraints(self): - self.create_function_name_index() - self.create_node_id_index() - self.create_entityId_index() - self.create_unique_constraint() - - def query(self, query: str, query_params: dict = {}, result_format: str = "data"): - with self.driver.session() as session: - result = session.run(query, query_params) - if result_format == "graph": - return result.graph() - return result.data() - def save_graph(self, nodes: List[Any], edges: List[Any]): self.create_nodes(nodes) self.create_edges(edges) - def create_function_name_index(self): - # Creates a fulltext index on the name and path properties of the nodes - with self.driver.session() as session: - node_query = """ - CREATE FULLTEXT INDEX functionNames IF NOT EXISTS FOR (n:CLASS|FUNCTION|FILE) ON EACH [n.name, n.path, n.node_id] - """ - session.run(node_query) - - def create_node_id_index(self): - with self.driver.session() as session: - node_query = """ - CREATE INDEX node_id_NODE IF NOT EXISTS FOR (n:NODE) ON (n.node_id) - """ - session.run(node_query) - - def create_entityId_index(self): - with self.driver.session() as session: - user_query = """ - CREATE INDEX entityId_INDEX IF NOT EXISTS FOR (n:NODE) ON (n.entityId) - """ - session.run(user_query) - - def create_unique_constraint(self): - with self.driver.session() as session: - constraint_query = """ - CREATE CONSTRAINT user_node_unique IF NOT EXISTS FOR (n:NODE) - REQUIRE (n.entityId, n.node_id) IS UNIQUE - """ - session.run(constraint_query) - def close(self): # Close the connection to the database self.driver.close() @@ -98,205 +53,6 @@ def create_edges(self, edgesList: List[Any]): with self.driver.session() as session: session.write_transaction(self._create_edges_txn, edgesList, 100, entityId=self.entityId) - def format_query(self, query: str): - # Function to format the query to be used in the fulltext index - special_characters = [ - "+", - "-", - "&&", - "||", - "!", - "(", - ")", - "{", - "}", - "[", - "]", - "^", - '"', - "~", - "*", - "?", - ":", - "\\", - "/", - ] - for character in special_characters: - query = query.replace(character, f"\\{character}") - return query - - def get_node_by_id(self, node_id: str): - query = """ - MATCH (n) - WHERE n.node_id = $node_id - return n - """ - with self.driver.session() as session: - result = session.run(query, {"node_id": node_id}) - node = result.data()[0]["n"] - neighbours = self.get_1_hop_neighbours_and_relations(node["node_id"]) - node_result = { - "node_id": node.get("node_id"), - "node_name": node.get("name"), - "file_path": node.get("file_path"), - "start_line": node.get("start_line"), - "end_line": node.get("end_line"), - "text": node.get("text"), - } - return node_result, neighbours - - def get_whole_graph(self, result_format: str = "data"): - query = "match (n {repoId: $repoId})-[r]-(m) return n, m, r" - with self.driver.session() as session: - result = session.run(query, repoId=self.repoId) - if result_format == "graph": - return result.graph() - return result.data() - - def get_all_user_nodes(self, result_format: str = "data"): - query = "match (n {entityId: $entityId})-[r]-(m) return n, m, r" - with self.driver.session() as session: - result = session.run(query, entityId=self.entityId) - if result_format == "graph": - return result.graph() - return result.data() - - def search_code(self, query: str): - # Function to get code from the Neo4j database based on a keyword query - formatted_query = self.format_query(query) - node_query = """ - CALL db.index.fulltext.queryNodes("functionNames", $formatted_query) YIELD node, score - where node.repoId = $repoId - RETURN node.node_id, node.name, node.file_path, score - """ - with self.driver.session() as session: - result = session.run(node_query, formatted_query=f"*{formatted_query}", repoId=self.repoId) - data_result = result.data() - if data_result is None: - result = session.run(node_query, formatted_query=formatted_query, repoId=self.repoId) - data_result = result.data() - data_result = [ - { - "node_id": record["node.node_id"], - "node_name": record["node.name"], - "file_path": record["node.file_path"], - "score": record["score"], - } - for record in data_result - ] - return data_result - - def get_code(self, query: str): - # Function to get code from the Neo4j database based on a keyword query - formatted_query = self.format_query(query) - node_query = """ - CALL db.index.fulltext.queryNodes("functionNames", $formatted_query) YIELD node, score - where node.repoId = $repoId - RETURN node.text, node.node_id, node.name, node.file_path, node.start_line, node.end_line, score - """ - with self.driver.session() as session: - result = session.run(node_query, formatted_query=f"*{formatted_query}", repoId=self.repoId) - first_result = result.peek() - if first_result is None: - result = session.run(node_query, formatted_query=formatted_query, repoId=self.repoId) - first_result = result.peek() - if first_result is None: - return None, None - neighbours = self.get_1_hop_neighbours_and_relations(first_result["node.node_id"]) - return first_result, neighbours - - def get_1_hop_neighbours_and_relations(self, node_id: str): - with self.driver.session() as session: - result = session.run( - """ - MATCH (p {node_id: $node_id})-[r]->(p2) - RETURN - type(r) as relationship_type, - p2.node_id AS node_id, - p2.name AS node_name, - labels(p2) AS node_type - """, - node_id=node_id, - ) - data = result.data() - nodes_info = [ - { - "node_id": record["node_id"], - "node_name": record["node_name"], - "node_type": record["node_type"], - "relationship_type": record["relationship_type"], - } - for record in data - ] - return nodes_info - - def get_n_hop_neighbours(self, node_id: str, num_hops: int): - # Function to get code from the Neo4j database based on a keyword query - with self.driver.session() as session: - result = session.run( - """ - MATCH (p {node_id: $node_id}) - CALL apoc.neighbors.byhop(p, ">", $num_hops) - YIELD nodes - UNWIND nodes AS all_nodes - RETURN all_nodes.node_id AS node_id, all_nodes.name AS function_name, labels(all_nodes) AS labels - """, - node_id=node_id, - num_hops=num_hops, - ) - data = result.data() - # Construct list of objects containing node_id and function_name - nodes_info = [ - { - "node_id": record["node_id"], - "function_name": record["function_name"], - "labels": record["labels"], - } - for record in data - ] - return nodes_info - - def get_incoming_neighbours( - self, node_id: str | None = None, path: str | None = None, relationship_types: list | None = None - ): - with self.driver.session() as session: - if path: - query = f""" - MATCH p1 {{ path:{path} }}<-[r]-(p2) - """ - elif node_id: - query = """ - MATCH (p {node_id: $node_id})<-[r]-(p2) - """ - - if relationship_types: - if len(relationship_types) == 1: - query += f"WHERE type(r) = '{relationship_types[0]}'" - else: - relationship_types_str = ", ".join([f"'{r}'" for r in relationship_types]) - query += f"WHERE type(r) IN [{relationship_types_str}]" - - query += """ - RETURN - type(r) as relationship_type, - p2.node_id AS node_id, - p2.name AS node_name, - labels(p2) AS node_type - """ - - result = session.run(query, node_id=node_id) - data = result.data() - nodes_info = [ - { - "node_id": record["node_id"], - "node_name": record["node_name"], - "node_type": record["node_type"], - "relationship_type": record["relationship_type"], - } - for record in data - ] - return nodes_info - @staticmethod def _create_nodes_txn(tx, nodeList: List[Any], batch_size: int, repoId: str, entityId: str): node_creation_query = """ From 16e621d0c853f64aebf239e79c4c5116e1fc730b Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 12:54:09 -0300 Subject: [PATCH 10/18] feat: enhance Neo4jManager initialization and add GraphBuilder for project graph construction --- blarify/db_managers/neo4j_manager.py | 26 +++++--- blarify/examples/graph_builder.py | 29 +++++++++ blarify/main.py | 8 ++- blarify/prebuilt/__init__.py | 3 + blarify/prebuilt/graph_builder.py | 57 +++++++++++++++++ blarify/project_graph_creator.py | 7 +-- docs/quickstart.md | 92 +++++++++++++++------------- 7 files changed, 162 insertions(+), 60 deletions(-) create mode 100644 blarify/examples/graph_builder.py create mode 100644 blarify/prebuilt/__init__.py create mode 100644 blarify/prebuilt/graph_builder.py diff --git a/blarify/db_managers/neo4j_manager.py b/blarify/db_managers/neo4j_manager.py index c973444d..392a8367 100644 --- a/blarify/db_managers/neo4j_manager.py +++ b/blarify/db_managers/neo4j_manager.py @@ -16,10 +16,18 @@ class Neo4jManager: repoId: str driver: Driver - def __init__(self, repoId: str = None, entityId: str = None, max_connections: int = 50, create_index=False): - uri = os.getenv("NEO4J_URI") - user = os.getenv("NEO4J_USERNAME") - password = os.getenv("NEO4J_PASSWORD") + def __init__( + self, + repo_id: str = None, + entityId: str = None, + max_connections: int = 50, + uri: str = None, + user: str = None, + password: str = None, + ): + uri = uri or os.getenv("NEO4J_URI") + user = user or os.getenv("NEO4J_USERNAME") + password = password or os.getenv("NEO4J_PASSWORD") retries = 3 for attempt in range(retries): @@ -32,17 +40,17 @@ def __init__(self, repoId: str = None, entityId: str = None, max_connections: in else: raise e - self.repoId = repoId if repoId is not None else "default_repo" + self.repoId = repo_id if repo_id is not None else "default_repo" self.entityId = entityId if entityId is not None else "default_user" - def save_graph(self, nodes: List[Any], edges: List[Any]): - self.create_nodes(nodes) - self.create_edges(edges) - def close(self): # Close the connection to the database self.driver.close() + def save_graph(self, nodes: List[Any], edges: List[Any]): + self.create_nodes(nodes) + self.create_edges(edges) + def create_nodes(self, nodeList: List[Any]): # Function to create nodes in the Neo4j database with self.driver.session() as session: diff --git a/blarify/examples/graph_builder.py b/blarify/examples/graph_builder.py new file mode 100644 index 00000000..a37fea71 --- /dev/null +++ b/blarify/examples/graph_builder.py @@ -0,0 +1,29 @@ +from blarify.prebuilt.graph_builder import GraphBuilder +from blarify.db_managers.neo4j_manager import Neo4jManager + +import dotenv +import os + + +def build(root_path: str = None): + graph_builder = GraphBuilder(root_path=root_path, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) + graph = graph_builder.build() + + relationships = graph.get_relationships_as_objects() + nodes = graph.get_nodes_as_objects() + + save_to_neo4j(relationships, nodes) + + +def save_to_neo4j(relationships, nodes): + graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + + print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + graph_manager.save_graph(nodes, relationships) + graph_manager.close() + + +if __name__ == "__main__": + dotenv.load_dotenv() + root_path = os.getenv("ROOT_PATH") + build(root_path=root_path) diff --git a/blarify/main.py b/blarify/main.py index cf3b2c7e..01b5d90d 100644 --- a/blarify/main.py +++ b/blarify/main.py @@ -11,6 +11,10 @@ import dotenv import os +URI = os.getenv("NEO4J_URI") +USER = os.getenv("NEO4J_USERNAME") +PASSWORD = os.getenv("NEO4J_PASSWORD") + def main(root_path: str = None, blarignore_path: str = None): lsp_query_helper = LspQueryHelper(root_uri=root_path) @@ -29,9 +33,7 @@ def main(root_path: str = None, blarignore_path: str = None): entity_id = "test" graph_manager = Neo4jManager(repoId, entity_id) - graph_creator = ProjectGraphCreator( - root_path, lsp_query_helper, project_files_iterator, GraphEnvironment("dev", "MAIN", root_path) - ) + graph_creator = ProjectGraphCreator(root_path, lsp_query_helper, project_files_iterator) graph = graph_creator.build() diff --git a/blarify/prebuilt/__init__.py b/blarify/prebuilt/__init__.py new file mode 100644 index 00000000..13644e62 --- /dev/null +++ b/blarify/prebuilt/__init__.py @@ -0,0 +1,3 @@ +from blarify.prebuilt.graph_builder import GraphBuilder + +__all__ = ["GraphBuilder"] diff --git a/blarify/prebuilt/graph_builder.py b/blarify/prebuilt/graph_builder.py new file mode 100644 index 00000000..f9f8cd95 --- /dev/null +++ b/blarify/prebuilt/graph_builder.py @@ -0,0 +1,57 @@ +from blarify.code_references.lsp_helper import LspQueryHelper +from blarify.graph.graph import Graph +from blarify.project_file_explorer.project_files_iterator import ProjectFilesIterator +from blarify.project_graph_creator import ProjectGraphCreator + + +class GraphBuilder: + def __init__( + self, + root_path: str, + extensions_to_skip: list[str] = None, + names_to_skip: list[str] = None, + ): + """ + A class responsible for constructing a graph representation of a project's codebase. + + Args: + root_path: Root directory path of the project to analyze + extensions_to_skip: File extensions to exclude from analysis (e.g., ['.md', '.txt']) + names_to_skip: Filenames/directory names to exclude from analysis (e.g., ['venv', 'tests']) + + Example: + builder = GraphBuilder( + "/path/to/project", + extensions_to_skip=[".json"], + names_to_skip=["__pycache__"] + ) + project_graph = builder.build() + + """ + + self.root_path = root_path + self.extensions_to_skip = extensions_to_skip or [] + self.names_to_skip = names_to_skip or [] + + self.repo_id = "REPO" + self.entity_id = "BLARIFY" + + def build(self) -> Graph: + lsp_query_helper = self._get_started_lsp_query_helper() + project_files_iterator = self._get_project_files_iterator() + + graph_creator = ProjectGraphCreator(self.root_path, lsp_query_helper, project_files_iterator) + graph = graph_creator.build() + lsp_query_helper.shutdown_exit_close() + + return graph + + def _get_project_files_iterator(self): + return ProjectFilesIterator( + root_path=self.root_path, extensions_to_skip=self.extensions_to_skip, names_to_skip=self.names_to_skip + ) + + def _get_started_lsp_query_helper(self): + lsp_query_helper = LspQueryHelper(root_uri=self.root_path) + lsp_query_helper.start() + return lsp_query_helper diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index 3cdea0b5..74b2e3e4 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -15,13 +15,14 @@ ) from typing import List, TYPE_CHECKING from blarify.logger import Logger +from blarify.graph.graph_environment import GraphEnvironment + if TYPE_CHECKING: from blarify.graph.node import FolderNode from blarify.project_file_explorer import File, Folder from blarify.graph.node import Node, FileNode from blarify.graph.relationship import Relationship - from blarify.graph.graph_environment import GraphEnvironment class ProjectGraphCreator: @@ -49,7 +50,7 @@ def __init__( self.root_path = root_path self.lsp_query_helper = lsp_query_helper self.project_files_iterator = project_files_iterator - self.graph_environment = graph_environment + self.graph_environment = graph_environment or GraphEnvironment("blarify", "repo", self.root_path) self.graph = Graph() @@ -173,8 +174,6 @@ def create_relationship_from_references(self, file_nodes: List["Node"]) -> None: if node.label == NodeLabels.FILE: continue - print(f"Processing node {node.name}") - tree_sitter_helper = self._get_tree_sitter_for_file_extension(node.extension) references_relationships.extend( self.create_node_relationships(node=node, tree_sitter_helper=tree_sitter_helper) diff --git a/docs/quickstart.md b/docs/quickstart.md index 188087aa..751fd241 100644 --- a/docs/quickstart.md +++ b/docs/quickstart.md @@ -4,21 +4,14 @@ Welcome to Blarify! This guide will help you get started using Blarify to visual ## Prerequisites -- Python (>=3.10,<=3.12.8) -- [lsp-ws-proxy](https://github.com/qualified/lsp-ws-proxy) +- Python (>=3.10,<=3.14) - A graph database instance (we recommend using [FalkorDB](https://falkordb.com/) or [AuraDB](https://neo4j.com/product/auradb/)) -and one or more of the following: - -- solargraph (for Ruby support) -- jedi-language-server (for Python support) -- typescript-language-server (for TypeScript/JavaScript support) - ## Installation First set up your virtual environment: ```bash -python3.11 -m venv .venv +python -m venv .venv source .venv/bin/activate ``` @@ -28,47 +21,30 @@ git clone git@github.com:blarApp/blarify.git pip install blarify/ ``` -Start the lsp-ws-proxy with the language servers you want to use on port 5000: -``` -./lsp-ws-proxy/target/debug/lsp-ws-proxy --listen 5000 -- solargraph stdio -- jedi-language-server -- typescript-language-server --stdio -``` - ## Usage ```python PATH_TO_YOUR_PROJECT = "/path/to/your/project/" ``` -Start the lsp query helper, this will be used to query the language server for information about your codebase +Import GraphBuilder from the prebuilt module ```python -lsp_query_helper = LspQueryHelper(root_uri=PATH_TO_YOUR_PROJECT) -lsp_query_helper.start() +from blarify.prebuilt.graph_builder import GraphBuilder ``` +Create the graph builder +You can skip files or directories by passing them in the extensions_to_skip or names_to_skip parameters -ProjectFilesIterator will iterate over all the files in your project, you can skip files or directories by passing them in the names_to_skip parameter ```python -project_files_iterator = ProjectFilesIterator( - root_path=PATH_TO_YOUR_PROJECT, - names_to_skip=[".git", ".idea", ".vscode", "__pycache__", ".pytest_cache"], -) +graph_builder = GraphBuilder(root_path=PATH_TO_YOUR_PROJECT, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) ``` -If you are using Ruby, remove the Gemfile from the project to avoid problems with the language server +Build the graph ```python -FileRemover.soft_delete_if_exists(PATH_TO_YOUR_PROJECT, "Gemfile") -``` - -Create the graph creator and build -```python -graph_creator = ProjectGraphCreator( - PATH_TO_YOUR_PROJECT, lsp_query_helper, project_files_iterator -) - -graph = graph_creator.build() +graph = graph_builder.build() ``` This will return a graph object that contains all the nodes and relationships in your codebase @@ -115,25 +91,53 @@ this will return a list of dictionaries with the following structure } ``` -Close the lsp query helper and graph manager - +Example using Neo4j ```python +relationships = graph.get_relationships_as_objects() +nodes = graph.get_nodes_as_objects() + +# This assumes you have the following environment variables set: +# NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD +graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + +graph_manager.save_graph(nodes, relationships) graph_manager.close() -lsp_query_helper.shutdown_exit_close() + ``` +## Complete Example -Example using Neo4j ```python -# Set up the graph manager, this will be used to save the graph to neo4j -repoId = "name_of_your_repo" -entity_id = "owner_of_the_repo" -graph_manager = Neo4jManager(repoId, entity_id) +# Taken from blarify/examples/graph_builder.py +from blarify.prebuilt.graph_builder import GraphBuilder +from blarify.db_managers.neo4j_manager import Neo4jManager -relationships = graph.get_relationships_as_objects() -nodes = graph.get_nodes_as_objects() +import dotenv +import os -graph_manager.save_graph(nodes, relationships) + +def build(root_path: str = None): + graph_builder = GraphBuilder(root_path=root_path, extensions_to_skip=[".json"], names_to_skip=["__pycache__"]) + graph = graph_builder.build() + + relationships = graph.get_relationships_as_objects() + nodes = graph.get_nodes_as_objects() + + save_to_neo4j(relationships, nodes) + + +def save_to_neo4j(relationships, nodes): + graph_manager = Neo4jManager(repo_id="repo", entity_id="organization") + + print(f"Saving graph with {len(nodes)} nodes and {len(relationships)} relationships") + graph_manager.save_graph(nodes, relationships) + graph_manager.close() + + +if __name__ == "__main__": + dotenv.load_dotenv() + root_path = os.getenv("ROOT_PATH") + build(root_path=root_path) ``` From 1e4e459719ebffeebec68267ce48126b116658eb Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 15:38:12 -0300 Subject: [PATCH 11/18] feat: integrate logging for LSP server operations and enhance project graph creator with Go language support --- .../languages/go_definitions.py | 77 +++++++++++++++++++ blarify/code_references/lsp_helper.py | 8 +- blarify/logger.py | 2 +- blarify/main.py | 3 + blarify/project_graph_creator.py | 9 ++- blarify/project_graph_diff_creator.py | 4 - poetry.lock | 23 +++++- pyproject.toml | 1 + 8 files changed, 116 insertions(+), 11 deletions(-) create mode 100644 blarify/code_hierarchy/languages/go_definitions.py diff --git a/blarify/code_hierarchy/languages/go_definitions.py b/blarify/code_hierarchy/languages/go_definitions.py new file mode 100644 index 00000000..c54f0c54 --- /dev/null +++ b/blarify/code_hierarchy/languages/go_definitions.py @@ -0,0 +1,77 @@ +from blarify.code_hierarchy.languages.FoundRelationshipScope import FoundRelationshipScope +from .language_definitions import LanguageDefinitions +from blarify.graph.relationship import RelationshipType + +import tree_sitter_go as tsgo +from tree_sitter import Language, Parser + +from typing import Optional, Set, Dict + +from blarify.graph.node import NodeLabels +from tree_sitter import Node +from blarify.graph.node import Node as GraphNode + + +class GoDefinitions(LanguageDefinitions): + def get_language_name() -> str: + return "go" + + def get_parsers_for_extensions() -> Dict[str, Parser]: + return { + ".go": Parser(Language(tsgo.language())), + } + + def should_create_node(node: Node) -> bool: + return LanguageDefinitions._should_create_node_base_implementation( + node, + ["type_declaration", "method_declaration", "function_declaration"], + ) + + def get_identifier_node(node: Node) -> Node: + if node.type == "type_declaration": + node = node.child_by_field_id("type_spec") + return LanguageDefinitions._get_identifier_node_base_implementation(node) + + def get_body_node(node: Node) -> Node: + if node.type == "type_declaration": + node = node.child_by_field_id("field_declaration_list") + + return LanguageDefinitions._get_body_node_base_implementation(node) + + def get_relationship_type(node: GraphNode, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + return GoDefinitions._find_relationship_type( + node_label=node.label, + node_in_point_reference=node_in_point_reference, + ) + + def get_node_label_from_type(type: str) -> NodeLabels: + return { + "type_declaration": NodeLabels.CLASS, + "method_declaration": NodeLabels.FUNCTION, + "function_declaration": NodeLabels.FUNCTION, + }[type] + + def get_language_file_extensions() -> Set[str]: + return {".go"} + + def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> Optional[FoundRelationshipScope]: + relationship_types = GoDefinitions._get_relationship_types_by_label() + relevant_relationship_types = relationship_types.get(node_label, {}) + + return LanguageDefinitions._traverse_and_find_relationships( + node_in_point_reference, relevant_relationship_types + ) + + def _get_relationship_types_by_label() -> dict[str, RelationshipType]: + return { + NodeLabels.CLASS: { + "object_creation_expression": RelationshipType.INSTANTIATES, + "using_directive": RelationshipType.IMPORTS, + "variable_declaration": RelationshipType.TYPES, + "parameter": RelationshipType.TYPES, + "base_list": RelationshipType.INHERITS, + }, + NodeLabels.FUNCTION: { + "invocation_expression": RelationshipType.CALLS, + }, + } diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 1a51e65a..fd16aff6 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -18,6 +18,10 @@ from multilspy.multilspy_config import MultilspyConfig from multilspy.multilspy_logger import MultilspyLogger +import logging + +logger = logging.getLogger(__name__) + class FileExtensionNotSupported(Exception): pass @@ -116,12 +120,12 @@ def _restart_lsp_for_extension(self, node): new_lsp = self._create_lsp_server(language_definitions) - print("Restarting LSP server") + logger.warning("Restarting LSP server") try: context = new_lsp.start_server() context.__enter__() except ConnectionResetError: - print("Connection reset error") + logger.error("Connection reset error") self.entered_lsp_servers[node.extension] = context diff --git a/blarify/logger.py b/blarify/logger.py index 5754ac43..50c44c11 100644 --- a/blarify/logger.py +++ b/blarify/logger.py @@ -8,4 +8,4 @@ class Logger: @staticmethod def log(message: str) -> None: # if os.getenv("DEBUG"): - print(message) + logger.info(message) diff --git a/blarify/main.py b/blarify/main.py index 01b5d90d..c945e977 100644 --- a/blarify/main.py +++ b/blarify/main.py @@ -11,6 +11,8 @@ import dotenv import os +import logging + URI = os.getenv("NEO4J_URI") USER = os.getenv("NEO4J_USERNAME") PASSWORD = os.getenv("NEO4J_PASSWORD") @@ -161,6 +163,7 @@ def main_diff_with_previous( if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) dotenv.load_dotenv() root_path = os.getenv("ROOT_PATH") blarignore_path = os.getenv("BLARIGNORE_PATH") diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index 74b2e3e4..6d04e994 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -16,6 +16,9 @@ from typing import List, TYPE_CHECKING from blarify.logger import Logger from blarify.graph.graph_environment import GraphEnvironment +import logging + +logger = logging.getLogger(__name__) if TYPE_CHECKING: @@ -58,9 +61,9 @@ def build(self) -> Graph: self.create_code_hierarchy() # TODO: Implement a better way to wait for the lsp to finish - Logger.log("Waiting for LSP to finish") + logger.info("Waiting for LSP to finish") # time.sleep(15) - Logger.log("LSP finished") + logger.info("LSP finished") self.create_relationships_from_references_for_files() return self.graph @@ -77,7 +80,7 @@ def create_code_hierarchy(self): end_time = time.time() execution_time = end_time - start_time - Logger.log(f"Execution time of create_code_hierarchy: {execution_time:.2f} seconds") + logger.info(f"Execution time of create_code_hierarchy: {execution_time:.2f} seconds") def process_folder(self, folder: "Folder") -> None: folder_node = self.add_or_get_folder_node(folder) diff --git a/blarify/project_graph_diff_creator.py b/blarify/project_graph_diff_creator.py index f4a0a264..33a1281b 100644 --- a/blarify/project_graph_diff_creator.py +++ b/blarify/project_graph_diff_creator.py @@ -143,14 +143,10 @@ def _mark_deleted_nodes_with_label(self, previous_node_states: List[PreviousNode for previous_node in previous_node_states: equivalent_node: DefinitionNode = self.graph.get_node_by_relative_id(previous_node.relative_id) if not equivalent_node: - print(f"Node not found: {previous_node.relative_id}") deleted_node = NodeFactory.create_deleted_node( graph_environment=self.pr_environment, ) - print(f"Creating deleted node: {deleted_node.hashed_id}") - print(deleted_node.as_object()) - self.graph.add_node(deleted_node) self.deleted_nodes_added_paths.append(deleted_node.path) diff --git a/poetry.lock b/poetry.lock index 0dc1aa6a..932390f9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -541,6 +541,27 @@ files = [ [package.extras] core = ["tree-sitter (>=0.22,<1.0)"] +[[package]] +name = "tree-sitter-go" +version = "0.23.4" +description = "Go grammar for tree-sitter" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "tree_sitter_go-0.23.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c9320f87a05cd47fa0f627b9329bbc09b7ed90de8fe4f5882aed318d6e19962d"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:914e63d16b36ab0e4f52b031e574b82d17d0bbfecca138ae83e887a1cf5b71ac"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:330ecbb38d6ea4ef41eba2d473056889705e64f6a51c2fb613de05b1bcb5ba22"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd14d23056ae980debfccc0db67d0a168da03792ca2968b1b5dd58ce288084e7"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:c3b40912487fdb78c4028860dd79493a521ffca0104f209849823358db3618a0"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-win_amd64.whl", hash = "sha256:ae4b231cad2ef76401d33617879cda6321c4d0853f7fd98cb5654c50a218effb"}, + {file = "tree_sitter_go-0.23.4-cp39-abi3-win_arm64.whl", hash = "sha256:2ac907362a3c347145dc1da0858248546500a323de90d2cb76d2a3fdbfc8da25"}, + {file = "tree_sitter_go-0.23.4.tar.gz", hash = "sha256:0ebff99820657066bec21690623a14c74d9e57a903f95f0837be112ddadf1a52"}, +] + +[package.extras] +core = ["tree-sitter (>=0.22,<1.0)"] + [[package]] name = "tree-sitter-javascript" version = "0.23.1" @@ -754,4 +775,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.10,<=3.14" -content-hash = "ffea608a9e38e11827e1c64114a8422f07f0052f8fe316f508f9596d552e07bf" +content-hash = "5a7403ca78dbdb36d7185e498e2223008abf732669128bc7498c7f510f155dd5" diff --git a/pyproject.toml b/pyproject.toml index fd72b63a..b695b6e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ tree-sitter-javascript = "^0.23.0" tree-sitter-typescript = "^0.23.2" tree-sitter-c-sharp = "^0.23.1" multilspy = {git = "https://github.com/blarApp/multilspy.git"} +tree-sitter-go = "^0.23.4" [build-system] From cdb577d2a511e50cf8770a49357a3ee4bd336913 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 16:47:29 -0300 Subject: [PATCH 12/18] feat: add Go language support and enhance project graph creation functionality --- blarify/code_hierarchy/languages/__init__.py | 1 + .../code_hierarchy/languages/go_definitions.py | 10 ++++++++-- .../languages/language_definitions.py | 6 ++---- blarify/code_hierarchy/tree_sitter_helper.py | 1 + blarify/code_references/lsp_helper.py | 3 +++ blarify/db_managers/neo4j_manager.py | 16 +++++++++------- blarify/examples/graph_builder.py | 4 ++++ blarify/prebuilt/graph_builder.py | 9 ++++++++- blarify/project_graph_creator.py | 2 ++ 9 files changed, 38 insertions(+), 14 deletions(-) diff --git a/blarify/code_hierarchy/languages/__init__.py b/blarify/code_hierarchy/languages/__init__.py index 0691030b..ce3036a8 100644 --- a/blarify/code_hierarchy/languages/__init__.py +++ b/blarify/code_hierarchy/languages/__init__.py @@ -5,3 +5,4 @@ from .ruby_definitions import RubyDefinitions from .fallback_definitions import FallbackDefinitions from .csharp_definitions import CsharpDefinitions +from .go_definitions import GoDefinitions diff --git a/blarify/code_hierarchy/languages/go_definitions.py b/blarify/code_hierarchy/languages/go_definitions.py index c54f0c54..d31ad796 100644 --- a/blarify/code_hierarchy/languages/go_definitions.py +++ b/blarify/code_hierarchy/languages/go_definitions.py @@ -28,13 +28,19 @@ def should_create_node(node: Node) -> bool: ) def get_identifier_node(node: Node) -> Node: + print("NODE IDENTIFIER", node.type) if node.type == "type_declaration": - node = node.child_by_field_id("type_spec") + for child in node.named_children: + if child.type == "type_spec" or "type_alias": + node = child + return LanguageDefinitions._get_identifier_node_base_implementation(node) def get_body_node(node: Node) -> Node: if node.type == "type_declaration": - node = node.child_by_field_id("field_declaration_list") + for child in node.named_children: + if child.type == "type_spec": + node = child return LanguageDefinitions._get_body_node_base_implementation(node) diff --git a/blarify/code_hierarchy/languages/language_definitions.py b/blarify/code_hierarchy/languages/language_definitions.py index 8ea5f4db..17dd90fc 100644 --- a/blarify/code_hierarchy/languages/language_definitions.py +++ b/blarify/code_hierarchy/languages/language_definitions.py @@ -48,10 +48,8 @@ def get_identifier_node(node: Node) -> Node: def _get_identifier_node_base_implementation(node: Node) -> Node: if identifier := node.child_by_field_name("name"): return identifier - - raise IdentifierNodeNotFound( - f"No identifier node found for node type {node.type} at {node.start_point} - {node.end_point}" - ) + error = f"No identifier node found for node type {node.type} at {node.start_point} - {node.end_point}" + raise IdentifierNodeNotFound(error) @staticmethod @abstractmethod diff --git a/blarify/code_hierarchy/tree_sitter_helper.py b/blarify/code_hierarchy/tree_sitter_helper.py index 6d93cc28..6b78ca03 100644 --- a/blarify/code_hierarchy/tree_sitter_helper.py +++ b/blarify/code_hierarchy/tree_sitter_helper.py @@ -174,6 +174,7 @@ def _process_identifier_node(self, node: "TreeSitterNode") -> Tuple[str, "Refere identifier_node = self.language_definitions.get_identifier_node(node) identifier_reference = self._get_reference_from_node(node=identifier_node) identifier_name = self._get_identifier_name(identifier_node=identifier_node) + print("identifier", identifier_name) return identifier_name, identifier_reference def _get_identifier_name(self, identifier_node: str) -> str: diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index fd16aff6..d687dd02 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -13,6 +13,7 @@ TypescriptDefinitions, LanguageDefinitions, CsharpDefinitions, + GoDefinitions, ) from multilspy.multilspy_config import MultilspyConfig @@ -49,6 +50,8 @@ def _get_language_definition_for_extension(self, extension: str) -> LanguageDefi return RubyDefinitions elif extension in CsharpDefinitions.get_language_file_extensions(): return CsharpDefinitions + elif extension in GoDefinitions.get_language_file_extensions(): + return GoDefinitions else: raise FileExtensionNotSupported(f'File extension "{extension}" is not supported)') diff --git a/blarify/db_managers/neo4j_manager.py b/blarify/db_managers/neo4j_manager.py index 392a8367..e3c67d4f 100644 --- a/blarify/db_managers/neo4j_manager.py +++ b/blarify/db_managers/neo4j_manager.py @@ -12,14 +12,14 @@ class Neo4jManager: - entityId: str - repoId: str + entity_id: str + repo_id: str driver: Driver def __init__( self, repo_id: str = None, - entityId: str = None, + entity_id: str = None, max_connections: int = 50, uri: str = None, user: str = None, @@ -40,8 +40,8 @@ def __init__( else: raise e - self.repoId = repo_id if repo_id is not None else "default_repo" - self.entityId = entityId if entityId is not None else "default_user" + self.repo_id = repo_id if repo_id is not None else "default_repo" + self.entity_id = entity_id if entity_id is not None else "default_user" def close(self): # Close the connection to the database @@ -54,12 +54,14 @@ def save_graph(self, nodes: List[Any], edges: List[Any]): def create_nodes(self, nodeList: List[Any]): # Function to create nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_nodes_txn, nodeList, 100, repoId=self.repoId, entityId=self.entityId) + session.write_transaction( + self._create_nodes_txn, nodeList, 100, repoId=self.repo_id, entityId=self.entity_id + ) def create_edges(self, edgesList: List[Any]): # Function to create edges between nodes in the Neo4j database with self.driver.session() as session: - session.write_transaction(self._create_edges_txn, edgesList, 100, entityId=self.entityId) + session.write_transaction(self._create_edges_txn, edgesList, 100, entityId=self.entity_id) @staticmethod def _create_nodes_txn(tx, nodeList: List[Any], batch_size: int, repoId: str, entityId: str): diff --git a/blarify/examples/graph_builder.py b/blarify/examples/graph_builder.py index a37fea71..e2c026a8 100644 --- a/blarify/examples/graph_builder.py +++ b/blarify/examples/graph_builder.py @@ -24,6 +24,10 @@ def save_to_neo4j(relationships, nodes): if __name__ == "__main__": + import logging + + logging.basicConfig(level=logging.INFO) + dotenv.load_dotenv() root_path = os.getenv("ROOT_PATH") build(root_path=root_path) diff --git a/blarify/prebuilt/graph_builder.py b/blarify/prebuilt/graph_builder.py index f9f8cd95..13271297 100644 --- a/blarify/prebuilt/graph_builder.py +++ b/blarify/prebuilt/graph_builder.py @@ -10,6 +10,7 @@ def __init__( root_path: str, extensions_to_skip: list[str] = None, names_to_skip: list[str] = None, + only_hierarchy: bool = False, ): """ A class responsible for constructing a graph representation of a project's codebase. @@ -35,13 +36,19 @@ def __init__( self.repo_id = "REPO" self.entity_id = "BLARIFY" + self.only_hierarchy = only_hierarchy def build(self) -> Graph: lsp_query_helper = self._get_started_lsp_query_helper() project_files_iterator = self._get_project_files_iterator() graph_creator = ProjectGraphCreator(self.root_path, lsp_query_helper, project_files_iterator) - graph = graph_creator.build() + + if self.only_hierarchy: + graph = graph_creator.build_hierarchy_only() + else: + graph = graph_creator.build() + lsp_query_helper.shutdown_exit_close() return graph diff --git a/blarify/project_graph_creator.py b/blarify/project_graph_creator.py index 6d04e994..594a04a3 100644 --- a/blarify/project_graph_creator.py +++ b/blarify/project_graph_creator.py @@ -1,4 +1,5 @@ import time +from blarify.code_hierarchy.languages.go_definitions import GoDefinitions from blarify.code_references import LspQueryHelper, FileExtensionNotSupported from blarify.project_file_explorer import ProjectFilesIterator from blarify.graph.node import NodeLabels, NodeFactory @@ -41,6 +42,7 @@ class ProjectGraphCreator: ".tsx": TypescriptDefinitions, ".rb": RubyDefinitions, ".cs": CsharpDefinitions, + ".go": GoDefinitions, } def __init__( From 2663aa1ed400b8790eae33c6c803264a5617be4b Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:14:33 -0300 Subject: [PATCH 13/18] fix --- .../languages/go_definitions.py | 11 +- blarify/code_references/lsp_helper.py | 2 +- log | 187 ------------------ 3 files changed, 6 insertions(+), 194 deletions(-) delete mode 100644 log diff --git a/blarify/code_hierarchy/languages/go_definitions.py b/blarify/code_hierarchy/languages/go_definitions.py index d31ad796..8d72c6ab 100644 --- a/blarify/code_hierarchy/languages/go_definitions.py +++ b/blarify/code_hierarchy/languages/go_definitions.py @@ -71,13 +71,12 @@ def _find_relationship_type(node_label: str, node_in_point_reference: Node) -> O def _get_relationship_types_by_label() -> dict[str, RelationshipType]: return { NodeLabels.CLASS: { - "object_creation_expression": RelationshipType.INSTANTIATES, - "using_directive": RelationshipType.IMPORTS, - "variable_declaration": RelationshipType.TYPES, - "parameter": RelationshipType.TYPES, - "base_list": RelationshipType.INHERITS, + "import_declaration": RelationshipType.IMPORTS, + "field_declaration": RelationshipType.TYPES, + "composite_literal": RelationshipType.INSTANTIATES, }, NodeLabels.FUNCTION: { - "invocation_expression": RelationshipType.CALLS, + "import_declaration": RelationshipType.IMPORTS, + "call_expression": RelationshipType.CALLS, }, } diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index d687dd02..22d1dbdf 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -109,7 +109,7 @@ def _request_references_with_fallback(self, node, lsp): ) except TimeoutError: self._restart_lsp_for_extension(node) - lsp = self.multi_lsp_callers[node.extension] + lsp = self._get_or_create_multi_lsp(node.extension) references = lsp.request_references( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), line=node.definition_range.start_dict["line"], diff --git a/log b/log deleted file mode 100644 index c24c6e1d..00000000 --- a/log +++ /dev/null @@ -1,187 +0,0 @@ -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Resolving TypeScript version from path "/usr/lib/node_modules/typescript/lib/tsserver.js"...'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Resolving TypeScript version from path \"/usr/lib/node_modules/typescript/lib/tsserver.js\"..." - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Reading version from package.json at "/usr/lib/node_modules/typescript/package.json"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Reading version from package.json at \"/usr/lib/node_modules/typescript/package.json\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] Resolved TypeScript version to "5.6.3"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] Resolved TypeScript version to \"5.6.3\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 3, 'message': 'Using Typescript version (bundled) 5.6.3 from path "/usr/lib/node_modules/typescript/lib/tsserver.js"'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 3, - "message": "Using Typescript version (bundled) 5.6.3 from path \"/usr/lib/node_modules/typescript/lib/tsserver.js\"" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] [tsclient] Starting tsserver'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] [tsclient] Starting tsserver" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] [tsclient] Starting tsserver'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] [tsclient] Starting tsserver" - } -} -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 4, 'message': '[lspserver] onInitialize result {\n "capabilities": {\n "textDocumentSync": 2,\n "completionProvider": {\n "triggerCharacters": [\n ".",\n "\\"",\n "\'",\n "/",\n "@",\n "<"\n ],\n "resolveProvider": true\n },\n "codeActionProvider": true,\n "codeLensProvider": {\n "resolveProvider": true\n },\n "definitionProvider": true,\n "documentFormattingProvider": true,\n "documentRangeFormattingProvider": true,\n "documentHighlightProvider": true,\n "documentSymbolProvider": true,\n "executeCommandProvider": {\n "commands": [\n "_typescript.applyWorkspaceEdit",\n "_typescript.applyCodeAction",\n "_typescript.applyRefactoring",\n "_typescript.configurePlugin",\n "_typescript.organizeImports",\n "_typescript.applyRenameFile",\n "_typescript.goToSourceDefinition"\n ]\n },\n "hoverProvider": true,\n "inlayHintProvider": true,\n "linkedEditingRangeProvider": false,\n "renameProvider": true,\n "referencesProvider": true,\n "selectionRangeProvider": true,\n "signatureHelpProvider": {\n "triggerCharacters": [\n "(",\n ",",\n "<"\n ],\n "retriggerCharacters": [\n ")"\n ]\n },\n "workspaceSymbolProvider": true,\n "implementationProvider": true,\n "typeDefinitionProvider": true,\n "foldingRangeProvider": true,\n "semanticTokensProvider": {\n "documentSelector": null,\n "legend": {\n "tokenTypes": [\n "class",\n "enum",\n "interface",\n "namespace",\n "typeParameter",\n "type",\n "parameter",\n "variable",\n "enumMember",\n "property",\n "function",\n "member"\n ],\n "tokenModifiers": [\n "declaration",\n "static",\n "async",\n "readonly",\n "defaultLibrary",\n "local"\n ]\n },\n "full": true,\n "range": true\n },\n "workspace": {\n "fileOperations": {\n "willRename": {\n "filters": [\n {\n "scheme": "file",\n "pattern": {\n "glob": "**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}",\n "matches": "file"\n }\n },\n {\n "scheme": "file",\n "pattern": {\n "glob": "**",\n "matches": "folder"\n }\n }\n ]\n }\n }\n }\n }\n}'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 4, - "message": "[lspserver] onInitialize result {\n \"capabilities\": {\n \"textDocumentSync\": 2,\n \"completionProvider\": {\n \"triggerCharacters\": [\n \".\",\n \"\\\"\",\n \"'\",\n \"/\",\n \"@\",\n \"<\"\n ],\n \"resolveProvider\": true\n },\n \"codeActionProvider\": true,\n \"codeLensProvider\": {\n \"resolveProvider\": true\n },\n \"definitionProvider\": true,\n \"documentFormattingProvider\": true,\n \"documentRangeFormattingProvider\": true,\n \"documentHighlightProvider\": true,\n \"documentSymbolProvider\": true,\n \"executeCommandProvider\": {\n \"commands\": [\n \"_typescript.applyWorkspaceEdit\",\n \"_typescript.applyCodeAction\",\n \"_typescript.applyRefactoring\",\n \"_typescript.configurePlugin\",\n \"_typescript.organizeImports\",\n \"_typescript.applyRenameFile\",\n \"_typescript.goToSourceDefinition\"\n ]\n },\n \"hoverProvider\": true,\n \"inlayHintProvider\": true,\n \"linkedEditingRangeProvider\": false,\n \"renameProvider\": true,\n \"referencesProvider\": true,\n \"selectionRangeProvider\": true,\n \"signatureHelpProvider\": {\n \"triggerCharacters\": [\n \"(\",\n \",\",\n \"<\"\n ],\n \"retriggerCharacters\": [\n \")\"\n ]\n },\n \"workspaceSymbolProvider\": true,\n \"implementationProvider\": true,\n \"typeDefinitionProvider\": true,\n \"foldingRangeProvider\": true,\n \"semanticTokensProvider\": {\n \"documentSelector\": null,\n \"legend\": {\n \"tokenTypes\": [\n \"class\",\n \"enum\",\n \"interface\",\n \"namespace\",\n \"typeParameter\",\n \"type\",\n \"parameter\",\n \"variable\",\n \"enumMember\",\n \"property\",\n \"function\",\n \"member\"\n ],\n \"tokenModifiers\": [\n \"declaration\",\n \"static\",\n \"async\",\n \"readonly\",\n \"defaultLibrary\",\n \"local\"\n ]\n },\n \"full\": true,\n \"range\": true\n },\n \"workspace\": {\n \"fileOperations\": {\n \"willRename\": {\n \"filters\": [\n {\n \"scheme\": \"file\",\n \"pattern\": {\n \"glob\": \"**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}\",\n \"matches\": \"file\"\n }\n },\n {\n \"scheme\": \"file\",\n \"pattern\": {\n \"glob\": \"**\",\n \"matches\": \"folder\"\n }\n }\n ]\n }\n }\n }\n }\n}" - } -} -{'jsonrpc': '2.0', 'id': 1, 'result': {'capabilities': {'textDocumentSync': 2, 'completionProvider': {'triggerCharacters': ['.', '"', "'", '/', '@', '<'], 'resolveProvider': True}, 'codeActionProvider': True, 'codeLensProvider': {'resolveProvider': True}, 'definitionProvider': True, 'documentFormattingProvider': True, 'documentRangeFormattingProvider': True, 'documentHighlightProvider': True, 'documentSymbolProvider': True, 'executeCommandProvider': {'commands': ['_typescript.applyWorkspaceEdit', '_typescript.applyCodeAction', '_typescript.applyRefactoring', '_typescript.configurePlugin', '_typescript.organizeImports', '_typescript.applyRenameFile', '_typescript.goToSourceDefinition']}, 'hoverProvider': True, 'inlayHintProvider': True, 'linkedEditingRangeProvider': False, 'renameProvider': True, 'referencesProvider': True, 'selectionRangeProvider': True, 'signatureHelpProvider': {'triggerCharacters': ['(', ',', '<'], 'retriggerCharacters': [')']}, 'workspaceSymbolProvider': True, 'implementationProvider': True, 'typeDefinitionProvider': True, 'foldingRangeProvider': True, 'semanticTokensProvider': {'documentSelector': None, 'legend': {'tokenTypes': ['class', 'enum', 'interface', 'namespace', 'typeParameter', 'type', 'parameter', 'variable', 'enumMember', 'property', 'function', 'member'], 'tokenModifiers': ['declaration', 'static', 'async', 'readonly', 'defaultLibrary', 'local']}, 'full': True, 'range': True}, 'workspace': {'fileOperations': {'willRename': {'filters': [{'scheme': 'file', 'pattern': {'glob': '**/*.{ts,js,jsx,tsx,mjs,mts,cjs,cts}', 'matches': 'file'}}, {'scheme': 'file', 'pattern': {'glob': '**', 'matches': 'folder'}}]}}}}}} -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/server.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/mikro-orm.config.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/index.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/book.controller.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/index.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/author.controller.spec.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/book.controller.spec.js -Creating file node for file:///home/juan/devel/blar/examples/express-js-example-app/app/controllers/author.controller.js -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js" - } -} -{'jsonrpc': '2.0', 'id': 2, 'result': []} -No references found for Publisher -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Publisher.js" - } -} -{'jsonrpc': '2.0', 'id': 3, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js" - } -} -{'jsonrpc': '2.0', 'id': 4, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Book.js" - } -} -{'jsonrpc': '2.0', 'id': 5, 'result': []} -No references found for Book -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js" - } -} -{'jsonrpc': '2.0', 'id': 6, 'result': []} -No references found for BaseEntity -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BaseEntity.js" - } -} -{'jsonrpc': '2.0', 'id': 7, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js" - } -} -{'jsonrpc': '2.0', 'id': 8, 'result': []} -No references found for BookTag -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/BookTag.js" - } -} -{'jsonrpc': '2.0', 'id': 9, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js" - } -} -{'jsonrpc': '2.0', 'id': 10, 'result': []} -No references found for constructor -{'jsonrpc': '2.0', 'method': 'window/logMessage', 'params': {'type': 1, 'message': 'Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js'}} -{ - "jsonrpc": "2.0", - "method": "window/logMessage", - "params": { - "type": 1, - "message": "Unexpected resource file:///home/juan/devel/blar/examples/express-js-example-app/app/entities/Author.js" - } -} -{'jsonrpc': '2.0', 'id': 11, 'result': []} -No references found for Author From 35d788d7bddf554ee2aa23ace01b7f4f09d74262 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:24:37 -0300 Subject: [PATCH 14/18] refactor: rename multi LSP helper methods for clarity --- blarify/code_references/lsp_helper.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 22d1dbdf..8db9dd12 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -69,7 +69,7 @@ def start(self) -> None: DEPRECATED, LSP servers are started on demand """ - def _get_or_create_multi_lsp(self, extension): + def _get_or_create_lsp_server(self, extension): language_definitions = self._get_language_definition_for_extension(extension) language = language_definitions.get_language_name() @@ -92,7 +92,7 @@ def initialize_directory(self, file) -> None: """ def get_paths_where_node_is_referenced(self, node: DefinitionNode) -> list[Reference]: - server = self._get_or_create_multi_lsp(node.extension) + server = self._get_or_create_lsp_server(node.extension) references = self._request_references_with_fallback(node, server) if not references: @@ -109,7 +109,7 @@ def _request_references_with_fallback(self, node, lsp): ) except TimeoutError: self._restart_lsp_for_extension(node) - lsp = self._get_or_create_multi_lsp(node.extension) + lsp = self._get_or_create_lsp_server(node.extension) references = lsp.request_references( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=node.path), line=node.definition_range.start_dict["line"], @@ -126,14 +126,16 @@ def _restart_lsp_for_extension(self, node): logger.warning("Restarting LSP server") try: context = new_lsp.start_server() - context.__enter__() + new_lsp = context.__enter__() + self.language_to_lsp_server[language_definitions.get_language_name()] = new_lsp + except ConnectionResetError: logger.error("Connection reset error") self.entered_lsp_servers[node.extension] = context def get_definition_path_for_reference(self, reference: Reference) -> str: - lsp_caller = self._get_or_create_multi_lsp(".py") + lsp_caller = self._get_or_create_lsp_server(".py") definitions = lsp_caller.request_definition( file_path=PathCalculator.get_relative_path_from_uri(root_uri=self.root_uri, uri=reference.uri), line=reference.range.start.line, From 411a470ab186f3b383ac50758b042f95b4571c2f Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:32:59 -0300 Subject: [PATCH 15/18] better error handling --- blarify/code_hierarchy/tree_sitter_helper.py | 1 - blarify/code_references/lsp_helper.py | 8 +++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/blarify/code_hierarchy/tree_sitter_helper.py b/blarify/code_hierarchy/tree_sitter_helper.py index 3a0df903..1200e2ba 100644 --- a/blarify/code_hierarchy/tree_sitter_helper.py +++ b/blarify/code_hierarchy/tree_sitter_helper.py @@ -176,7 +176,6 @@ def _process_identifier_node(self, node: "TreeSitterNode") -> Tuple[str, "Refere identifier_node = self.language_definitions.get_identifier_node(node) identifier_reference = self._get_reference_from_node(node=identifier_node) identifier_name = self._get_identifier_name(identifier_node=identifier_node) - print("identifier", identifier_name) return identifier_name, identifier_reference def _get_identifier_name(self, identifier_node: str) -> str: diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 8db9dd12..6e246cf0 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -149,4 +149,10 @@ def get_definition_path_for_reference(self, reference: Reference) -> str: def shutdown_exit_close(self) -> None: for lsp in self.entered_lsp_servers.values(): - lsp.__exit__(None, None, None) + try: + lsp.__exit__(None, None, None) + except Exception as e: + logger.error(f"Error shutting down LSP: {e}") + self.entered_lsp_servers = {} + self.language_to_lsp_server = {} + logger.info("LSP servers have been shut down") From 123b561c384a0d7e99c16a1d216090ad48486008 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 20:57:34 -0300 Subject: [PATCH 16/18] missing shutdown --- blarify/code_references/lsp_helper.py | 6 +----- poetry.lock | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 6e246cf0..1bb4503e 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -125,15 +125,11 @@ def _restart_lsp_for_extension(self, node): logger.warning("Restarting LSP server") try: - context = new_lsp.start_server() - new_lsp = context.__enter__() + self._initialize_lsp_server(language_definitions.get_language_name(), new_lsp) self.language_to_lsp_server[language_definitions.get_language_name()] = new_lsp - except ConnectionResetError: logger.error("Connection reset error") - self.entered_lsp_servers[node.extension] = context - def get_definition_path_for_reference(self, reference: Reference) -> str: lsp_caller = self._get_or_create_lsp_server(".py") definitions = lsp_caller.request_definition( diff --git a/poetry.lock b/poetry.lock index 932390f9..989caa97 100644 --- a/poetry.lock +++ b/poetry.lock @@ -292,7 +292,7 @@ requests = "2.32.3" type = "git" url = "https://github.com/blarApp/multilspy.git" reference = "HEAD" -resolved_reference = "c1f05ad2f083e6d8130ff4e19e0b4fbcacaef28a" +resolved_reference = "2d5a3906bcdea76b70799cf79739394b7eb27e91" [[package]] name = "neo4j" From e3c8199ec6dcc59ada0f6fcd23fb6e5232f6fe74 Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Wed, 19 Feb 2025 21:12:09 -0300 Subject: [PATCH 17/18] more error handling --- blarify/code_references/lsp_helper.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/blarify/code_references/lsp_helper.py b/blarify/code_references/lsp_helper.py index 1bb4503e..74f63cb8 100644 --- a/blarify/code_references/lsp_helper.py +++ b/blarify/code_references/lsp_helper.py @@ -107,7 +107,10 @@ def _request_references_with_fallback(self, node, lsp): line=node.definition_range.start_dict["line"], column=node.definition_range.start_dict["character"], ) - except TimeoutError: + + except (TimeoutError, ConnectionResetError) as e: + logger.warning(f"Error requesting references: {e}, attempting to restart LSP server") + self._restart_lsp_for_extension(node) lsp = self._get_or_create_lsp_server(node.extension) references = lsp.request_references( @@ -127,6 +130,7 @@ def _restart_lsp_for_extension(self, node): try: self._initialize_lsp_server(language_definitions.get_language_name(), new_lsp) self.language_to_lsp_server[language_definitions.get_language_name()] = new_lsp + logger.warning("LSP server restarted") except ConnectionResetError: logger.error("Connection reset error") From 7cfca64a01322e22ba2a4032a11fe9015ad65f1f Mon Sep 17 00:00:00 2001 From: v4rgas <66626747+v4rgas@users.noreply.github.com> Date: Thu, 20 Feb 2025 10:17:04 -0300 Subject: [PATCH 18/18] chore: README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 2ae7529b..2bc0fecc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,14 @@ This repo introduces a method to represent a local code repository as a graph structure. The objective is to allow an LLM to traverse this graph to understand the code logic and flow. Providing the LLM with the power to debug, refactor, and optimize queries. +# Supported Languages + +- Python +- JavaScript +- TypeScript +- Ruby +- Go +- C# + # Example This graph was generated from the code in this repository.