├── mip_pkg_mpy_22 ├── shell │ ├── vt100.mpy │ ├── wget.mpy │ ├── __init__.mpy │ ├── editor.mpy │ ├── editstr.mpy │ ├── terminal.mpy │ ├── new_urequests │ │ └── __init__.mpy │ └── octopus_shell_help.txt ├── README.md └── package.json ├── vt100.py ├── octopus_shell_help.txt ├── README.md ├── terminal.py ├── wget.py ├── new_urequests └── __init__.py ├── editstr.py ├── editor.py └── __init__.py /mip_pkg_mpy_22/shell/vt100.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/vt100.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/wget.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/wget.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/__init__.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/editor.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/editor.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/editstr.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/editstr.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/terminal.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/terminal.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/new_urequests/__init__.mpy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/octopusengine/micropython-shell/HEAD/mip_pkg_mpy_22/shell/new_urequests/__init__.mpy -------------------------------------------------------------------------------- /mip_pkg_mpy_22/README.md: -------------------------------------------------------------------------------- 1 | # micropython-shell 2 | 3 | 4 | ## mip - install 5 | 6 | MicroPython v1.19.1-803-g1583c1f67 on 2023-01-16; ESP32 module with ESP32 + 7 | 8 | 9 | ``` 10 | import mip 11 | mip.install("github:octopusengine/micropython-shell/mip_pkg_mpy_22", target=".") 12 | ``` 13 | -------------------------------------------------------------------------------- /vt100.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def get_cursor_position(): 4 | height=0 5 | width=0 6 | num='' 7 | 8 | sys.stdout.write('\x1b[6n') 9 | while True: 10 | char = sys.stdin.read(1) 11 | if char is 'R': 12 | width = int(num) 13 | break 14 | if char is '\x1b': 15 | width=0 16 | height=0 17 | num='' 18 | continue 19 | if char is '[': 20 | continue 21 | if char is ';': 22 | height=int(num) 23 | num='' 24 | continue 25 | num+=char 26 | return (width, height) 27 | 28 | def get_terminal_size(): 29 | # Save cursor position 30 | sys.stdout.write('\x1b[s') 31 | 32 | #Move cursor outside of screen 33 | sys.stdout.write('\x1b[999B\x1b[999C') 34 | (w, h) = get_cursor_position() 35 | 36 | # Restore cursor position 37 | sys.stdout.write('\x1b[u') 38 | 39 | return (w, h) 40 | -------------------------------------------------------------------------------- /mip_pkg_mpy_22/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["shell/__init__.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/__init__.mpy"], 4 | ["shell/vt100.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/vt100.mpy"], 5 | ["shell/terminal.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/terminal.mpy"], 6 | ["shell/wget.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/wget.mpy"], 7 | ["shell/editor.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/editor.mpy"], 8 | ["shell/editstr.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/editstr.mpy"], 9 | ["shell/octopus_shell_help.txt", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/octopus_shell_help.txt"], 10 | 11 | ["shell/new_urequests/__init__.mpy", "github:octopusengine/micropython-shell/mip_pkg_mpy_22/shell/new_urequests/__init__.mpy"], 12 | 13 | ], 14 | "deps": [ 15 | ], 16 | "version": "0.2.0" 17 | } 18 | -------------------------------------------------------------------------------- /octopus_shell_help.txt: -------------------------------------------------------------------------------- 1 | ---------+--------------------------+-------------------- 2 | | F: file name | 3 | | M: "main.py" | octopusLAB 2019-20 4 | ---------+--------------------------+-------------------- 5 | clear | clear display | 6 | free | free RAM (memory) | 7 | df | Disk Free (flash) | 8 | cd | Change Directory | cd examples / cd .. 9 | pwd | Print Working Dir. | 10 | ls | LiSt files and dir. | ls examples 11 | mkdir | make directory | mkdir newdir 12 | cp | CoPy F (defaul M) | cp test.py back.py 13 | rm | ReMove F | rm test.py 14 | top | main proces info | 15 | wifi | wireless "fidelity" | wifi on / wifi scan 16 | ping | Packet InterNet Groper | ping google.com 17 | ifconfig | wifi InterFace conf. | 18 | wget | wget URL subdir download | wget https://my.web/f.py 19 | find | find "text" | find oled 20 | cat | concatenate F (defaul M) | cat back.py 21 | edit| edit (defaul M) | simple "line editor" 22 | ./ | run F | ./examples/blink.py 23 | | run F & > process/thread | run examples/blink.py & 24 | exit | back to uPy | > Micropython 25 | ---------+--------------------------+--------------------- -------------------------------------------------------------------------------- /mip_pkg_mpy_22/shell/octopus_shell_help.txt: -------------------------------------------------------------------------------- 1 | ---------+--------------------------+-------------------- 2 | | F: file name | 3 | | M: "main.py" | octopusLAB 2019-20 4 | ---------+--------------------------+-------------------- 5 | clear | clear display | 6 | free | free RAM (memory) | 7 | df | Disk Free (flash) | 8 | cd | Change Directory | cd examples / cd .. 9 | pwd | Print Working Dir. | 10 | ls | LiSt files and dir. | ls examples 11 | mkdir | make directory | mkdir newdir 12 | cp | CoPy F (defaul M) | cp test.py back.py 13 | rm | ReMove F | rm test.py 14 | top | main proces info | 15 | wifi | wireless "fidelity" | wifi on / wifi scan 16 | ping | Packet InterNet Groper | ping google.com 17 | ifconfig | wifi InterFace conf. | 18 | wget | wget URL subdir download | wget https://my.web/f.py 19 | find | find "text" | find oled 20 | cat | concatenate F (defaul M) | cat back.py 21 | edit| edit (defaul M) | simple "line editor" 22 | ./ | run F | ./examples/blink.py 23 | | run F & > process/thread | run examples/blink.py & 24 | exit | back to uPy | > Micropython 25 | ---------+--------------------------+--------------------- -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # micropython-shell 2 | simple Linux terminal writen in (and for) ESP32-Micropython 3 |
4 | >>> shell() 5 | uPyShell:~/$ 6 |7 | ----- 8 |
9 | ---------+--------------------------+-------------------- 10 | | F: file name | 11 | | M: "main.py" | octopusLAB 2019-20 12 | ---------+--------------------------+-------------------- 13 | clear | clear display | 14 | free | free RAM (memory) | 15 | df | Disk Free (flash) | 16 | cd | Change Directory | cd examples / cd .. 17 | pwd | Print Working Dir. | 18 | ls | LiSt files and dir. | ls examples 19 | mkdir | make directory | mkdir newdir 20 | cp | CoPy F (defaul M) | cp test.py back.py 21 | rm | ReMove F | rm test.py 22 | top | main proces info | 23 | wifi | wireless "fidelity" | wifi on / wifi scan 24 | ping | Packet InterNet Groper | ping google.com 25 | ifconfig | wifi InterFace conf. | 26 | wget | wget URL subdir download | wget https://my.web/f.py 27 | find | find "text" | find oled 28 | cat | concatenate F (defaul M) | cat back.py 29 | edit| edit (defaul M) | simple "line editor" 30 | ./ | run F | ./examples/blink.py 31 | | run F & > process/thread | run examples/blink.py & 32 | exit | back to uPy | > Micropython 33 | ---------+--------------------------+--------------------- 34 |35 | -------------------------------------------------------------------------------- /terminal.py: -------------------------------------------------------------------------------- 1 | # This file is part of the octopusLAB project 2 | # The MIT License (MIT) 3 | # Copyright (c) 2016-2020 Jan Copak, Petr Kracik, Vasek Chalupnicek 4 | 5 | # from shell.terminal import printTitle 6 | 7 | SEPARATOR_WIDTH = 50 8 | 9 | 10 | def terminal_color(txt,col=33): # default yellow 11 | # 30 black / 31 red / 32 green / 33 yellow / 34 blue / 35 violet / 36 cyan 12 | # 21 underline 13 | # print("\033[32mgreen\033[m") 14 | return "\033[" + str(col) + "m" + str(txt) + "\033[m" 15 | 16 | 17 | def runningEffect(num = 16): 18 | from time import sleep_ms 19 | for ii in range(num): 20 | print(".",end="") 21 | sleep_ms(20) 22 | print() 23 | 24 | 25 | def printBar(num1,num2,char="|",col1=32,col2=33): 26 | print("[",end="") 27 | print((("\033[" + str(col1) + "m" + str(char) + "\033[m")*num1),end="") 28 | print((("\033[" + str(col2) + "m" + str(char) + "\033[m")*num2),end="") 29 | print("] ",end="") 30 | 31 | 32 | # --------------- 33 | 34 | def printHead(s): 35 | print() 36 | print('-' * SEPARATOR_WIDTH) 37 | print("[--- " + s + " ---] ") 38 | 39 | 40 | def printTitle(t,w=SEPARATOR_WIDTH): 41 | print() 42 | print('=' * w) 43 | print("|",end="") 44 | print(t.center(w-2),end="") 45 | print("|") 46 | print('=' * w) 47 | 48 | 49 | def printLog(i,s=""): 50 | print() 51 | print('-' * SEPARATOR_WIDTH) 52 | print("[--- " + str(i) + " ---] " + s) 53 | 54 | 55 | def getUid(short = False): # full or short (5 chars: first 2 + last 3) 56 | import machine, ubinascii 57 | id = ubinascii.hexlify(machine.unique_id()).decode() 58 | if short: 59 | return id[:2]+id[-3:] 60 | else: 61 | return id 62 | 63 | 64 | def printInfo(): 65 | import gc #mem_free 66 | print("> mem_free: "+str(gc.mem_free())) 67 | df() 68 | 69 | 70 | def printMachineInfo(): 71 | import machine 72 | print("> machine.freq: "+str(machine.freq())) 73 | -------------------------------------------------------------------------------- /wget.py: -------------------------------------------------------------------------------- 1 | # wget for octopusLAB MicroPython uPyShell 2 | # copyright (c) 2020 Milan Spacek 3 | # License: MIT 4 | 5 | # wget https://www.octopusengine.org/api/text123.txt [subdir] 6 | # default subdir: "download" 7 | 8 | __version__ = "1.0.0" 9 | 10 | def wget(url="",path="/download"): 11 | from shell.new_urequests import get 12 | from os import mkdir 13 | from gc import collect 14 | debug = False 15 | filename = url.split("/")[-1] 16 | 17 | valid_chars = '-_.()abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' 18 | correc_filename = ''.join(c for c in filename if c in valid_chars) 19 | 20 | if filename != correc_filename: 21 | print("Wrong filename corrected:" + correc_filename) 22 | 23 | if path != "": 24 | try: 25 | if path.endswith('/'): 26 | path = path[:-1] 27 | os.mkdir(path) 28 | except: 29 | pass 30 | path = path + "/" 31 | 32 | collect() 33 | try: 34 | if debug: print("get") 35 | res = get(url, stream = True) 36 | if debug: print("get done") 37 | if debug: print(res.status_code) 38 | collect() 39 | if res.status_code == 200: 40 | print("opening file:", path+correc_filename) 41 | f = open(path+correc_filename, 'w') 42 | if debug: print("opened file") 43 | while True: 44 | if debug: print("reading chunk") 45 | chunk = res.raw.read(256) 46 | if debug: print("writing chunk") 47 | #if debug: print(chunk) 48 | f.write(chunk) 49 | if debug: print("chunk writen") 50 | if not chunk: 51 | break 52 | f.close() 53 | print("Done") 54 | elif res.status_code == 404: 55 | print("File not found") 56 | else: 57 | print("Error status:", res.status_code) 58 | except OSError as e: 59 | if e.args[0] == -202: 60 | print("wget: unable to resolve host address") 61 | else: 62 | print("OSError, exception: {0}".format(e)) 63 | except Exception as e: 64 | print("Error, exception: {0}".format(e)) 65 | -------------------------------------------------------------------------------- /new_urequests/__init__.py: -------------------------------------------------------------------------------- 1 | import usocket 2 | 3 | class Response: 4 | 5 | def __init__(self, f): 6 | self.raw = f 7 | self.encoding = "utf-8" 8 | self._cached = None 9 | 10 | def close(self): 11 | if self.raw: 12 | self.raw.close() 13 | self.raw = None 14 | self._cached = None 15 | 16 | @property 17 | def content(self): 18 | if self._cached is None: 19 | try: 20 | self._cached = self.raw.read() 21 | finally: 22 | self.raw.close() 23 | self.raw = None 24 | return self._cached 25 | 26 | @property 27 | def text(self): 28 | return str(self.content, self.encoding) 29 | 30 | def json(self): 31 | import ujson 32 | return ujson.loads(self.content) 33 | 34 | 35 | def request(method, url, data=None, json=None, headers={}, stream=None, parse_headers=True): 36 | redir_cnt = 1 37 | if json is not None: 38 | assert data is None 39 | import ujson 40 | data = ujson.dumps(json) 41 | 42 | while True: 43 | try: 44 | proto, dummy, host, path = url.split("/", 3) 45 | except ValueError: 46 | proto, dummy, host = url.split("/", 2) 47 | path = "" 48 | if proto == "http:": 49 | port = 80 50 | elif proto == "https:": 51 | import ussl 52 | port = 443 53 | else: 54 | raise ValueError("Unsupported protocol: " + proto) 55 | 56 | if ":" in host: 57 | host, port = host.split(":", 1) 58 | port = int(port) 59 | 60 | ai = usocket.getaddrinfo(host, port, 0, usocket.SOCK_STREAM) 61 | ai = ai[0] 62 | 63 | resp_d = None 64 | if parse_headers is not False: 65 | resp_d = {} 66 | 67 | s = usocket.socket(ai[0], ai[1], ai[2]) 68 | try: 69 | s.connect(ai[-1]) 70 | if proto == "https:": 71 | s = ussl.wrap_socket(s, server_hostname=host) 72 | s.write(b"%s /%s HTTP/1.0\r\n" % (method, path)) 73 | if not "Host" in headers: 74 | s.write(b"Host: %s\r\n" % host) 75 | # Iterate over keys to avoid tuple alloc 76 | for k in headers: 77 | s.write(k) 78 | s.write(b": ") 79 | s.write(headers[k]) 80 | s.write(b"\r\n") 81 | if json is not None: 82 | s.write(b"Content-Type: application/json\r\n") 83 | if data: 84 | s.write(b"Content-Length: %d\r\n" % len(data)) 85 | s.write(b"Connection: close\r\n\r\n") 86 | if data: 87 | s.write(data) 88 | 89 | l = s.readline() 90 | #print(l) 91 | l = l.split(None, 2) 92 | status = int(l[1]) 93 | reason = "" 94 | if len(l) > 2: 95 | reason = l[2].rstrip() 96 | while True: 97 | l = s.readline() 98 | if not l or l == b"\r\n": 99 | break 100 | #print(l) 101 | 102 | if l.startswith(b"Transfer-Encoding:"): 103 | if b"chunked" in l: 104 | raise ValueError("Unsupported " + l.decode()) 105 | elif l.startswith(b"Location:") and 300 <= status <= 399: 106 | if not redir_cnt: 107 | raise ValueError("Too many redirects") 108 | redir_cnt -= 1 109 | url = l[9:].decode().strip() 110 | #print("redir to:", url) 111 | status = 300 112 | break 113 | 114 | if parse_headers is False: 115 | pass 116 | elif parse_headers is True: 117 | l = l.decode() 118 | k, v = l.split(":", 1) 119 | resp_d[k] = v.strip() 120 | else: 121 | parse_headers(l, resp_d) 122 | except OSError: 123 | s.close() 124 | raise 125 | 126 | if status != 300: 127 | break 128 | 129 | resp = Response(s) 130 | resp.status_code = status 131 | resp.reason = reason 132 | if resp_d is not None: 133 | resp.headers = resp_d 134 | return resp 135 | 136 | 137 | def head(url, **kw): 138 | return request("HEAD", url, **kw) 139 | 140 | def get(url, **kw): 141 | return request("GET", url, **kw) 142 | 143 | def post(url, **kw): 144 | return request("POST", url, **kw) 145 | 146 | def put(url, **kw): 147 | return request("PUT", url, **kw) 148 | 149 | def patch(url, **kw): 150 | return request("PATCH", url, **kw) 151 | 152 | def delete(url, **kw): 153 | return request("DELETE", url, **kw) 154 | -------------------------------------------------------------------------------- /editstr.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Petr Viktorin 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining 4 | # a copy of this software and associated documentation files (the "Software"), 5 | # to deal in the Software without restriction, including without limitation 6 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | # and/or sell copies of the Software, and to permit persons to whom the 8 | # Software is furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | # IN THE SOFTWARE. 20 | 21 | # 2020-04-29 Vasek Chalupnicek - fixing TAB and DELETE key 22 | 23 | import sys 24 | 25 | CSI = '\033[' 26 | MOVE_SOL = CSI + 'G' 27 | ERASE_LINE = CSI + '2K' 28 | TAB_SPACES = 4 29 | 30 | def editstr(string): 31 | """Return edited string, or None if cancelled.""" 32 | cursor_pos = len(string) 33 | 34 | def escape(): 35 | c = sys.stdin.read(1) 36 | if c == '[': 37 | esc_bracket() 38 | elif c == 'O': 39 | c = sys.stdin.read(1) 40 | if c == 'H': 41 | home() 42 | if c == 'F': 43 | end() 44 | else: 45 | unknown('ESC O ' + repr(c)) 46 | elif c == 'b': 47 | backward_word() 48 | elif c == 'f': 49 | forward_word() 50 | else: 51 | unknown('ESC ' + repr(c)) 52 | 53 | def esc_bracket(): 54 | c = sys.stdin.read(1) 55 | if ord('0') <= ord(c) <= ord('9'): 56 | c += sys.stdin.read(1) 57 | if c in ('1~', '7~'): 58 | home() 59 | elif c in ('4~', '8~'): 60 | end() 61 | elif c == '3~': 62 | delete() 63 | elif c == '1;': 64 | esc_bracket() 65 | elif c == '5C': 66 | forward_word() 67 | elif c == '5D': 68 | backward_word() 69 | else: 70 | unknown('ESC [ ' + repr(c)) 71 | elif c == 'A': 72 | up_arrow() 73 | elif c == 'B': 74 | down_arrow() 75 | elif c == 'C': 76 | right_arrow() 77 | elif c == 'D': 78 | left_arrow() 79 | elif c == 'H': 80 | home() 81 | elif c == 'F': 82 | end() 83 | else: 84 | unknown('ESC [ ' + repr(c)) 85 | 86 | def redraw(): 87 | print( 88 | MOVE_SOL + ERASE_LINE + string + CSI + str(cursor_pos+1) + 'G', 89 | end='' 90 | ) 91 | 92 | def home(): 93 | nonlocal cursor_pos, string 94 | cursor_pos = 0 95 | 96 | def end(): 97 | nonlocal cursor_pos, string 98 | cursor_pos = len(string) 99 | 100 | def backward_word(): 101 | nonlocal cursor_pos, string 102 | while cursor_pos and string[cursor_pos-1] == ' ': 103 | cursor_pos -= 1 104 | while cursor_pos and string[cursor_pos-1] != ' ': 105 | cursor_pos -= 1 106 | 107 | def forward_word(): 108 | nonlocal cursor_pos, string 109 | while cursor_pos < len(string) and (string+'_')[cursor_pos] == ' ': 110 | cursor_pos += 1 111 | while cursor_pos < len(string) and (string+' ')[cursor_pos] != ' ': 112 | cursor_pos += 1 113 | 114 | def delete(): 115 | nonlocal cursor_pos, string 116 | if cursor_pos < len(string): 117 | string = string[:cursor_pos] + string[cursor_pos+1:] 118 | 119 | def backspace(): 120 | nonlocal cursor_pos, string 121 | if cursor_pos > 0: 122 | cursor_pos -= 1 123 | string = string[:cursor_pos] + string[cursor_pos+1:] 124 | 125 | def right_arrow(): 126 | nonlocal cursor_pos, string 127 | if cursor_pos < len(string): 128 | cursor_pos += 1 129 | 130 | def left_arrow(): 131 | nonlocal cursor_pos, string 132 | if cursor_pos > 0: 133 | cursor_pos -= 1 134 | 135 | def kill_to_end(): 136 | nonlocal cursor_pos, string 137 | string = string[:cursor_pos] 138 | 139 | def kill_to_beginning(): 140 | nonlocal cursor_pos, string 141 | string = string[cursor_pos+1:] 142 | cursor_pos = 0 143 | 144 | def letter(c): 145 | nonlocal cursor_pos, string 146 | if cursor_pos == len(string): 147 | string += c 148 | cursor_pos += 1 149 | else: 150 | string = string[:cursor_pos] + c + string[cursor_pos:] 151 | cursor_pos += 1 152 | 153 | def tab(): 154 | nonlocal cursor_pos, string 155 | if cursor_pos == len(string): 156 | string += ' ' * TAB_SPACES 157 | cursor_pos += TAB_SPACES 158 | else: 159 | string = string[:cursor_pos] + ' ' * TAB_SPACES + string[cursor_pos:] 160 | cursor_pos += TAB_SPACES 161 | 162 | def unknown(s): 163 | print(MOVE_SOL + ERASE_LINE + 'Unknown: ' + s) 164 | 165 | def up_arrow(): 166 | nonlocal cursor_pos 167 | 168 | def down_arrow(): 169 | nonlocal cursor_pos 170 | 171 | try: 172 | while True: 173 | redraw() 174 | c = sys.stdin.read(1) 175 | oc = ord(c) 176 | if oc == 127: 177 | backspace() 178 | elif oc == 1: 179 | # CTRL-A 180 | home() 181 | elif oc == 2: 182 | # CTRL-B 183 | left_arrow() 184 | elif oc == 3: 185 | # CTRL-C 186 | print() 187 | return None 188 | elif oc == 4: 189 | # CTRL-D 190 | delete() 191 | elif oc == 5: 192 | # CTRL-E 193 | end() 194 | elif oc == 6: 195 | # CTRL-F 196 | right_arrow() 197 | elif oc == 11: 198 | # CTRL-K 199 | kill_to_end() 200 | elif oc == 14: 201 | # CTRL-N 202 | down_arrow() 203 | elif oc == 16: 204 | # CTRL-P 205 | up_arrow() 206 | elif oc == 21: 207 | # CTRL-U 208 | kill_to_beginning() 209 | elif oc == 23: 210 | # CTRL-W 211 | backward_word() 212 | elif c in ('\r', '\n'): 213 | return string 214 | elif oc == 27: 215 | escape() 216 | elif oc == 8: 217 | delete() 218 | elif oc == 9: 219 | tab() 220 | elif oc >= 32: 221 | letter(c) 222 | else: 223 | unknown(repr(c)) 224 | finally: 225 | print(MOVE_SOL + ERASE_LINE, end='') 226 | -------------------------------------------------------------------------------- /editor.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is simple interactive text file editor to be used as part of uPyShell 3 | 4 | This module has a dependency on re module and 5 | local .terminal for color support 6 | 7 | from shell.editor import edit 8 | edit('/test.py') 9 | 10 | # This file is part of the octopusLAB project 11 | # The MIT License (MIT) 12 | # Copyright (c) 2016-2020 Jan Copak, Vasek Chalupnicek 13 | ''' 14 | 15 | __version__ = "1.0.0" 16 | 17 | def edit(filename='/main.py'): 18 | import re 19 | from .editstr import editstr 20 | from .terminal import terminal_color 21 | 22 | show_line_numbers = True 23 | changed = False 24 | file_exists = True 25 | buff = [] # empty new file contains one line with a newline character 26 | 27 | def print_buff(): 28 | ''' 29 | Print current state of the file in editor memory 30 | ''' 31 | nonlocal show_line_numbers, changed, file_exists, buff 32 | 33 | EDITOR_WIDTH = 50 34 | LINE_NO_DIGITS = 4 35 | EDITOR_LINE_PREFIX_TPL = '{:>' + str(LINE_NO_DIGITS) + 'd}│' 36 | EDITOR_TITLE_TOP_PREFIX = ' ' * LINE_NO_DIGITS + '┌' 37 | EDITOR_TITLE_TOP_SUFIX = '┐' 38 | EDITOR_TITLE_PREFIX = ' ' * LINE_NO_DIGITS + '│' 39 | EDITOR_TITLE_SPACER = ' ' 40 | EDITOR_TITLE_SUFIX = '│' 41 | EDITOR_TOP_PREFIX = ' ' * LINE_NO_DIGITS + '├' 42 | EDITOR_TOP_TITLE_SUFIX = '┴' 43 | EDITOR_BOTTOM_PREFIX = ' ' * LINE_NO_DIGITS + '└' 44 | 45 | # find the longest line in the buffer 46 | max_len = 0 47 | for line in buff: 48 | l = len(line) 49 | if l > max_len: 50 | max_len = l 51 | if max_len > EDITOR_WIDTH: 52 | EDITOR_WIDTH = max_len 53 | if show_line_numbers: 54 | EDITOR_WIDTH += len(EDITOR_TITLE_PREFIX) 55 | 56 | # ┌────────────────────────────┐ 57 | # │ /test.py [NEW] [CHANGED] │ 58 | # ├────────────────────────────┴───────── 59 | 60 | title_top_prefix = '' 61 | if show_line_numbers: 62 | title_top_prefix = EDITOR_TITLE_TOP_PREFIX 63 | 64 | editor_title = '{:s}{:s}{:s}'.format( 65 | filename, 66 | not file_exists and ' [NEW]' or '', 67 | file_exists and changed and ' [CHANGED]' or '', 68 | ) 69 | 70 | title_border = '─' * (len(editor_title) + 2 * len(EDITOR_TITLE_SPACER)) 71 | 72 | title_prefix = '' 73 | if show_line_numbers: 74 | title_prefix = EDITOR_TITLE_PREFIX 75 | 76 | top_prefix = '' 77 | if show_line_numbers: 78 | top_prefix = EDITOR_TOP_PREFIX 79 | 80 | title_border_after = '─' * (EDITOR_WIDTH - len(top_prefix) - len(title_border) - len(EDITOR_TOP_TITLE_SUFIX)) 81 | 82 | print() 83 | print('{:s}{:s}{:s}'.format( 84 | terminal_color(title_top_prefix), 85 | terminal_color(title_border), 86 | terminal_color(EDITOR_TITLE_TOP_SUFIX) 87 | )) 88 | print('{:s}{:s}{:s}{:s}{:s}'.format( 89 | terminal_color(title_prefix), 90 | EDITOR_TITLE_SPACER, 91 | editor_title, 92 | EDITOR_TITLE_SPACER, 93 | terminal_color(EDITOR_TITLE_SUFIX) 94 | )) 95 | print('{:s}{:s}{:s}{:s}'.format( 96 | terminal_color(top_prefix), 97 | terminal_color(title_border), 98 | terminal_color(EDITOR_TOP_TITLE_SUFIX), 99 | terminal_color(title_border_after) 100 | )) 101 | 102 | # 1│ 103 | # 2│ 104 | # ... 105 | line_cnt = 0 106 | line_prefix = '' 107 | for line in buff: 108 | if show_line_numbers: 109 | line_cnt += 1 110 | line_prefix = EDITOR_LINE_PREFIX_TPL.format(line_cnt) 111 | print('{:s}{:s}'.format(terminal_color(line_prefix), line)) 112 | # newline at the end of the file 113 | if show_line_numbers: 114 | line_cnt += 1 115 | line_prefix = EDITOR_LINE_PREFIX_TPL.format(line_cnt) 116 | print('{:s}'.format(terminal_color(line_prefix))) 117 | 118 | # └───────────────────────────────────── 119 | bottom_prefix = '' 120 | if show_line_numbers: 121 | bottom_prefix = EDITOR_BOTTOM_PREFIX 122 | print('{:s}{:s}'.format( 123 | terminal_color(bottom_prefix), 124 | terminal_color('─' * (EDITOR_WIDTH - len(bottom_prefix))) 125 | )) 126 | 127 | def print_help(): 128 | print(' h print this help') 129 | print(' p print file (current state in buffer)') 130 | print(' l toggle line numbers (copy mode)') 131 | print(' q quit') 132 | print(' q! x quit without saving changes') 133 | print(' w write file (save)') 134 | print(' wq write into file and quit') 135 | print() 136 | print(' i