Skip to content
31 changes: 26 additions & 5 deletions src/codegen/sdk/core/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,12 @@
return first_node.insert_before(new_src, fix_indentation, newline, priority, dedupe)
return super().insert_before(new_src, fix_indentation, newline, priority, dedupe)

def move_to_file(self, file: SourceFile, include_dependencies: bool = True, strategy: str = "update_all_imports") -> None:
def move_to_file(
self,
file: SourceFile,
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",
) -> None:
"""Moves the given symbol to a new file and updates its imports and references.

This method moves a symbol to a new file and updates all references to that symbol throughout the codebase. The way imports are handled can be controlled via the strategy parameter.
Expand All @@ -288,7 +293,13 @@
self._move_to_file(file, encountered_symbols, include_dependencies, strategy)

@noapidoc
def _move_to_file(self, file: SourceFile, encountered_symbols: set[Symbol | Import], include_dependencies: bool = True, strategy: str = "update_all_imports") -> tuple[NodeId, NodeId]:
def _move_to_file(
self,
file: SourceFile,
encountered_symbols: set[Symbol | Import],
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",
) -> tuple[NodeId, NodeId]:
"""Helper recursive function for `move_to_file`"""
from codegen.sdk.core.import_resolution import Import

Expand Down Expand Up @@ -342,11 +353,20 @@
usage.file == self.file and usage.node_type == NodeType.SYMBOL and usage not in encountered_symbols and (usage.start_byte < self.start_byte or usage.end_byte > self.end_byte) # HACK
for usage in self.symbol_usages
)

# ======[ Strategy: Duplicate Dependencies ]=====
if strategy == "duplicate_dependencies":
# If not used in the original file. or if not imported from elsewhere, we can just remove the original symbol

Check warning on line 359 in src/codegen/sdk/core/symbol.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/sdk/core/symbol.py#L359

Added line #L359 was not covered by tests
if not is_used_in_file and not any(usage.kind is UsageKind.IMPORTED and usage.usage_symbol not in encountered_symbols for usage in self.usages):
self.remove()

# ======[ Strategy: Add Back Edge ]=====
# Here, we will add a "back edge" to the old file importing the symbol
if strategy == "add_back_edge":
elif strategy == "add_back_edge":
if is_used_in_file or any(usage.kind is UsageKind.IMPORTED and usage.usage_symbol not in encountered_symbols for usage in self.usages):
self.file.add_import_from_import_string(import_line)
# Delete the original symbol
self.remove()

# ======[ Strategy: Update All Imports ]=====
# Update the imports in all the files which use this symbol to get it from the new file now
Expand All @@ -365,10 +385,11 @@
usage.match.edit(self.name)
usage.usage_symbol.file.add_import_from_import_string(import_line)

# Add the import to the original file
if is_used_in_file:
self.file.add_import_from_import_string(import_line)
# =====[ Delete the original symbol ]=====
self.remove()
# Delete the original symbol
self.remove()

@property
@reader
Expand Down
28 changes: 21 additions & 7 deletions src/codegen/sdk/typescript/symbol.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Literal

from codegen.sdk.core.assignment import Assignment
from codegen.sdk.core.autocommit import reader, writer
from codegen.sdk.core.dataclasses.usage import UsageType
from codegen.sdk.core.dataclasses.usage import UsageKind, UsageType
from codegen.sdk.core.detached_symbols.function_call import FunctionCall
from codegen.sdk.core.expressions import Value
from codegen.sdk.core.expressions.chained_attribute import ChainedAttribute
Expand Down Expand Up @@ -253,9 +253,14 @@
return self.semicolon_node is not None

@noapidoc
def _move_to_file(self, file: SourceFile, encountered_symbols: set[Symbol | Import], include_dependencies: bool = True, strategy: str = "update_all_imports") -> tuple[NodeId, NodeId]:
def _move_to_file(
self,
file: SourceFile,
encountered_symbols: set[Symbol | Import],
include_dependencies: bool = True,
strategy: Literal["add_back_edge", "update_all_imports", "duplicate_dependencies"] = "update_all_imports",

Check warning on line 261 in src/codegen/sdk/typescript/symbol.py

View check run for this annotation

Codecov / codecov/patch

src/codegen/sdk/typescript/symbol.py#L261

Added line #L261 was not covered by tests
) -> tuple[NodeId, NodeId]:
# TODO: Prevent creation of import loops (!) - raise a ValueError and make the agent fix it
# TODO: Implement `update_all_imports` strategy
# =====[ Arg checking ]=====
if file == self.file:
return file.file_node_id, self.node_id
Expand Down Expand Up @@ -318,16 +323,25 @@
# =====[ Checks if symbol is used in original file ]=====
# Takes into account that it's dependencies will be moved
is_used_in_file = any(usage.file == self.file and usage.node_type == NodeType.SYMBOL and usage not in encountered_symbols for usage in self.symbol_usages)

# ======[ Strategy: Duplicate Dependencies ]=====
if strategy == "duplicate_dependencies":
# If not used in the original file. or if not imported from elsewhere, we can just remove the original symbol
if not is_used_in_file and not any(usage.kind is UsageKind.IMPORTED and usage.usage_symbol not in encountered_symbols for usage in self.usages):
self.remove()

# ======[ Strategy: Add Back Edge ]=====
# Here, we will add a "back edge" to the old file importing the self
if strategy == "add_back_edge":
elif strategy == "add_back_edge":
if is_used_in_file:
self.file.add_import_from_import_string(import_line)
if self.is_exported:
self.file.add_import_from_import_string(f"export {{ {self.name} }}")
elif self.is_exported:
module_name = file.name
self.file.add_import_from_import_string(f"export {{ {self.name} }} from '{module_name}'")
# Delete the original symbol
self.remove()

# ======[ Strategy: Update All Imports ]=====
# Update the imports in all the files which use this symbol to get it from the new file now
Expand All @@ -348,8 +362,8 @@
usage.usage_symbol.file.add_import_from_import_string(import_line)
if is_used_in_file:
self.file.add_import_from_import_string(import_line)
# =====[ Delete the original symbol ]=====
self.remove()
# Delete the original symbol
self.remove()

def _convert_proptype_to_typescript(self, prop_type: Editable, param: Parameter | None, level: int) -> str:
"""Converts a PropType definition to its TypeScript equivalent."""
Expand Down
Loading
Loading