Skip to content

Commit 2688f1a

Browse files
committed
More improvements and fixes
1 parent a2771fa commit 2688f1a

File tree

6 files changed

+65
-25
lines changed

6 files changed

+65
-25
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ classifiers = [
2222
]
2323
keywords = ["cedarscript", "code-editing", "refactoring", "code-analysis", "sql-like", "ai-assisted-development"]
2424
dependencies = [
25-
"cedarscript-ast-parser>=0.2.3",
25+
"cedarscript-ast-parser>=0.2.4",
2626
"rope>=1.13.0"
2727
]
2828
requires-python = ">=3.11"

src/cedarscript_editor/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ def find_commands(content: str):
1818
pattern = r'```CEDARScript\n(.*?)```'
1919
cedar_script_blocks = re.findall(pattern, content, re.DOTALL)
2020
print(f'[find_cedar_commands] Script block count: {len(cedar_script_blocks)}')
21-
if len(cedar_script_blocks) == 0:
21+
if len(cedar_script_blocks) == 0 and not content.strip().endswith('<NOCEDARSCRIPT/>'):
2222
raise ValueError(
2323
"No CEDARScript block detected. "
2424
"Perhaps you forgot to enclose the block using ```CEDARScript and ``` ? "
25-
"Or was that intentional? If so, just write tag <NOSCRIPT/> and nothing else."
25+
"Or was that intentional? If so, just write tag <NOCEDARSCRIPT/> as the last line"
2626
)
2727
cedarscript_parser = CEDARScriptASTParser()
2828
for cedar_script in cedar_script_blocks:

src/cedarscript_editor/cedarscript_editor.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def __init__(self, command_ordinal: int, description: str):
3333

3434
previous_cmd_notes = (
3535
f", bearing in mind the file was updated and now contains all changes expressed in "
36-
f"commands {items}"
36+
f"commands {items}."
3737
)
3838
if 'syntax' in description.casefold():
3939
probability_indicator = "most probably"
@@ -43,10 +43,11 @@ def __init__(self, command_ordinal: int, description: str):
4343
note = (
4444
f"<note>*ALL* commands *before* command #{command_ordinal} "
4545
"were applied and *their changes are already committed*. "
46-
f"Re-read the file to catch up with the applied changes."
46+
f"So, it's *CRUCIAL* to re-analyze the file to catch up with the applied changes "
47+
"and understand what still needs to be done. "
4748
f"ATTENTION: The previous command (#{command_ordinal - 1}) {probability_indicator} "
4849
f"caused command #{command_ordinal} to fail "
49-
f"due to changes that left the file in an invalid state (check that by re-analyzing the file!)</note>"
50+
f"due to changes that left the file in an invalid state (check that by re-reading the file!)</note>"
5051
)
5152
super().__init__(
5253
f"<error-details><error-location>COMMAND #{command_ordinal}</error-location>{note}"
@@ -57,7 +58,7 @@ def __init__(self, command_ordinal: int, description: str):
5758
"the state at which the file was left (saying what needs to be done now), "
5859
f"then write new commands that will fix the problem{previous_cmd_notes} "
5960
"(you'll get a one-million dollar tip if you get it right!) "
60-
"Use descriptive comment before each command.</suggestion></error-details>"
61+
"Use descriptive comment before each command; If showing CEDARScript commands to the user, *DON'T* enclose them in ```CEDARSCript and ``` otherwise they will be executed!</suggestion></error-details>"
6162
)
6263

6364

@@ -197,26 +198,34 @@ def identifier_resolver(m: Marker):
197198
)
198199
dest_indent = dest_range.indent
199200
content = move_src_range.read(lines)
200-
count = dest_indent + (relindent or 0)
201+
shift_count = dest_indent + (relindent or 0)
201202
content = IndentationInfo.from_content(content).shift_indentation(
202-
content, count
203+
content, shift_count
203204
)
204205
case _:
205206
raise ValueError(f'Invalid content: {content}')
206207

207-
self._apply_action(action, lines, search_range, content)
208+
self._apply_action(action, lines, search_range, content, range_spec_to_delete=move_src_range)
208209

209210
write_file(file_path, lines)
210211

211212
return f"Updated {target if target else 'file'} in {file_path}\n -> {action}"
212213

213214
@staticmethod
214-
def _apply_action(action: EditingAction, lines: Sequence[str], range_spec: RangeSpec, content: str | None = None):
215+
def _apply_action(
216+
action: EditingAction, lines: Sequence[str], range_spec: RangeSpec, content: str | None = None,
217+
range_spec_to_delete: RangeSpec | None = None
218+
):
215219
match action:
216220

217221
case MoveClause(insert_position=insert_position, to_other_file=other_file, relative_indentation=relindent):
218222
# TODO Move from 'lines' to the same file or to 'other_file'
219-
range_spec.write(content, lines)
223+
if range_spec <= range_spec_to_delete:
224+
range_spec_to_delete.delete(lines)
225+
range_spec.write(content, lines)
226+
else:
227+
range_spec.write(content, lines)
228+
range_spec_to_delete.delete(lines)
220229

