From caa0434a3ba9d0f7ea849417ec10839f2e0cb3af Mon Sep 17 00:00:00 2001 From: Robert HH Date: Tue, 29 Dec 2015 08:29:32 +0100 Subject: [PATCH] Fixing an inconsistency in the Save command --- Pyboard Editor.doc | Bin 61440 -> 61440 bytes Pyboard Editor.pdf | Bin 93228 -> 93018 bytes README.md | 23 +++-- pe.py | 142 ++++++++++++++--------------- pe2.py | 219 ++++++++++++++++++++++----------------------- pemin.py | 117 ++++++++++++------------ pye.py | 184 ++++++++++++++++++------------------- pye2.py | 194 ++++++++++++++++++++------------------- wipye.py | 117 ++++++++++++------------ wipye_sml.py | 111 +++++++++++------------ 10 files changed, 539 insertions(+), 568 deletions(-) diff --git a/Pyboard Editor.doc b/Pyboard Editor.doc index 254823fa801fdaba10fe5641043c67c948598add..a9b87ebc88a552b299730bf01225105231c605bb 100644 GIT binary patch delta 4237 zcmeI#e@xW(9mnza7fw7>2vCOyC5osgy{ctQlu@MA8cV6cA7fVW>f z_x*iezCT{C?``Su-_qe`2gei2);l0k0#QP1$THbRbrdcDrKaIF+xsJw* z*hKc|H~pQL*hM;BBF<$ZbC!!N&Jii{h`0+)-IGpNJhQ?QM4Da{>3M~9+#-?^*#l0Tq@t(rmTGc&lQa*7LJD+?IWpgJb9?;<0q;w@VY={p;oA;qN?~JlQuv zS~(5B`=-b+xyXE*yj7u}zNy|-uA}~;UkrTgZx754`Y3c{cwXf3u?60R)Bk4ufAatG znDkht&lzJXTsOl;?0Nat?lvoxlhoe(M&j|*-k%erPxoSIXnVbcry(<0#rn<6p=9k>m@sUrE$hzwvfcf<@- zp@RGE2u6R4c?g*<5`_hDV>haB7;U(S5RxkfE<}>AC5cid2NOl=(S)#XGXqWpBr%Tg zWEMooEWRj(gYuk6A6%U54(xbAq>(t>_7d}67CDJM^F;QdJcIS;vy%%%8c_Wm4pgSd zlJ7EoF|oOrmG_dP-RMO>`m#jsV*nG0VU6PcKk2`(`oH4EoA3Y7o0)%lg1x{0_ot7` zaWO5QjGZ`Pw#zE^a4||@TA*zm66~}BJD4`yRsy$O%#qWx%7g(4ezcI~wp(fXg(*nI zVk|*EJXnv7*p6~k<2@Y45gbE3PNESlIFHY80aww1ZuBfG;~X<^2LsSHv10_nFd9)9 z3nyX`hj=6*8FP?^1z3bExUmxXC_*tdq7-jp8+Ky1uDBQbQH>fLK`rXhfJQXoJX&!9 z7tw)E^x&p>y?NZYC`!aDCwg&nIhTAt1NY%aW7`mn5JVyhPQ)S}(~*Ky zq+=cyBMU3A5=AIRDK=p{%2A0uIEX5|kHguV^kWP(pb<@Ig+4x9LOZ(9joawM03?SC z1EUa*NKC;r%s?ViF&pWahfL@bjNFp0p?_xQ*;E9Zb7vFEbZ$0s;lUcL#d?%u2X=O#XSb@V*sOaITwgRG~y7C>6n2; zBq13on1>9wk&OZrVH37sJIYakO6jwI?#yOalaUWMkm9l=pssbaEj*i&T0totiY4GFo)*NrysowjmhX{u z3kCCAbW$juXV{H6t|3aKQXkqVh6;ow_?A!7$J?lxTmke!6-Ltqj%3CWHjCw-<6_4h zOi5*%*h!2xz35H4aK@UYkH*{Sf?|505j*Gr$9%qz{6B#jI$$mxkVOYL=zxpZN9^n4 z^k2*mS|d#9r66;-E67`XvBFxTleHP{;m(6r(16x()?n69)8p<6*y~@bt)h*hZK6%0EusxF7g_|`9@-q*8rm4z7TOfMu@71jS`bM#- zKr;UuIW1KB9TW@zA+Lxc7JG+=A-uTRGnlhd{8pU+3A?_8XX68+i7S@~t8X}=O< zE`1rD`GJFp4&A0^wdQmyG^aJAHJ|%10L^60W6feGG>h-8B!39mctSRVkhK%C{4C*Q caEZQY&U7s`i@x;mMH9`{E90#v4t3lA0Yh3Ei2wiq delta 4399 zcmeI#e@vA39l-I=0|y=_5_CupM(BwY^`?x`6B%BwSJyChT7NKVEk!`AN|8Ee&1M|o z&~=@$))Bv^F|9SuEUURXW;kMuA+EKGR2^ZuSwm5&b<9}G(9sTm%OZ)OD1l+ryv5ycQW1EeitCCOF2J(AOL$ zC2rx}IpH72{VZneC>D;A?*vk0e!woJffzH!S!TtU7oAV?+~~~X`7`Glp39Rmt(fpH zl5SXTf3}ECWINlDfASE!NW5Dlu~?+AMCAEWkwCdfZMA9qdWO4Vib#$}Y{@a!~N@Cjxx2ibHeSl$2VRKN-}E zeJb*h^9?==wy6vN$iU`Iwu`iHid?`=YNt~Vr(yAy4{-3 zo~|kwWm}69yJZ@VJr=ZhBXPqkNr_!%>(*1@LOu#mjtZ>9dThdG?7$n?ix66H0&Qr= zIdtF>E~5v%_yPmPLH3ZLJpvBIA^}dgkOnuV!UN5K4}KINfI<|Z1f^JxDy+d;)Swox zU=!-G4R2s)kf&ea*J!|g97Ho((291PMh7}^8Qthb9|mv}5%kH1SnU*?NJcs`kcDh` z;X?sN2YAx{U6X6(2gjc^r;dAqI$eYklwvumuo`PngIa9DW^Bjn*oEELix3*ogl4>p z7PR6t{)h|c!d3L(3k;NqnR$GCFqP1zBLi8;h8I2*Ab=t)L^&!@ja68O8oZ2GP>*fc ziCx%>5SnlR@8Sqfp$+F2%VO!|&1H0>57%)UcVOoZK<@$)kO&vjFcq1|g%@)%AB#|o z3h0f7S$6X2sBY<(ep7uc)m%CGQ1CgOUyW7x5!Rs&8?hN%upK)P!ag)$KN``512~8l zwBiI#p#z<`ieB7--oZq`hUhY`13PaVh($6|kO~(vkp&-Sp%6t_h(#zy2})6pRalK$ z)L|>?u?u^!7a=sF3C%&C4xzc{Wv+FCkT+xZb7x;QrY+U zO^mh>qI3e3M|chpopfT-gdES{`gwY5>d#@PW)L0Z(hu)Urjg@ieIF+ak{{;;M6{n1 zg>GEQ59gh2u^!Xg+7SKN&WPNN2D;&;8+kON3n%DC72T*M7hY0v8_ncFZ-qZMKWs}1 zFFY5rf;yQ3Cu6CO)jCe=7_H;8j?FqQ>zLdM9g}rD*0ET}VI6}9phK??yNS?YSBG33 zZgr@=!LM=u(RcqHZ$8n9;Lw~iLvQr;!A`V|{OG~04$U|-y!{8h)enX?qObq9)!{`E za)x`X1oQKcs+T^b$4j%MNzxo?iYlQg(hO;WG(Vc2ozUWFZ4Nb;-DqkKnHf#8td&g-SJ}F$L3DbOOxwKkZ zEUlH6$`37-R!R${bt;EesSaAB-Ow6oiL^o*AdQcPN28;`>0|Q??w$2l&2P@fL>x~s z2hXRO1Ak8ct|axbiuOobq#e=*x!{HeJ`^B;6iRY|f)u^LZcq?61*xMTH_g0@Pi?z6 h$@FwDH}$qhP2pcE&4u$W({a%i{ijsnyx()h{x2TF{CEHW diff --git a/Pyboard Editor.pdf b/Pyboard Editor.pdf index c448f15376f3be3dd81695a6b84b0e2d87a1103f..c29874743b586bb5bda5129b3e6277f0cc8c992d 100644 GIT binary patch delta 31437 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 P4a5yZq@og6ltBDnTK4D5 delta 31656 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 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: