aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/CMakeLists.txt5
-rw-r--r--src/nvim/api/extmark.c60
-rw-r--r--src/nvim/api/ui_events.in.h3
-rw-r--r--src/nvim/api/win_config.c18
-rw-r--r--src/nvim/decoration.c18
-rw-r--r--src/nvim/drawline.c48
-rw-r--r--src/nvim/drawscreen.c12
-rw-r--r--src/nvim/event/process.c1
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/fold.c9
-rw-r--r--src/nvim/grid_defs.h2
-rw-r--r--src/nvim/mouse.c94
-rw-r--r--src/nvim/tui/tui.c16
-rw-r--r--src/nvim/ui_client.h3
14 files changed, 177 insertions, 116 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 94e5e6ee28..db79de63cc 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -230,6 +230,11 @@ if(WIN32)
endif()
elseif(APPLE)
target_link_libraries(nvim PRIVATE "-framework CoreServices")
+
+ # Actually export symbols - symbols may not be visible even though
+ # ENABLE_EXPORTS is set to true. See
+ # https://github.com/neovim/neovim/issues/25295
+ set_target_properties(nvim PROPERTIES LINK_FLAGS "-Wl,-export_dynamic")
endif()
if(UNIX)
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index faab6e593c..91e197bea7 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -113,6 +113,36 @@ static Object hl_group_name(int hl_id, bool hl_name)
}
}
+Array virt_text_to_array(VirtText vt, bool hl_name)
+{
+ Array chunks = ARRAY_DICT_INIT;
+ Array hl_array = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < kv_size(vt); i++) {
+ char *text = kv_A(vt, i).text;
+ int hl_id = kv_A(vt, i).hl_id;
+ if (text == NULL) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ continue;
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, CSTR_TO_OBJ(text));
+ if (hl_array.size > 0) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunk, ARRAY_OBJ(hl_array));
+ hl_array = (Array)ARRAY_DICT_INIT;
+ } else if (hl_id > 0) {
+ ADD(chunk, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
+ }
+ assert(hl_array.size == 0);
+ return chunks;
+}
+
static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name)
{
Array rv = ARRAY_DICT_INIT;
@@ -145,16 +175,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
}
if (kv_size(decor->virt_text)) {
- Array chunks = ARRAY_DICT_INIT;
- for (size_t i = 0; i < decor->virt_text.size; i++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &decor->virt_text.items[i];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ Array chunks = virt_text_to_array(decor->virt_text, hl_name);
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
if (decor->virt_text_pos == kVTWinCol) {
@@ -171,19 +192,9 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
if (kv_size(decor->virt_lines)) {
Array all_chunks = ARRAY_DICT_INIT;
bool virt_lines_leftcol = false;
- for (size_t i = 0; i < decor->virt_lines.size; i++) {
- Array chunks = ARRAY_DICT_INIT;
- VirtText *vt = &decor->virt_lines.items[i].line;
- virt_lines_leftcol = decor->virt_lines.items[i].left_col;
- for (size_t j = 0; j < vt->size; j++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &vt->items[j];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ for (size_t i = 0; i < kv_size(decor->virt_lines); i++) {
+ virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col;
+ Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name);
ADD(all_chunks, ARRAY_OBJ(chunks));
}
PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
@@ -1188,8 +1199,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
goto free_exit;
}
if (j < arr.size - 1) {
- kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
- .hl_id = hl_id }));
+ kv_push(virt_text, ((VirtTextChunk){ .text = NULL, .hl_id = hl_id }));
}
}
} else {
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index fc70215352..6ca5024a04 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -167,4 +167,7 @@ void msg_history_show(Array entries)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_history_clear(void)
FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
+
+void error_exit(Integer status)
+ FUNC_API_SINCE(12);
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index ebdbd896a5..ea0b7ce512 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -259,34 +259,28 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
BorderTextType bordertext_type)
{
- VirtText chunks;
+ VirtText vt;
AlignTextPos align;
char *field_name;
char *field_pos_name;
switch (bordertext_type) {
case kBorderTextTitle:
- chunks = fconfig->title_chunks;
+ vt = fconfig->title_chunks;
align = fconfig->title_pos;
field_name = "title";
field_pos_name = "title_pos";
break;
case kBorderTextFooter:
- chunks = fconfig->footer_chunks;
+ vt = fconfig->footer_chunks;
align = fconfig->footer_pos;
field_name = "footer";
field_pos_name = "footer_pos";
break;
+ default:
+ abort();
}
- Array bordertext = ARRAY_DICT_INIT;
- for (size_t i = 0; i < chunks.size; i++) {
- Array tuple = ARRAY_DICT_INIT;
- ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text));
- if (chunks.items[i].hl_id > 0) {
- ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id)));
- }
- ADD(bordertext, ARRAY_OBJ(tuple));
- }
+ Array bordertext = virt_text_to_array(vt, true);
PUT(config, field_name, ARRAY_OBJ(bordertext));
char *pos;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index b70f070a51..9d391cde8c 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -153,6 +153,24 @@ void clear_virttext(VirtText *text)
*text = (VirtText)KV_INITIAL_VALUE;
}
+/// Get the next chunk of a virtual text item.
+///
+/// @param[in] vt The virtual text item
+/// @param[in,out] pos Position in the virtual text item
+/// @param[in,out] attr Highlight attribute
+///
+/// @return The text of the chunk, or NULL if there are no more chunks
+char *next_virt_text_chunk(VirtText vt, size_t *pos, int *attr)
+{
+ char *text = NULL;
+ for (; text == NULL && *pos < kv_size(vt); (*pos)++) {
+ text = kv_A(vt, *pos).text;
+ int hl_id = kv_A(vt, *pos).hl_id;
+ *attr = hl_combine_attr(*attr, hl_id > 0 ? syn_id2attr(hl_id) : 0);
+ }
+ return text;
+}
+
Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
{
MarkTreeIter itr[1] = { 0 };
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 73021d83a8..7d64d9fa3c 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -332,23 +332,17 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
size_t virt_pos = 0;
while (rl ? col > max_col : col < max_col) {
- if (!*s.p) {
+ if (*s.p == NUL) {
if (virt_pos >= kv_size(vt)) {
break;
}
virt_attr = 0;
- do {
- s.p = kv_A(vt, virt_pos).text;
- int hl_id = kv_A(vt, virt_pos).hl_id;
- virt_attr = hl_combine_attr(virt_attr,
- hl_id > 0 ? syn_id2attr(hl_id) : 0);
- virt_pos++;
- } while (!s.p && virt_pos < kv_size(vt));
- if (!s.p) {
+ s.p = next_virt_text_chunk(vt, &virt_pos, &virt_attr);
+ if (s.p == NULL) {
break;
}
}
- if (!*s.p) {
+ if (*s.p == NUL) {
continue;
}
int attr;
@@ -950,17 +944,20 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
}
} else {
// already inside existing inline virtual text with multiple chunks
- VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i);
- wlv->virt_inline_i++;
- wlv->p_extra = vtc.text;
- wlv->n_extra = (int)strlen(vtc.text);
+ int attr = 0;
+ char *text = next_virt_text_chunk(wlv->virt_inline, &wlv->virt_inline_i, &attr);
+ if (text == NULL) {
+ continue;
+ }
+ wlv->p_extra = text;
+ wlv->n_extra = (int)strlen(text);
if (wlv->n_extra == 0) {
continue;
}
wlv->c_extra = NUL;
wlv->c_final = NUL;
- wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
- wlv->n_attr = mb_charlen(vtc.text);
+ wlv->extra_attr = attr;
+ wlv->n_attr = mb_charlen(text);
// If the text didn't reach until the first window
// column we need to skip cells.
if (wlv->skip_cells > 0) {
@@ -3024,14 +3021,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_attr[wlv.off] = wlv.char_attr;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
-
- if (wlv.draw_state == WL_FOLD) {
- linebuf_vcol[wlv.off] = -2;
+ if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
+ linebuf_vcol[wlv.off] = wlv.vcol;
+ } else if (wlv.draw_state == WL_FOLD) {
if (wlv.n_closing > 0) {
linebuf_vcol[wlv.off] = -3;
wlv.n_closing--;
+ } else {
+ linebuf_vcol[wlv.off] = -2;
}
+ } else {
+ linebuf_vcol[wlv.off] = -1;
}
if (utf_char2cells(mb_c) > 1) {
@@ -3041,17 +3041,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// UTF-8: Put a 0 in the second screen char.
linebuf_char[wlv.off] = 0;
linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1];
+
if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
- wlv.vcol++;
+ linebuf_vcol[wlv.off] = ++wlv.vcol;
+ } else {
+ linebuf_vcol[wlv.off] = -1;
}
+
// When "wlv.tocol" is halfway through a character, set it to the end
// of the character, otherwise highlighting won't stop.
if (wlv.tocol == wlv.vcol) {
wlv.tocol++;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
-
if (wp->w_p_rl) {
// now it's time to backup one cell
wlv.off--;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 854c8590e4..1d22b67d40 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -709,13 +709,15 @@ void end_search_hl(void)
screen_search_hl.rm.regprog = NULL;
}
-static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText text_chunks, int row, int col)
+static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText vt, int row, int col)
{
- for (size_t i = 0; i < text_chunks.size; i++) {
- char *text = text_chunks.items[i].text;
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *text = next_virt_text_chunk(vt, &i, &attr);
+ if (text == NULL) {
+ break;
+ }
int cell = (int)mb_string2cells(text);
- int hl_id = text_chunks.items[i].hl_id;
- int attr = hl_id ? syn_id2attr(hl_id) : 0;
grid_puts(grid, text, row, col, attr);
col += cell;
}
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 95bf4d1c3b..00ba1334b0 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -423,6 +423,7 @@ static void exit_event(void **argv)
if (!exiting) {
if (ui_client_channel_id) {
+ ui_client_exit_status = status;
os_exit(status);
} else {
assert(status == 0); // Called from rpc_close(), which passes 0 as status.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 44610c81d8..0a0f7c244d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4591,7 +4591,9 @@ static void ex_cquit(exarg_T *eap)
FUNC_ATTR_NORETURN
{
// this does not always pass on the exit code to the Manx compiler. why?
- getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
+ int status = eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE;
+ ui_call_error_exit(status);
+ getout(status);
}
/// Do preparations for "qall" and "wqall".
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 1d5ba49301..d874e904d0 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -3344,8 +3344,13 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (kv_size(vt) > 0) {
assert(*text == NUL);
- for (size_t i = 0; i < kv_size(vt); i++) {
- char *new_text = concat_str(text, kv_A(vt, i).text);
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *new_text = next_virt_text_chunk(vt, &i, &attr);
+ if (new_text == NULL) {
+ break;
+ }
+ new_text = concat_str(text, new_text);
xfree(text);
text = new_text;
}
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 207bf72816..100d648263 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -44,7 +44,7 @@ enum {
/// attrs[] contains the highlighting attribute for each cell.
///
/// vcols[] contains the virtual columns in the line. -1 means not available
-/// (below last line), MAXCOL means after the end of the line.
+/// or before buffer text, MAXCOL means after the end of the line.
/// -2 or -3 means in fold column and a mouse click should:
/// -2: open a fold
/// -3: close a fold
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index e35385dd43..9b09a3fdf3 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1849,57 +1849,61 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
- int max_row = Rows;
- int max_col = Columns;
- bool multigrid = ui_has(kUIMultigrid);
- colnr_T col_from_screen = -1;
+ // XXX: this doesn't change click_grid if it is 1, even with multigrid
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
- if (wp && multigrid) {
- max_row = wp->w_grid_alloc.rows;
- max_col = wp->w_grid_alloc.cols;
- }
-
- if (wp && mouse_row >= 0 && mouse_row < max_row
- && mouse_col >= 0 && mouse_col < max_col) {
- ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
- int use_row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
- int use_col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
-
- if (gp->chars != NULL) {
- const size_t off = gp->line_offset[use_row] + (size_t)use_col;
-
- // Only use vcols[] after the window was redrawn. Mainly matters
- // for tests, a user would not click before redrawing.
- if (wp->w_redr_type == 0) {
- col_from_screen = gp->vcols[off];
- }
-
- if (col_from_screen == MAXCOL) {
- // When clicking after end of line, still need to set correct curswant
- size_t off_l = gp->line_offset[use_row];
- if (gp->vcols[off_l] < MAXCOL) {
- // Binary search to find last char in line
- size_t off_r = off;
- while (off_l < off_r) {
- size_t off_m = (off_l + off_r + 1) / 2;
- if (gp->vcols[off_m] < MAXCOL) {
- off_l = off_m;
- } else {
- off_r = off_m - 1;
- }
- }
- *vcolp = gp->vcols[off_r] + (int)(off - off_r);
+ // Only use vcols[] after the window was redrawn. Mainly matters
+ // for tests, a user would not click before redrawing.
+ if (wp == NULL || wp->w_redr_type != 0) {
+ return;
+ }
+ ScreenGrid *gp = &wp->w_grid;
+ int start_row = 0;
+ int start_col = 0;
+ grid_adjust(&gp, &start_row, &start_col);
+ if (gp->handle != click_grid || gp->chars == NULL) {
+ return;
+ }
+ click_row += start_row;
+ click_col += start_col;
+ if (click_row < 0 || click_row >= gp->rows
+ || click_col < 0 || click_col >= gp->cols) {
+ return;
+ }
+
+ const size_t off = gp->line_offset[click_row] + (size_t)click_col;
+ colnr_T col_from_screen = gp->vcols[off];
+
+ if (col_from_screen == MAXCOL) {
+ // When clicking after end of line, still need to set correct curswant
+ size_t off_l = gp->line_offset[click_row] + (size_t)start_col;
+ if (gp->vcols[off_l] < MAXCOL) {
+ // Binary search to find last char in line
+ size_t off_r = off;
+ while (off_l < off_r) {
+ size_t off_m = (off_l + off_r + 1) / 2;
+ if (gp->vcols[off_m] < MAXCOL) {
+ off_l = off_m;
} else {
- // Shouldn't normally happen
- *vcolp = MAXCOL;
+ off_r = off_m - 1;
}
- } else if (col_from_screen >= 0) {
- // Use the virtual column from vcols[], it is accurate also after
- // concealed characters.
- *vcolp = col_from_screen;
}
+ colnr_T eol_vcol = gp->vcols[off_r];
+ assert(eol_vcol < MAXCOL);
+ if (eol_vcol < 0) {
+ // Empty line or whole line before w_leftcol,
+ // with columns before buffer text
+ eol_vcol = wp->w_leftcol - 1;
+ }
+ *vcolp = eol_vcol + (int)(off - off_r);
+ } else {
+ // Empty line or whole line before w_leftcol
+ *vcolp = click_col - start_col + wp->w_leftcol;
}
+ } else if (col_from_screen >= 0) {
+ // Use the virtual column from vcols[], it is accurate also after
+ // concealed characters.
+ *vcolp = col_from_screen;
}
if (col_from_screen == -2) {
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 9fea6442a9..4097b770c9 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -145,6 +145,7 @@ struct TUIData {
} unibi_ext;
char *space_buf;
bool stopped;
+ int seen_error_exit;
int width;
int height;
bool rgb;
@@ -162,6 +163,7 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term)
tui->is_starting = true;
tui->screenshot = NULL;
tui->stopped = false;
+ tui->seen_error_exit = 0;
tui->loop = &main_loop;
kv_init(tui->invalid_regions);
signal_watcher_init(tui->loop, &tui->winch_handle, tui);
@@ -384,8 +386,13 @@ static void terminfo_stop(TUIData *tui)
unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys);
// May restore old title before exiting alternate screen.
tui_set_title(tui, (String)STRING_INIT);
- // Exit alternate screen.
- unibi_out(tui, unibi_exit_ca_mode);
+ // if nvim exited with nonzero status, without indicated this was an
+ // intentional exit (like `:1cquit`), it likely was an internal failure.
+ // Don't clobber the stderr error message in this case.
+ if (ui_client_exit_status == tui->seen_error_exit) {
+ // Exit alternate screen.
+ unibi_out(tui, unibi_exit_ca_mode);
+ }
if (tui->cursor_color_changed) {
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
@@ -443,6 +450,11 @@ static void tui_terminal_stop(TUIData *tui)
terminfo_stop(tui);
}
+void tui_error_exit(TUIData *tui, Integer status)
+{
+ tui->seen_error_exit = (int)status;
+}
+
void tui_stop(TUIData *tui)
{
tui_terminal_stop(tui);
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index 7e5f847039..05964422f3 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -23,6 +23,9 @@ EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
// ID of the ui client channel. If zero, the client is not running.
EXTERN uint64_t ui_client_channel_id INIT(= 0);
+// exit status from embedded nvim process
+EXTERN int ui_client_exit_status INIT(= 0);
+
// TODO(bfredl): the current structure for how tui and ui_client.c communicate is a bit awkward.
// This will be restructured as part of The UI Devirtualization Project.