This time I tried adding a full page on how to do it but the model reached the maximum tokens (thexe include both input and output).
# https://wasimlorgat.com/posts/editor.html
import curses
import sys
import argparse
class Window:
def __init__(self, n_rows, n_cols, row=0, col=0):asked
self.n_rows = n_rows
self.n_cols = n_cols
self.row = row
self.col = col
@property
def bottom(self):
return self.row + self.n_rows - 1
def up(self, cursor):
if cursor.row == self.row - 1 and self.row > 0:
self.row -= 1
def down(self, buffer, cursor):
if cursor.row == self.bottom + 1 and self.bottom < buffer.bottom:
self.row += 1
def translate(self, cursor):
return cursor.row - self.row, cursor.col - self.col
def horizontal_scroll(self, cursor, left_margin=5, right_margin=2):
n_pages = cursor.col // (self.n_cols - right_margin)
self.col = max(n_pages * self.n_cols - right_margin - left_margin, 0)
class Cursor:
def __init__(self, row=0, col=0, col_hint=None):
self.row = row
self._col = col
self._col_hint = col if col_hint is None else col_hint
@property
def col(self):
return self._col
@col.setter
def col(self, col):
self._col = col
self._col_hint = col
def _clamp_col(self, buffer):
self._col = min(self._col_hint, len(buffer[self.row]))
def up(self, buffer):
if self.row > 0:
self.row -= 1
self._clamp_col(buffer)
def down(self, buffer):
if self.row < len(buffer) - 1:
self.row += 1
self._clamp_col(buffer)
def left(self, buffer):
if self.col > 0:
self.col -= 1
elif self.row > 0:
self.row -= 1
self.col = len(buffer[self.row])
def right(self, buffer):
if self.col < len(buffer[self.row]):
self.col += 1
elif self.row < buffer.bottom:
self.row += 1
self.col = 0
class Buffer:
def __init__(self, lines):
self.lines = lines
def __len__(self):
return len(self.lines)
def __getitem__(self, index):
return self.lines[index]
@property
def bottom(self):
return len(self) - 1
def insert(self, cursor, string):
row, col = cursor.row, cursor.col
current = self.lines.pop(row)
new = current[:col] + string + current[col:]
self.lines.insert(row, new)
def split(self, cursor):
row, col = cursor.row, cursor.col
current = self.lines.pop(row)
self.lines.insert(row, current[:col])
self.lines.insert(row + 1, current[col:])
def delete(self, cursor):
row, col = cursor.row, cursor.col
if row < len(self.lines):
if col < len(self.lines[row]):
# Delete within the line
self.lines[row] = self.lines[row][:col] + self.lines[row][col+1:]
else:
# We're at the end of a line, merge with the next line if possible
if row + 1 < len(self.lines):
self.lines[row] += self.lines.pop(row + 1)
def right(window, buffer, cursor):
cursor.right(buffer)
window.down(buffer, cursor)
window.horizontal_scroll(cursor)
def left(window, buffer, cursor):
cursor.left(buffer)
window.up(cursor)
window.horizontal_scroll(cursor)
...
Then asked LLMs to add a menu on presssing Escape, make it warp from -1 to size-1 and size to 0.
To my surprise it used current_row = (current_row - 1) % len(menu)
which works both ways.
# https://wasimlorgat.com/posts/editor.htm
# 4 prompts
...
def show_menu(stdscr):
curses.curs_set(0) # Hide the cursor
# Options for the menu
menu = ['Return', 'Save', 'Exit']
choice = -1
current_row = 0
def print_menu(stdscr, selected_row_idx):
stdscr.clear()
h, w = stdscr.getmaxyx()
for idx, row in enumerate(menu):
x = w // 2 - len(row) // 2
y = h // 2 - len(menu) // 2 + idx
if idx == selected_row_idx:
stdscr.attron(curses.color_pair(1))
stdscr.addstr(y, x, row)
stdscr.attroff(curses.color_pair(1))
else:
stdscr.addstr(y, x, row)
stdscr.refresh()
# Initialize colors
curses.start_color()
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
# Print the initial menu
print_menu(stdscr, current_row)
while True:
key = stdscr.getch()
if key == 27: # Escape = Return
choice = -1
break
elif key == curses.KEY_UP:
current_row = (current_row - 1) % len(menu) # Wrap around
elif key == curses.KEY_DOWN:
current_row = (current_row + 1) % len(menu) # Wrap around
elif key == curses.KEY_ENTER or key in [10, 13]:
choice = current_row
break
print_menu(stdscr, current_row)
# Handle the selected action
if choice == 0:
return ""
elif choice == 1:
return "save"
elif choice == 2:
return "exit"
else:
return ""
...
if k in ("KEY_ESCAPE", '\x1b'):
action = show_menu(stdscr)
if action == "save":
save_file(stdscr, buffer, args.filename)
elif action == "open":
stdscr.addstr(2, 0, "File opened!")
elif action == "exit":
break
curses.curs_set(1)
stdscr.refresh()
...
I used lmsys so tne models kept switching and I also counted the prompts that did not produce usefull answers. And finally, add s border around that menu.
# https://wasimlorgat.com/posts/editor.htm
# 7 prompts
...
def draw_box(stdscr, y1, x1, y2, x2):
# Draw top and bottom borders
stdscr.addstr(y1, x1 + 1, "─" * (x2 - 1 - x1 + 1))
stdscr.addstr(y2, x1 + 1, "─" * (x2 - 1 - x1 + 1))
#stdscr.box()
# Draw left and right borders
for i in range(y1 + 1, y2):
stdscr.addstr(i, x1, "│")
stdscr.addstr(i, x2, "│")
stdscr.addstr(y1, x1, "┌")
stdscr.addstr(y2, x1, "└")
stdscr.addstr(y1, x2, "┐")
stdscr.addstr(y2, x2, "┘")
...
def print_menu(stdscr, selected_row_idx):
stdscr.clear()
h, w = stdscr.getmaxyx()
# Draw a border around the entire window
draw_box(stdscr, 9, 33, 15, 45) # Draw a box
...
While I also tried to add Copy and Paste key-bindings these fell short since Ctrl+C emits the Term signal and closes the application.