104 lines
3.2 KiB
Python
Executable File
104 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Multi-line input reader. C-j inserts newline, C-m (Enter) submits."""
|
|
import sys, tty, termios, os, signal
|
|
|
|
def readchar(fd):
|
|
"""Read a single UTF-8 character from fd in raw mode."""
|
|
b = os.read(fd, 1)
|
|
if not b:
|
|
return b
|
|
if b[0] < 0x80:
|
|
return b
|
|
# determine continuation bytes from leading byte
|
|
n = 1
|
|
if b[0] & 0xe0 == 0xc0: n = 1
|
|
elif b[0] & 0xf0 == 0xe0: n = 2
|
|
elif b[0] & 0xf8 == 0xf0: n = 3
|
|
rest = os.read(fd, n)
|
|
return b + rest
|
|
|
|
def main():
|
|
prompt = sys.argv[1] if len(sys.argv) > 1 else '> '
|
|
cont = sys.argv[2] if len(sys.argv) > 2 else '. '
|
|
fd = sys.stdin.fileno()
|
|
old = termios.tcgetattr(fd)
|
|
lines = ['']
|
|
cur = 0 # current line index
|
|
|
|
def redraw_current():
|
|
p = prompt if cur == 0 else cont
|
|
sys.stderr.write('\r\033[K' + p + lines[cur])
|
|
sys.stderr.flush()
|
|
|
|
sys.stderr.write(prompt)
|
|
sys.stderr.flush()
|
|
|
|
try:
|
|
tty.setraw(fd)
|
|
while True:
|
|
ch = readchar(fd)
|
|
if not ch:
|
|
break
|
|
b = ch[0]
|
|
|
|
if b == 0x0d: # C-m / Enter -> submit
|
|
sys.stderr.write('\r\n')
|
|
sys.stderr.flush()
|
|
break
|
|
elif b == 0x0a: # C-j -> newline
|
|
sys.stderr.write('\r\n')
|
|
cur += 1
|
|
lines.insert(cur, '')
|
|
redraw_current()
|
|
elif b == 0x03: # C-c
|
|
sys.stderr.write('\r\n')
|
|
sys.stderr.flush()
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
os.kill(os.getpid(), signal.SIGINT)
|
|
elif b == 0x04: # C-d
|
|
if not any(lines):
|
|
sys.stderr.write('\r\n')
|
|
sys.stderr.flush()
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
sys.exit(1)
|
|
else:
|
|
sys.stderr.write('\r\n')
|
|
sys.stderr.flush()
|
|
break
|
|
elif b in (0x7f, 0x08): # backspace
|
|
if lines[cur]:
|
|
lines[cur] = lines[cur][:-1]
|
|
redraw_current()
|
|
elif cur > 0:
|
|
prev = lines[cur - 1]
|
|
lines.pop(cur)
|
|
cur -= 1
|
|
lines[cur] = prev
|
|
sys.stderr.write('\033[A')
|
|
redraw_current()
|
|
elif b == 0x15: # C-u: kill line
|
|
lines[cur] = ''
|
|
redraw_current()
|
|
elif b == 0x17: # C-w: kill word
|
|
l = lines[cur].rstrip()
|
|
i = l.rfind(' ')
|
|
lines[cur] = l[:i+1] if i >= 0 else ''
|
|
redraw_current()
|
|
elif b == 0x1b: # escape sequence - consume and ignore
|
|
os.read(fd, 2)
|
|
elif b >= 0x20:
|
|
s = ch.decode('utf-8', errors='replace')
|
|
lines[cur] += s
|
|
sys.stderr.write(s)
|
|
sys.stderr.flush()
|
|
finally:
|
|
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
|
|
|
text = '\n'.join(lines)
|
|
if not text:
|
|
sys.exit(1)
|
|
sys.stdout.write(text)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|