Skip to content

Commit c6ce9e5

Browse files
committed
Use generic identifier finder based on Tree-Sitter instead of using the Python-specific Rope lib.
1 parent b10365f commit c6ce9e5

File tree

7 files changed

+1360
-152
lines changed

7 files changed

+1360
-152
lines changed

pyproject.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ classifiers = [
2222
]
2323
keywords = ["cedarscript", "code-editing", "refactoring", "code-analysis", "sql-like", "ai-assisted-development"]
2424
dependencies = [
25-
"cedarscript-ast-parser>=0.2.6"
25+
"cedarscript-ast-parser>=0.2.6",
26+
"grep-ast==0.3.3",
27+
"tree-sitter-languages==1.10.2",
2628
]
2729
requires-python = ">=3.11"
2830

src/cedarscript_editor/cedarscript_editor.py

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
from collections.abc import Sequence
3-
from typing import Callable
43

54
from cedarscript_ast_parser import Command, RmFileCommand, MvFileCommand, UpdateCommand, \
65
SelectCommand, IdentifierFromFile, Segment, Marker, MoveClause, DeleteClause, \
@@ -11,7 +10,7 @@
1110
IndentationInfo, IdentifierBoundaries, RangeSpec, read_file, write_file, bow_to_search_range
1211
)
1312

14-
from .identifier_selector import select_finder
13+
from .tree_sitter_identifier_finder import IdentifierFinder, find_identifier
1514

1615

1716
class CEDARScriptEditorException(Exception):
@@ -65,20 +64,11 @@ def __init__(self, command_ordinal: int, description: str):
6564

6665

6766
class CEDARScriptEditor:
68-
def __init__(self, root_path):
67+
def __init__(self, root_path: os.PathLike):
6968
self.root_path = os.path.abspath(root_path)
70-
print(f'[{self.__class__}] root: {self.root_path}')
69+
print(f'[{self.__class__.__name__}] root: {self.root_path}')
7170

7271
# TODO Add 'target_search_range: RangeSpec' parameter
73-
def find_identifier(self, source_info: tuple[str, str | Sequence[str]], marker: Marker) -> IdentifierBoundaries:
74-
file_path = source_info[0]
75-
source = source_info[1]
76-
if not isinstance(source, str):
77-
source = '\n'.join(source)
78-
return (
79-
select_finder(self.root_path, file_path, source)
80-
(self.root_path, file_path, source, marker)
81-
)
8272

8373
def apply_commands(self, commands: Sequence[Command]):
8474
result = []
@@ -119,8 +109,10 @@ def _update_command(self, cmd: UpdateCommand):
119109
# After parsing ->
120110
# UpdateCommand(
121111
# type='update',
122-
# target=SingleFileClause(file_path='tmp.benchmarks/2024-10-04-22-59-58--CEDARScript-Gemini-small/bowling/bowling.py'),
123-
# action=InsertClause(insert_position=RelativeMarker(type=<MarkerType.FUNCTION: 'function'>, value='__init__',
112+
# target=SingleFileClause(file_path='tmp.benchmarks/2024-10-04-22-59-58--CEDARScript-Gemini-small/bowling
113+
# /bowling.py'),
114+
# action=InsertClause(insert_position=RelativeMarker(type=<MarkerType.FUNCTION: 'function'>,
115+
# value='__init__',
124116
# offset=None)),
125117
# content='\n @0:print("This line will be inserted at the top")\n '
126118
# )
@@ -155,8 +147,7 @@ def _update_command(self, cmd: UpdateCommand):
155147

156148
source_info: tuple[str | bytes, str | Sequence[str]] = (file_path, src)
157149

158-
def identifier_resolver(m: Marker):
159-
return self.find_identifier(source_info, m)
150+
identifier_finder = find_identifier(source_info)
160151

161152
match action:
162153
case MoveClause():
@@ -167,16 +158,16 @@ def identifier_resolver(m: Marker):
167158
# action.insertclause.insert_position=FUNCTION(score)
168159
# target.as_marker = FUNCTION(roll) (the one to delete)
169160
search_range = RangeSpec.EMPTY
170-
move_src_range = restrict_search_range(action, target, identifier_resolver)
161+
move_src_range = restrict_search_range(action, target, identifier_finder)
171162
case _:
172163
move_src_range = None
173164
# Set range_spec to cover the identifier
174-
search_range = restrict_search_range(action, target, identifier_resolver)
165+
search_range = restrict_search_range(action, target, identifier_finder)
175166

176167
marker, search_range = find_marker_or_segment(action, lines, search_range)
177168

178169
search_range = restrict_search_range_for_marker(
179-
marker, action, lines, search_range, identifier_resolver
170+
marker, action, lines, search_range, identifier_finder
180171
)
181172

182173
match content:
@@ -185,7 +176,7 @@ def identifier_resolver(m: Marker):
185176
case (region, relindent):
186177
dest_indent = search_range.indent
187178
content_range = restrict_search_range_for_marker(
188-
region, action, lines, RangeSpec.EMPTY, identifier_resolver
179+
region, action, lines, RangeSpec.EMPTY, identifier_finder
189180
)
190181
content = content_range.read(lines)
191182
count = dest_indent + (relindent or 0)
@@ -197,7 +188,7 @@ def identifier_resolver(m: Marker):
197188
match action:
198189
case MoveClause(insert_position=region, relative_indentation=relindent):
199190
dest_range = restrict_search_range_for_marker(
200-
region, action, lines, RangeSpec.EMPTY, identifier_resolver
191+
region, action, lines, RangeSpec.EMPTY, identifier_finder
201192
)
202193
dest_indent = dest_range.indent
203194
content = move_src_range.read(lines)
@@ -263,38 +254,38 @@ def _delete_function(self, cmd): # TODO
263254
#
264255
# return f"Created file: {command['file']}"
265256

