From e0b7533c39a02af6b54ad15425e67798ca1db5dc Mon Sep 17 00:00:00 2001 From: bakpakin Date: Thu, 12 May 2022 15:36:29 -0500 Subject: [PATCH 1/6] Add toggle macro. --- src/boot/boot.janet | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index d7113da5..546a7461 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -75,6 +75,11 @@ [name & more] ~(var ,name :private ,;more)) +(defmacro toggle + "Set a value to its boolean inverse. Same as `(set value (not value))`." + [value] + ~(set ,value (,not ,value))) + (defn defglobal "Dynamically create a global def." [name value] @@ -2950,7 +2955,7 @@ :italics ["*" "*"] :bold ["**" "**"]})) (def modes @{}) - (defn toggle [mode] + (defn toggle-mode [mode] (def active (get modes mode)) (def delims (get delimiters mode)) (put modes mode (not active)) @@ -3060,7 +3065,7 @@ (def token @"") (var token-length 0) (defn delim [mode] - (def d (toggle mode)) + (def d (toggle-mode mode)) (if-not has-color (+= token-length (length d))) (buffer/push token d)) (defn endtoken [] From 1f41b6c138ace107bd01c09d92dbf32133b1682c Mon Sep 17 00:00:00 2001 From: Autumn! Date: Thu, 7 Jul 2022 14:41:46 +0100 Subject: [PATCH 2/6] doc-format no longer detects other modes within code blocks --- src/boot/boot.janet | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/boot/boot.janet b/src/boot/boot.janet index ae6415c5..23ddb04e 100644 --- a/src/boot/boot.janet +++ b/src/boot/boot.janet @@ -3141,16 +3141,18 @@ (def b (get line i)) (cond (or (= b (chr "\n")) (= b (chr " "))) (endtoken) - (= b (chr `\`)) (do - (++ token-length) - (buffer/push token (get line (++ i)))) - (= b (chr "_")) (delim :underline) (= b (chr "`")) (delim :code) - (= b (chr "*")) - (if (= (chr "*") (get line (+ i 1))) - (do (++ i) - (delim :bold)) - (delim :italics)) + (not (modes :code)) (cond + (= b (chr `\`)) (do + (++ token-length) + (buffer/push token (get line (++ i)))) + (= b (chr "_")) (delim :underline) + (= b (chr "*")) + (if (= (chr "*") (get line (+ i 1))) + (do (++ i) + (delim :bold)) + (delim :italics)) + (do (++ token-length) (buffer/push token b))) (do (++ token-length) (buffer/push token b)))) (endtoken) (tuple/slice tokens)) From 9da91a82179539f4633694d05a959153e8b5c394 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 9 Jul 2022 11:23:02 -0500 Subject: [PATCH 3/6] Update shell.c to have smart behavior on windows. --- src/mainclient/shell.c | 245 +++++++++++++++++++++++++++++------------ tools/format.sh | 0 2 files changed, 176 insertions(+), 69 deletions(-) mode change 100755 => 100644 tools/format.sh diff --git a/src/mainclient/shell.c b/src/mainclient/shell.c index 192e05fa..d6140e78 100644 --- a/src/mainclient/shell.c +++ b/src/mainclient/shell.c @@ -87,8 +87,30 @@ static void simpleline(JanetBuffer *buffer) { } } -/* Windows */ -#if defined(JANET_WINDOWS) || defined(JANET_SIMPLE_GETLINE) +/* State */ + +#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() { ; @@ -105,6 +127,79 @@ void janet_line_get(const char *p, JanetBuffer *buffer) { simpleline(buffer); } +/* Rich implementation */ +#else + +/* Windows */ +#ifdef _WIN32 + +#include +#include +#include + +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 */ #else @@ -125,24 +220,7 @@ https://github.com/antirez/linenoise/blob/master/linenoise.c #include #include -/* 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 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 */ static const char *badterms[] = { @@ -152,15 +230,6 @@ static const char *badterms[] = { 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 */ static int rawmode(void) { struct termios t; @@ -186,13 +255,53 @@ static void norawmode(void) { 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) { char buf[32]; int cols, rows; 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) { - if (read(STDIN_FILENO, buf + i, 1) != 1) break; + if (read_console(buf + i, 1) != 1) break; if (buf[i] == 'R') break; i++; } @@ -203,18 +312,23 @@ static int curpos(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; if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { int start, cols; start = curpos(); 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(); if (cols == -1) goto failed; if (cols > start) { char seq[32]; snprintf(seq, 32, "\x1b[%dD", cols - start); - if (write(STDOUT_FILENO, seq, strlen(seq)) == -1) { + if (write_console(seq, strlen(seq)) == -1) { exit(1); } } @@ -224,10 +338,11 @@ static int getcols(void) { } failed: return 80; +#endif } 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); } } @@ -259,7 +374,7 @@ static void refresh(void) { /* Move cursor to original position. */ snprintf(seq, 64, "\r\x1b[%dC", (int)(_pos + gbl_plen)); 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); } janet_buffer_deinit(&b); @@ -285,7 +400,7 @@ static int insert(char c, int draw) { if (gbl_plen + gbl_len < gbl_cols) { /* Avoid a full update of the line in the * trivial case. */ - if (write(STDOUT_FILENO, &c, 1) == -1) return -1; + if (write_console(&c, 1) == -1) return -1; } else { refresh(); } @@ -312,7 +427,7 @@ static void historymove(int delta) { gbl_historyi = gbl_history_count - 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'; refresh(); @@ -527,6 +642,7 @@ static void check_specials(JanetByteView src) { check_cmatch(src, "unquote"); check_cmatch(src, "var"); check_cmatch(src, "while"); + check_cmatch(src, "upscope"); } static void resolve_format(JanetTable *entry) { @@ -740,14 +856,14 @@ static int line() { addhistory(); - if (write(STDOUT_FILENO, gbl_prompt, gbl_plen) == -1) return -1; + if (write_console(gbl_prompt, gbl_plen) == -1) return -1; for (;;) { char c; char seq[3]; int rc; do { - rc = read(STDIN_FILENO, &c, 1); + rc = read_console(&c, 1); } while (rc < 0 && errno == EINTR); if (rc <= 0) return -1; @@ -764,8 +880,13 @@ static int line() { kleft(); break; case 3: /* ctrl-c */ + clearlines(); norawmode(); +#ifdef _WIN32 + ExitProcess(1); +#else kill(getpid(), SIGINT); +#endif /* fallthrough */ case 17: /* ctrl-q */ gbl_cancel_current_repl_form = 1; @@ -826,23 +947,25 @@ static int line() { case 23: /* ctrl-w */ kbackspacew(); break; +#ifndef _WIN32 case 26: /* ctrl-z */ norawmode(); kill(getpid(), SIGSTOP); rawmode(); refresh(); break; +#endif case 27: /* escape sequence */ /* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */ - if (read(STDIN_FILENO, seq, 1) == -1) break; + if (read_console(seq, 1) == -1) break; /* Esc[ = Control Sequence Introducer (CSI) */ 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') { /* 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] == '~') { switch (seq[1]) { case '1': /* Home */ @@ -861,7 +984,7 @@ static int line() { } } } 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]) { default: break; @@ -944,28 +1067,12 @@ void janet_line_deinit() { 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) { gbl_prompt = p; buffer->count = 0; gbl_historyi = 0; + if (check_simpleline(buffer)) return; FILE *out = janet_dynfile("err", stderr); - if (!isatty(STDIN_FILENO) || !checktermsupport()) { - simpleline(buffer); - return; - } - if (rawmode()) { - simpleline(buffer); - return; - } if (line()) { norawmode(); fputc('\n', out); @@ -981,6 +1088,13 @@ void janet_line_get(const char *p, JanetBuffer *buffer) { replacehistory(); } +static void clear_at_exit(void) { + if (!gbl_israwmode) { + clearlines(); + norawmode(); + } +} + #endif /* @@ -993,18 +1107,11 @@ int main(int argc, char **argv) { JanetTable *env; #ifdef _WIN32 - /* Enable color console on windows 10 console and utf8 output. */ - HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); - DWORD dwMode = 0; - GetConsoleMode(hOut, &dwMode); - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - SetConsoleMode(hOut, dwMode); - SetConsoleOutputCP(65001); + setup_console_output(); #endif -#if !defined(JANET_WINDOWS) && !defined(JANET_SIMPLE_GETLINE) - /* Try and not leave the terminal in a bad state */ - atexit(norawmode); +#if !defined(JANET_SIMPLE_GETLINE) + atexit(clear_at_exit); #endif #if defined(JANET_PRF) diff --git a/tools/format.sh b/tools/format.sh old mode 100755 new mode 100644 From 6a92e8b6096863abe0e7c676236e0e13b4a28ed3 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 9 Jul 2022 11:39:06 -0500 Subject: [PATCH 4/6] Update CHANGELOG and make tweaks to win32 shell --- CHANGELOG.md | 2 ++ src/mainclient/shell.c | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f3e3caa8..a7667604 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ All notable changes to this project will be documented in this file. ## 1.23.1 - ??? +- Add better support for windows console in the default shell.c for autocompletion and + other shell-like input features. - Improve default error message from `assert`. - Add the `tabseq` macro for simpler table comprehensions. - Allow setting `(dyn :task-id)` in fibers to improve context in supervisor messages. Prior to diff --git a/src/mainclient/shell.c b/src/mainclient/shell.c index d6140e78..28859318 100644 --- a/src/mainclient/shell.c +++ b/src/mainclient/shell.c @@ -136,6 +136,7 @@ void janet_line_get(const char *p, JanetBuffer *buffer) { #include #include #include +#include static void setup_console_output(void) { /* Enable color console on windows 10 console and utf8 output and other processing */ @@ -158,7 +159,7 @@ static int rawmode(void) { dwMode &= ~ENABLE_ECHO_INPUT; dwMode |= ENABLE_VIRTUAL_TERMINAL_INPUT; dwMode &= ~ENABLE_PROCESSED_INPUT; - SetConsoleMode(hOut, dwMode); + if (!SetConsoleMode(hOut, dwMode)) return 1; gbl_israwmode = 1; return 0; } @@ -193,7 +194,7 @@ static long read_console(char *into, size_t n) { } static int check_simpleline(JanetBuffer *buffer) { - if (rawmode()) { + if (!_isatty(_fileno(stdin)) || rawmode()) { simpleline(buffer); return 1; } From c7801ce27726f6e383eae7c9a3b0cbb55052a3b4 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 9 Jul 2022 11:43:51 -0500 Subject: [PATCH 5/6] Address #997 - clang undefined behavior warning. --- src/core/value.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/value.c b/src/core/value.c index b54df0ce..b4a17dab 100644 --- a/src/core/value.c +++ b/src/core/value.c @@ -348,7 +348,7 @@ int32_t janet_hash(Janet x) { hash = (int32_t)((hilo << 16) | (hilo >> 16)); } else { /* Assuming 4 byte pointer (or smaller) */ - ptrdiff_t diff = ((char *)janet_unwrap_pointer(x) - (char *)0); + uintptr_t diff = janet_unwrap_pointer(x); uint32_t hilo = (uint32_t) diff * 2654435769u; hash = (int32_t)((hilo << 16) | (hilo >> 16)); } From 7248c1dfdbc82bb1ffe302520a585fb8cb6ecb62 Mon Sep 17 00:00:00 2001 From: bakpakin Date: Sat, 9 Jul 2022 19:10:25 -0500 Subject: [PATCH 6/6] Give up if ln fail. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fb3ecda1..9ee523f9 100644 --- a/Makefile +++ b/Makefile @@ -283,7 +283,7 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc cp $(JANET_TARGET) '$(DESTDIR)$(BINDIR)/janet' mkdir -p '$(DESTDIR)$(INCLUDEDIR)/janet' cp -r build/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet' - ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' + ln -sf -T ./janet/janet.h '$(DESTDIR)$(INCLUDEDIR)/janet.h' || true #fixme bsd mkdir -p '$(DESTDIR)$(JANET_PATH)' mkdir -p '$(DESTDIR)$(LIBDIR)' if test $(UNAME) = Darwin ; then \