Skip to content

Instantly share code, notes, and snippets.

@scttnlsn
Created September 9, 2011 01:37
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save scttnlsn/1205279 to your computer and use it in GitHub Desktop.
Save scttnlsn/1205279 to your computer and use it in GitHub Desktop.
A toy text editor written in CoffeeScript
# =========================================
# 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