1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-26 00:10:27 +00:00

Update shell.c to have smart behavior on windows.

This commit is contained in:
bakpakin 2022-07-09 11:23:02 -05:00
parent 69853c8e5c
commit 9da91a8217
2 changed files with 176 additions and 69 deletions

View File

@ -87,8 +87,30 @@ static void simpleline(JanetBuffer *buffer) {
} }
} }
/* Windows */ /* State */
#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE)
#ifndef JANET_SIMPLE_GETLINE
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
#endif
/* Fallback */
#if defined(JANET_SIMPLE_GETLINE)
void janet_line_init() { void janet_line_init() {
; ;
@ -105,6 +127,79 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
simpleline(buffer); simpleline(buffer);
} }
/* Rich implementation */
#else
/* Windows */
#ifdef _WIN32
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
static void setup_console_output(void) {
/* Enable color console on windows 10 console and utf8 output and other processing */
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
}
/* Ansi terminal raw mode */
static int rawmode(void) {
if (gbl_israwmode) return 0;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode &= ~ENABLE_LINE_INPUT;
dwMode &= ~ENABLE_INSERT_MODE;
dwMode &= ~ENABLE_ECHO_INPUT;
dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode(hOut, dwMode);
gbl_israwmode = 1;
return 0;
}
/* Disable raw mode */
static void norawmode(void) {
if (!gbl_israwmode) return;
HANDLE hOut = GetStdHandle(STD_INPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_LINE_INPUT;
dwMode |= ENABLE_INSERT_MODE;
dwMode |= ENABLE_ECHO_INPUT;
dwMode &= ~ENABLE_VIRTUAL_TERMINAL_INPUT;
dwMode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(hOut, dwMode);
gbl_israwmode = 0;
}
static long write_console(const char *bytes, size_t n) {
DWORD nwritten = 0;
BOOL result = WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), bytes, (DWORD) n, &nwritten, NULL);
if (!result) return -1; /* error */
return (long)nwritten;
}
static long read_console(char *into, size_t n) {
DWORD numread;
BOOL result = ReadConsole(GetStdHandle(STD_INPUT_HANDLE), into, (DWORD) n, &numread, NULL);
if (!result) return -1; /* error */
return (long)numread;
}
static int check_simpleline(JanetBuffer *buffer) {
if (rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
/* Posix */ /* Posix */
#else #else
@ -125,24 +220,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
/* static state */
#define JANET_LINE_MAX 1024
#define JANET_MATCH_MAX 256
#define JANET_HISTORY_MAX 100
static JANET_THREAD_LOCAL int gbl_israwmode = 0;
static JANET_THREAD_LOCAL const char *gbl_prompt = "> ";
static JANET_THREAD_LOCAL int gbl_plen = 2;
static JANET_THREAD_LOCAL char gbl_buf[JANET_LINE_MAX];
static JANET_THREAD_LOCAL int gbl_len = 0;
static JANET_THREAD_LOCAL int gbl_pos = 0;
static JANET_THREAD_LOCAL int gbl_cols = 80;
static JANET_THREAD_LOCAL char *gbl_history[JANET_HISTORY_MAX];
static JANET_THREAD_LOCAL int gbl_history_count = 0;
static JANET_THREAD_LOCAL int gbl_historyi = 0;
static JANET_THREAD_LOCAL struct termios gbl_termios_start; static JANET_THREAD_LOCAL struct termios gbl_termios_start;
static JANET_THREAD_LOCAL JanetByteView gbl_matches[JANET_MATCH_MAX];
static JANET_THREAD_LOCAL int gbl_match_count = 0;
static JANET_THREAD_LOCAL int gbl_lines_below = 0;
/* Unsupported terminal list from linenoise */ /* Unsupported terminal list from linenoise */
static const char *badterms[] = { static const char *badterms[] = {
@ -152,15 +230,6 @@ static const char *badterms[] = {
NULL NULL
}; };
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
/* Ansi terminal raw mode */ /* Ansi terminal raw mode */
static int rawmode(void) { static int rawmode(void) {
struct termios t; struct termios t;
@ -186,13 +255,53 @@ static void norawmode(void) {
gbl_israwmode = 0; gbl_israwmode = 0;
} }
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
static long write_console(char *bytes, size_t n) {
return write(STDOUT_FILENO, bytes, n);
}
static long read_console(char *into, size_t n) {
return read(STDIN_FILENO, into, n);
}
static int check_simpleline(JanetBuffer *buffer) {
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return 1;
}
if (rawmode()) {
simpleline(buffer);
return 1;
}
return 0;
}
#endif
static char *sdup(const char *s) {
size_t len = strlen(s) + 1;
char *mem = janet_malloc(len);
if (!mem) {
return NULL;
}
return memcpy(mem, s, len);
}
static int curpos(void) { static int curpos(void) {
char buf[32]; char buf[32];
int cols, rows; int cols, rows;
unsigned int i = 0; unsigned int i = 0;
if (write(STDOUT_FILENO, "\x1b[6n", 4) != 4) return -1; if (write_console("\x1b[6n", 4) != 4) return -1;
while (i < sizeof(buf) - 1) { while (i < sizeof(buf) - 1) {
if (read(STDIN_FILENO, buf + i, 1) != 1) break; if (read_console(buf + i, 1) != 1) break;
if (buf[i] == 'R') break; if (buf[i] == 'R') break;
i++; i++;
} }
@ -203,18 +312,23 @@ static int curpos(void) {
} }
static int getcols(void) { static int getcols(void) {
#ifdef _WIN32
CONSOLE_SCREEN_BUFFER_INFO csbi;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
return (int)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
#else
struct winsize ws; struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
int start, cols; int start, cols;
start = curpos(); start = curpos();
if (start == -1) goto failed; if (start == -1) goto failed;
if (write(STDOUT_FILENO, "\x1b[999C", 6) != 6) goto failed; if (write_console("\x1b[999C", 6) != 6) goto failed;
cols = curpos(); cols = curpos();
if (cols == -1) goto failed; if (cols == -1) goto failed;
if (cols > start) { if (cols > start) {
char seq[32]; char seq[32];
snprintf(seq, 32, "\x1b[%dD", cols - start); snprintf(seq, 32, "\x1b[%dD", cols - start);
if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) { if (write_console(seq, strlen(seq)) == -1) {
exit(1); exit(1);
} }
} }
@ -224,10 +338,11 @@ static int getcols(void) {
} }
failed: failed:
return 80; return 80;
#endif
} }
static void clear(void) { static void clear(void) {
if (write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7) <= 0) { if (write_console("\x1b[H\x1b[2J", 7) <= 0) {
exit(1); exit(1);
} }
} }
@ -259,7 +374,7 @@ static void refresh(void) {
/* Move cursor to original position. */ /* Move cursor to original position. */
snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen)); snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen));
janet_buffer_push_cstring(&b, seq); janet_buffer_push_cstring(&b, seq);
if (write(STDOUT_FILENO, b.data, b.count) == -1) { if (write_console(b.data, b.count) == -1) {
exit(1); exit(1);
} }
janet_buffer_deinit(&b); janet_buffer_deinit(&b);
@ -285,7 +400,7 @@ static int insert(char c, int draw) {
if (gbl_plen + gbl_len < gbl_cols) { if (gbl_plen + gbl_len < gbl_cols) {
/* Avoid a full update of the line in the /* Avoid a full update of the line in the
* trivial case. */ * trivial case. */
if (write(STDOUT_FILENO, &c, 1) == -1) return -1; if (write_console(&c, 1) == -1) return -1;
} else { } else {
refresh(); refresh();
} }
@ -312,7 +427,7 @@ static void historymove(int delta) {
gbl_historyi = gbl_history_count - 1; gbl_historyi = gbl_history_count - 1;
} }
strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1); strncpy(gbl_buf, gbl_history[gbl_historyi], JANET_LINE_MAX - 1);
gbl_pos = gbl_len = strlen(gbl_buf); gbl_pos = gbl_len = (int) strlen(gbl_buf);
gbl_buf[gbl_len] = '\0'; gbl_buf[gbl_len] = '\0';
refresh(); refresh();
@ -527,6 +642,7 @@ static void check_specials(JanetByteView src) {
check_cmatch(src, "unquote"); check_cmatch(src, "unquote");
check_cmatch(src, "var"); check_cmatch(src, "var");
check_cmatch(src, "while"); check_cmatch(src, "while");
check_cmatch(src, "upscope");
} }
static void resolve_format(JanetTable *entry) { static void resolve_format(JanetTable *entry) {
@ -740,14 +856,14 @@ static int line() {
addhistory(); addhistory();
if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1; if (write_console(gbl_prompt, gbl_plen) == -1) return -1;
for (;;) { for (;;) {
char c; char c;
char seq[3]; char seq[3];
int rc; int rc;
do { do {
rc = read(STDIN_FILENO, &c, 1); rc = read_console(&c, 1);
} while (rc < 0 && errno == EINTR); } while (rc < 0 && errno == EINTR);
if (rc <= 0) return -1; if (rc <= 0) return -1;
@ -764,8 +880,13 @@ static int line() {
kleft(); kleft();
break; break;
case 3: /* ctrl-c */ case 3: /* ctrl-c */
clearlines();
norawmode(); norawmode();
#ifdef _WIN32
ExitProcess(1);
#else
kill(getpid(), SIGINT); kill(getpid(), SIGINT);
#endif
/* fallthrough */ /* fallthrough */
case 17: /* ctrl-q */ case 17: /* ctrl-q */
gbl_cancel_current_repl_form = 1; gbl_cancel_current_repl_form = 1;
@ -826,23 +947,25 @@ static int line() {
case 23: /* ctrl-w */ case 23: /* ctrl-w */
kbackspacew(); kbackspacew();
break; break;
#ifndef _WIN32
case 26: /* ctrl-z */ case 26: /* ctrl-z */
norawmode(); norawmode();
kill(getpid(), SIGSTOP); kill(getpid(), SIGSTOP);
rawmode(); rawmode();
refresh(); refresh();
break; break;
#endif
case 27: /* escape sequence */ case 27: /* escape sequence */
/* Read the next two bytes representing the escape sequence. /* Read the next two bytes representing the escape sequence.
* Use two calls to handle slow terminals returning the two * Use two calls to handle slow terminals returning the two
* chars at different times. */ * chars at different times. */
if (read(STDIN_FILENO, seq, 1) == -1) break; if (read_console(seq, 1) == -1) break;
/* Esc[ = Control Sequence Introducer (CSI) */ /* Esc[ = Control Sequence Introducer (CSI) */
if (seq[0] == '[') { if (seq[0] == '[') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break; if (read_console(seq + 1, 1) == -1) break;
if (seq[1] >= '0' && seq[1] <= '9') { if (seq[1] >= '0' && seq[1] <= '9') {
/* Extended escape, read additional byte. */ /* Extended escape, read additional byte. */
if (read(STDIN_FILENO, seq + 2, 1) == -1) break; if (read_console(seq + 2, 1) == -1) break;
if (seq[2] == '~') { if (seq[2] == '~') {
switch (seq[1]) { switch (seq[1]) {
case '1': /* Home */ case '1': /* Home */
@ -861,7 +984,7 @@ static int line() {
} }
} }
} else if (seq[0] == 'O') { } else if (seq[0] == 'O') {
if (read(STDIN_FILENO, seq + 1, 1) == -1) break; if (read_console(seq + 1, 1) == -1) break;
switch (seq[1]) { switch (seq[1]) {
default: default:
break; break;
@ -944,28 +1067,12 @@ void janet_line_deinit() {
gbl_historyi = 0; gbl_historyi = 0;
} }
static int checktermsupport() {
const char *t = getenv("TERM");
int i;
if (!t) return 1;
for (i = 0; badterms[i]; i++)
if (!strcmp(t, badterms[i])) return 0;
return 1;
}
void janet_line_get(const char *p, JanetBuffer *buffer) { void janet_line_get(const char *p, JanetBuffer *buffer) {
gbl_prompt = p; gbl_prompt = p;
buffer->count = 0; buffer->count = 0;
gbl_historyi = 0; gbl_historyi = 0;
if (check_simpleline(buffer)) return;
FILE *out = janet_dynfile("err", stderr); FILE *out = janet_dynfile("err", stderr);
if (!isatty(STDIN_FILENO) || !checktermsupport()) {
simpleline(buffer);
return;
}
if (rawmode()) {
simpleline(buffer);
return;
}
if (line()) { if (line()) {
norawmode(); norawmode();
fputc('\n', out); fputc('\n', out);
@ -981,6 +1088,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) {
replacehistory(); replacehistory();
} }
static void clear_at_exit(void) {
if (!gbl_israwmode) {
clearlines();
norawmode();
}
}
#endif #endif
/* /*
@ -993,18 +1107,11 @@ int main(int argc, char **argv) {
JanetTable *env; JanetTable *env;
#ifdef _WIN32 #ifdef _WIN32
/* Enable color console on windows 10 console and utf8 output. */ setup_console_output();
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = 0;
GetConsoleMode(hOut, &dwMode);
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(hOut, dwMode);
SetConsoleOutputCP(65001);
#endif #endif
#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE) #if !defined(JANET_SIMPLE_GETLINE)
/* Try and not leave the terminal in a bad state */ atexit(clear_at_exit);
atexit(norawmode);
#endif #endif
#if defined(JANET_PRF) #if defined(JANET_PRF)

0
tools/format.sh Executable file → Normal file
View File