rendered paste body/* de.d -- The DE editor made for use with DRE's concatenative module
* version 0.1, November 25th, 2007
*
* Copyright (C) 2007 Joseph Pusdesris <deformative0@gmail.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
import std.string;
import std.file;
import std.conv;
//TODO Empty files
//bindings
align(4){
int NCURSES_BITS(int mask, int shift){
return (mask) << ((shift) + NCURSES_ATTR_SHIFT);
}
int COLOR_PAIR(int n){
return NCURSES_BITS(n, 0);
}
const int NCURSES_ATTR_SHIFT = 8;
const int A_UNDERLINE = NCURSES_BITS(1UL,9);
const int A_BOLD = NCURSES_BITS(1UL,13);
const int COLOR_BLACK = 0;
const int COLOR_RED = 1;
const int COLOR_GREEN = 2;
const int COLOR_YELLOW = 3;
const int COLOR_BLUE = 4;
const int COLOR_MAGENTA = 5;
const int COLOR_CYAN = 6;
const int COLOR_WHITE = 7;
extern (C) extern int COLS;
extern (C) extern int LINES;
extern (C) int addch(uint);
extern (C) void* stdscr;
extern (C) void* initscr();
extern (C) int init_pair(short, short, short);
extern (C) int clrtobot();
extern (C) int addstr(char *);
extern (C) int attron(int);
extern (C) int attroff(int);
extern (C) int endwin();
extern (C) int move(int, int);
extern (C) int getch();
extern (C) int nonl();
extern (C) int keypad(void*, bool);
extern (C) int cbreak();
extern (C) int noecho();
extern (C) bool has_colors();
extern (C) int start_color();
//signals
typedef void(*sighandler)(int);
extern(C)void function(int) signal(int, sighandler);
}
void finish(int sig){
endwin();
throw new Exception("cool");
}
void resized(int sig){
Editor.resize();
}
static this(){
signal(2, &finish);
signal(28, &resized);
}
struct Color{
const char[6] ansi;
const int number;
Color* next;
}
Color blue = Color(" \033[94m", 4, &red);
Color yellow = Color(" \033[93m", 3, &blue);
Color green = Color(" \033[92m", 2, &yellow);
Color red = Color(" \033[91m", 1, &green);
struct segment{
char[] string;
Color color = Color(" \033[91m", 1, &green);
}
alias segment[] line;
int size(line l){
int si;
foreach(s; l)
si += s.string.length;
return si;
}
int rows(line l){
return l.size() / Editor.cols + 1;
}
void write(line l, inout int room, int offset){
int lineSize = l.rows() - offset;
room -= (lineSize - offset);
if(room < 0){
room = 0;
//Will lines seriously ever be this big?
}
else{
int start = offset * Editor.cols;
int segmentNumber = l.getSegmentNumber(start);
int innerIndex = l.getInnerIndexDammit(start);
attron(COLOR_PAIR(l[segmentNumber].color.number));
addstr(l[segmentNumber].string[innerIndex..$].toStringz());
foreach(s; l[segmentNumber+1..$]){
attron(COLOR_PAIR(s.color.number));
addstr(s.string.toStringz());
}
}
}
void write(line l, inout int room){
int lineSize = l.rows();
room -= lineSize;
if(room < 0){
//TODO Stop at end
//No hurry, will rarely be this large
room = 0;
foreach(s; l){
attron(COLOR_PAIR(s.color.number));
addstr(s.string.toStringz());
}
}
else{
foreach(s; l){
attron(COLOR_PAIR(s.color.number));
addstr(s.string.toStringz());
}
}
}
int getSegmentNumber(line l, int currentChar){
foreach(int index, s; l){
currentChar -= s.string.length;
if(currentChar <= 0)
return index;
}
return l.length - 1; //past line
}
segment* getsegment(line l, int currentChar){
return &l[getSegmentNumber(l, currentChar)];
}
int getInnerIndexDammit(line l, int currentChar){
foreach(int index, s; l){
if(currentChar < s.string.length) //<= ?
return currentChar;
else
currentChar -= s.string.length;
}
return l[$-1].string.length-1; //past line
}
char getCharDammit(line l, int currentChar){
return getsegment(l, currentChar).string[getInnerIndexDammit(l, currentChar)]; //unoptimal, but clean
}
class File{
char[] name;
int currentX = 0, currentY = 1; //cursor in screen
int currentLine = 0, currentChar = 0; //cursor in data
line[] data;
this(char[] filename){
this.name = filename;
if(exists(filename)){
char[][] lines = splitlines(cast(char[])read(filename));
data.length = lines.length;
foreach(int index, inout l; lines){
data[index].length = 1;
l ~= '\n';
loop:
if(l.length < 6){
data[index][$-1].string ~= l;
}
else{
switch(l[0..6]){
case " \033[91m":
data[index] ~= segment("", red);
break;
case " \033[92m":
data[index] ~= segment("", green);
break;
case " \033[93m":
data[index] ~= segment("", yellow);
break;
case " \033[94m":
data[index] ~= segment("", blue);
break;
default:
data[index][$-1].string ~= l[0];
l = l[1..$];
}
goto loop;
}
}
}
}
line getLine(){
return data[currentLine];
}
segment* getSegment(int offset = 0){
return getsegment(getLine(), currentChar + offset + 1);
}
Color getColor(){
return getSegment().color;
}
int getInnerIndex(int offset = 0){
return getLine().getInnerIndexDammit(currentChar + offset);
}
char getChar(int offset = 0){
return getLine().getCharDammit(currentChar + offset);
}
void append(char[] s){
int innerIndex = getInnerIndex();
getSegment().string =
getSegment().string[0..innerIndex]
~ s ~
getSegment().string[innerIndex..$];
for(int i = 0; i<s.length; i++)
right();
}
void up(){
if(currentLine){
int rowsAboveOld = currentChar / Editor.cols;
currentLine--;
if(currentChar > getLine().size()){
currentChar = getLine().size() - 1;
currentX = currentChar % Editor.cols;
}
int rowsBelowNew = (getLine().size() - currentChar) / Editor.cols + 1;;
currentY -= rowsAboveOld + rowsBelowNew;
if(currentY <= 1)
currentY = 1;
}
}
void down(){
if(currentLine != data.length-1){
int rowsBelowOld = getLine().rows() - (currentChar / Editor.cols); //fix algorithm
currentLine++;
if(currentChar > getLine().size()){
currentChar = getLine().size() - 1;
currentX = currentChar % Editor.cols;
}
int rowsAboveNew = currentChar / Editor.cols;
currentY += rowsBelowOld + rowsAboveNew;
if(currentY >= Editor.rows-3)
currentY = Editor.rows-3;
}
}
void right(){
if(currentChar != getLine().size()-1){
currentChar++;
if(currentX == Editor.cols-1){
currentY++;
if(currentY >= Editor.rows-3)
currentY = Editor.rows-3;
}
currentX = currentChar % Editor.cols;
}
}
void left(){
if(currentChar){
currentChar--;
if(!currentX){
currentY--;
if(currentY <= 1)
currentY = 1;
}
currentX = currentChar % Editor.cols;
}
}
void type(int c){
switch(c){
case ' ':
int innerIndex;
int segmentNumba;
if(!(getInnerIndex()))
getSegment().color = *(getSegment().color.next);
else if(!(getInnerIndex(1)))
getSegment(1).color = *(getSegment(1).color.next);
else if(getChar() == ' '){
innerIndex = getInnerIndex(1);
segmentNumba = getSegmentNumber(getLine(), currentChar)+1;
goto split;
}
else if(getChar(-1) == ' '){
innerIndex = getInnerIndex();
segmentNumba = getSegmentNumber(getLine(), currentChar)+1;
split:
segment s = segment(getSegment().string[innerIndex..$], *(getSegment().color.next));
getSegment().string = getSegment().string[0..innerIndex];
data[currentLine] = getLine()[0..segmentNumba] ~s~ getLine()[segmentNumba..$];
}
else
goto default;
break;
case 261: //right
right();
break;
case 260: //left
left();
break;
case 259: //up
up();
break;
case 258: //right
down();
break;
//TODO backspacing segments and lines away
case 127: //backspace
if(currentChar){
int innerIndex = getInnerIndex();
getSegment().string = getSegment().string[0..innerIndex-1] ~ getSegment().string[innerIndex..$];
left();
}
break;
case 13://return
break;
case 330://del
int innerIndex = getInnerIndex();
getSegment().string = getSegment().string[0..innerIndex] ~ getSegment().string[innerIndex+1..$]; //TODO Check of EOL and merge
//TODO Check for segment end
break;
default:
//throw new Exception(std.conv.toString(c));
append([cast(char)c]);
}
}
void returnto(){
attron(COLOR_PAIR(getColor().number));
move(currentY, currentX);
}
void display(){
Editor.drawInformation();
move(1,0);
clrtobot();
int rowsAbove = currentY - 1;//constant value trying to get "rowsCountedSoFar" to
int linePosition = currentLine;//counting up, where are we now?
int rowsLeftInLine; //how many rows are above the screen?
rowsAbove -= currentChar / Editor.cols;
while(rowsAbove){
linePosition--;//up one line of data
rowsLeftInLine = data[linePosition].rows();//How many rows does this line of data take up?
while(rowsLeftInLine != 0){//subtract each row from how many remain above cursor in screen
if(!(rowsAbove))
break;
rowsLeftInLine--;
rowsAbove--;
}
}
int roomLeft = Editor.rows-3;
data[linePosition].write(roomLeft, rowsLeftInLine);
linePosition++;
while(roomLeft && (data.length - linePosition)){
data[linePosition].write(roomLeft);
linePosition++;
}
Editor.highLightColor(getColor().number);
Editor.drawCommands();
returnto();
}
}
class Editor{
static int cols, rows;
static File[] opened;
static int currentFile = 0;
static File getFile(){
return opened[currentFile];
}
//TODO Those variables do not always functin
static void resize(){
int x, y;
resize(COLS, LINES);
}
static void resize(int c, int r){
rows = r;
if(cols != c){
cols = c;
drawInformation();
}
}
static void init(){
initscr(); // initialize the curses library
keypad(stdscr, true); // enable keyboard mapping
nonl(); //tell curses not to do NL->CR/NL on output
cbreak(); //take input chars one at a time, no wait
noecho(); //don't echo input
if (has_colors()){
start_color();
// Simple color assignment
init_pair(red.number, COLOR_RED, COLOR_BLACK);
init_pair(green.number, COLOR_GREEN, COLOR_BLACK);
init_pair(yellow.number, COLOR_YELLOW, COLOR_BLACK);
init_pair(blue.number, COLOR_BLUE, COLOR_BLACK);
init_pair(10, COLOR_WHITE, COLOR_RED);
init_pair(11, COLOR_WHITE, COLOR_GREEN);
init_pair(12, COLOR_WHITE, COLOR_YELLOW);
init_pair(13, COLOR_WHITE, COLOR_BLUE);
init_pair(COLOR_WHITE, COLOR_BLACK, COLOR_WHITE);
init_pair(8, COLOR_WHITE, COLOR_BLACK);
}
else
throw new Exception("No colors");
resize();
}
static void inputLoop(){
Editor.getFile().display();
while(1){
int c = getch();
switch(c){
case 14://TODO New
throw new Exception("^n");
case 17://TODO Quit
throw new Exception("^q");
case 18://TODO Run
throw new Exception("^r");
case 19://TODO Save
throw new Exception("^s");
case 27: //^arrows
int c2;
if((c2=getch()) == 91)
if((c2=getch()) == 49)
if((c2=getch()) == 59)
if((c2=getch()) == 53)
if((c2=getch()) == 67){
//^right
if(currentFile < opened.length-1){
Editor.getFile().display();
currentFile++;
}
break;
}
else if(c2 == 68){
//^left
if(currentFile > 0){
Editor.getFile().display();
currentFile--;
}
break;
}
throw new Exception(std.conv.toString(c2));
break;
default:
Editor.getFile().type(c);
}
getFile().display();
}
}
static bool save(){
return true;
}
static void drawInformation(){
move(0, 0);
attron(COLOR_PAIR(7));
char[] line = "File: "~getFile().name;
char[] spaces;
spaces.length = cols / 2 - line.length - 3;
foreach(inout c; spaces)
c = ' ';
line ~= spaces.dup;
if((getFile().data.length -1))
line ~= std.conv.toString(getFile().currentLine) ~','~ std.conv.toString(100*getFile().currentLine/(getFile().data.length-1)) ~'%';
else
line ~= std.conv.toString(getFile().currentLine);
char[] fileNum = std.conv.toString(currentFile+1) ~ '/' ~ std.conv.toString(opened.length);
spaces.length = cols - fileNum.length - line.length;
foreach(inout c; spaces)
c = ' ';
line ~= spaces.dup ~ fileNum;
addstr(line.toStringz());
}
static void highLightColor(int currentColor){
int spacing = cols/6;
drawColors();
switch(currentColor){
case 1:
move(rows-2, spacing);
attron(COLOR_PAIR(10));
addstr("evaluate");
break;
case 2:
move(rows-2, spacing*2);
attron(COLOR_PAIR(11));
addstr("literal"); //think of better word
break;
case 3:
move(rows-2, spacing*3);
attron(COLOR_PAIR(12));
addstr("contract"); //think of use
break;
case 4:
move(rows-2, spacing * 4);
attron(COLOR_PAIR(13));
addstr("comment");
break;
default:
throw new Exception("Invalid color");
}
getFile().returnto();
}
static void drawColors(){
int spacing = cols/6;
attron(COLOR_PAIR(8));
move(rows - 2, spacing-1);
addstr("[");
move(rows - 2, spacing);
attron(COLOR_PAIR(1));
addstr("evaluate");
move(rows - 2, spacing*2);
attron(COLOR_PAIR(2));
addstr("literal"); //think of better word
move(rows - 2, spacing*3);
attron(COLOR_PAIR(3));
addstr("contract"); //think of use
move(rows - 2, spacing*4);
attron(COLOR_PAIR(4));
addstr("comment");
attroff(A_UNDERLINE);
move(rows - 2, spacing*4+"comment".length);
attron(COLOR_PAIR(8));
addstr("]");
}
static void drawCommands(){
int spacing = cols/7;
move(rows-1, 0);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("^X");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" Exit");
move(rows-1, spacing);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("^S");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" Save");
move(rows-1, spacing*2);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("^N");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" New");
move(rows-1, spacing*3);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("^R");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" Run");
move(rows-1, spacing*4);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("^dir");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" Swap File");
move(rows-1, cols-"[space] Swap Color".length);
attron(COLOR_PAIR(7));
attroff(A_BOLD);
addstr("[space]");
attron(COLOR_PAIR(8) | A_BOLD);
addstr(" Swap Color");
attroff(A_BOLD);
}
}
void main(char[][] args){
//Editor.opened.length++;
if(args.length != 1)
foreach(arg; args[1..$])//add new file to list
Editor.opened ~= new File(arg);
else
//Editor.opened ~= new File();
throw new Exception("File creation not implemented yet.");
Editor.init();
while(1){
try
Editor.inputLoop();
catch(Exception e){
if(Editor.save()){
endwin();
throw e;
}
}
}
}