Skip to content

Commit e208623

Browse files
committed
Added Bracket Match; some polishing otherwise
1 parent 6514950 commit e208623

File tree

11 files changed

+982
-298
lines changed

11 files changed

+982
-298
lines changed

Pyboard Editor.doc

1.5 KB
Binary file not shown.

Pyboard Editor.pdf

2.15 KB
Binary file not shown.

README.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ A small text editor written in Python running on PYBoard and WiPy, allowing to e
77
- Use USB_VCP/Telnet or UART for input and output.
88
- Changed the read keyboard function to comply with slow byte-by-byte input on serial lines.
99
- Added support for Tab, BackTab, Save, Del and Backspace joining lines, Find, Replace, Goto Line, Undo, Get file, Auto-Indent, Set Flags, Copy/Delete & Paste, Indent, Un-Indent
10-
- handling tab (0x09) on reading & writing files,
11-
- Added a status line, line number column and single line prompts for
10+
- Handling tab (0x09) on reading & writing files,
11+
- Added a status line, and single line prompts for Quit, Save, Find, Replace, Goto, Get file and Flag settings.
1212
- Support of the basic mouse functions scrolling up/down and setting the cursor (not WiPy).
1313

1414
The editor assumes a VT100 terminal. It works in Insert mode. Cursor Keys, Home, End, PgUp, PgDn, Del and Backspace work as you would expect. The additional functions like FIND etc. are available with Ctrl-Keys. On reading files, tab characters are expanded to spaces with a tab size of 8, and trailing white space on a line will be discarded. The orginal state of tabs will not be restored when the file is written. Optionally, tabs can be written when saving the file, replacing spaces with tabs when possible. The screen size is determined, when the editor is started or when the Redraw-key (Ctrl-E) is hit.
@@ -19,7 +19,6 @@ The editor works also well in a Linux or MAC terminal environment, with both pyt
1919

2020
- pye.py: Source file with comments and code for PyBoard, WiPy and Linux micropython/python3. Runs on PyBoard as well, but the file size is much larger than the stripped down version.
2121
- pye2.py: a variant of pye.py which does not change the cursor column during vertical moves.
22-
- pye3.py: a variant of pye.py which tries to keep the cursor column during vertical moves as good as possible.
2322
- Pyboard Editor.pdf: A short documentation
2423
- README.md: This one
2524
- pe.py: Condensed source file for PyBoard with all functions
@@ -29,7 +28,7 @@ The editor works also well in a Linux or MAC terminal environment, with both pyt
2928
a) find_in_file() supporting regular expressions,
3029
b) line_edit() supporting the cursor left/right/home/end keys, and
3130
c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, but maybe useful)
32-
- strip.sh: sample Shell script which creates the different variants out of pye.py using cpp
31+
- strip.sh: sample Shell script which creates the different variants out of pye.py using cpp, including variants of wipye.py with either speed up scrolling or support replace or support got bracket.
3332

3433
**Short Version History**
3534

