/********************************************************************
 *                                                                  *
 *      CRISP - Custom Reduced Instruction Set Programmers Editor   *
 *                                                                  *
 *      (C) 1989 Paul Fox					    *
 *                                                                  *
 *                                                                  *
 *    Please See COPYLEFT notice.                                   *
 *                                                                  *
 ********************************************************************/
# include	"crisp.h"  
    
/*-------------------------------
/*   #defines used so we know
/*   what to do on an undo.
/*-------------------------------*/ 
# define	OP_NOTHING	0	/* No inserts/deletes done yet.  */
# define	OP_INSERT	1	/* Last operation was an insert. */
# define	OP_DELETE	2	/* Last operation was a delete.  */ 

# define	CHAR	0
# define	CUT	1

/*****************************/
/*   Registers.  	     */
/*****************************/ 
string vir_insert;	/* Register containing last text inserted. */
string vir_delete;	/* Register containing last test deleted so */
			/* we can undo it.			   */
   
string	vi_start_fn = "^\\{";
int	vi_doing_dot = FALSE; /* If TRUE we are executing a '.' command. */
int	vi_iline = 0;	/* Start line of insert. */
int	vi_icol = 0;	/* Start column of insert. */
int	vi_dline = 0;	/* Start line of deleted text. */
int	vi_dcol = 0;	/* Start column of deleted text. */ 
int	vi_delins = FALSE; /* TRUE if doing a delete + insert, eg change */
int	vi_report = 5;
int	vi_mode = FALSE;
int	vi_num = 1;
int	vi_1st_ch = 1; 
 
int	_command_keymap, _insert_keymap;
string	last_command;

