Skip to content

Add duplicate_dependencies strategy to move_to_file #238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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