All pastes #798806 Raw Edit

Someone

public text v1 · immutable
#798806 ·published 2007-11-27 01:06 UTC
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;
			}
		}
	}
}