Skip to content

Commit 7e1badc

Browse files
committed
V2: supports editing multiple files at once
1 parent 515c8de commit 7e1badc

File tree

12 files changed

+1777
-1216
lines changed

12 files changed

+1777
-1216
lines changed

Pyboard Editor.doc

1 KB
Binary file not shown.

Pyboard Editor.pdf

11.7 KB
Binary file not shown.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ a) find_in_file() supporting regular expressions,
2929
b) line_edit() supporting the cursor left/right/home/end keys, and
3030
c) expandtabs() and packtabs() with a second argument for tabsize (not for pye, but maybe useful)
3131
- 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.
32+
- pye_vt.py: a variant of pye.py, where all directly screen related functions are placed into a separate class. That's a better style, however it uses more memory.
3233

3334
**Short Version History**
3435

@@ -143,3 +144,9 @@ anyhow called one after the other, resulting in a enormous long function handlin
143144
- Move a few lines around, such that keys which are more probable for fast repeats are checked for earlier.
144145
- Some editorial changes
145146

147+
**2.0** Edit muliple files
148+
- Support for editing mutiple files and copy/paste between them
149+
- Ctrl-W steps through the list of files/buffers
150+
- Ctrl-O opens a new file/buffer.
151+
152+

pe.py

Lines changed: 130 additions & 152 deletions
Large diffs are not rendered by default.

pe2.py

Lines changed: 157 additions & 180 deletions
Large diffs are not rendered by default.

pemin.py