221230
case DeleteClause():
222231
range_spec.delete(lines)
@@ -339,8 +348,8 @@ def restrict_search_range_for_marker(
339348
case InsertClause():
340349
if action.insert_position.qualifier == RelativePositionType.BEFORE:
341350
search_range = search_range.inc()
342-
case DeleteClause():
343-
search_range = search_range.set_length(1)
351+
case RegionClause():
352+
search_range = search_range.set_line_count(1)
344353
case _:
345354
identifier_boundaries = identifier_resolver(marker)
346355
if not identifier_boundaries:

src/cedarscript_editor/python_identifier_finder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ def find_python_identifier(root_path: str, file_name: str, source: str, marker:
6060
return None
6161
if candidate_count > 1 and marker.offset is None:
6262
raise ValueError(
63-
f"There are {candidate_count} functions named `{marker.value}` in file `{file_name}`. "
64-
f"Use `OFFSET <0..{candidate_count - 1}>` to determine how many to skip. "
63+
f"There are {candidate_count} {marker.type} identifiers named `{marker.value}`. "
64+
f"Choose an `OFFSET` between 0 and {candidate_count - 1} to determine how many to skip. "
6565
f"Example to reference the *last* `{marker.value}`: `OFFSET {candidate_count - 1}`"
6666
)
6767
if marker.offset and marker.offset >= candidate_count:
6868
raise ValueError(
69-
f"There are only {candidate_count} functions named `{marker.value} in file `{file_name}`, "
70-
f"but 'offset' was set to {marker.offset} (you can only skip {candidate_count - 1} functions)"
69+
f"There are only {candidate_count} {marker.type} identifiers named `{marker.value}`, "
70+
f"but 'OFFSET' was set to {marker.offset} (you can skip at most {candidate_count - 1} of those)"
7171
)
7272
candidates.sort(key=lambda x: x.start_line)
7373
result: IdentifierBoundaries = get_by_offset(candidates, marker.offset or 0)

src/text_manipulation/range_spec.py

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import re
22
from collections.abc import Sequence
33
from typing import NamedTuple
4+
from functools import total_ordering
5+
46

57
from cedarscript_ast_parser import Marker, RelativeMarker, RelativePositionType, MarkerType, BodyOrWhole
68
from .indentation_kit import get_line_indent_count
79

810
MATCH_TYPES = ('exact', 'stripped', 'normalized', 'partial')
911

10-
12+
@total_ordering
1113
class RangeSpec(NamedTuple):
1214
start: int
1315
end: int
@@ -16,18 +18,31 @@ class RangeSpec(NamedTuple):
1618
def __str__(self):
1719
return (f'{self.start}:{self.end}' if self.as_index is None else f'%{self.as_index}') + f'@{self.indent}'
1820

19-
def __len__(self):
21+
def __lt__(self, other):
22+
return self.end < other.start
23+
24+
def __le__(self, other):
25+
return self.end <= other.start
26+
27+
def __gt__(self, other):
28+
return self.start > other.end
29+
30+
def __ge__(self, other):
31+
return self.start >= other.end
32+
33+
@property
34+
def line_count(self):
2035
return self.end - self.start
2136

2237
@property
2338
def as_index(self) -> int | None:
24-
return None if len(self) else self.start
39+
return None if self.line_count else self.start
2540

2641
@property
2742
def collapsed(self):
2843
return self.set_length(0)
2944

30-
def set_length(self, range_len: int):
45+
def set_line_count(self, range_len: int):
3146
return self._replace(end=self.start + range_len)
3247

3348
def inc(self, count: int = 1):
@@ -137,7 +152,22 @@ def from_line_marker(
137152

138153
offset = search_term.offset or 0
139154
for match_type in MATCH_TYPES:
140-
if offset < len(matches[match_type]):
155+
match_type_count = len(matches[match_type])
156+
if search_term.offset is None and match_type_count > 1:
157+
raise ValueError(
158+
f"There are {match_type_count} lines matching `{search_term.value}`. "
159+
f"Suggestion: Try using a different line as marker (a couple lines before or after the current one)."
160+
# f"Add an `OFFSET` (after the line marker) and a number between 0 and {match_type_count - 1} to determine how many to skip. "
161+
# f"Example to reference the *last* one of those: `LINE '{search_term.value.strip()}' OFFSET {match_type_count - 1}`"
162+
# ' (See `offset_clause` in `<grammar.js>` for details on OFFSET)'
163+
)
164+
if match_type_count and (search_term.offset or 0) >= match_type_count:
165+
raise ValueError(
166+
f"There are only {match_type_count} lines matching `{search_term.value}`, "
167+
f"but 'OFFSET' was set to {search_term.offset} (you can skip at most {match_type_count - 1} of those)"
168+
)
169+
170+
if offset < match_type_count:
141171
index, reference_indent = matches[match_type][offset]
142172
match match_type:
143173
case 'normalized':

src/text_manipulation/text_editor_kit.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ def segment_to_search_range(
8989
)
9090

9191
start_index_for_end_marker = start_match_result.as_index
92-
if start_relpos.qualifier == RelativePositionType.AFTER:
93-
start_index_for_end_marker += -1
92+
match start_relpos:
93+
case RelativeMarker(qualifier=RelativePositionType.AFTER):
94+
start_index_for_end_marker += -1
9495
end_match_result = RangeSpec.from_line_marker(lines, end_relpos, RangeSpec(
9596
start_index_for_end_marker, search_range.end, start_match_result.indent
9697
))

0 commit comments

Comments
 (0)