shotgun-sh 0.2.29.dev2__py3-none-any.whl → 0.6.1.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of shotgun-sh might be problematic. Click here for more details.
- shotgun/agents/agent_manager.py +497 -30
- shotgun/agents/cancellation.py +103 -0
- shotgun/agents/common.py +90 -77
- shotgun/agents/config/README.md +0 -1
- shotgun/agents/config/manager.py +52 -8
- shotgun/agents/config/models.py +48 -45
- shotgun/agents/config/provider.py +44 -29
- shotgun/agents/conversation/history/file_content_deduplication.py +66 -43
- shotgun/agents/conversation/history/token_counting/base.py +51 -9
- shotgun/agents/export.py +12 -13
- shotgun/agents/file_read.py +176 -0
- shotgun/agents/messages.py +15 -3
- shotgun/agents/models.py +90 -2
- shotgun/agents/plan.py +12 -13
- shotgun/agents/research.py +13 -10
- shotgun/agents/router/__init__.py +47 -0
- shotgun/agents/router/models.py +384 -0
- shotgun/agents/router/router.py +185 -0
- shotgun/agents/router/tools/__init__.py +18 -0
- shotgun/agents/router/tools/delegation_tools.py +557 -0
- shotgun/agents/router/tools/plan_tools.py +403 -0
- shotgun/agents/runner.py +17 -2
- shotgun/agents/specify.py +12 -13
- shotgun/agents/tasks.py +12 -13
- shotgun/agents/tools/__init__.py +8 -0
- shotgun/agents/tools/codebase/directory_lister.py +27 -39
- shotgun/agents/tools/codebase/file_read.py +26 -35
- shotgun/agents/tools/codebase/query_graph.py +9 -0
- shotgun/agents/tools/codebase/retrieve_code.py +9 -0
- shotgun/agents/tools/file_management.py +81 -3
- shotgun/agents/tools/file_read_tools/__init__.py +7 -0
- shotgun/agents/tools/file_read_tools/multimodal_file_read.py +167 -0
- shotgun/agents/tools/markdown_tools/__init__.py +62 -0
- shotgun/agents/tools/markdown_tools/insert_section.py +148 -0
- shotgun/agents/tools/markdown_tools/models.py +86 -0
- shotgun/agents/tools/markdown_tools/remove_section.py +114 -0
- shotgun/agents/tools/markdown_tools/replace_section.py +119 -0
- shotgun/agents/tools/markdown_tools/utils.py +453 -0
- shotgun/agents/tools/registry.py +41 -0
- shotgun/agents/tools/web_search/__init__.py +1 -2
- shotgun/agents/tools/web_search/gemini.py +1 -3
- shotgun/agents/tools/web_search/openai.py +42 -23
- shotgun/attachments/__init__.py +41 -0
- shotgun/attachments/errors.py +60 -0
- shotgun/attachments/models.py +107 -0
- shotgun/attachments/parser.py +257 -0
- shotgun/attachments/processor.py +193 -0
- shotgun/cli/clear.py +2 -2
- shotgun/cli/codebase/commands.py +181 -65
- shotgun/cli/compact.py +2 -2
- shotgun/cli/context.py +2 -2
- shotgun/cli/run.py +90 -0
- shotgun/cli/spec/backup.py +2 -1
- shotgun/cli/spec/commands.py +2 -0
- shotgun/cli/spec/models.py +18 -0
- shotgun/cli/spec/pull_service.py +122 -68
- shotgun/codebase/__init__.py +2 -0
- shotgun/codebase/benchmarks/__init__.py +35 -0
- shotgun/codebase/benchmarks/benchmark_runner.py +309 -0
- shotgun/codebase/benchmarks/exporters.py +119 -0
- shotgun/codebase/benchmarks/formatters/__init__.py +49 -0
- shotgun/codebase/benchmarks/formatters/base.py +34 -0
- shotgun/codebase/benchmarks/formatters/json_formatter.py +106 -0
- shotgun/codebase/benchmarks/formatters/markdown.py +136 -0
- shotgun/codebase/benchmarks/models.py +129 -0
- shotgun/codebase/core/__init__.py +4 -0
- shotgun/codebase/core/call_resolution.py +91 -0
- shotgun/codebase/core/change_detector.py +11 -6
- shotgun/codebase/core/errors.py +159 -0
- shotgun/codebase/core/extractors/__init__.py +23 -0
- shotgun/codebase/core/extractors/base.py +138 -0
- shotgun/codebase/core/extractors/factory.py +63 -0
- shotgun/codebase/core/extractors/go/__init__.py +7 -0
- shotgun/codebase/core/extractors/go/extractor.py +122 -0
- shotgun/codebase/core/extractors/javascript/__init__.py +7 -0
- shotgun/codebase/core/extractors/javascript/extractor.py +132 -0
- shotgun/codebase/core/extractors/protocol.py +109 -0
- shotgun/codebase/core/extractors/python/__init__.py +7 -0
- shotgun/codebase/core/extractors/python/extractor.py +141 -0
- shotgun/codebase/core/extractors/rust/__init__.py +7 -0
- shotgun/codebase/core/extractors/rust/extractor.py +139 -0
- shotgun/codebase/core/extractors/types.py +15 -0
- shotgun/codebase/core/extractors/typescript/__init__.py +7 -0
- shotgun/codebase/core/extractors/typescript/extractor.py +92 -0
- shotgun/codebase/core/gitignore.py +252 -0
- shotgun/codebase/core/ingestor.py +644 -354
- shotgun/codebase/core/kuzu_compat.py +119 -0
- shotgun/codebase/core/language_config.py +239 -0
- shotgun/codebase/core/manager.py +256 -46
- shotgun/codebase/core/metrics_collector.py +310 -0
- shotgun/codebase/core/metrics_types.py +347 -0
- shotgun/codebase/core/parallel_executor.py +424 -0
- shotgun/codebase/core/work_distributor.py +254 -0
- shotgun/codebase/core/worker.py +768 -0
- shotgun/codebase/indexing_state.py +86 -0
- shotgun/codebase/models.py +94 -0
- shotgun/codebase/service.py +13 -0
- shotgun/exceptions.py +1 -1
- shotgun/main.py +2 -10
- shotgun/prompts/agents/export.j2 +2 -0
- shotgun/prompts/agents/file_read.j2 +48 -0
- shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +20 -28
- shotgun/prompts/agents/partials/content_formatting.j2 +12 -33
- shotgun/prompts/agents/partials/interactive_mode.j2 +9 -32
- shotgun/prompts/agents/partials/router_delegation_mode.j2 +35 -0
- shotgun/prompts/agents/plan.j2 +43 -1
- shotgun/prompts/agents/research.j2 +75 -20
- shotgun/prompts/agents/router.j2 +713 -0
- shotgun/prompts/agents/specify.j2 +94 -4
- shotgun/prompts/agents/state/codebase/codebase_graphs_available.j2 +14 -1
- shotgun/prompts/agents/state/system_state.j2 +24 -15
- shotgun/prompts/agents/tasks.j2 +77 -23
- shotgun/settings.py +44 -0
- shotgun/shotgun_web/shared_specs/upload_pipeline.py +38 -0
- shotgun/tui/app.py +90 -23
- shotgun/tui/commands/__init__.py +9 -1
- shotgun/tui/components/attachment_bar.py +87 -0
- shotgun/tui/components/mode_indicator.py +120 -25
- shotgun/tui/components/prompt_input.py +23 -28
- shotgun/tui/components/status_bar.py +5 -4
- shotgun/tui/dependencies.py +58 -8
- shotgun/tui/protocols.py +37 -0
- shotgun/tui/screens/chat/chat.tcss +24 -1
- shotgun/tui/screens/chat/chat_screen.py +1374 -211
- shotgun/tui/screens/chat/codebase_index_prompt_screen.py +8 -4
- shotgun/tui/screens/chat_screen/attachment_hint.py +40 -0
- shotgun/tui/screens/chat_screen/command_providers.py +0 -97
- shotgun/tui/screens/chat_screen/history/agent_response.py +7 -3
- shotgun/tui/screens/chat_screen/history/chat_history.py +49 -6
- shotgun/tui/screens/chat_screen/history/formatters.py +75 -15
- shotgun/tui/screens/chat_screen/history/partial_response.py +11 -1
- shotgun/tui/screens/chat_screen/history/user_question.py +25 -3
- shotgun/tui/screens/chat_screen/messages.py +219 -0
- shotgun/tui/screens/database_locked_dialog.py +219 -0
- shotgun/tui/screens/database_timeout_dialog.py +158 -0
- shotgun/tui/screens/kuzu_error_dialog.py +135 -0
- shotgun/tui/screens/model_picker.py +14 -9
- shotgun/tui/screens/models.py +11 -0
- shotgun/tui/screens/shotgun_auth.py +50 -0
- shotgun/tui/screens/spec_pull.py +2 -0
- shotgun/tui/state/processing_state.py +19 -0
- shotgun/tui/utils/mode_progress.py +20 -86
- shotgun/tui/widgets/__init__.py +2 -1
- shotgun/tui/widgets/approval_widget.py +152 -0
- shotgun/tui/widgets/cascade_confirmation_widget.py +203 -0
- shotgun/tui/widgets/plan_panel.py +129 -0
- shotgun/tui/widgets/step_checkpoint_widget.py +180 -0
- shotgun/tui/widgets/widget_coordinator.py +18 -0
- shotgun/utils/file_system_utils.py +4 -1
- {shotgun_sh-0.2.29.dev2.dist-info → shotgun_sh-0.6.1.dev1.dist-info}/METADATA +88 -34
- shotgun_sh-0.6.1.dev1.dist-info/RECORD +292 -0
- shotgun/cli/export.py +0 -81
- shotgun/cli/plan.py +0 -73
- shotgun/cli/research.py +0 -93
- shotgun/cli/specify.py +0 -70
- shotgun/cli/tasks.py +0 -78
- shotgun/tui/screens/onboarding.py +0 -580
- shotgun_sh-0.2.29.dev2.dist-info/RECORD +0 -229
- {shotgun_sh-0.2.29.dev2.dist-info → shotgun_sh-0.6.1.dev1.dist-info}/WHEEL +0 -0
- {shotgun_sh-0.2.29.dev2.dist-info → shotgun_sh-0.6.1.dev1.dist-info}/entry_points.txt +0 -0
- {shotgun_sh-0.2.29.dev2.dist-info → shotgun_sh-0.6.1.dev1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Protocol definition for language extractors."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from tree_sitter import Node
|
|
9
|
+
|
|
10
|
+
from .types import SupportedLanguage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@runtime_checkable
|
|
14
|
+
class LanguageExtractor(Protocol):
|
|
15
|
+
"""Protocol for language-specific AST extraction.
|
|
16
|
+
|
|
17
|
+
Each language implementation provides methods to extract:
|
|
18
|
+
- Decorators/attributes from function/class nodes
|
|
19
|
+
- Docstrings/documentation
|
|
20
|
+
- Inheritance information from class definitions
|
|
21
|
+
- Call expression parsing
|
|
22
|
+
- Node type information for the language
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def language(self) -> SupportedLanguage:
|
|
27
|
+
"""The language this extractor handles."""
|
|
28
|
+
...
|
|
29
|
+
|
|
30
|
+
def extract_decorators(self, node: Node) -> list[str]:
|
|
31
|
+
"""Extract decorators/attributes from a function or class node.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
node: The AST node (function or class definition)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of decorator/attribute names
|
|
38
|
+
"""
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def extract_docstring(self, node: Node) -> str | None:
|
|
42
|
+
"""Extract documentation string from a function or class node.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
node: The AST node (function or class definition)
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
The docstring content, or None if not present
|
|
49
|
+
"""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
def extract_inheritance(self, class_node: Node) -> list[str]:
|
|
53
|
+
"""Extract parent class names from a class definition.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
class_node: The class definition AST node
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of parent class names (simple names, may need resolution)
|
|
60
|
+
"""
|
|
61
|
+
...
|
|
62
|
+
|
|
63
|
+
def parse_call_node(self, call_node: Node) -> tuple[str | None, str | None]:
|
|
64
|
+
"""Parse a call expression node to extract callee information.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
call_node: The call expression AST node
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Tuple of (callee_name, object_name) where:
|
|
71
|
+
- callee_name: The name of the function/method being called
|
|
72
|
+
- object_name: The object the method is called on (for method calls)
|
|
73
|
+
"""
|
|
74
|
+
...
|
|
75
|
+
|
|
76
|
+
def find_parent_class(self, func_node: Node, module_qn: str) -> str | None:
|
|
77
|
+
"""Find the parent class of a function node.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
func_node: The function definition AST node
|
|
81
|
+
module_qn: Module qualified name
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Qualified name of parent class, or None if not in a class
|
|
85
|
+
"""
|
|
86
|
+
...
|
|
87
|
+
|
|
88
|
+
def find_containing_function(self, node: Node, module_qn: str) -> str | None:
|
|
89
|
+
"""Find the containing function/method of a node.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
node: The AST node
|
|
93
|
+
module_qn: Module qualified name
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Qualified name of containing function, or None
|
|
97
|
+
"""
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
def count_ast_nodes(self, node: Node) -> int:
|
|
101
|
+
"""Count total AST nodes for metrics.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
node: Root AST node
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Total node count
|
|
108
|
+
"""
|
|
109
|
+
...
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Python language extractor implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from shotgun.codebase.core.extractors.base import BaseExtractor
|
|
8
|
+
from shotgun.codebase.core.extractors.types import SupportedLanguage
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from tree_sitter import Node
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PythonExtractor(BaseExtractor):
|
|
15
|
+
"""Extractor for Python source code."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def language(self) -> SupportedLanguage:
|
|
19
|
+
"""The language this extractor handles."""
|
|
20
|
+
return SupportedLanguage.PYTHON
|
|
21
|
+
|
|
22
|
+
def _class_definition_types(self) -> list[str]:
|
|
23
|
+
"""Return node types that represent class definitions."""
|
|
24
|
+
return ["class_definition"]
|
|
25
|
+
|
|
26
|
+
def _function_definition_types(self) -> list[str]:
|
|
27
|
+
"""Return node types that represent function definitions."""
|
|
28
|
+
return ["function_definition"]
|
|
29
|
+
|
|
30
|
+
def extract_decorators(self, node: Node) -> list[str]:
|
|
31
|
+
"""Extract decorators from a function or class node.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
node: The AST node (function or class definition)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
List of decorator names
|
|
38
|
+
"""
|
|
39
|
+
decorators: list[str] = []
|
|
40
|
+
|
|
41
|
+
for child in node.children:
|
|
42
|
+
if child.type == "decorator":
|
|
43
|
+
for grandchild in child.children:
|
|
44
|
+
if grandchild.type == "identifier" and grandchild.text:
|
|
45
|
+
decorators.append(grandchild.text.decode("utf-8"))
|
|
46
|
+
break
|
|
47
|
+
elif grandchild.type == "attribute":
|
|
48
|
+
attr_node = grandchild.child_by_field_name("attribute")
|
|
49
|
+
if attr_node and attr_node.text:
|
|
50
|
+
decorators.append(attr_node.text.decode("utf-8"))
|
|
51
|
+
break
|
|
52
|
+
|
|
53
|
+
return decorators
|
|
54
|
+
|
|
55
|
+
def extract_docstring(self, node: Node) -> str | None:
|
|
56
|
+
"""Extract docstring from a function or class node.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
node: The AST node (function or class definition)
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
The docstring content, or None if not present
|
|
63
|
+
"""
|
|
64
|
+
body_node = node.child_by_field_name("body")
|
|
65
|
+
if not body_node or not body_node.children:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
first_statement = body_node.children[0]
|
|
69
|
+
if first_statement.type == "expression_statement":
|
|
70
|
+
for child in first_statement.children:
|
|
71
|
+
if child.type == "string" and child.text:
|
|
72
|
+
docstring = child.text.decode("utf-8")
|
|
73
|
+
docstring = docstring.strip()
|
|
74
|
+
if (
|
|
75
|
+
docstring.startswith('"""')
|
|
76
|
+
and docstring.endswith('"""')
|
|
77
|
+
or docstring.startswith("'''")
|
|
78
|
+
and docstring.endswith("'''")
|
|
79
|
+
):
|
|
80
|
+
docstring = docstring[3:-3]
|
|
81
|
+
elif (
|
|
82
|
+
docstring.startswith('"')
|
|
83
|
+
and docstring.endswith('"')
|
|
84
|
+
or docstring.startswith("'")
|
|
85
|
+
and docstring.endswith("'")
|
|
86
|
+
):
|
|
87
|
+
docstring = docstring[1:-1]
|
|
88
|
+
return docstring.strip()
|
|
89
|
+
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
def extract_inheritance(self, class_node: Node) -> list[str]:
|
|
93
|
+
"""Extract parent class names from a class definition.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
class_node: The class definition AST node
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List of parent class names (simple names, may need resolution)
|
|
100
|
+
"""
|
|
101
|
+
parent_names: list[str] = []
|
|
102
|
+
|
|
103
|
+
for child in class_node.children:
|
|
104
|
+
if child.type == "argument_list":
|
|
105
|
+
for arg in child.children:
|
|
106
|
+
if arg.type == "identifier" and arg.text:
|
|
107
|
+
parent_names.append(arg.text.decode("utf-8"))
|
|
108
|
+
elif arg.type == "attribute":
|
|
109
|
+
full_name_parts: list[str] = []
|
|
110
|
+
self._extract_full_name(arg, full_name_parts)
|
|
111
|
+
if full_name_parts:
|
|
112
|
+
parent_names.append(".".join(full_name_parts))
|
|
113
|
+
|
|
114
|
+
return parent_names
|
|
115
|
+
|
|
116
|
+
def parse_call_node(self, call_node: Node) -> tuple[str | None, str | None]:
|
|
117
|
+
"""Parse a call expression node to extract callee information.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
call_node: The call expression AST node
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
Tuple of (callee_name, object_name)
|
|
124
|
+
"""
|
|
125
|
+
callee_name = None
|
|
126
|
+
object_name = None
|
|
127
|
+
|
|
128
|
+
for child in call_node.children:
|
|
129
|
+
if child.type == "identifier" and child.text:
|
|
130
|
+
callee_name = child.text.decode("utf-8")
|
|
131
|
+
break
|
|
132
|
+
elif child.type == "attribute":
|
|
133
|
+
obj_node = child.child_by_field_name("object")
|
|
134
|
+
attr_node = child.child_by_field_name("attribute")
|
|
135
|
+
if obj_node and obj_node.text:
|
|
136
|
+
object_name = obj_node.text.decode("utf-8")
|
|
137
|
+
if attr_node and attr_node.text:
|
|
138
|
+
callee_name = attr_node.text.decode("utf-8")
|
|
139
|
+
break
|
|
140
|
+
|
|
141
|
+
return callee_name, object_name
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""Rust language extractor implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from shotgun.codebase.core.extractors.base import BaseExtractor
|
|
8
|
+
from shotgun.codebase.core.extractors.types import SupportedLanguage
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from tree_sitter import Node
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RustExtractor(BaseExtractor):
|
|
15
|
+
"""Extractor for Rust source code."""
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def language(self) -> SupportedLanguage:
|
|
19
|
+
"""The language this extractor handles."""
|
|
20
|
+
return SupportedLanguage.RUST
|
|
21
|
+
|
|
22
|
+
def _class_definition_types(self) -> list[str]:
|
|
23
|
+
"""Return node types that represent type definitions.
|
|
24
|
+
|
|
25
|
+
Rust has structs, enums, traits, and type aliases.
|
|
26
|
+
"""
|
|
27
|
+
return ["struct_item", "enum_item", "trait_item", "type_item"]
|
|
28
|
+
|
|
29
|
+
def _function_definition_types(self) -> list[str]:
|
|
30
|
+
"""Return node types that represent function definitions."""
|
|
31
|
+
return ["function_item", "function_signature_item"]
|
|
32
|
+
|
|
33
|
+
def extract_decorators(self, node: Node) -> list[str]:
|
|
34
|
+
"""Extract attributes from a function or struct node.
|
|
35
|
+
|
|
36
|
+
Rust uses #[attribute] syntax.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
node: The AST node
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
List of attribute names
|
|
43
|
+
"""
|
|
44
|
+
attributes: list[str] = []
|
|
45
|
+
|
|
46
|
+
for child in node.children:
|
|
47
|
+
if child.type == "attribute_item":
|
|
48
|
+
for attr_child in child.children:
|
|
49
|
+
if attr_child.type == "attribute":
|
|
50
|
+
path = attr_child.child_by_field_name("path")
|
|
51
|
+
if path and path.text:
|
|
52
|
+
attributes.append(path.text.decode("utf-8"))
|
|
53
|
+
|
|
54
|
+
return attributes
|
|
55
|
+
|
|
56
|
+
def extract_docstring(self, node: Node) -> str | None:
|
|
57
|
+
"""Extract doc comment from a function or type node.
|
|
58
|
+
|
|
59
|
+
Rust uses /// for outer doc comments and //! for inner doc comments.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
node: The AST node
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
The doc comment, or None if not present
|
|
66
|
+
"""
|
|
67
|
+
doc_lines: list[str] = []
|
|
68
|
+
prev_sibling = node.prev_named_sibling
|
|
69
|
+
|
|
70
|
+
while prev_sibling and prev_sibling.type == "line_comment":
|
|
71
|
+
comment_text = prev_sibling.text
|
|
72
|
+
if comment_text:
|
|
73
|
+
text = comment_text.decode("utf-8")
|
|
74
|
+
if text.startswith("///"):
|
|
75
|
+
doc_lines.insert(0, text[3:].strip())
|
|
76
|
+
else:
|
|
77
|
+
break
|
|
78
|
+
prev_sibling = prev_sibling.prev_named_sibling
|
|
79
|
+
|
|
80
|
+
if doc_lines:
|
|
81
|
+
return "\n".join(doc_lines)
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
def extract_inheritance(self, class_node: Node) -> list[str]:
|
|
85
|
+
"""Extract trait bounds or supertraits from a type definition.
|
|
86
|
+
|
|
87
|
+
For structs, this returns nothing (Rust doesn't have struct inheritance).
|
|
88
|
+
For traits, this returns supertraits.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
class_node: The type definition AST node
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
List of supertrait names
|
|
95
|
+
"""
|
|
96
|
+
supertraits: list[str] = []
|
|
97
|
+
|
|
98
|
+
if class_node.type == "trait_item":
|
|
99
|
+
for child in class_node.children:
|
|
100
|
+
if child.type == "trait_bounds":
|
|
101
|
+
for bound in child.children:
|
|
102
|
+
if bound.type == "type_identifier" and bound.text:
|
|
103
|
+
supertraits.append(bound.text.decode("utf-8"))
|
|
104
|
+
elif bound.type == "generic_type":
|
|
105
|
+
type_node = bound.child_by_field_name("type")
|
|
106
|
+
if type_node and type_node.text:
|
|
107
|
+
supertraits.append(type_node.text.decode("utf-8"))
|
|
108
|
+
|
|
109
|
+
return supertraits
|
|
110
|
+
|
|
111
|
+
def parse_call_node(self, call_node: Node) -> tuple[str | None, str | None]:
|
|
112
|
+
"""Parse a call expression node to extract callee information.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
call_node: The call expression AST node
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Tuple of (callee_name, object_name)
|
|
119
|
+
"""
|
|
120
|
+
callee_name = None
|
|
121
|
+
object_name = None
|
|
122
|
+
|
|
123
|
+
func_node = call_node.child_by_field_name("function")
|
|
124
|
+
if func_node:
|
|
125
|
+
if func_node.type == "identifier" and func_node.text:
|
|
126
|
+
callee_name = func_node.text.decode("utf-8")
|
|
127
|
+
elif func_node.type == "field_expression":
|
|
128
|
+
value = func_node.child_by_field_name("value")
|
|
129
|
+
field = func_node.child_by_field_name("field")
|
|
130
|
+
if value and value.text:
|
|
131
|
+
object_name = value.text.decode("utf-8")
|
|
132
|
+
if field and field.text:
|
|
133
|
+
callee_name = field.text.decode("utf-8")
|
|
134
|
+
elif func_node.type == "scoped_identifier":
|
|
135
|
+
name = func_node.child_by_field_name("name")
|
|
136
|
+
if name and name.text:
|
|
137
|
+
callee_name = name.text.decode("utf-8")
|
|
138
|
+
|
|
139
|
+
return callee_name, object_name
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Type definitions for the extractors module."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SupportedLanguage(StrEnum):
|
|
9
|
+
"""Supported programming languages for AST extraction."""
|
|
10
|
+
|
|
11
|
+
PYTHON = "python"
|
|
12
|
+
JAVASCRIPT = "javascript"
|
|
13
|
+
TYPESCRIPT = "typescript"
|
|
14
|
+
GO = "go"
|
|
15
|
+
RUST = "rust"
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""TypeScript language extractor implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from shotgun.codebase.core.extractors.javascript.extractor import JavaScriptExtractor
|
|
8
|
+
from shotgun.codebase.core.extractors.types import SupportedLanguage
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from tree_sitter import Node
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class TypeScriptExtractor(JavaScriptExtractor):
|
|
15
|
+
"""Extractor for TypeScript source code.
|
|
16
|
+
|
|
17
|
+
TypeScript is a superset of JavaScript, so this extends
|
|
18
|
+
JavaScriptExtractor with TypeScript-specific features.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def language(self) -> SupportedLanguage:
|
|
23
|
+
"""The language this extractor handles."""
|
|
24
|
+
return SupportedLanguage.TYPESCRIPT
|
|
25
|
+
|
|
26
|
+
def _class_definition_types(self) -> list[str]:
|
|
27
|
+
"""Return node types that represent class definitions.
|
|
28
|
+
|
|
29
|
+
TypeScript adds interface_declaration and type_alias_declaration.
|
|
30
|
+
"""
|
|
31
|
+
return [
|
|
32
|
+
"class_declaration",
|
|
33
|
+
"class",
|
|
34
|
+
"interface_declaration",
|
|
35
|
+
"type_alias_declaration",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def extract_decorators(self, node: Node) -> list[str]:
|
|
39
|
+
"""Extract decorators from a function or class node.
|
|
40
|
+
|
|
41
|
+
TypeScript supports decorators (experimental feature).
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
node: The AST node
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
List of decorator names
|
|
48
|
+
"""
|
|
49
|
+
decorators: list[str] = []
|
|
50
|
+
|
|
51
|
+
for child in node.children:
|
|
52
|
+
if child.type == "decorator":
|
|
53
|
+
for grandchild in child.children:
|
|
54
|
+
if grandchild.type == "identifier" and grandchild.text:
|
|
55
|
+
decorators.append(grandchild.text.decode("utf-8"))
|
|
56
|
+
break
|
|
57
|
+
elif grandchild.type == "call_expression":
|
|
58
|
+
for call_child in grandchild.children:
|
|
59
|
+
if call_child.type == "identifier" and call_child.text:
|
|
60
|
+
decorators.append(call_child.text.decode("utf-8"))
|
|
61
|
+
break
|
|
62
|
+
|
|
63
|
+
return decorators
|
|
64
|
+
|
|
65
|
+
def extract_inheritance(self, class_node: Node) -> list[str]:
|
|
66
|
+
"""Extract parent class/interface names from a class or interface.
|
|
67
|
+
|
|
68
|
+
TypeScript classes can extend one class and implement multiple interfaces.
|
|
69
|
+
Interfaces can extend multiple interfaces.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
class_node: The class/interface definition AST node
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List of parent names
|
|
76
|
+
"""
|
|
77
|
+
parent_names: list[str] = []
|
|
78
|
+
|
|
79
|
+
for child in class_node.children:
|
|
80
|
+
if child.type in ["extends_clause", "implements_clause"]:
|
|
81
|
+
for type_node in child.children:
|
|
82
|
+
if type_node.type == "type_identifier" and type_node.text:
|
|
83
|
+
parent_names.append(type_node.text.decode("utf-8"))
|
|
84
|
+
elif type_node.type == "generic_type":
|
|
85
|
+
name_node = type_node.child_by_field_name("name")
|
|
86
|
+
if name_node and name_node.text:
|
|
87
|
+
parent_names.append(name_node.text.decode("utf-8"))
|
|
88
|
+
|
|
89
|
+
if not parent_names:
|
|
90
|
+
parent_names = super().extract_inheritance(class_node)
|
|
91
|
+
|
|
92
|
+
return parent_names
|