Last active
June 5, 2022 19:36
-
-
Save nroose/32997db80a45b570bc53174dbe7a5e66 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
# adapted from https://gist.github.com/gabehollombe/064a90351c204381832d749ca6b569e0 | |
# but it actually edits a file! | |
# `editor.rb [filename]` | |
# arrows, letters, backspace... Just the basics. | |
require 'curses' | |
include Curses | |
include Curses::Key | |
class Integer | |
def clamp(low, high) | |
v = self | |
v = low if self < low | |
v = high if self > high | |
v | |
end | |
end | |
class Cursor | |
attr_reader :row, :col | |
def initialize(row = 0, col = 0) | |
@row = row | |
@col = col | |
end | |
def right | |
Cursor.new @row, @col + 1 | |
end | |
def left | |
Cursor.new @row, @col - 1 | |
end | |
def down | |
Cursor.new @row + 1, @col | |
end | |
def up | |
Cursor.new @row - 1, @col | |
end | |
end | |
class Buffer | |
attr_reader :lines, :cursor | |
def initialize(lines = [], cursor = Cursor.new) | |
@lines = lines | |
@cursor = cursor | |
end | |
def cur_row | |
@cursor.row | |
end | |
def cur_col | |
@cursor.col | |
end | |
def current_line | |
@lines[cur_row] | |
end | |
def add_char(char) | |
lines = @lines.dup | |
line = lines[cur_row] || [] | |
line.insert cur_col, char | |
lines[cur_row] = line | |
Buffer.new lines, @cursor.right | |
end | |
def remove_char_before_cursor | |
return Buffer.new @lines, @cursor if cur_col == 0 | |
lines = @lines.dup | |
line = lines[cur_row] || [] | |
line.delete_at(cur_col - 1) | |
lines[@cursor.row] = line | |
Buffer.new lines, @cursor.left | |
end | |
def new_line | |
lines = @lines.dup | |
line, next_line = current_line.partition.with_index { |_, i| i < cur_col } | |
lines[cur_row] = line | |
lines = lines.insert cur_row + 1, next_line | |
Buffer.new lines, Cursor.new(cur_row + 1, 0) | |
end | |
def beginning_of_line | |
Buffer.new lines, Cursor.new(cur_row, 0) | |
end | |
def end_of_line | |
Buffer.new lines, Cursor.new(cur_row, current_line.length) | |
end | |
def cursor_left | |
Buffer.new lines, clamp(@cursor.left) | |
end | |
def cursor_right | |
Buffer.new lines, clamp(@cursor.right) | |
end | |
def cursor_up | |
Buffer.new lines, clamp(@cursor.up) | |
end | |
def cursor_down | |
Buffer.new lines, clamp(@cursor.down) | |
end | |
def clamp(cursor) | |
r = cursor.row.clamp(0, @lines.count - 1) | |
c = cursor.col.clamp(0, @lines[r].length) | |
Cursor.new r, c | |
end | |
end | |
class Editor | |
BACKSPACE = 127 | |
CTRLA = 1 | |
CTRLC = 3 | |
CTRLE = 5 | |
CTRLX = 24 | |
ENTER = 13 | |
def main(buffer = nil) | |
raw | |
noecho | |
nonl | |
stdscr.keypad = true | |
init_screen | |
buffer ||= Buffer.new | |
render buffer | |
begin | |
loop do | |
char = getch | |
buffer = | |
case char | |
when CTRLX then break | |
when CTRLC then exit | |
when CTRLA then buffer.beginning_of_line | |
when CTRLE then buffer.end_of_line | |
when KEY_UP then buffer.cursor_up | |
when KEY_DOWN then buffer.cursor_down | |
when KEY_LEFT then buffer.cursor_left | |
when KEY_RIGHT then buffer.cursor_right | |
when BACKSPACE then buffer.remove_char_before_cursor | |
when ENTER then buffer.new_line | |
when /[[:print:]]/ then buffer.add_char(char) | |
else buffer | |
end | |
render buffer | |
end | |
ensure | |
close_screen | |
end | |
buffer | |
end | |
def render(buffer) | |
clear | |
buffer.lines.each_with_index do |line, index| | |
setpos index, 0 | |
addstr(line.join) | |
end | |
setpos buffer.cursor.row, buffer.cursor.col | |
refresh | |
end | |
end | |
buf = Buffer.new File.read(ARGV[0]).lines.to_a.map { |r| r.scan(/[[:print:]]/) }, Cursor.new(0, 0) unless ARGV[0].nil? | |
buf = Editor.new.main buf | |
if ARGV[0].nil? | |
print "file name? ('q' to exit without save): " | |
filename = gets.strip | |
else | |
filename = ARGV[0] | |
end | |
File.write(filename, buf.lines.map { |line| line.join('') }.join("\n")) unless filename == 'q' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment