Browse Source

Improvements to experimental line editor

K. Lange 4 years ago
parent
commit
b25aa946e5
5 changed files with 346 additions and 41 deletions
  1. 104 28
      apps/sh.c
  2. 5 0
      base/home/local/.eshrc
  3. 5 0
      base/usr/include/toaru/rline_exp.h
  4. 231 12
      lib/rline_exp.c
  5. 1 1
      util/auto-dep.py

+ 104 - 28
apps/sh.c

@@ -119,7 +119,7 @@ void gethost() {
 	memcpy(_hostname, buf.nodename, len+1);
 }
 
-void print_extended_ps(char * format) {
+void print_extended_ps(char * format, char * buffer, int * display_width) {
 	/* Get the time */
 	struct tm * timeinfo;
 	struct timeval now;
@@ -151,12 +151,25 @@ void print_extended_ps(char * format) {
 		sprintf(ret, "%d ", last_ret);
 	}
 
+	size_t offset = 0;
+	int is_visible = 1;
+	*display_width = 0;
+
 	while (*format) {
 		if (*format == '\\') {
 			format++;
 			switch (*format) {
 				case '\\':
-					putchar(*format);
+					buffer[offset++] = *format;
+					(*display_width) += is_visible ? 1 : 0;
+					format++;
+					break;
+				case '[':
+					is_visible = 0;
+					format++;
+					break;
+				case ']':
+					is_visible = 1;
 					format++;
 					break;
 				case '0':
@@ -180,56 +193,93 @@ void print_extended_ps(char * format) {
 								format++;
 							}
 						}
-						putchar(i);
+						buffer[offset++] = i;
+						(*display_width) += is_visible ? 1 : 0;
 					}
 					break;
 				case 'e':
-					putchar('\033');
+					buffer[offset++] = '\033';
+					(*display_width) += is_visible ? 1 : 0;
 					format++;
 					break;
 				case 'd':
-					printf("%s", date_buffer);
+					{
+						int size = sprintf(buffer+offset, "%s", date_buffer);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				case 't':
-					printf("%s", time_buffer);
+					{
+						int size = sprintf(buffer+offset, "%s", time_buffer);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				case 'h':
-					printf("%s", _hostname);
+					{
+						int size = sprintf(buffer+offset, "%s", _hostname);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				case 'u':
-					printf("%s", username);
+					{
+						int size = sprintf(buffer+offset, "%s", username);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				case 'w':
-					printf("%s", _cwd);
+					{
+						int size = sprintf(buffer+offset, "%s", _cwd);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				case '$':
-					putchar(getuid() == 0 ? '#' : '$');
+					buffer[offset++] = (getuid() == 0 ? '#' : '$');
+					(*display_width) += is_visible ? 1 : 0;
 					format++;
 					break;
 				case 'U': /* prompt color string */
-					printf("%s", getuid() == 0 ? "\033[1;38;5;196m" : "\033[1;38;5;47m");
+					{
+						int size = sprintf(buffer+offset, "%s", getuid() == 0 ? "\033[1;38;5;196m" : "\033[1;38;5;47m");
+						offset += size;
+						/* Does not affect size */
+					}
 					format++;
 					break;
 				case 'r':
-					printf("%s", ret);
+					{
+						int size = sprintf(buffer+offset, "%s", ret);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 				default:
-					printf("\\%c", *format);
+					{
+						int size = sprintf(buffer+offset, "\\%c", *format);
+						offset += size;
+						(*display_width) += is_visible ? size : 0;
+					}
 					format++;
 					break;
 			}
 		} else {
-			putchar(*format);
+			buffer[offset++] = *format;
+			(*display_width) += is_visible ? 1 : 0;
 			format++;
 		}
 	}
 
+	buffer[offset] = '\0';
 }
 
 #define FALLBACK_PS1 "\\u@\\h \\w\\$ "
@@ -237,7 +287,10 @@ void print_extended_ps(char * format) {
 /* Draw the user prompt */
 void draw_prompt(void) {
 	char * ps1 = getenv("PS1");
-	print_extended_ps(ps1 ? ps1 : FALLBACK_PS1);
+	char buf[1024];
+	int display_width;
+	print_extended_ps(ps1 ? ps1 : FALLBACK_PS1, buf, &display_width);
+	fprintf(stdout, "%s", buf);
 	fflush(stdout);
 }
 
@@ -259,7 +312,10 @@ void redraw_prompt_func(rline_context_t * context) {
 void draw_prompt_c() {
 	char * ps2 = getenv("PS2");
 	if (ps2) {
-		print_extended_ps(ps2);
+		char buf[1024];
+		int display_width;
+		print_extended_ps(ps2, buf, &display_width);
+		fprintf(stdout, "%s", buf);
 	} else {
 		printf("> ");
 	}
@@ -512,21 +568,41 @@ void add_argument(list_t * argv, char * buf) {
 }
 
 int read_entry(char * buffer) {
-	rline_callbacks_t callbacks = {
-		tab_complete_func, redraw_prompt_func, NULL,
-		NULL, NULL, NULL, NULL, NULL
-	};
-	int buffer_size = experimental_rline ? rline_experimental(buffer, LINE_LEN) : rline((char *)buffer, LINE_LEN, &callbacks);
-	return buffer_size;
+	if (experimental_rline) {
+		char lprompt[1024], rprompt[1024];
+		int lwidth, rwidth;
+
+		char * ps1 = getenv("PS1_LEFT");
+		print_extended_ps(ps1 ? ps1 : FALLBACK_PS1, lprompt, &lwidth);
+
+		char * ps1r = getenv("PS1_RIGHT");
+		print_extended_ps(ps1r ? ps1r : "", rprompt, &rwidth);
+
+		rline_exp_set_prompts(lprompt, rprompt, lwidth, rwidth);
+		rline_exp_set_shell_commands(shell_commands, shell_commands_len);
+		rline_exp_set_tab_complete_func(tab_complete_func);
+		return rline_experimental(buffer, LINE_LEN);
+	} else {
+		rline_callbacks_t callbacks = {
+			tab_complete_func, redraw_prompt_func, NULL,
+			NULL, NULL, NULL, NULL, NULL
+		};
+		return rline((char *)buffer, LINE_LEN, &callbacks);
+	}
 }
 
 int read_entry_continued(char * buffer) {
-	rline_callbacks_t callbacks = {
-		tab_complete_func, redraw_prompt_func_c, NULL,
-		NULL, NULL, NULL, NULL, NULL
-	};
-	int buffer_size = experimental_rline ? rline_experimental(buffer, LINE_LEN) : rline((char *)buffer, LINE_LEN, &callbacks);
-	return buffer_size;
+	if (experimental_rline) {
+		rline_exp_set_prompts("> ", "", 2, 0);
+		rline_exp_set_shell_commands(shell_commands, shell_commands_len);
+		return rline_experimental(buffer, LINE_LEN);
+	} else {
+		rline_callbacks_t callbacks = {
+			tab_complete_func, redraw_prompt_func_c, NULL,
+			NULL, NULL, NULL, NULL, NULL
+		};
+		return rline((char *)buffer, LINE_LEN, &callbacks);
+	}
 }
 
 int variable_char(uint8_t c) {

+ 5 - 0
base/home/local/.eshrc

@@ -1 +1,6 @@
+export PS1_TITLE="\\[\\e]1;\\u@\\h:\\w\\007\\e]2;\\u@\\h:\\w\\007\\]"
+export PS1_RIGHT="\\[\\e[1m\\e[38;5;59m\\][\\[\\e[38;5;173m\\]\\d \\[\\e[38;5;167m\\]\\t\\[\\e[38;5;59m\\]] "
+export PS1_LEFT="${PS1_TITLE}\\[\\e[1m\\e[38;5;221m\\]\\u\\[\\e[38;5;59m\\]@\\[\\e[38;5;81m\\]\\h \\[\\e[38;5;167m\\]\\r\\[\\e[0m\\]\\w\\U\\\$\\[\\e[0m\\] "
+# Old prompt
 export PS1="\\e]1;\\u@\\h:\\w\\007\\e]2;\\u@\\h:\\w\\007\\e[1m\\e[s\\e[400C\\e[16D\\e[1m\\e[38;5;59m[\\e[38;5;173m\\d \\e[38;5;167m\\t\\e[38;5;59m]\\e[u\\e[38;5;221m\\u\\e[38;5;59m@\\e[38;5;81m\\h \\e[38;5;167m\\r\\e[0m\\w\\U\\\$\\e[0m "
+export RLINE_THEME="sunsmoke"

+ 5 - 0
base/usr/include/toaru/rline_exp.h

@@ -1,3 +1,8 @@
 #pragma once
 
+#include <toaru/rline.h>
+
 extern int rline_experimental(char * buffer, int buf_size);
+extern int rline_exp_set_prompts(char * left, char * right, int left_width, int right_width);
+extern int rline_exp_set_shell_commands(char ** cmds, int len);
+extern int rline_exp_set_tab_complete_func(rline_callback_t func);

+ 231 - 12
lib/rline_exp.c

@@ -26,6 +26,8 @@
 #include <locale.h>
 #include <sys/ioctl.h>
 
+#include <toaru/rline.h>
+
 #define ENTER_KEY     '\n'
 #define BACKSPACE_KEY 0x08
 #define DELETE_KEY    0x7F
@@ -45,9 +47,11 @@ typedef struct {
 
 line_t * the_line = NULL;
 
+static int loading = 0;
 static int column = 0;
 static int offset = 0;
 static int width =  0;
+static int buf_size_max = 0;
 
 /**
  * TODO: Need to make prompt configurable.
@@ -61,6 +65,30 @@ static char * prompt = "> ";
 static int prompt_right_width = 4;
 static char * prompt_right = " :) ";
 
+static char ** shell_commands = {0};
+static int shell_commands_len = 0;
+
+int rline_exp_set_shell_commands(char ** cmds, int len) {
+	shell_commands = cmds;
+	shell_commands_len = len;
+	return 0;
+}
+
+int rline_exp_set_prompts(char * left, char * right, int left_width, int right_width) {
+	prompt = left;
+	prompt_right = right;
+	prompt_width = left_width;
+	prompt_right_width = right_width;
+	return 0;
+}
+
+static rline_callback_t tab_complete_func = NULL;
+
+int rline_exp_set_tab_complete_func(rline_callback_t func) {
+	tab_complete_func = func;
+	return 0;
+}
+
 static int to_eight(uint32_t codepoint, char * out) {
 	memset(out, 0x00, 7);
 
@@ -193,9 +221,36 @@ static const char * COLOR_SELECTFG  = "@0";
 static const char * COLOR_RED       = "@1";
 static const char * COLOR_GREEN     = "@2";
 
-static void load_colorscheme_sunsmoke(void) {
+void rline_exp_load_colorscheme_default(void) {
+	COLOR_FG        = "@9";
+	COLOR_BG        = "@9";
+	COLOR_ALT_FG    = "@5";
+	COLOR_ALT_BG    = "@9";
+	COLOR_NUMBER_FG = "@3";
+	COLOR_NUMBER_BG = "@9";
+	COLOR_STATUS_FG = "@7";
+	COLOR_STATUS_BG = "@4";
+	COLOR_TABBAR_BG = "@4";
+	COLOR_TAB_BG    = "@4";
+	COLOR_KEYWORD   = "@4";
+	COLOR_STRING    = "@2";
+	COLOR_COMMENT   = "@5";
+	COLOR_TYPE      = "@3";
+	COLOR_PRAGMA    = "@1";
+	COLOR_NUMERAL   = "@1";
+	COLOR_ERROR_FG  = "@7";
+	COLOR_ERROR_BG  = "@1";
+	COLOR_SEARCH_FG = "@0";
+	COLOR_SEARCH_BG = "@3";
+	COLOR_SELECTBG  = "@7";
+	COLOR_SELECTFG  = "@0";
+	COLOR_RED       = "@1";
+	COLOR_GREEN     = "@2";
+}
+
+void rline_exp_load_colorscheme_sunsmoke(void) {
 	COLOR_FG        = "2;230;230;230";
-	COLOR_BG        = "2;31;31;31";
+	COLOR_BG        = "@9";
 	COLOR_ALT_FG    = "2;122;122;122";
 	COLOR_ALT_BG    = "2;46;43;46";
 	COLOR_NUMBER_FG = "2;150;139;57";
@@ -249,6 +304,15 @@ static char * syn_sh_keywords[] = {
 	NULL,
 };
 
+static int variable_char(uint8_t c) {
+	if (c >= 'A' && c <= 'Z')  return 1;
+	if (c >= 'a' && c <= 'z') return 1;
+	if (c >= '0' && c <= '9')  return 1;
+	if (c == '_') return 1;
+	if (c == '?') return 1;
+	return 0;
+}
+
 static int syn_sh_extended(line_t * line, int i, int c, int last, int * out_left) {
 	(void)last;
 
@@ -274,6 +338,23 @@ static int syn_sh_extended(line_t * line, int i, int c, int last, int * out_left
 		return FLAG_STRING;
 	}
 
+	if (line->text[i].codepoint == '$' && last != '\\') {
+		if (i < line->actual - 1 && line->text[i+1].codepoint == '{') {
+			int j = i + 2;
+			for (; j < line->actual+1; ++j) {
+				if (line->text[j].codepoint == '}') break;
+			}
+			*out_left = (j - i);
+			return FLAG_NUMERAL;
+		}
+		int j = i + 1;
+		for (; j < line->actual + 1; ++j) {
+			if (!variable_char(line->text[j].codepoint)) break;
+		}
+		*out_left = (j - i) - 1;
+		return FLAG_NUMERAL;
+	}
+
 	if (line->text[i].codepoint == '"') {
 		int last = 0;
 		for (int j = i+1; j < line->actual + 1; ++j) {
@@ -590,6 +671,15 @@ static void recalculate_syntax(line_t * line) {
 			}
 		}
 
+		for (int s = 0; s < shell_commands_len; ++s) {
+			int c = check_line(line, i, shell_commands[s], last);
+			if (c == 1) {
+				left = strlen(shell_commands[s])-1;
+				state = FLAG_KEYWORD;
+				goto _continue;
+			}
+		}
+
 _continue:
 		line->text[i].flags = state;
 	}
@@ -625,12 +715,22 @@ static line_t * line_insert(line_t * line, char_t c, int offset) {
 	/* There is one new character in the line */
 	line->actual += 1;
 
-	recalculate_syntax(line);
+	if (!loading) {
+		recalculate_syntax(line);
+	}
 
 	return line;
 }
 
+static void get_size(void) {
+	struct winsize w;
+	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
+	width = w.ws_col - prompt_right_width;
+}
+
 static void place_cursor_actual(void) {
+	get_size();
+
 	int x = prompt_width + 1 - offset;
 	for (int i = 0; i < column; ++i) {
 		char_t * c = &the_line->text[i];
@@ -670,7 +770,9 @@ static void line_delete(line_t * line, int offset) {
 	/* The line is one character shorter */
 	line->actual -= 1;
 
-	recalculate_syntax(line);
+	if (!loading) {
+		recalculate_syntax(line);
+	}
 }
 
 static void delete_at_cursor(void) {
@@ -683,6 +785,22 @@ static void delete_at_cursor(void) {
 	}
 }
 
+static void delete_word(void) {
+	if (!the_line->actual) return;
+	if (!column) return;
+
+	do {
+		if (column > 0) {
+			line_delete(the_line, column);
+			column--;
+			if (offset > 0) offset--;
+		}
+	} while (column && the_line->text[column-1].codepoint != ' ');
+
+	render_line();
+	place_cursor_actual();
+}
+
 static void insert_char(uint32_t c) {
 	char_t _c;
 	_c.codepoint = c;
@@ -692,8 +810,10 @@ static void insert_char(uint32_t c) {
 	the_line = line_insert(the_line, _c, column);
 
 	column++;
-	render_line();
-	place_cursor_actual();
+	if (!loading) {
+		render_line();
+		place_cursor_actual();
+	}
 }
 
 static void cursor_left(void) {
@@ -707,11 +827,28 @@ static void cursor_right(void) {
 }
 
 static void word_left(void) {
-	/* TODO */
+	if (column == 0) return;
+	column--;
+	while (column && the_line->text[column].codepoint == ' ') {
+		column--;
+	}
+	while (column > 0) {
+		if (the_line->text[column-1].codepoint == ' ') break;
+		column--;
+	}
+	place_cursor_actual();
 }
 
 static void word_right(void) {
 	/* TODO */
+	while (column < the_line->actual && the_line->text[column].codepoint == ' ') {
+		column++;
+	}
+	while (column < the_line->actual) {
+		column++;
+		if (the_line->text[column].codepoint == ' ') break;
+	}
+	place_cursor_actual();
 }
 
 static void cursor_home(void) {
@@ -833,6 +970,12 @@ static void set_buffered(void) {
 	tcsetattr(STDOUT_FILENO, TCSAFLUSH, &old);
 }
 
+static int tabbed;
+
+static void dummy_redraw(rline_context_t * context) {
+	/* Do nothing */
+}
+
 static int read_line(void) {
 	int cin;
 	uint32_t c;
@@ -846,6 +989,7 @@ static int read_line(void) {
 	while ((cin = getc(stdin))) {
 		if (!decode(&istate, &c, cin)) {
 			if (timeout == 0) {
+				if (c != '\t') tabbed = 0;
 				switch (c) {
 					case '\033':
 						if (timeout == 0) {
@@ -853,6 +997,27 @@ static int read_line(void) {
 							timeout++;
 						}
 						break;
+					case 3:
+						the_line->actual = 0;
+						set_colors(COLOR_ALT_FG, COLOR_ALT_BG);
+						printf("^C");
+						printf("\033[0m");
+						return 1;
+					case 4:
+						if (column == 0 && the_line->actual == 0) {
+							for (char *_c = "exit"; *_c; ++_c) {
+								insert_char(*_c);
+							}
+							return 1;
+						} else {
+							if (column < the_line->actual) {
+								line_delete(the_line, column+1);
+								if (offset > 0) offset--;
+								render_line();
+								place_cursor_actual();
+							}
+						}
+						break;
 					case DELETE_KEY:
 					case BACKSPACE_KEY:
 						delete_at_cursor();
@@ -860,6 +1025,8 @@ static int read_line(void) {
 					case ENTER_KEY:
 						/* Print buffer */
 						return 1;
+					case 23:
+						delete_word();
 						break;
 					case 12: /* ^L - Repaint the whole screen */
 						printf("\033[2J\033[H");
@@ -868,6 +1035,55 @@ static int read_line(void) {
 						break;
 					case '\t':
 						/* Tab complet e*/
+						if (tab_complete_func) {
+							rline_context_t context = {0};
+							context.buffer = malloc(buf_size_max); /* TODO */
+							memset(context.buffer,0,buf_size_max);
+							unsigned int off = 0;
+							for (int j = 0; j < the_line->actual; j++) {
+								if (j == column) {
+									context.offset = off;
+								}
+								char_t c = the_line->text[j];
+								off += to_eight(c.codepoint, &context.buffer[off]);
+							}
+							if (column == the_line->actual) context.offset = off;
+							context.tabbed = tabbed;
+							rline_callbacks_t tmp = {0};
+							tmp.redraw_prompt = dummy_redraw;
+							context.callbacks = &tmp;
+							context.collected = off;
+							context.buffer[off] = '\0';
+							context.requested = 1024;
+							printf("\033[0m");
+							tab_complete_func(&context);
+							/* Now convert back */
+							loading = 1;
+							int final_column = 0;
+							the_line->actual = 0;
+							column = 0;
+							istate = 0;
+							for (int i = 0; i < context.collected; ++i) {
+								if (i == context.offset) {
+									final_column = column;
+								}
+								if (!decode(&istate, &c, context.buffer[i])) {
+									insert_char(c);
+								}
+							}
+							if (context.offset == context.collected) {
+								column = the_line->actual;
+							} else {
+								column = final_column;
+							}
+
+							tabbed = context.tabbed;
+
+							loading = 0;
+							recalculate_syntax(the_line);
+							render_line();
+							place_cursor_actual();
+						}
 						break;
 					default:
 						insert_char(c);
@@ -888,15 +1104,18 @@ static int read_line(void) {
 int rline_experimental(char * buffer, int buf_size) {
 	get_initial_termios();
 	set_unbuffered();
-
-	struct winsize w;
-	ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
-	width = w.ws_col - prompt_right_width;
+	get_size();
 
 	column = 0;
 	offset = 0;
+	buf_size_max = buf_size;
 
-	load_colorscheme_sunsmoke();
+	char * theme = getenv("RLINE_THEME");
+	if (theme && !strcmp(theme,"sunsmoke")) { /* TODO bring back theme tables */
+		rline_exp_load_colorscheme_sunsmoke();
+	} else {
+		rline_exp_load_colorscheme_default();
+	}
 
 	the_line = line_create();
 	read_line();

+ 1 - 1
util/auto-dep.py

@@ -25,7 +25,7 @@ class Classifier(object):
         '<toaru/graphics.h>':    (None, '-ltoaru_graphics',    []),
         '<toaru/drawstring.h>':  (None, '-ltoaru_drawstring',  ['<toaru/graphics.h>']),
         '<toaru/rline.h>':       (None, '-ltoaru_rline',       ['<toaru/kbd.h>']),
-        '<toaru/rline_exp.h>':   (None, '-ltoaru_rline_exp',   ['<toaru/kbd.h>']),
+        '<toaru/rline_exp.h>':   (None, '-ltoaru_rline_exp',   ['<toaru/rline.h>']),
         '<toaru/confreader.h>':  (None, '-ltoaru_confreader',  ['<toaru/hashmap.h>']),
         '<toaru/yutani.h>':      (None, '-ltoaru_yutani',      ['<toaru/kbd.h>', '<toaru/list.h>', '<toaru/pex.h>', '<toaru/graphics.h>', '<toaru/hashmap.h>']),
         '<toaru/decorations.h>': (None, '-ltoaru_decorations', ['<toaru/menu.h>', '<toaru/sdf.h>', '<toaru/graphics.h>', '<toaru/yutani.h>']),