mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #4432 from bfredl/pum_ui
allow external UI:s to render the popupmenu
This commit is contained in:
commit
0b5a7e4ad5
@ -11,6 +11,7 @@ RPC API for Nvim *RPC* *rpc* *msgpack-rpc*
|
|||||||
3. Connecting |rpc-connecting|
|
3. Connecting |rpc-connecting|
|
||||||
4. Clients |rpc-api-client|
|
4. Clients |rpc-api-client|
|
||||||
5. Types |rpc-types|
|
5. Types |rpc-types|
|
||||||
|
6. Remote UIs |rpc-remote-ui|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
1. Introduction *rpc-intro*
|
1. Introduction *rpc-intro*
|
||||||
@ -237,5 +238,170 @@ Even for statically compiled clients it is good practice to avoid hardcoding
|
|||||||
the type codes, because a client may be built against one Nvim version but
|
the type codes, because a client may be built against one Nvim version but
|
||||||
connect to another with different type codes.
|
connect to another with different type codes.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
6. Remote UIs *rpc-remote-ui*
|
||||||
|
|
||||||
|
Nvim allows Graphical user interfaces to be implemented by separate processes
|
||||||
|
communicating with Nvim over the RPC API. Currently the ui model conists of a
|
||||||
|
terminal-like grid with one single, monospace font size, with a few elements
|
||||||
|
that could be drawn separately from the grid (for the momemnt only the popup
|
||||||
|
menu)
|
||||||
|
|
||||||
|
After connecting to a nvim instance (typically a spawned, embedded instance)
|
||||||
|
use the |nvim_ui_attach|(width, height, options) API method to tell nvim that your
|
||||||
|
program wants to draw the nvim screen on a grid with "width" times
|
||||||
|
"height" cells. "options" should be a dictionary with the following (all
|
||||||
|
optional) keys:
|
||||||
|
`rgb`: Controls what color format to use.
|
||||||
|
Set to true (default) to use 24-bit rgb
|
||||||
|
colors.
|
||||||
|
Set to false to use terminal color codes (at
|
||||||
|
most 256 different colors).
|
||||||
|
`popupmenu_external`: Instead of drawing the completion popupmenu on
|
||||||
|
the grid, Nvim will send higher-level events to
|
||||||
|
the ui and let it draw the popupmenu.
|
||||||
|
Defaults to false.
|
||||||
|
|
||||||
|
Nvim will then send msgpack-rpc notifications, with the method name "redraw"
|
||||||
|
and a single argument, an array of screen updates (described below).
|
||||||
|
These should be processed in order. Preferably the user should only be able to
|
||||||
|
see the screen state after all updates are processed (not any intermediate
|
||||||
|
state after processing only a part of the array).
|
||||||
|
|
||||||
|
Screen updates are arrays. The first element a string describing the kind
|
||||||
|
of update.
|
||||||
|
|
||||||
|
["resize", width, height]
|
||||||
|
The grid is resized to `width` and `height` cells.
|
||||||
|
|
||||||
|
["clear"]
|
||||||
|
Clear the screen.
|
||||||
|
|
||||||
|
["eol_clear"]
|
||||||
|
Clear from the cursor position to the end of the current line.
|
||||||
|
|
||||||
|
["cursor_goto", row, col]
|
||||||
|
Move the cursor to position (row, col). Currently, the same cursor is
|
||||||
|
used to define the position for text insertion and the visible cursor.
|
||||||
|
However, only the last cursor position, after processing the entire
|
||||||
|
array in the "redraw" event, is intended to be a visible cursor
|
||||||
|
position.
|
||||||
|
|
||||||
|
["update_fg", color]
|
||||||
|
["update_bg", color]
|
||||||
|
["update_sp", color]
|
||||||
|
Set the default foreground, background and special colors
|
||||||
|
respectively.
|
||||||
|
|
||||||
|
["highlight_set", attrs]
|
||||||
|
Set the attributes that the next text put on the screen will have.
|
||||||
|
`attrs` is a dict with the keys below. Any absent key is reset
|
||||||
|
to its default value. Color defaults are set by the `update_fg` etc
|
||||||
|
updates. All boolean keys default to false.
|
||||||
|
|
||||||
|
`foreground`: foreground color.
|
||||||
|
`background`: backround color.
|
||||||
|
`special`: color to use for underline and undercurl, when present.
|
||||||
|
`reverse`: reverse video. Foreground and background colors are
|
||||||
|
switched.
|
||||||
|
`italic`: italic text.
|
||||||
|
`bold`: bold text.
|
||||||
|
`underline`: underlined text. The line has `special` color.
|
||||||
|
`undercurl`: undercurled text. The curl has `special` color.
|
||||||
|
|
||||||
|
["put", text]
|
||||||
|
The (utf-8 encoded) string `text` is put at the cursor position
|
||||||
|
(and the cursor is advanced), with the highlights as set by the
|
||||||
|
last `highlight_set` update.
|
||||||
|
|
||||||
|
["set_scroll_region", top, bot, left, right]
|
||||||
|
Define the scroll region used by `scroll` below.
|
||||||
|
|
||||||
|
["scroll", count]
|
||||||
|
Scroll the text in the scroll region. The diagrams below illustrate
|
||||||
|
what will happen, depending on the scroll direction. "=" is used to
|
||||||
|
represent the SR(scroll region) boundaries and "-" the moved rectangles.
|
||||||
|
Note that dst and src share a common region.
|
||||||
|
|
||||||
|
If count is bigger than 0, move a rectangle in the SR up, this can
|
||||||
|
happen while scrolling down.
|
||||||
|
>
|
||||||
|
+-------------------------+
|
||||||
|
| (clipped above SR) | ^
|
||||||
|
|=========================| dst_top |
|
||||||
|
| dst (still in SR) | |
|
||||||
|
+-------------------------+ src_top |
|
||||||
|
| src (moved up) and dst | |
|
||||||
|
|-------------------------| dst_bot |
|
||||||
|
| src (cleared) | |
|
||||||
|
+=========================+ src_bot
|
||||||
|
<
|
||||||
|
If count is less than zero, move a rectangle in the SR down, this can
|
||||||
|
happen while scrolling up.
|
||||||
|
>
|
||||||
|
+=========================+ src_top
|
||||||
|
| src (cleared) | |
|
||||||
|
|------------------------ | dst_top |
|
||||||
|
| src (moved down) and dst| |
|
||||||
|
+-------------------------+ src_bot |
|
||||||
|
| dst (still in SR) | |
|
||||||
|
|=========================| dst_bot |
|
||||||
|
| (clipped below SR) | v
|
||||||
|
+-------------------------+
|
||||||
|
<
|
||||||
|
["set_title", title]
|
||||||
|
["set_icon", icon]
|
||||||
|
Set the window title, and icon (minimized) window title, respectively.
|
||||||
|
In windowing systems not distinguishing between the two, "set_icon"
|
||||||
|
can be ignored.
|
||||||
|
|
||||||
|
["mouse_on"]
|
||||||
|
["mouse_off"]
|
||||||
|
Tells the client whether mouse support, as determined by |'mouse'|
|
||||||
|
option, is considered to be active in the current mode. This is mostly
|
||||||
|
useful for a terminal frontend, or other situations where nvim mouse
|
||||||
|
would conflict with other usages of the mouse. It is safe for a client
|
||||||
|
to ignore this and always send mouse events.
|
||||||
|
|
||||||
|
["busy_on"]
|
||||||
|
["busy_off"]
|
||||||
|
Nvim started or stopped being busy, and possibly not responsible to user
|
||||||
|
input. This could be indicated to the user by hiding the cursor.
|
||||||
|
|
||||||
|
["suspend"]
|
||||||
|
|:suspend| command or |Ctrl-Z| mapping is used. A terminal client (or other
|
||||||
|
client where it makes sense) could suspend itself. Other clients can
|
||||||
|
safely ignore it.
|
||||||
|
|
||||||
|
["bell"]
|
||||||
|
["visual_bell"]
|
||||||
|
Notify the user with an audible or visual bell, respectively.
|
||||||
|
|
||||||
|
["update_menu"]
|
||||||
|
The menu mappings changed.
|
||||||
|
|
||||||
|
["mode_change", mode]
|
||||||
|
The mode changed. Currently sent when "insert", "replace" and "normal"
|
||||||
|
modes are entered. A client could for instance change the cursor shape.
|
||||||
|
|
||||||
|
["popupmenu_show", items, selected, row, col]
|
||||||
|
When `popupmenu_external` is set to true, nvim will not draw the
|
||||||
|
popupmenu on the grid, instead when the popupmenu is to be displayed
|
||||||
|
this update is sent. `items` is an array of the items to show, the
|
||||||
|
items are themselves arrays of the form [word, kind, menu, info]
|
||||||
|
as defined at |complete-items|, except that `word` is replaced by
|
||||||
|
`abbr` if present. `selected` is the initially selected item, either a
|
||||||
|
zero-based index into the array of items, or -1 if no item is
|
||||||
|
selected. `row` and `col` is the anchor position, where the first
|
||||||
|
character of the completed word will be.
|
||||||
|
|
||||||
|
["popupmenu_select", selected]
|
||||||
|
An item in the currently displayed popupmenu is selected. `selected`
|
||||||
|
is either a zero-based index into the array of items from the last
|
||||||
|
`popupmenu_show` event, or -1 if no item is selected.
|
||||||
|
|
||||||
|
["popupmenu_hide"]
|
||||||
|
The popupmenu is hidden.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||||
|
@ -8,8 +8,10 @@
|
|||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
#include "nvim/map.h"
|
#include "nvim/map.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
|
#include "nvim/api/ui.h"
|
||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/popupmnu.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "api/ui.c.generated.h"
|
# include "api/ui.c.generated.h"
|
||||||
@ -44,8 +46,8 @@ void remote_ui_disconnect(uint64_t channel_id)
|
|||||||
xfree(ui);
|
xfree(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_attach(uint64_t channel_id, Integer width, Integer height,
|
void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height,
|
||||||
Boolean enable_rgb, Error *err)
|
Dictionary options, Error *err)
|
||||||
{
|
{
|
||||||
if (pmap_has(uint64_t)(connected_uis, channel_id)) {
|
if (pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||||
api_set_error(err, Exception, _("UI already attached for channel"));
|
api_set_error(err, Exception, _("UI already attached for channel"));
|
||||||
@ -57,14 +59,11 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height,
|
|||||||
_("Expected width > 0 and height > 0"));
|
_("Expected width > 0 and height > 0"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UIData *data = xmalloc(sizeof(UIData));
|
|
||||||
data->channel_id = channel_id;
|
|
||||||
data->buffer = (Array)ARRAY_DICT_INIT;
|
|
||||||
UI *ui = xcalloc(1, sizeof(UI));
|
UI *ui = xcalloc(1, sizeof(UI));
|
||||||
ui->width = (int)width;
|
ui->width = (int)width;
|
||||||
ui->height = (int)height;
|
ui->height = (int)height;
|
||||||
ui->rgb = enable_rgb;
|
ui->rgb = true;
|
||||||
ui->data = data;
|
ui->pum_external = false;
|
||||||
ui->resize = remote_ui_resize;
|
ui->resize = remote_ui_resize;
|
||||||
ui->clear = remote_ui_clear;
|
ui->clear = remote_ui_clear;
|
||||||
ui->eol_clear = remote_ui_eol_clear;
|
ui->eol_clear = remote_ui_eol_clear;
|
||||||
@ -88,37 +87,108 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height,
|
|||||||
ui->suspend = remote_ui_suspend;
|
ui->suspend = remote_ui_suspend;
|
||||||
ui->set_title = remote_ui_set_title;
|
ui->set_title = remote_ui_set_title;
|
||||||
ui->set_icon = remote_ui_set_icon;
|
ui->set_icon = remote_ui_set_icon;
|
||||||
|
ui->event = remote_ui_event;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < options.size; i++) {
|
||||||
|
ui_set_option(ui, options.items[i].key, options.items[i].value, err);
|
||||||
|
if (err->set) {
|
||||||
|
xfree(ui);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UIData *data = xmalloc(sizeof(UIData));
|
||||||
|
data->channel_id = channel_id;
|
||||||
|
data->buffer = (Array)ARRAY_DICT_INIT;
|
||||||
|
ui->data = data;
|
||||||
|
|
||||||
pmap_put(uint64_t)(connected_uis, channel_id, ui);
|
pmap_put(uint64_t)(connected_uis, channel_id, ui);
|
||||||
ui_attach_impl(ui);
|
ui_attach_impl(ui);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_detach(uint64_t channel_id, Error *err)
|
/// @deprecated
|
||||||
|
void ui_attach(uint64_t channel_id, Integer width, Integer height,
|
||||||
|
Boolean enable_rgb, Error *err)
|
||||||
|
{
|
||||||
|
Dictionary opts = ARRAY_DICT_INIT;
|
||||||
|
PUT(opts, "rgb", BOOLEAN_OBJ(enable_rgb));
|
||||||
|
nvim_ui_attach(channel_id, width, height, opts, err);
|
||||||
|
api_free_dictionary(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvim_ui_detach(uint64_t channel_id, Error *err)
|
||||||
{
|
{
|
||||||
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||||
api_set_error(err, Exception, _("UI is not attached for channel"));
|
api_set_error(err, Exception, _("UI is not attached for channel"));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
remote_ui_disconnect(channel_id);
|
remote_ui_disconnect(channel_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object ui_try_resize(uint64_t channel_id, Integer width,
|
/// @deprecated
|
||||||
Integer height, Error *err)
|
void ui_detach(uint64_t channel_id, Error *err)
|
||||||
|
{
|
||||||
|
nvim_ui_detach(channel_id, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvim_ui_try_resize(uint64_t channel_id, Integer width,
|
||||||
|
Integer height, Error *err)
|
||||||
{
|
{
|
||||||
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||||
api_set_error(err, Exception, _("UI is not attached for channel"));
|
api_set_error(err, Exception, _("UI is not attached for channel"));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width <= 0 || height <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
api_set_error(err, Validation,
|
api_set_error(err, Validation,
|
||||||
_("Expected width > 0 and height > 0"));
|
_("Expected width > 0 and height > 0"));
|
||||||
return NIL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
||||||
ui->width = (int)width;
|
ui->width = (int)width;
|
||||||
ui->height = (int)height;
|
ui->height = (int)height;
|
||||||
ui_refresh();
|
ui_refresh();
|
||||||
return NIL;
|
}
|
||||||
|
|
||||||
|
/// @deprecated
|
||||||
|
void ui_try_resize(uint64_t channel_id, Integer width,
|
||||||
|
Integer height, Error *err)
|
||||||
|
{
|
||||||
|
nvim_ui_try_resize(channel_id, width, height, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nvim_ui_set_option(uint64_t channel_id, String name,
|
||||||
|
Object value, Error *error) {
|
||||||
|
if (!pmap_has(uint64_t)(connected_uis, channel_id)) {
|
||||||
|
api_set_error(error, Exception, _("UI is not attached for channel"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
UI *ui = pmap_get(uint64_t)(connected_uis, channel_id);
|
||||||
|
|
||||||
|
ui_set_option(ui, name, value, error);
|
||||||
|
if (!error->set) {
|
||||||
|
ui_refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ui_set_option(UI *ui, String name, Object value, Error *error) {
|
||||||
|
if (strcmp(name.data, "rgb") == 0) {
|
||||||
|
if (value.type != kObjectTypeBoolean) {
|
||||||
|
api_set_error(error, Validation, _("rgb must be a Boolean"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui->rgb = value.data.boolean;
|
||||||
|
} else if (strcmp(name.data, "popupmenu_external") == 0) {
|
||||||
|
if (value.type != kObjectTypeBoolean) {
|
||||||
|
api_set_error(error, Validation,
|
||||||
|
_("popupmenu_external must be a Boolean"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ui->pum_external = value.data.boolean;
|
||||||
|
} else {
|
||||||
|
api_set_error(error, Validation, _("No such ui option"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void push_call(UI *ui, char *name, Array args)
|
static void push_call(UI *ui, char *name, Array args)
|
||||||
@ -341,3 +411,19 @@ static void remote_ui_set_icon(UI *ui, char *icon)
|
|||||||
ADD(args, STRING_OBJ(cstr_to_string(icon)));
|
ADD(args, STRING_OBJ(cstr_to_string(icon)));
|
||||||
push_call(ui, "set_icon", args);
|
push_call(ui, "set_icon", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||||
|
{
|
||||||
|
Array my_args = ARRAY_DICT_INIT;
|
||||||
|
// Objects are currently single-reference
|
||||||
|
// make a copy, but only if necessary
|
||||||
|
if (*args_consumed) {
|
||||||
|
for (size_t i = 0; i < args.size; i++) {
|
||||||
|
ADD(my_args, copy_object(args.items[i]));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
my_args = args;
|
||||||
|
*args_consumed = true;
|
||||||
|
}
|
||||||
|
push_call(ui, name, my_args);
|
||||||
|
}
|
||||||
|
@ -2472,6 +2472,7 @@ void ins_compl_show_pum(void)
|
|||||||
int cur = -1;
|
int cur = -1;
|
||||||
colnr_T col;
|
colnr_T col;
|
||||||
int lead_len = 0;
|
int lead_len = 0;
|
||||||
|
bool array_changed = false;
|
||||||
|
|
||||||
if (!pum_wanted() || !pum_enough_matches())
|
if (!pum_wanted() || !pum_enough_matches())
|
||||||
return;
|
return;
|
||||||
@ -2483,7 +2484,8 @@ void ins_compl_show_pum(void)
|
|||||||
update_screen(0);
|
update_screen(0);
|
||||||
|
|
||||||
if (compl_match_array == NULL) {
|
if (compl_match_array == NULL) {
|
||||||
/* Need to build the popup menu list. */
|
array_changed = true;
|
||||||
|
// Need to build the popup menu list.
|
||||||
compl_match_arraysize = 0;
|
compl_match_arraysize = 0;
|
||||||
compl = compl_first_match;
|
compl = compl_first_match;
|
||||||
/*
|
/*
|
||||||
@ -2586,7 +2588,7 @@ void ins_compl_show_pum(void)
|
|||||||
// Use the cursor to get all wrapping and other settings right.
|
// Use the cursor to get all wrapping and other settings right.
|
||||||
col = curwin->w_cursor.col;
|
col = curwin->w_cursor.col;
|
||||||
curwin->w_cursor.col = compl_col;
|
curwin->w_cursor.col = compl_col;
|
||||||
pum_display(compl_match_array, compl_match_arraysize, cur);
|
pum_display(compl_match_array, compl_match_arraysize, cur, array_changed);
|
||||||
curwin->w_cursor.col = col;
|
curwin->w_cursor.col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/popupmnu.h"
|
#include "nvim/popupmnu.h"
|
||||||
#include "nvim/charset.h"
|
#include "nvim/charset.h"
|
||||||
@ -21,6 +22,7 @@
|
|||||||
#include "nvim/memory.h"
|
#include "nvim/memory.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
#include "nvim/edit.h"
|
#include "nvim/edit.h"
|
||||||
|
#include "nvim/ui.h"
|
||||||
|
|
||||||
static pumitem_T *pum_array = NULL; // items of displayed pum
|
static pumitem_T *pum_array = NULL; // items of displayed pum
|
||||||
static int pum_size; // nr of items in "pum_array"
|
static int pum_size; // nr of items in "pum_array"
|
||||||
@ -36,8 +38,10 @@ static int pum_scrollbar; // TRUE when scrollbar present
|
|||||||
static int pum_row; // top row of pum
|
static int pum_row; // top row of pum
|
||||||
static int pum_col; // left column of pum
|
static int pum_col; // left column of pum
|
||||||
|
|
||||||
static int pum_do_redraw = FALSE; // do redraw anyway
|
static bool pum_is_visible = false;
|
||||||
|
|
||||||
|
static bool pum_external = false;
|
||||||
|
static bool pum_wants_external = false;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "popupmnu.c.generated.h"
|
# include "popupmnu.c.generated.h"
|
||||||
@ -53,7 +57,10 @@ static int pum_do_redraw = FALSE; // do redraw anyway
|
|||||||
/// @param array
|
/// @param array
|
||||||
/// @param size
|
/// @param size
|
||||||
/// @param selected index of initially selected item, none if out of range
|
/// @param selected index of initially selected item, none if out of range
|
||||||
void pum_display(pumitem_T *array, int size, int selected)
|
/// @param array_changed if true, array contains different items since last call
|
||||||
|
/// if false, a new item is selected, but the array
|
||||||
|
/// is the same
|
||||||
|
void pum_display(pumitem_T *array, int size, int selected, bool array_changed)
|
||||||
{
|
{
|
||||||
int w;
|
int w;
|
||||||
int def_width;
|
int def_width;
|
||||||
@ -68,20 +75,55 @@ void pum_display(pumitem_T *array, int size, int selected)
|
|||||||
int above_row = cmdline_row;
|
int above_row = cmdline_row;
|
||||||
int redo_count = 0;
|
int redo_count = 0;
|
||||||
|
|
||||||
|
if (!pum_is_visible) {
|
||||||
|
// To keep the code simple, we only allow changing the
|
||||||
|
// draw mode when the popup menu is not being displayed
|
||||||
|
pum_external = pum_wants_external;
|
||||||
|
}
|
||||||
|
|
||||||
redo:
|
redo:
|
||||||
|
// Mark the pum as visible already here,
|
||||||
|
// to avoid that must_redraw is set when 'cursorcolumn' is on.
|
||||||
|
pum_is_visible = true;
|
||||||
|
validate_cursor_col();
|
||||||
|
|
||||||
|
// anchor position: the start of the completed word
|
||||||
|
row = curwin->w_wrow + curwin->w_winrow;
|
||||||
|
if (curwin->w_p_rl) {
|
||||||
|
col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1;
|
||||||
|
} else {
|
||||||
|
col = curwin->w_wincol + curwin->w_wcol;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pum_external) {
|
||||||
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
if (array_changed) {
|
||||||
|
Array arr = ARRAY_DICT_INIT;
|
||||||
|
for (i = 0; i < size; i++) {
|
||||||
|
Array item = ARRAY_DICT_INIT;
|
||||||
|
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_text)));
|
||||||
|
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_kind)));
|
||||||
|
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_extra)));
|
||||||
|
ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info)));
|
||||||
|
ADD(arr, ARRAY_OBJ(item));
|
||||||
|
}
|
||||||
|
ADD(args, ARRAY_OBJ(arr));
|
||||||
|
ADD(args, INTEGER_OBJ(selected));
|
||||||
|
ADD(args, INTEGER_OBJ(row));
|
||||||
|
ADD(args, INTEGER_OBJ(col));
|
||||||
|
ui_event("popupmenu_show", args);
|
||||||
|
} else {
|
||||||
|
ADD(args, INTEGER_OBJ(selected));
|
||||||
|
ui_event("popupmenu_select", args);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
def_width = PUM_DEF_WIDTH;
|
def_width = PUM_DEF_WIDTH;
|
||||||
max_width = 0;
|
max_width = 0;
|
||||||
kind_width = 0;
|
kind_width = 0;
|
||||||
extra_width = 0;
|
extra_width = 0;
|
||||||
|
|
||||||
// Pretend the pum is already there to avoid that must_redraw is set when
|
|
||||||
// 'cuc' is on.
|
|
||||||
pum_array = (pumitem_T *)1;
|
|
||||||
validate_cursor_col();
|
|
||||||
pum_array = NULL;
|
|
||||||
|
|
||||||
row = curwin->w_wrow + curwin->w_winrow;
|
|
||||||
|
|
||||||
if (firstwin->w_p_pvw) {
|
if (firstwin->w_p_pvw) {
|
||||||
top_clear = firstwin->w_height;
|
top_clear = firstwin->w_height;
|
||||||
} else {
|
} else {
|
||||||
@ -194,13 +236,6 @@ redo:
|
|||||||
pum_base_width = max_width;
|
pum_base_width = max_width;
|
||||||
pum_kind_width = kind_width;
|
pum_kind_width = kind_width;
|
||||||
|
|
||||||
// Calculate column
|
|
||||||
if (curwin->w_p_rl) {
|
|
||||||
col = curwin->w_wincol + curwin->w_width - curwin->w_wcol - 1;
|
|
||||||
} else {
|
|
||||||
col = curwin->w_wincol + curwin->w_wcol;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there are more items than room we need a scrollbar
|
// if there are more items than room we need a scrollbar
|
||||||
if (pum_height < size) {
|
if (pum_height < size) {
|
||||||
pum_scrollbar = 1;
|
pum_scrollbar = 1;
|
||||||
@ -641,9 +676,9 @@ static int pum_set_selected(int n, int repeat)
|
|||||||
|
|
||||||
// Update the screen before drawing the popup menu.
|
// Update the screen before drawing the popup menu.
|
||||||
// Enable updating the status lines.
|
// Enable updating the status lines.
|
||||||
pum_do_redraw = TRUE;
|
pum_is_visible = false;
|
||||||
update_screen(0);
|
update_screen(0);
|
||||||
pum_do_redraw = FALSE;
|
pum_is_visible = true;
|
||||||
|
|
||||||
if (!resized && win_valid(curwin_save)) {
|
if (!resized && win_valid(curwin_save)) {
|
||||||
no_u_sync++;
|
no_u_sync++;
|
||||||
@ -653,9 +688,9 @@ static int pum_set_selected(int n, int repeat)
|
|||||||
|
|
||||||
// May need to update the screen again when there are
|
// May need to update the screen again when there are
|
||||||
// autocommands involved.
|
// autocommands involved.
|
||||||
pum_do_redraw = TRUE;
|
pum_is_visible = false;
|
||||||
update_screen(0);
|
update_screen(0);
|
||||||
pum_do_redraw = FALSE;
|
pum_is_visible = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -672,10 +707,17 @@ static int pum_set_selected(int n, int repeat)
|
|||||||
/// Undisplay the popup menu (later).
|
/// Undisplay the popup menu (later).
|
||||||
void pum_undisplay(void)
|
void pum_undisplay(void)
|
||||||
{
|
{
|
||||||
|
pum_is_visible = false;
|
||||||
pum_array = NULL;
|
pum_array = NULL;
|
||||||
redraw_all_later(SOME_VALID);
|
|
||||||
redraw_tabline = TRUE;
|
if (pum_external) {
|
||||||
status_redraw_all();
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ui_event("popupmenu_hide", args);
|
||||||
|
} else {
|
||||||
|
redraw_all_later(SOME_VALID);
|
||||||
|
redraw_tabline = true;
|
||||||
|
status_redraw_all();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear the popup menu. Currently only resets the offset to the first
|
/// Clear the popup menu. Currently only resets the offset to the first
|
||||||
@ -685,12 +727,16 @@ void pum_clear(void)
|
|||||||
pum_first = 0;
|
pum_first = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Overruled when "pum_do_redraw" is set, used to redraw the status lines.
|
/// @return true if the popup menu is displayed.
|
||||||
///
|
bool pum_visible(void)
|
||||||
/// @return TRUE if the popup menu is displayed.
|
|
||||||
int pum_visible(void)
|
|
||||||
{
|
{
|
||||||
return !pum_do_redraw && pum_array != NULL;
|
return pum_is_visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @return true if the popup menu is displayed and drawn on the grid.
|
||||||
|
bool pum_drawn(void)
|
||||||
|
{
|
||||||
|
return pum_visible() && !pum_external;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the height of the menu.
|
/// Gets the height of the menu.
|
||||||
@ -701,3 +747,8 @@ int pum_get_height(void)
|
|||||||
{
|
{
|
||||||
return pum_height;
|
return pum_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pum_set_external(bool external)
|
||||||
|
{
|
||||||
|
pum_wants_external = external;
|
||||||
|
}
|
||||||
|
@ -420,9 +420,10 @@ void update_screen(int type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_search_hl();
|
end_search_hl();
|
||||||
/* May need to redraw the popup menu. */
|
// May need to redraw the popup menu.
|
||||||
if (pum_visible())
|
if (pum_drawn()) {
|
||||||
pum_redraw();
|
pum_redraw();
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset b_mod_set flags. Going through all windows is probably faster
|
/* Reset b_mod_set flags. Going through all windows is probably faster
|
||||||
* than going through all buffers (there could be many buffers). */
|
* than going through all buffers (there could be many buffers). */
|
||||||
@ -4827,15 +4828,12 @@ void win_redr_status(win_T *wp)
|
|||||||
|
|
||||||
wp->w_redr_status = FALSE;
|
wp->w_redr_status = FALSE;
|
||||||
if (wp->w_status_height == 0) {
|
if (wp->w_status_height == 0) {
|
||||||
/* no status line, can only be last window */
|
// no status line, can only be last window
|
||||||
redraw_cmdline = TRUE;
|
redraw_cmdline = true;
|
||||||
} else if (!redrawing()
|
} else if (!redrawing() || pum_drawn()) {
|
||||||
/* don't update status line when popup menu is visible and may be
|
// Don't redraw right now, do it later. Don't update status line when
|
||||||
* drawn over it */
|
// popup menu is visible and may be drawn over it
|
||||||
|| pum_visible()
|
wp->w_redr_status = true;
|
||||||
) {
|
|
||||||
/* Don't redraw right now, do it later. */
|
|
||||||
wp->w_redr_status = TRUE;
|
|
||||||
} else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
|
} else if (*p_stl != NUL || *wp->w_p_stl != NUL) {
|
||||||
/* redraw custom status line */
|
/* redraw custom status line */
|
||||||
redraw_custom_statusline(wp);
|
redraw_custom_statusline(wp);
|
||||||
@ -7081,9 +7079,9 @@ void showruler(int always)
|
|||||||
{
|
{
|
||||||
if (!always && !redrawing())
|
if (!always && !redrawing())
|
||||||
return;
|
return;
|
||||||
if (pum_visible()) {
|
if (pum_drawn()) {
|
||||||
/* Don't redraw right now, do it later. */
|
// Don't redraw right now, do it later.
|
||||||
curwin->w_redr_status = TRUE;
|
curwin->w_redr_status = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
|
if ((*p_stl != NUL || *curwin->w_p_stl != NUL) && curwin->w_status_height) {
|
||||||
@ -7119,9 +7117,10 @@ static void win_redr_ruler(win_T *wp, int always)
|
|||||||
if (wp == lastwin && lastwin->w_status_height == 0)
|
if (wp == lastwin && lastwin->w_status_height == 0)
|
||||||
if (edit_submode != NULL)
|
if (edit_submode != NULL)
|
||||||
return;
|
return;
|
||||||
/* Don't draw the ruler when the popup menu is visible, it may overlap. */
|
// Don't draw the ruler when the popup menu is visible, it may overlap.
|
||||||
if (pum_visible())
|
if (pum_drawn()) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (*p_ruf) {
|
if (*p_ruf) {
|
||||||
int save_called_emsg = called_emsg;
|
int save_called_emsg = called_emsg;
|
||||||
@ -7371,7 +7370,7 @@ void screen_resize(int width, int height)
|
|||||||
redrawcmdline();
|
redrawcmdline();
|
||||||
} else {
|
} else {
|
||||||
update_topline();
|
update_topline();
|
||||||
if (pum_visible()) {
|
if (pum_drawn()) {
|
||||||
redraw_later(NOT_VALID);
|
redraw_later(NOT_VALID);
|
||||||
ins_compl_show_pum(); /* This includes the redraw. */
|
ins_compl_show_pum(); /* This includes the redraw. */
|
||||||
} else
|
} else
|
||||||
|
@ -83,6 +83,7 @@ UI *tui_start(void)
|
|||||||
UI *ui = xcalloc(1, sizeof(UI));
|
UI *ui = xcalloc(1, sizeof(UI));
|
||||||
ui->stop = tui_stop;
|
ui->stop = tui_stop;
|
||||||
ui->rgb = p_tgc;
|
ui->rgb = p_tgc;
|
||||||
|
ui->pum_external = false;
|
||||||
ui->resize = tui_resize;
|
ui->resize = tui_resize;
|
||||||
ui->clear = tui_clear;
|
ui->clear = tui_clear;
|
||||||
ui->eol_clear = tui_eol_clear;
|
ui->eol_clear = tui_eol_clear;
|
||||||
@ -106,6 +107,7 @@ UI *tui_start(void)
|
|||||||
ui->suspend = tui_suspend;
|
ui->suspend = tui_suspend;
|
||||||
ui->set_title = tui_set_title;
|
ui->set_title = tui_set_title;
|
||||||
ui->set_icon = tui_set_icon;
|
ui->set_icon = tui_set_icon;
|
||||||
|
ui->event = tui_event;
|
||||||
return ui_bridge_attach(ui, tui_main, tui_scheduler);
|
return ui_bridge_attach(ui, tui_main, tui_scheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -650,6 +652,12 @@ static void tui_set_icon(UI *ui, char *icon)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NB: if we start to use this, the ui_bridge must be updated
|
||||||
|
// to make a copy for the tui thread
|
||||||
|
static void tui_event(UI *ui, char *name, Array args, bool *args_consumed)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static void invalidate(UI *ui, int top, int bot, int left, int right)
|
static void invalidate(UI *ui, int top, int bot, int left, int right)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/signal.h"
|
#include "nvim/os/signal.h"
|
||||||
|
#include "nvim/popupmnu.h"
|
||||||
#include "nvim/screen.h"
|
#include "nvim/screen.h"
|
||||||
#include "nvim/syntax.h"
|
#include "nvim/syntax.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
@ -35,6 +36,7 @@
|
|||||||
#else
|
#else
|
||||||
# include "nvim/msgpack_rpc/server.h"
|
# include "nvim/msgpack_rpc/server.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ui.c.generated.h"
|
# include "ui.c.generated.h"
|
||||||
@ -143,6 +145,15 @@ void ui_set_icon(char *icon)
|
|||||||
UI_CALL(flush);
|
UI_CALL(flush);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ui_event(char *name, Array args)
|
||||||
|
{
|
||||||
|
bool args_consumed = false;
|
||||||
|
UI_CALL(event, name, args, &args_consumed);
|
||||||
|
if (!args_consumed) {
|
||||||
|
api_free_array(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// May update the shape of the cursor.
|
// May update the shape of the cursor.
|
||||||
void ui_cursor_shape(void)
|
void ui_cursor_shape(void)
|
||||||
{
|
{
|
||||||
@ -156,15 +167,18 @@ void ui_refresh(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int width = INT_MAX, height = INT_MAX;
|
int width = INT_MAX, height = INT_MAX;
|
||||||
|
bool pum_external = true;
|
||||||
|
|
||||||
for (size_t i = 0; i < ui_count; i++) {
|
for (size_t i = 0; i < ui_count; i++) {
|
||||||
UI *ui = uis[i];
|
UI *ui = uis[i];
|
||||||
width = ui->width < width ? ui->width : width;
|
width = ui->width < width ? ui->width : width;
|
||||||
height = ui->height < height ? ui->height : height;
|
height = ui->height < height ? ui->height : height;
|
||||||
|
pum_external &= ui->pum_external;
|
||||||
}
|
}
|
||||||
|
|
||||||
row = col = 0;
|
row = col = 0;
|
||||||
screen_resize(width, height);
|
screen_resize(width, height);
|
||||||
|
pum_set_external(pum_external);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_resize(int new_width, int new_height)
|
void ui_resize(int new_width, int new_height)
|
||||||
@ -519,3 +533,4 @@ static void ui_mode_change(void)
|
|||||||
UI_CALL(mode_change, mode);
|
UI_CALL(mode_change, mode);
|
||||||
conceal_check_cursur_line();
|
conceal_check_cursur_line();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "api/private/defs.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool bold, underline, undercurl, italic, reverse;
|
bool bold, underline, undercurl, italic, reverse;
|
||||||
int foreground, background, special;
|
int foreground, background, special;
|
||||||
@ -13,7 +15,7 @@ typedef struct {
|
|||||||
typedef struct ui_t UI;
|
typedef struct ui_t UI;
|
||||||
|
|
||||||
struct ui_t {
|
struct ui_t {
|
||||||
bool rgb;
|
bool rgb, pum_external;
|
||||||
int width, height;
|
int width, height;
|
||||||
void *data;
|
void *data;
|
||||||
void (*resize)(UI *ui, int rows, int columns);
|
void (*resize)(UI *ui, int rows, int columns);
|
||||||
@ -39,6 +41,7 @@ struct ui_t {
|
|||||||
void (*suspend)(UI *ui);
|
void (*suspend)(UI *ui);
|
||||||
void (*set_title)(UI *ui, char *title);
|
void (*set_title)(UI *ui, char *title);
|
||||||
void (*set_icon)(UI *ui, char *icon);
|
void (*set_icon)(UI *ui, char *icon);
|
||||||
|
void (*event)(UI *ui, char *name, Array args, bool *args_consumed);
|
||||||
void (*stop)(UI *ui);
|
void (*stop)(UI *ui);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
|
|||||||
UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData));
|
UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData));
|
||||||
rv->ui = ui;
|
rv->ui = ui;
|
||||||
rv->bridge.rgb = ui->rgb;
|
rv->bridge.rgb = ui->rgb;
|
||||||
|
rv->bridge.pum_external = ui->pum_external;
|
||||||
rv->bridge.stop = ui_bridge_stop;
|
rv->bridge.stop = ui_bridge_stop;
|
||||||
rv->bridge.resize = ui_bridge_resize;
|
rv->bridge.resize = ui_bridge_resize;
|
||||||
rv->bridge.clear = ui_bridge_clear;
|
rv->bridge.clear = ui_bridge_clear;
|
||||||
|
@ -12,7 +12,7 @@ describe('TermClose event', function()
|
|||||||
nvim('set_option', 'shell', nvim_dir .. '/shell-test')
|
nvim('set_option', 'shell', nvim_dir .. '/shell-test')
|
||||||
nvim('set_option', 'shellcmdflag', 'EXE')
|
nvim('set_option', 'shellcmdflag', 'EXE')
|
||||||
screen = Screen.new(20, 4)
|
screen = Screen.new(20, 4)
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('works as expected', function()
|
it('works as expected', function()
|
||||||
|
@ -306,6 +306,10 @@ local function nvim(method, ...)
|
|||||||
return request('vim_'..method, ...)
|
return request('vim_'..method, ...)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function ui(method, ...)
|
||||||
|
return request('nvim_ui_'..method, ...)
|
||||||
|
end
|
||||||
|
|
||||||
local function nvim_async(method, ...)
|
local function nvim_async(method, ...)
|
||||||
session:notify('vim_'..method, ...)
|
session:notify('vim_'..method, ...)
|
||||||
end
|
end
|
||||||
@ -432,6 +436,7 @@ end
|
|||||||
|
|
||||||
local funcs = create_callindex(nvim_call)
|
local funcs = create_callindex(nvim_call)
|
||||||
local meths = create_callindex(nvim)
|
local meths = create_callindex(nvim)
|
||||||
|
local uimeths = create_callindex(ui)
|
||||||
local bufmeths = create_callindex(buffer)
|
local bufmeths = create_callindex(buffer)
|
||||||
local winmeths = create_callindex(window)
|
local winmeths = create_callindex(window)
|
||||||
local tabmeths = create_callindex(tabpage)
|
local tabmeths = create_callindex(tabpage)
|
||||||
@ -490,6 +495,7 @@ return function(after_each)
|
|||||||
bufmeths = bufmeths,
|
bufmeths = bufmeths,
|
||||||
winmeths = winmeths,
|
winmeths = winmeths,
|
||||||
tabmeths = tabmeths,
|
tabmeths = tabmeths,
|
||||||
|
uimeths = uimeths,
|
||||||
curbufmeths = curbufmeths,
|
curbufmeths = curbufmeths,
|
||||||
curwinmeths = curwinmeths,
|
curwinmeths = curwinmeths,
|
||||||
curtabmeths = curtabmeths,
|
curtabmeths = curtabmeths,
|
||||||
|
@ -135,7 +135,7 @@ describe('cursor with customized highlighting', function()
|
|||||||
[2] = {foreground = 55, background = 56},
|
[2] = {foreground = 55, background = 56},
|
||||||
[3] = {bold = true},
|
[3] = {bold = true},
|
||||||
})
|
})
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
execute('call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
execute('call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ local eq = helpers.eq
|
|||||||
describe(':edit term://*', function()
|
describe(':edit term://*', function()
|
||||||
local get_screen = function(columns, lines)
|
local get_screen = function(columns, lines)
|
||||||
local scr = screen.new(columns, lines)
|
local scr = screen.new(columns, lines)
|
||||||
scr:attach(false)
|
scr:attach({rgb=false})
|
||||||
return scr
|
return scr
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ describe(':terminal', function()
|
|||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
screen = Screen.new(50, 4)
|
screen = Screen.new(50, 4)
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
nvim('set_option', 'shell', nvim_dir..'/shell-test')
|
nvim('set_option', 'shell', nvim_dir..'/shell-test')
|
||||||
nvim('set_option', 'shellcmdflag', 'EXE')
|
nvim('set_option', 'shellcmdflag', 'EXE')
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ local function screen_setup(extra_height, command)
|
|||||||
[9] = {foreground = 4},
|
[9] = {foreground = 4},
|
||||||
})
|
})
|
||||||
|
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
-- tty-test puts the terminal into raw mode and echoes all input. tests are
|
-- tty-test puts the terminal into raw mode and echoes all input. tests are
|
||||||
-- done by feeding it with terminfo codes to control the display and
|
-- done by feeding it with terminfo codes to control the display and
|
||||||
-- verifying output with screen:expect.
|
-- verifying output with screen:expect.
|
||||||
|
@ -25,7 +25,7 @@ describe('terminal window highlighting', function()
|
|||||||
[10] = {reverse = true},
|
[10] = {reverse = true},
|
||||||
[11] = {background = 11},
|
[11] = {background = 11},
|
||||||
})
|
})
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
tty ready |
|
tty ready |
|
||||||
@ -127,7 +127,7 @@ describe('terminal window highlighting with custom palette', function()
|
|||||||
[8] = {background = 11},
|
[8] = {background = 11},
|
||||||
[9] = {bold = true},
|
[9] = {bold = true},
|
||||||
})
|
})
|
||||||
screen:attach(true)
|
screen:attach({rgb=true})
|
||||||
nvim('set_var', 'terminal_color_3', '#123456')
|
nvim('set_var', 'terminal_color_3', '#123456')
|
||||||
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
execute('enew | call termopen(["'..nvim_dir..'/tty-test"]) | startinsert')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
@ -185,7 +185,7 @@ describe('synIDattr()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('returns gui-color if RGB-capable UI is attached', function()
|
it('returns gui-color if RGB-capable UI is attached', function()
|
||||||
screen:attach(true)
|
screen:attach({rgb=true})
|
||||||
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")'))
|
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg")'))
|
||||||
eq('Black', eval('synIDattr(hlID("Normal"), "bg")'))
|
eq('Black', eval('synIDattr(hlID("Normal"), "bg")'))
|
||||||
eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")'))
|
eq('Salmon', eval('synIDattr(hlID("Keyword"), "fg")'))
|
||||||
@ -193,7 +193,7 @@ describe('synIDattr()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('returns #RRGGBB value for fg#/bg#/sp#', function()
|
it('returns #RRGGBB value for fg#/bg#/sp#', function()
|
||||||
screen:attach(true)
|
screen:attach({rgb=true})
|
||||||
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")'))
|
eq('#ff0000', eval('synIDattr(hlID("Normal"), "fg#")'))
|
||||||
eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")'))
|
eq('#000000', eval('synIDattr(hlID("Normal"), "bg#")'))
|
||||||
eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")'))
|
eq('#fa8072', eval('synIDattr(hlID("Keyword"), "fg#")'))
|
||||||
@ -201,7 +201,7 @@ describe('synIDattr()', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('returns color number if non-GUI', function()
|
it('returns color number if non-GUI', function()
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
|
eq('252', eval('synIDattr(hlID("Normal"), "fg")'))
|
||||||
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
|
eq('79', eval('synIDattr(hlID("Keyword"), "fg")'))
|
||||||
end)
|
end)
|
||||||
|
@ -331,7 +331,7 @@ describe('terminal prints more lines than the screen height and exits', function
|
|||||||
it('will push extra lines to scrollback', function()
|
it('will push extra lines to scrollback', function()
|
||||||
clear()
|
clear()
|
||||||
local screen = Screen.new(50, 7)
|
local screen = Screen.new(50, 7)
|
||||||
screen:attach(false)
|
screen:attach({rgb=false})
|
||||||
execute('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert')
|
execute('call termopen(["'..nvim_dir..'/tty-test", "10"]) | startinsert')
|
||||||
wait()
|
wait()
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
-- use `screen:snapshot_util({},true)`
|
-- use `screen:snapshot_util({},true)`
|
||||||
|
|
||||||
local helpers = require('test.functional.helpers')(nil)
|
local helpers = require('test.functional.helpers')(nil)
|
||||||
local request, run = helpers.request, helpers.run
|
local request, run, uimeths = helpers.request, helpers.run, helpers.uimeths
|
||||||
local dedent = helpers.dedent
|
local dedent = helpers.dedent
|
||||||
|
|
||||||
local Screen = {}
|
local Screen = {}
|
||||||
@ -192,22 +192,22 @@ function Screen:set_default_attr_ignore(attr_ignore)
|
|||||||
self._default_attr_ignore = attr_ignore
|
self._default_attr_ignore = attr_ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:attach(rgb)
|
function Screen:attach(options)
|
||||||
if rgb == nil then
|
if options == nil then
|
||||||
rgb = true
|
options = {rgb=true}
|
||||||
end
|
end
|
||||||
request('ui_attach', self._width, self._height, rgb)
|
uimeths.attach(self._width, self._height, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:detach()
|
function Screen:detach()
|
||||||
request('ui_detach')
|
uimeths.detach()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:try_resize(columns, rows)
|
function Screen:try_resize(columns, rows)
|
||||||
request('ui_try_resize', columns, rows)
|
uimeths.try_resize(columns, rows)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Screen:expect(expected, attr_ids, attr_ignore)
|
function Screen:expect(expected, attr_ids, attr_ignore, condition)
|
||||||
-- remove the last line and dedent
|
-- remove the last line and dedent
|
||||||
expected = dedent(expected:gsub('\n[ ]+$', ''))
|
expected = dedent(expected:gsub('\n[ ]+$', ''))
|
||||||
local expected_rows = {}
|
local expected_rows = {}
|
||||||
@ -219,6 +219,12 @@ function Screen:expect(expected, attr_ids, attr_ignore)
|
|||||||
local ids = attr_ids or self._default_attr_ids
|
local ids = attr_ids or self._default_attr_ids
|
||||||
local ignore = attr_ignore or self._default_attr_ignore
|
local ignore = attr_ignore or self._default_attr_ignore
|
||||||
self:wait(function()
|
self:wait(function()
|
||||||
|
if condition ~= nil then
|
||||||
|
local status, res = pcall(condition)
|
||||||
|
if not status then
|
||||||
|
return tostring(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
local actual_rows = {}
|
local actual_rows = {}
|
||||||
for i = 1, self._height do
|
for i = 1, self._height do
|
||||||
actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore)
|
actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore)
|
||||||
@ -303,12 +309,20 @@ function Screen:_redraw(updates)
|
|||||||
local method = update[1]
|
local method = update[1]
|
||||||
for i = 2, #update do
|
for i = 2, #update do
|
||||||
local handler = self['_handle_'..method]
|
local handler = self['_handle_'..method]
|
||||||
handler(self, unpack(update[i]))
|
if handler ~= nil then
|
||||||
|
handler(self, unpack(update[i]))
|
||||||
|
else
|
||||||
|
self._on_event(method, update[i])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
-- print(self:_current_screen())
|
-- print(self:_current_screen())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Screen:set_on_event_handler(callback)
|
||||||
|
self._on_event = callback
|
||||||
|
end
|
||||||
|
|
||||||
function Screen:_handle_resize(width, height)
|
function Screen:_handle_resize(width, height)
|
||||||
local rows = {}
|
local rows = {}
|
||||||
for _ = 1, height do
|
for _ = 1, height do
|
||||||
|
@ -755,4 +755,106 @@ describe('completion', function()
|
|||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('External completion popupmenu', function()
|
||||||
|
local screen
|
||||||
|
local items, selected, anchor
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
screen = Screen.new(60, 8)
|
||||||
|
screen:attach({rgb=true, popupmenu_external=true})
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold=true, foreground=Screen.colors.Blue},
|
||||||
|
[2] = {bold = true},
|
||||||
|
})
|
||||||
|
screen:set_on_event_handler(function(name, data)
|
||||||
|
if name == "popupmenu_show" then
|
||||||
|
local row, col
|
||||||
|
items, selected, row, col = unpack(data)
|
||||||
|
anchor = {row, col}
|
||||||
|
elseif name == "popupmenu_select" then
|
||||||
|
selected = data[1]
|
||||||
|
elseif name == "popupmenu_hide" then
|
||||||
|
items = nil
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works', function()
|
||||||
|
source([[
|
||||||
|
function! TestComplete() abort
|
||||||
|
call complete(1, ['foo', 'bar', 'spam'])
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
]])
|
||||||
|
local expected = {
|
||||||
|
{'foo', '', '', ''},
|
||||||
|
{'bar', '', '', ''},
|
||||||
|
{'spam', '', '', ''},
|
||||||
|
}
|
||||||
|
feed('o<C-r>=TestComplete()<CR>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
foo^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]], nil, nil, function()
|
||||||
|
eq(expected, items)
|
||||||
|
eq(0, selected)
|
||||||
|
eq({1,0}, anchor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
feed('<c-p>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]], nil, nil, function()
|
||||||
|
eq(expected, items)
|
||||||
|
eq(-1, selected)
|
||||||
|
eq({1,0}, anchor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- down moves the selection in the menu, but does not insert anything
|
||||||
|
feed('<down><down>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]], nil, nil, function()
|
||||||
|
eq(expected, items)
|
||||||
|
eq(1, selected)
|
||||||
|
eq({1,0}, anchor)
|
||||||
|
end)
|
||||||
|
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
bar^ |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{2:-- INSERT --} |
|
||||||
|
]], nil, nil, function()
|
||||||
|
eq(nil, items) -- popupmenu was hidden
|
||||||
|
end)
|
||||||
|
end)
|
||||||
end)
|
end)
|
||||||
|
Loading…
Reference in New Issue
Block a user