Trying out a main Editor class
Also may have greatly simplified "clamping" logic to a single if statement. The key was to reset x_hint on any horizontal motion.
This commit is contained in:
parent
e80aaa1395
commit
7766f95556
1 changed files with 89 additions and 35 deletions
124
editor/main.py
124
editor/main.py
|
|
@ -1,50 +1,104 @@
|
|||
import argparse
|
||||
import curses
|
||||
import sys
|
||||
from typing import List
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
|
||||
from .window import Window
|
||||
|
||||
|
||||
def main(argv: Optional[Sequence[str]] = None) -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("filename")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
return curses.wrapper(c_main, args.filename)
|
||||
|
||||
|
||||
def c_main(stdscr: "curses._CursesWindow", filename: str) -> int:
|
||||
with open(filename) as f:
|
||||
lines = f.read().split("\n")
|
||||
|
||||
win = Window(lines, curses.COLS, curses.LINES)
|
||||
|
||||
prev_wx, prev_wy = None, None
|
||||
while True:
|
||||
# Update screen
|
||||
if (prev_wx, prev_wy) != (win.wx, win.wy):
|
||||
stdscr.erase()
|
||||
for y, line in enumerate(win.screen_lines()):
|
||||
stdscr.addstr(y, 0, line)
|
||||
prev_wy, prev_wx = win.screen_cursor()
|
||||
stdscr.move(*win.screen_cursor())
|
||||
|
||||
# Handle keypresses
|
||||
c = stdscr.getkey()
|
||||
if c == "q":
|
||||
break
|
||||
elif c == "k":
|
||||
win.up()
|
||||
elif c == "j":
|
||||
win.down()
|
||||
elif c == "h":
|
||||
win.left()
|
||||
elif c == "l":
|
||||
win.right()
|
||||
elif c == "0":
|
||||
win.home()
|
||||
elif c == "$":
|
||||
win.end()
|
||||
|
||||
Editor(filename).run(stdscr)
|
||||
return 0
|
||||
|
||||
|
||||
class Editor:
|
||||
def __init__(self, filename: str):
|
||||
with open(filename) as f:
|
||||
lines = f.read().split("\n")
|
||||
self.buffer = Buffer(lines)
|
||||
self.cursor = Cursor()
|
||||
|
||||
def run(self, stdscr: "curses._CursesWindow") -> None:
|
||||
while True:
|
||||
self.render(stdscr)
|
||||
self.handle_key(stdscr)
|
||||
|
||||
def render(self, stdscr: "curses._CursesWindow") -> None:
|
||||
stdscr.erase()
|
||||
for y, line in enumerate(self.buffer.lines):
|
||||
stdscr.addstr(y, 0, line)
|
||||
stdscr.move(self.cursor.y, self.cursor.x)
|
||||
|
||||
def handle_key(self, stdscr: "curses._CursesWindow") -> None:
|
||||
c = curses.keyname(stdscr.getch()).decode()
|
||||
if c == "^Q":
|
||||
sys.exit(0)
|
||||
elif c == "^P":
|
||||
self.previous_line()
|
||||
elif c == "^N":
|
||||
self.next_line()
|
||||
elif c == "^B":
|
||||
self.backward_char()
|
||||
elif c == "^F":
|
||||
self.forward_char()
|
||||
|
||||
def previous_line(self) -> None:
|
||||
if self.cursor.y > 0:
|
||||
self.cursor = self.cursor.up().clamp(self.buffer)
|
||||
|
||||
def next_line(self) -> None:
|
||||
if self.cursor.y < self.buffer.line_count() - 1:
|
||||
self.cursor = self.cursor.down().clamp(self.buffer)
|
||||
|
||||
def backward_char(self) -> None:
|
||||
if self.cursor.x > 0:
|
||||
self.cursor = self.cursor.left()
|
||||
|
||||
def forward_char(self) -> None:
|
||||
if self.cursor.x < self.buffer.line_length(self.cursor.y):
|
||||
self.cursor = self.cursor.right()
|
||||
|
||||
|
||||
class Cursor:
|
||||
def __init__(self, x: int = 0, y: int = 0, x_hint: Optional[int] = None):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self._x_hint = x if x_hint is None else x_hint
|
||||
|
||||
def up(self) -> "Cursor":
|
||||
return Cursor(self.x, self.y - 1, self._x_hint)
|
||||
|
||||
def down(self) -> "Cursor":
|
||||
return Cursor(self.x, self.y + 1, self._x_hint)
|
||||
|
||||
def left(self) -> "Cursor":
|
||||
return Cursor(self.x - 1, self.y)
|
||||
|
||||
def right(self) -> "Cursor":
|
||||
return Cursor(self.x + 1, self.y)
|
||||
|
||||
def clamp(self, buffer: "Buffer") -> "Cursor":
|
||||
line_length = buffer.line_length(self.y)
|
||||
if self._x_hint < line_length:
|
||||
x = self._x_hint
|
||||
else:
|
||||
x = line_length
|
||||
return Cursor(x, self.y, self._x_hint)
|
||||
|
||||
|
||||
class Buffer:
|
||||
def __init__(self, lines: List[str]):
|
||||
self.lines = lines
|
||||
|
||||
def line_count(self) -> int:
|
||||
return len(self.lines)
|
||||
|
||||
def line_length(self, y: int) -> int:
|
||||
return len(self.lines[y])
|
||||
|
|
|
|||
Loading…
Reference in a new issue