diff --git a/README.md b/README.md index e502158..acf5b04 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # dedlin -Dedlin is an interactive line-by-line text editor and a DSL. Line editors -suck, but they are easy to write and the DSL is mildly interesting. +Dedlin is an interactive line-by-line text editor and a DSL, similar to edlin or ed. It is perfect for +letting AI chatbots edit documents without sending the whole document back and forth. -While this is a clone of [edlin](https://en.wikipedia.org/wiki/Edlin), this is not intended to be backwards compatible -with anything. I have made changes to make the app less user hostile, but there is a `--vim_mode` +It is scriptable making it similar to `sed` for doing find, insert, replace, delete operations on existing files. + +Soon it will support non-line number ranges, e.g. `"/Done/ DELETE` + +Dedlin extends on [edlin](https://en.wikipedia.org/wiki/Edlin) enough that it is not backwards compatible. + +I have made changes to make the app less user hostile than classic ed or edlin, but there is a `--vim_mode` where all help, warnings, feedback will be suppressed. ## Badges diff --git a/dedlin/__init__.py b/dedlin/__init__.py index e69de29..6383c9c 100644 --- a/dedlin/__init__.py +++ b/dedlin/__init__.py @@ -0,0 +1,23 @@ +from dedlin.command_sources import CommandGenerator, InteractiveGenerator +from dedlin.document_sources import PrefillInputter, SimpleInputter, input_with_prefill +from dedlin.flash import title_screen +from dedlin.logging_utils import configure_logging +from dedlin.main import Dedlin +from dedlin.outputters import rich_output, talking_outputter +from dedlin.outputters.plain import plain_printer + +# All the parts necessary to implement an alternative to __main__ +__all__ = [ + "CommandGenerator", + "InteractiveGenerator", + "StringCommandGenerator", + "PrefillInputter", + "SimpleInputter", + "input_with_prefill", + "title_screen", + "configure_logging", + "Dedlin", + "rich_output", + "talking_outputter", + "plain_printer", +] diff --git a/dedlin/command_sources.py b/dedlin/command_sources.py index 82f846d..96cc53e 100644 --- a/dedlin/command_sources.py +++ b/dedlin/command_sources.py @@ -19,12 +19,6 @@ from dedlin.parsers import parse_command from dedlin.pygments_code import EdLexer -# from prompt_toolkit.shortcuts import prompt -# from pygments.lexers import guess_lexer_for_filename -# from prompt_toolkit.completion import Completer, Completion -# from prompt_toolkit.formatted_text import HTML - -# thing = guess_lexer_for_filename("cats.py","") style = style_from_pygments_cls(get_style_by_name("borland")) @@ -126,5 +120,22 @@ def generate( yield from self.commands -if __name__ == "__main__": - print("This is a module, not a program.") +class StringCommandGenerator: + """Get a typed command from a string""" + + def __init__(self, source: str): + """Initialize the generator""" + self.source: str = source + self.current_line: int = 0 + self.document_length: int = 0 + self.prompt: str = "> " + + def generate( + self, + ) -> Generator[Command, None, None]: + """Turn a sring into a bunch of commands""" + for line in self.source.split("\n"): + command = parse_command( + line.strip("\r"), current_line=self.current_line, document_length=self.document_length + ) + yield command diff --git a/dedlin/document.py b/dedlin/document.py index 3c65038..ff5edac 100644 --- a/dedlin/document.py +++ b/dedlin/document.py @@ -358,7 +358,10 @@ def lorem(self, line_range: Optional[LineRange]) -> None: def undo(self) -> None: """Undo last change""" self.lines = self.previous_lines - self.previous_current_line = self.current_line + if self.previous_current_line < 1: + self.current_line = 1 + else: + self.current_line = self.previous_current_line logger.debug("Undid last step") def sort(self) -> None: diff --git a/dedlin/parsers.py b/dedlin/parsers.py index 81fdf1d..5a0e319 100644 --- a/dedlin/parsers.py +++ b/dedlin/parsers.py @@ -244,6 +244,8 @@ def bare_command(command: str) -> Optional[Command]: def parse_command(command: str, current_line: int, document_length: int) -> Command: """Parse a command""" original_text = command + + # Handle empty text. if not command: return Command( command=Commands.EMPTY, @@ -253,13 +255,14 @@ def parse_command(command: str, current_line: int, document_length: int) -> Comm original_text_upper = command.upper() command = command.upper().strip() + # Handle comments if not command or command.startswith("#"): return Command( command=Commands.EMPTY, original_text=original_text, ) - # bare number is insert. + # Handle shortcuts, bare number is insert. candidate_int = try_parse_int(command) if candidate_int is not None: target = candidate_int @@ -277,7 +280,10 @@ def parse_command(command: str, current_line: int, document_length: int) -> Comm original_text=original_text, ) - # TODO: maybe use regex. + # Divide into + # - front part (pre command) + # - command + # - phrases (post command) front_part_chars = [] found_first_alpha = False just_command_chars = [] @@ -295,6 +301,7 @@ def parse_command(command: str, current_line: int, document_length: int) -> Comm just_command = "".join(just_command_chars) location_of_command = original_text_upper.find(just_command) + # Handle post command phrases end_part = original_text[location_of_command + len(just_command) :] if end_part: # must preserve case! diff --git a/pyproject.toml b/pyproject.toml index a0cc738..3e31c35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,11 +1,11 @@ [tool.poetry] name = "dedlin" -version = "1.14.0" +version = "1.15.0" description = "Line editor, edlin clone with many improvements" authors = ["Matthew Martin "] keywords = ["editor", "edlin", "line editor",] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", diff --git a/scripts/a.py b/scripts/a.py deleted file mode 100644 index c66a0e7..0000000 --- a/scripts/a.py +++ /dev/null @@ -1,35 +0,0 @@ -import asyncio - - -async def fetch_google_html(): - return await asyncio.sleep(1, "2") - - -async def echo_text(text): - print(f"Echoed: {text}") - - -async def main(): - while True: - print("\nOptions:") - print("1. Echo text") - print("2. Fetch HTML from google.com") - print("3. Exit") - - choice = input("Enter your choice (1/2/3): ") - - if choice == "1": - text = input("Enter text to echo: ") - echo_text(text) - elif choice == "2": - html_content = await fetch_google_html() - print(html_content[:500] + "...") # Print only the first 500 characters for brevity - elif choice == "3": - print("Exiting...") - break - else: - return "yo" - - -if __name__ == "__main__": - asyncio.run(main())