@@ -118,6 +117,12 @@ c) expandtabs() and packtabs() with a second argument for tabsize (not for pye,
118117

119118
**1.11** Minor fixes
120119
- Change the way a marked area is highlighted from reverse to a different background color. That works well for black chars on yellow background (code 43). For white chars on black background, the setting for background color in the function hilite() has to be changed, e.g. to blue (code 44).
121-
- Save file to a temporary file first, and rename it to the target name when successfully written.
122-
- Lazy screen update: defer screen update, until all chars from the keyboard are processed.
123-
- Use os.unlink() instead of os.remove(), because remove is not suported by unix micropython
120+
- Save to a temporary file first, and rename it to the target name when successfully written.
121+
- 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.
122+
123+
**1.12** Bracket Match and Minor changes
124+
- 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.
125+
- On Copy the mark will be cleared, since it is assumed that the just copied lines will not be overwritten.
126+
- Ctrl-A at least toggles autoindent in the minimal version.
127+
- 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.
128+
- Some editorial changes and fixign of tyops.

pe.py

Lines changed: 102 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class Editor:
44
b"\x1b[A" : 0x0b,
55
b"\x1b[B" : 0x0d,
66
b"\x1b[D" : 0x1f,
7-
b"\x1b[C" : 0x0f,
7+
b"\x1b[C" : 0x1e,
88
b"\x1b[H" : 0x10,
99
b"\x1bOH" : 0x10,
1010
b"\x1b[1~": 0x10,
@@ -36,12 +36,13 @@ class Editor:
3636
b"\x0c" : 0x0c,
3737
b"\x14" : 0x14,
3838
b"\x02" : 0x02,
39-
b"\x1b[M" : 0x1b,
4039
b"\x01" : 0x01,
40+
b"\x0f" : 0x0f,
41+
b"\x1b[M" : 0x1b,
4142
b"\x1b[1;5H": 0x14,
4243
b"\x1b[1;5F": 0x02,
4344
b"\x1b[3;5~": 0x18,
44-
b"\x0f" : 0x1e,
45+
b"\x0b" : 0xfffe,
4546
}
4647
def __init__(self, tab_size, undo_limit):
4748
self.top_line = self.cur_line = self.row = self.col = self.margin = 0
@@ -121,7 +122,6 @@ def set_screen_parms(self):
121122
(self.height, self.width) = [int(i, 10) for i in pos[2:].split(b';')]
122123
self.height -= 1
123124
self.scrbuf = [(False,"\x00")] * self.height
124-
self.scroll_region(self.height)
125125
def get_input(self):
126126
while True:
127127
in_buffer = self.rd()
@@ -191,8 +191,8 @@ def spaces(self, line, pos = None):
191191
return (len(line) - len(line.lstrip(" ")) if pos == None else
192192
len(line[:pos]) - len(line[:pos].rstrip(" ")))
193193
def line_range(self):
194-
return ((self.mark, self.cur_line + 1) if self.mark < self.cur_line else
195-
(self.cur_line, self.mark + 1))
194+
return ((self.mark, self.cur_line + 1) if self.mark < self.cur_line else
195+
(self.cur_line, self.mark + 1))
196196
def line_edit(self, prompt, default):
197197
self.goto(self.height, 0)
198198
self.hilite(1)
@@ -238,14 +238,12 @@ def find_in_file(self, pattern, pos, end):
238238
self.col = match + spos
239239
self.cur_line = line
240240
return len(pattern)
241-
def cursor_down(self):
242-
if self.cur_line < self.total_lines - 1:
243-
self.cur_line += 1
244-
if self.cur_line == self.top_line + self.height:
245-
self.scroll_down(1)
246241
def handle_cursor_keys(self, key):
247242
if key == 0x0d:
248-
self.cursor_down()
243+
if self.cur_line < self.total_lines - 1:
244+
self.cur_line += 1
245+
if self.cur_line == self.top_line + self.height:
246+
self.scroll_down(1)
249247
elif key == 0x0b:
250248
if self.cur_line > 0:
251249
self.cur_line -= 1
@@ -259,10 +257,12 @@ def handle_cursor_keys(self, key):
259257
self.scroll_up(1)
260258
else:
261259
self.col -= 1
262-
elif key == 0x0f:
260+
elif key == 0x1e:
263261
if self.col >= len(self.content[self.cur_line]) and self.cur_line < self.total_lines - 1:
264-
self.cursor_down()
265262
self.col = 0
263+
self.cur_line += 1
264+
if self.cur_line == self.top_line + self.height:
265+
self.scroll_down(1)
266266
else:
267267
self.col += 1
268268
elif key == 0x10:
@@ -290,6 +290,20 @@ def handle_cursor_keys(self, key):
290290
self.row = self.height >> 1
291291
except:
292292
pass
293+
elif key == 0x01:
294+
self.autoindent = 'y' if self.autoindent != 'y' else 'n'
295+
self.autoindent = 'y' if self.autoindent != 'y' else 'n'
296+
pat = self.line_edit("Case Sensitive Search {}, Autoindent {}, Tab Size {}, Write Tabs {}: ".format(self.case, self.autoindent, self.tab_size, self.write_tabs), "")
297+
try:
298+
res = [i.strip().lower() for i in pat.split(",")]
299+
if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n'
300+
if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n'
301+
if res[2]:
302+
try: self.tab_size = int(res[2])
303+
except: pass
304+
if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n'
305+
except:
306+
pass
293307
elif key == 0x1b:
294308
if self.mouse_y < self.height:
295309
self.col = self.mouse_x + self.margin
@@ -306,23 +320,53 @@ def handle_cursor_keys(self, key):
306320
self.top_line = min(self.top_line + 3, self.total_lines - 1)
307321
self.cur_line = max(self.cur_line, self.top_line)
308322
self.scroll_down(3)
309-
elif key == 0x01:
310-
pat = self.line_edit("Case Sensitive Search {}, Autoindent {}, Tab Size {}, Write Tabs {}: ".format(self.case, self.autoindent, self.tab_size, self.write_tabs), "")
311-
try:
312-
res = [i.strip().lower() for i in pat.split(",")]
313-
if res[0]: self.case = 'y' if res[0][0] == 'y' else 'n'
314-
if res[1]: self.autoindent = 'y' if res[1][0] == 'y' else 'n'
315-
if res[2]:
316-
try: self.tab_size = int(res[2])
317-
except: pass
318-
if res[3]: self.write_tabs = 'y' if res[3][0] == 'y' else 'n'
319-
except:
320-
pass
323+
elif key == 0xfffe:
324+
opening = "([{<"
325+
closing = ")]}>"
326+
level = 0
327+
pos = self.col
328+
srch = self.content[self.cur_line][pos]
329+
i = opening.find(srch)
330+
if i >= 0:
331+
pos += 1
332+
match = closing[i]
333+
for i in range(self.cur_line, self.total_lines):
334+
for c in range(pos, len(self.content[i])):
335+
if self.content[i][c] == match:
336+
if level == 0:
337+
self.cur_line = i
338+
self.col = c
339+
return True
340+
else:
341+
level -= 1
342+
elif self.content[i][c] == srch:
343+
level += 1
344+
pos = 0
345+
else:
346+
i = closing.find(srch)
347+
if i >= 0:
348+
pos -= 1
349+
match = opening[i]
350+
for i in range(self.cur_line, -1, -1):
351+
for c in range(pos, -1, -1):
352+
if self.content[i][c] == match:
353+
if level == 0:
354+
self.cur_line = i
355+
self.col = c
356+
return True
357+
else:
358+
level -= 1
359+
elif self.content[i][c] == srch:
360+
level += 1
361+
if i > 0:
362+
pos = len(self.content[i - 1]) - 1
321363
elif key == 0x14:
322364
self.cur_line = 0
323365
elif key == 0x02:
324366
self.cur_line = self.total_lines - 1
325367
self.row = self.height - 1
368+
elif key == 0x0c:
369+
self.mark = self.cur_line if self.mark == None else None
326370
else:
327371
return False
328372
return True
@@ -334,9 +378,10 @@ def undo_add(self, lnum, text, key, span = 1):
334378
del self.undo[0]
335379
self.undo_zero -= 1
336380
self.undo.append((lnum, span, text, key, self.col))
337-
def delete_lines(self, yank):
381+
def delete_lines(self, yank):
338382
lrange = self.line_range()
339-
if yank: self.yank_buffer = self.content[lrange[0]:lrange[1]]
383+
if yank:
384+
self.yank_buffer = self.content[lrange[0]:lrange[1]]
340385
self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], 0, 0)
341386
del self.content[lrange[0]:lrange[1]]
342387
if self.content == []:
@@ -412,22 +457,23 @@ def handle_edit_key(self, key):
412457
self.col -= ni
413458
elif key == 0x12:
414459
count = 0
415-
pat = self.line_edit("Find: ", self.find_pattern)
460+
pat = self.line_edit("Replace: ", self.find_pattern)
416461
if pat:
417-
rpat = self.line_edit("Replace with: ", self.replc_pattern)
418-
if rpat != None:
462+
rpat = self.line_edit("With: ", self.replc_pattern)
463+
if rpat != None:
419464
self.replc_pattern = rpat
420465
q = ''
466+
cur_line = self.cur_line
421467
if self.mark != None:
422-
lrange = self.line_range()
423-
self.cur_line = lrange[0]
424-
else:
425-
lrange = (self.cur_line, self.total_lines)
426-
while True:
427-
ni = self.find_in_file(pat, self.col, lrange[1])
428-
if ni:
468+
(self.cur_line, end_line) = self.line_range()
469+
self.col = 0
470+
else:
471+
end_line = self.total_lines
472+
self.message = "Replace (yes/No/all/quit) ? "
473+
while True:
474+
ni = self.find_in_file(pat, self.col, end_line)
475+
if ni:
429476
if q != 'a':
430-
self.message = "Replace (yes/No/all/quit) ? "
431477
self.display_window()
432478
key = self.get_input()
433479
q = chr(key).lower()
@@ -440,26 +486,26 @@ def handle_edit_key(self, key):
440486
count += 1
441487
else:
442488
self.col += 1
443-
else:
489+
else:
444490
break
491+
self.cur_line = cur_line
445492
self.message = "'{}' replaced {} times".format(pat, count)
446-
elif key == 0x1e:
493+
elif key == 0x0f:
447494
fname = self.line_edit("Insert File: ", "")
448495
if fname:
449496
(content, self.message) = self.get_file(fname)
450497
if content:
451498
self.undo_add(self.cur_line, None, 0, -len(content))
452499
self.content[self.cur_line:self.cur_line] = content
453500
self.total_lines = len(self.content)
454-
elif key == 0x0c:
455-
self.mark = self.cur_line if self.mark == None else None
456501
elif key == 0x18:
457502
if self.mark != None:
458503
self.delete_lines(True)
459504
elif key == 0x04:
460505
if self.mark != None:
461506
lrange = self.line_range()
462507
self.yank_buffer = self.content[lrange[0]:lrange[1]]
508+
self.mark = None
463509
elif key == 0x16:
464510
if self.yank_buffer:
465511
if self.mark != None:
@@ -468,16 +514,16 @@ def handle_edit_key(self, key):
468514
self.content[self.cur_line:self.cur_line] = self.yank_buffer
469515
self.total_lines += len(self.yank_buffer)
470516
elif key == 0x13:
471-
if False: pass
472-
elif self.mark != None:
473-
fname = self.line_edit("Save Mark: ", "")
474-
lrange = self.line_range()
475-
else:
476-
fname = self.fname
477-
if fname == None:
478-
fname = ""
479-
fname = self.line_edit("Save File: ", fname)
480-
lrange = (0, self.total_lines)
517+
if True:
518+
if self.mark != None:
519+
fname = self.line_edit("Save Mark: ", "")
520+
lrange = self.line_range()
521+
else:
522+
fname = self.fname
523+
if fname == None:
524+
fname = ""
525+
fname = self.line_edit("Save File: ", fname)
526+
lrange = (0, self.total_lines)
481527
if fname:
482528
try:
483529
with open("tmpfile.pye", "w") as f:
@@ -509,6 +555,7 @@ def handle_edit_key(self, key):
509555
del self.content[action[0]:action[0] - action[1]]
510556
self.total_lines = len(self.content)
511557
self.changed = ' ' if len(self.undo) == self.undo_zero else '*'
558+
self.mark = None
512559
elif key >= 0x20:
513560
self.mark = None
514561
self.undo_add(self.cur_line, [l], 0x20 if key == 0x20 else 0x41)
@@ -520,6 +567,7 @@ def edit_loop(self):
520567
self.total_lines = len(self.content)
521568
self.set_screen_parms()
522569
self.mouse_reporting(True)
570+
self.scroll_region(self.height)
523571
while True:
524572
if self.not_pending():
525573
self.display_window()
@@ -590,8 +638,7 @@ def pye(content = None, tab_size = 4, undo = 50, device = 0, baud = 115200):
590638
print (e.message)
591639
return
592640
elif type(content) == list and len(content) > 0 and type(content[0]) == str:
593-
594-
e.content = content
641+
e.content = content
595642
e.init_tty(device, baud)
596643
e.edit_loop()
597644
e.deinit_tty()

0 commit comments

Comments
 (0)