aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThiago de Arruda <tpadilha84@gmail.com>2014-12-09 08:55:31 -0300
committerThiago de Arruda <tpadilha84@gmail.com>2014-12-09 08:55:31 -0300
commitc5b9e5d1d317b74d4adf7637cd9081be4ee52722 (patch)
tree46f2b1692851ae6afe3ffc5d9c2ebc700fe6b452 /src
parent8bb7aa329d20cb265d8952c96c84a0e54a5726ab (diff)
parent1192fbd08a054cece0b48dfb695e77e689997980 (diff)
downloadrneovim-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.c6
-rw-r--r--src/nvim/ex_getln.c4
-rw-r--r--src/nvim/getchar.c7
-rw-r--r--src/nvim/globals.h4
-rw-r--r--src/nvim/main.c36
-rw-r--r--src/nvim/mouse.c11
-rw-r--r--src/nvim/msgpack_rpc/channel.c16
-rw-r--r--src/nvim/msgpack_rpc/defs.h4
-rw-r--r--src/nvim/msgpack_rpc/remote_ui.c280
-rw-r--r--src/nvim/msgpack_rpc/remote_ui.h9
-rw-r--r--src/nvim/os/input.c30
-rw-r--r--src/nvim/os/signal.c2
-rw-r--r--src/nvim/screen.c43
-rw-r--r--src/nvim/syntax.c290
-rw-r--r--src/nvim/syntax.h2
-rw-r--r--src/nvim/syntax_defs.h3
-rw-r--r--src/nvim/term.c68
-rw-r--r--src/nvim/ui.c445
-rw-r--r--src/nvim/ui.h32
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(&params);
- }
-
/* 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(&params);
+ }
+
/*
* 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, &current_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, &current_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"