added the ed app
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# A simple line-oriented text editor for MicroPython/CircuitPython
|
||||
# implementing the POSIX 'ed' subset
|
||||
# Usage: from app.ed import edit
|
||||
# Created by Luxferre in 2025, released into public domain
|
||||
|
||||
from deck.input import input
|
||||
|
||||
class Ed:
|
||||
def __init__(self, filename=None):
|
||||
self.filename = filename
|
||||
self.buffer = []
|
||||
self.current_line = 0 # 1-indexed for the user, 0 means empty or before first line
|
||||
self.dirty = False
|
||||
self.last_error = ""
|
||||
|
||||
if filename:
|
||||
try:
|
||||
with open(filename, "r") as f:
|
||||
self.buffer = [line.rstrip("\n\r") for line in f.readlines()]
|
||||
self.current_line = len(self.buffer)
|
||||
print(len("".join(self.buffer)) + len(self.buffer)) # Print byte count
|
||||
except OSError:
|
||||
print(f"cannot open input file {filename}")
|
||||
|
||||
def _parse_address(self, addr_str):
|
||||
"""Simple address parser for line numbers, '.', and '$'."""
|
||||
if not addr_str:
|
||||
return self.current_line
|
||||
if addr_str == ".":
|
||||
return self.current_line
|
||||
if addr_str == "$":
|
||||
return len(self.buffer)
|
||||
try:
|
||||
return int(addr_str)
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def _get_range(self, addr_range):
|
||||
"""Parses ranges like '1,5' or '%'."""
|
||||
if not addr_range:
|
||||
return self.current_line, self.current_line
|
||||
if addr_range == ",":
|
||||
return 1, len(self.buffer)
|
||||
if addr_range == "%":
|
||||
return 1, len(self.buffer)
|
||||
|
||||
parts = addr_range.split(",")
|
||||
if len(parts) == 1:
|
||||
addr = self._parse_address(parts[0])
|
||||
return addr, addr
|
||||
else:
|
||||
return self._parse_address(parts[0]), self._parse_address(parts[1])
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
try:
|
||||
cmd_line = input(": ").strip()
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
if not cmd_line:
|
||||
# A null command (just newline) prints the current line
|
||||
if self.buffer:
|
||||
print(self.buffer[self.current_line - 1])
|
||||
continue
|
||||
|
||||
# Basic command regex-like split
|
||||
# Format: [address]command[parameters]
|
||||
cmd_char = ""
|
||||
addr_part = ""
|
||||
for i, char in enumerate(cmd_line):
|
||||
if char.isalpha():
|
||||
addr_part = cmd_line[:i]
|
||||
cmd_char = char
|
||||
params = cmd_line[i+1:].strip()
|
||||
break
|
||||
else:
|
||||
# If no alpha command found, try interpreting as address only
|
||||
addr = self._parse_address(cmd_line)
|
||||
if addr is not None and 0 <= addr <= len(self.buffer):
|
||||
self.current_line = addr
|
||||
if self.current_line > 0:
|
||||
print(self.buffer[self.current_line - 1])
|
||||
continue
|
||||
else:
|
||||
print("?")
|
||||
continue
|
||||
|
||||
try:
|
||||
start, end = self._get_range(addr_part)
|
||||
|
||||
if cmd_char == 'q':
|
||||
if self.dirty and params != "!":
|
||||
print("?")
|
||||
self.dirty = False # Standard ed behavior: second q forces exit
|
||||
continue
|
||||
break
|
||||
|
||||
elif cmd_char == 'p' or cmd_char == 'n':
|
||||
if not self.buffer:
|
||||
print("?")
|
||||
continue
|
||||
start = max(1, start)
|
||||
end = min(len(self.buffer), end)
|
||||
for i in range(start - 1, end):
|
||||
if cmd_char == 'n':
|
||||
print(f"{i + 1}\t{self.buffer[i]}")
|
||||
else:
|
||||
print(self.buffer[i])
|
||||
self.current_line = end
|
||||
|
||||
elif cmd_char == 'a':
|
||||
idx = start
|
||||
while True:
|
||||
line = input("")
|
||||
if line == ".":
|
||||
break
|
||||
self.buffer.insert(idx, line)
|
||||
idx += 1
|
||||
self.dirty = True
|
||||
self.current_line = idx
|
||||
|
||||
elif cmd_char == 'i':
|
||||
idx = max(0, start - 1)
|
||||
while True:
|
||||
line = input("")
|
||||
if line == ".":
|
||||
break
|
||||
self.buffer.insert(idx, line)
|
||||
idx += 1
|
||||
self.dirty = True
|
||||
self.current_line = idx
|
||||
|
||||
elif cmd_char == 'd':
|
||||
if not self.buffer:
|
||||
print("?")
|
||||
continue
|
||||
del self.buffer[start-1 : end]
|
||||
self.current_line = min(start, len(self.buffer))
|
||||
self.dirty = True
|
||||
|
||||
elif cmd_char == 'w':
|
||||
target = params if params else self.filename
|
||||
if not target:
|
||||
print("?")
|
||||
continue
|
||||
with open(target, "w") as f:
|
||||
for line in self.buffer:
|
||||
f.write(line + "\n")
|
||||
print(len("".join(self.buffer)) + len(self.buffer))
|
||||
self.dirty = False
|
||||
self.filename = target
|
||||
|
||||
elif cmd_char == 'f':
|
||||
if params:
|
||||
self.filename = params
|
||||
print(self.filename if self.filename else "No current filename")
|
||||
|
||||
else:
|
||||
print("?")
|
||||
|
||||
except Exception:
|
||||
print("?")
|
||||
|
||||
def edit(fname):
|
||||
editor = Ed(fname)
|
||||
editor.run()
|
||||
Reference in New Issue
Block a user