diff --git a/Pyboard Editor.doc b/Pyboard Editor.doc index 254823f..a9b87eb 100644 Binary files a/Pyboard Editor.doc and b/Pyboard Editor.doc differ diff --git a/Pyboard Editor.pdf b/Pyboard Editor.pdf index c448f15..c298747 100644 Binary files a/Pyboard Editor.pdf and b/Pyboard Editor.pdf differ diff --git a/README.md b/README.md index de7876d..47277b6 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, **1.0** Initial release with all the basic functions -**1.1** Same function set, but simplified keyboard mapping. +**1.1** Same function set, but simplified keyboard mapping. - Removed the duplicated definitions for cursor motion keys. -- Allowed both \r and \n for ENTER, and both \x08 and \x7f for BACKSPACE, which avoid some hazzle with terminal settings. +- Allowed both \r and \n for ENTER, and both \x08 and \x7f for BACKSPACE, which avoid some hazzle with terminal settings. - Removed auto-indent from the minimal version. **1.2** Mouse support added, as well as some other minor changes. @@ -61,16 +61,16 @@ c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, - Support for WiPy added. WiPy runs only the minimal version. - Aligned function set of the minimal version, in order to comply with WiPy. Dropped Mouse support, GET file, Line number column, and write tabs; but included Tab, Backtab, the buffer functions Yank, Dup & ZAP and scrolling optimization. - LEFT and RIGHT move to the adjacent line if needed -- When used with Linux **and** CPython, a terminal window resize cause redrawing the screen content. The REDRAW key (Ctrl-E) stays functional and is required for all other use cases, when the window size is changed. +- When used with Linux **and** CPython, a terminal window resize cause redrawing the screen content. The REDRAW key (Ctrl-E) stays functional and is required for all other use cases, when the window size is changed. - HOME toggles again between start-of-line and start-of-text. END moves always to end-of-line -- Dropped context sensitive behaviour of Tab, Backtab, Backspace and Delete. Too confusing. +- Dropped context sensitive behaviour of Tab, Backtab, Backspace and Delete. Too confusing. - Dropped the line number column, and made the status line permanent in all modes. - Rearranged the code such that any platform related sections are grouped together. **1.6** WiPy fixes and further trimming: - Making rarely used small functions inline again, which saves some space. Important for WiPy. - Catch Ctrl-C on WiPy. Not really nice yet, since the next input byte is lost. -- Tab Size can be set with the Ctrl-A command (if available). +- Tab Size can be set with the Ctrl-A command (if available). - Simplified Linux main(). No calling options any more. - Always ask when leaving w/o saving after the content was changed. @@ -99,7 +99,7 @@ c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, **1.8** Clean Copy & Paste, Indent, Un-Indent - Added a Mark Line key for Line Delete, Line Copy, Indent and Un-Indent - Changed Line Delete, Line Copy and Buffer Insert into a cleaner Copy & Paste mode -- Added a cleaner Indent and Un-Indent method; for WiPy too +- Added a cleaner Indent and Un-Indent method; for WiPy too - Removed the attempt to recover from out-of-memory situations: did not work. - Still runs on WiPy, but really at it's limit @@ -121,8 +121,15 @@ c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, - Lazy screen update: defer screen update, until all chars from the keyboard are processed. Not provided for WiPY, even if needed there most. WiPy has no way to tell if more chars are waiting in the input or at least a read with timeout. **1.12** Bracket Match and Minor changes -- Ctrl-K causes the cursor set to the matching bracket, if any. Pretty raw, not elegant. Brackets in comments and strings are counting as well. +- Ctrl-K causes the cursor set to the matching bracket, if any. Pretty raw, not elegant. +Brackets in comments and strings are counting as well. - On Copy the mark will be cleared, since it is assumed that the just copied lines will not be overwritten. - High level try/except catching internal errors (mostly coding errors) - Separate cpp options for including scroll optimization, replace or bracket match into the minimal version. Changes in strip.sh script to generate the minimal wipye version too. -- Some editorial changes and fixign of tyops. +- Some editorial changes and fixing of typos. + + +**1.12b** Fixing a inconsistency in the Save command +- Fixing a inconsistency in the Save command, which caused the change flag being reset when writing just a block +- Squeezing a few lines out of the source code + diff --git a/pe.py b/pe.py index 2225d08..1483928 100644 --- a/pe.py +++ b/pe.py @@ -67,8 +67,8 @@ def wr(self,s): res = self.serialcomm.write(s[ns:]) if res != None: ns += res - def not_pending(self): - return not self.serialcomm.any() + def rd_any(self): + return self.serialcomm.any() def rd(self): while not self.serialcomm.any(): pass @@ -215,7 +215,7 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' - elif key >= 0x20: + elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -286,22 +286,17 @@ def handle_cursor_keys(self, key): elif key == 0x07: line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass + self.cur_line = int(line) - 1 + self.row = self.height >> 1 elif key == 0x01: - - + self.autoindent = 'y' if self.autoindent != 'y' else 'n' + self.autoindent = 'y' if self.autoindent != 'y' else 'n' pat = self.line_edit("Case Sensitive Search {}, Autoindent {}, Tab Size {}, Write Tabs {}: ".format(self.case, self.autoindent, self.tab_size, self.write_tabs), "") try: res = [i.strip().lower() for i in pat.split(",")] if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n' if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n' - if res[2]: - try: self.tab_size = int(res[2]) - except: pass + if res[2]: self.tab_size = int(res[2]) if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n' except: pass @@ -341,8 +336,7 @@ def handle_cursor_keys(self, key): for c in range(pos, len(self.content[i])): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -358,8 +352,7 @@ def handle_cursor_keys(self, key): for c in range(pos, -1, -1): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -391,23 +384,23 @@ def delete_lines(self, yank): self.total_lines = len(self.content) self.cur_line = lrange[0] self.mark = None - def handle_edit_key(self, key): - from os import rename, unlink + def handle_edit_keys(self, key): l = self.content[self.cur_line] - if key == 0x0a: + if key == 0x7f: + if self.mark != None: + self.delete_lines(False) + elif self.col < len(l): + self.undo_add(self.cur_line, [l], 0x7f) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: self.mark = None - self.undo_add(self.cur_line, [l], 0, 2) - self.content[self.cur_line] = l[:self.col] - ni = 0 - if self.autoindent == "y": - ni = min(self.spaces(l), self.col) - r = l.partition("\x23")[0].rstrip() - if r and r[-1] == ':' and self.col >= len(r): - ni += self.tab_size - self.cur_line += 1 - self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] - self.total_lines += 1 - self.col = ni + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 elif key == 0x08: if self.mark != None: self.delete_lines(False) @@ -421,16 +414,20 @@ def handle_edit_key(self, key): self.content[self.cur_line - 1] += self.content.pop(self.cur_line) self.cur_line -= 1 self.total_lines -= 1 - elif key == 0x7f: - if self.mark != None: - self.delete_lines(False) - elif self.col < len(l): - self.undo_add(self.cur_line, [l], 0x7f) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 + elif key == 0x0a: + self.mark = None + self.undo_add(self.cur_line, [l], 0, 2) + self.content[self.cur_line] = l[:self.col] + ni = 0 + if self.autoindent == "y": + ni = min(self.spaces(l), self.col) + r = l.partition("\x23")[0].rstrip() + if r and r[-1] == ':' and self.col >= len(r): + ni += self.tab_size + self.cur_line += 1 + self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] + self.total_lines += 1 + self.col = ni elif key == 0x09: if self.mark != None: lrange = self.line_range() @@ -520,28 +517,16 @@ def handle_edit_key(self, key): if self.mark != None: fname = self.line_edit("Save Mark: ", "") lrange = self.line_range() + self.put_file(fname, lrange[0], lrange[1]) else: fname = self.fname if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: - if self.write_tabs == 'y': - f.write(self.packtabs(l) + '\n') - else: - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' self.undo_zero = len(self.undo) self.fname = fname - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == 0x1a: if len(self.undo) > 0: action = self.undo.pop(-1) @@ -558,24 +543,25 @@ def handle_edit_key(self, key): self.total_lines = len(self.content) self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: - self.mark = None - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): if self.content == []: self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() self.mouse_reporting(True) - self.scroll_region(self.height) + key = 0x05 while True: - if self.not_pending(): - self.display_window() - key = self.get_input() - self.message = '' try: + if key == 0x05: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) + self.scroll_region(self.height) + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + 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") @@ -586,17 +572,11 @@ def edit_loop(self): self.goto(self.height, 0) self.clear_to_eol() return None - elif key == 0x05: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) + else: self.handle_edit_keys(key) except Exception as err: - self.message = "Internal error: {}".format(err) + self.message = "{}".format(err) def packtabs(self, s): from _io import StringIO sb = StringIO() @@ -618,6 +598,18 @@ def get_file(self, fname): for i in range(len(content)): content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: + if self.write_tabs == 'y': + f.write(self.packtabs(l) + '\n') + else: + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) def expandtabs(s): from _io import StringIO if '\t' in s: diff --git a/pe2.py b/pe2.py index cec4c46..c2285cd 100644 --- a/pe2.py +++ b/pe2.py @@ -13,7 +13,7 @@ class Editor: b"\x1b[4~": 0x03, b"\x1b[5~": 0x17, b"\x1b[6~": 0x19, - b"\x03" : 0x04, + b"\x03" : 0x11, b"\r" : 0x0a, b"\x7f" : 0x08, b"\x1b[3~": 0x7f, @@ -71,8 +71,8 @@ def rd(self): while not self.serialcomm.any(): pass return self.serialcomm.read(1) - def not_pending(self): - return not self.serialcomm.any() + def rd_any(self): + return self.serialcomm.any() def init_tty(self, device, baud): import pyb self.sdev = device @@ -214,7 +214,7 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' - elif key >= 0x20: + elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -238,6 +238,7 @@ def find_in_file(self, pattern, pos, end): self.cur_line = line return len(pattern) def handle_cursor_keys(self, key): + l = self.content[self.cur_line] if key == 0x0d: if self.cur_line < self.total_lines - 1: self.cur_line += 1 @@ -251,7 +252,7 @@ def handle_cursor_keys(self, key): elif key == 0x1f: if self.col == 0 and self.cur_line > 0: self.cur_line -= 1 - self.col = len(self.content[self.cur_line]) + self.col = len(l) if self.cur_line < self.top_line: self.scroll_up(1) else: @@ -259,9 +260,9 @@ def handle_cursor_keys(self, key): elif key == 0x1e: self.col += 1 elif key == 0x10: - self.col = self.spaces(self.content[self.cur_line]) if self.col == 0 else 0 + self.col = self.spaces(l) if self.col == 0 else 0 elif key == 0x03: - self.col = len(self.content[self.cur_line]) + self.col = len(l) elif key == 0x17: self.cur_line -= self.height elif key == 0x19: @@ -278,11 +279,8 @@ def handle_cursor_keys(self, key): elif key == 0x07: line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass + self.cur_line = int(line) - 1 + self.row = self.height >> 1 elif key == 0x01: self.autoindent = 'y' if self.autoindent != 'y' else 'n' self.autoindent = 'y' if self.autoindent != 'y' else 'n' @@ -291,9 +289,7 @@ def handle_cursor_keys(self, key): res = [i.strip().lower() for i in pat.split(",")] if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n' if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n' - if res[2]: - try: self.tab_size = int(res[2]) - except: pass + if res[2]: self.tab_size = int(res[2]) if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n' except: pass @@ -314,36 +310,18 @@ def handle_cursor_keys(self, key): self.cur_line = max(self.cur_line, self.top_line) self.scroll_down(3) elif key == 0xfffe: - if self.col >= len(self.content[self.cur_line]): - return True - opening = "([{<" - closing = ")]}>" - level = 0 - pos = self.col - srch = self.content[self.cur_line][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 = i - self.col = c - return True - else: - level -= 1 - elif self.content[i][c] == srch: - level += 1 - pos = 0 - else: - i = closing.find(srch) + 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 = opening[i] - for i in range(self.cur_line, -1, -1): - for c in range(pos, -1, -1): + 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 = i @@ -353,8 +331,25 @@ def handle_cursor_keys(self, key): level -= 1 elif self.content[i][c] == srch: level += 1 - if i > 0: - pos = len(self.content[i - 1]) - 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 = i + self.col = 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 == 0x14: self.cur_line = 0 elif key == 0x02: @@ -383,24 +378,32 @@ def delete_lines(self, yank): self.total_lines = len(self.content) self.cur_line = lrange[0] self.mark = None - def handle_edit_key(self, key): + def handle_edit_keys(self, key): from os import rename, unlink l = self.content[self.cur_line] jut = self.col - len(l) - if key == 0x0a: - self.undo_add(self.cur_line, [l], 0, 2) - self.content[self.cur_line] = l[:self.col] - ni = 0 - if self.autoindent == "y": - ni = min(self.spaces(l), self.col) - r = l.partition("\x23")[0].rstrip() - if r and r[-1] == ':' and self.col >= len(r): - ni += self.tab_size - self.cur_line += 1 - self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] if jut < 0 else [""] - self.total_lines += 1 - self.col = ni - self.mark = None + if key == 0x7f: + if self.mark != None: + self.delete_lines(False) + elif jut < 0: + self.undo_add(self.cur_line, [l], 0x7f) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + if jut > 0: + l += ' ' * jut + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: + self.mark = None + if jut >= 0: + if key != 0x20: + self.undo_add(self.cur_line, [l], 0x41) + self.content[self.cur_line] = l + ' ' * jut + chr(key) + else: + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 elif key == 0x08: if self.mark != None: self.delete_lines(False) @@ -415,18 +418,20 @@ def handle_edit_key(self, key): self.content[self.cur_line - 1] += self.content.pop(self.cur_line) self.cur_line -= 1 self.total_lines -= 1 - elif key == 0x7f: - if self.mark != None: - self.delete_lines(False) - elif jut < 0: - self.undo_add(self.cur_line, [l], 0x7f) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - if jut > 0: - l += ' ' * jut - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 + elif key == 0x0a: + self.undo_add(self.cur_line, [l], 0, 2) + self.content[self.cur_line] = l[:self.col] + ni = 0 + if self.autoindent == "y": + ni = min(self.spaces(l), self.col) + r = l.partition("\x23")[0].rstrip() + if r and r[-1] == ':' and self.col >= len(r): + ni += self.tab_size + self.cur_line += 1 + self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] if jut < 0 else [""] + self.total_lines += 1 + self.col = ni + self.mark = None elif key == 0x09: if self.mark != None: lrange = self.line_range() @@ -521,28 +526,16 @@ def handle_edit_key(self, key): if self.mark != None: fname = self.line_edit("Save Mark: ", "") lrange = self.line_range() + self.put_file(fname, lrange[0], lrange[1]) else: fname = self.fname if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: - if self.write_tabs == 'y': - f.write(self.packtabs(l) + '\n') - else: - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' self.undo_zero = len(self.undo) self.fname = fname - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == 0x1a: if len(self.undo) > 0: action = self.undo.pop(-1) @@ -559,29 +552,25 @@ def handle_edit_key(self, key): self.total_lines = len(self.content) self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: - self.mark = None - if jut >= 0: - if key != 0x20: - self.undo_add(self.cur_line, [l], 0x41) - self.content[self.cur_line] = l + ' ' * jut + chr(key) - else: - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): if self.content == []: self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() self.mouse_reporting(True) - self.scroll_region(self.height) + key = 0x05 while True: - if self.not_pending(): - self.display_window() - key = self.get_input() - self.message = '' try: + if key == 0x05: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) + self.scroll_region(self.height) + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + 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") @@ -592,17 +581,11 @@ def edit_loop(self): self.goto(self.height, 0) self.clear_to_eol() return None - elif key == 0x05: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) - except: - self.message = "Oops!" + else: self.handle_edit_keys(key) + except Exception as err: + self.message = "{}".format(err) def packtabs(self, s): from _io import StringIO sb = StringIO() @@ -624,6 +607,18 @@ def get_file(self, fname): for i in range(len(content)): content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: + if self.write_tabs == 'y': + f.write(self.packtabs(l) + '\n') + else: + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) def expandtabs(s): from _io import StringIO if '\t' in s: diff --git a/pemin.py b/pemin.py index 72b5e35..b18a019 100644 --- a/pemin.py +++ b/pemin.py @@ -41,8 +41,8 @@ def wr(self,s): res = self.serialcomm.write(s[ns:]) if res != None: ns += res - def not_pending(self): - return not self.serialcomm.any() + def rd_any(self): + return self.serialcomm.any() def rd(self): while not self.serialcomm.any(): pass @@ -165,7 +165,7 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' - elif key >= 0x20: + elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -216,11 +216,10 @@ def handle_cursor_keys(self, key): elif key == 0x07: line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass + self.cur_line = int(line) - 1 + self.row = self.height >> 1 + elif key == 0x01: + self.autoindent = 'y' if self.autoindent != 'y' else 'n' elif key == 0xfffe: if self.col < len(l): opening = "([{<" @@ -236,8 +235,7 @@ def handle_cursor_keys(self, key): for c in range(pos, len(self.content[i])): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -253,8 +251,7 @@ def handle_cursor_keys(self, key): for c in range(pos, -1, -1): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -286,10 +283,31 @@ def delete_lines(self, yank): self.total_lines = len(self.content) self.cur_line = lrange[0] self.mark = None - def handle_edit_key(self, key): - from os import rename, unlink + def handle_edit_keys(self, key): l = self.content[self.cur_line] - if key == 0x0a: + if key == 0x7f: + if self.mark != None: + self.delete_lines(False) + elif self.col < len(l): + self.undo_add(self.cur_line, [l], 0x7f) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: + self.mark = None + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 + elif key == 0x08: + if self.mark != None: + self.delete_lines(False) + elif self.col > 0: + self.undo_add(self.cur_line, [l], 0x08) + self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] + self.col -= 1 + elif key == 0x0a: self.mark = None self.undo_add(self.cur_line, [l], 0, 2) self.content[self.cur_line] = l[:self.col] @@ -300,23 +318,6 @@ def handle_edit_key(self, key): self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] self.total_lines += 1 self.col = ni - elif key == 0x08: - if self.mark != None: - self.delete_lines(False) - elif self.col > 0: - self.undo_add(self.cur_line, [l], 0x08) - self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] - self.col -= 1 - elif key == 0x7f: - if self.mark != None: - self.delete_lines(False) - elif self.col < len(l): - self.undo_add(self.cur_line, [l], 0x7f) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 elif key == 0x09: if self.mark != None: lrange = self.line_range() @@ -364,20 +365,10 @@ def handle_edit_key(self, key): if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' self.undo_zero = len(self.undo) self.fname = fname - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == 0x1a: if len(self.undo) > 0: action = self.undo.pop(-1) @@ -394,22 +385,23 @@ def handle_edit_key(self, key): self.total_lines = len(self.content) self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: - self.mark = None - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): if self.content == []: self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() + key = 0x05 while True: - if self.not_pending(): - self.display_window() - key = self.get_input() - self.message = '' try: + if key == 0x05: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + 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") @@ -418,17 +410,11 @@ def edit_loop(self): self.goto(self.height, 0) self.clear_to_eol() return None - elif key == 0x05: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) + else: self.handle_edit_keys(key) except Exception as err: - self.message = "Internal error: {}".format(err) + self.message = "{}".format(err) def get_file(self, fname): try: with open(fname) as f: @@ -439,6 +425,15 @@ def get_file(self, fname): for i in range(len(content)): content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) def expandtabs(s): from _io import StringIO if '\t' in s: diff --git a/pye.py b/pye.py index da191d3..86fa8ad 100644 --- a/pye.py +++ b/pye.py @@ -13,7 +13,7 @@ ## Copy/Delete & Paste, Indent, Un-Indent ## - Added mouse support for pointing and scrolling (not WiPy) ## - handling tab (0x09) on reading & writing files, -## - Added a status line, line number column and single line prompts for +## - Added a status line and single line prompts for ## Quit, Save, Find, Replace, Flags and Goto ## - moved main into a function with some optional parameters ## @@ -178,12 +178,12 @@ def wr(self,s): s = bytes(s, "utf-8") os.write(1, s) - def not_pending(self): + def rd_any(self): if sys.implementation.name == "cpython": import select - return select.select([self.sdev], [], [], 0)[0] == [] + return select.select([self.sdev], [], [], 0)[0] != [] else: - return True + return False def rd(self): while True: @@ -199,8 +199,6 @@ def init_tty(self, device, baud): tty.setraw(device) self.sdev = device Editor.winch = False - if sys.implementation.name == "cpython": - signal.signal(signal.SIGWINCH, Editor.signal_handler) def deinit_tty(self): import termios @@ -221,8 +219,8 @@ def wr(self,s): if res != None: ns += res - def not_pending(self): - return not self.serialcomm.any() + def rd_any(self): + return self.serialcomm.any() def rd(self): while not self.serialcomm.any(): @@ -247,8 +245,8 @@ def deinit_tty(self): def wr(self, s): sys.stdout.write(s) - def not_pending(self): - return True + def rd_any(self): + return False def rd(self): while True: @@ -418,7 +416,7 @@ 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 >= 0x20: ## char to be added at the end + elif 0x20 <= key < 0xfff0: ## character to be added if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -505,23 +503,18 @@ def handle_cursor_keys(self, key): ## keys which move, sanity checks later elif key == KEY_GOTO: ## goto line line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass -#ifndef BASIC + self.cur_line = int(line) - 1 + self.row = self.height >> 1 elif key == KEY_TOGGLE: ## Toggle Autoindent/Statusline/Search case - ##self.autoindent = 'y' if self.autoindent != 'y' else 'n' ## toggle - ##self.autoindent = 'y' if self.autoindent != 'y' else 'n' ## toggle again + self.autoindent = 'y' if self.autoindent != 'y' else 'n' ## toggle +#ifndef BASIC + self.autoindent = 'y' if self.autoindent != 'y' else 'n' ## toggle again pat = self.line_edit("Case Sensitive Search {}, Autoindent {}, Tab Size {}, Write Tabs {}: ".format(self.case, self.autoindent, self.tab_size, self.write_tabs), "") try: res = [i.strip().lower() for i in pat.split(",")] if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n' if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n' - if res[2]: - try: self.tab_size = int(res[2]) - except: pass + if res[2]: self.tab_size = int(res[2]) if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n' except: pass @@ -563,8 +556,7 @@ def handle_cursor_keys(self, key): ## keys which move, sanity checks later for c in range(pos, len(self.content[i])): if self.content[i][c] == match: if level == 0: ## match found - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True ## return here instead of ml-breaking else: level -= 1 @@ -580,8 +572,7 @@ def handle_cursor_keys(self, key): ## keys which move, sanity checks later for c in range(pos, -1, -1): if self.content[i][c] == match: if level == 0: ## match found - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True ## return here instead of ml-breaking else: level -= 1 @@ -617,25 +608,23 @@ def delete_lines(self, yank): ## copy marked lines (opt) and delete them self.cur_line = lrange[0] self.mark = None ## unset line mark - def handle_edit_key(self, key): ## keys which change content - from os import rename, unlink + def handle_edit_keys(self, key): ## keys which change content l = self.content[self.cur_line] - if key == KEY_ENTER: + if key == KEY_DELETE: ## must be first, since 0x7f is in std char range + if self.mark != None: + self.delete_lines(False) + elif self.col < len(l): + self.undo_add(self.cur_line, [l], KEY_DELETE) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: ## test for last line + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: ## character to be added self.mark = None - self.undo_add(self.cur_line, [l], 0, 2) - self.content[self.cur_line] = l[:self.col] - ni = 0 - if self.autoindent == "y": ## Autoindent - ni = min(self.spaces(l), self.col) ## query indentation -#ifndef BASIC - r = l.partition("\x23")[0].rstrip() ## \x23 == # - if r and r[-1] == ':' and self.col >= len(r): ## look for : as the last non-space before comment - ni += self.tab_size -#endif - self.cur_line += 1 - self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] - self.total_lines += 1 - self.col = ni + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 elif key == KEY_BACKSPACE: if self.mark != None: self.delete_lines(False) @@ -651,16 +640,22 @@ def handle_edit_key(self, key): ## keys which change content self.cur_line -= 1 self.total_lines -= 1 #endif - elif key == KEY_DELETE: - if self.mark != None: - self.delete_lines(False) - elif self.col < len(l): - self.undo_add(self.cur_line, [l], KEY_DELETE) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: ## test for last line - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 + elif key == KEY_ENTER: + self.mark = None + self.undo_add(self.cur_line, [l], 0, 2) + self.content[self.cur_line] = l[:self.col] + ni = 0 + if self.autoindent == "y": ## Autoindent + ni = min(self.spaces(l), self.col) ## query indentation +#ifndef BASIC + r = l.partition("\x23")[0].rstrip() ## \x23 == # + if r and r[-1] == ':' and self.col >= len(r): ## look for : as the last non-space before comment + ni += self.tab_size +#endif + self.cur_line += 1 + self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] + self.total_lines += 1 + self.col = ni elif key == KEY_TAB: if self.mark != None: lrange = self.line_range() @@ -755,31 +750,17 @@ def handle_edit_key(self, key): ## keys which change content if self.mark != None: fname = self.line_edit("Save Mark: ", "") lrange = self.line_range() + self.put_file(fname, lrange[0], lrange[1]) else: #endif fname = self.fname if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: -#ifndef BASIC - if self.write_tabs == 'y': - f.write(self.packtabs(l) + '\n') - else: -#endif - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' ## clear change flag self.undo_zero = len(self.undo) ## remember state self.fname = fname ## remember (new) name - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == KEY_UNDO: if len(self.undo) > 0: action = self.undo.pop(-1) ## get action from stack @@ -796,33 +777,38 @@ def handle_edit_key(self, key): ## keys which change content self.total_lines = len(self.content) ## brute force self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: ## character to be added - self.mark = None - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 - def edit_loop(self): ## main editing loop if self.content == []: ## check for empty content self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() #ifndef BASIC self.mouse_reporting(True) ## enable mouse reporting #endif + key = KEY_REDRAW + + while True: + try: + if key == KEY_REDRAW: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) #ifdef SCROLL - self.scroll_region(self.height) + self.scroll_region(self.height) #endif +#ifdef LINUX + if sys.platform in ("linux", "darwin") and sys.implementation.name == "cpython": + signal.signal(signal.SIGWINCH, Editor.signal_handler) +#endif + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) - while True: - if self.not_pending(): ## 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 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 - try: if key == KEY_QUIT: if self.changed != ' ': res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") @@ -838,21 +824,11 @@ def edit_loop(self): ## main editing loop self.goto(self.height, 0) self.clear_to_eol() return None - elif key == KEY_REDRAW: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) -#ifdef LINUX - if sys.platform in ("linux", "darwin") and sys.implementation.name == "cpython": - signal.signal(signal.SIGWINCH, Editor.signal_handler) -#endif - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) + else: self.handle_edit_keys(key) except Exception as err: - self.message = "Internal error: {}".format(err) + self.message = "{}".format(err) ## packtabs: replace sequence of space by tab #ifndef BASIC @@ -868,6 +844,7 @@ def packtabs(self, s): sb.write(c) return sb.getvalue() #endif +## Read file into content def get_file(self, fname): try: #ifdef LINUX @@ -884,6 +861,23 @@ def get_file(self, fname): for i in range(len(content)): ## strip and convert content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + +## write file + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: +#ifndef BASIC + if self.write_tabs == 'y': + f.write(self.packtabs(l) + '\n') + else: +#endif + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) + ## expandtabs: hopefully sometimes replaced by the built-in function def expandtabs(s): from _io import StringIO diff --git a/pye2.py b/pye2.py index e8d9ad0..1c2b81d 100644 --- a/pye2.py +++ b/pye2.py @@ -13,7 +13,7 @@ ## Copy/Delete & Paste, Indent, Un-Indent ## - Added mouse support for pointing and scrolling (not WiPy) ## - handling tab (0x09) on reading & writing files, -## - Added a status line, line number column and single line prompts for +## - Added a status line and single line prompts for ## Quit, Save, Find, Replace, Flags and Goto ## - moved main into a function with some optional parameters ## @@ -115,7 +115,7 @@ class Editor: b"\x1b[4~": KEY_END, ## Putty b"\x1b[5~": KEY_PGUP, b"\x1b[6~": KEY_PGDN, - b"\x03" : KEY_DUP, ## Ctrl-C + b"\x03" : KEY_QUIT, ## Ctrl-C b"\r" : KEY_ENTER, b"\x7f" : KEY_BACKSPACE, ## Ctrl-? (127) b"\x1b[3~": KEY_DELETE, @@ -187,20 +187,18 @@ def rd(self): Editor.winch = False return b'\x05' - def not_pending(self): + def rd_any(self): if sys.implementation.name == "cpython": import select - return select.select([self.sdev], [], [], 0)[0] == [] + return select.select([self.sdev], [], [], 0)[0] != [] else: - return True + return False def init_tty(self, device, baud): self.org_termios = termios.tcgetattr(device) tty.setraw(device) self.sdev = device Editor.winch = False - if sys.implementation.name == "cpython": - signal.signal(signal.SIGWINCH, Editor.signal_handler) def deinit_tty(self): import termios @@ -226,8 +224,8 @@ def rd(self): pass return self.serialcomm.read(1) - def not_pending(self): - return not self.serialcomm.any() + def rd_any(self): + return self.serialcomm.any() def init_tty(self, device, baud): import pyb @@ -254,8 +252,8 @@ def rd(self): except: pass - def not_pending(self): - return True + def rd_any(self): + return False ## def init_tty(self, device, baud): ## pass @@ -419,7 +417,7 @@ 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 >= 0x20: ## char to be added at the end + elif 0x20 <= key < 0xfff0: ## char to be added at the end if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -498,23 +496,18 @@ def handle_cursor_keys(self, key): ## keys which move, sanity checks later elif key == KEY_GOTO: ## goto line line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass -#ifndef BASIC + self.cur_line = int(line) - 1 + self.row = self.height >> 1 elif key == KEY_TOGGLE: ## Toggle Autoindent/Statusline/Search case - ##self.autoindent = 'y' if self.autoindent != 'y' else 'n' - ##self.autoindent = 'y' if self.autoindent != 'y' else 'n' + self.autoindent = 'y' if self.autoindent != 'y' else 'n' +#ifndef BASIC + self.autoindent = 'y' if self.autoindent != 'y' else 'n' pat = self.line_edit("Case Sensitive Search {}, Autoindent {}, Tab Size {}, Write Tabs {}: ".format(self.case, self.autoindent, self.tab_size, self.write_tabs), "") try: res = [i.strip().lower() for i in pat.split(",")] if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n' if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n' - if res[2]: - try: self.tab_size = int(res[2]) - except: pass + if res[2]: self.tab_size = int(res[2]) if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n' except: pass @@ -609,26 +602,32 @@ def delete_lines(self, yank): self.cur_line = lrange[0] self.mark = None ## unset line mark - def handle_edit_key(self, key): ## keys which change content + def handle_edit_keys(self, key): ## keys which change content from os import rename, unlink l = self.content[self.cur_line] jut = self.col - len(l) ## <0: before text end, =0 at text end, >0 beyond text end - if key == KEY_ENTER: - self.undo_add(self.cur_line, [l], 0, 2) - self.content[self.cur_line] = l[:self.col] - ni = 0 - if self.autoindent == "y": ## Autoindent - ni = min(self.spaces(l), self.col) ## query indentation -#ifndef BASIC - r = l.partition("\x23")[0].rstrip() ## \x23 == # - if r and r[-1] == ':' and self.col >= len(r): ## look for : as the last non-space before comment - ni += self.tab_size -#endif - self.cur_line += 1 - self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] if jut < 0 else [""] - self.total_lines += 1 - self.col = ni - self.mark = None ## unset line mark + if key == KEY_DELETE: + if self.mark != None: + self.delete_lines(False) + elif jut < 0: + self.undo_add(self.cur_line, [l], KEY_DELETE) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: ## test for last line + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + if jut > 0: ## Landfill needed + l += ' ' * jut + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: ## char to be added + self.mark = None + if jut >= 0: + if key != 0x20: + self.undo_add(self.cur_line, [l], 0x41) + self.content[self.cur_line] = l + ' ' * jut + chr(key) + else: + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 elif key == KEY_BACKSPACE: if self.mark != None: self.delete_lines(False) @@ -645,18 +644,22 @@ def handle_edit_key(self, key): ## keys which change content self.cur_line -= 1 self.total_lines -= 1 #endif - elif key == KEY_DELETE: - if self.mark != None: - self.delete_lines(False) - elif jut < 0: - self.undo_add(self.cur_line, [l], KEY_DELETE) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: ## test for last line - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - if jut > 0: ## Landfill needed - l += ' ' * jut - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 + elif key == KEY_ENTER: + self.undo_add(self.cur_line, [l], 0, 2) + self.content[self.cur_line] = l[:self.col] + ni = 0 + if self.autoindent == "y": ## Autoindent + ni = min(self.spaces(l), self.col) ## query indentation +#ifndef BASIC + r = l.partition("\x23")[0].rstrip() ## \x23 == # + if r and r[-1] == ':' and self.col >= len(r): ## look for : as the last non-space before comment + ni += self.tab_size +#endif + self.cur_line += 1 + self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] if jut < 0 else [""] + self.total_lines += 1 + self.col = ni + self.mark = None ## unset line mark elif key == KEY_TAB: if self.mark != None: lrange = self.line_range() @@ -756,31 +759,17 @@ def handle_edit_key(self, key): ## keys which change content if self.mark != None: fname = self.line_edit("Save Mark: ", "") lrange = self.line_range() + self.put_file(fname, lrange[0], lrange[1]) else: #endif fname = self.fname if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: -#ifndef BASIC - if self.write_tabs == 'y': - f.write(self.packtabs(l) + '\n') - else: -#endif - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' ## clear change flag self.undo_zero = len(self.undo) ## remember state self.fname = fname ## remember (new) name - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == KEY_UNDO: if len(self.undo) > 0: action = self.undo.pop(-1) ## get action from stack @@ -797,36 +786,38 @@ def handle_edit_key(self, key): ## keys which change content self.total_lines = len(self.content) ## brute force self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: ## character to be added - self.mark = None - if jut >= 0: - if key != 0x20: - self.undo_add(self.cur_line, [l], 0x41) - self.content[self.cur_line] = l + ' ' * jut + chr(key) - else: - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): ## main editing loop if self.content == []: ## check for empty content self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() #ifndef BASIC self.mouse_reporting(True) ## enable mouse reporting #endif + key = KEY_REDRAW + + while True: + try: + if key == KEY_REDRAW: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) #ifdef SCROLL - self.scroll_region(self.height) + self.scroll_region(self.height) #endif - while True: - if self.not_pending(): ## 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 +#ifdef LINUX + if sys.platform in ("linux", "darwin") and sys.implementation.name == "cpython": + signal.signal(signal.SIGWINCH, Editor.signal_handler) +#endif + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + + 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 - try: if key == KEY_QUIT: if self.changed != ' ': res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N") @@ -842,21 +833,11 @@ def edit_loop(self): ## main editing loop self.goto(self.height, 0) self.clear_to_eol() return None - elif key == KEY_REDRAW: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) -#ifdef LINUX - if sys.platform in ("linux", "darwin") and sys.implementation.name == "cpython": - signal.signal(signal.SIGWINCH, Editor.signal_handler) -#endif - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) - except: - self.message = "Oops!" + else: self.handle_edit_keys(key) + except Exception as err: + self.message = "{}".format(err) ## packtabs: replace sequence of space by tab #ifndef BASIC @@ -888,6 +869,21 @@ def get_file(self, fname): for i in range(len(content)): ## strip and convert content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") +## write file + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: +#ifndef BASIC + if self.write_tabs == 'y': + f.write(self.packtabs(l) + '\n') + else: +#endif + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) ## expandtabs: hopefully sometimes replaced by the built-in function def expandtabs(s): from _io import StringIO diff --git a/wipye.py b/wipye.py index 2a0a196..636ea78 100644 --- a/wipye.py +++ b/wipye.py @@ -37,8 +37,8 @@ def __init__(self, tab_size, undo_limit): if sys.platform == "WiPy": def wr(self, s): sys.stdout.write(s) - def not_pending(self): - return True + def rd_any(self): + return False def rd(self): while True: try: @@ -152,7 +152,7 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' - elif key >= 0x20: + elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -203,11 +203,10 @@ def handle_cursor_keys(self, key): elif key == 0x07: line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass + self.cur_line = int(line) - 1 + self.row = self.height >> 1 + elif key == 0x01: + self.autoindent = 'y' if self.autoindent != 'y' else 'n' elif key == 0xfffe: if self.col < len(l): opening = "([{<" @@ -223,8 +222,7 @@ def handle_cursor_keys(self, key): for c in range(pos, len(self.content[i])): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -240,8 +238,7 @@ def handle_cursor_keys(self, key): for c in range(pos, -1, -1): if self.content[i][c] == match: if level == 0: - self.cur_line = i - self.col = c + self.cur_line, self.col = i, c return True else: level -= 1 @@ -273,10 +270,31 @@ def delete_lines(self, yank): self.total_lines = len(self.content) self.cur_line = lrange[0] self.mark = None - def handle_edit_key(self, key): - from os import rename, unlink + def handle_edit_keys(self, key): l = self.content[self.cur_line] - if key == 0x0a: + if key == 0x7f: + if self.mark != None: + self.delete_lines(False) + elif self.col < len(l): + self.undo_add(self.cur_line, [l], 0x7f) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: + self.mark = None + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 + elif key == 0x08: + if self.mark != None: + self.delete_lines(False) + elif self.col > 0: + self.undo_add(self.cur_line, [l], 0x08) + self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] + self.col -= 1 + elif key == 0x0a: self.mark = None self.undo_add(self.cur_line, [l], 0, 2) self.content[self.cur_line] = l[:self.col] @@ -287,23 +305,6 @@ def handle_edit_key(self, key): self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] self.total_lines += 1 self.col = ni - elif key == 0x08: - if self.mark != None: - self.delete_lines(False) - elif self.col > 0: - self.undo_add(self.cur_line, [l], 0x08) - self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] - self.col -= 1 - elif key == 0x7f: - if self.mark != None: - self.delete_lines(False) - elif self.col < len(l): - self.undo_add(self.cur_line, [l], 0x7f) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 elif key == 0x09: if self.mark != None: lrange = self.line_range() @@ -351,20 +352,10 @@ def handle_edit_key(self, key): if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' self.undo_zero = len(self.undo) self.fname = fname - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == 0x1a: if len(self.undo) > 0: action = self.undo.pop(-1) @@ -381,22 +372,23 @@ def handle_edit_key(self, key): self.total_lines = len(self.content) self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: - self.mark = None - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): if self.content == []: self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() + key = 0x05 while True: - if self.not_pending(): - self.display_window() - key = self.get_input() - self.message = '' try: + if key == 0x05: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + 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") @@ -405,17 +397,11 @@ def edit_loop(self): self.goto(self.height, 0) self.clear_to_eol() return None - elif key == 0x05: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) + else: self.handle_edit_keys(key) except Exception as err: - self.message = "Internal error: {}".format(err) + self.message = "{}".format(err) def get_file(self, fname): try: with open(fname) as f: @@ -426,6 +412,15 @@ def get_file(self, fname): for i in range(len(content)): content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) def expandtabs(s): from _io import StringIO if '\t' in s: diff --git a/wipye_sml.py b/wipye_sml.py index b9ad243..78442ec 100644 --- a/wipye_sml.py +++ b/wipye_sml.py @@ -36,8 +36,8 @@ def __init__(self, tab_size, undo_limit): if sys.platform == "WiPy": def wr(self, s): sys.stdout.write(s) - def not_pending(self): - return True + def rd_any(self): + return False def rd(self): while True: try: @@ -151,7 +151,7 @@ def line_edit(self, prompt, default): elif key == 0x7f: self.wr('\b \b' * len(res)) res = '' - elif key >= 0x20: + elif 0x20 <= key < 0xfff0: if len(prompt) + len(res) < self.width - 2: res += chr(key) self.wr(chr(key)) @@ -202,11 +202,10 @@ def handle_cursor_keys(self, key): elif key == 0x07: line = self.line_edit("Goto Line: ", "") if line: - try: - self.cur_line = int(line) - 1 - self.row = self.height >> 1 - except: - pass + self.cur_line = int(line) - 1 + self.row = self.height >> 1 + elif key == 0x01: + self.autoindent = 'y' if self.autoindent != 'y' else 'n' elif key == 0x0c: self.mark = self.cur_line if self.mark == None else None else: @@ -231,10 +230,31 @@ def delete_lines(self, yank): self.total_lines = len(self.content) self.cur_line = lrange[0] self.mark = None - def handle_edit_key(self, key): - from os import rename, unlink + def handle_edit_keys(self, key): l = self.content[self.cur_line] - if key == 0x0a: + if key == 0x7f: + if self.mark != None: + self.delete_lines(False) + elif self.col < len(l): + self.undo_add(self.cur_line, [l], 0x7f) + self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] + elif (self.cur_line + 1) < self.total_lines: + self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) + self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) + self.total_lines -= 1 + elif 0x20 <= key < 0xfff0: + self.mark = None + self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) + self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] + self.col += 1 + elif key == 0x08: + if self.mark != None: + self.delete_lines(False) + elif self.col > 0: + self.undo_add(self.cur_line, [l], 0x08) + self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] + self.col -= 1 + elif key == 0x0a: self.mark = None self.undo_add(self.cur_line, [l], 0, 2) self.content[self.cur_line] = l[:self.col] @@ -245,23 +265,6 @@ def handle_edit_key(self, key): self.content[self.cur_line:self.cur_line] = [' ' * ni + l[self.col:]] self.total_lines += 1 self.col = ni - elif key == 0x08: - if self.mark != None: - self.delete_lines(False) - elif self.col > 0: - self.undo_add(self.cur_line, [l], 0x08) - self.content[self.cur_line] = l[:self.col - 1] + l[self.col:] - self.col -= 1 - elif key == 0x7f: - if self.mark != None: - self.delete_lines(False) - elif self.col < len(l): - self.undo_add(self.cur_line, [l], 0x7f) - self.content[self.cur_line] = l[:self.col] + l[self.col + 1:] - elif (self.cur_line + 1) < self.total_lines: - self.undo_add(self.cur_line, [l, self.content[self.cur_line + 1]], 0) - self.content[self.cur_line] = l + self.content.pop(self.cur_line + 1) - self.total_lines -= 1 elif key == 0x09: if self.mark != None: lrange = self.line_range() @@ -309,20 +312,10 @@ def handle_edit_key(self, key): if fname == None: fname = "" fname = self.line_edit("Save File: ", fname) - lrange = (0, self.total_lines) - if fname: - try: - with open("tmpfile.pye", "w") as f: - for l in self.content[lrange[0]:lrange[1]]: - f.write(l + '\n') - try: unlink(fname) - except: pass - rename("tmpfile.pye", fname) + self.put_file(fname, 0, self.total_lines) self.changed = ' ' self.undo_zero = len(self.undo) self.fname = fname - except Exception as err: - self.message = 'Could not save {}, {!r}'.format(fname, err) elif key == 0x1a: if len(self.undo) > 0: action = self.undo.pop(-1) @@ -339,22 +332,23 @@ def handle_edit_key(self, key): self.total_lines = len(self.content) self.changed = ' ' if len(self.undo) == self.undo_zero else '*' self.mark = None - elif key >= 0x20: - self.mark = None - self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41) - self.content[self.cur_line] = l[:self.col] + chr(key) + l[self.col:] - self.col += 1 def edit_loop(self): if self.content == []: self.content = [""] self.total_lines = len(self.content) - self.set_screen_parms() + key = 0x05 while True: - if self.not_pending(): - self.display_window() - key = self.get_input() - self.message = '' try: + if key == 0x05: + self.set_screen_parms() + self.row = min(self.height - 1, self.row) + if sys.implementation.name == "micropython": + gc.collect() + self.message = "{} Bytes Memory available".format(gc.mem_free()) + 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") @@ -363,17 +357,11 @@ def edit_loop(self): self.goto(self.height, 0) self.clear_to_eol() return None - elif key == 0x05: - self.set_screen_parms() - self.row = min(self.height - 1, self.row) - if sys.implementation.name == "micropython": - gc.collect() - self.message = "{} Bytes Memory available".format(gc.mem_free()) elif self.handle_cursor_keys(key): pass - else: self.handle_edit_key(key) + else: self.handle_edit_keys(key) except Exception as err: - self.message = "Internal error: {}".format(err) + self.message = "{}".format(err) def get_file(self, fname): try: with open(fname) as f: @@ -384,6 +372,15 @@ def get_file(self, fname): for i in range(len(content)): content[i] = expandtabs(content[i].rstrip('\r\n\t ')) return (content, "") + def put_file(self, fname, start, stop): + from os import rename, unlink + if fname: + with open("tmpfile.pye", "w") as f: + for l in self.content[start:stop]: + f.write(l + '\n') + try: unlink(fname) + except: pass + rename("tmpfile.pye", fname) def expandtabs(s): from _io import StringIO if '\t' in s: