/********************************************************************
 *                                                                  *
 *      CRISP - Custom Reduced Instruction Set Programmers Editor   *
 *                                                                  *
 *      (C) Paul Fox, 1989                                          *
 *                                                                  *
 *                                                                  *
 *    Please See COPYLEFT notice.                                   *
 *                                                                  *
 ********************************************************************/
# include	"crisp.h"

/**********************************************************************/
/*   If  TRUE  get previous line to function definition. For C files  */
/*   this usually means get the type info as well.		      */
/**********************************************************************/
int	routines_prev_line = FALSE;

/**********************************************************************/
/*   Variable needed to keep track of Ctrl-G nesting.		      */
/**********************************************************************/
int	rtn_nested = FALSE;

void
objects(string function, int arg1)
{
	string	ext,	   /* Extension of current buffer.	*/
		macro_name;/* Name of macro to call.		*/

	inq_names(NULL, ext, NULL);
	macro_name = ext + "_" + function;
	if (!inq_macro(macro_name))
		macro_name = "default_" + function;
	execute_macro(macro_name, arg1);
}
/**************************************************************/
/*   Macros to shift left & shift right the currently marked
/*   block.
/**************************************************************/
void
shift_right()
{
	objects("shift_right");
}
/**********************************************************************/
/*   Define synonyms for shift_left to make it easier to type.	      */
/**********************************************************************/
void
shiftl(declare arg)
{
	shift_left(arg);
}
void
lshift(declare arg)
{
	shift_left(arg);
}
/**********************************************************************/
/*   Define synonyms for shift_right to make it easier to type.	      */
/**********************************************************************/
void
shiftr()
{
	shift_right();
}
void
rshift()
{
	shift_right();
}
void
shift_left()
{	int	count = 1;
	declare	arg;
	
	if (get_parm(0, arg) > 0) {
		if (is_string(arg))
			count = atoi(arg);
		else if (is_integer(arg))
			count = arg;
		}
	if (count <= 0)
		count = 1;
	while (count-- > 0)
		objects("shift_left");
}
void
default_shift_right()
{
	int marked = inq_marked();
	int	use_tabs = use_tab_char("y");
	
	use_tab_char(use_tabs);
	if (marked == 0)
		drop_anchor(MK_LINE);
	beginning_of_line();
	re_translate(SF_GLOBAL | SF_BLOCK, "<", use_tabs ? "\t" : " ");
	if (marked == 0)
		raise_anchor();
}
void
default_shift_left()
{
	int marked = inq_marked();

	if (marked == 0)
		drop_anchor(MK_LINE);
	beginning_of_line();
	re_translate(SF_GLOBAL | SF_BLOCK, "<{\t}|{ }", "");
	if (marked == 0)
		raise_anchor();
}
/*------------------------------------------------*/
/*   Delete word left/right macros.
/*   Uses the word_left/word_right macros.
/*------------------------------------------------*/
void
default_delete_word_right()
{
	delete_word(default_word_right());
}
void
default_delete_word_left()
{
	delete_word(default_word_left()); 
}
void
delete_word()
{	int	i;
	
	drop_anchor(4);
	/* Get argument as a side effect only */
	get_parm(0, i);
	delete_block();
}
/*------------------------------------------------*/
/*	word_left macros.			  */
/*------------------------------------------------*/
int
default_word_left()
{
	return word_left("<|[ .()/\t]\\c[~ .()/\t]");
}
int
word_left(string pat)
{	int line, col, line1, col1; 

	inq_position(line, col);
	re_search(SF_BACKWARDS | SF_MAXIMAL, pat);
	inq_position(line1, col1);
	if (line == line1 && col == col1) {
		prev_char();
		re_search(SF_BACKWARDS | SF_MAXIMAL, pat);
		}
	return 0;
}
/*------------------------------------------------*/
/*	word_right macros.			  */
/*------------------------------------------------*/
void
default_word_right() 
{
	word_right();
}
string	word_chars = "_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
string	space_chars = " \t";
string	unword_chars = "]-\"'\t.>=";
/**********************************************************************/
/*   Vi compatible word right function.				      */
/**********************************************************************/
void
word_right()
{	string	ch = trim(read(1));
	int	col;

	if (ch == "") {
		if (re_search(NULL, "?") <= 0) {
			beep();
			return;
			}
		ch = read(1);
		}
	else {
		inq_position(NULL, col);
		if (col == 1)
			next_char();
		}
	if (index(word_chars, ch)) {
		re_search(NULL, "^|[^" + word_chars + "]");
		re_search(NULL, "[^" + space_chars + "]");
		}
	else if (index(space_chars, ch))
		re_search(NULL, "^|[^" + space_chars + "]");
	else
		re_search(NULL, "^|[" + word_chars + space_chars + "]");
	if (index(space_chars, read(1)))
		re_search(NULL, "[^" + space_chars + "]");
}

