Skip to content

Commit

Permalink
2 steps forward 1 step back
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewdeanmartin committed Dec 15, 2023
1 parent c3a2e18 commit 6b0c06e
Show file tree
Hide file tree
Showing 18 changed files with 148 additions and 34 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,9 @@ dedlin.pyz
/.ruff_cache/

node_modules/

bot_history/

test2.txt

test3.txt
9 changes: 9 additions & 0 deletions dedlin/basic_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
class Commands(Enum):
"""Enum of commands that can be executed on a document."""

COMMENT = auto()
EMPTY = auto()
NOOP = auto() # ed compatibility
# display
LIST = auto()
PAGE = auto()
Expand Down Expand Up @@ -252,6 +254,7 @@ class Command:
line_range: Optional[LineRange] = None
phrases: Optional[Phrases] = None
original_text: Optional[str] = dataclasses.field(default=None, compare=False)
comment: Optional[str] = None

def validate(self) -> bool:
"""Check if ranges are sensible"""
Expand All @@ -267,6 +270,12 @@ def validate(self) -> bool:

def format(self) -> str:
"""Format the command as a string"""
if self.command == Commands.COMMENT:
text = self.comment if self.comment else ""
return f"# {text}"
if self.command == Commands.UNKNOWN:
text = self.original_text if self.original_text else ""
return f"# Unknown: {text}"
range_part = self.line_range.format() if self.line_range is not None else ""
phrase_part = self.phrases.format() if self.phrases is not None else ""
return " ".join([range_part, self.command.name, phrase_part]).strip()
Expand Down
10 changes: 6 additions & 4 deletions dedlin/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def print(*args, **kwargs):
@icontract.invariant(lambda self: all("\n" not in line and "\r" not in line for line in self.lines))
@icontract.invariant(
# and not self.lines <-- I'd have to update current line this on every .append()
lambda self: (1 <= self.current_line <= len(self.lines) or self.current_line in (0, 1)),
lambda self: (1 <= self.current_line <= len(self.lines) + 1 or self.current_line in (0, 1)),
"Current line must be a valid line",
)
class Document:
Expand Down Expand Up @@ -270,11 +270,11 @@ def edit(self, line_number: int) -> EditStatus:
logger.warning("Didn't get an input, nothing changed.")
return EditStatus(can_edit_again=False, text=None, line_edited=None)
except KeyboardInterrupt:
logger.warning("Cancelling out of edit, line not changed.")
logger.warning("\nCancelling out of edit, line not changed.")
return EditStatus(can_edit_again=False, text=None, line_edited=None)

if new_line is None:
logger.warning("Cancelling out of edit, line not changed.")
logger.warning("\nCancelling out of edit, line not changed.")
return EditStatus(can_edit_again=False, text=None, line_edited=None)

