From 72e2f310bd105c0040083c8d634809c52b44dae4 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:01:08 -0400 Subject: [PATCH 01/29] Initial commit --- it/LICENSE | 24 ++++++++++++++++++++++++ it/README.md | 2 ++ 2 files changed, 26 insertions(+) create mode 100644 it/LICENSE create mode 100644 it/README.md diff --git a/it/LICENSE b/it/LICENSE new file mode 100644 index 0000000..fdddb29 --- /dev/null +++ b/it/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/it/README.md b/it/README.md new file mode 100644 index 0000000..2dfe946 --- /dev/null +++ b/it/README.md @@ -0,0 +1,2 @@ +# it +ass end of ed From b83e702d8a7be5bd76bb8e8ebd4492b1b4f099e4 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:01:44 -0400 Subject: [PATCH 02/29] Add files via upload --- it/main.py | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 it/main.py diff --git a/it/main.py b/it/main.py new file mode 100644 index 0000000..580bc5d --- /dev/null +++ b/it/main.py @@ -0,0 +1,74 @@ +# it, a shITtier version of ed +import importlib +import sys + +oldinput = input + +def input(*args, **kwargs): + message = args[0] if len(args) > 0 else "" + while 1: + try: + user_input = str(oldinput(message)) + except KeyboardInterrupt: + pass + except EOFError: + sys.exit(0) + return user_input + +def parsecommand(command): + casesensitive = True + escapes = ["\\"] + quotes = ["'", '"'] + spaces = [" ", "\t"] + + command = command.rstrip().lstrip() + + inQuotes = 0 + word = "" + parsed_command = [] + + for i in range(len(command)): + if (command[i] in quotes) and inQuotes == 0\ + and (i == 0 \ + or (i > 0 and command[i-1] != escape)): + inQuotes = command[i] + + elif command[i] == inQuotes \ + and (i > 0 and command[i-1] != escape): + inQuotes = 0 + + elif command[i] in space and inQuotes == 0\ + and (i > 0 and command[i-1] != escape): + parsed_command += [word] + word = "" + + elif command[i] != "\\" or (i == len(command) - 1) \ + or not(command[i+1] in space + quotes): + word += command[i] + + parsed_command += [word] + if parsed_command == ['']: return [] + return parsed_command + +class buffer: # all the information about the current file + content = [] + dot = -1 + filename = "" + saved = 0 + +def main(argc, argv): + file = buffer() + modules = {} + while True: + command = parsecommand(input()) + if(command == []): + continue + try: + modules[command[0]] = importlib.import_module(command[0]) + except ModuleNotFoundError as err: + print(err) + else: + file = modules[command[0]].main(len(command), command, file) + +if __name__ == "__main__": + return main(len(sys.argv), sys.argv) From fa1d985535a0ba60e896d39707790de3378224b2 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:04:51 -0400 Subject: [PATCH 03/29] for veronika --- it/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/it/main.py b/it/main.py index 580bc5d..e606233 100644 --- a/it/main.py +++ b/it/main.py @@ -21,7 +21,7 @@ def parsecommand(command): quotes = ["'", '"'] spaces = [" ", "\t"] - command = command.rstrip().lstrip() + command = command.strip() inQuotes = 0 word = "" From eeffd70fe37c3f78af58c36f961a488e658af524 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:06:44 -0400 Subject: [PATCH 04/29] no kwargs in input --- it/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/it/main.py b/it/main.py index e606233..724b72e 100644 --- a/it/main.py +++ b/it/main.py @@ -4,7 +4,7 @@ import sys oldinput = input -def input(*args, **kwargs): +def input(*args): message = args[0] if len(args) > 0 else "" while 1: try: From 8ec00ab8320d9e9d339129f7a7fce9c9fa8159f7 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:09:19 -0400 Subject: [PATCH 05/29] s/space/spaces/g and spaces is now a list --- it/main.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/it/main.py b/it/main.py index 724b72e..6244c49 100644 --- a/it/main.py +++ b/it/main.py @@ -30,15 +30,15 @@ def parsecommand(command): for i in range(len(command)): if (command[i] in quotes) and inQuotes == 0\ and (i == 0 \ - or (i > 0 and command[i-1] != escape)): + or (i > 0 and not(command[i-1] in escapes))): inQuotes = command[i] elif command[i] == inQuotes \ - and (i > 0 and command[i-1] != escape): + and (i > 0 and not(command[i-1] in escapes)): inQuotes = 0 elif command[i] in space and inQuotes == 0\ - and (i > 0 and command[i-1] != escape): + and (i > 0 and not(command[i-1] in escapes)): parsed_command += [word] word = "" From fab099f7acbeef7afdfd891a2366da6543c2f3b7 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 09:18:37 -0400 Subject: [PATCH 06/29] ed f --- it/f.py | 6 ++++++ it/filename.py | 1 + 2 files changed, 7 insertions(+) create mode 100644 it/f.py create mode 100644 it/filename.py diff --git a/it/f.py b/it/f.py new file mode 100644 index 0000000..b6f6b0c --- /dev/null +++ b/it/f.py @@ -0,0 +1,6 @@ +def main(argc, argv, buffer): + if(argc > 2): + print("?") + else: + buffer.filename = argv[1] + return buffer diff --git a/it/filename.py b/it/filename.py new file mode 100644 index 0000000..3d0f87a --- /dev/null +++ b/it/filename.py @@ -0,0 +1 @@ +from f import * From 67d4eb27487d5fd9583fcdc363f19916f4ad7e56 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 12:43:47 -0400 Subject: [PATCH 07/29] tons of suggested fixes from alen --- it/main.py | 72 ++++++++++++++++++++++++++---------------------------- 1 file changed, 34 insertions(+), 38 deletions(-) mode change 100644 => 100755 it/main.py diff --git a/it/main.py b/it/main.py old mode 100644 new mode 100755 index 6244c49..6521aa0 --- a/it/main.py +++ b/it/main.py @@ -1,25 +1,13 @@ +#!/usr/bin/env python3 # it, a shITtier version of ed import importlib import sys -oldinput = input - -def input(*args): - message = args[0] if len(args) > 0 else "" - while 1: - try: - user_input = str(oldinput(message)) - except KeyboardInterrupt: - pass - except EOFError: - sys.exit(0) - return user_input - def parsecommand(command): casesensitive = True - escapes = ["\\"] - quotes = ["'", '"'] - spaces = [" ", "\t"] + escapes = {"\\"} + quotes = {"'", '"'} + spaces = {" ", "\t"} command = command.strip() @@ -27,48 +15,56 @@ def parsecommand(command): word = "" parsed_command = [] + # copied from other project. mystery code. works though for i in range(len(command)): - if (command[i] in quotes) and inQuotes == 0\ - and (i == 0 \ - or (i > 0 and not(command[i-1] in escapes))): + if ((command[i] in quotes) + and inQuotes == 0 + and (i == 0 + or (i > 0 and not(command[i-1] in escapes)))): inQuotes = command[i] - elif command[i] == inQuotes \ - and (i > 0 and not(command[i-1] in escapes)): + elif (command[i] == inQuotes + and (i > 0 and not(command[i-1] in escapes))): inQuotes = 0 - elif command[i] in space and inQuotes == 0\ - and (i > 0 and not(command[i-1] in escapes)): + elif (command[i] in spaces and inQuotes == 0 + and (i > 0 and not(command[i-1] in escapes))): parsed_command += [word] word = "" - elif command[i] != "\\" or (i == len(command) - 1) \ - or not(command[i+1] in space + quotes): + elif (command[i] != "\\" or (i == len(command) - 1) + or not(command[i+1] in spaces + quotes)): word += command[i] parsed_command += [word] - if parsed_command == ['']: return [] - return parsed_command + return [] if parsed_command == [''] else parsed_command -class buffer: # all the information about the current file - content = [] - dot = -1 - filename = "" - saved = 0 +class Buffer: # all the information about the current file + def __init__(self): + self.content = [] # content of the file + self.dot = 0 # where we are in the file + self.filename = "" # name of where we'll save the file + self.saved = 1 # bool that says whether or not we have saved the file -def main(argc, argv): - file = buffer() +def main(): + buffer = Buffer() modules = {} while True: - command = parsecommand(input()) - if(command == []): + try: + command = parsecommand(input()) + except KeyboardInterrupt: # bastard behavior from ed + pass + except EOFError: + break + if command == []: continue try: modules[command[0]] = importlib.import_module(command[0]) except ModuleNotFoundError as err: print(err) else: - file = modules[command[0]].main(len(command), command, file) + buffer = modules[command[0]].main(buffer, command) + return 0 if __name__ == "__main__": - return main(len(sys.argv), sys.argv) + sys.exit(main()) From 6432d2bfc3b1f6d8838a0eabc675e36015d51f5d Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 12:44:06 -0400 Subject: [PATCH 08/29] works --- it/f.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/it/f.py b/it/f.py index b6f6b0c..c8c9b96 100644 --- a/it/f.py +++ b/it/f.py @@ -1,6 +1,8 @@ -def main(argc, argv, buffer): - if(argc > 2): +def main(buffer, argv): + if len(argv) > 2: print("?") + elif len(argv) == 1: + print(buffer.filename) else: buffer.filename = argv[1] return buffer From a1cd51d8db697550969bc061abc5b5d2537fa640 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 6 May 2021 18:23:52 -0400 Subject: [PATCH 09/29] quitting --- it/Q.py | 8 ++++++++ it/q.py | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 it/Q.py create mode 100644 it/q.py diff --git a/it/Q.py b/it/Q.py new file mode 100644 index 0000000..aac715a --- /dev/null +++ b/it/Q.py @@ -0,0 +1,8 @@ +from sys import exit + +def main(buffer, argv): + if len(argv) > 1: + print("?") + else: + exit(0) + return buffer diff --git a/it/q.py b/it/q.py new file mode 100644 index 0000000..e73f7ec --- /dev/null +++ b/it/q.py @@ -0,0 +1,8 @@ +from sys import exit + +def main(buffer, argv): + if len(argv) > 1 or not(buffer.saved): + print("?") + else: + exit(0) + return buffer From 4628d3dbf2b27cb54d732c66283093375ab919d1 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:16:06 -0400 Subject: [PATCH 10/29] file stuff --- it/filename.py | 1 - it/{main.py => it.py} | 0 2 files changed, 1 deletion(-) delete mode 100644 it/filename.py rename it/{main.py => it.py} (100%) diff --git a/it/filename.py b/it/filename.py deleted file mode 100644 index 3d0f87a..0000000 --- a/it/filename.py +++ /dev/null @@ -1 +0,0 @@ -from f import * diff --git a/it/main.py b/it/it.py similarity index 100% rename from it/main.py rename to it/it.py From 98923dbe9fd23d9a4e5659e470b5b3f446d36892 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:16:34 -0400 Subject: [PATCH 11/29] divide labor --- it/buffer.py | 65 +++++++++++++++++++++++++++++++++ it/it.py | 89 ++++++++++++++++----------------------------- it/parse_command.py | 65 +++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 58 deletions(-) create mode 100755 it/buffer.py create mode 100755 it/parse_command.py diff --git a/it/buffer.py b/it/buffer.py new file mode 100755 index 0000000..8a6a4d2 --- /dev/null +++ b/it/buffer.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +import importlib +import sys +from parse_command import parse_command + +class Buffer: # all the information about the current file + def carat(self): + return self.index + def dollar(self): + return self.length() - 1 + self.index + + # Rather than get the content as it's presented in the buffer object + # (as one big string), get it as a list. + def content_list(self): + return self.split(self.delimiter) + + # Build a string with the same format as the buffer.content and set the + # buffer.content to that string, from the kind of list output by + # buffer.content_list(). + # buffer.content_set_list(buffer.content_list()) is an expensive nop. + def content_set_list(self, content_as_list): + content = "" + for line in content_as_list: + content += line + self.delimiter + self.content = content + return None + + # this is really bad because any module can call import_module_ just as + # easily as any other. so malicious modules that, say, take 'q' and + # make it upload a file to some external server before exiting would be + # super easy to make. + # the solution I see is OS-level permissions but this needs to be + # talked about like all the time if this tool gets popular + # [why would it?] lest some fool runs a zelda.sh script that changes + # ~/src/it/w.py and doesn't realize in time + def import_module_(self, name): + try: + self.modules[name] = importlib.import_module(name) + except (ModuleNotFoundError, TypeError) as err: + print(err) + return False + else: + return True + + def length(self): + return self.content.count(self.delimiter) + + def __init__(self): + self.content = '' # content of the file + self.delimiter = '\n' # line delimiter + self.filename = "" # name of where we'll save the file + self.index = 0 # indexing of dot + self.dot = self.index - 1 # invalid position to start + self.modules = {} + self.saved = 1 # bool that says whether or not we have saved the file + +def main(buffer, command): + if len(command) == 1: + command = command + list(vars(buffer)) + for attrib in command[1:]: + if attrib in list(vars(buffer)): + print("%s:\t%s" % (attrib, vars(buffer)[attrib])) + else: + print("No attribute: %s" % attrib) + return buffer diff --git a/it/it.py b/it/it.py index 6521aa0..50ff947 100755 --- a/it/it.py +++ b/it/it.py @@ -1,70 +1,43 @@ #!/usr/bin/env python3 -# it, a shITtier version of ed import importlib import sys +from buffer import Buffer +from parse_command import parse_command -def parsecommand(command): - casesensitive = True - escapes = {"\\"} - quotes = {"'", '"'} - spaces = {" ", "\t"} +def get_command(): + try: + return parse_command(input()) + except KeyboardInterrupt: # bastard behavior from ed + pass + except EOFError: + return 0 - command = command.strip() +def main(buffer, supplied_command): + if supplied_command != [] and len(supplied_command) > 1: + command = supplied_command[1:] + else: + command = get_command() - inQuotes = 0 - word = "" - parsed_command = [] - - # copied from other project. mystery code. works though - for i in range(len(command)): - if ((command[i] in quotes) - and inQuotes == 0 - and (i == 0 - or (i > 0 and not(command[i-1] in escapes)))): - inQuotes = command[i] - - elif (command[i] == inQuotes - and (i > 0 and not(command[i-1] in escapes))): - inQuotes = 0 - - elif (command[i] in spaces and inQuotes == 0 - and (i > 0 and not(command[i-1] in escapes))): - parsed_command += [word] - word = "" - - elif (command[i] != "\\" or (i == len(command) - 1) - or not(command[i+1] in spaces + quotes)): - word += command[i] - - parsed_command += [word] - return [] if parsed_command == [''] else parsed_command - -class Buffer: # all the information about the current file - def __init__(self): - self.content = [] # content of the file - self.dot = 0 # where we are in the file - self.filename = "" # name of where we'll save the file - self.saved = 1 # bool that says whether or not we have saved the file - -def main(): - buffer = Buffer() - modules = {} while True: - try: - command = parsecommand(input()) - except KeyboardInterrupt: # bastard behavior from ed - pass - except EOFError: + # EOFError in get_command(); ^D + if command == 0: break + if command == []: continue - try: - modules[command[0]] = importlib.import_module(command[0]) - except ModuleNotFoundError as err: - print(err) - else: - buffer = modules[command[0]].main(buffer, command) - return 0 + + if command[0] in buffer.modules.keys() or buffer.import_module_(command[0]): + buffer = buffer.modules[command[0]].main(buffer, command) + if type(buffer) is int: + break + + command = get_command() + + return buffer if __name__ == "__main__": - sys.exit(main()) + buffer = main(Buffer(), []) + if type(buffer) is int: + sys.exit(buffer) + else: + sys.exit(0) diff --git a/it/parse_command.py b/it/parse_command.py new file mode 100755 index 0000000..151e06d --- /dev/null +++ b/it/parse_command.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +import sys + +def parse_command(command): + casesensitive = True + escapes = {"\\"} + quotes = {"'", '"'} + spaces = {" ", "\t"} + + command = command.strip() + + in_quotes = 0 + word = "" + parsed_command = [] + + for i in range(len(command)): + # if this char is a quote char + # , we're in quotes + if ((command[i] in quotes) + and in_quotes == 0 + and (i == 0 + or (i > 0 and not(command[i-1] in escapes)))): + in_quotes = command[i] + + # if this char matches the char by which we're in quotes + # , we're not in quotes + elif (command[i] == in_quotes + and (i > 0 and not(command[i-1] in escapes))): + in_quotes = 0 + + # if this char is an arg delimiter + # and we're not in quotes + # and the last char isn't an escape + # , this word is an argument + elif (command[i] in spaces and in_quotes == 0 + and (i > 0 and not(command[i-1] in escapes))): + parsed_command += [word] + word = "" + + elif (not(command[i] in escapes) or (i == len(command) - 1) + or not(command[i+1] in spaces + quotes)): + word += command[i] + + parsed_command += [word] + return [] if parsed_command == [''] else parsed_command + +def main(*args): + while True: + try: + command = input() + except: + break + + if command == ".": + break + + command = parse_command(command) + + for i in range(len(command)): + print("\t%d:\t%s" % (i, command[i])) + + return 0 if len(args) != 2 else args[0] + +if __name__ == "__main__": + sys.exit(main()) From 6630ad1e3f2fef8922f23ceff0187d651224645e Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:16:53 -0400 Subject: [PATCH 12/29] example module --- it/hello_world.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 it/hello_world.py diff --git a/it/hello_world.py b/it/hello_world.py new file mode 100644 index 0000000..4b1c040 --- /dev/null +++ b/it/hello_world.py @@ -0,0 +1,3 @@ +def main(buffer, command): + print("Hello, world!") + return buffer From 5b7fa586a081dac862a27cda3e774a7153a709e2 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:17:11 -0400 Subject: [PATCH 13/29] forcefully mark buffer as saved/unsaved --- it/saved.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 it/saved.py diff --git a/it/saved.py b/it/saved.py new file mode 100644 index 0000000..47f12d6 --- /dev/null +++ b/it/saved.py @@ -0,0 +1,15 @@ +def main(buffer, command): + if len(command) > 2: + print("?") + elif len(command) == 2: + if command[1].isdigit(): + buffer.saved = int(command[1]) + elif command[1].lower() in {"true","false"}: + buffer.saved = command[1].lower == "true" + else: + print("?") + else: + buffer.saved = not(buffer.saved) + print("Buffer marked as %sSAVED" % ("" if buffer.saved else "NOT ")) + + return buffer From 53b4c5cbcb9e3d4481c5c2cea4486954a5e1cad6 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:17:30 -0400 Subject: [PATCH 14/29] quit program --- it/Q.py | 6 +----- it/exit.py | 4 ++++ it/q.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 it/exit.py diff --git a/it/Q.py b/it/Q.py index aac715a..8ebad32 100644 --- a/it/Q.py +++ b/it/Q.py @@ -1,8 +1,4 @@ from sys import exit def main(buffer, argv): - if len(argv) > 1: - print("?") - else: - exit(0) - return buffer + return 0 diff --git a/it/exit.py b/it/exit.py new file mode 100644 index 0000000..9080618 --- /dev/null +++ b/it/exit.py @@ -0,0 +1,4 @@ +import sys + +def main(buffer, command): + sys.exit(0) diff --git a/it/q.py b/it/q.py index e73f7ec..f05cd12 100644 --- a/it/q.py +++ b/it/q.py @@ -1,8 +1,8 @@ from sys import exit def main(buffer, argv): - if len(argv) > 1 or not(buffer.saved): + if not(buffer.saved): print("?") else: - exit(0) + buffer = 0 return buffer From fbd72dcd700bbd0e506c9edd599359bf66e394f1 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:17:42 -0400 Subject: [PATCH 15/29] README --- it/README.md | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/it/README.md b/it/README.md index 2dfe946..aa84489 100644 --- a/it/README.md +++ b/it/README.md @@ -1,2 +1,54 @@ # it -ass end of ed + +*UNIX ed for a new age...* + +Just kidding, no need to use this over ed. +This is just my take on a line editor, heavily inspired by ed. + +For disambiguation it's suggested that this project be referred to as "ited", pronounced however one likes (suggested: "iht-ehd"). + +## Included commands + +### buffer + +This is where code relating to the Buffer class is kept but calling this from within the editor will allow you to view the current buffer's attributes. + +### dot + +Change the current position within the buffer. +### exit + +Will exit via sys.exit rather than telling main.py to break out of the main loop. Functionally identical to `Q`. + +### f + +Displays or changes the filename associated with the current buffer. + +### it + +Launches a new instance of `it`. + +It should be noted that quitting a nested instance of `it` through the usual means (`exit`, `q`, or `Q`) will exit all nested instances of `it` as well. +To return to a higher instance of `it`, send an EOF character via your terminal. In xterm this is CTRL+d. + +### parse\_command + +Used by `it`'s main Python module but calling this from within the editor will allow you to test the command argument splitting. + +### Q + +Quits. + +### q + +Quits, unless your current buffer is unsaved. + +### saved + +With no arguments given, flips the buffer's "saved" boolean attribute. +Otherwise, sets the buffer's "saved" attribute to the first argument or errors if multiple arguments given. + +## Making new commands + +Making new commands is very easy. +Read `buffer.py` and `it.py` (<100 LOC total). From 40ef42a24637eaae7bf56db12333a5cd1c612b49 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:25:45 -0400 Subject: [PATCH 16/29] execute external commands --- it/!.py | 11 +++++++++++ it/README.md | 4 ++++ 2 files changed, 15 insertions(+) create mode 100644 it/!.py diff --git a/it/!.py b/it/!.py new file mode 100644 index 0000000..29cdb9a --- /dev/null +++ b/it/!.py @@ -0,0 +1,11 @@ +import subprocess + +def main(buffer, command): + if len(command) == 1: + print("?") + else: + try: + print("[%d]" % subprocess.run(command[1:]).returncode) + except Exception as err: + print("%s" % str(err)) + return buffer diff --git a/it/README.md b/it/README.md index aa84489..00047c1 100644 --- a/it/README.md +++ b/it/README.md @@ -9,6 +9,10 @@ For disambiguation it's suggested that this project be referred to as "ited", pr ## Included commands +### ! + +Passes arguments to Python `subprocess.run`. + ### buffer This is where code relating to the Buffer class is kept but calling this from within the editor will allow you to view the current buffer's attributes. From b0db6717ca103c9226b4d6c76a83e11cc6566c24 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:26:03 -0400 Subject: [PATCH 17/29] works in progress --- it/i.py | 22 ++++++++++++++++++++++ it/p.py | 7 +++++++ 2 files changed, 29 insertions(+) create mode 100644 it/i.py create mode 100644 it/p.py diff --git a/it/i.py b/it/i.py new file mode 100644 index 0000000..1fe385c --- /dev/null +++ b/it/i.py @@ -0,0 +1,22 @@ +def main(buffer, command): + if len(command) > 1: + print("?") + return buffer + + i = "" + while len(i) < 1 or i[-1] != ".\n": + try: + i += input() + "\n" + # unintuitive behavior from ed + except KeyboardInterrupt: + print("?") + return buffer + except EOFError: + break + + if buffer.dot < buffer.index: + buffer.content = i + else: + buffer.content = + buffer.dot += i.count("\n") + return buffer diff --git a/it/p.py b/it/p.py new file mode 100644 index 0000000..34a6a21 --- /dev/null +++ b/it/p.py @@ -0,0 +1,7 @@ +def main(buffer, command): + if len(command) < 1 or len(command) > 2: + + if buffer.dot < buffer.index: + buffer.content = i + buffer.dot += len(i) + return buffer From 7626d24fad49097da8f3ae5a8576233921c4eeec Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:26:19 -0400 Subject: [PATCH 18/29] WIP --- it/dot.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 it/dot.py diff --git a/it/dot.py b/it/dot.py new file mode 100644 index 0000000..c45fe1a --- /dev/null +++ b/it/dot.py @@ -0,0 +1,20 @@ +def main(buffer, command): + if len(command) == 2 and (command[1].isdigit() or command[1] in {"^","$"}): + if command[1].isdigit(): + dot = int(command[1]) + elif command[1] == "^": + dot = buffer.carat() + elif command[1] == "$": + dot = buffer.dollar() + elif len(command) == 3 and command[1] in {"+","-"} and command[2].isdigit(): + if command[1] == "+": + dot = buffer.dot + int(command[2]) + elif command[1] == "-": + dot = buffer.dot - int(command[2]) + + if not("dot" in locals()) or dot < buffer.carat() or dot > buffer.dollar(): + print("?") + else: + buffer.dot = dot + + return buffer From 69184fc79a3150c085b9e5181bf38dc9fcd38fde Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:36:19 -0400 Subject: [PATCH 19/29] clean up root --- it/it | 3 +++ it/{ => src}/!.py | 0 it/{ => src}/Q.py | 0 it/{ => src}/buffer.py | 0 it/{ => src}/dot.py | 0 it/{ => src}/exit.py | 0 it/{ => src}/f.py | 0 it/{ => src}/hello_world.py | 0 it/{ => src}/i.py | 0 it/{ => src}/it.py | 0 it/{ => src}/p.py | 0 it/{ => src}/parse_command.py | 0 it/{ => src}/q.py | 0 it/{ => src}/saved.py | 0 14 files changed, 3 insertions(+) create mode 100755 it/it rename it/{ => src}/!.py (100%) rename it/{ => src}/Q.py (100%) rename it/{ => src}/buffer.py (100%) rename it/{ => src}/dot.py (100%) rename it/{ => src}/exit.py (100%) rename it/{ => src}/f.py (100%) rename it/{ => src}/hello_world.py (100%) rename it/{ => src}/i.py (100%) rename it/{ => src}/it.py (100%) rename it/{ => src}/p.py (100%) rename it/{ => src}/parse_command.py (100%) rename it/{ => src}/q.py (100%) rename it/{ => src}/saved.py (100%) diff --git a/it/it b/it/it new file mode 100755 index 0000000..de0f4b9 --- /dev/null +++ b/it/it @@ -0,0 +1,3 @@ +#!/bin/sh + +python3 src/it.py "$@" diff --git a/it/!.py b/it/src/!.py similarity index 100% rename from it/!.py rename to it/src/!.py diff --git a/it/Q.py b/it/src/Q.py similarity index 100% rename from it/Q.py rename to it/src/Q.py diff --git a/it/buffer.py b/it/src/buffer.py similarity index 100% rename from it/buffer.py rename to it/src/buffer.py diff --git a/it/dot.py b/it/src/dot.py similarity index 100% rename from it/dot.py rename to it/src/dot.py diff --git a/it/exit.py b/it/src/exit.py similarity index 100% rename from it/exit.py rename to it/src/exit.py diff --git a/it/f.py b/it/src/f.py similarity index 100% rename from it/f.py rename to it/src/f.py diff --git a/it/hello_world.py b/it/src/hello_world.py similarity index 100% rename from it/hello_world.py rename to it/src/hello_world.py diff --git a/it/i.py b/it/src/i.py similarity index 100% rename from it/i.py rename to it/src/i.py diff --git a/it/it.py b/it/src/it.py similarity index 100% rename from it/it.py rename to it/src/it.py diff --git a/it/p.py b/it/src/p.py similarity index 100% rename from it/p.py rename to it/src/p.py diff --git a/it/parse_command.py b/it/src/parse_command.py similarity index 100% rename from it/parse_command.py rename to it/src/parse_command.py diff --git a/it/q.py b/it/src/q.py similarity index 100% rename from it/q.py rename to it/src/q.py diff --git a/it/saved.py b/it/src/saved.py similarity index 100% rename from it/saved.py rename to it/src/saved.py From 1e64786a376b8179a8af732b95b5197805c7e8da Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 17:47:17 -0400 Subject: [PATCH 20/29] content --- it/src/buffer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/it/src/buffer.py b/it/src/buffer.py index 8a6a4d2..11533e8 100755 --- a/it/src/buffer.py +++ b/it/src/buffer.py @@ -12,7 +12,7 @@ class Buffer: # all the information about the current file # Rather than get the content as it's presented in the buffer object # (as one big string), get it as a list. def content_list(self): - return self.split(self.delimiter) + return self.content.split(self.delimiter) # Build a string with the same format as the buffer.content and set the # buffer.content to that string, from the kind of list output by From e1c2f0125668cd0e78b906fc0b3f181fff88347c Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 20:14:59 -0400 Subject: [PATCH 21/29] get rid of unnecessary imports --- it/src/it.py | 1 - 1 file changed, 1 deletion(-) diff --git a/it/src/it.py b/it/src/it.py index 50ff947..988acf7 100755 --- a/it/src/it.py +++ b/it/src/it.py @@ -1,5 +1,4 @@ #!/usr/bin/env python3 -import importlib import sys from buffer import Buffer from parse_command import parse_command From fc390b3a146dd30aa6634c410964b4c1fc3bd902 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Sun, 30 May 2021 20:15:17 -0400 Subject: [PATCH 22/29] improve printing --- it/src/buffer.py | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/it/src/buffer.py b/it/src/buffer.py index 11533e8..72da5f2 100755 --- a/it/src/buffer.py +++ b/it/src/buffer.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 import importlib -import sys -from parse_command import parse_command class Buffer: # all the information about the current file def carat(self): @@ -46,20 +44,30 @@ class Buffer: # all the information about the current file return self.content.count(self.delimiter) def __init__(self): - self.content = '' # content of the file - self.delimiter = '\n' # line delimiter - self.filename = "" # name of where we'll save the file - self.index = 0 # indexing of dot + self.content = '' # content of the file + self.delimiter = '\n' # line delimiter + self.filename = "" # name of where we'll save the file + self.index = 0 # indexing of dot self.dot = self.index - 1 # invalid position to start - self.modules = {} - self.saved = 1 # bool that says whether or not we have saved the file + self.modules = {} # see it.py + self.saved = 1 # is buffer saved? def main(buffer, command): if len(command) == 1: - command = command + list(vars(buffer)) + command += list(vars(buffer)) for attrib in command[1:]: if attrib in list(vars(buffer)): - print("%s:\t%s" % (attrib, vars(buffer)[attrib])) + val = vars(buffer)[attrib] + # so self.modules shows as + if not(type(val) is int or type(val) is str): + val = str(type(val)) + # special case usually for self.delimiter + elif val == "\n": + val = "" + # only affects it if type(val) is int + else: + val = str(val) + print("%s:\t%s" % (attrib, val)) else: - print("No attribute: %s" % attrib) + print("No attribute: %s\n" % attrib, end='') return buffer From a3812c78dcdc68070a78e3a640765679ed2c2b0b Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Mon, 31 May 2021 14:57:30 -0400 Subject: [PATCH 23/29] module versioning --- it/src/it.py | 4 ++++ it/src/version.py | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 it/src/version.py diff --git a/it/src/it.py b/it/src/it.py index 988acf7..dd6cb40 100755 --- a/it/src/it.py +++ b/it/src/it.py @@ -3,6 +3,10 @@ import sys from buffer import Buffer from parse_command import parse_command +def version(): + print("it.py line editor; ALPHA 2021") + return None + def get_command(): try: return parse_command(input()) diff --git a/it/src/version.py b/it/src/version.py new file mode 100644 index 0000000..8808343 --- /dev/null +++ b/it/src/version.py @@ -0,0 +1,16 @@ +def version(): + print("it.py version querying; ALPHA 2021") + return None + +def main(buffer, command): + if len(command) > 1: + fetching = command[1:] + else: + fetching = ["it"] + for module in fetching: + if module in list(buffer.modules) or buffer.import_module_(module): + try: + buffer.modules[module].version() + except AttributeError as err: + print(err) + return buffer From 7c39e9afd310699f8f1d8efab2fffd28158f2be8 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Mon, 31 May 2021 15:02:35 -0400 Subject: [PATCH 24/29] Manual module loading/reloading --- it/src/load_module.py | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 it/src/load_module.py diff --git a/it/src/load_module.py b/it/src/load_module.py new file mode 100644 index 0000000..df02050 --- /dev/null +++ b/it/src/load_module.py @@ -0,0 +1,8 @@ +def main(buffer, command): + if len(command) == 1: + print("?") + else: + for module in command[1:]: + if buffer.import_module_(module): + print("Loaded %s" % module) + return buffer From 60e04e42f4e60ed6d3866a182b04f242d3e71a0e Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Mon, 31 May 2021 15:03:34 -0400 Subject: [PATCH 25/29] moar readme --- it/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/it/README.md b/it/README.md index 00047c1..0c32f40 100644 --- a/it/README.md +++ b/it/README.md @@ -35,6 +35,10 @@ Launches a new instance of `it`. It should be noted that quitting a nested instance of `it` through the usual means (`exit`, `q`, or `Q`) will exit all nested instances of `it` as well. To return to a higher instance of `it`, send an EOF character via your terminal. In xterm this is CTRL+d. +### load\_module + +Manually loads a module, whether or not it was already loaded. + ### parse\_command Used by `it`'s main Python module but calling this from within the editor will allow you to test the command argument splitting. @@ -52,6 +56,11 @@ Quits, unless your current buffer is unsaved. With no arguments given, flips the buffer's "saved" boolean attribute. Otherwise, sets the buffer's "saved" attribute to the first argument or errors if multiple arguments given. +### version + +Prints the versions of given modules. +If no modules are specified, prints the version of module "it". + ## Making new commands Making new commands is very easy. From b03ff6813cc265afa1c2410d89ab8622df0e43b0 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Mon, 31 May 2021 22:59:48 -0400 Subject: [PATCH 26/29] separate get_command --- it/src/get_command.py | 13 +++++++++++++ it/src/it.py | 10 +--------- 2 files changed, 14 insertions(+), 9 deletions(-) create mode 100755 it/src/get_command.py diff --git a/it/src/get_command.py b/it/src/get_command.py new file mode 100755 index 0000000..83c6fbf --- /dev/null +++ b/it/src/get_command.py @@ -0,0 +1,13 @@ +from parse_command import parse_command + +def get_command(prompt): + try: + return parse_command(input(prompt)) + except KeyboardInterrupt: # bastard behavior from ed + pass + except EOFError: + return 0 + +def main(buffer, command): + print("Command returned as %s" % get_command("Input command (won't be executed): ")) + return buffer diff --git a/it/src/it.py b/it/src/it.py index dd6cb40..8056c36 100755 --- a/it/src/it.py +++ b/it/src/it.py @@ -1,20 +1,12 @@ #!/usr/bin/env python3 import sys from buffer import Buffer -from parse_command import parse_command +from get_command import get_command def version(): print("it.py line editor; ALPHA 2021") return None -def get_command(): - try: - return parse_command(input()) - except KeyboardInterrupt: # bastard behavior from ed - pass - except EOFError: - return 0 - def main(buffer, supplied_command): if supplied_command != [] and len(supplied_command) > 1: command = supplied_command[1:] From 39c3606255844cfc9e84dd6606e09a9ac0d3dead Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Mon, 31 May 2021 23:18:42 -0400 Subject: [PATCH 27/29] fix bugs, start work on env vars --- it/src/buffer.py | 7 ++++--- it/src/config.py | 10 ++++++++++ it/src/it.py | 4 ++-- 3 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 it/src/config.py diff --git a/it/src/buffer.py b/it/src/buffer.py index 72da5f2..18ac6b1 100755 --- a/it/src/buffer.py +++ b/it/src/buffer.py @@ -10,7 +10,7 @@ class Buffer: # all the information about the current file # Rather than get the content as it's presented in the buffer object # (as one big string), get it as a list. def content_list(self): - return self.content.split(self.delimiter) + return self.content.rstrip(self.delimiter).split(self.delimiter) # Build a string with the same format as the buffer.content and set the # buffer.content to that string, from the kind of list output by @@ -44,8 +44,9 @@ class Buffer: # all the information about the current file return self.content.count(self.delimiter) def __init__(self): - self.content = '' # content of the file - self.delimiter = '\n' # line delimiter + self.content = "" # content of the file + self.delimiter = "\n" # line delimiter + self.env = {} # configuration dictionary self.filename = "" # name of where we'll save the file self.index = 0 # indexing of dot self.dot = self.index - 1 # invalid position to start diff --git a/it/src/config.py b/it/src/config.py new file mode 100644 index 0000000..32021ed --- /dev/null +++ b/it/src/config.py @@ -0,0 +1,10 @@ +def main(buffer, command): + # hard-coded for consistency across multiple people's configs + config_delimiter = "\t" + for i in range(len(buffer.content_list())): + line = buffer.content_list()[i].split(config_delimiter) + if len(line) != 2: + print("%s: %d: Bad column quantity" % (command[0], i+buffer.index)) + else: + buffer.env[line[0]] = line[1] + return buffer diff --git a/it/src/it.py b/it/src/it.py index 8056c36..350ca84 100755 --- a/it/src/it.py +++ b/it/src/it.py @@ -11,7 +11,7 @@ def main(buffer, supplied_command): if supplied_command != [] and len(supplied_command) > 1: command = supplied_command[1:] else: - command = get_command() + command = get_command("") while True: # EOFError in get_command(); ^D @@ -26,7 +26,7 @@ def main(buffer, supplied_command): if type(buffer) is int: break - command = get_command() + command = get_command("") return buffer From 212d349a8a45acb84f463d5395c028490702cea6 Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Wed, 2 Jun 2021 12:11:02 -0400 Subject: [PATCH 28/29] unmark as executable --- it/src/buffer.py | 0 it/src/get_command.py | 0 it/src/parse_command.py | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 it/src/buffer.py mode change 100755 => 100644 it/src/get_command.py mode change 100755 => 100644 it/src/parse_command.py diff --git a/it/src/buffer.py b/it/src/buffer.py old mode 100755 new mode 100644 diff --git a/it/src/get_command.py b/it/src/get_command.py old mode 100755 new mode 100644 diff --git a/it/src/parse_command.py b/it/src/parse_command.py old mode 100755 new mode 100644 From b96038db130b9be1b2544366df4876b087af2fdd Mon Sep 17 00:00:00 2001 From: Deven Blake Date: Thu, 3 Jun 2021 18:29:32 -0400 Subject: [PATCH 29/29] potentially works --- it/src/i.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/it/src/i.py b/it/src/i.py index 1fe385c..89ab7c0 100644 --- a/it/src/i.py +++ b/it/src/i.py @@ -3,10 +3,10 @@ def main(buffer, command): print("?") return buffer - i = "" - while len(i) < 1 or i[-1] != ".\n": + i = [] + while True: try: - i += input() + "\n" + line = input() # unintuitive behavior from ed except KeyboardInterrupt: print("?") @@ -14,9 +14,18 @@ def main(buffer, command): except EOFError: break + if line == ".": + break + + i.append(line) + if buffer.dot < buffer.index: - buffer.content = i + buffer.content_set_list(i) else: - buffer.content = - buffer.dot += i.count("\n") + buffer.content_set_list( + buffer.content_list()[:buffer.dot] + + i + + buffer.content_list()[buffer.dot:] + ) + buffer.dot += len(i) return buffer