/**********************************************************************/
/*   If  no  routines  available  for  current file, then generate a  */
/*   list of all routines macros and let him have a go at these.      */
/**********************************************************************/
void
default_routines()
{	list	macs = macro_list();
	list	rtn_list;
	int	entry = 0;
	int	cnt = 0;
	
	while (1) {
		entry = re_search(NULL, "_routines$", macs, entry);
		if (entry < 0)
			break;
		if (macs[entry] != "default_routines") {
			rtn_list[cnt] = macs[entry];
			cnt++;
			}
		entry++;
		}
	entry = select_list("Routine macros", 
		"Select function",
		1, rtn_list, TRUE, NULL);
	if (entry < 0)
		return;
	execute_macro(rtn_list[entry - 1]);
}
/**********************************************************************/
/*   Routines for Intel assembler files				      */
/**********************************************************************/
void
s_routines()
{
	asm_routines();
}
string
s_routines_trim(string routine_name)
{
	return routine_name;
}
void
asm_routines() 
{
	select_routine("<*{PROC[ \t]}|{proc[ \t]}",
			"Assembler Subroutines", "asm_routines_trim");
}
string
asm_routines_trim(string routine_name)
{
	if (substr(routine_name, 1, 1) == "_")
		return substr(routine_name, 2);
	return routine_name;
}
/**********************************************************************/
/*   Routines for PostScript files.				      */
/**********************************************************************/
void
ps_routines() 
{
	select_routine("</*\\{",
			"PostScript Definitions", "ps_routines_trim");
}
string
ps_routines_trim(string routine_name)
{
	return routine_name;
}
/**********************************************************************/
/*   Routines for Yacc source files.				      */
/**********************************************************************/
void
y_routines() 
{
	select_routine("<[_a-zA-Z0-9]+[ \t]@:",
			"Yacc Rules", "y_routines_trim");
}
string
y_routines_trim(string routine_name)
{	int	spos = re_search(NULL, ":", routine_name); 

	if (spos > 0)
		routine_name = substr(routine_name, 1, spos - 1);
	return trim(routine_name);
}
void
cr_routines() 
{
	select_routine("<[_a-zA-Z0-9]+[ \t]@*([^)\"]@)[^,;]@>",
			"Functions", "c_routines_trim");
}
void
cb_routines()
{
	c_routines();
}
void
cc_routines()
{
	c_routines();
}
void
C_routines()
{
	c_routines();
}
void
c_routines() 
{
	select_routine("<[_a-zA-Z0-9]+[ \t]@*([^)\"]@)[^,;]@>",
			"Functions", "c_routines_trim");
}
string
c_routines_trim(string routine_name)
{
	int	spos;

	spos = re_search(NULL, "[;/{]", routine_name);
	if (spos > 0)
		routine_name = substr(routine_name, 1, spos - 1);
	return trim(routine_name);
}
void
news_routines() 
{
	select_routine("<Subject:",
			"Subjects", "news_routines_trim");
}
string
news_routines_trim(string routine_name)
{
	int	spos;

	spos = re_search(NULL, ":", routine_name);
	if (spos > 0)
		routine_name = substr(routine_name, spos + 1);
	return trim(routine_name);
}
void
h_routines() 
{
	select_routine("<{typedef}|{struct}|{union}[^;]+$", "Structures", "h_routines_trim");
}
string
h_routines_trim(string routine_name)
{
	int	spos;

	spos = re_search(NULL, "[;/{]", routine_name);
	if (spos > 0)
		routine_name = substr(routine_name, 1, spos - 1);
		
	/***********************************************/
	/*   If  we  have  'typedef  struct ...' then  */
	/*   remove the typedef part.		       */
	/***********************************************/
	if (substr(routine_name, 1, 1) == "t" && index(routine_name, "struct"))
		routine_name = substr(routine_name, 9);
	spos = re_search(NULL, "[ \t]", routine_name);
	if (spos > 0)
		routine_name = trim(substr(routine_name, spos + 1)) + 
			" : " + substr(routine_name, 1, spos - 1);

	return trim(routine_name);
}
void
hlp_routines() 
{
	select_routine("<\\> ", "Sections", "hlp_routines_trim");
}
string
hlp_routines_trim(string routine_name)
{
	return substr(routine_name, 3);
}
void
m_routines() 
{
	select_routine("<({macro}|{replacement}\\c", "Macros", "m_routines_trim");
}
string
m_routines_trim(string routine_name)
{
	int	spos;

	spos = re_search(NULL, "[ \t;]", routine_name);
	if (spos > 0)
		return substr(routine_name, 1, spos - 1);
	return routine_name;
}
void
mm_routines() 
{	list	levels;
	int	i;
	
	for (i = 0; i < 5; )
		levels[i++] = 0;
	select_routine("<\.{TH??}|{H}|{SH}", "Sections", "mm_routines_trim");
}
string
mm_routines_trim(string routine_name)
{	extern list levels;
	int	i, j;
	string	s, s1;
	
	if (substr(routine_name, 1, 2) != ".H" || routines_prev_line)
		return routine_name;
	s = substr(routine_name, 4);
	j = atoi(s) - 1;
	if (j < 0 || j > 5)
		return routine_name;
	for (i = j; i < 5; )
		levels[++i] = 0;
	levels[j] = levels[j] + 1;
	s = substr(s, index(s, " "));
	s1 = "";
	for (i = 0; levels[i]; i++)
		s1 += levels[i] + ".";
	return s1 + s;
}
/*------------------------------------------------*/
/*
/*   Routine to select language sepecific entities from a buffer.
/*
/*	(macro select_routine
/*			sstr		search-string to find matching line.
/*			name		name of things we are looking for.
/*			)
/*------------------------------------------------*/
	
void
select_routine(string sstr, string name, string trim_func)
{
	list	line_no_list;	/* List of line-numbers so we know	*/
				/* where to go to when the user makes	*/
				/* a selection.				*/
	int	curbuf,		/* Current buffer.			*/
		macbuf,		/* Buffer to put macro names in.	*/
		mac_cnt,	/* Count of macros encountered so far.  */
		line,		/* Temporary to contain line number of  */
				/* of matched macro-name.		*/
		display_win,	/* Window to display macros in.		*/
		selection,	/* Users selection.			*/
		cur_line,	/* Current line -- so we can home in on */
				/* the popup.				*/
		mac_line = 0,	/* Line to place cursor on to start with.*/
		width;		/* Maximum width so far.		*/
	string	routine_name,	/* Name of currently matched macro.	*/
		msg;
		
	inq_position(cur_line);
	curbuf = inq_buffer();
	save_position();
	macbuf = create_buffer(name, NULL, 1);
	top_of_buffer();
	message("Scanning for %s...", lower(name));
	mac_cnt = 0;
	width = 10;
	
	
	while (re_search(NULL, sstr)) {
		routine_name = ltrim(trim(compress(read())));
		routine_name = execute_macro(trim_func, routine_name);
		/***********************************************/
		/*   If   user   wants   previous   line   to  */
		/*   function definition -- get that too.      */
		/***********************************************/
		if (routines_prev_line) {
			up();
			if (index(routine_name, "("))
				routine_name = substr(routine_name, 1, 
					index(routine_name, "(") - 1) + "()";
			routine_name = ltrim(trim(compress(read()))) + 
				"\t" + routine_name + ";";
			down();
			}
		inq_position(line);
		if (line == cur_line && mac_line == 0)
			mac_line = mac_cnt + 1;
		else if (line > cur_line && mac_line == 0)
			mac_line = mac_cnt;
			
		line_no_list[mac_cnt] = line;
		set_buffer(macbuf);
		if (mac_cnt)
			insert("\n");
			
		insert(routine_name);
		++ mac_cnt;
		if (strlen(routine_name) > width)
			width = strlen(routine_name);
		set_buffer(curbuf);
		next_char();
		}
	restore_position();

	/*-------------------------------------------------*/
	/*   If no macros found just tell the user and exit.
	/*-------------------------------------------------*/
	if (mac_cnt == 0) {
		message("No %s found.", lower(name));
		delete_buffer(macbuf);
		return; 
		}
	/***********************************************/
	/*   We found some macros -- display them.     */
	/***********************************************/
	if (++width < 26)
		width = 26;
	msg = int_to_key(ALT_C) + " - copy to scrap. ";
	display_win = sized_window(mac_cnt + 1, width, msg);
	message("Use arrow keys to make a selection.");
	selection = select_buffer(macbuf, display_win, SEL_NORMAL,
		routine_keys(),
		NULL,
"help_display \"features/program.hlp\" \"Function List\" \"> The List Functions Macro\"", 
		mac_line);
	delete_buffer(macbuf);
	message("");
	if (selection < 0) {
		if (routines_prev_line && !rtn_nested) {
			rtn_nested = TRUE;
			select_routine(sstr, name, trim_func);
			routines_prev_line = FALSE;
			rtn_nested = FALSE;
			}
		return;
		}
	goto_line(line_no_list[selection - 1]);
}
void
routine_keys()
{
	assign_to_key("<Ctrl-G>", "routines_detailed");
}
void
routines_detailed()
{
	routines_prev_line = TRUE;
	push_back(key_to_int("<Esc>"));
}
