diff --git a/Pyboard Editor.doc b/Pyboard Editor.doc index 2511753..16cdd74 100644 Binary files a/Pyboard Editor.doc and b/Pyboard Editor.doc differ diff --git a/Pyboard Editor.pdf b/Pyboard Editor.pdf index 4a55e73..0da7299 100644 Binary files a/Pyboard Editor.pdf and b/Pyboard Editor.pdf differ diff --git a/README.md b/README.md index 9574b6e..3bbf9c2 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ a) find_in_file() supporting regular expressions, b) line_edit() supporting the cursor left/right/home/end keys, and c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, but maybe useful) - strip.sh: sample Shell script which creates the different variants out of pye.py using cpp, including variants of wipye.py with either speed up scrolling or support replace or support got bracket. -- pye_vt.py: a variant of pye.py, where all directly screen related functions are placed into a separate class. That's a better style, however it uses more memory. +- pye_vt.py: a variant of pye.py, where all directly screen related functions are placed into a separate class. That's a better style, however it uses more memory. This file is just given as exmaple and not maintained. **Short Version History** @@ -150,7 +150,7 @@ anyhow called one after the other, resulting in a enormous long function handlin - Ctrl-O opens a new file/buffer. **2.1** Some shrinking for WiPy -- Make Indent/Un-Indent optional in the WiPy version, to allow all variants to get compile w/o running out of memory. +- Make Indent/Un-Indent optional in the WiPy version, to allow all variants to get compiled w/o running out of memory. The final code saving is just a few hundred bytes, so it's still not clear to me why these few extra lines dont't fit. - Fixing a glitch which added an extra line when un-doing the delete of all lines - Some shifting around of code lines @@ -158,3 +158,10 @@ The final code saving is just a few hundred bytes, so it's still not clear to me - Removed the extra indent after ':' as the last char on the line. More confusing than helpful. - Update of the doc file +**2.2** Further cleaning and some slight improvements +- Moved error catching one level up to the function pye(), catching load-file errors too. +- If open file names a directory, the list of files is loaded to the edit buffer. +- Ctrl-V in line edit mode gets the first line of the paste buffer. +- The WiPy version does not support undo for Indent/Un-indent, even if Indent is enabled. It is too memory consuming at runtime. It's questionable whether this is needed at all. +- And of course: update of the doc file + diff --git a/pe.py b/pe.py index c80c2d5..f89db94 100644 --- a/pe.py +++ b/pe.py @@ -119,8 +119,7 @@ def redraw(self, flag): self.mouse_reporting(True) if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): while True: in_buffer = self.rd() @@ -214,6 +213,11 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' + elif key == 0x16: + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -475,8 +479,7 @@ def handle_edit_keys(self, key): self.cur_line = cur_line self.message = "'{}' replaced {} times".format(pat, count) elif key == 0x18: - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == 0x04: if self.mark != None: lrange = self.line_range() @@ -484,8 +487,7 @@ def handle_edit_keys(self, key): self.mark = None elif key == 0x16: if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, 0, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer self.total_lines += len(Editor.yank_buffer) @@ -510,8 +512,7 @@ def handle_edit_keys(self, key): else: del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == 0x05: self.redraw(True) @@ -521,48 +522,44 @@ def edit_loop(self): self.total_lines = len(self.content) self.redraw(self.message == "") while True: - try: - if not self.rd_any(): - self.display_window() - key = self.get_input() - self.message = '' - if key == 0x11: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue - self.mouse_reporting(False) - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (0x17, 0x0f): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + if not self.rd_any(): + self.display_window() + key = self.get_input() + self.message = '' + if key == 0x11: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue + self.mouse_reporting(False) + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (0x17, 0x0f): + return key + else: self.handle_edit_keys(key) def packtabs(self, s): from _io import StringIO sb = StringIO() for i in range(0, len(s), 8): c = s[i:i + 8] cr = c.rstrip(" ") - if c != cr: - sb.write(cr + "\t") - else: - sb.write(c) + if c != cr: sb.write(cr + "\t") + else: sb.write(c) return sb.getvalue() def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) def put_file(self, fname): @@ -593,33 +590,34 @@ def expandtabs(s): return s def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): gc.collect() + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f index += 1 - else: - slot = [Editor(tab_size, undo)] Editor.init_tty(device, baud) index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() - if key == 0x11: - if len(slot) == 1: - break - del slot[index] - elif key == 0x0f: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == 0x17: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() + if key == 0x11: + if len(slot) == 1: + break + del slot[index] + elif key == 0x0f: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == 0x17: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) Editor.deinit_tty() Editor.yank_buffer = [] return slot[0].content if (slot[0].fname == "") else slot[0].fname diff --git a/pe2.py b/pe2.py index 8f01af8..7ae49b8 100644 --- a/pe2.py +++ b/pe2.py @@ -119,8 +119,7 @@ def redraw(self, flag): self.mouse_reporting(True) if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): while True: in_buffer = self.rd() @@ -213,6 +212,11 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' + elif key == 0x16: + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -483,8 +487,7 @@ def handle_edit_keys(self, key): self.cur_line = cur_line self.message = "'{}' replaced {} times".format(pat, count) elif key == 0x18: - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == 0x04: if self.mark != None: lrange = self.line_range() @@ -492,8 +495,7 @@ def handle_edit_keys(self, key): self.mark = None elif key == 0x16: if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, 0, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer self.total_lines += len(Editor.yank_buffer) @@ -518,8 +520,7 @@ def handle_edit_keys(self, key): else: del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == 0x05: self.redraw(True) @@ -529,48 +530,44 @@ def edit_loop(self): self.total_lines = len(self.content) self.redraw(self.message == "") while True: - try: - if not self.rd_any(): - self.display_window() - key = self.get_input() - self.message = '' - if key == 0x11: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue - self.mouse_reporting(False) - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (0x17, 0x0f): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + if not self.rd_any(): + self.display_window() + key = self.get_input() + self.message = '' + if key == 0x11: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue + self.mouse_reporting(False) + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (0x17, 0x0f): + return key + else: self.handle_edit_keys(key) def packtabs(self, s): from _io import StringIO sb = StringIO() for i in range(0, len(s), 8): c = s[i:i + 8] cr = c.rstrip(" ") - if c != cr: - sb.write(cr + "\t") - else: - sb.write(c) + if c != cr: sb.write(cr + "\t") + else: sb.write(c) return sb.getvalue() def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) def put_file(self, fname): @@ -601,33 +598,34 @@ def expandtabs(s): return s def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): gc.collect() + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f index += 1 - else: - slot = [Editor(tab_size, undo)] Editor.init_tty(device, baud) index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() - if key == 0x11: - if len(slot) == 1: - break - del slot[index] - elif key == 0x0f: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == 0x17: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() + if key == 0x11: + if len(slot) == 1: + break + del slot[index] + elif key == 0x0f: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == 0x17: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) Editor.deinit_tty() Editor.yank_buffer = [] return slot[0].content if (slot[0].fname == "") else slot[0].fname diff --git a/pemin.py b/pemin.py index 7278e18..0871f88 100644 --- a/pemin.py +++ b/pemin.py @@ -18,10 +18,10 @@ class Editor: b"\x7f" : 0x08, b"\x1b[3~": 0x7f, b"\x1b[Z" : 0x15, - b"\x0b" : 0xfffd, } yank_buffer = [] find_pattern = "" + replc_pattern = "" def __init__(self, tab_size, undo_limit): self.top_line = self.cur_line = self.row = self.col = self.margin = 0 self.tab_size = tab_size @@ -88,8 +88,7 @@ def redraw(self, flag): self.row = min(Editor.height - 1, self.row) if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): while True: in_buffer = self.rd() @@ -173,6 +172,11 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' + elif key == 0x16: + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -269,53 +273,9 @@ def handle_edit_keys(self, key): if line: self.cur_line = int(line) - 1 self.row = Editor.height >> 1 - elif key == 0x14: - self.cur_line = 0 - elif key == 0x02: - self.cur_line = self.total_lines - 1 - self.row = Editor.height - 1 elif key == 0x01: if True: self.autoindent = 'y' if self.autoindent != 'y' else 'n' - elif key == 0xfffd: - if self.col < len(l): - opening = "([{<" - closing = ")]}>" - level = 0 - pos = self.col - srch = l[pos] - i = opening.find(srch) - if i >= 0: - pos += 1 - match = closing[i] - for i in range(self.cur_line, self.total_lines): - for c in range(pos, len(self.content[i])): - if self.content[i][c] == match: - if level == 0: - self.cur_line, self.col = i, c - return True - else: - level -= 1 - elif self.content[i][c] == srch: - level += 1 - pos = 0 - else: - i = closing.find(srch) - if i >= 0: - pos -= 1 - match = opening[i] - for i in range(self.cur_line, -1, -1): - for c in range(pos, -1, -1): - if self.content[i][c] == match: - if level == 0: - self.cur_line, self.col = i, c - return True - else: - level -= 1 - elif self.content[i][c] == srch: - level += 1 - if i > 0: - pos = len(self.content[i - 1]) - 1 elif key == 0x0c: self.mark = self.cur_line if self.mark == None else None elif key == 0x0a: @@ -330,19 +290,66 @@ def handle_edit_keys(self, key): self.total_lines += 1 self.col = ni elif key == 0x09: + if self.mark == None: ni = self.tab_size - self.col % self.tab_size self.undo_add(self.cur_line, [l], 0x09) self.content[self.cur_line] = l[:self.col] + ' ' * ni + l[self.col:] self.col += ni + else: + lrange = self.line_range() + for i in range(lrange[0],lrange[1]): + if len(self.content[i]) > 0: + self.content[i] = ' ' * (self.tab_size - self.spaces(self.content[i]) % self.tab_size) + self.content[i] elif key == 0x15: + if self.mark == None: ni = min((self.col - 1) % self.tab_size + 1, self.spaces(l, self.col)) if ni > 0: self.undo_add(self.cur_line, [l], 0x15) self.content[self.cur_line] = l[:self.col - ni] + l[self.col:] self.col -= ni + else: + lrange = self.line_range() + for i in range(lrange[0],lrange[1]): + ns = self.spaces(self.content[i]) + if ns > 0: + self.content[i] = self.content[i][(ns - 1) % self.tab_size + 1:] + elif key == 0x12: + count = 0 + pat = self.line_edit("Replace: ", Editor.find_pattern) + if pat: + rpat = self.line_edit("With: ", Editor.replc_pattern) + if rpat != None: + Editor.replc_pattern = rpat + q = '' + cur_line = self.cur_line + if self.mark != None: + (self.cur_line, end_line) = self.line_range() + self.col = 0 + else: + end_line = self.total_lines + self.message = "Replace (yes/No/all/quit) ? " + while True: + ni = self.find_in_file(pat, self.col, end_line) + if ni: + if q != 'a': + self.display_window() + key = self.get_input() + q = chr(key).lower() + if q == 'q' or key == 0x11: + break + elif q in ('a','y'): + self.undo_add(self.cur_line, [self.content[self.cur_line]], 0) + self.content[self.cur_line] = self.content[self.cur_line][:self.col] + rpat + self.content[self.cur_line][self.col + ni:] + self.col += len(rpat) + count += 1 + else: + self.col += 1 + else: + break + self.cur_line = cur_line + self.message = "'{}' replaced {} times".format(pat, count) elif key == 0x18: - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == 0x04: if self.mark != None: lrange = self.line_range() @@ -350,8 +357,7 @@ def handle_edit_keys(self, key): self.mark = None elif key == 0x16: if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, 0, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer self.total_lines += len(Editor.yank_buffer) @@ -376,8 +382,7 @@ def handle_edit_keys(self, key): else: del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == 0x05: self.redraw(True) @@ -387,36 +392,34 @@ def edit_loop(self): self.total_lines = len(self.content) self.redraw(self.message == "") while True: - try: - if not self.rd_any(): - self.display_window() - key = self.get_input() - self.message = '' - if key == 0x11: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (0x17, 0x0f): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + if not self.rd_any(): + self.display_window() + key = self.get_input() + self.message = '' + if key == 0x11: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (0x17, 0x0f): + return key + else: self.handle_edit_keys(key) def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) def put_file(self, fname): @@ -444,33 +447,34 @@ def expandtabs(s): return s def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): gc.collect() + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f index += 1 - else: - slot = [Editor(tab_size, undo)] Editor.init_tty(device, baud) index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() - if key == 0x11: - if len(slot) == 1: - break - del slot[index] - elif key == 0x0f: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == 0x17: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() + if key == 0x11: + if len(slot) == 1: + break + del slot[index] + elif key == 0x0f: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == 0x17: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) Editor.deinit_tty() Editor.yank_buffer = [] return slot[0].content if (slot[0].fname == "") else slot[0].fname diff --git a/pye.py b/pye.py index 7e173a9..cf63bec 100644 --- a/pye.py +++ b/pye.py @@ -268,10 +268,8 @@ def rd_any(self): def rd(self): while True: - try: - return sys.stdin.read(1).encode() - except: - pass + try: return sys.stdin.read(1).encode() + except: pass ## @staticmethod ## def init_tty(device, baud): @@ -345,8 +343,7 @@ def redraw(self, flag): #endif if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): ## read from interface/keyboard one byte each and match against function keys while True: @@ -431,7 +428,6 @@ def line_range(self): return ((self.mark, self.cur_line + 1) if self.mark < self.cur_line else (self.cur_line, self.mark + 1)) - def line_edit(self, prompt, default): ## simple one: only 4 fcts self.goto(Editor.height, 0) self.hilite(1) @@ -454,6 +450,11 @@ def line_edit(self, prompt, default): ## simple one: only 4 fcts elif key == KEY_DELETE: ## Delete prev. Entry self.wr('\b \b' * len(res)) res = '' + elif key == KEY_ZAP: ## Get from paste buffer + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: ## character to be added if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -605,11 +606,13 @@ def handle_edit_keys(self, key): ## keys which change content if line: self.cur_line = int(line) - 1 self.row = Editor.height >> 1 +#ifndef BASIC elif key == KEY_FIRST: ## first line self.cur_line = 0 elif key == KEY_LAST: ## last line self.cur_line = self.total_lines - 1 self.row = Editor.height - 1 ## will be fixed if required +#endif elif key == KEY_TOGGLE: ## Toggle Autoindent/Statusline/Search case if True: #ifndef BASIC @@ -712,7 +715,9 @@ def handle_edit_keys(self, key): ## keys which change content #ifdef INDENT else: lrange = self.line_range() +#ifndef BASIC self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], KEY_INDENT, lrange[1] - lrange[0]) ## undo replaces +#endif for i in range(lrange[0],lrange[1]): if len(self.content[i]) > 0: self.content[i] = ' ' * (self.tab_size - self.spaces(self.content[i]) % self.tab_size) + self.content[i] @@ -729,7 +734,9 @@ def handle_edit_keys(self, key): ## keys which change content #ifdef INDENT else: lrange = self.line_range() +#ifndef BASIC self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], KEY_UNDENT, lrange[1] - lrange[0]) ## undo replaces +#endif for i in range(lrange[0],lrange[1]): ns = self.spaces(self.content[i]) if ns > 0: @@ -773,8 +780,7 @@ def handle_edit_keys(self, key): ## keys which change content self.message = "'{}' replaced {} times".format(pat, count) #endif elif key == KEY_YANK: # delete line or line(s) into buffer - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == KEY_DUP: # copy line(s) into buffer if self.mark != None: lrange = self.line_range() @@ -782,8 +788,7 @@ def handle_edit_keys(self, key): ## keys which change content self.mark = None elif key == KEY_ZAP: ## insert buffer if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, KEY_NONE, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer # insert lines self.total_lines += len(Editor.yank_buffer) @@ -808,8 +813,7 @@ def handle_edit_keys(self, key): ## keys which change content else: ## delete lines del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) ## brute force - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == KEY_REDRAW: self.redraw(True) @@ -821,32 +825,29 @@ def edit_loop(self): ## main editing loop self.redraw(self.message == "") while True: - try: - if not self.rd_any(): ## skip update if a char is waiting - self.display_window() ## Update & display window - key = self.get_input() ## Get Char of Fct-key code - self.message = '' ## clear message - - if key == KEY_QUIT: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue + if not self.rd_any(): ## skip update if a char is waiting + self.display_window() ## Update & display window + key = self.get_input() ## Get Char of Fct-key code + self.message = '' ## clear message + + if key == KEY_QUIT: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue #ifdef SCROLL - self.scroll_region(0) + self.scroll_region(0) #endif #ifdef MOUSE - self.mouse_reporting(False) ## disable mouse reporting + self.mouse_reporting(False) ## disable mouse reporting #endif - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (KEY_NEXT, KEY_GET): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (KEY_NEXT, KEY_GET): + return key + else: self.handle_edit_keys(key) ## packtabs: replace sequence of space by tab #ifndef BASIC @@ -856,20 +857,23 @@ def packtabs(self, s): for i in range(0, len(s), 8): c = s[i:i + 8] cr = c.rstrip(" ") - if c != cr: ## Spaces at the end of a section - sb.write(cr + "\t") ## replace by tab - else: - sb.write(c) + if c != cr: sb.write(cr + "\t") ## Spaces at the end of a section + else: sb.write(c) return sb.getvalue() #endif ## Read file into content def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): ## Dir + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: #ifdef LINUX + pass if sys.implementation.name == "cpython": with open(fname, errors="ignore") as f: self.content = f.readlines() @@ -877,9 +881,6 @@ def get_file(self, fname): #endif with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): ## strip and convert self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) @@ -918,36 +919,37 @@ def expandtabs(s): def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): ## prepare content gc.collect() ## all (memory) is mine + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: ## String = non-empty Filename slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f ## non-empty list of strings -> edit index += 1 - else: - slot = [Editor(tab_size, undo)] ## edit #if defined(PYBOARD) || defined(LINUX) Editor.init_tty(device, baud) #endif index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() ## edit buffer - if key == KEY_QUIT: - if len(slot) == 1: ## the last man standing is kept - break - del slot[index] - elif key == KEY_GET: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == KEY_NEXT: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() ## edit buffer + if key == KEY_QUIT: + if len(slot) == 1: ## the last man standing is kept + break + del slot[index] + elif key == KEY_GET: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == KEY_NEXT: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) ## All windows closed, clean up #if defined(PYBOARD) || defined(LINUX) Editor.deinit_tty() diff --git a/pye2.py b/pye2.py index 599d422..128828f 100644 --- a/pye2.py +++ b/pye2.py @@ -209,14 +209,14 @@ def rd(self): return b'\x05' @staticmethod - def init_tty(self, device, baud): + def init_tty(device, baud): Editor.org_termios = termios.tcgetattr(device) tty.setraw(device) Editor.sdev = device Editor.winch = False @staticmethod - def deinit_tty(self): + def deinit_tty(): termios.tcsetattr(Editor.sdev, termios.TCSANOW, Editor.org_termios) @staticmethod @@ -267,10 +267,8 @@ def rd_any(self): def rd(self): while True: - try: - return sys.stdin.read(1).encode() - except: - pass + try: return sys.stdin.read(1).encode() + except: pass ## @staticmethod ## def init_tty(self, device, baud): @@ -344,8 +342,7 @@ def redraw(self, flag): #endif if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): ## read from interface/keyboard one byte each and match against function keys while True: @@ -429,7 +426,6 @@ def line_range(self): return ((self.mark, self.cur_line + 1) if self.mark < self.cur_line else (self.cur_line, self.mark + 1)) - def line_edit(self, prompt, default): ## simple one: only 4 fcts self.goto(Editor.height, 0) self.hilite(1) @@ -452,6 +448,11 @@ def line_edit(self, prompt, default): ## simple one: only 4 fcts elif key == KEY_DELETE: ## Delete prev. Entry self.wr('\b \b' * len(res)) res = '' + elif key == KEY_ZAP: ## Get from paste buffer + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: ## character to be added if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -601,11 +602,13 @@ def handle_edit_keys(self, key): ## keys which change content if line: self.cur_line = int(line) - 1 self.row = Editor.height >> 1 +#ifndef BASIC elif key == KEY_FIRST: ## first line self.cur_line = 0 elif key == KEY_LAST: ## last line self.cur_line = self.total_lines - 1 self.row = Editor.height - 1 ## will be fixed if required +#endif elif key == KEY_TOGGLE: ## Toggle Autoindent/Statusline/Search case if True: #ifndef BASIC @@ -709,7 +712,9 @@ def handle_edit_keys(self, key): ## keys which change content #ifdef INDENT else: lrange = self.line_range() +#ifndef BASIC self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], KEY_INDENT, lrange[1] - lrange[0]) ## undo replaces +#endif for i in range(lrange[0],lrange[1]): if len(self.content[i]) > 0: self.content[i] = ' ' * (self.tab_size - self.spaces(self.content[i]) % self.tab_size) + self.content[i] @@ -730,7 +735,9 @@ def handle_edit_keys(self, key): ## keys which change content #ifdef INDENT else: lrange = self.line_range() +#ifndef BASIC self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], KEY_UNDENT, lrange[1] - lrange[0]) ## undo replaces +#endif for i in range(lrange[0],lrange[1]): ns = self.spaces(self.content[i]) if ns > 0: @@ -774,17 +781,15 @@ def handle_edit_keys(self, key): ## keys which change content self.message = "'{}' replaced {} times".format(pat, count) #endif elif key == KEY_YANK: # delete line or line(s) into buffer - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == KEY_DUP: # copy line(s) into buffer - if self.mark != None: + if self.mark != None: lrange = self.line_range() Editor.yank_buffer = self.content[lrange[0]:lrange[1]] self.mark = None elif key == KEY_ZAP: ## insert buffer if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, KEY_NONE, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer # insert lines self.total_lines += len(Editor.yank_buffer) @@ -809,8 +814,7 @@ def handle_edit_keys(self, key): ## keys which change content else: ## delete lines del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) ## brute force - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == KEY_REDRAW: self.redraw(True) @@ -822,32 +826,29 @@ def edit_loop(self): ## main editing loop self.redraw(self.message == "") while True: - try: - if not self.rd_any(): ## skip update if a char is waiting - self.display_window() ## Update & display window - key = self.get_input() ## Get Char of Fct-key code - self.message = '' ## clear message - - if key == KEY_QUIT: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue + if not self.rd_any(): ## skip update if a char is waiting + self.display_window() ## Update & display window + key = self.get_input() ## Get Char of Fct-key code + self.message = '' ## clear message + + if key == KEY_QUIT: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue #ifdef SCROLL - self.scroll_region(0) + self.scroll_region(0) #endif #ifdef MOUSE - self.mouse_reporting(False) ## disable mouse reporting + self.mouse_reporting(False) ## disable mouse reporting #endif - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (KEY_NEXT, KEY_GET): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (KEY_NEXT, KEY_GET): + return key + else: self.handle_edit_keys(key) ## packtabs: replace sequence of space by tab #ifndef BASIC @@ -857,20 +858,23 @@ def packtabs(self, s): for i in range(0, len(s), 8): c = s[i:i + 8] cr = c.rstrip(" ") - if c != cr: ## Spaces at the end of a section - sb.write(cr + "\t") ## replace by tab - else: - sb.write(c) + if c != cr: sb.write(cr + "\t") ## Spaces at the end of a section + else: sb.write(c) return sb.getvalue() #endif ## Read file into content def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: #ifdef LINUX + pass if sys.implementation.name == "cpython": with open(fname, errors="ignore") as f: self.content = f.readlines() @@ -878,9 +882,6 @@ def get_file(self, fname): #endif with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): ## strip and convert self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) @@ -919,36 +920,37 @@ def expandtabs(s): def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): ## prepare content gc.collect() ## all (memory) is mine + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: ## String = non-empty Filename slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f ## non-empty list of strings -> edit index += 1 - else: - slot = [Editor(tab_size, undo)] ## edit #if defined(PYBOARD) || defined(LINUX) Editor.init_tty(device, baud) #endif index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() ## edit buffer - if key == KEY_QUIT: - if len(slot) == 1: ## the last man standing is kept - break - del slot[index] - elif key == KEY_GET: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == KEY_NEXT: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() ## edit buffer + if key == KEY_QUIT: + if len(slot) == 1: ## the last man standing is kept + break + del slot[index] + elif key == KEY_GET: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == KEY_NEXT: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) ## All windows closed, clean up #if defined(PYBOARD) || defined(LINUX) Editor.deinit_tty() diff --git a/strip.sh b/strip.sh index 98e378d..6f37615 100755 --- a/strip.sh +++ b/strip.sh @@ -1,10 +1,11 @@ # !sh cpp -D BASIC -D WIPY -D DEFINES pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye.py +cpp -D BASIC -D WIPY -D DEFINES -D MOUSE pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye_mouse.py cpp -D BASIC -D WIPY -D DEFINES -D INDENT pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye_indt.py cpp -D BASIC -D WIPY -D DEFINES -D REPLACE pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye_rplc.py cpp -D BASIC -D WIPY -D DEFINES -D SCROLL pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye_scrl.py cpp -D BASIC -D WIPY -D DEFINES -D BRACKET pye.py | sed "s/#.*$//" | sed "/^$/d" >wipye_brkt.py -cpp -D BASIC -D PYBOARD -D DEFINES -D BRACKET pye.py | sed "s/#.*$//" | sed "/^$/d" >pemin.py +cpp -D BASIC -D PYBOARD -D DEFINES -D REPLACE -D INDENT pye.py | sed "s/#.*$//" | sed "/^$/d" >pemin.py cpp -D PYBOARD -D DEFINES pye.py | sed "s/#.*$//" | sed "/^$/d" >pe.py cpp -D LINUX -D SCROLL -D DEFINES pye.py | sed "s/#.*$//" | sed "/^$/d" >pex.py cat shebang pex.py >pye @@ -18,6 +19,4 @@ cat shebang pex2.py >pye2 chmod +x pye2 rm pex2.py # -cpp -D BASIC -D WIPY -D DEFINES pye_vt.py | sed "s/#.*$//" | sed "/^$/d" >wipyt.py -cpp -D PYBOARD -D DEFINES pye_vt.py | sed "s/#.*$//" | sed "/^$/d" >pet.py diff --git a/tuning_pye.py b/tuning_pye.py index df11588..0b0973b 100644 --- a/tuning_pye.py +++ b/tuning_pye.py @@ -73,7 +73,13 @@ def line_edit(self, prompt, default): ## better one: added cursor keys and back self.wr("\b") pos -= 1 push_msg(res[pos:] + ' ') ## update tail - elif key >= 0x20: ## char to be inserted + elif key == KEY_ZAP: ## Get from content + if Editor.yank_buffer: + self.wr('\b' * pos + ' ' * len(res) + '\b' * len(res)) + res = Editor.yank_buffer[0].strip() + self.wr(res) + pos = len(res) + elif 0x20 <= key < 0xfff0: ## char to be inserted if len(prompt) + len(res) < self.width - 2: res = res[:pos] + chr(key) + res[pos:] self.wr(res[pos]) diff --git a/wipye.py b/wipye.py index 6a73337..3842c61 100644 --- a/wipye.py +++ b/wipye.py @@ -39,10 +39,8 @@ def rd_any(self): return False def rd(self): while True: - try: - return sys.stdin.read(1).encode() - except: - pass + try: return sys.stdin.read(1).encode() + except: pass def goto(self, row, col): self.wr("\x1b[{};{}H".format(row + 1, col + 1)) def clear_to_eol(self): @@ -72,8 +70,7 @@ def redraw(self, flag): self.row = min(Editor.height - 1, self.row) if sys.implementation.name == "micropython": gc.collect() - if flag: - self.message = "{} Bytes Memory available".format(gc.mem_free()) + if flag: self.message = "{} Bytes Memory available".format(gc.mem_free()) def get_input(self): while True: in_buffer = self.rd() @@ -157,6 +154,11 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' + elif key == 0x16: + if Editor.yank_buffer: + self.wr('\b \b' * len(res)) + res = Editor.yank_buffer[0].strip()[:len(prompt) + Editor.width - 2] + self.wr(res) elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < Editor.width - 2: res += chr(key) @@ -253,11 +255,6 @@ def handle_edit_keys(self, key): if line: self.cur_line = int(line) - 1 self.row = Editor.height >> 1 - elif key == 0x14: - self.cur_line = 0 - elif key == 0x02: - self.cur_line = self.total_lines - 1 - self.row = Editor.height - 1 elif key == 0x01: if True: self.autoindent = 'y' if self.autoindent != 'y' else 'n' @@ -286,8 +283,7 @@ def handle_edit_keys(self, key): self.content[self.cur_line] = l[:self.col - ni] + l[self.col:] self.col -= ni elif key == 0x18: - if self.mark != None: - self.delete_lines(True) + if self.mark != None: self.delete_lines(True) elif key == 0x04: if self.mark != None: lrange = self.line_range() @@ -295,8 +291,7 @@ def handle_edit_keys(self, key): self.mark = None elif key == 0x16: if Editor.yank_buffer: - if self.mark != None: - self.delete_lines(False) + if self.mark != None: self.delete_lines(False) self.undo_add(self.cur_line, None, 0, -len(Editor.yank_buffer)) self.content[self.cur_line:self.cur_line] = Editor.yank_buffer self.total_lines += len(Editor.yank_buffer) @@ -321,8 +316,7 @@ def handle_edit_keys(self, key): else: del self.content[action[0]:action[0] - action[1]] self.total_lines = len(self.content) - if len(self.undo) == self.undo_zero: - self.changed = '' + if len(self.undo) == self.undo_zero: self.changed = '' self.mark = None elif key == 0x05: self.redraw(True) @@ -332,36 +326,34 @@ def edit_loop(self): self.total_lines = len(self.content) self.redraw(self.message == "") while True: - try: - if not self.rd_any(): - self.display_window() - key = self.get_input() - self.message = '' - if key == 0x11: - if self.changed: - res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") - if not res or res[0].upper() != 'Y': - continue - self.goto(Editor.height, 0) - self.clear_to_eol() - self.undo = [] - return key - elif key in (0x17, 0x0f): - return key - else: self.handle_edit_keys(key) - except Exception as err: - self.message = "{!r}".format(err) + if not self.rd_any(): + self.display_window() + key = self.get_input() + self.message = '' + if key == 0x11: + if self.changed: + res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") + if not res or res[0].upper() != 'Y': + continue + self.goto(Editor.height, 0) + self.clear_to_eol() + self.undo = [] + return key + elif key in (0x17, 0x0f): + return key + else: self.handle_edit_keys(key) def get_file(self, fname): + import os if not fname: fname = self.line_edit("Open file: ", "") if fname: - self.fname = fname - try: + if (os.stat(fname)[0] & 0x4000): + self.content = sorted(os.listdir(fname)) + else: + self.fname = fname + if True: with open(fname) as f: self.content = f.readlines() - except Exception as err: - self.content, self.message = [""], "{!r}".format(err) - else: for i in range(len(self.content)): self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t ')) def put_file(self, fname): @@ -389,31 +381,32 @@ def expandtabs(s): return s def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200): gc.collect() + slot = [Editor(tab_size, undo)] if content: - slot = [] index = 0 for f in content: - slot.append(Editor(tab_size, undo)) + if index: slot.append(Editor(tab_size, undo)) if type(f) == str and f: slot[index].get_file(f) elif type(f) == list and len(f) > 0 and type(f[0]) == str: slot[index].content = f index += 1 - else: - slot = [Editor(tab_size, undo)] index = 0 while True: - index %= len(slot) - key = slot[index].edit_loop() - if key == 0x11: - if len(slot) == 1: - break - del slot[index] - elif key == 0x0f: - slot.append(Editor(tab_size, undo)) - index = len(slot) - 1 - slot[index].get_file(None) - elif key == 0x17: - index += 1 + try: + index %= len(slot) + key = slot[index].edit_loop() + if key == 0x11: + if len(slot) == 1: + break + del slot[index] + elif key == 0x0f: + slot.append(Editor(tab_size, undo)) + index = len(slot) - 1 + slot[index].get_file(None) + elif key == 0x17: + index += 1 + except Exception as err: + slot[index].message = "{!r}".format(err) Editor.yank_buffer = [] return slot[0].content if (slot[0].fname == "") else slot[0].fname