266-
def find_index_range_for_region(self,
267-
region: BodyOrWhole | Marker | Segment | RelativeMarker,
268-
lines: Sequence[str],
269-
identifier_resolver: Callable[[Marker], IdentifierBoundaries],
270-
search_range: RangeSpec | IdentifierBoundaries | None = None,
271-
) -> RangeSpec:
272-
# BodyOrWhole | RelativeMarker | MarkerOrSegment
273-
# marker_or_segment_to_index_range_impl
274-
# IdentifierBoundaries.location_to_search_range(self, location: BodyOrWhole | RelativePositionType) -> RangeSpec
275-
match region:
276-
case BodyOrWhole() as bow:
277-
# TODO Set indent char count
278-
index_range = bow_to_search_range(bow, search_range)
279-
case Marker() | Segment() as mos:
280-
if isinstance(search_range, IdentifierBoundaries):
281-
search_range = search_range.whole
282-
match mos:
283-
case Marker(type=marker_type):
284-
match marker_type:
285-
case MarkerType.LINE:
286-
pass
287-
case _:
288-
# TODO transform to RangeSpec
289-
mos = self.find_identifier(("find_index_range_for_region", lines), mos).body
290-
index_range = mos.to_search_range(
291-
lines,
292-
search_range.start if search_range else 0,
293-
search_range.end if search_range else -1,
294-
)
295-
case _ as invalid:
296-
raise ValueError(f"Invalid: {invalid}")
297-
return index_range
257+
258+
def find_index_range_for_region(region: BodyOrWhole | Marker | Segment | RelativeMarker,
259+
lines: Sequence[str],
260+
identifier_finder: IdentifierFinder,
261+
search_range: RangeSpec | IdentifierBoundaries | None = None,
262+
) -> RangeSpec:
263+
# BodyOrWhole | RelativeMarker | MarkerOrSegment
264+
# marker_or_segment_to_index_range_impl
265+
# IdentifierBoundaries.location_to_search_range(self, location: BodyOrWhole | RelativePositionType) -> RangeSpec
266+
match region:
267+
case BodyOrWhole() as bow:
268+
# TODO Set indent char count
269+
index_range = bow_to_search_range(bow, search_range)
270+
case Marker() | Segment() as mos:
271+
if isinstance(search_range, IdentifierBoundaries):
272+
search_range = search_range.whole
273+
match mos:
274+
case Marker(type=marker_type):
275+
match marker_type:
276+
case MarkerType.LINE:
277+
pass
278+
case _:
279+
# TODO transform to RangeSpec
280+
mos = find_identifier(("TODO?.py", lines))(mos).body
281+
index_range = mos.to_search_range(
282+
lines,
283+
search_range.start if search_range else 0,
284+
search_range.end if search_range else -1,
285+
)
286+
case _ as invalid:
287+
raise ValueError(f"Invalid: {invalid}")
288+
return index_range
298289

299290

300291
def find_marker_or_segment(
@@ -315,12 +306,12 @@ def find_marker_or_segment(
315306
return marker, search_range
316307

317308

318-
def restrict_search_range(action, target, identifier_resolver: Callable[[Marker], IdentifierBoundaries]) -> RangeSpec:
309+
def restrict_search_range(action, target, identifier_finder: IdentifierFinder) -> RangeSpec:
319310
search_range = RangeSpec.EMPTY
320311
match target:
321312
case IdentifierFromFile() as identifier_from_file:
322313
identifier_marker = identifier_from_file.as_marker
323-
identifier_boundaries = identifier_resolver(identifier_marker)
314+
identifier_boundaries = identifier_finder(identifier_marker)
324315
if not identifier_boundaries:
325316
raise ValueError(f"'{identifier_marker}' not found")
326317
match action:
@@ -338,7 +329,7 @@ def restrict_search_range_for_marker(
338329
action: EditingAction,
339330
lines: Sequence[str],
340331
search_range: RangeSpec,
341-
identifier_resolver: Callable[[Marker], IdentifierBoundaries]
332+
identifier_finder: IdentifierFinder
342333
) -> RangeSpec:
343334
if marker is None:
344335
return search_range
@@ -355,7 +346,7 @@ def restrict_search_range_for_marker(
355346
case RegionClause():
356347
search_range = search_range.set_line_count(1)
357348
case _:
358-
identifier_boundaries = identifier_resolver(marker)
349+
identifier_boundaries = identifier_finder(marker)
359350
if not identifier_boundaries:
360351
raise ValueError(f"'{marker}' not found")
361352
qualifier: RelativePositionType = marker.qualifier if isinstance(

src/cedarscript_editor/identifier_selector.py

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/cedarscript_editor/python_identifier_finder.py

Lines changed: 0 additions & 74 deletions
This file was deleted.

0 commit comments

Comments
 (0)