diff
minitox.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 94 insertions(+), 5 deletions(-)
diff --git a/minitox.c b/minitox.c
index 0db5d06..963a5c3 100644
--- a/minitox.c
+++ b/minitox.c
@@ -16,6 +16,7 @@
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
+#include <sys/ioctl.h>
#include <tox/tox.h>
@@ -383,6 +384,7 @@ struct AsyncREPL {
size_t sz;
int nbuf;
int nstack;
+ int rendered_rows;
};
struct termios saved_tattr;
@@ -401,6 +403,7 @@ void setup_arepl(void) {
async_repl = malloc(sizeof(struct AsyncREPL));
async_repl->nbuf = 0;
async_repl->nstack = 0;
+ async_repl->rendered_rows = 0;
async_repl->sz = LINE_MAX_SIZE;
async_repl->line = malloc(LINE_MAX_SIZE);
async_repl->prompt = malloc(LINE_MAX_SIZE);
@@ -448,13 +451,95 @@ void setup_arepl(void) {
atexit(arepl_exit);
}
+static int terminal_width(void) {
+ struct winsize ws;
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) {
+ return ws.ws_col;
+ }
+ return 80;
+}
+
+static int prompt_visible_len(const char *s) {
+ int n = 0;
+ while (*s) {
+ if ((unsigned char)s[0] == 0x1b && s[1] == '[') {
+ s += 2;
+ while (*s && ((*s >= '0' && *s <= '9') || *s == ';' || *s == '?')) s++;
+ if (*s) s++;
+ continue;
+ }
+ n++;
+ s++;
+ }
+ return n;
+}
+
+static char arepl_line_at(const struct AsyncREPL *arepl, int idx) {
+ if (idx < arepl->nbuf) {
+ return arepl->line[idx];
+ }
+ int tail = idx - arepl->nbuf;
+ return arepl->line[arepl->sz - arepl->nstack + tail];
+}
+
+static void arepl_cursor_pos(int cols, int prompt_cols, int idx, int *row, int *col) {
+ int r = 1;
+ int c = prompt_cols;
+ if (cols <= prompt_cols) cols = prompt_cols + 1;
+
+ for (int i = 0; i < idx; i++) {
+ if (c >= cols) {
+ r++;
+ c = prompt_cols;
+ }
+ c++;
+ }
+ *row = r;
+ *col = c;
+}
+
void arepl_reprint(struct AsyncREPL *arepl) {
- fputs(CODE_ERASE_LINE, stdout);
+ int rows_to_clear = arepl->rendered_rows > 0 ? arepl->rendered_rows : 1;
+ int cols = terminal_width();
+ int prompt_cols = prompt_visible_len(arepl->prompt ? arepl->prompt : "");
+ int total = arepl->nbuf + arepl->nstack;
+
+ fputs("\r", stdout);
+ if (rows_to_clear > 1) {
+ printf("\033[%dA", rows_to_clear - 1);
+ }
+ for (int i = 0; i < rows_to_clear; i++) {
+ fputs("\r\033[2K", stdout);
+ if (i + 1 < rows_to_clear) fputs("\033[1B", stdout);
+ }
+ if (rows_to_clear > 1) {
+ printf("\033[%dA\r", rows_to_clear - 1);
+ }
+
if (arepl->prompt) fputs(arepl->prompt, stdout);
- if (arepl->nbuf > 0) printf("%.*s", arepl->nbuf, arepl->line);
+
+ int rendered_rows = 1;
+ int col = prompt_cols;
+ if (cols <= prompt_cols) cols = prompt_cols + 1;
+ for (int i = 0; i < total; i++) {
+ if (col >= cols) {
+ fputs("\n", stdout);
+ for (int j = 0; j < prompt_cols; j++) fputc(' ', stdout);
+ rendered_rows++;
+ col = prompt_cols;
+ }
+ fputc(arepl_line_at(arepl, i), stdout);
+ col++;
+ }
+ arepl->rendered_rows = rendered_rows;
+
if (arepl->nstack > 0) {
- printf("%.*s",(int)arepl->nstack, arepl->line + arepl->sz - arepl->nstack);
- printf("\033[%dD",arepl->nstack); // move cursor
+ int end_row, end_col, cur_row, cur_col;
+ arepl_cursor_pos(cols, prompt_cols, total, &end_row, &end_col);
+ arepl_cursor_pos(cols, prompt_cols, arepl->nbuf, &cur_row, &cur_col);
+ if (end_row > cur_row) printf("\033[%dA", end_row - cur_row);
+ printf("\r");
+ if (cur_col > 0) printf("\033[%dC", cur_col);
}
fflush(stdout);
}
@@ -1288,11 +1373,13 @@ char *poptok(char **strp) {
void repl_iterate(void){
static char buf[128];
static char line[LINE_MAX_SIZE];
+ bool has_input = false;
while (1) {
int n = read(NEW_STDIN_FILENO, buf, sizeof(buf));
if (n <= 0) {
break;
}
+ has_input = true;
for (int i=0;i<n;i++) { // for_1
char c = buf[i];
if (c == '\004') /* C-d */
@@ -1354,7 +1441,9 @@ void repl_iterate(void){
WARN("! Invalid command, use `/help` to get list of available commands.");
} // end for_1
} // end while
- arepl_reprint(async_repl);
+ if (has_input) {
+ arepl_reprint(async_repl);
+ }
}