diff options
author | Thiago de Arruda <tpadilha84@gmail.com> | 2014-12-09 08:55:31 -0300 |
---|---|---|
committer | Thiago de Arruda <tpadilha84@gmail.com> | 2014-12-09 08:55:31 -0300 |
commit | c5b9e5d1d317b74d4adf7637cd9081be4ee52722 (patch) | |
tree | 46f2b1692851ae6afe3ffc5d9c2ebc700fe6b452 /src | |
parent | 8bb7aa329d20cb265d8952c96c84a0e54a5726ab (diff) | |
parent | 1192fbd08a054cece0b48dfb695e77e689997980 (diff) | |
download | rneovim-c5b9e5d1d317b74d4adf7637cd9081be4ee52722.tar.gz rneovim-c5b9e5d1d317b74d4adf7637cd9081be4ee52722.tar.bz2 rneovim-c5b9e5d1d317b74d4adf7637cd9081be4ee52722.zip |
Merge PR #1605 'Abstract UI termcap'
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/vim.c | 6 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 4 | ||||
-rw-r--r-- | src/nvim/getchar.c | 7 | ||||
-rw-r--r-- | src/nvim/globals.h | 4 | ||||
-rw-r--r-- | src/nvim/main.c | 36 | ||||
-rw-r--r-- | src/nvim/mouse.c | 11 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/channel.c | 16 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/defs.h | 4 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/remote_ui.c | 280 | ||||
-rw-r--r-- | src/nvim/msgpack_rpc/remote_ui.h | 9 | ||||
-rw-r--r-- | src/nvim/os/input.c | 30 | ||||
-rw-r--r-- | src/nvim/os/signal.c | 2 | ||||
-rw-r--r-- | src/nvim/screen.c | 43 | ||||
-rw-r--r-- | src/nvim/syntax.c | 290 | ||||
-rw-r--r-- | src/nvim/syntax.h | 2 | ||||
-rw-r--r-- | src/nvim/syntax_defs.h | 3 | ||||
-rw-r--r-- | src/nvim/term.c | 68 | ||||
-rw-r--r-- | src/nvim/ui.c | 445 | ||||
-rw-r--r-- | src/nvim/ui.h | 32 |
19 files changed, 1176 insertions, 116 deletions
diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index fe5fa6274b..eab79d970e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -21,6 +21,7 @@ #include "nvim/message.h" #include "nvim/eval.h" #include "nvim/misc2.h" +#include "nvim/syntax.h" #include "nvim/term.h" #include "nvim/getchar.h" #include "nvim/os/input.h" @@ -546,6 +547,11 @@ void vim_unsubscribe(uint64_t channel_id, String event) channel_unsubscribe(channel_id, e); } +Integer vim_name_to_color(String name) +{ + return name_to_color((uint8_t *)name.data); +} + Array vim_get_api_info(uint64_t channel_id) { Array rv = ARRAY_DICT_INIT; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index e56592923d..d3051c5202 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1984,10 +1984,6 @@ void free_cmdline_buf(void) */ static void draw_cmdline(int start, int len) { - if (embedded_mode) { - return; - } - int i; if (cmdline_star > 0) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index d0bdcde9e8..5dec7e38fd 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2513,6 +2513,13 @@ fix_input_buffer ( int script /* TRUE when reading from a script */ ) { + if (abstract_ui) { + // Should not escape K_SPECIAL/CSI while in embedded mode because vim key + // codes keys are processed in input.c/input_enqueue. + buf[len] = NUL; + return len; + } + int i; char_u *p = buf; diff --git a/src/nvim/globals.h b/src/nvim/globals.h index ea91135194..233d326a40 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -465,6 +465,8 @@ EXTERN int highlight_stlnc[9]; /* On top of user */ EXTERN int cterm_normal_fg_color INIT(= 0); EXTERN int cterm_normal_fg_bold INIT(= 0); EXTERN int cterm_normal_bg_color INIT(= 0); +EXTERN RgbValue normal_fg INIT(= -1); +EXTERN RgbValue normal_bg INIT(= -1); EXTERN int autocmd_busy INIT(= FALSE); /* Is apply_autocmds() busy? */ EXTERN int autocmd_no_enter INIT(= FALSE); /* *Enter autocmds disabled */ @@ -1251,6 +1253,8 @@ EXTERN int curr_tmode INIT(= TMODE_COOK); /* contains current terminal mode */ // If a msgpack-rpc channel should be started over stdin/stdout EXTERN bool embedded_mode INIT(= false); +// Using the "abstract_ui" termcap +EXTERN bool abstract_ui INIT(= false); /// Used to track the status of external functions. /// Currently only used for iconv(). diff --git a/src/nvim/main.c b/src/nvim/main.c index 8e19cf3686..c806431872 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -265,13 +265,6 @@ int main(int argc, char **argv) term_init(); TIME_MSG("shell init"); - event_init(); - - if (!embedded_mode) { - // Print a warning if stdout is not a terminal. - check_tty(¶ms); - } - /* This message comes before term inits, but after setting "silent_mode" * when the input is not a tty. */ if (GARGCOUNT > 1 && !silent_mode) @@ -283,6 +276,7 @@ int main(int argc, char **argv) // initial screen size of 80x20 full_screen = true; screen_resize(80, 20, false); + termcapinit((uint8_t *)"abstract_ui"); } else { // set terminal name and get terminal capabilities (will set full_screen) // Do some initialization of the screen @@ -292,6 +286,16 @@ int main(int argc, char **argv) TIME_MSG("Termcap init"); } + event_init(); + + if (abstract_ui) { + t_colors = 256; + } else { + // Print a warning if stdout is not a terminal TODO(tarruda): Remove this + // check once the new terminal UI is implemented + check_tty(¶ms); + } + /* * Set the default values for the options that use Rows and Columns. */ @@ -424,19 +428,17 @@ int main(int argc, char **argv) TIME_MSG("waiting for return"); } - if (!embedded_mode) { - starttermcap(); // start termcap if not done by wait_return() - TIME_MSG("start termcap"); - may_req_ambiguous_char_width(); - setmouse(); // may start using the mouse + starttermcap(); // start termcap if not done by wait_return() + TIME_MSG("start termcap"); + may_req_ambiguous_char_width(); + setmouse(); // may start using the mouse - if (scroll_region) { - scroll_region_reset(); // In case Rows changed - } - - scroll_start(); // may scroll the screen to the right position + if (scroll_region) { + scroll_region_reset(); // In case Rows changed } + scroll_start(); // may scroll the screen to the right position + /* * Don't clear the screen when starting in Ex mode, unless using the GUI. */ diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index 439cdbd5c8..9f67bd1760 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -452,7 +452,7 @@ void setmouse(void) return; /* don't switch mouse on when not in raw mode (Ex mode) */ - if (cur_tmode != TMODE_RAW) { + if (!abstract_ui && cur_tmode != TMODE_RAW) { mch_setmouse(false); return; } @@ -470,10 +470,11 @@ void setmouse(void) else checkfor = MOUSE_NORMAL; /* assume normal mode */ - if (mouse_has(checkfor)) - mch_setmouse(true); - else - mch_setmouse(false); + if (mouse_has(checkfor)) { + ui_mouse_on(); + } else { + ui_mouse_off(); + } } /* diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index b6ac3fab82..4c35cce09a 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -10,6 +10,7 @@ #include "nvim/api/private/helpers.h" #include "nvim/api/vim.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/remote_ui.h" #include "nvim/os/event.h" #include "nvim/os/rstream.h" #include "nvim/os/rstream_defs.h" @@ -100,6 +101,17 @@ void channel_init(void) if (embedded_mode) { channel_from_stdio(); } + + if (abstract_ui) { + // Add handler for "attach_ui" + remote_ui_init(); + String method = cstr_as_string("attach_ui"); + MsgpackRpcRequestHandler handler = {.fn = remote_ui_attach, .defer = true}; + msgpack_rpc_add_method_handler(method, handler); + method = cstr_as_string("detach_ui"); + handler.fn = remote_ui_detach; + msgpack_rpc_add_method_handler(method, handler); + } } /// Teardown the module @@ -645,6 +657,10 @@ static void on_stdio_close(Event e) static void free_channel(Channel *channel) { + if (abstract_ui) { + remote_ui_disconnect(channel->id); + } + pmap_del(uint64_t)(channels, channel->id); msgpack_unpacker_free(channel->unpacker); diff --git a/src/nvim/msgpack_rpc/defs.h b/src/nvim/msgpack_rpc/defs.h index 13067fb7b4..0492a65290 100644 --- a/src/nvim/msgpack_rpc/defs.h +++ b/src/nvim/msgpack_rpc/defs.h @@ -19,6 +19,10 @@ typedef struct { /// Initializes the msgpack-rpc method table void msgpack_rpc_init_method_table(void); +// Add a handler to the method table +void msgpack_rpc_add_method_handler(String method, + MsgpackRpcRequestHandler handler); + void msgpack_rpc_init_function_metadata(Dictionary *metadata); /// Dispatches to the actual API function after basic payload validation by diff --git a/src/nvim/msgpack_rpc/remote_ui.c b/src/nvim/msgpack_rpc/remote_ui.c new file mode 100644 index 0000000000..f980a77b4c --- /dev/null +++ b/src/nvim/msgpack_rpc/remote_ui.c @@ -0,0 +1,280 @@ +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdbool.h> + +#include "nvim/vim.h" +#include "nvim/ui.h" +#include "nvim/memory.h" +#include "nvim/map.h" +#include "nvim/msgpack_rpc/remote_ui.h" +#include "nvim/msgpack_rpc/channel.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/remote_ui.c.generated.h" +#endif + +typedef struct { + uint64_t channel_id; + Array buffer; +} UIData; + +static PMap(uint64_t) *connected_uis = NULL; + +void remote_ui_init(void) +{ + connected_uis = pmap_new(uint64_t)(); +} + +Object remote_ui_attach(uint64_t channel_id, uint64_t request_id, Array args, + Error *error) +{ + if (pmap_has(uint64_t)(connected_uis, channel_id)) { + api_set_error(error, Exception, _("UI already attached for channel")); + return NIL; + } + + if (args.size != 2 || args.items[0].type != kObjectTypeInteger + || args.items[1].type != kObjectTypeInteger + || args.items[0].data.integer <= 0 || args.items[1].data.integer <= 0) { + api_set_error(error, Validation, + _("Arguments must be a pair of positive integers " + "representing the remote screen width/height")); + return NIL; + } + UIData *data = xmalloc(sizeof(UIData)); + data->channel_id = channel_id; + data->buffer = (Array)ARRAY_DICT_INIT; + UI *ui = xcalloc(1, sizeof(UI)); + ui->width = (int)args.items[0].data.integer; + ui->height = (int)args.items[1].data.integer; + ui->data = data; + ui->resize = remote_ui_resize; + ui->clear = remote_ui_clear; + ui->eol_clear = remote_ui_eol_clear; + ui->cursor_goto = remote_ui_cursor_goto; + ui->cursor_on = remote_ui_cursor_on; + ui->cursor_off = remote_ui_cursor_off; + ui->mouse_on = remote_ui_mouse_on; + ui->mouse_off = remote_ui_mouse_off; + ui->insert_mode = remote_ui_insert_mode; + ui->normal_mode = remote_ui_normal_mode; + ui->set_scroll_region = remote_ui_set_scroll_region; + ui->scroll = remote_ui_scroll; + ui->highlight_set = remote_ui_highlight_set; + ui->put = remote_ui_put; + ui->bell = remote_ui_bell; + ui->visual_bell = remote_ui_visual_bell; + ui->flush = remote_ui_flush; + ui->suspend = remote_ui_suspend; + pmap_put(uint64_t)(connected_uis, channel_id, ui); + ui_attach(ui); + + return NIL; +} + +Object remote_ui_detach(uint64_t channel_id, uint64_t request_id, Array args, + Error *error) +{ + if (!pmap_has(uint64_t)(connected_uis, channel_id)) { + api_set_error(error, Exception, _("UI is not attached for channel")); + } + remote_ui_disconnect(channel_id); + + return NIL; +} + +void remote_ui_disconnect(uint64_t channel_id) +{ + UI *ui = pmap_get(uint64_t)(connected_uis, channel_id); + if (!ui) { + return; + } + UIData *data = ui->data; + // destroy pending screen updates + api_free_array(data->buffer); + pmap_del(uint64_t)(connected_uis, channel_id); + free(ui->data); + ui_detach(ui); + free(ui); +} + +static void push_call(UI *ui, char *name, Array args) +{ + Array call = ARRAY_DICT_INIT; + UIData *data = ui->data; + + // To optimize data transfer(especially for "put"), we bundle adjacent + // calls to same method together, so only add a new call entry if the last + // method call is different from "name" + if (kv_size(data->buffer)) { + call = kv_A(data->buffer, kv_size(data->buffer) - 1).data.array; + } + + if (!kv_size(call) || strcmp(kv_A(call, 0).data.string.data, name)) { + call = (Array)ARRAY_DICT_INIT; + ADD(data->buffer, ARRAY_OBJ(call)); + ADD(call, STRING_OBJ(cstr_to_string(name))); + } + + ADD(call, ARRAY_OBJ(args)); + kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; +} + +static void remote_ui_resize(UI *ui, int width, int height) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(width)); + ADD(args, INTEGER_OBJ(height)); + push_call(ui, "resize", args); +} + +static void remote_ui_clear(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "clear", args); +} + +static void remote_ui_eol_clear(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "eol_clear", args); +} + +static void remote_ui_cursor_goto(UI *ui, int row, int col) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(row)); + ADD(args, INTEGER_OBJ(col)); + push_call(ui, "cursor_goto", args); +} + +static void remote_ui_cursor_on(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "cursor_on", args); +} + +static void remote_ui_cursor_off(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "cursor_off", args); +} + +static void remote_ui_mouse_on(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "mouse_on", args); +} + +static void remote_ui_mouse_off(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "mouse_off", args); +} + +static void remote_ui_insert_mode(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "insert_mode", args); +} + +static void remote_ui_normal_mode(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "normal_mode", args); +} + +static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left, + int right) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(top)); + ADD(args, INTEGER_OBJ(bot)); + ADD(args, INTEGER_OBJ(left)); + ADD(args, INTEGER_OBJ(right)); + push_call(ui, "set_scroll_region", args); +} + +static void remote_ui_scroll(UI *ui, int count) +{ + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(count)); + push_call(ui, "scroll", args); +} + +static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) +{ + Array args = ARRAY_DICT_INIT; + Dictionary hl = ARRAY_DICT_INIT; + + if (attrs.bold) { + PUT(hl, "bold", BOOLEAN_OBJ(true)); + } + + if (attrs.standout) { + PUT(hl, "standout", BOOLEAN_OBJ(true)); + } + + if (attrs.underline) { + PUT(hl, "underline", BOOLEAN_OBJ(true)); + } + + if (attrs.undercurl) { + PUT(hl, "undercurl", BOOLEAN_OBJ(true)); + } + + if (attrs.italic) { + PUT(hl, "italic", BOOLEAN_OBJ(true)); + } + + if (attrs.reverse) { + PUT(hl, "reverse", BOOLEAN_OBJ(true)); + } + + if (attrs.foreground != -1) { + PUT(hl, "foreground", INTEGER_OBJ(attrs.foreground)); + } + + if (attrs.background != -1) { + PUT(hl, "background", INTEGER_OBJ(attrs.background)); + } + + ADD(args, DICTIONARY_OBJ(hl)); + push_call(ui, "highlight_set", args); +} + +static void remote_ui_put(UI *ui, uint8_t *data, size_t size) +{ + Array args = ARRAY_DICT_INIT; + String str = {.data = xmemdupz(data, size), .size = size}; + ADD(args, STRING_OBJ(str)); + push_call(ui, "put", args); +} + +static void remote_ui_bell(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "bell", args); +} + +static void remote_ui_visual_bell(UI *ui) +{ + Array args = ARRAY_DICT_INIT; + push_call(ui, "visual_bell", args); +} + +static void remote_ui_flush(UI *ui) +{ + UIData *data = ui->data; + channel_send_event(data->channel_id, "redraw", data->buffer); + data->buffer = (Array)ARRAY_DICT_INIT; +} + +static void remote_ui_suspend(UI *ui) +{ + UIData *data = ui->data; + remote_ui_disconnect(data->channel_id); +} diff --git a/src/nvim/msgpack_rpc/remote_ui.h b/src/nvim/msgpack_rpc/remote_ui.h new file mode 100644 index 0000000000..8af86dc1b8 --- /dev/null +++ b/src/nvim/msgpack_rpc/remote_ui.h @@ -0,0 +1,9 @@ +#ifndef NVIM_MSGPACK_RPC_REMOTE_UI_H +#define NVIM_MSGPACK_RPC_REMOTE_UI_H + +#include "nvim/ui.h" + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "msgpack_rpc/remote_ui.h.generated.h" +#endif +#endif // NVIM_MSGPACK_RPC_REMOTE_UI_H diff --git a/src/nvim/os/input.c b/src/nvim/os/input.c index 686fe1f06d..246ebf123c 100644 --- a/src/nvim/os/input.c +++ b/src/nvim/os/input.c @@ -46,7 +46,7 @@ void input_init(void) { input_buffer = rbuffer_new(INPUT_BUFFER_SIZE + MAX_KEY_CODE_LEN); - if (embedded_mode) { + if (abstract_ui) { return; } @@ -57,7 +57,7 @@ void input_init(void) void input_teardown(void) { - if (embedded_mode) { + if (abstract_ui) { return; } @@ -67,7 +67,7 @@ void input_teardown(void) // Listen for input void input_start(void) { - if (embedded_mode) { + if (abstract_ui) { return; } @@ -77,7 +77,7 @@ void input_start(void) // Stop listening for input void input_stop(void) { - if (embedded_mode) { + if (abstract_ui) { return; } @@ -180,7 +180,23 @@ void input_buffer_restore(String str) size_t input_enqueue(String keys) { - size_t rv = rbuffer_write(input_buffer, keys.data, keys.size); + char *ptr = keys.data, *end = ptr + keys.size; + + while (rbuffer_available(input_buffer) >= 6 && ptr < end) { + int new_size = trans_special((char_u **)&ptr, + (char_u *)rbuffer_write_ptr(input_buffer), + false); + if (!new_size) { + // copy the character unmodified + *rbuffer_write_ptr(input_buffer) = *ptr++; + new_size = 1; + } + // TODO(tarruda): Don't produce past unclosed '<' characters, except if + // there's a lot of characters after the '<' + rbuffer_produced(input_buffer, (size_t)new_size); + } + + size_t rv = (size_t)(ptr - keys.data); process_interrupts(); return rv; } @@ -255,7 +271,7 @@ static void read_cb(RStream *rstream, void *data, bool at_eof) static void convert_input(void) { - if (embedded_mode || !rbuffer_available(input_buffer)) { + if (abstract_ui || !rbuffer_available(input_buffer)) { // No input buffer space return; } @@ -335,7 +351,7 @@ static bool input_ready(void) return typebuf_was_filled || // API call filled typeahead rbuffer_pending(input_buffer) > 0 || // Stdin input event_has_deferred() || // Events must be processed - (!embedded_mode && eof); // Stdin closed + (!abstract_ui && eof); // Stdin closed } // Exit because of an input read error. diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index cf8ba85ed5..ca3ba052d7 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -45,7 +45,7 @@ void signal_init(void) uv_signal_start(&shup, signal_cb, SIGHUP); uv_signal_start(&squit, signal_cb, SIGQUIT); uv_signal_start(&sterm, signal_cb, SIGTERM); - if (!embedded_mode) { + if (!abstract_ui) { // TODO(tarruda): There must be an API function for resizing window uv_signal_start(&swinch, signal_cb, SIGWINCH); } diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 0225eb72c1..c0a909f147 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5824,9 +5824,12 @@ static void screen_start_highlight(int attr) attrentry_T *aep = NULL; screen_attr = attr; - if (full_screen - ) { - { + if (full_screen) { + if (abstract_ui) { + char buf[20]; + sprintf(buf, "\033|%dh", attr); + OUT_STR(buf); + } else { if (attr > HL_ALL) { /* special HL attr. */ if (t_colors > 1) aep = syn_cterm_attr2entry(attr); @@ -5877,9 +5880,13 @@ void screen_stop_highlight(void) { int do_ME = FALSE; /* output T_ME code */ - if (screen_attr != 0 - ) { - { + if (screen_attr != 0) { + if (abstract_ui) { + // Handled in ui.c + char buf[20]; + sprintf(buf, "\033|%dH", screen_attr); + OUT_STR(buf); + } else { if (screen_attr > HL_ALL) { /* special HL attr. */ attrentry_T *aep; @@ -6558,11 +6565,14 @@ static void screenclear2(void) { int i; - if (starting == NO_SCREEN || ScreenLines == NULL - ) + if (starting == NO_SCREEN || ScreenLines == NULL) { return; + } + + if (!abstract_ui) { + screen_attr = -1; /* force setting the Normal colors */ + } - screen_attr = -1; /* force setting the Normal colors */ screen_stop_highlight(); /* don't want highlighting here */ @@ -8156,14 +8166,19 @@ void screen_resize(int width, int height, int mustset) ++busy; - - if (mustset || (ui_get_shellsize() == FAIL && height != 0)) { + // TODO(tarruda): "mustset" is still used in the old tests, which don't use + // "abstract_ui" yet. This will change when a new TUI is merged. + if (abstract_ui || mustset || (ui_get_shellsize() == FAIL && height != 0)) { Rows = height; Columns = width; - check_shellsize(); + } + check_shellsize(); + + if (abstract_ui) { + ui_resize(width, height); + } else { mch_set_shellsize(); - } else - check_shellsize(); + } /* The window layout used to be adjusted here, but it now happens in * screenalloc() (also invoked from screenclear()). That is because the diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 69d6479cf3..45b42730bc 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -11,6 +11,7 @@ */ #include <assert.h> +#include <ctype.h> #include <errno.h> #include <inttypes.h> #include <stdbool.h> @@ -68,9 +69,10 @@ struct hl_group { int sg_cterm_attr; /* Screen attr for color term mode */ /* Store the sp color name for the GUI or synIDattr() */ int sg_gui; /* "gui=" highlighting attributes */ - char_u *sg_gui_fg_name; /* GUI foreground color name */ - char_u *sg_gui_bg_name; /* GUI background color name */ - char_u *sg_gui_sp_name; /* GUI special color name */ + RgbValue sg_rgb_fg; // RGB foreground color + RgbValue sg_rgb_bg; // RGB background color + uint8_t *sg_rgb_fg_name; // RGB foreground color name + uint8_t *sg_rgb_bg_name; // RGB background color name int sg_link; /* link to this highlight group ID */ int sg_set; /* combination of SG_* flags */ scid_T sg_scriptID; /* script in which the group was last set */ @@ -6518,34 +6520,39 @@ do_highlight ( if (!init) HL_TABLE()[idx].sg_set |= SG_GUI; - free(HL_TABLE()[idx].sg_gui_fg_name); - if (STRCMP(arg, "NONE")) - HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg); - else - HL_TABLE()[idx].sg_gui_fg_name = NULL; + free(HL_TABLE()[idx].sg_rgb_fg_name); + if (STRCMP(arg, "NONE")) { + HL_TABLE()[idx].sg_rgb_fg_name = (uint8_t *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_fg = name_to_color(arg); + } else { + HL_TABLE()[idx].sg_rgb_fg_name = NULL; + HL_TABLE()[idx].sg_rgb_fg = -1; + } + } + + if (is_normal_group) { + normal_fg = HL_TABLE()[idx].sg_rgb_fg; } } else if (STRCMP(key, "GUIBG") == 0) { if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { if (!init) HL_TABLE()[idx].sg_set |= SG_GUI; - free(HL_TABLE()[idx].sg_gui_bg_name); - if (STRCMP(arg, "NONE") != 0) - HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg); - else - HL_TABLE()[idx].sg_gui_bg_name = NULL; + free(HL_TABLE()[idx].sg_rgb_bg_name); + if (STRCMP(arg, "NONE") != 0) { + HL_TABLE()[idx].sg_rgb_bg_name = (uint8_t *)xstrdup((char *)arg); + HL_TABLE()[idx].sg_rgb_bg = name_to_color(arg); + } else { + HL_TABLE()[idx].sg_rgb_bg_name = NULL; + HL_TABLE()[idx].sg_rgb_bg = -1; + } } - } else if (STRCMP(key, "GUISP") == 0) { - if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI)) { - if (!init) - HL_TABLE()[idx].sg_set |= SG_GUI; - free(HL_TABLE()[idx].sg_gui_sp_name); - if (STRCMP(arg, "NONE") != 0) - HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg); - else - HL_TABLE()[idx].sg_gui_sp_name = NULL; + if (is_normal_group) { + normal_bg = HL_TABLE()[idx].sg_rgb_bg; } + } else if (STRCMP(key, "GUISP") == 0) { + // Ignored } else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0) { char_u buf[100]; char_u *tname; @@ -6670,6 +6677,8 @@ void free_highlight(void) */ void restore_cterm_colors(void) { + normal_fg = -1; + normal_bg = -1; cterm_normal_fg_color = 0; cterm_normal_fg_bold = 0; cterm_normal_bg_color = 0; @@ -6705,12 +6714,12 @@ static void highlight_clear(int idx) HL_TABLE()[idx].sg_cterm_bg = 0; HL_TABLE()[idx].sg_cterm_attr = 0; HL_TABLE()[idx].sg_gui = 0; - free(HL_TABLE()[idx].sg_gui_fg_name); - HL_TABLE()[idx].sg_gui_fg_name = NULL; - free(HL_TABLE()[idx].sg_gui_bg_name); - HL_TABLE()[idx].sg_gui_bg_name = NULL; - free(HL_TABLE()[idx].sg_gui_sp_name); - HL_TABLE()[idx].sg_gui_sp_name = NULL; + HL_TABLE()[idx].sg_rgb_fg = -1; + HL_TABLE()[idx].sg_rgb_bg = -1; + free(HL_TABLE()[idx].sg_rgb_fg_name); + HL_TABLE()[idx].sg_rgb_fg_name = NULL; + free(HL_TABLE()[idx].sg_rgb_bg_name); + HL_TABLE()[idx].sg_rgb_bg_name = NULL; /* Clear the script ID only when there is no link, since that is not * cleared. */ if (HL_TABLE()[idx].sg_link == 0) @@ -6771,7 +6780,11 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep) && aep->ae_u.cterm.fg_color == taep->ae_u.cterm.fg_color && aep->ae_u.cterm.bg_color - == taep->ae_u.cterm.bg_color) + == taep->ae_u.cterm.bg_color + && aep->fg_color + == taep->fg_color + && aep->bg_color + == taep->bg_color) )) return i + ATTR_OFF; @@ -6818,6 +6831,8 @@ static int get_attr_entry(garray_T *table, attrentry_T *aep) } else if (table == &cterm_attr_table) { taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color; taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color; + taep->fg_color = aep->fg_color; + taep->bg_color = aep->bg_color; } return table->ga_len - 1 + ATTR_OFF; @@ -6880,6 +6895,10 @@ int hl_combine_attr(int char_attr, int prim_attr) new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color; if (spell_aep->ae_u.cterm.bg_color > 0) new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color; + if (spell_aep->fg_color >= 0) + new_en.fg_color = spell_aep->fg_color; + if (spell_aep->bg_color >= 0) + new_en.bg_color = spell_aep->bg_color; } } return get_attr_entry(&cterm_attr_table, &new_en); @@ -6974,11 +6993,11 @@ static void highlight_list_one(int id) didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_gui, NULL, "gui"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_gui_fg_name, "guifg"); + 0, sgp->sg_rgb_fg_name, "guifg"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_gui_bg_name, "guibg"); + 0, sgp->sg_rgb_bg_name, "guibg"); didh = highlight_list_arg(id, didh, LIST_STRING, - 0, sgp->sg_gui_sp_name, "guisp"); + 0, NULL, "guisp"); if (sgp->sg_link && !got_int) { (void)syn_list_header(didh, 9999, id); @@ -7092,10 +7111,10 @@ highlight_color ( return NULL; if (modec == 'g') { if (fg) - return HL_TABLE()[id - 1].sg_gui_fg_name; + return HL_TABLE()[id - 1].sg_rgb_fg_name; if (sp) - return HL_TABLE()[id - 1].sg_gui_sp_name; - return HL_TABLE()[id - 1].sg_gui_bg_name; + return NULL; + return HL_TABLE()[id - 1].sg_rgb_bg_name; } if (font || sp) return NULL; @@ -7192,9 +7211,14 @@ set_hl_attr ( if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0) sgp->sg_cterm_attr = sgp->sg_cterm; else { - at_en.ae_attr = sgp->sg_cterm; + at_en.ae_attr = abstract_ui ? sgp->sg_gui : sgp->sg_cterm; at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg; at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg; + // FIXME(tarruda): The "unset value" for rgb is -1, but since hlgroup is + // initialized with 0(by garray functions), check for sg_rgb_{f,b}g_name + // before setting attr_entry->{f,g}g_color to a other than -1 + at_en.fg_color = sgp->sg_rgb_fg_name ? sgp->sg_rgb_fg : -1; + at_en.bg_color = sgp->sg_rgb_bg_name ? sgp->sg_rgb_bg : -1; sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en); } } @@ -7633,6 +7657,200 @@ char_u *get_highlight_name(expand_T *xp, int idx) } +RgbValue name_to_color(uint8_t *name) +{ +#define RGB(r, g, b) ((r << 16) | (g << 8) | b) + static struct { + char *name; + RgbValue color; + } color_name_table[] = { + // Color names taken from + // http://www.rapidtables.com/web/color/RGB_Color.htm + {"Maroon", RGB(0x80, 0x00, 0x00)}, + {"DarkRed", RGB(0x8b, 0x00, 0x00)}, + {"Brown", RGB(0xa5, 0x2a, 0x2a)}, + {"Firebrick", RGB(0xb2, 0x22, 0x22)}, + {"Crimson", RGB(0xdc, 0x14, 0x3c)}, + {"Red", RGB(0xff, 0x00, 0x00)}, + {"Tomato", RGB(0xff, 0x63, 0x47)}, + {"Coral", RGB(0xff, 0x7f, 0x50)}, + {"IndianRed", RGB(0xcd, 0x5c, 0x5c)}, + {"LightCoral", RGB(0xf0, 0x80, 0x80)}, + {"DarkSalmon", RGB(0xe9, 0x96, 0x7a)}, + {"Salmon", RGB(0xfa, 0x80, 0x72)}, + {"LightSalmon", RGB(0xff, 0xa0, 0x7a)}, + {"OrangeRed", RGB(0xff, 0x45, 0x00)}, + {"DarkOrange", RGB(0xff, 0x8c, 0x00)}, + {"Orange", RGB(0xff, 0xa5, 0x00)}, + {"Gold", RGB(0xff, 0xd7, 0x00)}, + {"DarkGoldenRod", RGB(0xb8, 0x86, 0x0b)}, + {"GoldenRod", RGB(0xda, 0xa5, 0x20)}, + {"PaleGoldenRod", RGB(0xee, 0xe8, 0xaa)}, + {"DarkKhaki", RGB(0xbd, 0xb7, 0x6b)}, + {"Khaki", RGB(0xf0, 0xe6, 0x8c)}, + {"Olive", RGB(0x80, 0x80, 0x00)}, + {"Yellow", RGB(0xff, 0xff, 0x00)}, + {"YellowGreen", RGB(0x9a, 0xcd, 0x32)}, + {"DarkOliveGreen", RGB(0x55, 0x6b, 0x2f)}, + {"OliveDrab", RGB(0x6b, 0x8e, 0x23)}, + {"LawnGreen", RGB(0x7c, 0xfc, 0x00)}, + {"ChartReuse", RGB(0x7f, 0xff, 0x00)}, + {"GreenYellow", RGB(0xad, 0xff, 0x2f)}, + {"DarkGreen", RGB(0x00, 0x64, 0x00)}, + {"Green", RGB(0x00, 0x80, 0x00)}, + {"ForestGreen", RGB(0x22, 0x8b, 0x22)}, + {"Lime", RGB(0x00, 0xff, 0x00)}, + {"LimeGreen", RGB(0x32, 0xcd, 0x32)}, + {"LightGreen", RGB(0x90, 0xee, 0x90)}, + {"PaleGreen", RGB(0x98, 0xfb, 0x98)}, + {"DarkSeaGreen", RGB(0x8f, 0xbc, 0x8f)}, + {"MediumSpringGreen", RGB(0x00, 0xfa, 0x9a)}, + {"SpringGreen", RGB(0x00, 0xff, 0x7f)}, + {"SeaGreen", RGB(0x2e, 0x8b, 0x57)}, + {"MediumAquamarine", RGB(0x66, 0xcd, 0xaa)}, + {"MediumSeaGreen", RGB(0x3c, 0xb3, 0x71)}, + {"LightSeaGreen", RGB(0x20, 0xb2, 0xaa)}, + {"DarkSlateGray", RGB(0x2f, 0x4f, 0x4f)}, + {"Teal", RGB(0x00, 0x80, 0x80)}, + {"DarkCyan", RGB(0x00, 0x8b, 0x8b)}, + {"Aqua", RGB(0x00, 0xff, 0xff)}, + {"Cyan", RGB(0x00, 0xff, 0xff)}, + {"LightCyan", RGB(0xe0, 0xff, 0xff)}, + {"DarkTurquoise", RGB(0x00, 0xce, 0xd1)}, + {"Turquoise", RGB(0x40, 0xe0, 0xd0)}, + {"MediumTurquoise", RGB(0x48, 0xd1, 0xcc)}, + {"PaleTurquoise", RGB(0xaf, 0xee, 0xee)}, + {"Aquamarine", RGB(0x7f, 0xff, 0xd4)}, + {"PowderBlue", RGB(0xb0, 0xe0, 0xe6)}, + {"CadetBlue", RGB(0x5f, 0x9e, 0xa0)}, + {"SteelBlue", RGB(0x46, 0x82, 0xb4)}, + {"CornFlowerBlue", RGB(0x64, 0x95, 0xed)}, + {"DeepSkyBlue", RGB(0x00, 0xbf, 0xff)}, + {"DodgerBlue", RGB(0x1e, 0x90, 0xff)}, + {"LightBlue", RGB(0xad, 0xd8, 0xe6)}, + {"SkyBlue", RGB(0x87, 0xce, 0xeb)}, + {"LightSkyBlue", RGB(0x87, 0xce, 0xfa)}, + {"MidnightBlue", RGB(0x19, 0x19, 0x70)}, + {"Navy", RGB(0x00, 0x00, 0x80)}, + {"DarkBlue", RGB(0x00, 0x00, 0x8b)}, + {"MediumBlue", RGB(0x00, 0x00, 0xcd)}, + {"Blue", RGB(0x00, 0x00, 0xff)}, + {"RoyalBlue", RGB(0x41, 0x69, 0xe1)}, + {"BlueViolet", RGB(0x8a, 0x2b, 0xe2)}, + {"Indigo", RGB(0x4b, 0x00, 0x82)}, + {"DarkSlateBlue", RGB(0x48, 0x3d, 0x8b)}, + {"SlateBlue", RGB(0x6a, 0x5a, 0xcd)}, + {"MediumSlateBlue", RGB(0x7b, 0x68, 0xee)}, + {"MediumPurple", RGB(0x93, 0x70, 0xdb)}, + {"DarkMagenta", RGB(0x8b, 0x00, 0x8b)}, + {"DarkViolet", RGB(0x94, 0x00, 0xd3)}, + {"DarkOrchid", RGB(0x99, 0x32, 0xcc)}, + {"MediumOrchid", RGB(0xba, 0x55, 0xd3)}, + {"Purple", RGB(0x80, 0x00, 0x80)}, + {"Thistle", RGB(0xd8, 0xbf, 0xd8)}, + {"Plum", RGB(0xdd, 0xa0, 0xdd)}, + {"Violet", RGB(0xee, 0x82, 0xee)}, + {"Magenta", RGB(0xff, 0x00, 0xff)}, + {"Fuchsia", RGB(0xff, 0x00, 0xff)}, + {"Orchid", RGB(0xda, 0x70, 0xd6)}, + {"MediumVioletRed", RGB(0xc7, 0x15, 0x85)}, + {"PaleVioletRed", RGB(0xdb, 0x70, 0x93)}, + {"DeepPink", RGB(0xff, 0x14, 0x93)}, + {"HotPink", RGB(0xff, 0x69, 0xb4)}, + {"LightPink", RGB(0xff, 0xb6, 0xc1)}, + {"Pink", RGB(0xff, 0xc0, 0xcb)}, + {"AntiqueWhite", RGB(0xfa, 0xeb, 0xd7)}, + {"Beige", RGB(0xf5, 0xf5, 0xdc)}, + {"Bisque", RGB(0xff, 0xe4, 0xc4)}, + {"BlanchedAlmond", RGB(0xff, 0xeb, 0xcd)}, + {"Wheat", RGB(0xf5, 0xde, 0xb3)}, + {"Cornsilk", RGB(0xff, 0xf8, 0xdc)}, + {"LemonChiffon", RGB(0xff, 0xfa, 0xcd)}, + {"LightGoldenRodYellow", RGB(0xfa, 0xfa, 0xd2)}, + {"LightYellow", RGB(0xff, 0xff, 0xe0)}, + {"SaddleBrown", RGB(0x8b, 0x45, 0x13)}, + {"Sienna", RGB(0xa0, 0x52, 0x2d)}, + {"Chocolate", RGB(0xd2, 0x69, 0x1e)}, + {"Peru", RGB(0xcd, 0x85, 0x3f)}, + {"SandyBrown", RGB(0xf4, 0xa4, 0x60)}, + {"BurlyWood", RGB(0xde, 0xb8, 0x87)}, + {"Tan", RGB(0xd2, 0xb4, 0x8c)}, + {"RosyBrown", RGB(0xbc, 0x8f, 0x8f)}, + {"Moccasin", RGB(0xff, 0xe4, 0xb5)}, + {"NavajoWhite", RGB(0xff, 0xde, 0xad)}, + {"PeachPuff", RGB(0xff, 0xda, 0xb9)}, + {"MistyRose", RGB(0xff, 0xe4, 0xe1)}, + {"LavenderBlush", RGB(0xff, 0xf0, 0xf5)}, + {"Linen", RGB(0xfa, 0xf0, 0xe6)}, + {"Oldlace", RGB(0xfd, 0xf5, 0xe6)}, + {"PapayaWhip", RGB(0xff, 0xef, 0xd5)}, + {"SeaShell", RGB(0xff, 0xf5, 0xee)}, + {"MintCream", RGB(0xf5, 0xff, 0xfa)}, + {"SlateGray", RGB(0x70, 0x80, 0x90)}, + {"LightSlateGray", RGB(0x77, 0x88, 0x99)}, + {"LightSteelBlue", RGB(0xb0, 0xc4, 0xde)}, + {"Lavender", RGB(0xe6, 0xe6, 0xfa)}, + {"FloralWhite", RGB(0xff, 0xfa, 0xf0)}, + {"AliceBlue", RGB(0xf0, 0xf8, 0xff)}, + {"GhostWhite", RGB(0xf8, 0xf8, 0xff)}, + {"Honeydew", RGB(0xf0, 0xff, 0xf0)}, + {"Ivory", RGB(0xff, 0xff, 0xf0)}, + {"Azure", RGB(0xf0, 0xff, 0xff)}, + {"Snow", RGB(0xff, 0xfa, 0xfa)}, + {"Black", RGB(0x00, 0x00, 0x00)}, + {"DimGray", RGB(0x69, 0x69, 0x69)}, + {"DimGrey", RGB(0x69, 0x69, 0x69)}, + {"Gray", RGB(0x80, 0x80, 0x80)}, + {"Grey", RGB(0x80, 0x80, 0x80)}, + {"DarkGray", RGB(0xa9, 0xa9, 0xa9)}, + {"DarkGrey", RGB(0xa9, 0xa9, 0xa9)}, + {"Silver", RGB(0xc0, 0xc0, 0xc0)}, + {"LightGray", RGB(0xd3, 0xd3, 0xd3)}, + {"LightGrey", RGB(0xd3, 0xd3, 0xd3)}, + {"Gainsboro", RGB(0xdc, 0xdc, 0xdc)}, + {"WhiteSmoke", RGB(0xf5, 0xf5, 0xf5)}, + {"White", RGB(0xff, 0xff, 0xff)}, + // The color names below were taken from gui_x11.c in vim source + {"LightRed", RGB(0xff, 0xbb, 0xbb)}, + {"LightMagenta",RGB(0xff, 0xbb, 0xff)}, + {"DarkYellow", RGB(0xbb, 0xbb, 0x00)}, + {"Gray10", RGB(0x1a, 0x1a, 0x1a)}, + {"Grey10", RGB(0x1a, 0x1a, 0x1a)}, + {"Gray20", RGB(0x33, 0x33, 0x33)}, + {"Grey20", RGB(0x33, 0x33, 0x33)}, + {"Gray30", RGB(0x4d, 0x4d, 0x4d)}, + {"Grey30", RGB(0x4d, 0x4d, 0x4d)}, + {"Gray40", RGB(0x66, 0x66, 0x66)}, + {"Grey40", RGB(0x66, 0x66, 0x66)}, + {"Gray50", RGB(0x7f, 0x7f, 0x7f)}, + {"Grey50", RGB(0x7f, 0x7f, 0x7f)}, + {"Gray60", RGB(0x99, 0x99, 0x99)}, + {"Grey60", RGB(0x99, 0x99, 0x99)}, + {"Gray70", RGB(0xb3, 0xb3, 0xb3)}, + {"Grey70", RGB(0xb3, 0xb3, 0xb3)}, + {"Gray80", RGB(0xcc, 0xcc, 0xcc)}, + {"Grey80", RGB(0xcc, 0xcc, 0xcc)}, + {"Gray90", RGB(0xe5, 0xe5, 0xe5)}, + {"Grey90", RGB(0xe5, 0xe5, 0xe5)}, + {NULL, 0}, + }; + + if (name[0] == '#' && isxdigit(name[1]) && isxdigit(name[2]) + && isxdigit(name[3]) && isxdigit(name[4]) && isxdigit(name[5]) + && isxdigit(name[6]) && name[7] == NUL) { + // rgb hex string + return strtol((char *)(name + 1), NULL, 16); + } + + for (int i = 0; color_name_table[i].name != NULL; i++) { + if (!STRICMP(name, color_name_table[i].name)) { + return color_name_table[i].color; + } + } + + return -1; +} + /************************************** * End of Highlighting stuff * **************************************/ diff --git a/src/nvim/syntax.h b/src/nvim/syntax.h index a03bd1e604..9a284c8a8d 100644 --- a/src/nvim/syntax.h +++ b/src/nvim/syntax.h @@ -5,8 +5,6 @@ #include "nvim/buffer_defs.h" -typedef int guicolor_T; - /* * Terminal highlighting attribute bits. * Attributes above HL_ALL are used for syntax highlighting. diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index 11e342f870..abf7ea5a7d 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -3,6 +3,8 @@ #include "nvim/regexp_defs.h" +typedef int32_t RgbValue; + # define SST_MIN_ENTRIES 150 /* minimal size for state stack array */ # define SST_MAX_ENTRIES 1000 /* maximal size for state stack array */ # define SST_FIX_STATES 7 /* size of sst_stack[]. */ @@ -70,6 +72,7 @@ struct syn_state { */ typedef struct attr_entry { short ae_attr; /* HL_BOLD, etc. */ + RgbValue fg_color, bg_color; union { struct { char_u *start; /* start escape sequence */ diff --git a/src/nvim/term.c b/src/nvim/term.c index 54508b1daa..40d6b4c170 100644 --- a/src/nvim/term.c +++ b/src/nvim/term.c @@ -161,6 +161,33 @@ static bool detected_8bit = false; // detected 8-bit terminal static struct builtin_term builtin_termcaps[] = { + // abstract UI pseudo termcap, based on vim's "builtin_gui" termcap + {(int)KS_NAME, "abstract_ui"}, + {(int)KS_CE, "\033|$"}, + {(int)KS_AL, "\033|i"}, + {(int)KS_CAL, "\033|%p1%dI"}, + {(int)KS_DL, "\033|d"}, + {(int)KS_CDL, "\033|%p1%dD"}, + {(int)KS_CS, "\033|%p1%d;%p2%dR"}, + {(int)KS_CL, "\033|C"}, + // attributes switched on with 'h', off with * 'H' + {(int)KS_ME, "\033|31H"}, // HL_ALL + {(int)KS_MR, "\033|1h"}, // HL_INVERSE + {(int)KS_MD, "\033|2h"}, // HL_BOLD + {(int)KS_SE, "\033|16H"}, // HL_STANDOUT + {(int)KS_SO, "\033|16h"}, // HL_STANDOUT + {(int)KS_UE, "\033|8H"}, // HL_UNDERLINE + {(int)KS_US, "\033|8h"}, // HL_UNDERLINE + {(int)KS_CZR, "\033|4H"}, // HL_ITALIC + {(int)KS_CZH, "\033|4h"}, // HL_ITALIC + {(int)KS_VB, "\033|f"}, + {(int)KS_MS, "y"}, + {(int)KS_UT, "y"}, + {(int)KS_LE, "\b"}, // cursor-left = BS + {(int)KS_ND, "\014"}, // cursor-right = CTRL-L + {(int)KS_CM, "\033|%p1%d;%p2%dM"}, + // there are no key sequences here, for "abstract_ui" vim key codes are + // parsed directly in input_enqueue() #ifndef NO_BUILTIN_TCAPS @@ -1162,6 +1189,10 @@ int set_termname(char_u *term) if (silent_mode) return OK; + if (!STRCMP(term, "abstract_ui")) { + abstract_ui = true; + } + detected_8bit = false; // reset 8-bit detection if (term_is_builtin(term)) { @@ -1829,18 +1860,6 @@ void termcapinit(char_u *name) /// Write s[len] to the screen. void term_write(char_u *s, size_t len) { - if (embedded_mode) { - // TODO(tarruda): This is a temporary hack to stop Neovim from writing - // messages to stdout in embedded mode. In the future, embedded mode will - // be the only possibility(GUIs will always start neovim with a msgpack-rpc - // over stdio) and this function won't exist. - // - // The reason for this is because before Neovim fully migrates to a - // msgpack-rpc-driven architecture, we must have a fully functional - // UI working - return; - } - (void) fwrite(s, len, 1, stdout); #ifdef UNIX @@ -2296,7 +2315,7 @@ void shell_resized_check(void) */ void settmode(int tmode) { - if (embedded_mode) { + if (abstract_ui) { return; } @@ -2340,7 +2359,7 @@ void starttermcap(void) out_flush(); termcap_active = TRUE; screen_start(); /* don't know where cursor is now */ - { + if (!abstract_ui) { may_req_termresponse(); /* Immediately check for a response. If t_Co changes, we don't * want to redraw with wrong colors first. */ @@ -2356,7 +2375,7 @@ void stoptermcap(void) screen_stop_highlight(); reset_cterm_colors(); if (termcap_active) { - { + if (!abstract_ui) { /* May need to discard T_CRV or T_U7 response. */ if (crv_status == CRV_SENT || u7_status == U7_SENT) { # ifdef UNIX @@ -2545,6 +2564,11 @@ static int cursor_is_off = FALSE; */ void cursor_on(void) { + if (abstract_ui) { + ui_cursor_on(); + return; + } + if (cursor_is_off) { out_str(T_VE); cursor_is_off = FALSE; @@ -2556,6 +2580,11 @@ void cursor_on(void) */ void cursor_off(void) { + if (abstract_ui) { + ui_cursor_off(); + return; + } + if (full_screen) { if (!cursor_is_off) out_str(T_VI); /* disable cursor */ @@ -2852,6 +2881,11 @@ void set_mouse_topline(win_T *wp) */ int check_termcode(int max_offset, char_u *buf, int bufsize, int *buflen) { + if (abstract_ui) { + // codes are parsed by input.c/input_enqueue + return 0; + } + char_u *tp; char_u *p; int slen = 0; /* init for GCC */ @@ -3883,6 +3917,10 @@ int find_term_bykeys(char_u *src) */ static void gather_termleader(void) { + if (abstract_ui) { + return; + } + int len = 0; if (check_for_codes) diff --git a/src/nvim/ui.c b/src/nvim/ui.c index eab6251288..9c58193e8c 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -15,20 +15,24 @@ * 3. Input buffer stuff. */ +#include <assert.h> #include <inttypes.h> #include <stdbool.h> #include <string.h> #include "nvim/vim.h" #include "nvim/ui.h" +#include "nvim/charset.h" #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/ex_cmds2.h" #include "nvim/fold.h" #include "nvim/main.h" #include "nvim/mbyte.h" +#include "nvim/ascii.h" #include "nvim/misc1.h" #include "nvim/misc2.h" +#include "nvim/mbyte.h" #include "nvim/garray.h" #include "nvim/memory.h" #include "nvim/move.h" @@ -39,27 +43,74 @@ #include "nvim/os/input.h" #include "nvim/os/signal.h" #include "nvim/screen.h" +#include "nvim/syntax.h" #include "nvim/term.h" #include "nvim/window.h" -void ui_write(char_u *s, int len) +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ui.c.generated.h" +#endif + +#define MAX_UI_COUNT 16 + +static UI *uis[MAX_UI_COUNT]; +static size_t ui_count = 0; +static int row, col; +static struct { + int top, bot, left, right; +} sr; +static int current_highlight_mask = 0; +static HlAttrs current_attrs = { + false, false, false, false, false, false, -1, -1 +}; +static bool cursor_enabled = true; +static int height = INT_MAX, width = INT_MAX; + +// This set of macros allow us to use UI_CALL to invoke any function on +// registered UI instances. The functions can have 0-5 arguments(configurable +// by SELECT_NTH) +// +// See http://stackoverflow.com/a/11172679 for a better explanation of how it +// works. +#define UI_CALL(...) \ + do { \ + for (size_t i = 0; i < ui_count; i++) { \ + UI *ui = uis[i]; \ + UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \ + } \ + } while (0) +#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, ZERO, ignore) +#define SELECT_NTH(a1, a2, a3, a4, a5, a6, ...) a6 +#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__) +#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__) +#define UI_CALL_MORE(method, ...) ui->method(ui, __VA_ARGS__) +#define UI_CALL_ZERO(method) ui->method(ui) + +void ui_write(uint8_t *s, int len) { - /* Don't output anything in silent mode ("ex -s") unless 'verbose' set */ - if (!(silent_mode && p_verbose == 0)) { - char_u *tofree = NULL; + if (silent_mode && !p_verbose) { + // Don't output anything in silent mode ("ex -s") unless 'verbose' set + return; + } - if (output_conv.vc_type != CONV_NONE) { - /* Convert characters from 'encoding' to 'termencoding'. */ - tofree = string_convert(&output_conv, s, &len); - if (tofree != NULL) - s = tofree; - } + if (abstract_ui) { + parse_abstract_ui_codes(s, len); + return; + } - term_write(s, len); + char_u *tofree = NULL; - if (output_conv.vc_type != CONV_NONE) - free(tofree); + if (output_conv.vc_type != CONV_NONE) { + /* Convert characters from 'encoding' to 'termencoding'. */ + tofree = string_convert(&output_conv, s, &len); + if (tofree != NULL) + s = tofree; } + + term_write(s, len); + + if (output_conv.vc_type != CONV_NONE) + free(tofree); } /* @@ -69,7 +120,11 @@ void ui_write(char_u *s, int len) */ void ui_suspend(void) { - mch_suspend(); + if (abstract_ui) { + UI_CALL(suspend); + } else { + mch_suspend(); + } } /* @@ -79,6 +134,10 @@ void ui_suspend(void) */ int ui_get_shellsize(void) { + if (abstract_ui) { + return FAIL; + } + int retval; retval = mch_get_shellsize(); @@ -98,7 +157,363 @@ int ui_get_shellsize(void) */ void ui_cursor_shape(void) { - term_cursor_shape(); + if (abstract_ui) { + ui_change_mode(); + } else { + term_cursor_shape(); + conceal_check_cursur_line(); + } +} + +void ui_resize(int width, int height) +{ + sr.top = 0; + sr.bot = height - 1; + sr.left = 0; + sr.right = width - 1; + UI_CALL(resize, width, height); +} + +void ui_cursor_on(void) +{ + if (!cursor_enabled) { + UI_CALL(cursor_on); + cursor_enabled = true; + } +} + +void ui_cursor_off(void) +{ + if (full_screen) { + if (cursor_enabled) { + UI_CALL(cursor_off); + } + cursor_enabled = false; + } +} + +void ui_mouse_on(void) +{ + if (abstract_ui) { + UI_CALL(mouse_on); + } else { + mch_setmouse(true); + } +} + +void ui_mouse_off(void) +{ + if (abstract_ui) { + UI_CALL(mouse_off); + } else { + mch_setmouse(false); + } +} + +// Notify that the current mode has changed. Can be used to change cursor +// shape, for example. +void ui_change_mode(void) +{ + static int showing_insert_mode = MAYBE; + + if (!full_screen) + return; + + if (State & INSERT) { + if (showing_insert_mode != TRUE) { + UI_CALL(insert_mode); + } + showing_insert_mode = TRUE; + } else { + if (showing_insert_mode != FALSE) { + UI_CALL(normal_mode); + } + showing_insert_mode = FALSE; + } conceal_check_cursur_line(); } +void ui_attach(UI *ui) +{ + if (ui_count == MAX_UI_COUNT) { + abort(); + } + + uis[ui_count++] = ui; + resized(ui); +} + +void ui_detach(UI *ui) +{ + size_t shift_index = MAX_UI_COUNT; + + // Find the index that will be removed + for (size_t i = 0; i < ui_count; i++) { + if (uis[i] == ui) { + shift_index = i; + break; + } + } + + if (shift_index == MAX_UI_COUNT) { + abort(); + } + + // Shift UIs at "shift_index" + while (shift_index < ui_count - 1) { + uis[shift_index] = uis[shift_index + 1]; + shift_index++; + } + + ui_count--; + + if (ui->width == width || ui->height == height) { + // It is possible that the UI being detached had the smallest screen, + // so check for the new minimum dimensions + width = height = INT_MAX; + for (size_t i = 0; i < ui_count; i++) { + check_dimensions(uis[i]); + } + } + + if (ui_count) { + screen_resize(width, height, true); + } +} + +static void highlight_start(int mask) +{ + if (mask > HL_ALL) { + // attribute code + current_highlight_mask = mask; + } else { + // attribute mask + current_highlight_mask |= mask; + } + + if (!ui_count) { + return; + } + + set_highlight_args(current_highlight_mask, ¤t_attrs); + UI_CALL(highlight_set, current_attrs); +} + +static void highlight_stop(int mask) +{ + if (mask > HL_ALL) { + // attribute code + current_highlight_mask = HL_NORMAL; + } else { + // attribute mask + current_highlight_mask &= ~mask; + } + + set_highlight_args(current_highlight_mask, ¤t_attrs); + UI_CALL(highlight_set, current_attrs); +} + +static void set_highlight_args(int mask, HlAttrs *attrs) +{ + attrentry_T *aep = NULL; + + if (mask > HL_ALL) { + aep = syn_cterm_attr2entry(mask); + mask = aep ? aep->ae_attr : 0; + } + + attrs->bold = mask & HL_BOLD; + attrs->standout = mask & HL_STANDOUT; + attrs->underline = mask & HL_UNDERLINE; + attrs->undercurl = mask & HL_UNDERCURL; + attrs->italic = mask & HL_ITALIC; + attrs->reverse = mask & HL_INVERSE; + attrs->foreground = aep && aep->fg_color >= 0 ? aep->fg_color : normal_fg; + attrs->background = aep && aep->bg_color >= 0 ? aep->bg_color : normal_bg; +} + +static void parse_abstract_ui_codes(uint8_t *ptr, int len) +{ + int arg1 = 0, arg2 = 0; + uint8_t *end = ptr + len, *p, c; + bool update_cursor = false; + + while (ptr < end) { + if (ptr < end - 1 && ptr[0] == ESC && ptr[1] == '|') { + p = ptr + 2; + assert(p != end); + + if (VIM_ISDIGIT(*p)) { + arg1 = (int)getdigits(&p); + if (p >= end) { + break; + } + + if (*p == ';') { + p++; + arg2 = (int)getdigits(&p); + if (p >= end) + break; + } + } + + switch (*p) { + case 'C': + UI_CALL(clear); + break; + case 'M': + ui_cursor_goto(arg1, arg2); + break; + case 's': + update_cursor = true; + break; + case 'R': + if (arg1 < arg2) { + sr.top = arg1; + sr.bot = arg2; + UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); + } else { + sr.top = arg2; + sr.bot = arg1; + UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); + } + break; + case 'V': + if (arg1 < arg2) { + sr.left = arg1; + sr.right = arg2; + UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); + } else { + sr.left = arg2; + sr.right = arg1; + UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); + } + break; + case 'd': + UI_CALL(scroll, 1); + break; + case 'D': + UI_CALL(scroll, arg1); + break; + case 'i': + UI_CALL(scroll, -1); + break; + case 'I': + UI_CALL(scroll, -arg1); + break; + case '$': + UI_CALL(eol_clear); + break; + case 'h': + highlight_start(arg1); + break; + case 'H': + highlight_stop(arg1); + break; + case 'f': + UI_CALL(visual_bell); + break; + default: + // Skip the ESC + p = ptr + 1; + break; + } + ptr = ++p; + } else if ((c = *ptr) < 0x20) { + // Ctrl character + if (c == '\n') { + ui_linefeed(); + } else if (c == '\r') { + ui_carriage_return(); + } else if (c == '\b') { + ui_cursor_left(); + } else if (c == Ctrl_L) { // cursor right + ui_cursor_right(); + } else if (c == Ctrl_G) { + UI_CALL(bell); + } + ptr++; + } else { + p = ptr; + while (p < end && (*p >= 0x20)) { + size_t clen = (size_t)mb_ptr2len(p); + UI_CALL(put, p, (size_t)clen); + col++; + if (mb_ptr2cells(p) > 1) { + // double cell character, blank the next cell + UI_CALL(put, NULL, 0); + col++; + } + p += clen; + } + ptr = p; + } + } + + if (update_cursor) { + ui_cursor_shape(); + } + + UI_CALL(flush); +} + +static void resized(UI *ui) +{ + check_dimensions(ui); + screen_resize(width, height, true); +} + +static void check_dimensions(UI *ui) +{ + // The internal screen dimensions are always the minimum required to fit on + // all connected screens + if (ui->width < width) { + width = ui->width; + } + + if (ui->height < height) { + height = ui->height; + } +} + +static void ui_linefeed(void) +{ + int new_col = 0; + int new_row = row; + if (new_row < sr.bot) { + new_row++; + } else { + UI_CALL(scroll, 1); + } + ui_cursor_goto(new_row, new_col); +} + +static void ui_carriage_return(void) +{ + int new_col = 0; + ui_cursor_goto(row, new_col); +} + +static void ui_cursor_left(void) +{ + int new_col = col - 1; + assert(new_col >= 0); + ui_cursor_goto(row, new_col); +} + +static void ui_cursor_right(void) +{ + int new_col = col + 1; + assert(new_col < width); + ui_cursor_goto(row, new_col); +} + +static void ui_cursor_goto(int new_row, int new_col) +{ + if (new_row == row && new_col == col) { + return; + } + row = new_row; + col = new_col; + UI_CALL(cursor_goto, row, col); +} diff --git a/src/nvim/ui.h b/src/nvim/ui.h index b174af9abe..d0933055cc 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -1,7 +1,39 @@ #ifndef NVIM_UI_H #define NVIM_UI_H +#include <stddef.h> #include <stdbool.h> +#include <stdint.h> + +typedef struct { + bool bold, standout, underline, undercurl, italic, reverse; + int foreground, background; +} HlAttrs; + +typedef struct ui_t UI; + +struct ui_t { + int width, height; + void *data; + void (*resize)(UI *ui, int rows, int columns); + void (*clear)(UI *ui); + void (*eol_clear)(UI *ui); + void (*cursor_goto)(UI *ui, int row, int col); + void (*cursor_on)(UI *ui); + void (*cursor_off)(UI *ui); + void (*mouse_on)(UI *ui); + void (*mouse_off)(UI *ui); + void (*insert_mode)(UI *ui); + void (*normal_mode)(UI *ui); + void (*set_scroll_region)(UI *ui, int top, int bot, int left, int right); + void (*scroll)(UI *ui, int count); + void (*highlight_set)(UI *ui, HlAttrs attrs); + void (*put)(UI *ui, uint8_t *str, size_t len); + void (*bell)(UI *ui); + void (*visual_bell)(UI *ui); + void (*flush)(UI *ui); + void (*suspend)(UI *ui); +}; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.h.generated.h" |