self.lines[line_number - 1] = new_line
Expand Down Expand Up @@ -314,8 +314,10 @@ def insert(
for phrase in phrases.as_list():
self.lines.insert(line_number - 1, phrase)
self.dirty = True
self.current_line = line_number
self.current_line = line_number + 1
line_number += 1
# HACK: if you don't do this, sequential scripted INSERT skip lines.
self.current_line -= 1
return phrases

user_input_text: Optional[str] = "GO!"
Expand Down
1 change: 0 additions & 1 deletion dedlin/file_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ def read_or_create_file(path: Path) -> list[str]:
"""Attempt to read file, create if it doesn't exist"""

if path:
print(f"Editing {path.absolute()}")
if not path.exists():
with open(str(path.absolute()), "w", encoding="utf-8"):
pass
Expand Down
2 changes: 1 addition & 1 deletion dedlin/history_feature.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def count_files_in_history_folder(self) -> int:
if not self.persist:
return 0
history_folder = self.initialize_history_folder()
return len(list(history_folder.glob("*.ed")))
return len(list(history_folder.glob("*.ed"))) + 1

def make_sequential_history_file_name(self) -> str:
"""
Expand Down
33 changes: 23 additions & 10 deletions dedlin/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ def __init__(
headless: bool = False,
disabled_commands: Optional[list[Commands]] = None,
untrusted_user: bool = False,
history: bool = True,
) -> None:
"""Set up initial state and some dependency injection"""

Expand Down Expand Up @@ -114,7 +115,7 @@ def __init__(

self.file_path: Optional[Path] = None
self.history: list[Command] = []
self.history_log = HistoryLog(persist=not self.headless)
self.history_log = HistoryLog(persist=history)
self.macro_file_name: Optional[Path] = None

def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional[str] = None) -> int:
Expand All @@ -139,6 +140,8 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional

self.macro_file_name = Path(macro_file_name) if macro_file_name else None
self.file_path = Path(file_name) if file_name else None
if self.file_path:
self.feedback(f"Editing {self.file_path.absolute()}")
lines = file_system.read_or_create_file(self.file_path)

self.doc = Document(
Expand Down Expand Up @@ -173,9 +176,7 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
self.feedback(f"Invalid command {command}")
self.print_ai_help(command)

self.history.append(command)
if not self.headless:
self.history_log.write_command_to_history_file(command.format(), self.preferred_line_break)
self.log_history(command)
self.echo_if_needed(command.format())

if command.command == Commands.REDO:
Expand All @@ -184,7 +185,7 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
except IndexError:
self.feedback("Nothing to redo, not enough history")
continue
self.history.append(command)
self.log_history(command)
self.echo_if_needed(command.original_text)

if command.command == Commands.BROWSE:
Expand All @@ -200,7 +201,7 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
elif command.command == Commands.HISTORY:
for command in self.history:
# self.feedback(command.original_text.strip("\n\t\r "))
self.feedback(command.format())
self.feedback(command.format(), no_comment=True)
elif command.command == Commands.EMPTY:
pass
elif command.command == Commands.LIST and command.line_range:
Expand Down Expand Up @@ -247,7 +248,9 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
line_range=command.line_range,
original_text=command.original_text,
)
self.history.append(rewritten_history)
self.log_history(rewritten_history)
else:
self.log_history(command)
elif command.command == Commands.PUSH and command.phrases and command.line_range:
line_number = command.line_range.start if command.line_range else 1
self.doc.push(line_number, command.phrases.as_list())
Expand All @@ -273,7 +276,7 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
if edit_status.text is not None:
# rewrite history
# _ = self.history.pop()
self.history.append(
self.log_history(
Command(
command=Commands.EDIT,
line_range=LineRange(start=edit_status.line_edited, offset=0),
Expand Down Expand Up @@ -366,6 +369,11 @@ def entry_point(self, file_name: Optional[str] = None, macro_file_name: Optional
self.feedback(status)
return 0

def log_history(self, command):
self.history.append(command)
if self.history:
self.history_log.write_command_to_history_file(command.format(), self.preferred_line_break)

def print_ai_help(self, command: str) -> None:
if not self.enable_ai_help:
return
Expand All @@ -377,8 +385,13 @@ def print_ai_help(self, command: str) -> None:
ask = ChatCompletionMessageParam(content=content, role="user")
asyncio.run(client.completion([ask]))

def feedback(self, string, end="\n") -> None:
def feedback(self, string, end="\n", no_comment: bool = False) -> None:
"""Output feedback to the user"""
if not no_comment:
# prevent infinite loop for HISTORY command
comment = Command(command=Commands.COMMENT, comment=string)
self.log_history(command=comment)

if not (self.vim_mode or self.quiet):
self.command_outputter(string, end)
return
Expand Down Expand Up @@ -435,7 +448,7 @@ def save_macro(self):

def final_report(self) -> None:
"""Print out the final report"""
if not self.headless:
if self.history:
self.feedback(f"History saved to {self.history_log.history_file_string}")

def save_on_crash(self, exception_type: Optional[Exception], value: Any, tb: Any) -> None:
Expand Down
21 changes: 19 additions & 2 deletions dedlin/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def extract_one_range(value: str, current_line: int, document_length: int) -> Op
$ = last line
"""
value = value.strip()
if value == "":
# Implicit range means different things depending on command... I think
return None
if "," in value:
parts = value.split(",")
start_string = parts[0]
Expand Down Expand Up @@ -120,7 +123,7 @@ def get_command_length(value: str, suffixes: Iterable[str]) -> int:
Commands.HISTORY: ("HISTORY",),
Commands.MACRO: ("MACRO",),
Commands.BROWSE: ("BROWSE",),
Commands.CURRENT: ("CURRENT",),
Commands.CURRENT: ("C", "CURRENT"),
Commands.SHUFFLE: ("SHUFFLE",),
Commands.SORT: ("SORT",),
Commands.REVERSE: ("REVERSE",),
Expand Down Expand Up @@ -155,7 +158,9 @@ def parse_range_only(
# TODO: the biggest generic parser should replace all of these
for command_code, command_forms in RANGE_ONLY.items():
if just_command in command_forms:
if front_part in command_forms:
if front_part and front_part in command_forms:
# Bare command because front part is just the command.
# Incorrectly assuming all commands default to entire document for missing range
line_range: Optional[LineRange] = LineRange(
start=1, offset=0 if document_length <= 0 else document_length - 1
)
Expand All @@ -173,6 +178,12 @@ def parse_range_only(
# In interactive mode in means, start accepting input for line 2.
# `2 INSERT`
phrases = Phrases(("",))
# override range, because if they specify it, it is meaningless
# if they don't specify, we insert/edit current line
if command_code == Commands.INSERT:
line_range = LineRange(start=current_line + 1 if current_line > 0 else 1, offset=0)
else:
line_range = LineRange(start=current_line if current_line > 0 else 1, offset=0)

return Command(
command_code,
Expand Down Expand Up @@ -265,6 +276,12 @@ def parse_command(command: str, current_line: int, document_length: int, headles
command=Commands.EMPTY,
original_text=original_text,
)
if command == ".":
# This is for "ed" compatibility, where . meant, switch out of input mode back to command mode.
return Command(
command=Commands.NOOP,
original_text=original_text,
)

original_text_upper = command.upper()
command = command.upper().strip()
Expand Down
40 changes: 40 additions & 0 deletions tests/sample_headless_scripts/lorem.ed_snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,43 @@ aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et mol
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias
consequatur aut perferendis doloribus asperiores repellat.

Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt,
explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia
consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit, sed quia non numquam do
eius modi tempora incididunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad
minima veniam, quis nostrumd exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid
ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse,
quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?

At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum
deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non
provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum
fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis
est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis
voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis
aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae.
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias
consequatur aut perferendis doloribus asperiores repellat.

Sed ut perspiciatis, unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt,
explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia
consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est,
qui dolorem ipsum, quia dolor sit amet consectetur adipiscing velit, sed quia non numquam do
eius modi tempora incididunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad
minima veniam, quis nostrumd exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid
ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse,
quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?

At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum
deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non
provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum
fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis
est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis
voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis
aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae.
Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias
consequatur aut perferendis doloribus asperiores repellat.

8 changes: 8 additions & 0 deletions tests/sample_headless_scripts/robo.ed_snapshot.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ Cats
Cats
Cats
Cats
Cats
Cats
Dogs
Dogs
Dogs
Dogs
Dogs
Expand All @@ -19,6 +23,10 @@ Fish
Fish
Fish
Fish
Fish
Fish
Rabbits
Rabbits
Rabbits
Rabbits
Rabbits
Expand Down
6 changes: 6 additions & 0 deletions tests/sample_headless_scripts/walrus_facts.ed_snapshot.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Walrus Facts
# Walrus Facts

Walruses are large aquatic mammals that live in the Arctic. They are known for their long tusks and large bodies. Walruses use their tusks to help them climb out of the water and onto the ice. They have a layer of blubber that keeps them warm in the cold temperatures. Walruses feed on a diet of clams, mussels, and other benthic invertebrates. They can weigh up to 1.5 tons and reach lengths of up to 12 feet. Walruses are highly social animals and can be found in large groups called herds.
# Walrus Facts

Walruses are large aquatic mammals that live in the Arctic. They are known for their long tusks and large bodies. Walruses use their tusks to help them climb out of the water and onto the ice. They have a layer of blubber that keeps them warm in the cold temperatures. Walruses feed on a diet of clams, mussels, and other benthic invertebrates. They can weigh up to 1.5 tons and reach lengths of up to 12 feet. Walruses are highly social animals and can be found in large groups called herds.

Walruses are large aquatic mammals that live in the Arctic. They are known for their long tusks and large bodies. Walruses use their tusks to help them climb out of the water and onto the ice. They have a layer of blubber that keeps them warm in the cold temperatures. Walruses feed on a diet of clams, mussels, and other benthic invertebrates. They can weigh up to 1.5 tons and reach lengths of up to 12 feet. Walruses are highly social animals and can be found in large groups called herds.
# Walrus Facts
Expand Down
6 changes: 6 additions & 0 deletions tests/sample_headless_scripts/walrus_facts2.ed_snapshot.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# Walrus Fact 1
# Walrus Fact 1
# Walrus Fact 2
# Walrus Fact 3
# Walrus Fact 1
# Walrus Fact 2
# Walrus Fact 3
# Walrus Fact 2
# Walrus Fact 3
# Walrus Fact 1
Expand Down
1 change: 1 addition & 0 deletions tests/sample_macros/grep_log.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Editing E:\github\dedlin\tests\sample_macros\grep_out.txt
Current line 1 of 2
1 : This is a cat.
Current line 1 of 2
Expand Down
1 change: 1 addition & 0 deletions tests/sample_macros/sed_log.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Editing E:\github\dedlin\tests\sample_macros\sed_out.txt
Current line 1 of 2
Replacing
1 : That is a butt.
Expand Down
25 changes: 13 additions & 12 deletions tests/sample_macros/walrus_facts1_log.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
Editing E:\github\dedlin\tests\sample_macros\walrus_facts1_out.txt
Current line 1 of 14
1 : They big
2 : They walrus
Expand All @@ -14,17 +15,17 @@ Current line 1 of 14
1 : Their diet is fish and stuff.
Current line 1 of 14
Current line 1 of 14
Current line 10 of 14
Current line 10 of 14
Current line 1 of 14
Current line 1 of 14
Control C to exit insert mode
Current line 11 of 15
Current line 11 of 15
Current line 11 of 15
Current line 11 of 15
8 : They are lazy
9 : They are funny
10 : Walruses would eat pizza rosa if they could.
11 : Walruses primarily feed on benthic bivalve mollusks.
Current line 2 of 15
Current line 2 of 15
Current line 2 of 15
Current line 2 of 15
8 : They are fat
9 : They are lazy
10 : They are funny
11 : Their diet is fish and stuff.
12 : They are mammals
Current line 11 of 15
Current line 11 of 15
Current line 2 of 15
Current line 2 of 15
Loading

0 comments on commit 6b0c06e

Please sign in to comment.