Created
September 9, 2011 01:37
-
-
Save scttnlsn/1205279 to your computer and use it in GitHub Desktop.
A toy text editor written in CoffeeScript
This file contains 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
# ========================================= | |
# A toy text editor written in CoffeeScript | |
# ========================================= | |
# | |
# Requires ncurses bindings for node.js: | |
# npm install ncurses | |
# | |
# Usage: | |
# coffee editor.coffee FILENAME | |
# | |
# Ctrl+w saves | |
# Ctrl+x exits | |
# | |
# ========================================= | |
fs = require 'fs' | |
ncurses = require 'ncurses' | |
class Position | |
constructor: -> | |
@x = 0 | |
@y = 0 | |
set: (content, index) -> | |
lines = content[0...index].split "\n" | |
@x = lines[lines.length - 1].length | |
@y = lines.length - 1 | |
index: (content) -> | |
lines = content.split "\n" | |
index = lines[0...@y].join("\n").length + @x | |
index += 1 if @y > 0 | |
index | |
move: (content, dx, dy) -> | |
lines = content.split "\n" | |
@y = Math.min Math.max(@y + dy, 0), lines.length - 1 | |
@x = Math.min Math.max(@x + dx, 0), (lines[@y] || '').length | |
class Viewport | |
constructor: (@width, @height) -> | |
@offset = { x: 0, y: 0 } | |
@cursor = { x: 0, y: 0 } | |
adjust: (position) -> | |
@reposition position | |
@scroll position | |
@reposition position | |
scroll: (position) -> | |
if @cursor.x >= @width | |
@offset.x = position.x - @width + 1 | |
@offset.x = Math.min Math.max(@offset.x, 0), position.x | |
if @cursor.y >= @height | |
@offset.y = position.y - @height + 1 | |
@offset.y = Math.min Math.max(@offset.y, 0), position.y | |
reposition: (position) -> | |
@cursor.x = position.x - @offset.x | |
@cursor.y = position.y - @offset.y | |
crop: (content) -> | |
lines = content.split "\n" | |
line[@offset.x...@offset.x + @width] for line in lines[@offset.y...@offset.y + @height] | |
class Editor | |
constructor: (@viewport, @content) -> | |
@position = new Position() | |
view: -> | |
@viewport.crop @content | |
move: (dx, dy) -> | |
@position.move @content, dx, dy | |
@viewport.adjust @position | |
insert: (text) -> | |
index = @position.index @content | |
@content = @content[0...index] + text + @content[index...] | |
newlines = text.split "\n" | |
@move newlines[newlines.length - 1].length, newlines.length - 1 | |
backspace: (count) -> | |
index = @position.index(@content) - count | |
if index < 0 | |
count += index | |
index = 0 | |
@content = @content[0...index] + @content[index + count...] | |
@position.set @content, index | |
@viewport.adjust @position | |
class Application | |
constructor: (@file) -> | |
@window = new ncurses.Window | |
@viewport = new Viewport @window.width, @window.height | |
@editor = new Editor @viewport, fs.readFileSync @file, 'utf8' | |
@window.on 'inputChar', (c, i) => | |
switch i | |
when ncurses.keys.UP | |
@editor.move 0, -1 | |
when ncurses.keys.RIGHT | |
@editor.move 1, 0 | |
when ncurses.keys.DOWN | |
@editor.move 0, 1 | |
when ncurses.keys.LEFT | |
@editor.move -1, 0 | |
when ncurses.keys.NEWLINE | |
@editor.insert "\n" | |
when ncurses.keys.BACKSPACE, 127 | |
@editor.backspace 1 | |
when 23 # Ctrl+w | |
fs.writeFileSync @file, @editor.content | |
when 24 # Ctrl+x | |
@window.close() | |
return | |
else | |
if i in [32..126] | |
@editor.insert c | |
@refresh() | |
@refresh() | |
@window.refresh() | |
refresh: -> | |
for line, i in @editor.view() | |
padding = Array(@viewport.width - line.length + 1).join ' ' | |
@window.addstr i, 0, line + padding | |
@window.cursor @viewport.cursor.y, @viewport.cursor.x | |
do -> | |
file = process.argv[2] | |
unless file? | |
console.warn 'Must specify a file' | |
else | |
app = new Application file |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment