mirror of
https://github.com/neovim/neovim.git
synced 2026-04-05 07:09:23 +00:00
Merge pull request #9530 from bfredl/pum_float
Implement popupmenu as a floating grid internally to reduce flicker
This commit is contained in:
@@ -23,6 +23,7 @@ set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)
|
||||
set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto)
|
||||
set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua)
|
||||
set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua)
|
||||
set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
|
||||
set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack)
|
||||
set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack)
|
||||
set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c)
|
||||
@@ -269,6 +270,7 @@ add_custom_command(
|
||||
${API_HEADERS}
|
||||
${MSGPACK_RPC_HEADERS}
|
||||
${API_DISPATCH_GENERATOR}
|
||||
${GENERATOR_C_GRAMMAR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua
|
||||
)
|
||||
|
||||
@@ -300,6 +302,7 @@ add_custom_command(
|
||||
${GENERATED_UI_EVENTS_METADATA}
|
||||
DEPENDS
|
||||
${API_UI_EVENTS_GENERATOR}
|
||||
${GENERATOR_C_GRAMMAR}
|
||||
${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h
|
||||
)
|
||||
|
||||
|
||||
@@ -113,6 +113,8 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
|
||||
ui->set_title = remote_ui_set_title;
|
||||
ui->set_icon = remote_ui_set_icon;
|
||||
ui->option_set = remote_ui_option_set;
|
||||
ui->win_scroll_over_start = remote_ui_win_scroll_over_start;
|
||||
ui->win_scroll_over_reset = remote_ui_win_scroll_over_reset;
|
||||
ui->event = remote_ui_event;
|
||||
ui->inspect = remote_ui_inspect;
|
||||
|
||||
@@ -459,7 +461,7 @@ static void remote_ui_put(UI *ui, const char *cell)
|
||||
static void remote_ui_raw_line(UI *ui, Integer grid, Integer row,
|
||||
Integer startcol, Integer endcol,
|
||||
Integer clearcol, Integer clearattr,
|
||||
Boolean wrap, const schar_T *chunk,
|
||||
LineFlags flags, const schar_T *chunk,
|
||||
const sattr_T *attrs)
|
||||
{
|
||||
UIData *data = ui->data;
|
||||
|
||||
@@ -38,6 +38,9 @@ void set_icon(String icon)
|
||||
FUNC_API_SINCE(3);
|
||||
void option_set(String name, Object value)
|
||||
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
|
||||
// Stop event is not exported as such, represented by EOF in the msgpack stream.
|
||||
void stop(void)
|
||||
FUNC_API_NOEXPORT;
|
||||
|
||||
// First revison of the grid protocol, used by default
|
||||
void update_fg(Integer fg)
|
||||
@@ -71,28 +74,39 @@ void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs,
|
||||
Array info)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
|
||||
void grid_resize(Integer grid, Integer width, Integer height)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
void grid_clear(Integer grid)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
void grid_cursor_goto(Integer grid, Integer row, Integer col)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
void grid_line(Integer grid, Integer row, Integer col_start, Array data)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_ONLY;
|
||||
void grid_scroll(Integer grid, Integer top, Integer bot,
|
||||
Integer left, Integer right, Integer rows, Integer cols)
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
|
||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
void grid_destroy(Integer grid)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
|
||||
// For perfomance and simplicity, we use the dense screen representation
|
||||
// in internal code, such as compositor and TUI. The remote_ui module will
|
||||
// translate this in to the public grid_line format.
|
||||
void raw_line(Integer grid, Integer row, Integer startcol,
|
||||
Integer endcol, Integer clearcol, Integer clearattr,
|
||||
LineFlags flags, const schar_T *chunk, const sattr_T *attrs)
|
||||
FUNC_API_NOEXPORT FUNC_API_COMPOSITOR_IMPL;
|
||||
|
||||
void event(char *name, Array args, bool *args_consumed)
|
||||
FUNC_API_NOEXPORT;
|
||||
|
||||
void win_pos(Integer grid, Integer win, Integer startrow,
|
||||
Integer startcol, Integer width, Integer height)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
void win_hide(Integer grid)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
void win_scroll_over_start(void)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
void win_scroll_over_reset(void)
|
||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
||||
|
||||
void popupmenu_show(Array items, Integer selected,
|
||||
Integer row, Integer col, Integer grid)
|
||||
|
||||
@@ -473,6 +473,8 @@ static void insert_enter(InsertState *s)
|
||||
o_lnum = curwin->w_cursor.lnum;
|
||||
}
|
||||
|
||||
pum_check_clear();
|
||||
|
||||
foldUpdateAfterInsert();
|
||||
// When CTRL-C was typed got_int will be set, with the result
|
||||
// that the autocommands won't be executed. When mapped got_int
|
||||
@@ -1447,6 +1449,7 @@ ins_redraw (
|
||||
redrawWinline(curwin, curwin->w_cursor.lnum);
|
||||
}
|
||||
|
||||
pum_check_clear();
|
||||
if (must_redraw) {
|
||||
update_screen(0);
|
||||
} else if (clear_cmdline || redraw_cmdline) {
|
||||
@@ -2490,20 +2493,10 @@ void set_completion(colnr_T startcol, list_T *list)
|
||||
static pumitem_T *compl_match_array = NULL;
|
||||
static int compl_match_arraysize;
|
||||
|
||||
|
||||
/*
|
||||
* Update the screen and when there is any scrolling remove the popup menu.
|
||||
*/
|
||||
static void ins_compl_upd_pum(void)
|
||||
{
|
||||
int h;
|
||||
|
||||
if (compl_match_array != NULL) {
|
||||
h = curwin->w_cline_height;
|
||||
update_screen(0);
|
||||
if (h != curwin->w_cline_height)
|
||||
ins_compl_del_pum();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove any popup menu.
|
||||
@@ -2511,7 +2504,7 @@ static void ins_compl_upd_pum(void)
|
||||
static void ins_compl_del_pum(void)
|
||||
{
|
||||
if (compl_match_array != NULL) {
|
||||
pum_undisplay();
|
||||
pum_undisplay(false);
|
||||
xfree(compl_match_array);
|
||||
compl_match_array = NULL;
|
||||
}
|
||||
@@ -4305,17 +4298,14 @@ ins_compl_next (
|
||||
}
|
||||
|
||||
if (!allow_get_expansion) {
|
||||
/* may undisplay the popup menu first */
|
||||
ins_compl_upd_pum();
|
||||
|
||||
/* redraw to show the user what was inserted */
|
||||
// redraw to show the user what was inserted
|
||||
update_screen(0);
|
||||
|
||||
/* display the updated popup menu */
|
||||
// display the updated popup menu
|
||||
ins_compl_show_pum();
|
||||
|
||||
/* Delete old text to be replaced, since we're still searching and
|
||||
* don't want to match ourselves! */
|
||||
// Delete old text to be replaced, since we're still searching and
|
||||
// don't want to match ourselves!
|
||||
ins_compl_delete();
|
||||
}
|
||||
|
||||
@@ -4862,8 +4852,6 @@ static int ins_complete(int c, bool enable_pum)
|
||||
save_w_leftcol = curwin->w_leftcol;
|
||||
n = ins_compl_next(true, ins_compl_key2count(c), insert_match, false);
|
||||
|
||||
/* may undisplay the popup menu */
|
||||
ins_compl_upd_pum();
|
||||
|
||||
if (n > 1) /* all matches have been found */
|
||||
compl_matches = n;
|
||||
@@ -7144,11 +7132,17 @@ static void ins_reg(void)
|
||||
* message for it. Only call it explicitly. */
|
||||
++no_u_sync;
|
||||
if (regname == '=') {
|
||||
/* Sync undo when evaluating the expression calls setline() or
|
||||
* append(), so that it can be undone separately. */
|
||||
pos_T curpos = curwin->w_cursor;
|
||||
|
||||
// Sync undo when evaluating the expression calls setline() or
|
||||
// append(), so that it can be undone separately.
|
||||
u_sync_once = 2;
|
||||
|
||||
regname = get_expr_register();
|
||||
|
||||
// Cursor may be moved back a column.
|
||||
curwin->w_cursor = curpos;
|
||||
check_cursor();
|
||||
}
|
||||
if (regname == NUL || !valid_yank_reg(regname, false)) {
|
||||
vim_beep(BO_REG);
|
||||
@@ -7939,7 +7933,6 @@ static void ins_mouse(int c)
|
||||
static void ins_mousescroll(int dir)
|
||||
{
|
||||
win_T *const old_curwin = curwin;
|
||||
bool did_scroll = false;
|
||||
pos_T tpos = curwin->w_cursor;
|
||||
|
||||
if (mouse_row >= 0 && mouse_col >= 0) {
|
||||
@@ -7969,7 +7962,6 @@ static void ins_mousescroll(int dir)
|
||||
} else {
|
||||
mouse_scroll_horiz(dir);
|
||||
}
|
||||
did_scroll = true;
|
||||
}
|
||||
|
||||
curwin->w_redr_status = TRUE;
|
||||
@@ -7977,14 +7969,6 @@ static void ins_mousescroll(int dir)
|
||||
curwin = old_curwin;
|
||||
curbuf = curwin->w_buffer;
|
||||
|
||||
/* The popup menu may overlay the window, need to redraw it.
|
||||
* TODO: Would be more efficient to only redraw the windows that are
|
||||
* overlapped by the popup menu. */
|
||||
if (pum_visible() && did_scroll) {
|
||||
redraw_all_later(NOT_VALID);
|
||||
ins_compl_show_pum();
|
||||
}
|
||||
|
||||
if (!equalpos(curwin->w_cursor, tpos)) {
|
||||
start_arrow(&tpos);
|
||||
can_cindent = true;
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/os_unix.h"
|
||||
#include "nvim/path.h"
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/search.h"
|
||||
@@ -6051,7 +6052,12 @@ static int open_cmdwin(void)
|
||||
|
||||
/* Don't execute autocommands while creating the window. */
|
||||
block_autocmds();
|
||||
/* don't use a new tab page */
|
||||
|
||||
// When using completion in Insert mode with <C-R>=<C-F> one can open the
|
||||
// command line window, but we don't want the popup menu then.
|
||||
pum_undisplay(true);
|
||||
|
||||
// don't use a new tab page
|
||||
cmdmod.tab = 0;
|
||||
cmdmod.noswapfile = 1;
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ local c_id = (
|
||||
local c_void = P('void')
|
||||
local c_param_type = (
|
||||
((P('Error') * fill * P('*') * fill) * Cc('error')) +
|
||||
C((P('const ') ^ -1) * (c_id) * (ws ^ 1) * P('*')) +
|
||||
(C(c_id) * (ws ^ 1))
|
||||
)
|
||||
local c_type = (C(c_void) * (ws ^ 1)) + c_param_type
|
||||
@@ -43,6 +44,7 @@ local c_proto = Ct(
|
||||
(fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) *
|
||||
(fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) *
|
||||
(fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) *
|
||||
(fill * Cg((P('FUNC_API_COMPOSITOR_IMPL') * Cc(true)), 'compositor_impl') ^ -1) *
|
||||
fill * P(';')
|
||||
)
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ for i = 1, #events do
|
||||
ev = events[i]
|
||||
assert(ev.return_type == 'void')
|
||||
|
||||
if ev.since == nil then
|
||||
if ev.since == nil and not ev.noexport then
|
||||
print("Ui event "..ev.name.." lacks since field.\n")
|
||||
os.exit(1)
|
||||
end
|
||||
@@ -65,7 +65,7 @@ for i = 1, #events do
|
||||
write_signature(proto_output, ev, 'UI *ui')
|
||||
proto_output:write(';\n')
|
||||
|
||||
if not ev.remote_impl then
|
||||
if not ev.remote_impl and not ev.noexport then
|
||||
remote_output:write('static void remote_ui_'..ev.name)
|
||||
write_signature(remote_output, ev, 'UI *ui')
|
||||
remote_output:write('\n{\n')
|
||||
@@ -74,8 +74,7 @@ for i = 1, #events do
|
||||
remote_output:write('}\n\n')
|
||||
end
|
||||
|
||||
if not ev.bridge_impl then
|
||||
|
||||
if not ev.bridge_impl and not ev.noexport then
|
||||
send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
|
||||
argc = 1
|
||||
for j = 1, #ev.parameters do
|
||||
@@ -138,21 +137,36 @@ for i = 1, #events do
|
||||
call_output:write('\n{\n')
|
||||
if ev.remote_only then
|
||||
write_arglist(call_output, ev, false)
|
||||
call_output:write(' UI_LOG('..ev.name..', 0);\n')
|
||||
call_output:write(' UI_LOG('..ev.name..');\n')
|
||||
call_output:write(' ui_event("'..ev.name..'", args);\n')
|
||||
elseif ev.compositor_impl then
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, '!ui->composed, '..ev.name..', ui', true)
|
||||
call_output:write(";\n")
|
||||
else
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, ev.name, true)
|
||||
write_signature(call_output, ev, 'true, '..ev.name..', ui', true)
|
||||
call_output:write(";\n")
|
||||
end
|
||||
call_output:write("}\n\n")
|
||||
end
|
||||
|
||||
if ev.compositor_impl then
|
||||
call_output:write('void ui_composed_call_'..ev.name)
|
||||
write_signature(call_output, ev, '')
|
||||
call_output:write('\n{\n')
|
||||
call_output:write(' UI_CALL')
|
||||
write_signature(call_output, ev, 'ui->composed, '..ev.name..', ui', true)
|
||||
call_output:write(";\n")
|
||||
call_output:write("}\n\n")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
proto_output:close()
|
||||
call_output:close()
|
||||
remote_output:close()
|
||||
bridge_output:close()
|
||||
|
||||
-- don't expose internal attributes like "impl_name" in public metadata
|
||||
exported_attributes = {'name', 'parameters',
|
||||
@@ -168,7 +182,9 @@ for _,ev in ipairs(events) do
|
||||
p[1] = 'Dictionary'
|
||||
end
|
||||
end
|
||||
exported_events[#exported_events+1] = ev_exported
|
||||
if not ev.noexport then
|
||||
exported_events[#exported_events+1] = ev_exported
|
||||
end
|
||||
end
|
||||
|
||||
packed = mpack.pack(exported_events)
|
||||
|
||||
@@ -194,12 +194,15 @@ EXTERN int compl_cont_status INIT(= 0);
|
||||
EXTERN int cmdmsg_rl INIT(= false); // cmdline is drawn right to left
|
||||
EXTERN int msg_col;
|
||||
EXTERN int msg_row;
|
||||
EXTERN int msg_scrolled; /* Number of screen lines that windows have
|
||||
* scrolled because of printing messages. */
|
||||
EXTERN int msg_scrolled_ign INIT(= FALSE);
|
||||
/* when TRUE don't set need_wait_return in
|
||||
msg_puts_attr() when msg_scrolled is
|
||||
non-zero */
|
||||
EXTERN int msg_scrolled; // Number of screen lines that windows have
|
||||
// scrolled because of printing messages.
|
||||
// when true don't set need_wait_return in msg_puts_attr()
|
||||
// when msg_scrolled is non-zero
|
||||
EXTERN bool msg_scrolled_ign INIT(= false);
|
||||
// Whether the screen is damaged due to scrolling. Sometimes msg_scrolled
|
||||
// is reset before the screen is redrawn, so we need to keep track of this.
|
||||
EXTERN bool msg_did_scroll INIT(= false);
|
||||
|
||||
|
||||
EXTERN char_u *keep_msg INIT(= NULL); /* msg to be shown after redraw */
|
||||
EXTERN int keep_msg_attr INIT(= 0); /* highlight attr for keep_msg */
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef NVIM_GRID_DEFS_H
|
||||
#define NVIM_GRID_DEFS_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "nvim/types.h"
|
||||
@@ -48,8 +50,15 @@ typedef struct {
|
||||
// offsets for the grid relative to the global screen
|
||||
int row_offset;
|
||||
int col_offset;
|
||||
|
||||
// state owned by the compositor.
|
||||
int comp_row;
|
||||
int comp_col;
|
||||
size_t comp_index;
|
||||
bool comp_disabled;
|
||||
} ScreenGrid;
|
||||
|
||||
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0 }
|
||||
#define SCREEN_GRID_INIT { 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, \
|
||||
false }
|
||||
|
||||
#endif // NVIM_GRID_DEFS_H
|
||||
|
||||
@@ -101,6 +101,9 @@ static int get_attr_entry(HlEntry entry)
|
||||
/// When a UI connects, we need to send it the table of highlights used so far.
|
||||
void ui_send_all_hls(UI *ui)
|
||||
{
|
||||
if (!ui->hl_attr_define) {
|
||||
return;
|
||||
}
|
||||
for (size_t i = 1; i < kv_size(attr_entries); i++) {
|
||||
Array inspect = hl_inspect((int)i);
|
||||
ui->hl_attr_define(ui, (Integer)i, kv_A(attr_entries, i).attr,
|
||||
|
||||
@@ -154,6 +154,7 @@ void event_init(void)
|
||||
remote_ui_init();
|
||||
api_vim_init();
|
||||
terminal_init();
|
||||
ui_init();
|
||||
}
|
||||
|
||||
/// @returns false if main_loop could not be closed gracefully
|
||||
|
||||
@@ -3412,7 +3412,9 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname,
|
||||
# endif
|
||||
xfree(name);
|
||||
|
||||
/* pretend screen didn't scroll, need redraw anyway */
|
||||
// pretend screen didn't scroll, need redraw anyway
|
||||
// TODO(bfredl): when doing the message grid refactor,
|
||||
// simplify this special case.
|
||||
msg_scrolled = 0;
|
||||
redraw_all_later(NOT_VALID);
|
||||
}
|
||||
|
||||
@@ -1960,8 +1960,9 @@ int msg_scrollsize(void)
|
||||
*/
|
||||
static void msg_scroll_up(void)
|
||||
{
|
||||
if (msg_scrolled == 0) {
|
||||
if (!msg_did_scroll) {
|
||||
ui_call_win_scroll_over_start();
|
||||
msg_did_scroll = true;
|
||||
}
|
||||
if (dy_flags & DY_MSGSEP) {
|
||||
if (msg_scrolled == 0) {
|
||||
|
||||
@@ -139,7 +139,7 @@ void mch_exit(int r)
|
||||
exiting = true;
|
||||
|
||||
ui_flush();
|
||||
ui_builtin_stop();
|
||||
ui_call_stop();
|
||||
ml_close_all(true); // remove all memfiles
|
||||
|
||||
if (!event_teardown() && r == 0) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "nvim/move.h"
|
||||
#include "nvim/option.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/search.h"
|
||||
#include "nvim/strings.h"
|
||||
#include "nvim/memory.h"
|
||||
@@ -41,8 +42,11 @@ static int pum_row; // top row of pum
|
||||
static int pum_col; // left column of pum
|
||||
|
||||
static bool pum_is_visible = false;
|
||||
static bool pum_is_drawn = false;
|
||||
static bool pum_external = false;
|
||||
|
||||
static ScreenGrid pum_grid = SCREEN_GRID_INIT;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "popupmnu.c.generated.h"
|
||||
#endif
|
||||
@@ -85,6 +89,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
|
||||
// Mark the pum as visible already here,
|
||||
// to avoid that must_redraw is set when 'cursorcolumn' is on.
|
||||
pum_is_visible = true;
|
||||
pum_is_drawn = true;
|
||||
validate_cursor_col();
|
||||
above_row = 0;
|
||||
below_row = cmdline_row;
|
||||
@@ -317,7 +322,7 @@ void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
|
||||
/// Redraw the popup menu, using "pum_first" and "pum_selected".
|
||||
void pum_redraw(void)
|
||||
{
|
||||
int row = pum_row;
|
||||
int row = 0;
|
||||
int col;
|
||||
int attr_norm = win_hl_attr(curwin, HLF_PNI);
|
||||
int attr_select = win_hl_attr(curwin, HLF_PSI);
|
||||
@@ -334,6 +339,37 @@ void pum_redraw(void)
|
||||
int round;
|
||||
int n;
|
||||
|
||||
int grid_width = pum_width;
|
||||
int col_off = 0;
|
||||
bool extra_space = false;
|
||||
if (curwin->w_p_rl) {
|
||||
col_off = pum_width;
|
||||
if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
|
||||
grid_width += 1;
|
||||
extra_space = true;
|
||||
}
|
||||
} else if (pum_col > 0) {
|
||||
grid_width += 1;
|
||||
col_off = 1;
|
||||
extra_space = true;
|
||||
}
|
||||
if (pum_scrollbar > 0) {
|
||||
grid_width++;
|
||||
}
|
||||
|
||||
grid_assign_handle(&pum_grid);
|
||||
bool moved = ui_comp_put_grid(&pum_grid, pum_row, pum_col-col_off,
|
||||
pum_height, grid_width);
|
||||
|
||||
if (!pum_grid.chars
|
||||
|| pum_grid.Rows != pum_height || pum_grid.Columns != grid_width) {
|
||||
grid_alloc(&pum_grid, pum_height, grid_width, !moved, false);
|
||||
ui_call_grid_resize(pum_grid.handle, pum_grid.Columns, pum_grid.Rows);
|
||||
} else if (moved) {
|
||||
grid_invalidate(&pum_grid);
|
||||
}
|
||||
|
||||
|
||||
// Never display more than we have
|
||||
if (pum_first > pum_size - pum_height) {
|
||||
pum_first = pum_size - pum_height;
|
||||
@@ -356,17 +392,17 @@ void pum_redraw(void)
|
||||
screen_puts_line_start(row);
|
||||
|
||||
// prepend a space if there is room
|
||||
if (curwin->w_p_rl) {
|
||||
if (pum_col < curwin->w_wincol + curwin->w_width - 1) {
|
||||
grid_putchar(&default_grid, ' ', row, pum_col + 1, attr);
|
||||
if (extra_space) {
|
||||
if (curwin->w_p_rl) {
|
||||
grid_putchar(&pum_grid, ' ', row, col_off + 1, attr);
|
||||
} else {
|
||||
grid_putchar(&pum_grid, ' ', row, col_off - 1, attr);
|
||||
}
|
||||
} else if (pum_col > 0) {
|
||||
grid_putchar(&default_grid, ' ', row, pum_col - 1, attr);
|
||||
}
|
||||
|
||||
// Display each entry, use two spaces for a Tab.
|
||||
// Do this 3 times: For the main text, kind and extra info
|
||||
col = pum_col;
|
||||
col = col_off;
|
||||
totwidth = 0;
|
||||
|
||||
for (round = 1; round <= 3; ++round) {
|
||||
@@ -423,13 +459,13 @@ void pum_redraw(void)
|
||||
size++;
|
||||
}
|
||||
}
|
||||
grid_puts_len(&default_grid, rt, (int)STRLEN(rt), row,
|
||||
grid_puts_len(&pum_grid, rt, (int)STRLEN(rt), row,
|
||||
col - size + 1, attr);
|
||||
xfree(rt_start);
|
||||
xfree(st);
|
||||
col -= width;
|
||||
} else {
|
||||
grid_puts_len(&default_grid, st, (int)STRLEN(st), row, col, attr);
|
||||
grid_puts_len(&pum_grid, st, (int)STRLEN(st), row, col, attr);
|
||||
xfree(st);
|
||||
col += width;
|
||||
}
|
||||
@@ -440,11 +476,11 @@ void pum_redraw(void)
|
||||
|
||||
// Display two spaces for a Tab.
|
||||
if (curwin->w_p_rl) {
|
||||
grid_puts_len(&default_grid, (char_u *)" ", 2, row, col - 1,
|
||||
grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col - 1,
|
||||
attr);
|
||||
col -= 2;
|
||||
} else {
|
||||
grid_puts_len(&default_grid, (char_u *)" ", 2, row, col, attr);
|
||||
grid_puts_len(&pum_grid, (char_u *)" ", 2, row, col, attr);
|
||||
col += 2;
|
||||
}
|
||||
totwidth += 2;
|
||||
@@ -475,37 +511,37 @@ void pum_redraw(void)
|
||||
}
|
||||
|
||||
if (curwin->w_p_rl) {
|
||||
grid_fill(&default_grid, row, row + 1, pum_col - pum_base_width - n + 1,
|
||||
grid_fill(&pum_grid, row, row + 1, col_off - pum_base_width - n + 1,
|
||||
col + 1, ' ', ' ', attr);
|
||||
col = pum_col - pum_base_width - n + 1;
|
||||
col = col_off - pum_base_width - n + 1;
|
||||
} else {
|
||||
grid_fill(&default_grid, row, row + 1, col,
|
||||
pum_col + pum_base_width + n, ' ', ' ', attr);
|
||||
col = pum_col + pum_base_width + n;
|
||||
grid_fill(&pum_grid, row, row + 1, col,
|
||||
col_off + pum_base_width + n, ' ', ' ', attr);
|
||||
col = pum_base_width + n;
|
||||
}
|
||||
totwidth = pum_base_width + n;
|
||||
}
|
||||
|
||||
if (curwin->w_p_rl) {
|
||||
grid_fill(&default_grid, row, row + 1, pum_col - pum_width + 1, col + 1,
|
||||
grid_fill(&pum_grid, row, row + 1, col_off - pum_width + 1, col + 1,
|
||||
' ', ' ', attr);
|
||||
} else {
|
||||
grid_fill(&default_grid, row, row + 1, col, pum_col + pum_width, ' ', ' ',
|
||||
grid_fill(&pum_grid, row, row + 1, col, col_off + pum_width, ' ', ' ',
|
||||
attr);
|
||||
}
|
||||
|
||||
if (pum_scrollbar > 0) {
|
||||
if (curwin->w_p_rl) {
|
||||
grid_putchar(&default_grid, ' ', row, pum_col - pum_width,
|
||||
grid_putchar(&pum_grid, ' ', row, col_off - pum_width,
|
||||
i >= thumb_pos && i < thumb_pos + thumb_heigth
|
||||
? attr_thumb : attr_scroll);
|
||||
} else {
|
||||
grid_putchar(&default_grid, ' ', row, pum_col + pum_width,
|
||||
grid_putchar(&pum_grid, ' ', row, col_off + pum_width,
|
||||
i >= thumb_pos && i < thumb_pos + thumb_heigth
|
||||
? attr_thumb : attr_scroll);
|
||||
}
|
||||
}
|
||||
grid_puts_line_flush(&default_grid, false);
|
||||
grid_puts_line_flush(&pum_grid, false);
|
||||
row++;
|
||||
}
|
||||
}
|
||||
@@ -696,6 +732,8 @@ static int pum_set_selected(int n, int repeat)
|
||||
|
||||
// Update the screen before drawing the popup menu.
|
||||
// Enable updating the status lines.
|
||||
// TODO(bfredl): can simplify, get rid of the flag munging?
|
||||
// or at least eliminate extra redraw before win_enter()?
|
||||
pum_is_visible = false;
|
||||
update_screen(0);
|
||||
pum_is_visible = true;
|
||||
@@ -725,17 +763,27 @@ static int pum_set_selected(int n, int repeat)
|
||||
}
|
||||
|
||||
/// Undisplay the popup menu (later).
|
||||
void pum_undisplay(void)
|
||||
void pum_undisplay(bool immediate)
|
||||
{
|
||||
pum_is_visible = false;
|
||||
pum_array = NULL;
|
||||
|
||||
if (pum_external) {
|
||||
ui_call_popupmenu_hide();
|
||||
} else {
|
||||
redraw_all_later(SOME_VALID);
|
||||
redraw_tabline = true;
|
||||
status_redraw_all();
|
||||
if (immediate) {
|
||||
pum_check_clear();
|
||||
}
|
||||
}
|
||||
|
||||
void pum_check_clear(void)
|
||||
{
|
||||
if (!pum_is_visible && pum_is_drawn) {
|
||||
if (pum_external) {
|
||||
ui_call_popupmenu_hide();
|
||||
} else {
|
||||
ui_comp_remove_grid(&pum_grid);
|
||||
// TODO(bfredl): consider keeping float grids allocated.
|
||||
grid_free(&pum_grid);
|
||||
}
|
||||
pum_is_drawn = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/terminal.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/undo.h"
|
||||
#include "nvim/version.h"
|
||||
#include "nvim/window.h"
|
||||
@@ -153,6 +154,8 @@ static bool highlights_invalid = false;
|
||||
|
||||
static bool conceal_cursor_used = false;
|
||||
|
||||
static bool floats_invalid = false;
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "screen.c.generated.h"
|
||||
#endif
|
||||
@@ -304,11 +307,15 @@ void update_screen(int type)
|
||||
++display_tick; /* let syntax code know we're in a next round of
|
||||
* display updating */
|
||||
|
||||
/*
|
||||
* if the screen was scrolled up when displaying a message, scroll it down
|
||||
*/
|
||||
if (msg_scrolled) {
|
||||
// Tricky: vim code can reset msg_scrolled behind our back, so need
|
||||
// separate bookkeeping for now.
|
||||
if (msg_did_scroll) {
|
||||
ui_call_win_scroll_over_reset();
|
||||
msg_did_scroll = false;
|
||||
}
|
||||
|
||||
// if the screen was scrolled up when displaying a message, scroll it down
|
||||
if (msg_scrolled) {
|
||||
clear_cmdline = true;
|
||||
if (dy_flags & DY_MSGSEP) {
|
||||
int valid = MAX(Rows - msg_scrollsize(), 0);
|
||||
@@ -453,17 +460,20 @@ void update_screen(int type)
|
||||
|
||||
/* redraw status line after the window to minimize cursor movement */
|
||||
if (wp->w_redr_status) {
|
||||
win_redr_status(wp, true); // any popup menu will be redrawn below
|
||||
win_redr_status(wp);
|
||||
}
|
||||
}
|
||||
send_grid_resize = false;
|
||||
highlights_invalid = false;
|
||||
|
||||
end_search_hl();
|
||||
// May need to redraw the popup menu.
|
||||
if (pum_drawn()) {
|
||||
if (pum_drawn() && floats_invalid) {
|
||||
pum_redraw();
|
||||
}
|
||||
|
||||
send_grid_resize = false;
|
||||
highlights_invalid = false;
|
||||
floats_invalid = false;
|
||||
|
||||
/* Reset b_mod_set flags. Going through all windows is probably faster
|
||||
* than going through all buffers (there could be many buffers). */
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
@@ -4287,7 +4297,7 @@ win_line (
|
||||
/// screen positions.
|
||||
static void screen_adjust_grid(ScreenGrid **grid, int *row_off, int *col_off)
|
||||
{
|
||||
if (!ui_is_external(kUIMultigrid) && *grid != &default_grid) {
|
||||
if (!(*grid)->chars && *grid != &default_grid) {
|
||||
*row_off += (*grid)->row_offset;
|
||||
*col_off += (*grid)->col_offset;
|
||||
*grid = &default_grid;
|
||||
@@ -4537,7 +4547,7 @@ void redraw_statuslines(void)
|
||||
{
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
if (wp->w_redr_status) {
|
||||
win_redr_status(wp, false);
|
||||
win_redr_status(wp);
|
||||
}
|
||||
}
|
||||
if (redraw_tabline)
|
||||
@@ -4808,7 +4818,7 @@ win_redr_status_matches (
|
||||
/// If inversion is possible we use it. Else '=' characters are used.
|
||||
/// If "ignore_pum" is true, also redraw statusline when the popup menu is
|
||||
/// displayed.
|
||||
static void win_redr_status(win_T *wp, int ignore_pum)
|
||||
static void win_redr_status(win_T *wp)
|
||||
{
|
||||
int row;
|
||||
char_u *p;
|
||||
@@ -4831,7 +4841,7 @@ static void win_redr_status(win_T *wp, int ignore_pum)
|
||||
if (wp->w_status_height == 0) {
|
||||
// no status line, can only be last window
|
||||
redraw_cmdline = true;
|
||||
} else if (!redrawing() || (!ignore_pum && pum_drawn())) {
|
||||
} else if (!redrawing()) {
|
||||
// Don't redraw right now, do it later. Don't update status line when
|
||||
// popup menu is visible and may be drawn over it
|
||||
wp->w_redr_status = true;
|
||||
@@ -5310,7 +5320,7 @@ void grid_puts(ScreenGrid *grid, char_u *text, int row, int col, int attr)
|
||||
}
|
||||
|
||||
static int put_dirty_row = -1;
|
||||
static int put_dirty_first = -1;
|
||||
static int put_dirty_first = INT_MAX;
|
||||
static int put_dirty_last = 0;
|
||||
|
||||
/// Start a group of screen_puts_len calls that builds a single screen line.
|
||||
@@ -5341,8 +5351,6 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
|
||||
int prev_c = 0; /* previous Arabic character */
|
||||
int pc, nc, nc1;
|
||||
int pcc[MAX_MCO];
|
||||
int force_redraw_this;
|
||||
int force_redraw_next = FALSE;
|
||||
int need_redraw;
|
||||
bool do_flush = false;
|
||||
|
||||
@@ -5365,16 +5373,10 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
|
||||
|
||||
/* When drawing over the right halve of a double-wide char clear out the
|
||||
* left halve. Only needed in a terminal. */
|
||||
if (col > 0 && col < grid->Columns && grid_fix_col(grid, col, row) != col) {
|
||||
schar_from_ascii(grid->chars[off - 1], ' ');
|
||||
grid->attrs[off - 1] = 0;
|
||||
if (grid != &default_grid && col == 0 && grid_invalid_row(grid, row)) {
|
||||
// redraw the previous cell, make it empty
|
||||
if (put_dirty_first == -1) {
|
||||
put_dirty_first = col-1;
|
||||
}
|
||||
put_dirty_last = col+1;
|
||||
// force the cell at "col" to be redrawn
|
||||
force_redraw_next = true;
|
||||
put_dirty_first = -1;
|
||||
put_dirty_last = MAX(put_dirty_last, 1);
|
||||
}
|
||||
|
||||
max_off = grid->line_offset[row] + grid->Columns;
|
||||
@@ -5422,15 +5424,12 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
|
||||
schar_from_cc(buf, u8c, u8cc);
|
||||
|
||||
|
||||
force_redraw_this = force_redraw_next;
|
||||
force_redraw_next = FALSE;
|
||||
|
||||
need_redraw = schar_cmp(grid->chars[off], buf)
|
||||
|| (mbyte_cells == 2 && grid->chars[off + 1][0] != 0)
|
||||
|| grid->attrs[off] != attr
|
||||
|| exmode_active;
|
||||
|
||||
if (need_redraw || force_redraw_this) {
|
||||
if (need_redraw) {
|
||||
// When at the end of the text and overwriting a two-cell
|
||||
// character with a one-cell character, need to clear the next
|
||||
// cell. Also when overwriting the left halve of a two-cell char
|
||||
@@ -5454,10 +5453,8 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
|
||||
grid->chars[off + 1][0] = 0;
|
||||
grid->attrs[off + 1] = attr;
|
||||
}
|
||||
if (put_dirty_first == -1) {
|
||||
put_dirty_first = col;
|
||||
}
|
||||
put_dirty_last = col+mbyte_cells;
|
||||
put_dirty_first = MIN(put_dirty_first, col);
|
||||
put_dirty_last = MAX(put_dirty_last, col+mbyte_cells);
|
||||
}
|
||||
|
||||
off += mbyte_cells;
|
||||
@@ -5485,14 +5482,14 @@ void grid_puts_len(ScreenGrid *grid, char_u *text, int textlen, int row,
|
||||
void grid_puts_line_flush(ScreenGrid *grid, bool set_cursor)
|
||||
{
|
||||
assert(put_dirty_row != -1);
|
||||
if (put_dirty_first != -1) {
|
||||
if (put_dirty_first < put_dirty_last) {
|
||||
if (set_cursor) {
|
||||
ui_grid_cursor_goto(grid->handle, put_dirty_row,
|
||||
MIN(put_dirty_last, grid->Columns-1));
|
||||
}
|
||||
ui_line(grid, put_dirty_row, put_dirty_first, put_dirty_last,
|
||||
put_dirty_last, 0, false);
|
||||
put_dirty_first = -1;
|
||||
put_dirty_first = INT_MAX;
|
||||
put_dirty_last = 0;
|
||||
}
|
||||
put_dirty_row = -1;
|
||||
@@ -5864,9 +5861,7 @@ void grid_fill(ScreenGrid *grid, int start_row, int end_row, int start_col,
|
||||
if (dirty_last > dirty_first) {
|
||||
// TODO(bfredl): support a cleared suffix even with a batched line?
|
||||
if (put_dirty_row == row) {
|
||||
if (put_dirty_first == -1) {
|
||||
put_dirty_first = dirty_first;
|
||||
}
|
||||
put_dirty_first = MIN(put_dirty_first, dirty_first);
|
||||
put_dirty_last = MAX(put_dirty_last, dirty_last);
|
||||
} else {
|
||||
int last = c2 != ' ' ? dirty_last : dirty_first + (c1 != ' ');
|
||||
@@ -5952,7 +5947,7 @@ void win_grid_alloc(win_T *wp)
|
||||
|| grid->Rows != rows
|
||||
|| grid->Columns != cols) {
|
||||
if (want_allocation) {
|
||||
grid_alloc(grid, rows, cols, true);
|
||||
grid_alloc(grid, rows, cols, true, true);
|
||||
} else {
|
||||
// Single grid mode, all rendering will be redirected to default_grid.
|
||||
// Only keep track of the size and offset of the window.
|
||||
@@ -6030,6 +6025,10 @@ retry:
|
||||
*/
|
||||
++RedrawingDisabled;
|
||||
|
||||
// win_new_shellsize will recompute floats posititon, but tell the
|
||||
// compositor to now redraw them yet
|
||||
ui_comp_invalidate_screen();
|
||||
|
||||
win_new_shellsize(); /* fit the windows in the new sized shell */
|
||||
|
||||
comp_col(); /* recompute columns for shown command and ruler */
|
||||
@@ -6044,7 +6043,7 @@ retry:
|
||||
// Continuing with the old arrays may result in a crash, because the
|
||||
// size is wrong.
|
||||
|
||||
grid_alloc(&default_grid, Rows, Columns, !doclear);
|
||||
grid_alloc(&default_grid, Rows, Columns, !doclear, true);
|
||||
StlClickDefinition *new_tab_page_click_defs = xcalloc(
|
||||
(size_t)Columns, sizeof(*new_tab_page_click_defs));
|
||||
|
||||
@@ -6078,7 +6077,7 @@ retry:
|
||||
}
|
||||
}
|
||||
|
||||
void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
|
||||
void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy, bool valid)
|
||||
{
|
||||
int new_row;
|
||||
ScreenGrid new = *grid;
|
||||
@@ -6096,7 +6095,7 @@ void grid_alloc(ScreenGrid *grid, int rows, int columns, bool copy)
|
||||
new.line_offset[new_row] = new_row * new.Columns;
|
||||
new.line_wraps[new_row] = false;
|
||||
|
||||
grid_clear_line(&new, new.line_offset[new_row], columns, true);
|
||||
grid_clear_line(&new, new.line_offset[new_row], columns, valid);
|
||||
|
||||
if (copy) {
|
||||
// If the screen is not going to be cleared, copy as much as
|
||||
@@ -6188,6 +6187,7 @@ static void screenclear2(void)
|
||||
default_grid.line_wraps[i] = false;
|
||||
}
|
||||
|
||||
floats_invalid = true;
|
||||
ui_call_grid_clear(1); // clear the display
|
||||
clear_cmdline = false;
|
||||
mode_displayed = false;
|
||||
@@ -6218,11 +6218,17 @@ static void grid_clear_line(ScreenGrid *grid, unsigned off, int width,
|
||||
(void)memset(grid->attrs + off, fill, (size_t)width * sizeof(sattr_T));
|
||||
}
|
||||
|
||||
static void grid_invalidate(ScreenGrid *grid)
|
||||
void grid_invalidate(ScreenGrid *grid)
|
||||
{
|
||||
(void)memset(grid->attrs, -1, grid->Rows * grid->Columns * sizeof(sattr_T));
|
||||
}
|
||||
|
||||
bool grid_invalid_row(ScreenGrid *grid, int row)
|
||||
{
|
||||
return grid->attrs[grid->line_offset[row]] < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// Copy part of a grid line for vertically split window.
|
||||
static void linecopy(ScreenGrid *grid, int to, int from, int col, int width)
|
||||
@@ -6916,11 +6922,6 @@ void showruler(int always)
|
||||
{
|
||||
if (!always && !redrawing())
|
||||
return;
|
||||
if (pum_drawn()) {
|
||||
// Don't redraw right now, do it later.
|
||||
curwin->w_redr_status = true;
|
||||
return;
|
||||
}
|
||||
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
|
||||
redraw_custom_statusline(curwin);
|
||||
} else {
|
||||
@@ -6955,10 +6956,6 @@ static void win_redr_ruler(win_T *wp, int always)
|
||||
if (wp == lastwin && lastwin->w_status_height == 0)
|
||||
if (edit_submode != NULL)
|
||||
return;
|
||||
// Don't draw the ruler when the popup menu is visible, it may overlap.
|
||||
if (pum_drawn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (*p_ruf) {
|
||||
int save_called_emsg = called_emsg;
|
||||
@@ -7201,7 +7198,6 @@ void screen_resize(int width, int height)
|
||||
} else {
|
||||
update_topline();
|
||||
if (pum_drawn()) {
|
||||
redraw_later(NOT_VALID);
|
||||
ins_compl_show_pum();
|
||||
}
|
||||
update_screen(NOT_VALID);
|
||||
|
||||
@@ -1273,7 +1273,7 @@ static void tui_option_set(UI *ui, String name, Object value)
|
||||
|
||||
static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
|
||||
Integer endcol, Integer clearcol, Integer clearattr,
|
||||
Boolean wrap, const schar_T *chunk,
|
||||
LineFlags flags, const schar_T *chunk,
|
||||
const sattr_T *attrs)
|
||||
{
|
||||
TUIData *data = ui->data;
|
||||
@@ -1295,7 +1295,8 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol,
|
||||
(int)clearattr);
|
||||
}
|
||||
|
||||
if (wrap && ui->width == grid->width && linerow + 1 < grid->height) {
|
||||
if (flags & kLineFlagWrap && ui->width == grid->width
|
||||
&& linerow + 1 < grid->height) {
|
||||
// Only do line wrapping if the grid width is equal to the terminal
|
||||
// width and the line continuation is within the grid.
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "nvim/popupmnu.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/highlight.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/window.h"
|
||||
#include "nvim/cursor_shape.h"
|
||||
#ifdef FEAT_TUI
|
||||
@@ -60,11 +61,11 @@ static bool pending_mode_update = false;
|
||||
static handle_T cursor_grid_handle = DEFAULT_GRID_HANDLE;
|
||||
|
||||
#if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL
|
||||
# define UI_LOG(funname, ...)
|
||||
# define UI_LOG(funname)
|
||||
#else
|
||||
static size_t uilog_seen = 0;
|
||||
static char uilog_last_event[1024] = { 0 };
|
||||
# define UI_LOG(funname, ...) \
|
||||
# define UI_LOG(funname) \
|
||||
do { \
|
||||
if (strequal(uilog_last_event, STR(funname))) { \
|
||||
uilog_seen++; \
|
||||
@@ -80,42 +81,34 @@ static char uilog_last_event[1024] = { 0 };
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
// UI_CALL invokes a function on all registered UI instances. The functions can
|
||||
// have 0-10 arguments (configurable by SELECT_NTH).
|
||||
//
|
||||
// See http://stackoverflow.com/a/11172679 for how it works.
|
||||
#ifdef _MSC_VER
|
||||
# define UI_CALL(funname, ...) \
|
||||
do { \
|
||||
UI_LOG(funname, 0); \
|
||||
for (size_t i = 0; i < ui_count; i++) { \
|
||||
UI *ui = uis[i]; \
|
||||
UI_CALL_MORE(funname, __VA_ARGS__); \
|
||||
// UI_CALL invokes a function on all registered UI instances.
|
||||
// This is called by code generated by generators/gen_api_ui_events.lua
|
||||
// C code should use ui_call_{funname} instead.
|
||||
# define UI_CALL(cond, funname, ...) \
|
||||
do { \
|
||||
bool any_call = false; \
|
||||
for (size_t i = 0; i < ui_count; i++) { \
|
||||
UI *ui = uis[i]; \
|
||||
if (ui->funname && (cond)) { \
|
||||
ui->funname(__VA_ARGS__); \
|
||||
any_call = true; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
# define UI_CALL(...) \
|
||||
do { \
|
||||
UI_LOG(__VA_ARGS__, 0); \
|
||||
for (size_t i = 0; i < ui_count; i++) { \
|
||||
UI *ui = uis[i]; \
|
||||
UI_CALL_HELPER(CNT(__VA_ARGS__), __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
#endif
|
||||
#define CNT(...) SELECT_NTH(__VA_ARGS__, MORE, MORE, MORE, MORE, MORE, \
|
||||
MORE, MORE, MORE, MORE, ZERO, ignore)
|
||||
#define SELECT_NTH(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11
|
||||
#define UI_CALL_HELPER(c, ...) UI_CALL_HELPER2(c, __VA_ARGS__)
|
||||
// Resolves to UI_CALL_MORE or UI_CALL_ZERO.
|
||||
#define UI_CALL_HELPER2(c, ...) UI_CALL_##c(__VA_ARGS__)
|
||||
#define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__)
|
||||
#define UI_CALL_ZERO(method) if (ui->method) ui->method(ui)
|
||||
} \
|
||||
if (any_call) { \
|
||||
UI_LOG(funname); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "ui_events_call.generated.h"
|
||||
#endif
|
||||
|
||||
void ui_init(void)
|
||||
{
|
||||
default_grid.handle = 1;
|
||||
ui_comp_init();
|
||||
}
|
||||
|
||||
void ui_builtin_start(void)
|
||||
{
|
||||
#ifdef FEAT_TUI
|
||||
@@ -135,17 +128,12 @@ void ui_builtin_start(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void ui_builtin_stop(void)
|
||||
{
|
||||
UI_CALL(stop);
|
||||
}
|
||||
|
||||
bool ui_rgb_attached(void)
|
||||
{
|
||||
if (!headless_mode && p_tgc) {
|
||||
return true;
|
||||
}
|
||||
for (size_t i = 0; i < ui_count; i++) {
|
||||
for (size_t i = 1; i < ui_count; i++) {
|
||||
if (uis[i]->rgb) {
|
||||
return true;
|
||||
}
|
||||
@@ -155,13 +143,13 @@ bool ui_rgb_attached(void)
|
||||
|
||||
bool ui_active(void)
|
||||
{
|
||||
return ui_count != 0;
|
||||
return ui_count > 1;
|
||||
}
|
||||
|
||||
void ui_event(char *name, Array args)
|
||||
{
|
||||
bool args_consumed = false;
|
||||
UI_CALL(event, name, args, &args_consumed);
|
||||
ui_call_event(name, args, &args_consumed);
|
||||
if (!args_consumed) {
|
||||
api_free_array(args);
|
||||
}
|
||||
@@ -257,6 +245,9 @@ void ui_attach_impl(UI *ui)
|
||||
if (ui_count == MAX_UI_COUNT) {
|
||||
abort();
|
||||
}
|
||||
if (!ui->ui_ext[kUIMultigrid]) {
|
||||
ui_comp_attach(ui);
|
||||
}
|
||||
|
||||
uis[ui_count++] = ui;
|
||||
ui_refresh_options();
|
||||
@@ -303,6 +294,10 @@ void ui_detach_impl(UI *ui)
|
||||
&& !exiting) {
|
||||
ui_schedule_refresh();
|
||||
}
|
||||
|
||||
if (!ui->ui_ext[kUIMultigrid]) {
|
||||
ui_comp_detach(ui);
|
||||
}
|
||||
}
|
||||
|
||||
void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
|
||||
@@ -320,11 +315,17 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active)
|
||||
void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol,
|
||||
int clearattr, bool wrap)
|
||||
{
|
||||
LineFlags flags = wrap ? kLineFlagWrap : 0;
|
||||
if (startcol == -1) {
|
||||
startcol = 0;
|
||||
flags |= kLineFlagInvalid;
|
||||
}
|
||||
|
||||
size_t off = grid->line_offset[row] + (size_t)startcol;
|
||||
|
||||
UI_CALL(raw_line, grid->handle, row, startcol, endcol, clearcol, clearattr,
|
||||
wrap, (const schar_T *)grid->chars + off,
|
||||
(const sattr_T *)grid->attrs + off);
|
||||
ui_call_raw_line(grid->handle, row, startcol, endcol, clearcol, clearattr,
|
||||
flags, (const schar_T *)grid->chars + off,
|
||||
(const sattr_T *)grid->attrs + off);
|
||||
|
||||
if (p_wd) { // 'writedelay': flush & delay each time.
|
||||
int old_row = cursor_row, old_col = cursor_col;
|
||||
@@ -421,7 +422,7 @@ bool ui_is_external(UIExtension widget)
|
||||
Array ui_array(void)
|
||||
{
|
||||
Array all_uis = ARRAY_DICT_INIT;
|
||||
for (size_t i = 0; i < ui_count; i++) {
|
||||
for (size_t i = 1; i < ui_count; i++) {
|
||||
UI *ui = uis[i];
|
||||
Dictionary info = ARRAY_DICT_INIT;
|
||||
PUT(info, "width", INTEGER_OBJ(ui->width));
|
||||
|
||||
@@ -34,8 +34,16 @@ EXTERN const char *ui_ext_names[] INIT(= {
|
||||
|
||||
typedef struct ui_t UI;
|
||||
|
||||
enum {
|
||||
kLineFlagWrap = 1,
|
||||
kLineFlagInvalid = 2,
|
||||
};
|
||||
|
||||
typedef int LineFlags;
|
||||
|
||||
struct ui_t {
|
||||
bool rgb;
|
||||
bool composed;
|
||||
bool ui_ext[kUIExtCount]; ///< Externalized widgets
|
||||
int width, height;
|
||||
void *data;
|
||||
@@ -44,14 +52,6 @@ struct ui_t {
|
||||
# include "ui_events.generated.h"
|
||||
#endif
|
||||
|
||||
// For perfomance and simplicity, we use the dense screen representation
|
||||
// in the bridge and the TUI. The remote_ui module will translate this
|
||||
// in to the public grid_line format.
|
||||
void (*raw_line)(UI *ui, Integer grid, Integer row, Integer startcol,
|
||||
Integer endcol, Integer clearcol, Integer clearattr,
|
||||
Boolean wrap, const schar_T *chunk, const sattr_T *attrs);
|
||||
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
|
||||
void (*stop)(UI *ui);
|
||||
void (*inspect)(UI *ui, Dictionary *info);
|
||||
};
|
||||
|
||||
|
||||
@@ -153,14 +153,14 @@ static void ui_bridge_raw_line_event(void **argv)
|
||||
UI *ui = UI(argv[0]);
|
||||
ui->raw_line(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), PTR2INT(argv[3]),
|
||||
PTR2INT(argv[4]), PTR2INT(argv[5]), PTR2INT(argv[6]),
|
||||
PTR2INT(argv[7]), argv[8], argv[9]);
|
||||
(LineFlags)PTR2INT(argv[7]), argv[8], argv[9]);
|
||||
xfree(argv[8]);
|
||||
xfree(argv[9]);
|
||||
}
|
||||
static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
|
||||
Integer startcol, Integer endcol,
|
||||
Integer clearcol, Integer clearattr,
|
||||
Boolean wrap, const schar_T *chunk,
|
||||
LineFlags flags, const schar_T *chunk,
|
||||
const sattr_T *attrs)
|
||||
{
|
||||
size_t ncol = (size_t)(endcol-startcol);
|
||||
@@ -168,7 +168,7 @@ static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row,
|
||||
sattr_T *hl = xmemdup(attrs, ncol * sizeof(sattr_T));
|
||||
UI_BRIDGE_CALL(ui, raw_line, 10, ui, INT2PTR(grid), INT2PTR(row),
|
||||
INT2PTR(startcol), INT2PTR(endcol), INT2PTR(clearcol),
|
||||
INT2PTR(clearattr), INT2PTR(wrap), c, hl);
|
||||
INT2PTR(clearattr), INT2PTR(flags), c, hl);
|
||||
}
|
||||
|
||||
static void ui_bridge_suspend(UI *b)
|
||||
|
||||
411
src/nvim/ui_compositor.c
Normal file
411
src/nvim/ui_compositor.c
Normal file
@@ -0,0 +1,411 @@
|
||||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
||||
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||
|
||||
// Compositor: merge floating grids with the main grid for display in
|
||||
// TUI and non-multigrid UIs.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "nvim/lib/kvec.h"
|
||||
#include "nvim/log.h"
|
||||
#include "nvim/main.h"
|
||||
#include "nvim/ascii.h"
|
||||
#include "nvim/vim.h"
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/ui_compositor.h"
|
||||
#include "nvim/ugrid.h"
|
||||
#include "nvim/screen.h"
|
||||
#include "nvim/syntax.h"
|
||||
#include "nvim/api/private/helpers.h"
|
||||
#include "nvim/os/os.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "ui_compositor.c.generated.h"
|
||||
#endif
|
||||
|
||||
static UI *compositor = NULL;
|
||||
static int composed_uis = 0;
|
||||
kvec_t(ScreenGrid *) layers = KV_INITIAL_VALUE;
|
||||
|
||||
static size_t bufsize = 0;
|
||||
static schar_T *linebuf;
|
||||
static sattr_T *attrbuf;
|
||||
|
||||
#ifndef NDEBUG
|
||||
static int chk_width = 0, chk_height = 0;
|
||||
#endif
|
||||
|
||||
static ScreenGrid *curgrid;
|
||||
|
||||
static bool valid_screen = true;
|
||||
static bool msg_scroll_mode = false;
|
||||
static int msg_first_invalid = 0;
|
||||
|
||||
void ui_comp_init(void)
|
||||
{
|
||||
if (compositor != NULL) {
|
||||
return;
|
||||
}
|
||||
compositor = xcalloc(1, sizeof(UI));
|
||||
|
||||
compositor->rgb = true;
|
||||
compositor->grid_resize = ui_comp_grid_resize;
|
||||
compositor->grid_clear = ui_comp_grid_clear;
|
||||
compositor->grid_scroll = ui_comp_grid_scroll;
|
||||
compositor->grid_cursor_goto = ui_comp_grid_cursor_goto;
|
||||
compositor->raw_line = ui_comp_raw_line;
|
||||
compositor->win_scroll_over_start = ui_comp_win_scroll_over_start;
|
||||
compositor->win_scroll_over_reset = ui_comp_win_scroll_over_reset;
|
||||
|
||||
// Be unopinionated: will be attached together with a "real" ui anyway
|
||||
compositor->width = INT_MAX;
|
||||
compositor->height = INT_MAX;
|
||||
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
|
||||
compositor->ui_ext[i] = true;
|
||||
}
|
||||
|
||||
// TODO(bfredl): this will be more complicated if we implement
|
||||
// hlstate per UI (i e reduce hl ids for non-hlstate UIs)
|
||||
compositor->ui_ext[kUIHlState] = false;
|
||||
|
||||
kv_push(layers, &default_grid);
|
||||
curgrid = &default_grid;
|
||||
|
||||
ui_attach_impl(compositor);
|
||||
}
|
||||
|
||||
|
||||
void ui_comp_attach(UI *ui)
|
||||
{
|
||||
composed_uis++;
|
||||
ui->composed = true;
|
||||
}
|
||||
|
||||
void ui_comp_detach(UI *ui)
|
||||
{
|
||||
composed_uis--;
|
||||
if (composed_uis == 0) {
|
||||
xfree(linebuf);
|
||||
xfree(attrbuf);
|
||||
linebuf = NULL;
|
||||
attrbuf = NULL;
|
||||
bufsize = 0;
|
||||
}
|
||||
ui->composed = false;
|
||||
}
|
||||
|
||||
bool ui_comp_should_draw(void)
|
||||
{
|
||||
return composed_uis != 0 && valid_screen;
|
||||
}
|
||||
|
||||
/// TODO(bfredl): later on the compositor should just use win_float_pos events,
|
||||
/// though that will require slight event order adjustment: emit the win_pos
|
||||
/// events in the beginning of update_screen(0), rather than in ui_flush()
|
||||
bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width)
|
||||
{
|
||||
if (grid->comp_index != 0) {
|
||||
bool moved = (row != grid->comp_row) || (col != grid->comp_col);
|
||||
if (ui_comp_should_draw()) {
|
||||
// Redraw the area covered by the old position, and is not covered
|
||||
// by the new position. Disable the grid so that compose_area() will not
|
||||
// use it.
|
||||
grid->comp_disabled = true;
|
||||
compose_area(grid->comp_row, row,
|
||||
grid->comp_col, grid->comp_col + grid->Columns);
|
||||
if (grid->comp_col < col) {
|
||||
compose_area(MAX(row, grid->comp_row),
|
||||
MIN(row+height, grid->comp_row+grid->Rows),
|
||||
grid->comp_col, col);
|
||||
}
|
||||
if (col+width < grid->comp_col+grid->Columns) {
|
||||
compose_area(MAX(row, grid->comp_row),
|
||||
MIN(row+height, grid->comp_row+grid->Rows),
|
||||
col+width, grid->comp_col+grid->Columns);
|
||||
}
|
||||
compose_area(row+height, grid->comp_row+grid->Rows,
|
||||
grid->comp_col, grid->comp_col + grid->Columns);
|
||||
grid->comp_disabled = false;
|
||||
}
|
||||
grid->comp_row = row;
|
||||
grid->comp_col = col;
|
||||
return moved;
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
for (size_t i = 0; i < kv_size(layers); i++) {
|
||||
if (kv_A(layers, i) == grid) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// not found: new grid
|
||||
kv_push(layers, grid);
|
||||
grid->comp_row = row;
|
||||
grid->comp_col = col;
|
||||
grid->comp_index = kv_size(layers)-1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ui_comp_remove_grid(ScreenGrid *grid)
|
||||
{
|
||||
assert(grid != &default_grid);
|
||||
if (grid->comp_index == 0) {
|
||||
// grid wasn't present
|
||||
return;
|
||||
}
|
||||
|
||||
if (curgrid == grid) {
|
||||
curgrid = &default_grid;
|
||||
}
|
||||
|
||||
for (size_t i = grid->comp_index; i < kv_size(layers)-1; i++) {
|
||||
kv_A(layers, i) = kv_A(layers, i+1);
|
||||
kv_A(layers, i)->comp_index = i;
|
||||
}
|
||||
(void)kv_pop(layers);
|
||||
grid->comp_index = 0;
|
||||
|
||||
if (ui_comp_should_draw()) {
|
||||
// inefficent: only draw up to grid->comp_index
|
||||
compose_area(grid->comp_row, grid->comp_row+grid->Rows,
|
||||
grid->comp_col, grid->comp_col+grid->Columns);
|
||||
}
|
||||
}
|
||||
|
||||
bool ui_comp_set_grid(handle_T handle)
|
||||
{
|
||||
if (curgrid->handle == handle) {
|
||||
return true;
|
||||
}
|
||||
ScreenGrid *grid = NULL;
|
||||
for (size_t i = 0; i < kv_size(layers); i++) {
|
||||
if (kv_A(layers, i)->handle == handle) {
|
||||
grid = kv_A(layers, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (grid != NULL) {
|
||||
curgrid = grid;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle,
|
||||
Integer r, Integer c)
|
||||
{
|
||||
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) {
|
||||
return;
|
||||
}
|
||||
int cursor_row = curgrid->comp_row+(int)r;
|
||||
int cursor_col = curgrid->comp_col+(int)c;
|
||||
|
||||
if (cursor_col >= default_grid.Columns || cursor_row >= default_grid.Rows) {
|
||||
// TODO(bfredl): this happens with 'writedelay', refactor?
|
||||
// abort();
|
||||
return;
|
||||
}
|
||||
ui_composed_call_grid_cursor_goto(1, cursor_row, cursor_col);
|
||||
}
|
||||
|
||||
|
||||
/// Baseline implementation. This is always correct, but we can sometimes
|
||||
/// do something more efficient (where efficiency means smaller deltas to
|
||||
/// the downstream UI.)
|
||||
static void compose_line(Integer row, Integer startcol, Integer endcol,
|
||||
LineFlags flags)
|
||||
{
|
||||
// in case we start on the right half of a double-width char, we need to
|
||||
// check the left half. But skip it in output if it wasn't doublewidth.
|
||||
int skip = 0;
|
||||
if (startcol > 0 && (flags & kLineFlagInvalid)) {
|
||||
startcol--;
|
||||
skip = 1;
|
||||
}
|
||||
|
||||
int col = (int)startcol;
|
||||
ScreenGrid *grid = NULL;
|
||||
|
||||
while (col < endcol) {
|
||||
int until = 0;
|
||||
for (size_t i = 0; i < kv_size(layers); i++) {
|
||||
ScreenGrid *g = kv_A(layers, i);
|
||||
if (g->comp_row > row || row >= g->comp_row + g->Rows
|
||||
|| g->comp_disabled) {
|
||||
continue;
|
||||
}
|
||||
if (g->comp_col <= col && col < g->comp_col+g->Columns) {
|
||||
grid = g;
|
||||
until = g->comp_col+g->Columns;
|
||||
} else if (g->comp_col > col) {
|
||||
until = MIN(until, g->comp_col);
|
||||
}
|
||||
}
|
||||
until = MIN(until, (int)endcol);
|
||||
|
||||
assert(grid != NULL);
|
||||
assert(until > col);
|
||||
assert(until <= default_grid.Columns);
|
||||
size_t n = (size_t)(until-col);
|
||||
size_t off = grid->line_offset[row-grid->comp_row]
|
||||
+ (size_t)(col-grid->comp_col);
|
||||
memcpy(linebuf+(col-startcol), grid->chars+off, n * sizeof(*linebuf));
|
||||
memcpy(attrbuf+(col-startcol), grid->attrs+off, n * sizeof(*attrbuf));
|
||||
|
||||
// Tricky: if overlap caused a doublewidth char to get cut-off, must
|
||||
// replace the visible half with a space.
|
||||
if (linebuf[col-startcol][0] == NUL) {
|
||||
linebuf[col-startcol][0] = ' ';
|
||||
linebuf[col-startcol][1] = NUL;
|
||||
} else if (n > 1 && linebuf[col-startcol+1][0] == NUL) {
|
||||
skip = 0;
|
||||
}
|
||||
if (grid->comp_col+grid->Columns > until
|
||||
&& grid->chars[off+n][0] == NUL) {
|
||||
linebuf[until-1-startcol][0] = ' ';
|
||||
linebuf[until-1-startcol][1] = '\0';
|
||||
if (col == startcol && n == 1) {
|
||||
skip = 0;
|
||||
}
|
||||
}
|
||||
col = until;
|
||||
}
|
||||
assert(endcol <= chk_width);
|
||||
assert(row < chk_height);
|
||||
|
||||
if (!(grid && grid == &default_grid)) {
|
||||
// TODO(bfredl): too conservative, need check
|
||||
// grid->line_wraps if grid->Width == Width
|
||||
flags = flags & ~kLineFlagWrap;
|
||||
}
|
||||
|
||||
ui_composed_call_raw_line(1, row, startcol+skip, endcol, endcol, 0, flags,
|
||||
(const schar_T *)linebuf+skip,
|
||||
(const sattr_T *)attrbuf+skip);
|
||||
}
|
||||
|
||||
static void compose_area(Integer startrow, Integer endrow,
|
||||
Integer startcol, Integer endcol)
|
||||
{
|
||||
endrow = MIN(endrow, default_grid.Rows);
|
||||
endcol = MIN(endcol, default_grid.Columns);
|
||||
for (int r = (int)startrow; r < endrow; r++) {
|
||||
compose_line(r, startcol, endcol, kLineFlagInvalid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ui_comp_raw_line(UI *ui, Integer grid, Integer row,
|
||||
Integer startcol, Integer endcol,
|
||||
Integer clearcol, Integer clearattr,
|
||||
LineFlags flags, const schar_T *chunk,
|
||||
const sattr_T *attrs)
|
||||
{
|
||||
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
row += curgrid->comp_row;
|
||||
startcol += curgrid->comp_col;
|
||||
endcol += curgrid->comp_col;
|
||||
clearcol += curgrid->comp_col;
|
||||
if (curgrid != &default_grid) {
|
||||
flags = flags & ~kLineFlagWrap;
|
||||
}
|
||||
assert(clearcol <= default_grid.Columns);
|
||||
if (flags & kLineFlagInvalid || kv_size(layers) > curgrid->comp_index+1) {
|
||||
compose_line(row, startcol, clearcol, flags);
|
||||
} else {
|
||||
ui_composed_call_raw_line(1, row, startcol, endcol, clearcol, clearattr,
|
||||
flags, chunk, attrs);
|
||||
}
|
||||
}
|
||||
|
||||
/// The screen is invalid and will soon be cleared
|
||||
///
|
||||
/// Don't redraw floats until screen is cleared
|
||||
void ui_comp_invalidate_screen(void)
|
||||
{
|
||||
valid_screen = false;
|
||||
}
|
||||
|
||||
static void ui_comp_grid_clear(UI *ui, Integer grid)
|
||||
{
|
||||
// By design, only first grid uses clearing.
|
||||
assert(grid == 1);
|
||||
ui_composed_call_grid_clear(1);
|
||||
valid_screen = true;
|
||||
}
|
||||
|
||||
// TODO(bfredl): These events are somewhat of a hack. multiline messages
|
||||
// should later on be a separate grid, then this would just be ordinary
|
||||
// ui_comp_put_grid and ui_comp_remove_grid calls.
|
||||
static void ui_comp_win_scroll_over_start(UI *ui)
|
||||
{
|
||||
msg_scroll_mode = true;
|
||||
msg_first_invalid = ui->height;
|
||||
}
|
||||
|
||||
static void ui_comp_win_scroll_over_reset(UI *ui)
|
||||
{
|
||||
msg_scroll_mode = false;
|
||||
for (size_t i = 1; i < kv_size(layers); i++) {
|
||||
ScreenGrid *grid = kv_A(layers, i);
|
||||
if (grid->comp_row+grid->Rows > msg_first_invalid) {
|
||||
compose_area(msg_first_invalid, grid->comp_row+grid->Rows,
|
||||
grid->comp_col, grid->comp_col+grid->Columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top,
|
||||
Integer bot, Integer left, Integer right,
|
||||
Integer rows, Integer cols)
|
||||
{
|
||||
if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) {
|
||||
return;
|
||||
}
|
||||
top += curgrid->comp_row;
|
||||
bot += curgrid->comp_row;
|
||||
left += curgrid->comp_col;
|
||||
right += curgrid->comp_col;
|
||||
if (!msg_scroll_mode && kv_size(layers) > curgrid->comp_index+1) {
|
||||
// TODO(bfredl):
|
||||
// 1. check if rectangles actually overlap
|
||||
// 2. calulate subareas that can scroll.
|
||||
if (rows > 0) {
|
||||
bot -= rows;
|
||||
} else {
|
||||
top += (-rows);
|
||||
}
|
||||
compose_area(top, bot, left, right);
|
||||
} else {
|
||||
msg_first_invalid = MIN(msg_first_invalid, (int)top);
|
||||
ui_composed_call_grid_scroll(1, top, bot, left, right, rows, cols);
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_comp_grid_resize(UI *ui, Integer grid,
|
||||
Integer width, Integer height)
|
||||
{
|
||||
if (grid == 1) {
|
||||
ui_composed_call_grid_resize(1, width, height);
|
||||
#ifndef NDEBUG
|
||||
chk_width = (int)width;
|
||||
chk_height = (int)height;
|
||||
#endif
|
||||
size_t new_bufsize = (size_t)width;
|
||||
if (bufsize != new_bufsize) {
|
||||
xfree(linebuf);
|
||||
xfree(attrbuf);
|
||||
linebuf = xmalloc(new_bufsize * sizeof(*linebuf));
|
||||
attrbuf = xmalloc(new_bufsize * sizeof(*attrbuf));
|
||||
bufsize = new_bufsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src/nvim/ui_compositor.h
Normal file
12
src/nvim/ui_compositor.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Bridge for communication between a UI thread and nvim core.
|
||||
// Used by the built-in TUI and libnvim-based UIs.
|
||||
#ifndef NVIM_UI_COMPOSITOR_H
|
||||
#define NVIM_UI_COMPOSITOR_H
|
||||
|
||||
#include "nvim/ui.h"
|
||||
#include "nvim/event/defs.h"
|
||||
|
||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||
# include "ui_compositor.h.generated.h"
|
||||
#endif
|
||||
#endif // NVIM_UI_COMPOSITOR_H
|
||||
@@ -5,6 +5,7 @@ local source = helpers.source
|
||||
local insert = helpers.insert
|
||||
local meths = helpers.meths
|
||||
local command = helpers.command
|
||||
local funcs = helpers.funcs
|
||||
|
||||
describe('ui/ext_popupmenu', function()
|
||||
local screen
|
||||
@@ -368,7 +369,7 @@ describe('ui/ext_popupmenu', function()
|
||||
end)
|
||||
|
||||
|
||||
describe('popup placement', function()
|
||||
describe('builtin popupmenu', function()
|
||||
local screen
|
||||
before_each(function()
|
||||
clear()
|
||||
@@ -385,7 +386,8 @@ describe('popup placement', function()
|
||||
[2] = {bold = true},
|
||||
[3] = {reverse = true},
|
||||
[4] = {bold = true, reverse = true},
|
||||
[5] = {bold = true, foreground = Screen.colors.SeaGreen}
|
||||
[5] = {bold = true, foreground = Screen.colors.SeaGreen},
|
||||
[6] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||
})
|
||||
end)
|
||||
|
||||
@@ -607,4 +609,574 @@ describe('popup placement', function()
|
||||
{2:-- }{5:match 1 of 3} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('works with split and scroll', function()
|
||||
screen:try_resize(60,14)
|
||||
command("split")
|
||||
command("set completeopt+=noinsert")
|
||||
command("set mouse=a")
|
||||
insert([[
|
||||
Lorem ipsum dolor sit amet, consectetur
|
||||
adipisicing elit, sed do eiusmod tempor
|
||||
incididunt ut labore et dolore magna aliqua.
|
||||
Ut enim ad minim veniam, quis nostrud
|
||||
exercitation ullamco laboris nisi ut aliquip ex
|
||||
ea commodo consequat. Duis aute irure dolor in
|
||||
reprehenderit in voluptate velit esse cillum
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint
|
||||
occaecat cupidatat non proident, sunt in culpa
|
||||
qui officia deserunt mollit anim id est
|
||||
laborum.
|
||||
]])
|
||||
|
||||
screen:expect([[
|
||||
reprehenderit in voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
occaecat cupidatat non proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
^ |
|
||||
{4:[No Name] [+] }|
|
||||
Lorem ipsum dolor sit amet, consectetur |
|
||||
adipisicing elit, sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{3:[No Name] [+] }|
|
||||
|
|
||||
]])
|
||||
|
||||
feed('ggOEst <c-x><c-p>')
|
||||
screen:expect([[
|
||||
Est ^ |
|
||||
L{n: sunt }{s: }sit amet, consectetur |
|
||||
a{n: in }{s: }sed do eiusmod tempor |
|
||||
i{n: culpa }{s: }re et dolore magna aliqua. |
|
||||
U{n: qui }{s: }eniam, quis nostrud |
|
||||
e{n: officia }{s: }co laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: deserunt }{s: }{4: }|
|
||||
L{n: mollit }{s: }sit amet, consectetur |
|
||||
a{n: anim }{s: }sed do eiusmod tempor |
|
||||
i{n: id }{s: }re et dolore magna aliqua. |
|
||||
U{n: est }{s: }eniam, quis nostrud |
|
||||
e{n: laborum }{c: }co laboris nisi ut aliquip ex |
|
||||
{3:[No}{s: Est }{c: }{3: }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 9, 40)
|
||||
screen:expect([[
|
||||
Est ^ |
|
||||
L{n: sunt }{s: }sit amet, consectetur |
|
||||
a{n: in }{s: }sed do eiusmod tempor |
|
||||
i{n: culpa }{s: }re et dolore magna aliqua. |
|
||||
U{n: qui }{s: }eniam, quis nostrud |
|
||||
e{n: officia }{s: }co laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: deserunt }{s: }{4: }|
|
||||
U{n: mollit }{s: }eniam, quis nostrud |
|
||||
e{n: anim }{s: }co laboris nisi ut aliquip ex |
|
||||
e{n: id }{s: }at. Duis aute irure dolor in |
|
||||
r{n: est }{s: }oluptate velit esse cillum |
|
||||
d{n: laborum }{c: }ulla pariatur. Excepteur sint |
|
||||
{3:[No}{s: Est }{c: }{3: }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
feed('e')
|
||||
screen:expect([[
|
||||
Est e^ |
|
||||
L{n: elit } sit amet, consectetur |
|
||||
a{n: eiusmod } sed do eiusmod tempor |
|
||||
i{n: et }ore et dolore magna aliqua. |
|
||||
U{n: enim }veniam, quis nostrud |
|
||||
e{n: exercitation }mco laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: ex }{4: }|
|
||||
U{n: ea }veniam, quis nostrud |
|
||||
e{n: esse }mco laboris nisi ut aliquip ex |
|
||||
e{n: eu }uat. Duis aute irure dolor in |
|
||||
r{s: est }voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
meths.input_mouse('wheel', 'up', '', 0, 9, 40)
|
||||
screen:expect([[
|
||||
Est e^ |
|
||||
L{n: elit } sit amet, consectetur |
|
||||
a{n: eiusmod } sed do eiusmod tempor |
|
||||
i{n: et }ore et dolore magna aliqua. |
|
||||
U{n: enim }veniam, quis nostrud |
|
||||
e{n: exercitation }mco laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: ex }{4: }|
|
||||
L{n: ea } sit amet, consectetur |
|
||||
a{n: esse } sed do eiusmod tempor |
|
||||
i{n: eu }ore et dolore magna aliqua. |
|
||||
U{s: est }veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
feed('s')
|
||||
screen:expect([[
|
||||
Est es^ |
|
||||
L{n: esse } sit amet, consectetur |
|
||||
a{s: est } sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{4:[No Name] [+] }|
|
||||
Lorem ipsum dolor sit amet, consectetur |
|
||||
adipisicing elit, sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 9, 40)
|
||||
screen:expect([[
|
||||
Est es^ |
|
||||
L{n: esse } sit amet, consectetur |
|
||||
a{s: est } sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{4:[No Name] [+] }|
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
ea commodo consequat. Duis aute irure dolor in |
|
||||
reprehenderit in voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
feed('<bs>')
|
||||
screen:expect([[
|
||||
Est e^ |
|
||||
L{n: elit } sit amet, consectetur |
|
||||
a{n: eiusmod } sed do eiusmod tempor |
|
||||
i{n: et }ore et dolore magna aliqua. |
|
||||
U{n: enim }veniam, quis nostrud |
|
||||
e{n: exercitation }mco laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: ex }{4: }|
|
||||
U{n: ea }veniam, quis nostrud |
|
||||
e{n: esse }mco laboris nisi ut aliquip ex |
|
||||
e{n: eu }uat. Duis aute irure dolor in |
|
||||
r{s: est }voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 65} |
|
||||
]])
|
||||
|
||||
feed('<c-p>')
|
||||
screen:expect([[
|
||||
Est eu^ |
|
||||
L{n: elit } sit amet, consectetur |
|
||||
a{n: eiusmod } sed do eiusmod tempor |
|
||||
i{n: et }ore et dolore magna aliqua. |
|
||||
U{n: enim }veniam, quis nostrud |
|
||||
e{n: exercitation }mco laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: ex }{4: }|
|
||||
U{n: ea }veniam, quis nostrud |
|
||||
e{n: esse }mco laboris nisi ut aliquip ex |
|
||||
e{s: eu }uat. Duis aute irure dolor in |
|
||||
r{n: est }voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 22 of 65} |
|
||||
]])
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 9, 40)
|
||||
screen:expect([[
|
||||
Est eu^ |
|
||||
L{n: elit } sit amet, consectetur |
|
||||
a{n: eiusmod } sed do eiusmod tempor |
|
||||
i{n: et }ore et dolore magna aliqua. |
|
||||
U{n: enim }veniam, quis nostrud |
|
||||
e{n: exercitation }mco laboris nisi ut aliquip ex |
|
||||
{4:[No}{n: ex }{4: }|
|
||||
r{n: ea }voluptate velit esse cillum |
|
||||
d{n: esse }nulla pariatur. Excepteur sint |
|
||||
o{s: eu }t non proident, sunt in culpa |
|
||||
q{n: est }unt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 22 of 65} |
|
||||
]])
|
||||
|
||||
|
||||
funcs.complete(4, {'ea', 'eeeeeeeeeeeeeeeeee', 'ei', 'eo', 'eu', 'ey', 'eå', 'eä', 'eö'})
|
||||
screen:expect([[
|
||||
Est eu^ |
|
||||
{s: ea }t amet, consectetur |
|
||||
{n: eeeeeeeeeeeeeeeeee }d do eiusmod tempor |
|
||||
{n: ei } et dolore magna aliqua. |
|
||||
{n: eo }iam, quis nostrud |
|
||||
{n: eu } laboris nisi ut aliquip ex |
|
||||
{4:[N}{n: ey }{4: }|
|
||||
{n: eå }uptate velit esse cillum |
|
||||
{n: eä }la pariatur. Excepteur sint |
|
||||
{n: eö }on proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- Keyword Local completion (^N^P) }{5:match 1 of 9} |
|
||||
]])
|
||||
|
||||
funcs.complete(4, {'ea', 'eee', 'ei', 'eo', 'eu', 'ey', 'eå', 'eä', 'eö'})
|
||||
screen:expect([[
|
||||
Est eu^ |
|
||||
{s: ea }r sit amet, consectetur |
|
||||
{n: eee }, sed do eiusmod tempor |
|
||||
{n: ei }bore et dolore magna aliqua. |
|
||||
{n: eo } veniam, quis nostrud |
|
||||
{n: eu }amco laboris nisi ut aliquip ex |
|
||||
{4:[N}{n: ey }{4: }|
|
||||
{n: eå } voluptate velit esse cillum |
|
||||
{n: eä } nulla pariatur. Excepteur sint |
|
||||
{n: eö }at non proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-n>')
|
||||
screen:expect([[
|
||||
Esteee^ |
|
||||
{n: ea }r sit amet, consectetur |
|
||||
{s: eee }, sed do eiusmod tempor |
|
||||
{n: ei }bore et dolore magna aliqua. |
|
||||
{n: eo } veniam, quis nostrud |
|
||||
{n: eu }amco laboris nisi ut aliquip ex |
|
||||
{4:[N}{n: ey }{4: }|
|
||||
{n: eå } voluptate velit esse cillum |
|
||||
{n: eä } nulla pariatur. Excepteur sint |
|
||||
{n: eö }at non proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
funcs.complete(6, {'foo', 'bar'})
|
||||
screen:expect([[
|
||||
Esteee^ |
|
||||
Lo{s: foo }sit amet, consectetur |
|
||||
ad{n: bar }sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{4:[No Name] [+] }|
|
||||
reprehenderit in voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
occaecat cupidatat non proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-y>')
|
||||
screen:expect([[
|
||||
Esteefoo^ |
|
||||
Lorem ipsum dolor sit amet, consectetur |
|
||||
adipisicing elit, sed do eiusmod tempor |
|
||||
incididunt ut labore et dolore magna aliqua. |
|
||||
Ut enim ad minim veniam, quis nostrud |
|
||||
exercitation ullamco laboris nisi ut aliquip ex |
|
||||
{4:[No Name] [+] }|
|
||||
reprehenderit in voluptate velit esse cillum |
|
||||
dolore eu fugiat nulla pariatur. Excepteur sint |
|
||||
occaecat cupidatat non proident, sunt in culpa |
|
||||
qui officia deserunt mollit anim id est |
|
||||
laborum. |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can be moved due to wrap or resize', function()
|
||||
feed('isome long prefix before the ')
|
||||
command("set completeopt+=noinsert,noselect")
|
||||
command("set linebreak")
|
||||
funcs.complete(29, {'word', 'choice', 'text', 'thing'})
|
||||
screen:expect([[
|
||||
some long prefix before the ^ |
|
||||
{1:~ }{n: word }|
|
||||
{1:~ }{n: choice}|
|
||||
{1:~ }{n: text }|
|
||||
{1:~ }{n: thing }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-p>')
|
||||
screen:expect([[
|
||||
some long prefix before the |
|
||||
thing^ |
|
||||
{n:word }{1: }|
|
||||
{n:choice }{1: }|
|
||||
{n:text }{1: }|
|
||||
{s:thing }{1: }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-p>')
|
||||
screen:expect([[
|
||||
some long prefix before the text|
|
||||
{1:^~ }{n: word }|
|
||||
{1:~ }{n: choice}|
|
||||
{1:~ }{s: text }|
|
||||
{1:~ }{n: thing }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
screen:try_resize(30,8)
|
||||
screen:expect([[
|
||||
some long prefix before the |
|
||||
text^ |
|
||||
{n:word }{1: }|
|
||||
{n:choice }{1: }|
|
||||
{s:text }{1: }|
|
||||
{n:thing }{1: }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
screen:try_resize(50,8)
|
||||
screen:expect([[
|
||||
some long prefix before the text^ |
|
||||
{1:~ }{n: word }{1: }|
|
||||
{1:~ }{n: choice }{1: }|
|
||||
{1:~ }{s: text }{1: }|
|
||||
{1:~ }{n: thing }{1: }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('can be moved due to wrap or resize', function()
|
||||
command("set rl")
|
||||
feed('isome rightleft ')
|
||||
screen:expect([[
|
||||
^ tfelthgir emos|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
command("set completeopt+=noinsert,noselect")
|
||||
funcs.complete(16, {'word', 'choice', 'text', 'thing'})
|
||||
screen:expect([[
|
||||
^ tfelthgir emos|
|
||||
{1: }{n: drow}{1: ~}|
|
||||
{1: }{n: eciohc}{1: ~}|
|
||||
{1: }{n: txet}{1: ~}|
|
||||
{1: }{n: gniht}{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-n>')
|
||||
screen:expect([[
|
||||
^ drow tfelthgir emos|
|
||||
{1: }{s: drow}{1: ~}|
|
||||
{1: }{n: eciohc}{1: ~}|
|
||||
{1: }{n: txet}{1: ~}|
|
||||
{1: }{n: gniht}{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-y>')
|
||||
screen:expect([[
|
||||
^ drow tfelthgir emos|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{1: ~}|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
it('works with multiline messages', function()
|
||||
screen:try_resize(40,8)
|
||||
feed('ixx<cr>')
|
||||
command('imap <f2> <cmd>echoerr "very"\\|echoerr "much"\\|echoerr "error"<cr>')
|
||||
funcs.complete(1, {'word', 'choice', 'text', 'thing'})
|
||||
screen:expect([[
|
||||
xx |
|
||||
word^ |
|
||||
{s:word }{1: }|
|
||||
{n:choice }{1: }|
|
||||
{n:text }{1: }|
|
||||
{n:thing }{1: }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<f2>')
|
||||
screen:expect([[
|
||||
xx |
|
||||
word |
|
||||
{s:word }{1: }|
|
||||
{4: }|
|
||||
{6:very} |
|
||||
{6:much} |
|
||||
{6:error} |
|
||||
{5:Press ENTER or type command to continue}^ |
|
||||
]])
|
||||
|
||||
feed('<cr>')
|
||||
screen:expect([[
|
||||
xx |
|
||||
word^ |
|
||||
{s:word }{1: }|
|
||||
{n:choice }{1: }|
|
||||
{n:text }{1: }|
|
||||
{n:thing }{1: }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
feed('<c-n>')
|
||||
screen:expect([[
|
||||
xx |
|
||||
choice^ |
|
||||
{n:word }{1: }|
|
||||
{s:choice }{1: }|
|
||||
{n:text }{1: }|
|
||||
{n:thing }{1: }|
|
||||
{1:~ }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
command("split")
|
||||
screen:expect([[
|
||||
xx |
|
||||
choice^ |
|
||||
{n:word }{1: }|
|
||||
{s:choice }{4: }|
|
||||
{n:text } |
|
||||
{n:thing } |
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 6, 15)
|
||||
screen:expect([[
|
||||
xx |
|
||||
choice^ |
|
||||
{n:word }{1: }|
|
||||
{s:choice }{4: }|
|
||||
{n:text } |
|
||||
{n:thing }{1: }|
|
||||
{3:[No Name] [+] }|
|
||||
{2:-- INSERT --} |
|
||||
]])
|
||||
end)
|
||||
|
||||
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user