void
main()
{
	keyboard_push();
	assign_to_key("<F1>", "change_window");
	assign_to_key("<F2>", "move_edge");
	assign_to_key("<F3>", "create_edge");
	assign_to_key("<F4>", "delete_edge");
	assign_to_key("<F7>", "remember");
	assign_to_key("<F8>", "playback");
	assign_to_key("<F9>", "load_macro");
	assign_to_key("<F10>", "execute_macro");
	assign_to_key("<Left Arrow>", "cmd \"h\"");
	assign_to_key("<Right Arrow>", "cmd \"l\"");
	assign_to_key("<Up Arrow>", "cmd \"k\"");
	assign_to_key("<Down Arrow>", "cmd \"j\"");
	assign_to_key("<PgUp>",	"page_up");
	assign_to_key("<PgDn>",	"page_down"); 
	assign_to_key("<Esc>", "esc_cmd");
	assign_to_key(" ", "cmd \" \"");
	assign_to_key(".",	"cmd \".\"");
	assign_to_key("-", "cmd \"k\"");
	assign_to_key("+", "cmd \"j\"");
	assign_to_key("$", "cmd \"$\"") ;
	assign_to_key("~", "cmd \"~\"");
	assign_to_key("/", "search__fwd");
	assign_to_key("?", "search__back"); 
	assign_to_key("[", "search_fn \"[\"");
	assign_to_key("]", "search_fn \"]\"");
	assign_to_key(">", "cmdcmd \">\"");
	assign_to_key("\\<", "cmdcmd \"<\"");
	assign_to_key("\\%", "cmd \"%\"");
	assign_to_key("\\^", "up_arrow_cmd");
	assign_to_key("<Ctrl-B>", "page_up");
	assign_to_key("<Ctrl-E>", "edit_file");
	assign_to_key("<Ctrl-F>", "page_down");
	assign_to_key("<Ctrl-G>", "objects routines");
	assign_to_key("<Ctrl-H>", "cmd \"h\"");
	assign_to_key("<Ctrl-J>", "cmd \"j\"");
	assign_to_key("<Ctrl-L>", "mark 3");
	assign_to_key("<Ctrl-M>", "cmd \"j\"");
	assign_to_key("<Ctrl-N>", "edit_next_buffer");
	assign_to_key("<Ctrl-R>", "redraw");
	assign_to_key("<Ctrl-T>", "translate__fwd");
	assign_to_key("<Ctrl-U>", "cmd \"\025\"");
	assign_to_key("<Ctrl-W>", "w");
	assign_to_key("\x1a", "shell");
	assign_to_key("<Ctrl-]>", "tag_function");
	assign_to_key("#127", "cmd \"h\"");
	assign_to_key("0",	"cmd_zero");
	assign_to_key("1",	"vi_digit 1");
	assign_to_key("2",	"vi_digit 2");
	assign_to_key("3",	"vi_digit 3");
	assign_to_key("4",	"vi_digit 4");
	assign_to_key("5",	"vi_digit 5");
	assign_to_key("6",	"vi_digit 6");
	assign_to_key("7",	"vi_digit 7");
	assign_to_key("8",	"vi_digit 8");
	assign_to_key("9",	"vi_digit 9");
	assign_to_key("A", "cmd \"A\"");
	assign_to_key("B", "objects word_left");
	assign_to_key("C", "Change");
	assign_to_key("D",	"delete_to_eol");
	assign_to_key("F", "cmd \"F\"");
	assign_to_key("G", "cmd1 \"goto_line\"");
	assign_to_key("H", "top_of_window");
	assign_to_key("I",     "i_command");
	assign_to_key("J", "cmd \"J\"");
	assign_to_key("L", "end_of_window");
	assign_to_key("O",	"vi_Open");
	assign_to_key("P", "cmd \"P\"");
	assign_to_key("R", "cmd \"R\"");
	assign_to_key("W", "objects word_right");
	assign_to_key("X", "backspace");
	assign_to_key("Y", "cmdcmd \"Y\"");
	assign_to_key("Z",	"Z_cmd");
	assign_to_key("a", "cmd \"a\"");
	assign_to_key("b", "cmd \"b\"");
	assign_to_key("c", "cmdcmd \"c\"");
	assign_to_key("d", "cmdcmd \"d\"");
	assign_to_key("f", "cmd \"f\"");
	assign_to_key("h", "cmd \"h\"");
	assign_to_key("i", "cmd \"i\"");
	assign_to_key("j", "cmd \"j\"");
	assign_to_key("k", "cmd \"k\"");
	assign_to_key("l", "cmd \"l\"");
	assign_to_key("n",	"cmd \"n\"");
	assign_to_key("o",	"vi_open");
	assign_to_key("p",	"cmd \"p\"");
	assign_to_key("r",   "replace");
	assign_to_key("s", "cmd \"s\"");
	assign_to_key("u", "cmd \"u\"");
	assign_to_key("w", "cmd \"w\"");
	assign_to_key("x",	"cmd \"x\"");
	assign_to_key("y", "cmdcmd \"y\"") ;
	assign_to_key("z", "z_cmd") ;
	assign_to_key(":", "cmd \":\"");
	_command_keymap = inq_keyboard();
	keyboard_pop(1);

	keyboard_push();
	keyboard_typeables();
	assign_to_key("<Esc>", "vi_command_mode");
	assign_to_key("^H", "backspace");
	assign_to_key("#127", "backspace");
	autoindent("y");
	_insert_keymap = inq_keyboard();
	keyboard_pop(1);

	keyboard_push(_command_keymap);
}
int
vi_word_left()
{
	return word_left("<|[= .()/\t]\\c[~= .()/\t]");
}
void
z_cmd()
{
	switch (read_char()) {
		case '.': set_center_of_window();
		case '-': set_bottom_of_window();
		case '\r': set_top_of_window();
		case '+': set_top_of_window();
		default: beep();
		} 
	esc_cmd();
}
void
replace()
{
	int ch = read_char();
	string buf;

	sprintf(buf, "%c", ch); 
	vir_delete = read(1); 
	vir_insert = buf;
	inq_position(vi_iline, vi_icol); 
	inq_position(vi_dline, vi_dcol); 
	delete_char(vi_num);
	insert(buf, vi_num);
	last_command = "r" + buf;
}
void
cmd_zero()
{
	if (vi_1st_ch)
		beginning_of_line();
	else
		vi_digit(0);
}
void
vi_digit(int i)
{
	if (vi_1st_ch) {
		vi_num = 0;
		vi_1st_ch = FALSE;
		}
	vi_num = 10 * vi_num + i;
}
void
cmdcmd(string cmd_ch)
{
	string	buf, verb;
	int	ch;

	if (upper(cmd_ch) == cmd_ch &&
	    index("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", cmd_ch)) {
		cmd_ch = lower(cmd_ch);
		buf = cmd_ch;
		}
	else {
		ch = read_char();
		sprintf(buf, "%c", ch);
		}

	sprintf(last_command, "%d%s%c", vi_num, cmd_ch, ch);
	save_position();
	drop_anchor(MK_NONINC);
	if (index("#$/?%behjklw", buf) != 0) 
		cmd(buf);
	else {
		switch (ch) {
		  case '0':	beginning_of_line();
		  default:	if (cmd_ch == buf) { 
					raise_anchor(); 
					restore_position(); 
					beginning_of_line();
					save_position(); 
					drop_anchor(MK_NONINC);
					down(vi_num);
					}
				else {
					beep();
					raise_anchor();
					restore_position();
					return;
					}
		  }
		}

	switch (cmd_ch) {
	  case ">": 
	  	up();
		execute_macro(">>");
		raise_anchor();
		verb = ">ed";
	  case "<": 
	  	up();
		execute_macro("<<");
		raise_anchor();
		verb = "<ed";
	  case "c":
		remember_deletion(CUT, 0, !vi_doing_dot);
		if (ch == 'c') {
			insert("\n");
			up(); 
			}
		verb = "";  
		vi_delins = TRUE;
		vi_insert_mode();
	  case "d":
		remember_deletion(CUT, 0, TRUE); 
		verb = "deleted";
	  case "y":
		copy();
		verb = "yanked";
	  }

	if (vi_num >= vi_report && verb != "")
		message("%d lines %s.", 
			vi_num, 
			verb);
	restore_position(); 
	esc_cmd();
}
void
remember_deletion(int type, int chars, int clear_insert)
{ 
	switch (type) {
	  case CHAR: 
	  	vir_delete = read(chars);
	  	inq_position(vi_dline, vi_dcol);
		delete_char(chars);
	  case CUT:
		vir_delete = assign_mark(1);  
		inq_marked(vi_dline, vi_dcol); 
		cut();
	  } 
	if (clear_insert)
		vir_insert = "";
}
void
esc_cmd()
{
	vi_num = 1;
	vi_1st_ch = 1;
}
void
cmd(string cmd_ch)
{	int	i, col; 
	int first_loop = TRUE; 
	string s;

	set_calling_name("");
	if (cmd_ch == " ")
		cmd_ch = "l"; 
	if (index("%u:jhkl .#$bfnw", cmd_ch) == 0)
		sprintf(last_command, "%d%s", vi_num, cmd_ch);
	while (vi_num > 0) {
		switch (cmd_ch) {  
		  case "\025":
		  	undo();
			vi_num = 0;
		  case ":":
		  	execute_macro();
			vi_num = 0;
		  case ".":
			dot_cmd();
			vi_num = 0;
		  case "/":	search__fwd();
		  case "?":	search__back();
		  case "%":	
			brace(); 
			vi_num = 0;
		  case "~":
			s = read(1);
			delete_char();
			if (upper(s) == s)
				insert(lower(s));
			else
				insert(upper(s));
		  case "#":
			down(vi_num - 1);
			end_of_line();
			inq_position(NULL, col);
			vi_num = 0;
		  case "$":
			down(vi_num - 1);
			end_of_line();
			inq_position(NULL, col);
			if (col != 1)
				left();
			vi_num = 0;
		  case "a":
			right();
			vi_insert_mode();
		  case "b": objects("word_left");
		  case "f": 
		  	sprintf(s, "%c", read_char()); 
			right();
			re_search(NULL, s);
			break;
		  case "h":
			left(vi_num);
			vi_num = 0; 
		  case "i": 
		  	if (!first_loop)
				vi_doing_dot = TRUE;
		  	vi_insert_mode();  
		  case "j":
			down(vi_num);
			vi_num = 0;
		  case "k":
			up(vi_num);
			vi_num = 0;
		  case "l":
			right(vi_num);
			vi_num = 0;
		  case "n": 	search_next();
		  case "p":
			down(); 
			beginning_of_line();
			paste();
			up(); 
		  case "s":
		  	delete_char(vi_num); 
			vi_insert_mode();
			vi_num = 0;
		  case "u":	  
  			if (vir_insert != "") {
			  	move_abs(vi_iline, vi_icol); 
				delete_char(strlen(vir_insert)); 
				}   
		  	if (vir_delete != "") {
			  	move_abs(vi_dline, vi_dcol);
				insert(vir_delete);
			  	move_abs(vi_dline, vi_dcol);
				} 
# define SWAP(a,b,t) t = a; a = b; b = t; 
			{int x;
			SWAP(vi_iline, vi_dline, x); 
			SWAP(vi_icol, vi_dcol, x);
			SWAP(vir_insert, vir_delete, s);
  	 		vi_num = 0;	
			}
		  case "w": objects("word_right");
		  case "x": 
		  	remember_deletion(CHAR, vi_num, TRUE); 
			vir_insert = "";
			vi_num = 0; 
		  case "A":
		  	if (first_loop) 
				end_of_line();
			else
				vi_doing_dot = TRUE;
		  	vi_insert_mode();  
		  case "J":	vi_join_line();
		  case "P":	
			inq_position(i); 
			beginning_of_line();
			paste(); 
			move_abs(i);
		  case "R":
				insert_mode();
				vi_insert_mode();
				insert_mode();
				vi_num = 0;
		  } 
		first_loop = FALSE;
		-- vi_num;
		}
	vi_num = 1;
	vi_1st_ch = 1;
}
void
dot_cmd()
{
	int	i;

	for (i = 1; i <= strlen(last_command); i ++)
		push_back(key_to_int("\\" + substr(last_command, i, 1))); 
	vi_doing_dot = TRUE;
}
void
cmd1(string mac)
{
	string buf;

	if (vi_1st_ch && (mac == "goto_line"))
		buf = "end_of_buffer";
	else
		sprintf(buf, "%s %d", mac, vi_num);

	execute_macro(buf);
	vi_num = 1;
	vi_1st_ch = 1;
}
void
vi_insert_mode()
{ 
	if (vi_doing_dot) { 
		insert(vir_insert);
		vi_doing_dot = FALSE;
		return;
		}
	keyboard_pop(1);
	keyboard_push(_insert_keymap);
	if (vi_mode)
		message("Insert mode."); 
	inq_position(vi_iline, vi_icol); 
}
void
vi_command_mode()
{	int line, col; 

	keyboard_pop(1);
	keyboard_push(_command_keymap);
	if (vi_mode)
		message("Command mode.");   
		
	inq_position(line, col);
	vir_insert = assign_region(vi_iline, vi_icol, line, col);    
	if (! vi_delins) {
		vir_delete = ""; 
		vi_delins = FALSE;
		}
}
void
vi_open()
{
	end_of_line();
	right();
	_indent();
	vi_insert_mode();
}
void
vi_Open()
{	int line;

	inq_position(line); 
	if (line == 1) {
		beginning_of_line();
		insert("\n"); 
		up(); 
		vi_insert_mode();
		}
	else {
		up();
		vi_open(); 
		}
}
void
vi_add()
{
	right();
	vi_insert_mode();
}
void
e(string file)
{
	edit_file(file);
}
void
r(string file)
{
	read_file(file);
}
void
w()
{
	set_calling_name("");
	write_buffer();
}
void
n()
{
	edit_next_buffer();
}
void
p()
{
	previous_edited_buffer();
} 
void
q()
{
	x();
}
void
x()
{
	set_calling_name("");
	exit();
}
void
vi_join_line()
{
	last_command = "vi_join_line"; 
	join_line();
}
void
Change()
{  
	push_back(key_to_int("\\#"));
	cmdcmd("c");
}
void
up_arrow_cmd()
{
	beginning_of_line();
	re_search(NULL, "$|[~ \t]");
}
void
i_command()
{
	up_arrow_cmd();
	vi_insert_mode();
}
void
Z_cmd()
{
	int ch = read_char();
	if (ch != 'Z') {
		beep();
		return;
		}
	w();
	x();
} 
void
show()
{
	buffer_list(1);
} 
void
search_fn(string str)
{ 
	int ch1 = read_char(); 
	int ch = atoi(str, 0);
	
	if (ch != ch1) {
		beep();
		ch1 = 0x1b;
		}
	if (ch1 == 0x1b) {
		vi_num = 0;
		vi_1st_ch = TRUE;
		return;
		}  
	if (ch1 == ']') { 
		down();
		if (re_search(NULL, vi_start_fn) <= 0)
			end_of_buffer();
		}
	else { 
		up();
		if (re_search(SF_BACKWARDS, vi_start_fn) <= 0)
			top_of_buffer();
		} 
	set_center_of_window();
}
/***************************************************
/*   Routines to manipulate the scrap.
/***************************************************/
string
assign_mark(int n)
{	int	start_line, start_col, end_line, end_col; 

	if (inq_marked(start_line, start_col, end_line, end_col) == 0)
		return ""; 
	return assign_region(start_line, start_col, end_line, end_col+n);
}
string
assign_region(int start_line, int start_col, int end_line, int end_col)
{ 	string	scrap = "";

	save_position();
	move_abs(start_line, start_col);
	while (start_line < end_line) { 
		scrap += read();
		start_line++;
		move_abs(start_line, 1);
		} 
	if (start_line == end_line) { 
  		move_abs(0, end_col);
		save_position();
		insert("\n");
		up();
		move_abs(0, start_col);
		scrap += read(strlen(read()) - 1); 
		restore_position();
		delete_char();
		}
	else { 
		scrap += read(end_col);
		}  
	restore_position();
	return scrap;
}  