Lines changed: 120 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ class Editor:
1111
b"\x1b[F" : 0x03,
1212
b"\x1bOF" : 0x03,
1313
b"\x1b[4~": 0x03,
14-
b"\x1b[5~": 0x17,
15-
b"\x1b[6~": 0x19,
14+
b"\x1b[5~": 0xfff1,
15+
b"\x1b[6~": 0xfff3,
1616
b"\x03" : 0x11,
1717
b"\r" : 0x0a,
1818
b"\x7f" : 0x08,
@@ -25,7 +25,7 @@ class Editor:
2525
def __init__(self, tab_size, undo_limit):
2626
self.top_line = self.cur_line = self.row = self.col = self.margin = 0
2727
self.tab_size = tab_size
28-
self.changed = " "
28+
self.changed = ""
2929
self.message = self.fname = ""
3030
self.content = [""]
3131
self.undo = []
@@ -34,6 +34,8 @@ def __init__(self, tab_size, undo_limit):
3434
self.case = "n"
3535
self.autoindent = "y"
3636
self.mark = None
37+
serialcomm = 0
38+
sdev = 0
3739
if sys.platform == "pyboard":
3840
def wr(self,s):
3941
ns = 0
@@ -49,15 +51,15 @@ def rd(self):
4951
return self.serialcomm.read(1)
5052
def init_tty(self, device, baud):
5153
import pyb
52-
self.sdev = device
53-
if self.sdev:
54-
self.serialcomm = pyb.UART(device, baud)
54+
Editor.sdev = device
55+
if Editor.sdev:
56+
Editor.serialcomm = pyb.UART(device, baud)
5557
else:
56-
self.serialcomm = pyb.USB_VCP()
57-
self.serialcomm.setinterrupt(-1)
58+
Editor.serialcomm = pyb.USB_VCP()
59+
Editor.serialcomm.setinterrupt(-1)
5860
def deinit_tty(self):
59-
if not self.sdev:
60-
self.serialcomm.setinterrupt(3)
61+
if not Editor.sdev:
62+
Editor.serialcomm.setinterrupt(3)
6163
def goto(self, row, col):
6264
self.wr("\x1b[{};{}H".format(row + 1, col + 1))
6365
def clear_to_eol(self):
@@ -71,17 +73,24 @@ def hilite(self, mode):
7173
self.wr(b"\x1b[43m")
7274
else:
7375
self.wr(b"\x1b[0m")
74-
def set_screen_parms(self):
75-
self.cursor(False)
76+
def get_screen_size(self):
7677
self.wr('\x1b[999;999H\x1b[6n')
7778
pos = b''
7879
char = self.rd()
7980
while char != b'R':
8081
pos += char
8182
char = self.rd()
82-
(self.height, self.width) = [int(i, 10) for i in pos[2:].split(b';')]
83-
self.height -= 1
84-
self.scrbuf = [(False,"\x00")] * self.height
83+
return [int(i, 10) for i in pos[2:].split(b';')]
84+
def redraw(self, flag):
85+
self.cursor(False)
86+
Editor.height, Editor.width = self.get_screen_size()
87+
Editor.height -= 1
88+
Editor.scrbuf = [(False,"\x00")] * Editor.height
89+
self.row = min(Editor.height - 1, self.row)
90+
if sys.implementation.name == "micropython":
91+
gc.collect()
92+
if flag:
93+
self.message = "{} Bytes Memory available".format(gc.mem_free())
8594
def get_input(self):
8695
while True:
8796
in_buffer = self.rd()
@@ -100,39 +109,39 @@ def get_input(self):
100109
def display_window(self):
101110
self.cur_line = min(self.total_lines - 1, max(self.cur_line, 0))
102111
self.col = max(0, min(self.col, len(self.content[self.cur_line])))
103-
if self.col >= self.width + self.margin:
104-
self.margin = self.col - self.width + (self.width >> 2)
112+
if self.col >= Editor.width + self.margin:
113+
self.margin = self.col - Editor.width + (Editor.width >> 2)
105114
elif self.col < self.margin:
106-
self.margin = max(self.col - (self.width >> 2), 0)
107-
if not (self.top_line <= self.cur_line < self.top_line + self.height):
115+
self.margin = max(self.col - (Editor.width >> 2), 0)
116+
if not (self.top_line <= self.cur_line < self.top_line + Editor.height):
108117
self.top_line = max(self.cur_line - self.row, 0)
109118
self.row = self.cur_line - self.top_line
110119
self.cursor(False)
111120
i = self.top_line
112-
for c in range(self.height):
121+
for c in range(Editor.height):
113122
if i == self.total_lines:
114-
if self.scrbuf[c] != (False,''):
123+
if Editor.scrbuf[c] != (False,''):
115124
self.goto(c, 0)
116125
self.clear_to_eol()
117-
self.scrbuf[c] = (False,'')
126+
Editor.scrbuf[c] = (False,'')
118127
else:
119128
l = (self.mark != None and (
120129
(self.mark <= i <= self.cur_line) or (self.cur_line <= i <= self.mark)),
121-
self.content[i][self.margin:self.margin + self.width])
122-
if l != self.scrbuf[c]:
130+
self.content[i][self.margin:self.margin + Editor.width])
131+
if l != Editor.scrbuf[c]:
123132
self.goto(c, 0)
124133
if l[0]: self.hilite(2)
125134
self.wr(l[1])
126-
if len(l[1]) < self.width:
135+
if len(l[1]) < Editor.width:
127136
self.clear_to_eol()
128137
if l[0]: self.hilite(0)
129-
self.scrbuf[c] = l
138+
Editor.scrbuf[c] = l
130139
i += 1
131-
self.goto(self.height, 0)
140+
self.goto(Editor.height, 0)
132141
self.hilite(1)
133-
self.wr("[{}] {} Row: {} Col: {} {}".format(
134-
self.total_lines, self.changed, self.cur_line + 1,
135-
self.col + 1, self.message[:self.width - 25]))
142+
self.wr("{}{} Row: {}/{} Col: {} {}".format(
143+
self.changed, self.fname, self.cur_line + 1, self.total_lines,
144+
self.col + 1, self.message[:Editor.width - 25 - len(self.fname)]))
136145
self.clear_to_eol()
137146
self.hilite(0)
138147
self.goto(self.row, self.col - self.margin)
@@ -144,7 +153,7 @@ def line_range(self):
144153
return ((self.mark, self.cur_line + 1) if self.mark < self.cur_line else
145154
(self.cur_line, self.mark + 1))
146155
def line_edit(self, prompt, default):
147-
self.goto(self.height, 0)
156+
self.goto(Editor.height, 0)
148157
self.hilite(1)
149158
self.wr(prompt)
150159
self.wr(default)
@@ -166,11 +175,11 @@ def line_edit(self, prompt, default):
166175
self.wr('\b \b' * len(res))
167176
res = ''
168177
elif 0x20 <= key < 0xfff0:
169-
if len(prompt) + len(res) < self.width - 2:
178+
if len(prompt) + len(res) < Editor.width - 2:
170179
res += chr(key)
171180
self.wr(chr(key))
172181
def find_in_file(self, pattern, pos, end):
173-
self.find_pattern = pattern
182+
Editor.find_pattern = pattern
174183
if self.case != "y":
175184
pattern = pattern.lower()
176185
spos = pos
@@ -197,7 +206,7 @@ def undo_add(self, lnum, text, key, span = 1):
197206
def delete_lines(self, yank):
198207
lrange = self.line_range()
199208
if yank:
200-
self.yank_buffer = self.content[lrange[0]:lrange[1]]
209+
Editor.yank_buffer = self.content[lrange[0]:lrange[1]]
201210
self.undo_add(lrange[0], self.content[lrange[0]:lrange[1]], 0, 0)
202211
del self.content[lrange[0]:lrange[1]]
203212
if self.content == []:
@@ -241,24 +250,24 @@ def handle_edit_keys(self, key):
241250
self.col = self.spaces(l) if self.col == 0 else 0
242251
elif key == 0x03:
243252
self.col = len(l)
244-
elif key == 0x17:
245-
self.cur_line -= self.height
246-
elif key == 0x19:
247-
self.cur_line += self.height
253+
elif key == 0xfff1:
254+
self.cur_line -= Editor.height
255+
elif key == 0xfff3:
256+
self.cur_line += Editor.height
248257
elif key == 0x06:
249-
pat = self.line_edit("Find: ", self.find_pattern)
258+
pat = self.line_edit("Find: ", Editor.find_pattern)
250259
if pat:
251260
self.find_in_file(pat, self.col, self.total_lines)
252-
self.row = self.height >> 1
261+
self.row = Editor.height >> 1
253262
elif key == 0x0e:
254-
if self.find_pattern:
255-
self.find_in_file(self.find_pattern, self.col + 1, self.total_lines)
256-
self.row = self.height >> 1
263+
if Editor.find_pattern:
264+
self.find_in_file(Editor.find_pattern, self.col + 1, self.total_lines)
265+
self.row = Editor.height >> 1
257266
elif key == 0x07:
258267
line = self.line_edit("Goto Line: ", "")
259268
if line:
260269
self.cur_line = int(line) - 1
261-
self.row = self.height >> 1
270+
self.row = Editor.height >> 1
262271
elif key == 0x01:
263272
self.autoindent = 'y' if self.autoindent != 'y' else 'n'
264273
elif key == 0xfffd:
@@ -321,8 +330,8 @@ def handle_edit_keys(self, key):
321330
if len(self.content[i]) > 0:
322331
self.content[i] = ' ' * (self.tab_size - self.spaces(self.content[i]) % self.tab_size) + self.content[i]
323332
else:
324-
self.undo_add(self.cur_line, [l], 0x09)
325333
ni = self.tab_size - self.col % self.tab_size
334+
self.undo_add(self.cur_line, [l], 0x09)
326335
self.content[self.cur_line] = l[:self.col] + ' ' * ni + l[self.col:]
327336
self.col += ni
328337
elif key == 0x15:
@@ -345,23 +354,22 @@ def handle_edit_keys(self, key):
345354
elif key == 0x04:
346355
if self.mark != None:
347356
lrange = self.line_range()
348-
self.yank_buffer = self.content[lrange[0]:lrange[1]]
357+
Editor.yank_buffer = self.content[lrange[0]:lrange[1]]
349358
self.mark = None
350359
elif key == 0x16:
351-
if self.yank_buffer:
360+
if Editor.yank_buffer:
352361
if self.mark != None:
353362
self.delete_lines(False)
354-
self.undo_add(self.cur_line, None, 0, -len(self.yank_buffer))
355-
self.content[self.cur_line:self.cur_line] = self.yank_buffer
356-
self.total_lines += len(self.yank_buffer)
363+
self.undo_add(self.cur_line, None, 0, -len(Editor.yank_buffer))
364+
self.content[self.cur_line:self.cur_line] = Editor.yank_buffer
365+
self.total_lines += len(Editor.yank_buffer)
357366
elif key == 0x13:
358-
if True:
359-
fname = self.line_edit("Save File: ", self.fname)
360-
if fname:
361-
self.put_file(fname, 0, self.total_lines)
362-
self.changed = ' '
363-
self.undo_zero = len(self.undo)
364-
self.fname = fname
367+
fname = self.line_edit("Save File: ", self.fname)
368+
if fname:
369+
self.put_file(fname)
370+
self.changed = ''
371+
self.undo_zero = len(self.undo)
372+
if not self.fname: self.fname = fname
365373
elif key == 0x1a:
366374
if len(self.undo) > 0:
367375
action = self.undo.pop(-1)
@@ -376,50 +384,49 @@ def handle_edit_keys(self, key):
376384
else:
377385
del self.content[action[0]:action[0] - action[1]]
378386
self.total_lines = len(self.content)
379-
self.changed = ' ' if len(self.undo) == self.undo_zero else '*'
387+
if len(self.undo) == self.undo_zero:
388+
self.changed = ''
380389
self.mark = None
390+
elif key == 0x05:
391+
self.redraw(True)
381392
def edit_loop(self):
382-
if self.content == []:
393+
if not self.content:
383394
self.content = [""]
384395
self.total_lines = len(self.content)
385-
key = 0x05
396+
self.redraw(self.message == "")
386397
while True:
387398
try:
388-
if key == 0x05:
389-
self.set_screen_parms()
390-
self.row = min(self.height - 1, self.row)
391-
if sys.implementation.name == "micropython":
392-
gc.collect()
393-
self.message = "{} Bytes Memory available".format(gc.mem_free())
394399
if not self.rd_any():
395400
self.display_window()
396401
key = self.get_input()
397402
self.message = ''
398403
if key == 0x11:
399-
if self.changed != ' ':
404+
if self.changed != '':
400405
res = self.line_edit("Content changed! Quit without saving (y/N)? ", "N")
401406
if not res or res[0].upper() != 'Y':
402407
continue
403-
self.goto(self.height, 0)
404-
self.clear_to_eol()
405-
return None
408+
return (key, "")
409+
elif key == 0x17:
410+
return (key, "")
411+
elif key == 0x0f:
412+
return (key, self.line_edit("Open file: ", ""))
406413
else: self.handle_edit_keys(key)
407414
except Exception as err:
408-
self.message = "{}".format(err)
415+
self.message = "{!r}".format(err)
409416
def get_file(self, fname):
417+
self.fname = fname
410418
try:
411419
with open(fname) as f:
412-
content = f.readlines()
420+
self.content = f.readlines()
413421
except Exception as err:
414-
message = 'Could not load {}, {!r}'.format(fname, err)
415-
return (None, message)
416-
for i in range(len(content)):
417-
content[i] = expandtabs(content[i].rstrip('\r\n\t '))
418-
return (content, "")
419-
def put_file(self, fname, start, stop):
422+
self.content, self.message = [""], "{!r}".format(err)
423+
else:
424+
for i in range(len(self.content)):
425+
self.content[i] = expandtabs(self.content[i].rstrip('\r\n\t '))
426+
def put_file(self, fname):
420427
import os
421428
with open("tmpfile.pye", "w") as f:
422-
for l in self.content[start:stop]:
429+
for l in self.content:
423430
f.write(l + '\n')
424431
try: os.unlink(fname)
425432
except: pass
@@ -439,18 +446,38 @@ def expandtabs(s):
439446
return sb.getvalue()
440447
else:
441448
return s
442-
def pye(content = None, tab_size = 4, undo = 50, device = 0, baud = 115200):
449+
def pye(*content, tab_size = 4, undo = 50, device = 0, baud = 115200):
443450
gc.collect()
444-
e = Editor(tab_size, undo)
445-
if type(content) == str and content:
446-
e.fname = content
447-
(e.content, e.message) = e.get_file(e.fname)
448-
if e.content == None:
449-
print (e.message)
450-
return
451-
elif type(content) == list and len(content) > 0 and type(content[0]) == str:
452-
e.content = content
453-
e.init_tty(device, baud)
454-
e.edit_loop()
455-
e.deinit_tty()
456-
return e.content if (e.fname == "") else e.fname
451+
if content:
452+
slot = []
453+
index = 0
454+
for f in content:
455+
slot.append(Editor(tab_size, undo))
456+
if type(f) == str and f:
457+
slot[index].get_file(f)
458+
elif type(f) == list and len(f) > 0 and type(f[0]) == str:
459+
slot[index].content = f
460+
index += 1
461+
else:
462+
slot = [Editor(tab_size, undo)]
463+
index = 0
464+
slot[0].init_tty(device, baud)
465+
while True:
466+
key,f = slot[index].edit_loop()
467+
if key == 0x11:
468+
if len(slot) == 1:
469+
break
470+
del slot[index]
471+
index %= len(slot)
472+
elif key == 0x0f:
473+
slot.append(Editor(tab_size, undo))
474+
index = len(slot) - 1
475+
if f:
476+
slot[index].get_file(f)
477+
elif key == 0x17:
478+
index = (index + 1) % len(slot)
479+
slot[0].goto(slot[0].height, 0)
480+
slot[0].clear_to_eol()
481+
slot[0].deinit_tty()
482+
slot[0].undo, Editor.yank_buffer = [],[]
483+
return slot[0].content if (slot[0].fname == "") else slot[0].fname

0 commit comments

Comments
 (0)