mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 03:18:16 +00:00
Merge pull request #18375 from bfredl/tui_rework
feat(ui): refactor TUI from thread to separate process
This commit is contained in:
@@ -143,9 +143,9 @@ set(NVIM_VERSION_PATCH 0)
|
|||||||
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers
|
||||||
|
|
||||||
# API level
|
# API level
|
||||||
set(NVIM_API_LEVEL 10) # Bump this after any API change.
|
set(NVIM_API_LEVEL 11) # Bump this after any API change.
|
||||||
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
|
set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change.
|
||||||
set(NVIM_API_PRERELEASE false)
|
set(NVIM_API_PRERELEASE true)
|
||||||
|
|
||||||
set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
|
set(NVIM_VERSION_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
|
||||||
# NVIM_VERSION_CFLAGS set further below.
|
# NVIM_VERSION_CFLAGS set further below.
|
||||||
@@ -498,34 +498,30 @@ if(MSGPACK_HAS_FLOAT32)
|
|||||||
add_definitions(-DNVIM_MSGPACK_HAS_FLOAT32)
|
add_definitions(-DNVIM_MSGPACK_HAS_FLOAT32)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
option(FEAT_TUI "Enable the Terminal UI" ON)
|
find_package(UNIBILIUM 2.0 REQUIRED)
|
||||||
|
include_directories(SYSTEM ${UNIBILIUM_INCLUDE_DIRS})
|
||||||
|
|
||||||
if(FEAT_TUI)
|
list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
|
||||||
find_package(UNIBILIUM 2.0 REQUIRED)
|
list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
|
||||||
include_directories(SYSTEM ${UNIBILIUM_INCLUDE_DIRS})
|
check_c_source_compiles("
|
||||||
|
#include <unibilium.h>
|
||||||
|
|
||||||
list(APPEND CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
|
int
|
||||||
list(APPEND CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
|
main(void)
|
||||||
check_c_source_compiles("
|
{
|
||||||
#include <unibilium.h>
|
unibi_str_from_var(unibi_var_from_str(\"\"));
|
||||||
|
return unibi_num_from_var(unibi_var_from_num(0));
|
||||||
int
|
}
|
||||||
main(void)
|
" UNIBI_HAS_VAR_FROM)
|
||||||
{
|
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
|
||||||
unibi_str_from_var(unibi_var_from_str(\"\"));
|
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
|
||||||
return unibi_num_from_var(unibi_var_from_num(0));
|
if(UNIBI_HAS_VAR_FROM)
|
||||||
}
|
add_definitions(-DNVIM_UNIBI_HAS_VAR_FROM)
|
||||||
" UNIBI_HAS_VAR_FROM)
|
|
||||||
list(REMOVE_ITEM CMAKE_REQUIRED_INCLUDES "${UNIBILIUM_INCLUDE_DIRS}")
|
|
||||||
list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES "${UNIBILIUM_LIBRARIES}")
|
|
||||||
if(UNIBI_HAS_VAR_FROM)
|
|
||||||
add_definitions(-DNVIM_UNIBI_HAS_VAR_FROM)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(LibTermkey 0.22 REQUIRED)
|
|
||||||
include_directories(SYSTEM ${LIBTERMKEY_INCLUDE_DIRS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(LibTermkey 0.22 REQUIRED)
|
||||||
|
include_directories(SYSTEM ${LIBTERMKEY_INCLUDE_DIRS})
|
||||||
|
|
||||||
find_package(LIBVTERM 0.3 REQUIRED)
|
find_package(LIBVTERM 0.3 REQUIRED)
|
||||||
include_directories(SYSTEM ${LIBVTERM_INCLUDE_DIRS})
|
include_directories(SYSTEM ${LIBVTERM_INCLUDE_DIRS})
|
||||||
|
|
||||||
|
@@ -56,8 +56,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#cmakedefine HAVE_FORKPTY
|
#cmakedefine HAVE_FORKPTY
|
||||||
|
|
||||||
#cmakedefine FEAT_TUI
|
|
||||||
|
|
||||||
#ifndef UNIT_TESTING
|
#ifndef UNIT_TESTING
|
||||||
#cmakedefine LOG_LIST_ACTIONS
|
#cmakedefine LOG_LIST_ACTIONS
|
||||||
#endif
|
#endif
|
||||||
|
@@ -3471,6 +3471,12 @@ nvim_ui_pum_set_height({height}) *nvim_ui_pum_set_height()*
|
|||||||
Parameters: ~
|
Parameters: ~
|
||||||
• {height} Popupmenu height, must be greater than zero.
|
• {height} Popupmenu height, must be greater than zero.
|
||||||
|
|
||||||
|
nvim_ui_set_focus({gained}) *nvim_ui_set_focus()*
|
||||||
|
Tells the nvim server if focus was gained or lost by the GUI.
|
||||||
|
|
||||||
|
Attributes: ~
|
||||||
|
|RPC| only
|
||||||
|
|
||||||
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
|
nvim_ui_set_option({name}, {value}) *nvim_ui_set_option()*
|
||||||
TODO: Documentation
|
TODO: Documentation
|
||||||
|
|
||||||
|
@@ -101,6 +101,11 @@ The following new APIs or features were added.
|
|||||||
|
|
||||||
See https://github.com/neovim/neovim/pull/14537.
|
See https://github.com/neovim/neovim/pull/14537.
|
||||||
|
|
||||||
|
• |--remote-ui| option was added to connect to a remote instance and display
|
||||||
|
in it in a |TUI| in the local terminal. This can be used run a headless nvim
|
||||||
|
instance in the background and display its UI on demand, which previously
|
||||||
|
only was possible usiing a external UI implementation.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
CHANGED FEATURES *news-changes*
|
CHANGED FEATURES *news-changes*
|
||||||
|
|
||||||
@@ -109,6 +114,14 @@ The following changes to existing APIs or features add new behavior.
|
|||||||
• 'exrc' now supports `.nvim.lua` file.
|
• 'exrc' now supports `.nvim.lua` file.
|
||||||
• 'exrc' is no longer marked deprecated.
|
• 'exrc' is no longer marked deprecated.
|
||||||
|
|
||||||
|
• The |TUI| is changed to run in a separate process (previously, a separate
|
||||||
|
thread was used). This is not supposed to be a visible change to the user,
|
||||||
|
but might be the cause of subtle changes of behavior and bugs.
|
||||||
|
|
||||||
|
Previously, the TUI could be disabled as a build time feature (+tui/-tui),
|
||||||
|
resulting in a nvim binary which only could be run headless or embedded
|
||||||
|
in an external process. As of this version, TUI is always avalibale.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
REMOVED FEATURES *news-removed*
|
REMOVED FEATURES *news-removed*
|
||||||
|
|
||||||
|
@@ -52,6 +52,10 @@ The following command line arguments are available:
|
|||||||
*--remote-expr*
|
*--remote-expr*
|
||||||
--remote-expr {expr} Evaluate {expr} in server and print the result
|
--remote-expr {expr} Evaluate {expr} in server and print the result
|
||||||
on stdout.
|
on stdout.
|
||||||
|
*--remote-ui*
|
||||||
|
--remote-ui Display the UI of the server in the terminal.
|
||||||
|
Fully interactive: keyboard and mouse input
|
||||||
|
are forwarded to the server.
|
||||||
*--server*
|
*--server*
|
||||||
--server {addr} Connect to the named pipe or socket at the
|
--server {addr} Connect to the named pipe or socket at the
|
||||||
given address for executing remote commands.
|
given address for executing remote commands.
|
||||||
|
@@ -56,6 +56,8 @@ with these (optional) keys:
|
|||||||
- `stdin_fd` Read buffer from `fd` as if it was a stdin pipe
|
- `stdin_fd` Read buffer from `fd` as if it was a stdin pipe
|
||||||
This option can only used by |--embed| ui,
|
This option can only used by |--embed| ui,
|
||||||
see |ui-startup-stdin|.
|
see |ui-startup-stdin|.
|
||||||
|
`stdin_tty` Tells if `stdin` is a `tty` or not.
|
||||||
|
`stdout_tty` Tells if `stdout` is a `tty` or not.
|
||||||
|
|
||||||
Specifying an unknown option is an error; UIs can check the |api-metadata|
|
Specifying an unknown option is an error; UIs can check the |api-metadata|
|
||||||
`ui_options` key for supported options.
|
`ui_options` key for supported options.
|
||||||
|
@@ -39,7 +39,6 @@ set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.generat
|
|||||||
set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h)
|
set(GENERATED_UI_EVENTS ${GENERATED_DIR}/ui_events.generated.h)
|
||||||
set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h)
|
set(GENERATED_UI_EVENTS_CALL ${GENERATED_DIR}/ui_events_call.generated.h)
|
||||||
set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h)
|
set(GENERATED_UI_EVENTS_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h)
|
||||||
set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.generated.h)
|
|
||||||
set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h)
|
set(GENERATED_UI_EVENTS_CLIENT ${GENERATED_DIR}/ui_events_client.generated.h)
|
||||||
set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h)
|
set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata.generated.h)
|
||||||
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
|
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
|
||||||
@@ -111,9 +110,6 @@ foreach(subdir
|
|||||||
viml
|
viml
|
||||||
viml/parser
|
viml/parser
|
||||||
)
|
)
|
||||||
if(${subdir} MATCHES "tui" AND NOT FEAT_TUI)
|
|
||||||
continue()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
|
file(MAKE_DIRECTORY ${GENERATED_DIR}/${subdir})
|
||||||
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
|
file(MAKE_DIRECTORY ${GENERATED_INCLUDES_DIR}/${subdir})
|
||||||
@@ -220,7 +216,6 @@ foreach(sfile ${NVIM_SOURCES}
|
|||||||
${GENERATED_API_DISPATCH}
|
${GENERATED_API_DISPATCH}
|
||||||
"${GENERATED_UI_EVENTS_CALL}"
|
"${GENERATED_UI_EVENTS_CALL}"
|
||||||
"${GENERATED_UI_EVENTS_REMOTE}"
|
"${GENERATED_UI_EVENTS_REMOTE}"
|
||||||
"${GENERATED_UI_EVENTS_BRIDGE}"
|
|
||||||
"${GENERATED_KEYSETS}"
|
"${GENERATED_KEYSETS}"
|
||||||
"${GENERATED_UI_EVENTS_CLIENT}"
|
"${GENERATED_UI_EVENTS_CLIENT}"
|
||||||
)
|
)
|
||||||
@@ -318,7 +313,6 @@ add_custom_command(
|
|||||||
OUTPUT ${GENERATED_UI_EVENTS}
|
OUTPUT ${GENERATED_UI_EVENTS}
|
||||||
${GENERATED_UI_EVENTS_CALL}
|
${GENERATED_UI_EVENTS_CALL}
|
||||||
${GENERATED_UI_EVENTS_REMOTE}
|
${GENERATED_UI_EVENTS_REMOTE}
|
||||||
${GENERATED_UI_EVENTS_BRIDGE}
|
|
||||||
${GENERATED_UI_EVENTS_METADATA}
|
${GENERATED_UI_EVENTS_METADATA}
|
||||||
${GENERATED_UI_EVENTS_CLIENT}
|
${GENERATED_UI_EVENTS_CLIENT}
|
||||||
COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}
|
||||||
@@ -326,7 +320,6 @@ add_custom_command(
|
|||||||
${GENERATED_UI_EVENTS}
|
${GENERATED_UI_EVENTS}
|
||||||
${GENERATED_UI_EVENTS_CALL}
|
${GENERATED_UI_EVENTS_CALL}
|
||||||
${GENERATED_UI_EVENTS_REMOTE}
|
${GENERATED_UI_EVENTS_REMOTE}
|
||||||
${GENERATED_UI_EVENTS_BRIDGE}
|
|
||||||
${GENERATED_UI_EVENTS_METADATA}
|
${GENERATED_UI_EVENTS_METADATA}
|
||||||
${GENERATED_UI_EVENTS_CLIENT}
|
${GENERATED_UI_EVENTS_CLIENT}
|
||||||
DEPENDS
|
DEPENDS
|
||||||
|
@@ -126,6 +126,8 @@ return {
|
|||||||
"global_link";
|
"global_link";
|
||||||
"fallback";
|
"fallback";
|
||||||
"blend";
|
"blend";
|
||||||
|
"fg_indexed";
|
||||||
|
"bg_indexed";
|
||||||
};
|
};
|
||||||
highlight_cterm = {
|
highlight_cterm = {
|
||||||
"bold";
|
"bold";
|
||||||
|
@@ -14,9 +14,6 @@
|
|||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/api/ui.h"
|
#include "nvim/api/ui.h"
|
||||||
#include "nvim/channel.h"
|
#include "nvim/channel.h"
|
||||||
#include "nvim/event/loop.h"
|
|
||||||
#include "nvim/event/wstream.h"
|
|
||||||
#include "nvim/globals.h"
|
|
||||||
#include "nvim/grid.h"
|
#include "nvim/grid.h"
|
||||||
#include "nvim/highlight.h"
|
#include "nvim/highlight.h"
|
||||||
#include "nvim/main.h"
|
#include "nvim/main.h"
|
||||||
@@ -30,6 +27,7 @@
|
|||||||
#include "nvim/types.h"
|
#include "nvim/types.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
|
#include "nvim/window.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t channel_id;
|
uint64_t channel_id;
|
||||||
@@ -285,6 +283,19 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, Boolean enabl
|
|||||||
api_free_dictionary(opts);
|
api_free_dictionary(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tells the nvim server if focus was gained or lost by the GUI
|
||||||
|
void nvim_ui_set_focus(uint64_t channel_id, Boolean gained, Error *error)
|
||||||
|
FUNC_API_SINCE(11) FUNC_API_REMOTE_ONLY
|
||||||
|
{
|
||||||
|
if (!pmap_has(uint64_t)(&connected_uis, channel_id)) {
|
||||||
|
api_set_error(error, kErrorTypeException,
|
||||||
|
"UI not attached to channel: %" PRId64, channel_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_autocmd_focusgained((bool)gained);
|
||||||
|
}
|
||||||
|
|
||||||
/// Deactivates UI events on the channel.
|
/// Deactivates UI events on the channel.
|
||||||
///
|
///
|
||||||
/// Removes the client from the list of UIs. |nvim_list_uis()|
|
/// Removes the client from the list of UIs. |nvim_list_uis()|
|
||||||
@@ -404,6 +415,24 @@ static void ui_set_option(UI *ui, bool init, String name, Object value, Error *e
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strequal(name.data, "stdin_tty")) {
|
||||||
|
if (value.type != kObjectTypeBoolean) {
|
||||||
|
api_set_error(error, kErrorTypeValidation, "stdin_tty must be a Boolean");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stdin_isatty = value.data.boolean;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strequal(name.data, "stdout_tty")) {
|
||||||
|
if (value.type != kObjectTypeBoolean) {
|
||||||
|
api_set_error(error, kErrorTypeValidation, "stdout_tty must be a Boolean");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stdout_isatty = value.data.boolean;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// LEGACY: Deprecated option, use `ext_cmdline` instead.
|
// LEGACY: Deprecated option, use `ext_cmdline` instead.
|
||||||
bool is_popupmenu = strequal(name.data, "popupmenu_external");
|
bool is_popupmenu = strequal(name.data, "popupmenu_external");
|
||||||
|
|
||||||
|
@@ -31,7 +31,7 @@ void visual_bell(void)
|
|||||||
void flush(void)
|
void flush(void)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
|
||||||
void suspend(void)
|
void suspend(void)
|
||||||
FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(3);
|
||||||
void set_title(String title)
|
void set_title(String title)
|
||||||
FUNC_API_SINCE(3);
|
FUNC_API_SINCE(3);
|
||||||
void set_icon(String icon)
|
void set_icon(String icon)
|
||||||
@@ -39,7 +39,7 @@ void set_icon(String icon)
|
|||||||
void screenshot(String path)
|
void screenshot(String path)
|
||||||
FUNC_API_SINCE(7) FUNC_API_REMOTE_IMPL;
|
FUNC_API_SINCE(7) FUNC_API_REMOTE_IMPL;
|
||||||
void option_set(String name, Object value)
|
void option_set(String name, Object value)
|
||||||
FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(4);
|
||||||
// Stop event is not exported as such, represented by EOF in the msgpack stream.
|
// Stop event is not exported as such, represented by EOF in the msgpack stream.
|
||||||
void stop(void)
|
void stop(void)
|
||||||
FUNC_API_NOEXPORT;
|
FUNC_API_NOEXPORT;
|
||||||
@@ -73,9 +73,9 @@ void default_colors_set(Integer rgb_fg, Integer rgb_bg, Integer rgb_sp, Integer
|
|||||||
Integer cterm_bg)
|
Integer cterm_bg)
|
||||||
FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL;
|
FUNC_API_SINCE(4) FUNC_API_REMOTE_IMPL;
|
||||||
void hl_attr_define(Integer id, HlAttrs rgb_attrs, HlAttrs cterm_attrs, Array info)
|
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;
|
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL;
|
||||||
void hl_group_set(String name, Integer id)
|
void hl_group_set(String name, Integer id)
|
||||||
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(6);
|
||||||
void grid_resize(Integer grid, Integer width, Integer height)
|
void grid_resize(Integer grid, Integer width, Integer height)
|
||||||
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL;
|
FUNC_API_SINCE(5) FUNC_API_REMOTE_IMPL FUNC_API_COMPOSITOR_IMPL FUNC_API_CLIENT_IMPL;
|
||||||
void grid_clear(Integer grid)
|
void grid_clear(Integer grid)
|
||||||
@@ -112,8 +112,9 @@ void win_hide(Integer grid)
|
|||||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
void win_close(Integer grid)
|
void win_close(Integer grid)
|
||||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
|
||||||
void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
|
void msg_set_pos(Integer grid, Integer row, Boolean scrolled, String sep_char)
|
||||||
FUNC_API_SINCE(6) FUNC_API_BRIDGE_IMPL FUNC_API_COMPOSITOR_IMPL;
|
FUNC_API_SINCE(6) FUNC_API_COMPOSITOR_IMPL;
|
||||||
|
|
||||||
void win_viewport(Integer grid, Window win, Integer topline, Integer botline, Integer curline,
|
void win_viewport(Integer grid, Window win, Integer topline, Integer botline, Integer curline,
|
||||||
Integer curcol, Integer line_count)
|
Integer curcol, Integer line_count)
|
||||||
@@ -149,11 +150,11 @@ void cmdline_block_hide(void)
|
|||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY;
|
||||||
|
|
||||||
void wildmenu_show(Array items)
|
void wildmenu_show(Array items)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
|
||||||
void wildmenu_select(Integer selected)
|
void wildmenu_select(Integer selected)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
|
||||||
void wildmenu_hide(void)
|
void wildmenu_hide(void)
|
||||||
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL;
|
FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL;
|
||||||
|
|
||||||
void msg_show(String kind, Array content, Boolean replace_last)
|
void msg_show(String kind, Array content, Boolean replace_last)
|
||||||
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
|
||||||
|
@@ -2732,22 +2732,7 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
|
|||||||
|
|
||||||
// FocusGained
|
// FocusGained
|
||||||
|
|
||||||
static void focusgained_event(void **argv)
|
void do_autocmd_focusgained(bool gained)
|
||||||
{
|
|
||||||
bool *gainedp = argv[0];
|
|
||||||
do_autocmd_focusgained(*gainedp);
|
|
||||||
xfree(gainedp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void autocmd_schedule_focusgained(bool gained)
|
|
||||||
{
|
|
||||||
bool *gainedp = xmalloc(sizeof(*gainedp));
|
|
||||||
*gainedp = gained;
|
|
||||||
loop_schedule_deferred(&main_loop,
|
|
||||||
event_create(focusgained_event, 1, gainedp));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_autocmd_focusgained(bool gained)
|
|
||||||
{
|
{
|
||||||
static bool recursive = false;
|
static bool recursive = false;
|
||||||
static Timestamp last_time = (time_t)0;
|
static Timestamp last_time = (time_t)0;
|
||||||
|
@@ -40,11 +40,19 @@ int libuv_process_spawn(LibuvProcess *uvproc)
|
|||||||
#endif
|
#endif
|
||||||
uvproc->uvopts.exit_cb = exit_cb;
|
uvproc->uvopts.exit_cb = exit_cb;
|
||||||
uvproc->uvopts.cwd = proc->cwd;
|
uvproc->uvopts.cwd = proc->cwd;
|
||||||
|
|
||||||
uvproc->uvopts.stdio = uvproc->uvstdio;
|
uvproc->uvopts.stdio = uvproc->uvstdio;
|
||||||
uvproc->uvopts.stdio_count = 3;
|
uvproc->uvopts.stdio_count = 3;
|
||||||
uvproc->uvstdio[0].flags = UV_IGNORE;
|
uvproc->uvstdio[0].flags = UV_IGNORE;
|
||||||
uvproc->uvstdio[1].flags = UV_IGNORE;
|
uvproc->uvstdio[1].flags = UV_IGNORE;
|
||||||
uvproc->uvstdio[2].flags = UV_IGNORE;
|
uvproc->uvstdio[2].flags = UV_IGNORE;
|
||||||
|
|
||||||
|
if (ui_client_forward_stdin) {
|
||||||
|
assert(UI_CLIENT_STDIN_FD == 3);
|
||||||
|
uvproc->uvopts.stdio_count = 4;
|
||||||
|
uvproc->uvstdio[3].data.fd = 0;
|
||||||
|
uvproc->uvstdio[3].flags = UV_INHERIT_FD;
|
||||||
|
}
|
||||||
uvproc->uv.data = proc;
|
uvproc->uv.data = proc;
|
||||||
|
|
||||||
if (proc->env) {
|
if (proc->env) {
|
||||||
|
@@ -10,7 +10,7 @@ typedef struct libuv_process {
|
|||||||
Process process;
|
Process process;
|
||||||
uv_process_t uv;
|
uv_process_t uv;
|
||||||
uv_process_options_t uvopts;
|
uv_process_options_t uvopts;
|
||||||
uv_stdio_container_t uvstdio[3];
|
uv_stdio_container_t uvstdio[4];
|
||||||
} LibuvProcess;
|
} LibuvProcess;
|
||||||
|
|
||||||
static inline LibuvProcess libuv_process_init(Loop *loop, void *data)
|
static inline LibuvProcess libuv_process_init(Loop *loop, void *data)
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
#include "nvim/globals.h"
|
#include "nvim/globals.h"
|
||||||
#include "nvim/log.h"
|
#include "nvim/log.h"
|
||||||
#include "nvim/macros.h"
|
#include "nvim/macros.h"
|
||||||
|
#include "nvim/main.h"
|
||||||
#include "nvim/os/process.h"
|
#include "nvim/os/process.h"
|
||||||
#include "nvim/os/pty_process.h"
|
#include "nvim/os/pty_process.h"
|
||||||
#include "nvim/os/shell.h"
|
#include "nvim/os/shell.h"
|
||||||
@@ -35,6 +36,9 @@ void __gcov_flush(void);
|
|||||||
|
|
||||||
static bool process_is_tearing_down = false;
|
static bool process_is_tearing_down = false;
|
||||||
|
|
||||||
|
// Delay exit until handles are closed, to avoid deadlocks
|
||||||
|
static int exit_need_delay = 0;
|
||||||
|
|
||||||
/// @returns zero on success, or negative error code
|
/// @returns zero on success, or negative error code
|
||||||
int process_spawn(Process *proc, bool in, bool out, bool err)
|
int process_spawn(Process *proc, bool in, bool out, bool err)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
@@ -398,12 +402,41 @@ static void process_close_handles(void **argv)
|
|||||||
exit_need_delay--;
|
exit_need_delay--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void exit_delay_cb(uv_timer_t *handle)
|
||||||
|
{
|
||||||
|
uv_timer_stop(&main_loop.exit_delay_timer);
|
||||||
|
multiqueue_put(main_loop.fast_events, exit_event, 1, main_loop.exit_delay_timer.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exit_event(void **argv)
|
||||||
|
{
|
||||||
|
int status = (int)(intptr_t)argv[0];
|
||||||
|
if (exit_need_delay) {
|
||||||
|
main_loop.exit_delay_timer.data = argv[0];
|
||||||
|
uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exiting) {
|
||||||
|
os_exit(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_from_channel(int status)
|
||||||
|
{
|
||||||
|
multiqueue_put(main_loop.fast_events, exit_event, 1, status);
|
||||||
|
}
|
||||||
|
|
||||||
static void on_process_exit(Process *proc)
|
static void on_process_exit(Process *proc)
|
||||||
{
|
{
|
||||||
Loop *loop = proc->loop;
|
Loop *loop = proc->loop;
|
||||||
ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
|
ILOG("exited: pid=%d status=%d stoptime=%" PRIu64, proc->pid, proc->status,
|
||||||
proc->stopped_time);
|
proc->stopped_time);
|
||||||
|
|
||||||
|
if (ui_client_channel_id) {
|
||||||
|
exit_from_channel(proc->status);
|
||||||
|
}
|
||||||
|
|
||||||
// Process has terminated, but there could still be data to be read from the
|
// Process has terminated, but there could still be data to be read from the
|
||||||
// OS. We are still in the libuv loop, so we cannot call code that polls for
|
// OS. We are still in the libuv loop, so we cannot call code that polls for
|
||||||
// more data directly. Instead delay the reading after the libuv loop by
|
// more data directly. Instead delay the reading after the libuv loop by
|
||||||
|
@@ -1931,9 +1931,11 @@ static char *do_one_cmd(char **cmdlinep, int flags, cstack_T *cstack, LineGetter
|
|||||||
|
|
||||||
profile_cmd(&ea, cstack, fgetline, cookie);
|
profile_cmd(&ea, cstack, fgetline, cookie);
|
||||||
|
|
||||||
// May go to debug mode. If this happens and the ">quit" debug command is
|
if (!exiting) {
|
||||||
// used, throw an interrupt exception and skip the next command.
|
// May go to debug mode. If this happens and the ">quit" debug command is
|
||||||
dbg_check_breakpoint(&ea);
|
// used, throw an interrupt exception and skip the next command.
|
||||||
|
dbg_check_breakpoint(&ea);
|
||||||
|
}
|
||||||
if (!ea.skip && got_int) {
|
if (!ea.skip && got_int) {
|
||||||
ea.skip = true;
|
ea.skip = true;
|
||||||
(void)do_intthrow(cstack);
|
(void)do_intthrow(cstack);
|
||||||
|
@@ -60,6 +60,12 @@ for i = 6, #arg do
|
|||||||
if public and not fn.noexport then
|
if public and not fn.noexport then
|
||||||
functions[#functions + 1] = tmp[j]
|
functions[#functions + 1] = tmp[j]
|
||||||
function_names[fn.name] = true
|
function_names[fn.name] = true
|
||||||
|
if #fn.parameters >= 2 and fn.parameters[2][1] == 'Array' and fn.parameters[2][2] == 'uidata' then
|
||||||
|
-- function recieves the "args" as a parameter
|
||||||
|
fn.receives_array_args = true
|
||||||
|
-- remove the args parameter
|
||||||
|
table.remove(fn.parameters, 2)
|
||||||
|
end
|
||||||
if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then
|
if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then
|
||||||
-- this function should receive the channel id
|
-- this function should receive the channel id
|
||||||
fn.receives_channel_id = true
|
fn.receives_channel_id = true
|
||||||
@@ -159,7 +165,7 @@ local exported_attributes = {'name', 'return_type', 'method',
|
|||||||
'since', 'deprecated_since'}
|
'since', 'deprecated_since'}
|
||||||
local exported_functions = {}
|
local exported_functions = {}
|
||||||
for _,f in ipairs(functions) do
|
for _,f in ipairs(functions) do
|
||||||
if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event") then
|
if not (startswith(f.name, "nvim__") or f.name == "nvim_error_event" or f.name == "redraw") then
|
||||||
local f_exported = {}
|
local f_exported = {}
|
||||||
for _,attr in ipairs(exported_attributes) do
|
for _,attr in ipairs(exported_attributes) do
|
||||||
f_exported[attr] = f[attr]
|
f_exported[attr] = f[attr]
|
||||||
@@ -264,11 +270,13 @@ for i = 1, #functions do
|
|||||||
output:write('\n '..rt..' '..converted..';')
|
output:write('\n '..rt..' '..converted..';')
|
||||||
end
|
end
|
||||||
output:write('\n')
|
output:write('\n')
|
||||||
output:write('\n if (args.size != '..#fn.parameters..') {')
|
if not fn.receives_array_args then
|
||||||
output:write('\n api_set_error(error, kErrorTypeException, \
|
output:write('\n if (args.size != '..#fn.parameters..') {')
|
||||||
"Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);')
|
output:write('\n api_set_error(error, kErrorTypeException, \
|
||||||
output:write('\n goto cleanup;')
|
"Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);')
|
||||||
output:write('\n }\n')
|
output:write('\n goto cleanup;')
|
||||||
|
output:write('\n }\n')
|
||||||
|
end
|
||||||
|
|
||||||
-- Validation/conversion for each argument
|
-- Validation/conversion for each argument
|
||||||
for j = 1, #fn.parameters do
|
for j = 1, #fn.parameters do
|
||||||
@@ -350,12 +358,28 @@ for i = 1, #functions do
|
|||||||
if fn.receives_channel_id then
|
if fn.receives_channel_id then
|
||||||
-- if the function receives the channel id, pass it as first argument
|
-- if the function receives the channel id, pass it as first argument
|
||||||
if #args > 0 or fn.can_fail then
|
if #args > 0 or fn.can_fail then
|
||||||
output:write('channel_id, '..call_args)
|
output:write('channel_id, ')
|
||||||
|
if fn.receives_array_args then
|
||||||
|
-- if the function recieves the array args, pass it the second argument
|
||||||
|
output:write('args, ')
|
||||||
|
end
|
||||||
|
output:write(call_args)
|
||||||
else
|
else
|
||||||
output:write('channel_id')
|
output:write('channel_id')
|
||||||
|
if fn.receives_array_args then
|
||||||
|
output:write(', args')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
output:write(call_args)
|
if fn.receives_array_args then
|
||||||
|
if #args > 0 or fn.call_fail then
|
||||||
|
output:write('args, '..call_args)
|
||||||
|
else
|
||||||
|
output:write('args')
|
||||||
|
end
|
||||||
|
else
|
||||||
|
output:write(call_args)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if fn.arena_return then
|
if fn.arena_return then
|
||||||
|
@@ -3,14 +3,13 @@ local mpack = require('mpack')
|
|||||||
local nvimdir = arg[1]
|
local nvimdir = arg[1]
|
||||||
package.path = nvimdir .. '/?.lua;' .. package.path
|
package.path = nvimdir .. '/?.lua;' .. package.path
|
||||||
|
|
||||||
assert(#arg == 8)
|
assert(#arg == 7)
|
||||||
local input = io.open(arg[2], 'rb')
|
local input = io.open(arg[2], 'rb')
|
||||||
local proto_output = io.open(arg[3], 'wb')
|
local proto_output = io.open(arg[3], 'wb')
|
||||||
local call_output = io.open(arg[4], 'wb')
|
local call_output = io.open(arg[4], 'wb')
|
||||||
local remote_output = io.open(arg[5], 'wb')
|
local remote_output = io.open(arg[5], 'wb')
|
||||||
local bridge_output = io.open(arg[6], 'wb')
|
local metadata_output = io.open(arg[6], 'wb')
|
||||||
local metadata_output = io.open(arg[7], 'wb')
|
local client_output = io.open(arg[7], 'wb')
|
||||||
local client_output = io.open(arg[8], 'wb')
|
|
||||||
|
|
||||||
local c_grammar = require('generators.c_grammar')
|
local c_grammar = require('generators.c_grammar')
|
||||||
local events = c_grammar.grammar:match(input:read('*all'))
|
local events = c_grammar.grammar:match(input:read('*all'))
|
||||||
@@ -119,62 +118,6 @@ for i = 1, #events do
|
|||||||
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
|
remote_output:write(' push_call(ui, "'..ev.name..'", args);\n')
|
||||||
remote_output:write('}\n\n')
|
remote_output:write('}\n\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
if not ev.bridge_impl and not ev.noexport then
|
|
||||||
local send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', ''
|
|
||||||
local argc = 1
|
|
||||||
for j = 1, #ev.parameters do
|
|
||||||
local param = ev.parameters[j]
|
|
||||||
local copy = 'copy_'..param[2]
|
|
||||||
if param[1] == 'String' then
|
|
||||||
send = send..' String copy_'..param[2]..' = copy_string('..param[2]..', NULL);\n'
|
|
||||||
argv = argv..', '..copy..'.data, INT2PTR('..copy..'.size)'
|
|
||||||
recv = (recv..' String '..param[2]..
|
|
||||||
' = (String){.data = argv['..argc..'],'..
|
|
||||||
'.size = (size_t)argv['..(argc+1)..']};\n')
|
|
||||||
recv_argv = recv_argv..', '..param[2]
|
|
||||||
recv_cleanup = recv_cleanup..' api_free_string('..param[2]..');\n'
|
|
||||||
argc = argc+2
|
|
||||||
elseif param[1] == 'Array' then
|
|
||||||
send = send..' Array '..copy..' = copy_array('..param[2]..', NULL);\n'
|
|
||||||
argv = argv..', '..copy..'.items, INT2PTR('..copy..'.size)'
|
|
||||||
recv = (recv..' Array '..param[2]..
|
|
||||||
' = (Array){.items = argv['..argc..'],'..
|
|
||||||
'.size = (size_t)argv['..(argc+1)..']};\n')
|
|
||||||
recv_argv = recv_argv..', '..param[2]
|
|
||||||
recv_cleanup = recv_cleanup..' api_free_array('..param[2]..');\n'
|
|
||||||
argc = argc+2
|
|
||||||
elseif param[1] == 'Object' then
|
|
||||||
send = send..' Object *'..copy..' = xmalloc(sizeof(Object));\n'
|
|
||||||
send = send..' *'..copy..' = copy_object('..param[2]..', NULL);\n'
|
|
||||||
argv = argv..', '..copy
|
|
||||||
recv = recv..' Object '..param[2]..' = *(Object *)argv['..argc..'];\n'
|
|
||||||
recv_argv = recv_argv..', '..param[2]
|
|
||||||
recv_cleanup = (recv_cleanup..' api_free_object('..param[2]..');\n'..
|
|
||||||
' xfree(argv['..argc..']);\n')
|
|
||||||
argc = argc+1
|
|
||||||
elseif param[1] == 'Integer' or param[1] == 'Boolean' then
|
|
||||||
argv = argv..', INT2PTR('..param[2]..')'
|
|
||||||
recv_argv = recv_argv..', PTR2INT(argv['..argc..'])'
|
|
||||||
argc = argc+1
|
|
||||||
else
|
|
||||||
assert(false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
bridge_output:write('static void ui_bridge_'..ev.name..
|
|
||||||
'_event(void **argv)\n{\n')
|
|
||||||
bridge_output:write(' UI *ui = UI(argv[0]);\n')
|
|
||||||
bridge_output:write(recv)
|
|
||||||
bridge_output:write(' ui->'..ev.name..'(ui'..recv_argv..');\n')
|
|
||||||
bridge_output:write(recv_cleanup)
|
|
||||||
bridge_output:write('}\n\n')
|
|
||||||
|
|
||||||
bridge_output:write('static void ui_bridge_'..ev.name)
|
|
||||||
write_signature(bridge_output, ev, 'UI *ui')
|
|
||||||
bridge_output:write('\n{\n')
|
|
||||||
bridge_output:write(send)
|
|
||||||
bridge_output:write(' UI_BRIDGE_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n\n')
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if not (ev.remote_only and ev.remote_impl) then
|
if not (ev.remote_only and ev.remote_impl) then
|
||||||
|
@@ -328,9 +328,6 @@ EXTERN sctx_T current_sctx INIT(= { 0, 0, 0 });
|
|||||||
// ID of the current channel making a client API call
|
// ID of the current channel making a client API call
|
||||||
EXTERN uint64_t current_channel_id INIT(= 0);
|
EXTERN uint64_t current_channel_id INIT(= 0);
|
||||||
|
|
||||||
// ID of the client channel. Used by ui client
|
|
||||||
EXTERN uint64_t ui_client_channel_id INIT(= 0);
|
|
||||||
|
|
||||||
EXTERN bool did_source_packages INIT(= false);
|
EXTERN bool did_source_packages INIT(= false);
|
||||||
|
|
||||||
// Scope information for the code that indirectly triggered the current
|
// Scope information for the code that indirectly triggered the current
|
||||||
@@ -494,9 +491,9 @@ EXTERN bool exiting INIT(= false);
|
|||||||
// internal value of v:dying
|
// internal value of v:dying
|
||||||
EXTERN int v_dying INIT(= 0);
|
EXTERN int v_dying INIT(= 0);
|
||||||
// is stdin a terminal?
|
// is stdin a terminal?
|
||||||
EXTERN int stdin_isatty INIT(= true);
|
EXTERN bool stdin_isatty INIT(= true);
|
||||||
// is stdout a terminal?
|
// is stdout a terminal?
|
||||||
EXTERN int stdout_isatty INIT(= true);
|
EXTERN bool stdout_isatty INIT(= true);
|
||||||
/// filedesc set by embedder for reading first buffer like `cmd | nvim -`
|
/// filedesc set by embedder for reading first buffer like `cmd | nvim -`
|
||||||
EXTERN int stdin_fd INIT(= -1);
|
EXTERN int stdin_fd INIT(= -1);
|
||||||
|
|
||||||
@@ -1088,8 +1085,6 @@ typedef enum {
|
|||||||
// Only filled for Win32.
|
// Only filled for Win32.
|
||||||
EXTERN char windowsVersion[20] INIT(= { 0 });
|
EXTERN char windowsVersion[20] INIT(= { 0 });
|
||||||
|
|
||||||
EXTERN int exit_need_delay INIT(= 0);
|
|
||||||
|
|
||||||
/// While executing a regexp and set to OPTION_MAGIC_ON or OPTION_MAGIC_OFF this
|
/// While executing a regexp and set to OPTION_MAGIC_ON or OPTION_MAGIC_OFF this
|
||||||
/// overrules p_magic. Otherwise set to OPTION_MAGIC_NOT_SET.
|
/// overrules p_magic. Otherwise set to OPTION_MAGIC_NOT_SET.
|
||||||
EXTERN optmagic_T magic_overruled INIT(= OPTION_MAGIC_NOT_SET);
|
EXTERN optmagic_T magic_overruled INIT(= OPTION_MAGIC_NOT_SET);
|
||||||
|
@@ -933,22 +933,26 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
|
|||||||
CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
|
CHECK_FLAG(dict, mask, italic, , HL_ITALIC);
|
||||||
CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
|
CHECK_FLAG(dict, mask, reverse, , HL_INVERSE);
|
||||||
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
|
CHECK_FLAG(dict, mask, strikethrough, , HL_STRIKETHROUGH);
|
||||||
|
if (use_rgb) {
|
||||||
|
CHECK_FLAG(dict, mask, fg_indexed, , HL_FG_INDEXED);
|
||||||
|
CHECK_FLAG(dict, mask, bg_indexed, , HL_BG_INDEXED);
|
||||||
|
}
|
||||||
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
|
CHECK_FLAG(dict, mask, nocombine, , HL_NOCOMBINE);
|
||||||
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
|
CHECK_FLAG(dict, mask, default, _, HL_DEFAULT);
|
||||||
|
|
||||||
if (HAS_KEY(dict->fg)) {
|
if (HAS_KEY(dict->fg)) {
|
||||||
fg = object_to_color(dict->fg, "fg", true, err);
|
fg = object_to_color(dict->fg, "fg", use_rgb, err);
|
||||||
} else if (HAS_KEY(dict->foreground)) {
|
} else if (HAS_KEY(dict->foreground)) {
|
||||||
fg = object_to_color(dict->foreground, "foreground", true, err);
|
fg = object_to_color(dict->foreground, "foreground", use_rgb, err);
|
||||||
}
|
}
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
return hlattrs;
|
return hlattrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (HAS_KEY(dict->bg)) {
|
if (HAS_KEY(dict->bg)) {
|
||||||
bg = object_to_color(dict->bg, "bg", true, err);
|
bg = object_to_color(dict->bg, "bg", use_rgb, err);
|
||||||
} else if (HAS_KEY(dict->background)) {
|
} else if (HAS_KEY(dict->background)) {
|
||||||
bg = object_to_color(dict->background, "background", true, err);
|
bg = object_to_color(dict->background, "background", use_rgb, err);
|
||||||
}
|
}
|
||||||
if (ERROR_SET(err)) {
|
if (ERROR_SET(err)) {
|
||||||
return hlattrs;
|
return hlattrs;
|
||||||
@@ -1035,11 +1039,11 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// apply gui mask as default for cterm mask
|
|
||||||
if (!cterm_mask_provided) {
|
|
||||||
cterm_mask = mask;
|
|
||||||
}
|
|
||||||
if (use_rgb) {
|
if (use_rgb) {
|
||||||
|
// apply gui mask as default for cterm mask
|
||||||
|
if (!cterm_mask_provided) {
|
||||||
|
cterm_mask = mask;
|
||||||
|
}
|
||||||
hlattrs.rgb_ae_attr = mask;
|
hlattrs.rgb_ae_attr = mask;
|
||||||
hlattrs.rgb_bg_color = bg;
|
hlattrs.rgb_bg_color = bg;
|
||||||
hlattrs.rgb_fg_color = fg;
|
hlattrs.rgb_fg_color = fg;
|
||||||
@@ -1049,9 +1053,9 @@ HlAttrs dict2hlattrs(Dict(highlight) *dict, bool use_rgb, int *link_id, Error *e
|
|||||||
hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1;
|
hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1;
|
||||||
hlattrs.cterm_ae_attr = cterm_mask;
|
hlattrs.cterm_ae_attr = cterm_mask;
|
||||||
} else {
|
} else {
|
||||||
hlattrs.cterm_bg_color = ctermbg == -1 ? 0 : ctermbg + 1;
|
hlattrs.cterm_bg_color = bg == -1 ? 0 : bg + 1;
|
||||||
hlattrs.cterm_fg_color = ctermfg == -1 ? 0 : ctermfg + 1;
|
hlattrs.cterm_fg_color = fg == -1 ? 0 : fg + 1;
|
||||||
hlattrs.cterm_ae_attr = cterm_mask;
|
hlattrs.cterm_ae_attr = mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hlattrs;
|
return hlattrs;
|
||||||
|
@@ -97,6 +97,7 @@
|
|||||||
#include "nvim/msgpack_rpc/helpers.h"
|
#include "nvim/msgpack_rpc/helpers.h"
|
||||||
#include "nvim/msgpack_rpc/server.h"
|
#include "nvim/msgpack_rpc/server.h"
|
||||||
#include "nvim/os/signal.h"
|
#include "nvim/os/signal.h"
|
||||||
|
#include "nvim/tui/tui.h"
|
||||||
|
|
||||||
// values for "window_layout"
|
// values for "window_layout"
|
||||||
enum {
|
enum {
|
||||||
@@ -278,8 +279,6 @@ int main(int argc, char **argv)
|
|||||||
// argument list "global_alist".
|
// argument list "global_alist".
|
||||||
command_line_scan(¶ms);
|
command_line_scan(¶ms);
|
||||||
|
|
||||||
open_script_files(¶ms);
|
|
||||||
|
|
||||||
nlua_init();
|
nlua_init();
|
||||||
|
|
||||||
TIME_MSG("init lua interpreter");
|
TIME_MSG("init lua interpreter");
|
||||||
@@ -291,9 +290,29 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server_init(params.listen_addr);
|
bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode);
|
||||||
|
|
||||||
|
// don't bind the server yet, if we are using builtin ui.
|
||||||
|
// This will be done when nvim server has been forked from the ui process
|
||||||
|
if (!use_builtin_ui) {
|
||||||
|
server_init(params.listen_addr);
|
||||||
|
}
|
||||||
|
|
||||||
if (params.remote) {
|
if (params.remote) {
|
||||||
remote_request(¶ms, params.remote, params.server_addr, argc, argv);
|
remote_request(¶ms, params.remote, params.server_addr, argc, argv,
|
||||||
|
use_builtin_ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool remote_ui = (ui_client_channel_id != 0);
|
||||||
|
|
||||||
|
if (use_builtin_ui && !remote_ui) {
|
||||||
|
ui_client_forward_stdin = !params.input_isatty;
|
||||||
|
uint64_t rv = ui_client_start_server(params.argc, params.argv);
|
||||||
|
if (!rv) {
|
||||||
|
os_errmsg("Failed to start Nvim server!\n");
|
||||||
|
getout(1);
|
||||||
|
}
|
||||||
|
ui_client_channel_id = rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GARGCOUNT > 0) {
|
if (GARGCOUNT > 0) {
|
||||||
@@ -349,17 +368,16 @@ int main(int argc, char **argv)
|
|||||||
input_start(STDIN_FILENO);
|
input_start(STDIN_FILENO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ui_client_channel_id) {
|
||||||
|
ui_client_run(remote_ui); // NORETURN
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for UIs to set up Nvim or show early messages
|
// Wait for UIs to set up Nvim or show early messages
|
||||||
// and prompts (--cmd, swapfile dialog, …).
|
// and prompts (--cmd, swapfile dialog, …).
|
||||||
bool use_remote_ui = (embedded_mode && !headless_mode);
|
bool use_remote_ui = (embedded_mode && !headless_mode);
|
||||||
bool use_builtin_ui = (!headless_mode && !embedded_mode && !silent_mode);
|
if (use_remote_ui) {
|
||||||
if (use_remote_ui || use_builtin_ui) {
|
|
||||||
TIME_MSG("waiting for UI");
|
TIME_MSG("waiting for UI");
|
||||||
if (use_remote_ui) {
|
remote_ui_wait_for_attach();
|
||||||
remote_ui_wait_for_attach();
|
|
||||||
} else {
|
|
||||||
ui_builtin_start();
|
|
||||||
}
|
|
||||||
TIME_MSG("done waiting for UI");
|
TIME_MSG("done waiting for UI");
|
||||||
firstwin->w_prev_height = firstwin->w_height; // may have changed
|
firstwin->w_prev_height = firstwin->w_height; // may have changed
|
||||||
}
|
}
|
||||||
@@ -370,12 +388,13 @@ int main(int argc, char **argv)
|
|||||||
win_new_screensize();
|
win_new_screensize();
|
||||||
TIME_MSG("clear screen");
|
TIME_MSG("clear screen");
|
||||||
|
|
||||||
if (ui_client_channel_id) {
|
// Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
|
||||||
ui_client_init(ui_client_channel_id);
|
if (edit_stdin(¶ms)) {
|
||||||
ui_client_execute(ui_client_channel_id);
|
params.edit_type = EDIT_STDIN;
|
||||||
abort(); // unreachable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open_script_files(¶ms);
|
||||||
|
|
||||||
// Default mappings (incl. menus)
|
// Default mappings (incl. menus)
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
Object o = NLUA_EXEC_STATIC("return vim._init_default_mappings()",
|
Object o = NLUA_EXEC_STATIC("return vim._init_default_mappings()",
|
||||||
@@ -384,6 +403,7 @@ int main(int argc, char **argv)
|
|||||||
api_clear_error(&err);
|
api_clear_error(&err);
|
||||||
assert(o.type == kObjectTypeNil);
|
assert(o.type == kObjectTypeNil);
|
||||||
api_free_object(o);
|
api_free_object(o);
|
||||||
|
|
||||||
TIME_MSG("init default mappings");
|
TIME_MSG("init default mappings");
|
||||||
|
|
||||||
init_default_autocmds();
|
init_default_autocmds();
|
||||||
@@ -483,7 +503,7 @@ int main(int argc, char **argv)
|
|||||||
// writing end of the pipe doesn't like, e.g., in case stdin and stderr
|
// writing end of the pipe doesn't like, e.g., in case stdin and stderr
|
||||||
// are the same terminal: "cat | vim -".
|
// are the same terminal: "cat | vim -".
|
||||||
// Using autocommands here may cause trouble...
|
// Using autocommands here may cause trouble...
|
||||||
if ((params.edit_type == EDIT_STDIN || stdin_fd >= 0) && !recoverymode) {
|
if (params.edit_type == EDIT_STDIN && !recoverymode) {
|
||||||
read_stdin();
|
read_stdin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -837,15 +857,24 @@ static uint64_t server_connect(char *server_addr, const char **errmsg)
|
|||||||
|
|
||||||
/// Handle remote subcommands
|
/// Handle remote subcommands
|
||||||
static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc,
|
static void remote_request(mparm_T *params, int remote_args, char *server_addr, int argc,
|
||||||
char **argv)
|
char **argv, bool ui_only)
|
||||||
{
|
{
|
||||||
|
bool is_ui = strequal(argv[remote_args], "--remote-ui");
|
||||||
|
if (ui_only && !is_ui) {
|
||||||
|
// TODO(bfredl): this implies always starting the TUI.
|
||||||
|
// if we be smart we could delay this past should_exit
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const char *connect_error = NULL;
|
const char *connect_error = NULL;
|
||||||
uint64_t chan = server_connect(server_addr, &connect_error);
|
uint64_t chan = server_connect(server_addr, &connect_error);
|
||||||
Object rvobj = OBJECT_INIT;
|
Object rvobj = OBJECT_INIT;
|
||||||
|
|
||||||
if (strequal(argv[remote_args], "--remote-ui-test")) {
|
if (is_ui) {
|
||||||
if (!chan) {
|
if (!chan) {
|
||||||
emsg(connect_error);
|
os_errmsg("Remote ui failed to start: ");
|
||||||
|
os_errmsg(connect_error);
|
||||||
|
os_errmsg("\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -925,14 +954,14 @@ static void remote_request(mparm_T *params, int remote_args, char *server_addr,
|
|||||||
|
|
||||||
/// Decides whether text (as opposed to commands) will be read from stdin.
|
/// Decides whether text (as opposed to commands) will be read from stdin.
|
||||||
/// @see EDIT_STDIN
|
/// @see EDIT_STDIN
|
||||||
static bool edit_stdin(bool explicit, mparm_T *parmp)
|
static bool edit_stdin(mparm_T *parmp)
|
||||||
{
|
{
|
||||||
bool implicit = !headless_mode
|
bool implicit = !headless_mode
|
||||||
&& !embedded_mode
|
&& !(embedded_mode && stdin_fd <= 0)
|
||||||
&& (!exmode_active || parmp->input_neverscript)
|
&& (!exmode_active || parmp->input_neverscript)
|
||||||
&& !parmp->input_isatty
|
&& !parmp->input_isatty
|
||||||
&& parmp->scriptin == NULL; // `-s -` was not given.
|
&& parmp->scriptin == NULL; // `-s -` was not given.
|
||||||
return explicit || implicit;
|
return parmp->had_stdin_file || implicit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scan the command line arguments.
|
/// Scan the command line arguments.
|
||||||
@@ -941,7 +970,6 @@ static void command_line_scan(mparm_T *parmp)
|
|||||||
int argc = parmp->argc;
|
int argc = parmp->argc;
|
||||||
char **argv = parmp->argv;
|
char **argv = parmp->argv;
|
||||||
int argv_idx; // index in argv[n][]
|
int argv_idx; // index in argv[n][]
|
||||||
bool had_stdin_file = false; // found explicit "-" argument
|
|
||||||
bool had_minmin = false; // found "--" argument
|
bool had_minmin = false; // found "--" argument
|
||||||
int want_argument; // option argument with argument
|
int want_argument; // option argument with argument
|
||||||
long n;
|
long n;
|
||||||
@@ -978,7 +1006,7 @@ static void command_line_scan(mparm_T *parmp)
|
|||||||
&& parmp->edit_type != EDIT_STDIN) {
|
&& parmp->edit_type != EDIT_STDIN) {
|
||||||
mainerr(err_too_many_args, argv[0]);
|
mainerr(err_too_many_args, argv[0]);
|
||||||
}
|
}
|
||||||
had_stdin_file = true;
|
parmp->had_stdin_file = true;
|
||||||
parmp->edit_type = EDIT_STDIN;
|
parmp->edit_type = EDIT_STDIN;
|
||||||
}
|
}
|
||||||
argv_idx = -1; // skip to next argument
|
argv_idx = -1; // skip to next argument
|
||||||
@@ -1345,7 +1373,7 @@ scripterror:
|
|||||||
path_fix_case(p);
|
path_fix_case(p);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int alist_fnum_flag = edit_stdin(had_stdin_file, parmp)
|
int alist_fnum_flag = edit_stdin(parmp)
|
||||||
? 1 // add buffer nr after exp.
|
? 1 // add buffer nr after exp.
|
||||||
: 2; // add buffer number now and use curbuf
|
: 2; // add buffer number now and use curbuf
|
||||||
alist_add(&global_alist, p, alist_fnum_flag);
|
alist_add(&global_alist, p, alist_fnum_flag);
|
||||||
@@ -1373,11 +1401,6 @@ scripterror:
|
|||||||
xfree(swcmd);
|
xfree(swcmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle "foo | nvim". EDIT_FILE may be overwritten now. #6299
|
|
||||||
if (edit_stdin(had_stdin_file, parmp)) {
|
|
||||||
parmp->edit_type = EDIT_STDIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
TIME_MSG("parsing arguments");
|
TIME_MSG("parsing arguments");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1527,11 +1550,16 @@ static void open_script_files(mparm_T *parmp)
|
|||||||
if (parmp->scriptin) {
|
if (parmp->scriptin) {
|
||||||
int error;
|
int error;
|
||||||
if (strequal(parmp->scriptin, "-")) {
|
if (strequal(parmp->scriptin, "-")) {
|
||||||
const int stdin_dup_fd = os_dup(STDIN_FILENO);
|
int stdin_dup_fd;
|
||||||
|
if (stdin_fd > 0) {
|
||||||
|
stdin_dup_fd = stdin_fd;
|
||||||
|
} else {
|
||||||
|
stdin_dup_fd = os_dup(STDIN_FILENO);
|
||||||
#ifdef MSWIN
|
#ifdef MSWIN
|
||||||
// Replace the original stdin with the console input handle.
|
// Replace the original stdin with the console input handle.
|
||||||
os_replace_stdin_to_conin();
|
os_replace_stdin_to_conin();
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd,
|
FileDescriptor *const stdin_dup = file_open_fd_new(&error, stdin_dup_fd,
|
||||||
kFileReadOnly|kFileNonBlocking);
|
kFileReadOnly|kFileNonBlocking);
|
||||||
assert(stdin_dup != NULL);
|
assert(stdin_dup != NULL);
|
||||||
|
@@ -45,6 +45,7 @@ typedef struct {
|
|||||||
char *scriptin; // -s {filename}
|
char *scriptin; // -s {filename}
|
||||||
char *scriptout; // -w/-W {filename}
|
char *scriptout; // -w/-W {filename}
|
||||||
bool scriptout_append; // append (-w) instead of overwrite (-W)
|
bool scriptout_append; // append (-w) instead of overwrite (-W)
|
||||||
|
bool had_stdin_file; // explicit - as a file to edit
|
||||||
} mparm_T;
|
} mparm_T;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
|
@@ -250,8 +250,8 @@ static void parse_msgpack(Channel *channel)
|
|||||||
ui_client_event_raw_line(p->grid_line_event);
|
ui_client_event_raw_line(p->grid_line_event);
|
||||||
} else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
|
} else if (p->ui_handler.fn != NULL && p->result.type == kObjectTypeArray) {
|
||||||
p->ui_handler.fn(p->result.data.array);
|
p->ui_handler.fn(p->result.data.array);
|
||||||
arena_mem_free(arena_finish(&p->arena));
|
|
||||||
}
|
}
|
||||||
|
arena_mem_free(arena_finish(&p->arena));
|
||||||
} else if (p->type == kMessageTypeResponse) {
|
} else if (p->type == kMessageTypeResponse) {
|
||||||
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
|
ChannelCallFrame *frame = kv_last(channel->rpc.call_stack);
|
||||||
if (p->request_id != frame->request_id) {
|
if (p->request_id != frame->request_id) {
|
||||||
@@ -299,7 +299,7 @@ static void handle_request(Channel *channel, Unpacker *p, Array args)
|
|||||||
assert(p->type == kMessageTypeRequest || p->type == kMessageTypeNotification);
|
assert(p->type == kMessageTypeRequest || p->type == kMessageTypeNotification);
|
||||||
|
|
||||||
if (!p->handler.fn) {
|
if (!p->handler.fn) {
|
||||||
send_error(channel, p->type, p->request_id, p->unpack_error.msg);
|
send_error(channel, p->handler, p->type, p->request_id, p->unpack_error.msg);
|
||||||
api_clear_error(&p->unpack_error);
|
api_clear_error(&p->unpack_error);
|
||||||
arena_mem_free(arena_finish(&p->arena));
|
arena_mem_free(arena_finish(&p->arena));
|
||||||
return;
|
return;
|
||||||
@@ -358,6 +358,7 @@ static void request_event(void **argv)
|
|||||||
msgpack_packer response;
|
msgpack_packer response;
|
||||||
msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write);
|
msgpack_packer_init(&response, &out_buffer, msgpack_sbuffer_write);
|
||||||
channel_write(channel, serialize_response(channel->id,
|
channel_write(channel, serialize_response(channel->id,
|
||||||
|
e->handler,
|
||||||
e->type,
|
e->type,
|
||||||
e->request_id,
|
e->request_id,
|
||||||
&error,
|
&error,
|
||||||
@@ -440,11 +441,13 @@ static void internal_read_event(void **argv)
|
|||||||
wstream_release_wbuffer(buffer);
|
wstream_release_wbuffer(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_error(Channel *chan, MessageType type, uint32_t id, char *err)
|
static void send_error(Channel *chan, MsgpackRpcRequestHandler handler, MessageType type,
|
||||||
|
uint32_t id, char *err)
|
||||||
{
|
{
|
||||||
Error e = ERROR_INIT;
|
Error e = ERROR_INIT;
|
||||||
api_set_error(&e, kErrorTypeException, "%s", err);
|
api_set_error(&e, kErrorTypeException, "%s", err);
|
||||||
channel_write(chan, serialize_response(chan->id,
|
channel_write(chan, serialize_response(chan->id,
|
||||||
|
handler,
|
||||||
type,
|
type,
|
||||||
id,
|
id,
|
||||||
&e,
|
&e,
|
||||||
@@ -543,26 +546,8 @@ void rpc_close(Channel *channel)
|
|||||||
channel_decref(channel);
|
channel_decref(channel);
|
||||||
|
|
||||||
if (channel->streamtype == kChannelStreamStdio
|
if (channel->streamtype == kChannelStreamStdio
|
||||||
|| channel->id == ui_client_channel_id) {
|
|| (channel->id == ui_client_channel_id && channel->streamtype != kChannelStreamProc)) {
|
||||||
multiqueue_put(main_loop.fast_events, exit_event, 0);
|
exit_from_channel(0);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exit_delay_cb(uv_timer_t *handle)
|
|
||||||
{
|
|
||||||
uv_timer_stop(&main_loop.exit_delay_timer);
|
|
||||||
multiqueue_put(main_loop.fast_events, exit_event, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void exit_event(void **argv)
|
|
||||||
{
|
|
||||||
if (exit_need_delay) {
|
|
||||||
uv_timer_start(&main_loop.exit_delay_timer, exit_delay_cb, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!exiting) {
|
|
||||||
os_exit(0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,18 +597,27 @@ static WBuffer *serialize_request(uint64_t channel_id, uint32_t request_id, cons
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static WBuffer *serialize_response(uint64_t channel_id, MessageType type, uint32_t response_id,
|
static WBuffer *serialize_response(uint64_t channel_id, MsgpackRpcRequestHandler handler,
|
||||||
Error *err, Object arg, msgpack_sbuffer *sbuffer)
|
MessageType type, uint32_t response_id, Error *err, Object arg,
|
||||||
|
msgpack_sbuffer *sbuffer)
|
||||||
{
|
{
|
||||||
msgpack_packer pac;
|
msgpack_packer pac;
|
||||||
msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
|
msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write);
|
||||||
if (ERROR_SET(err) && type == kMessageTypeNotification) {
|
if (ERROR_SET(err) && type == kMessageTypeNotification) {
|
||||||
Array args = ARRAY_DICT_INIT;
|
if (handler.fn == handle_nvim_paste) {
|
||||||
ADD(args, INTEGER_OBJ(err->type));
|
// TODO(bfredl): this is pretty much ad-hoc. maybe TUI and UI:s should be
|
||||||
ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
|
// allowed to ask nvim to just scream directly in the users face
|
||||||
msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"),
|
// instead of sending nvim_error_event, in general.
|
||||||
args, &pac);
|
semsg("paste: %s", err->msg);
|
||||||
api_free_array(args);
|
api_clear_error(err);
|
||||||
|
} else {
|
||||||
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ADD(args, INTEGER_OBJ(err->type));
|
||||||
|
ADD(args, STRING_OBJ(cstr_to_string(err->msg)));
|
||||||
|
msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"),
|
||||||
|
args, &pac);
|
||||||
|
api_free_array(args);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
msgpack_rpc_serialize_response(response_id, err, arg, &pac);
|
msgpack_rpc_serialize_response(response_id, err, arg, &pac);
|
||||||
}
|
}
|
||||||
|
@@ -298,7 +298,7 @@ error:
|
|||||||
//
|
//
|
||||||
// When method is "grid_line", we furthermore decode a cell at a time like:
|
// When method is "grid_line", we furthermore decode a cell at a time like:
|
||||||
//
|
//
|
||||||
// <0>[2, "redraw", <10>[{11}["grid_line", <13>[g, r, c, [<14>[cell], <14>[cell], ...]], ...], <11>[...], ...]]
|
// <0>[2, "redraw", <10>[{11}["grid_line", <14>[g, r, c, [<15>[cell], <15>[cell], ...]], ...], <11>[...], ...]]
|
||||||
//
|
//
|
||||||
// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional
|
// where [cell] is [char, repeat, attr], where 'repeat' and 'attr' is optional
|
||||||
|
|
||||||
@@ -318,17 +318,19 @@ bool unpacker_advance(Unpacker *p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->state >= 10 && p->state != 12) {
|
if (p->state >= 10 && p->state != 13) {
|
||||||
if (!unpacker_parse_redraw(p)) {
|
if (!unpacker_parse_redraw(p)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p->state == 14) {
|
if (p->state == 15) {
|
||||||
// grid_line event already unpacked
|
// grid_line event already unpacked
|
||||||
goto done;
|
goto done;
|
||||||
} else {
|
} else {
|
||||||
|
assert(p->state == 12);
|
||||||
// unpack other ui events using mpack_parse()
|
// unpack other ui events using mpack_parse()
|
||||||
p->arena = (Arena)ARENA_EMPTY;
|
p->arena = (Arena)ARENA_EMPTY;
|
||||||
|
p->state = 13;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -355,11 +357,11 @@ done:
|
|||||||
case 2:
|
case 2:
|
||||||
p->state = 0;
|
p->state = 0;
|
||||||
return true;
|
return true;
|
||||||
case 12:
|
case 13:
|
||||||
case 14:
|
case 15:
|
||||||
p->ncalls--;
|
p->ncalls--;
|
||||||
if (p->ncalls > 0) {
|
if (p->ncalls > 0) {
|
||||||
p->state = (p->state == 14) ? 13 : 12;
|
p->state = (p->state == 15) ? 14 : 12;
|
||||||
} else if (p->nevents > 0) {
|
} else if (p->nevents > 0) {
|
||||||
p->state = 11;
|
p->state = 11;
|
||||||
} else {
|
} else {
|
||||||
@@ -428,14 +430,14 @@ redo:
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
p->state = 13;
|
p->state = 14;
|
||||||
p->arena = (Arena)ARENA_EMPTY;
|
p->arena = (Arena)ARENA_EMPTY;
|
||||||
p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true);
|
p->grid_line_event = arena_alloc(&p->arena, sizeof *p->grid_line_event, true);
|
||||||
g = p->grid_line_event;
|
g = p->grid_line_event;
|
||||||
}
|
}
|
||||||
FALLTHROUGH;
|
FALLTHROUGH;
|
||||||
|
|
||||||
case 13:
|
case 14:
|
||||||
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
|
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
|
||||||
int eventarrsize = (int)tok.length;
|
int eventarrsize = (int)tok.length;
|
||||||
if (eventarrsize != 4) {
|
if (eventarrsize != 4) {
|
||||||
@@ -456,10 +458,10 @@ redo:
|
|||||||
|
|
||||||
p->read_ptr = data;
|
p->read_ptr = data;
|
||||||
p->read_size = size;
|
p->read_size = size;
|
||||||
p->state = 14;
|
p->state = 15;
|
||||||
FALLTHROUGH;
|
FALLTHROUGH;
|
||||||
|
|
||||||
case 14:
|
case 15:
|
||||||
assert(g->icell < g->ncells);
|
assert(g->icell < g->ncells);
|
||||||
|
|
||||||
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
|
NEXT_TYPE(tok, MPACK_TOKEN_ARRAY);
|
||||||
@@ -513,6 +515,9 @@ redo:
|
|||||||
}
|
}
|
||||||
goto redo;
|
goto redo;
|
||||||
|
|
||||||
|
case 12:
|
||||||
|
return true;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "nvim/event/rstream.h"
|
#include "nvim/event/rstream.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
|
#include "nvim/ui.h"
|
||||||
|
|
||||||
#define KEY_BUFFER_SIZE 0xfff
|
#define KEY_BUFFER_SIZE 0xfff
|
||||||
|
|
||||||
@@ -219,16 +220,11 @@ static void tinput_wait_enqueue(void **argv)
|
|||||||
const size_t len = rbuffer_size(input->key_buffer);
|
const size_t len = rbuffer_size(input->key_buffer);
|
||||||
String keys = { .data = xmallocz(len), .size = len };
|
String keys = { .data = xmallocz(len), .size = len };
|
||||||
rbuffer_read(input->key_buffer, keys.data, len);
|
rbuffer_read(input->key_buffer, keys.data, len);
|
||||||
if (ui_client_channel_id) {
|
Array args = ARRAY_DICT_INIT;
|
||||||
Array args = ARRAY_DICT_INIT;
|
ADD(args, STRING_OBJ(keys)); // 'data'
|
||||||
ADD(args, STRING_OBJ(keys)); // 'data'
|
ADD(args, BOOLEAN_OBJ(true)); // 'crlf'
|
||||||
ADD(args, BOOLEAN_OBJ(true)); // 'crlf'
|
ADD(args, INTEGER_OBJ(input->paste)); // 'phase'
|
||||||
ADD(args, INTEGER_OBJ(input->paste)); // 'phase'
|
rpc_send_event(ui_client_channel_id, "nvim_paste", args);
|
||||||
rpc_send_event(ui_client_channel_id, "nvim_paste", args);
|
|
||||||
} else {
|
|
||||||
multiqueue_put(main_loop.events, tinput_paste_event, 3,
|
|
||||||
keys.data, keys.size, (intptr_t)input->paste);
|
|
||||||
}
|
|
||||||
if (input->paste == 1) {
|
if (input->paste == 1) {
|
||||||
// Paste phase: "continue"
|
// Paste phase: "continue"
|
||||||
input->paste = 2;
|
input->paste = 2;
|
||||||
@@ -237,60 +233,22 @@ static void tinput_wait_enqueue(void **argv)
|
|||||||
} else { // enqueue input for the main thread or Nvim server
|
} else { // enqueue input for the main thread or Nvim server
|
||||||
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
|
RBUFFER_UNTIL_EMPTY(input->key_buffer, buf, len) {
|
||||||
const String keys = { .data = buf, .size = len };
|
const String keys = { .data = buf, .size = len };
|
||||||
size_t consumed;
|
Array args = ARRAY_DICT_INIT;
|
||||||
if (ui_client_channel_id) {
|
ADD(args, STRING_OBJ(copy_string(keys, NULL)));
|
||||||
Array args = ARRAY_DICT_INIT;
|
// NOTE: This is non-blocking and won't check partially processed input,
|
||||||
Error err = ERROR_INIT;
|
// but should be fine as all big sends are handled with nvim_paste, not nvim_input
|
||||||
ADD(args, STRING_OBJ(copy_string(keys, NULL)));
|
rpc_send_event(ui_client_channel_id, "nvim_input", args);
|
||||||
// TODO(bfredl): could be non-blocking now with paste?
|
rbuffer_consumed(input->key_buffer, len);
|
||||||
ArenaMem res_mem = NULL;
|
|
||||||
Object result = rpc_send_call(ui_client_channel_id, "nvim_input", args, &res_mem, &err);
|
|
||||||
consumed = result.type == kObjectTypeInteger ? (size_t)result.data.integer : 0;
|
|
||||||
arena_mem_free(res_mem);
|
|
||||||
} else {
|
|
||||||
consumed = input_enqueue(keys);
|
|
||||||
}
|
|
||||||
if (consumed) {
|
|
||||||
rbuffer_consumed(input->key_buffer, consumed);
|
|
||||||
}
|
|
||||||
rbuffer_reset(input->key_buffer);
|
rbuffer_reset(input->key_buffer);
|
||||||
if (consumed < len) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uv_mutex_lock(&input->key_buffer_mutex);
|
|
||||||
input->waiting = false;
|
|
||||||
uv_cond_signal(&input->key_buffer_cond);
|
|
||||||
uv_mutex_unlock(&input->key_buffer_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void tinput_paste_event(void **argv)
|
|
||||||
{
|
|
||||||
String keys = { .data = argv[0], .size = (size_t)argv[1] };
|
|
||||||
intptr_t phase = (intptr_t)argv[2];
|
|
||||||
|
|
||||||
Error err = ERROR_INIT;
|
|
||||||
nvim_paste(keys, true, phase, &err);
|
|
||||||
if (ERROR_SET(&err)) {
|
|
||||||
semsg("paste: %s", err.msg);
|
|
||||||
api_clear_error(&err);
|
|
||||||
}
|
|
||||||
|
|
||||||
api_free_string(keys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tinput_flush(TermInput *input, bool wait_until_empty)
|
static void tinput_flush(TermInput *input, bool wait_until_empty)
|
||||||
{
|
{
|
||||||
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
size_t drain_boundary = wait_until_empty ? 0 : 0xff;
|
||||||
do {
|
do {
|
||||||
uv_mutex_lock(&input->key_buffer_mutex);
|
tinput_wait_enqueue((void **)&input);
|
||||||
loop_schedule_fast(&main_loop, event_create(tinput_wait_enqueue, 1, input));
|
|
||||||
input->waiting = true;
|
|
||||||
while (input->waiting) {
|
|
||||||
uv_cond_wait(&input->key_buffer_cond, &input->key_buffer_mutex);
|
|
||||||
}
|
|
||||||
uv_mutex_unlock(&input->key_buffer_mutex);
|
|
||||||
} while (rbuffer_size(input->key_buffer) > drain_boundary);
|
} while (rbuffer_size(input->key_buffer) > drain_boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,7 +528,10 @@ static bool handle_focus_event(TermInput *input)
|
|||||||
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
|
bool focus_gained = *rbuffer_get(input->read_stream.buffer, 2) == 'I';
|
||||||
// Advance past the sequence
|
// Advance past the sequence
|
||||||
rbuffer_consumed(input->read_stream.buffer, 3);
|
rbuffer_consumed(input->read_stream.buffer, 3);
|
||||||
autocmd_schedule_focusgained(focus_gained);
|
|
||||||
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ADD(args, BOOLEAN_OBJ(focus_gained));
|
||||||
|
rpc_send_event(ui_client_channel_id, "nvim_ui_set_focus", args);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -617,10 +578,15 @@ static HandleState handle_bracketed_paste(TermInput *input)
|
|||||||
return kNotApplicable;
|
return kNotApplicable;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void set_bg_deferred(void **argv)
|
static void set_bg(char *bgvalue)
|
||||||
{
|
{
|
||||||
char *bgvalue = argv[0];
|
if (ui_client_attached) {
|
||||||
set_tty_background(bgvalue);
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ADD(args, STRING_OBJ(cstr_to_string("term_background")));
|
||||||
|
ADD(args, STRING_OBJ(cstr_as_string(xstrdup(bgvalue))));
|
||||||
|
|
||||||
|
rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// During startup, tui.c requests the background color (see `ext.get_bg`).
|
// During startup, tui.c requests the background color (see `ext.get_bg`).
|
||||||
@@ -702,10 +668,11 @@ static HandleState handle_background_color(TermInput *input)
|
|||||||
double g = (double)rgb[1] / (double)rgb_max[1];
|
double g = (double)rgb[1] / (double)rgb_max[1];
|
||||||
double b = (double)rgb[2] / (double)rgb_max[2];
|
double b = (double)rgb[2] / (double)rgb_max[2];
|
||||||
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
|
double luminance = (0.299 * r) + (0.587 * g) + (0.114 * b); // CCIR 601
|
||||||
char *bgvalue = luminance < 0.5 ? "dark" : "light";
|
bool is_dark = luminance < 0.5;
|
||||||
|
char *bgvalue = is_dark ? "dark" : "light";
|
||||||
DLOG("bg response: %s", bgvalue);
|
DLOG("bg response: %s", bgvalue);
|
||||||
loop_schedule_deferred(&main_loop,
|
ui_client_bg_respose = is_dark ? kTrue : kFalse;
|
||||||
event_create(set_bg_deferred, 1, bgvalue));
|
set_bg(bgvalue);
|
||||||
input->waiting_for_bg_response = 0;
|
input->waiting_for_bg_response = 0;
|
||||||
} else if (!done && !bad) {
|
} else if (!done && !bad) {
|
||||||
// An incomplete sequence was found, waiting for the next input.
|
// An incomplete sequence was found, waiting for the next input.
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
// This is an open source non-commercial project. Dear PVS-Studio, please check
|
// 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
|
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
|
||||||
|
|
||||||
// Terminal UI functions. Invoked (by ui_bridge.c) on the TUI thread.
|
// Terminal UI functions. Invoked (by UI_CALL) on the UI process.
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/api/vim.h"
|
#include "nvim/api/vim.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
|
#include "nvim/cursor_shape.h"
|
||||||
#include "nvim/event/defs.h"
|
#include "nvim/event/defs.h"
|
||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/event/multiqueue.h"
|
#include "nvim/event/multiqueue.h"
|
||||||
@@ -37,18 +38,15 @@
|
|||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/os/signal.h"
|
#include "nvim/os/signal.h"
|
||||||
#include "nvim/ui.h"
|
|
||||||
#include "nvim/vim.h"
|
|
||||||
#ifdef MSWIN
|
#ifdef MSWIN
|
||||||
# include "nvim/os/os_win_console.h"
|
# include "nvim/os/os_win_console.h"
|
||||||
#endif
|
#endif
|
||||||
#include "nvim/cursor_shape.h"
|
|
||||||
#include "nvim/macros.h"
|
|
||||||
#include "nvim/tui/input.h"
|
#include "nvim/tui/input.h"
|
||||||
#include "nvim/tui/terminfo.h"
|
#include "nvim/tui/terminfo.h"
|
||||||
#include "nvim/tui/tui.h"
|
#include "nvim/tui/tui.h"
|
||||||
#include "nvim/ugrid.h"
|
#include "nvim/ugrid.h"
|
||||||
#include "nvim/ui_bridge.h"
|
#include "nvim/ui.h"
|
||||||
|
#include "nvim/vim.h"
|
||||||
|
|
||||||
// Space reserved in two output buffers to make the cursor normal or invisible
|
// Space reserved in two output buffers to make the cursor normal or invisible
|
||||||
// when flushing. No existing terminal will require 32 bytes to do that.
|
// when flushing. No existing terminal will require 32 bytes to do that.
|
||||||
@@ -91,7 +89,7 @@ typedef struct {
|
|||||||
} Rect;
|
} Rect;
|
||||||
|
|
||||||
struct TUIData {
|
struct TUIData {
|
||||||
UIBridgeData *bridge;
|
UI *ui;
|
||||||
Loop *loop;
|
Loop *loop;
|
||||||
unibi_var_t params[9];
|
unibi_var_t params[9];
|
||||||
char buf[OUTBUF_SIZE];
|
char buf[OUTBUF_SIZE];
|
||||||
@@ -108,8 +106,8 @@ struct TUIData {
|
|||||||
uv_pipe_t pipe;
|
uv_pipe_t pipe;
|
||||||
} output_handle;
|
} output_handle;
|
||||||
bool out_isatty;
|
bool out_isatty;
|
||||||
SignalWatcher winch_handle, cont_handle;
|
SignalWatcher winch_handle;
|
||||||
bool cont_received;
|
uv_timer_t startup_delay_timer;
|
||||||
UGrid grid;
|
UGrid grid;
|
||||||
kvec_t(Rect) invalid_regions;
|
kvec_t(Rect) invalid_regions;
|
||||||
int row, col;
|
int row, col;
|
||||||
@@ -159,18 +157,18 @@ struct TUIData {
|
|||||||
int get_extkeys;
|
int get_extkeys;
|
||||||
} unibi_ext;
|
} unibi_ext;
|
||||||
char *space_buf;
|
char *space_buf;
|
||||||
|
bool stopped;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int got_winch = 0;
|
static int got_winch = 0;
|
||||||
static bool cursor_style_enabled = false;
|
static bool cursor_style_enabled = false;
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "tui/tui.c.generated.h"
|
# include "tui/tui.c.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
UI *tui_start(void)
|
UI *tui_start(void)
|
||||||
{
|
{
|
||||||
UI *ui = xcalloc(1, sizeof(UI)); // Freed by ui_bridge_stop().
|
UI *ui = xcalloc(1, sizeof(UI)); // Freed by tui_data_destroy().
|
||||||
ui->stop = tui_stop;
|
ui->stop = tui_stop;
|
||||||
ui->grid_resize = tui_grid_resize;
|
ui->grid_resize = tui_grid_resize;
|
||||||
ui->grid_clear = tui_grid_clear;
|
ui->grid_clear = tui_grid_clear;
|
||||||
@@ -199,14 +197,39 @@ UI *tui_start(void)
|
|||||||
ui->ui_ext[kUILinegrid] = true;
|
ui->ui_ext[kUILinegrid] = true;
|
||||||
ui->ui_ext[kUITermColors] = true;
|
ui->ui_ext[kUITermColors] = true;
|
||||||
|
|
||||||
return ui_bridge_attach(ui, tui_main, tui_scheduler);
|
TUIData *data = xcalloc(1, sizeof(TUIData));
|
||||||
|
ui->data = data;
|
||||||
|
data->ui = ui;
|
||||||
|
data->is_starting = true;
|
||||||
|
data->screenshot = NULL;
|
||||||
|
data->stopped = false;
|
||||||
|
data->loop = &main_loop;
|
||||||
|
kv_init(data->invalid_regions);
|
||||||
|
signal_watcher_init(data->loop, &data->winch_handle, ui);
|
||||||
|
|
||||||
|
// TODO(bfredl): zero hl is empty, send this explicitly?
|
||||||
|
kv_push(data->attrs, HLATTRS_INIT);
|
||||||
|
|
||||||
|
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
|
||||||
|
tinput_init(&data->input, &main_loop);
|
||||||
|
ugrid_init(&data->grid);
|
||||||
|
tui_terminal_start(ui);
|
||||||
|
|
||||||
|
uv_timer_init(&data->loop->uv, &data->startup_delay_timer);
|
||||||
|
data->startup_delay_timer.data = ui;
|
||||||
|
uv_timer_start(&data->startup_delay_timer, after_startup_cb,
|
||||||
|
100, 0);
|
||||||
|
|
||||||
|
ui_attach_impl(ui, 0);
|
||||||
|
|
||||||
|
return ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tui_enable_extkeys(TUIData *data)
|
void tui_enable_extkeys(TUIData *data)
|
||||||
{
|
{
|
||||||
TermInput input = data->input;
|
TermInput input = data->input;
|
||||||
unibi_term *ut = data->ut;
|
unibi_term *ut = data->ut;
|
||||||
UI *ui = data->bridge->ui;
|
UI *ui = data->ui;
|
||||||
|
|
||||||
switch (input.extkeys_type) {
|
switch (input.extkeys_type) {
|
||||||
case kExtkeysCSIu:
|
case kExtkeysCSIu:
|
||||||
@@ -237,13 +260,6 @@ static size_t unibi_pre_fmt_str(TUIData *data, unsigned int unibi_index, char *b
|
|||||||
return unibi_run(str, data->params, buf, len);
|
return unibi_run(str, data->params, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void termname_set_event(void **argv)
|
|
||||||
{
|
|
||||||
char *termname = argv[0];
|
|
||||||
set_tty_option("term", termname);
|
|
||||||
// Do not free termname, it is freed by set_tty_option.
|
|
||||||
}
|
|
||||||
|
|
||||||
static void terminfo_start(UI *ui)
|
static void terminfo_start(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
@@ -294,22 +310,19 @@ static void terminfo_start(UI *ui)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Set up unibilium/terminfo.
|
// Set up unibilium/terminfo.
|
||||||
char *termname = NULL;
|
ui_client_termname = NULL;
|
||||||
if (term) {
|
if (term) {
|
||||||
os_env_var_lock();
|
|
||||||
data->ut = unibi_from_term(term);
|
data->ut = unibi_from_term(term);
|
||||||
os_env_var_unlock();
|
|
||||||
if (data->ut) {
|
if (data->ut) {
|
||||||
termname = xstrdup(term);
|
ui_client_termname = xstrdup(term);
|
||||||
data->term = xstrdup(term);
|
if (!data->term) {
|
||||||
|
data->term = xstrdup(term);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!data->ut) {
|
if (!data->ut) {
|
||||||
data->ut = terminfo_from_builtin(term, &termname);
|
data->ut = terminfo_from_builtin(term, &ui_client_termname);
|
||||||
}
|
}
|
||||||
// Update 'term' option.
|
|
||||||
loop_schedule_deferred(&main_loop,
|
|
||||||
event_create(termname_set_event, 1, termname));
|
|
||||||
|
|
||||||
// None of the following work over SSH; see :help TERM .
|
// None of the following work over SSH; see :help TERM .
|
||||||
const char *colorterm = os_getenv("COLORTERM");
|
const char *colorterm = os_getenv("COLORTERM");
|
||||||
@@ -443,13 +456,18 @@ static void tui_terminal_start(UI *ui)
|
|||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
data->print_attr_id = -1;
|
data->print_attr_id = -1;
|
||||||
ugrid_init(&data->grid);
|
|
||||||
terminfo_start(ui);
|
terminfo_start(ui);
|
||||||
tui_guess_size(ui);
|
tui_guess_size(ui);
|
||||||
signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
|
signal_watcher_start(&data->winch_handle, sigwinch_cb, SIGWINCH);
|
||||||
tinput_start(&data->input);
|
tinput_start(&data->input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void after_startup_cb(uv_timer_t *handle)
|
||||||
|
{
|
||||||
|
UI *ui = handle->data;
|
||||||
|
tui_terminal_after_startup(ui);
|
||||||
|
}
|
||||||
|
|
||||||
static void tui_terminal_after_startup(UI *ui)
|
static void tui_terminal_after_startup(UI *ui)
|
||||||
FUNC_ATTR_NONNULL_ALL
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
@@ -461,98 +479,50 @@ static void tui_terminal_after_startup(UI *ui)
|
|||||||
flush_buf(ui);
|
flush_buf(ui);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// stop the terminal but allow it to restart later (like after suspend)
|
||||||
static void tui_terminal_stop(UI *ui)
|
static void tui_terminal_stop(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) {
|
if (uv_is_closing(STRUCT_CAST(uv_handle_t, &data->output_handle))) {
|
||||||
// Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075
|
// Race between SIGCONT (tui.c) and SIGHUP (os/signal.c)? #8075
|
||||||
ELOG("TUI already stopped (race?)");
|
ELOG("TUI already stopped (race?)");
|
||||||
ui->data = NULL; // Flag UI as "stopped".
|
data->stopped = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
tinput_stop(&data->input);
|
tinput_stop(&data->input);
|
||||||
signal_watcher_stop(&data->winch_handle);
|
signal_watcher_stop(&data->winch_handle);
|
||||||
terminfo_stop(ui);
|
terminfo_stop(ui);
|
||||||
ugrid_free(&data->grid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tui_stop(UI *ui)
|
static void tui_stop(UI *ui)
|
||||||
{
|
{
|
||||||
|
TUIData *data = ui->data;
|
||||||
tui_terminal_stop(ui);
|
tui_terminal_stop(ui);
|
||||||
ui->data = NULL; // Flag UI as "stopped".
|
tinput_destroy(&data->input);
|
||||||
|
data->stopped = true;
|
||||||
|
signal_watcher_close(&data->winch_handle, NULL);
|
||||||
|
uv_close((uv_handle_t *)&data->startup_delay_timer, NULL);
|
||||||
|
ui_detach_impl(ui, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if UI `ui` is stopped.
|
/// Returns true if UI `ui` is stopped.
|
||||||
static bool tui_is_stopped(UI *ui)
|
static bool tui_is_stopped(UI *ui)
|
||||||
{
|
{
|
||||||
return ui->data == NULL;
|
TUIData *data = ui->data;
|
||||||
|
return data->stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Main function of the TUI thread.
|
#ifdef EXITFREE
|
||||||
static void tui_main(UIBridgeData *bridge, UI *ui)
|
void tui_free_all_mem(UI *ui)
|
||||||
{
|
{
|
||||||
Loop tui_loop;
|
TUIData *data = ui->data;
|
||||||
loop_init(&tui_loop, NULL);
|
ugrid_free(&data->grid);
|
||||||
TUIData *data = xcalloc(1, sizeof(TUIData));
|
|
||||||
ui->data = data;
|
|
||||||
data->bridge = bridge;
|
|
||||||
data->loop = &tui_loop;
|
|
||||||
data->is_starting = true;
|
|
||||||
data->screenshot = NULL;
|
|
||||||
kv_init(data->invalid_regions);
|
|
||||||
signal_watcher_init(data->loop, &data->winch_handle, ui);
|
|
||||||
signal_watcher_init(data->loop, &data->cont_handle, data);
|
|
||||||
#ifdef UNIX
|
|
||||||
signal_watcher_start(&data->cont_handle, sigcont_cb, SIGCONT);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO(bfredl): zero hl is empty, send this explicitly?
|
|
||||||
kv_push(data->attrs, HLATTRS_INIT);
|
|
||||||
|
|
||||||
data->input.tk_ti_hook_fn = tui_tk_ti_getstr;
|
|
||||||
tinput_init(&data->input, &tui_loop);
|
|
||||||
tui_terminal_start(ui);
|
|
||||||
|
|
||||||
// Allow main thread to continue, we are ready to handle UI callbacks.
|
|
||||||
CONTINUE(bridge);
|
|
||||||
|
|
||||||
// "Active" loop: first ~100 ms of startup.
|
|
||||||
for (size_t ms = 0; ms < 100 && !tui_is_stopped(ui);) {
|
|
||||||
ms += (loop_poll_events(&tui_loop, 20) ? 20 : 1);
|
|
||||||
}
|
|
||||||
if (!tui_is_stopped(ui)) {
|
|
||||||
tui_terminal_after_startup(ui);
|
|
||||||
}
|
|
||||||
// "Passive" (I/O-driven) loop: TUI thread "main loop".
|
|
||||||
while (!tui_is_stopped(ui)) {
|
|
||||||
loop_poll_events(&tui_loop, -1); // tui_loop.events is never processed
|
|
||||||
}
|
|
||||||
|
|
||||||
ui_bridge_stopped(bridge);
|
|
||||||
tinput_destroy(&data->input);
|
|
||||||
signal_watcher_stop(&data->cont_handle);
|
|
||||||
signal_watcher_close(&data->cont_handle, NULL);
|
|
||||||
signal_watcher_close(&data->winch_handle, NULL);
|
|
||||||
loop_close(&tui_loop, false);
|
|
||||||
kv_destroy(data->invalid_regions);
|
kv_destroy(data->invalid_regions);
|
||||||
kv_destroy(data->attrs);
|
kv_destroy(data->attrs);
|
||||||
xfree(data->space_buf);
|
xfree(data->space_buf);
|
||||||
xfree(data->term);
|
xfree(data->term);
|
||||||
xfree(data);
|
xfree(data);
|
||||||
}
|
xfree(ui);
|
||||||
|
|
||||||
/// Handoff point between the main (ui_bridge) thread and the TUI thread.
|
|
||||||
static void tui_scheduler(Event event, void *d)
|
|
||||||
{
|
|
||||||
UI *ui = d;
|
|
||||||
TUIData *data = ui->data;
|
|
||||||
loop_schedule_fast(data->loop, event); // `tui_loop` local to tui_main().
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef UNIX
|
|
||||||
static void sigcont_cb(SignalWatcher *watcher, int signum, void *data)
|
|
||||||
{
|
|
||||||
((TUIData *)data)->cont_received = true;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -565,7 +535,6 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tui_guess_size(ui);
|
tui_guess_size(ui);
|
||||||
ui_schedule_refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
|
static bool attrs_differ(UI *ui, int id1, int id2, bool rgb)
|
||||||
@@ -1324,6 +1293,9 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, // -V751
|
|||||||
static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info)
|
static void tui_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, Array info)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
|
attrs.cterm_ae_attr = cterm_attrs.cterm_ae_attr;
|
||||||
|
attrs.cterm_fg_color = cterm_attrs.cterm_fg_color;
|
||||||
|
attrs.cterm_bg_color = cterm_attrs.cterm_bg_color;
|
||||||
kv_a(data->attrs, (size_t)id) = attrs;
|
kv_a(data->attrs, (size_t)id) = attrs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1421,32 +1393,13 @@ static void show_verbose_terminfo(TUIData *data)
|
|||||||
ADD(end_fold, STRING_OBJ(cstr_to_string("Title")));
|
ADD(end_fold, STRING_OBJ(cstr_to_string("Title")));
|
||||||
ADD(chunks, ARRAY_OBJ(end_fold));
|
ADD(chunks, ARRAY_OBJ(end_fold));
|
||||||
|
|
||||||
if (ui_client_channel_id) {
|
Array args = ARRAY_DICT_INIT;
|
||||||
Array args = ARRAY_DICT_INIT;
|
ADD(args, ARRAY_OBJ(chunks));
|
||||||
ADD(args, ARRAY_OBJ(chunks));
|
ADD(args, BOOLEAN_OBJ(true)); // history
|
||||||
ADD(args, BOOLEAN_OBJ(true)); // history
|
Dictionary opts = ARRAY_DICT_INIT;
|
||||||
Dictionary opts = ARRAY_DICT_INIT;
|
PUT(opts, "verbose", BOOLEAN_OBJ(true));
|
||||||
PUT(opts, "verbose", BOOLEAN_OBJ(true));
|
ADD(args, DICTIONARY_OBJ(opts));
|
||||||
ADD(args, DICTIONARY_OBJ(opts));
|
rpc_send_event(ui_client_channel_id, "nvim_echo", args);
|
||||||
rpc_send_event(ui_client_channel_id, "nvim_echo", args);
|
|
||||||
} else {
|
|
||||||
loop_schedule_deferred(&main_loop, event_create(verbose_terminfo_event, 2,
|
|
||||||
chunks.items, chunks.size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void verbose_terminfo_event(void **argv)
|
|
||||||
{
|
|
||||||
Array chunks = { .items = argv[0], .size = (size_t)argv[1] };
|
|
||||||
Dict(echo_opts) opts = { .verbose = BOOLEAN_OBJ(true) };
|
|
||||||
Error err = ERROR_INIT;
|
|
||||||
nvim_echo(chunks, true, &opts, &err);
|
|
||||||
api_free_array(chunks);
|
|
||||||
if (ERROR_SET(&err)) {
|
|
||||||
fprintf(stderr, "TUI bought the farm: %s\n", err.msg);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
api_clear_error(&err);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
@@ -1456,38 +1409,28 @@ static void suspend_event(void **argv)
|
|||||||
TUIData *data = ui->data;
|
TUIData *data = ui->data;
|
||||||
bool enable_mouse = data->mouse_enabled;
|
bool enable_mouse = data->mouse_enabled;
|
||||||
tui_terminal_stop(ui);
|
tui_terminal_stop(ui);
|
||||||
data->cont_received = false;
|
|
||||||
stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
|
stream_set_blocking(input_global_fd(), true); // normalize stream (#2598)
|
||||||
signal_stop();
|
|
||||||
kill(0, SIGTSTP);
|
kill(0, SIGTSTP);
|
||||||
signal_start();
|
|
||||||
while (!data->cont_received) {
|
|
||||||
// poll the event loop until SIGCONT is received
|
|
||||||
loop_poll_events(data->loop, -1);
|
|
||||||
}
|
|
||||||
tui_terminal_start(ui);
|
tui_terminal_start(ui);
|
||||||
tui_terminal_after_startup(ui);
|
tui_terminal_after_startup(ui);
|
||||||
if (enable_mouse) {
|
if (enable_mouse) {
|
||||||
tui_mouse_on(ui);
|
tui_mouse_on(ui);
|
||||||
}
|
}
|
||||||
stream_set_blocking(input_global_fd(), false); // libuv expects this
|
stream_set_blocking(input_global_fd(), false); // libuv expects this
|
||||||
// resume the main thread
|
|
||||||
CONTINUE(data->bridge);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void tui_suspend(UI *ui)
|
static void tui_suspend(UI *ui)
|
||||||
{
|
{
|
||||||
TUIData *data = ui->data;
|
// on a non-UNIX system, this is a no-op
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
// kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
|
// kill(0, SIGTSTP) won't stop the UI thread, so we must poll for SIGCONT
|
||||||
// before continuing. This is done in another callback to avoid
|
// before continuing. This is done in another callback to avoid
|
||||||
// loop_poll_events recursion
|
// loop_poll_events recursion
|
||||||
multiqueue_put_event(data->loop->fast_events,
|
multiqueue_put_event(resize_events,
|
||||||
event_create(suspend_event, 1, ui));
|
event_create(suspend_event, 1, ui));
|
||||||
#else
|
|
||||||
// Resume the main thread as suspending isn't implemented.
|
|
||||||
CONTINUE(data->bridge);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1547,6 +1490,13 @@ static void tui_option_set(UI *ui, String name, Object value)
|
|||||||
ui->rgb = value.data.boolean;
|
ui->rgb = value.data.boolean;
|
||||||
data->print_attr_id = -1;
|
data->print_attr_id = -1;
|
||||||
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
|
invalidate(ui, 0, data->grid.height, 0, data->grid.width);
|
||||||
|
|
||||||
|
if (ui_client_channel_id) {
|
||||||
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ADD(args, STRING_OBJ(cstr_as_string(xstrdup("rgb"))));
|
||||||
|
ADD(args, BOOLEAN_OBJ(value.data.boolean));
|
||||||
|
rpc_send_event(ui_client_channel_id, "nvim_ui_set_option", args);
|
||||||
|
}
|
||||||
} else if (strequal(name.data, "ttimeout")) {
|
} else if (strequal(name.data, "ttimeout")) {
|
||||||
data->input.ttimeout = value.data.boolean;
|
data->input.ttimeout = value.data.boolean;
|
||||||
} else if (strequal(name.data, "ttimeoutlen")) {
|
} else if (strequal(name.data, "ttimeoutlen")) {
|
||||||
@@ -1659,8 +1609,11 @@ static void tui_guess_size(UI *ui)
|
|||||||
height = DFLT_ROWS;
|
height = DFLT_ROWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->bridge->bridge.width = ui->width = width;
|
ui->width = width;
|
||||||
data->bridge->bridge.height = ui->height = height;
|
ui->height = height;
|
||||||
|
|
||||||
|
// TODO(bfredl): only if different from last value
|
||||||
|
ui_schedule_refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void unibi_goto(UI *ui, int row, int col)
|
static void unibi_goto(UI *ui, int row, int col)
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "auto/config.h"
|
#include "auto/config.h"
|
||||||
#include "klib/kvec.h"
|
#include "klib/kvec.h"
|
||||||
|
#include "nvim/api/private/helpers.h"
|
||||||
#include "nvim/ascii.h"
|
#include "nvim/ascii.h"
|
||||||
#include "nvim/autocmd.h"
|
#include "nvim/autocmd.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
@@ -31,16 +32,11 @@
|
|||||||
#include "nvim/option.h"
|
#include "nvim/option.h"
|
||||||
#include "nvim/os/time.h"
|
#include "nvim/os/time.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
|
#include "nvim/tui/tui.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/ui_compositor.h"
|
#include "nvim/ui_compositor.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
#include "nvim/window.h"
|
#include "nvim/window.h"
|
||||||
#ifdef FEAT_TUI
|
|
||||||
# include "nvim/tui/tui.h"
|
|
||||||
#else
|
|
||||||
# include "nvim/msgpack_rpc/server.h"
|
|
||||||
#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"
|
||||||
@@ -126,28 +122,28 @@ void ui_init(void)
|
|||||||
kv_ensure_space(call_buf, 16);
|
kv_ensure_space(call_buf, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static UI *builtin_ui = NULL;
|
||||||
|
|
||||||
|
#ifdef EXITFREE
|
||||||
void ui_free_all_mem(void)
|
void ui_free_all_mem(void)
|
||||||
{
|
{
|
||||||
kv_destroy(call_buf);
|
kv_destroy(call_buf);
|
||||||
|
if (builtin_ui) {
|
||||||
|
tui_free_all_mem(builtin_ui);
|
||||||
|
builtin_ui = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ui_builtin_start(void)
|
void ui_builtin_start(void)
|
||||||
{
|
{
|
||||||
#ifdef FEAT_TUI
|
builtin_ui = tui_start();
|
||||||
tui_start();
|
}
|
||||||
#else
|
|
||||||
fprintf(stderr, "Nvim headless-mode started.\n");
|
UI *ui_get_by_index(int idx)
|
||||||
size_t len;
|
{
|
||||||
char **addrs = server_address_list(&len);
|
assert(idx < 16);
|
||||||
if (addrs != NULL) {
|
return uis[idx];
|
||||||
fprintf(stderr, "Listening on:\n");
|
|
||||||
for (size_t i = 0; i < len; i++) {
|
|
||||||
fprintf(stderr, "\t%s\n", addrs[i]);
|
|
||||||
}
|
|
||||||
xfree(addrs);
|
|
||||||
}
|
|
||||||
fprintf(stderr, "Press CTRL+C to exit.\n");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ui_rgb_attached(void)
|
bool ui_rgb_attached(void)
|
||||||
@@ -186,7 +182,7 @@ void ui_refresh(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updating_screen) {
|
if (updating_screen) {
|
||||||
deferred_refresh_event(NULL);
|
ui_schedule_refresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,10 +224,20 @@ void ui_refresh(void)
|
|||||||
screen_resize(width, height);
|
screen_resize(width, height);
|
||||||
p_lz = save_p_lz;
|
p_lz = save_p_lz;
|
||||||
} else {
|
} else {
|
||||||
Array args = ARRAY_DICT_INIT;
|
if (ui_client_attached) {
|
||||||
ADD(args, INTEGER_OBJ((int)width));
|
// TODO(bfredl): ui_refresh() should only be used on the server
|
||||||
ADD(args, INTEGER_OBJ((int)height));
|
// we are in the client process. forward the resize
|
||||||
rpc_send_event(ui_client_channel_id, "nvim_ui_try_resize", args);
|
Array args = ARRAY_DICT_INIT;
|
||||||
|
ADD(args, INTEGER_OBJ((int)width));
|
||||||
|
ADD(args, INTEGER_OBJ((int)height));
|
||||||
|
rpc_send_event(ui_client_channel_id, "nvim_ui_try_resize", args);
|
||||||
|
} else {
|
||||||
|
/// TODO(bfredl): Messy! The screen does not yet exist, but we need to
|
||||||
|
/// communicate its size from the TUI to the client. Clean this up
|
||||||
|
/// in The UI Devirtualization Project.
|
||||||
|
Rows = height;
|
||||||
|
Columns = width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext_widgets[kUIMessages]) {
|
if (ext_widgets[kUIMessages]) {
|
||||||
@@ -278,10 +284,6 @@ static void ui_refresh_event(void **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ui_schedule_refresh(void)
|
void ui_schedule_refresh(void)
|
||||||
{
|
|
||||||
loop_schedule_fast(&main_loop, event_create(deferred_refresh_event, 0));
|
|
||||||
}
|
|
||||||
static void deferred_refresh_event(void **argv)
|
|
||||||
{
|
{
|
||||||
multiqueue_put(resize_events, ui_refresh_event, 0);
|
multiqueue_put(resize_events, ui_refresh_event, 0);
|
||||||
}
|
}
|
||||||
|
@@ -1,223 +0,0 @@
|
|||||||
// 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
|
|
||||||
|
|
||||||
// UI wrapper that sends requests to the UI thread.
|
|
||||||
// Used by the built-in TUI and libnvim-based UIs.
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "nvim/api/private/defs.h"
|
|
||||||
#include "nvim/api/private/helpers.h"
|
|
||||||
#include "nvim/event/loop.h"
|
|
||||||
#include "nvim/grid_defs.h"
|
|
||||||
#include "nvim/highlight_defs.h"
|
|
||||||
#include "nvim/main.h"
|
|
||||||
#include "nvim/memory.h"
|
|
||||||
#include "nvim/ui.h"
|
|
||||||
#include "nvim/ui_bridge.h"
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
||||||
# include "ui_bridge.c.generated.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define UI(b) (((UIBridgeData *)b)->ui)
|
|
||||||
|
|
||||||
// Schedule a function call on the UI bridge thread.
|
|
||||||
#define UI_BRIDGE_CALL(ui, name, argc, ...) \
|
|
||||||
((UIBridgeData *)ui)->scheduler(event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui))
|
|
||||||
|
|
||||||
#define INT2PTR(i) ((void *)(intptr_t)i)
|
|
||||||
#define PTR2INT(p) ((Integer)(intptr_t)p)
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
||||||
# include "ui_events_bridge.generated.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler)
|
|
||||||
{
|
|
||||||
UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData));
|
|
||||||
rv->ui = ui;
|
|
||||||
rv->bridge.rgb = ui->rgb;
|
|
||||||
rv->bridge.width = ui->width;
|
|
||||||
rv->bridge.height = ui->height;
|
|
||||||
rv->bridge.stop = ui_bridge_stop;
|
|
||||||
rv->bridge.grid_resize = ui_bridge_grid_resize;
|
|
||||||
rv->bridge.grid_clear = ui_bridge_grid_clear;
|
|
||||||
rv->bridge.grid_cursor_goto = ui_bridge_grid_cursor_goto;
|
|
||||||
rv->bridge.mode_info_set = ui_bridge_mode_info_set;
|
|
||||||
rv->bridge.update_menu = ui_bridge_update_menu;
|
|
||||||
rv->bridge.busy_start = ui_bridge_busy_start;
|
|
||||||
rv->bridge.busy_stop = ui_bridge_busy_stop;
|
|
||||||
rv->bridge.mouse_on = ui_bridge_mouse_on;
|
|
||||||
rv->bridge.mouse_off = ui_bridge_mouse_off;
|
|
||||||
rv->bridge.mode_change = ui_bridge_mode_change;
|
|
||||||
rv->bridge.grid_scroll = ui_bridge_grid_scroll;
|
|
||||||
rv->bridge.hl_attr_define = ui_bridge_hl_attr_define;
|
|
||||||
rv->bridge.bell = ui_bridge_bell;
|
|
||||||
rv->bridge.visual_bell = ui_bridge_visual_bell;
|
|
||||||
rv->bridge.default_colors_set = ui_bridge_default_colors_set;
|
|
||||||
rv->bridge.flush = ui_bridge_flush;
|
|
||||||
rv->bridge.suspend = ui_bridge_suspend;
|
|
||||||
rv->bridge.set_title = ui_bridge_set_title;
|
|
||||||
rv->bridge.set_icon = ui_bridge_set_icon;
|
|
||||||
rv->bridge.screenshot = ui_bridge_screenshot;
|
|
||||||
rv->bridge.option_set = ui_bridge_option_set;
|
|
||||||
rv->bridge.raw_line = ui_bridge_raw_line;
|
|
||||||
rv->bridge.inspect = ui_bridge_inspect;
|
|
||||||
rv->scheduler = scheduler;
|
|
||||||
|
|
||||||
for (UIExtension i = 0; (int)i < kUIExtCount; i++) {
|
|
||||||
rv->bridge.ui_ext[i] = ui->ui_ext[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
rv->ui_main = ui_main;
|
|
||||||
uv_mutex_init(&rv->mutex);
|
|
||||||
uv_cond_init(&rv->cond);
|
|
||||||
uv_mutex_lock(&rv->mutex);
|
|
||||||
rv->ready = false;
|
|
||||||
|
|
||||||
if (uv_thread_create(&rv->ui_thread, ui_thread_run, rv)) {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Suspend the main thread until CONTINUE is called by the UI thread.
|
|
||||||
while (!rv->ready) {
|
|
||||||
uv_cond_wait(&rv->cond, &rv->mutex);
|
|
||||||
}
|
|
||||||
uv_mutex_unlock(&rv->mutex);
|
|
||||||
|
|
||||||
ui_attach_impl(&rv->bridge, 0);
|
|
||||||
|
|
||||||
return &rv->bridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ui_bridge_stopped(UIBridgeData *bridge)
|
|
||||||
{
|
|
||||||
uv_mutex_lock(&bridge->mutex);
|
|
||||||
bridge->stopped = true;
|
|
||||||
uv_mutex_unlock(&bridge->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_thread_run(void *data)
|
|
||||||
{
|
|
||||||
UIBridgeData *bridge = data;
|
|
||||||
bridge->ui_main(bridge, bridge->ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_bridge_stop(UI *b)
|
|
||||||
{
|
|
||||||
// Detach bridge first, so that "stop" is the last event the TUI loop
|
|
||||||
// receives from the main thread. #8041
|
|
||||||
ui_detach_impl(b, 0);
|
|
||||||
|
|
||||||
UIBridgeData *bridge = (UIBridgeData *)b;
|
|
||||||
bool stopped = bridge->stopped = false;
|
|
||||||
UI_BRIDGE_CALL(b, stop, 1, b);
|
|
||||||
for (;;) {
|
|
||||||
uv_mutex_lock(&bridge->mutex);
|
|
||||||
stopped = bridge->stopped;
|
|
||||||
uv_mutex_unlock(&bridge->mutex);
|
|
||||||
if (stopped) { // -V547
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// TODO(justinmk): Remove this. Use a cond-wait above. #9274
|
|
||||||
loop_poll_events(&main_loop, 10); // Process one event.
|
|
||||||
}
|
|
||||||
uv_thread_join(&bridge->ui_thread);
|
|
||||||
uv_mutex_destroy(&bridge->mutex);
|
|
||||||
uv_cond_destroy(&bridge->cond);
|
|
||||||
xfree(bridge->ui); // Threads joined, now safe to free UI container. #7922
|
|
||||||
xfree(b);
|
|
||||||
}
|
|
||||||
static void ui_bridge_stop_event(void **argv)
|
|
||||||
{
|
|
||||||
UI *ui = UI(argv[0]);
|
|
||||||
ui->stop(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs,
|
|
||||||
Array info)
|
|
||||||
{
|
|
||||||
HlAttrs *a = xmalloc(sizeof(HlAttrs));
|
|
||||||
*a = attrs;
|
|
||||||
UI_BRIDGE_CALL(ui, hl_attr_define, 3, ui, INT2PTR(id), a);
|
|
||||||
}
|
|
||||||
static void ui_bridge_hl_attr_define_event(void **argv)
|
|
||||||
{
|
|
||||||
UI *ui = UI(argv[0]);
|
|
||||||
Array info = ARRAY_DICT_INIT;
|
|
||||||
ui->hl_attr_define(ui, PTR2INT(argv[1]), *((HlAttrs *)argv[2]),
|
|
||||||
*((HlAttrs *)argv[2]), info);
|
|
||||||
xfree(argv[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
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]),
|
|
||||||
(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, LineFlags flags,
|
|
||||||
const schar_T *chunk, const sattr_T *attrs)
|
|
||||||
{
|
|
||||||
size_t ncol = (size_t)(endcol - startcol);
|
|
||||||
schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T));
|
|
||||||
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(flags), c, hl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_bridge_suspend(UI *b)
|
|
||||||
{
|
|
||||||
UIBridgeData *data = (UIBridgeData *)b;
|
|
||||||
uv_mutex_lock(&data->mutex);
|
|
||||||
UI_BRIDGE_CALL(b, suspend, 1, b);
|
|
||||||
data->ready = false;
|
|
||||||
// Suspend the main thread until CONTINUE is called by the UI thread.
|
|
||||||
while (!data->ready) {
|
|
||||||
uv_cond_wait(&data->cond, &data->mutex);
|
|
||||||
}
|
|
||||||
uv_mutex_unlock(&data->mutex);
|
|
||||||
}
|
|
||||||
static void ui_bridge_suspend_event(void **argv)
|
|
||||||
{
|
|
||||||
UI *ui = UI(argv[0]);
|
|
||||||
ui->suspend(ui);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_bridge_option_set(UI *ui, String name, Object value)
|
|
||||||
{
|
|
||||||
String copy_name = copy_string(name, NULL);
|
|
||||||
Object *copy_value = xmalloc(sizeof(Object));
|
|
||||||
*copy_value = copy_object(value, NULL);
|
|
||||||
UI_BRIDGE_CALL(ui, option_set, 4, ui, copy_name.data,
|
|
||||||
INT2PTR(copy_name.size), copy_value);
|
|
||||||
// TODO(bfredl): when/if TUI/bridge teardown is refactored to use events, the
|
|
||||||
// commit that introduced this special case can be reverted.
|
|
||||||
// For now this is needed for nvim_list_uis().
|
|
||||||
if (strequal(name.data, "termguicolors")) {
|
|
||||||
ui->rgb = value.data.boolean;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static void ui_bridge_option_set_event(void **argv)
|
|
||||||
{
|
|
||||||
UI *ui = UI(argv[0]);
|
|
||||||
String name = (String){ .data = argv[1], .size = (size_t)argv[2] };
|
|
||||||
Object value = *(Object *)argv[3];
|
|
||||||
ui->option_set(ui, name, value);
|
|
||||||
api_free_string(name);
|
|
||||||
api_free_object(value);
|
|
||||||
xfree(argv[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ui_bridge_inspect(UI *ui, Dictionary *info)
|
|
||||||
{
|
|
||||||
PUT(*info, "chan", INTEGER_OBJ(0));
|
|
||||||
}
|
|
@@ -1,47 +0,0 @@
|
|||||||
// Bridge for communication between a UI thread and nvim core.
|
|
||||||
// Used by the built-in TUI and libnvim-based UIs.
|
|
||||||
#ifndef NVIM_UI_BRIDGE_H
|
|
||||||
#define NVIM_UI_BRIDGE_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <uv.h>
|
|
||||||
|
|
||||||
#include "nvim/event/defs.h"
|
|
||||||
#include "nvim/ui.h"
|
|
||||||
|
|
||||||
struct ui_bridge_data;
|
|
||||||
|
|
||||||
typedef struct ui_bridge_data UIBridgeData;
|
|
||||||
typedef void (*ui_main_fn)(UIBridgeData *bridge, UI *ui);
|
|
||||||
struct ui_bridge_data {
|
|
||||||
UI bridge; // actual UI passed to ui_attach
|
|
||||||
UI *ui; // UI pointer that will have its callback called in
|
|
||||||
// another thread
|
|
||||||
event_scheduler scheduler;
|
|
||||||
uv_thread_t ui_thread;
|
|
||||||
ui_main_fn ui_main;
|
|
||||||
uv_mutex_t mutex;
|
|
||||||
uv_cond_t cond;
|
|
||||||
// When the UI thread is called, the main thread will suspend until
|
|
||||||
// the call returns. This flag is used as a condition for the main
|
|
||||||
// thread to continue.
|
|
||||||
bool ready;
|
|
||||||
// When a stop request is sent from the main thread, it must wait until the UI
|
|
||||||
// thread finishes handling all events. This flag is set by the UI thread as a
|
|
||||||
// signal that it will no longer send messages to the main thread.
|
|
||||||
bool stopped;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define CONTINUE(b) \
|
|
||||||
do { \
|
|
||||||
UIBridgeData *d = (UIBridgeData *)b; \
|
|
||||||
uv_mutex_lock(&d->mutex); \
|
|
||||||
d->ready = true; \
|
|
||||||
uv_cond_signal(&d->cond); \
|
|
||||||
uv_mutex_unlock(&d->mutex); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
|
||||||
# include "ui_bridge.h.generated.h"
|
|
||||||
#endif
|
|
||||||
#endif // NVIM_UI_BRIDGE_H
|
|
@@ -6,6 +6,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "nvim/api/private/helpers.h"
|
#include "nvim/api/private/helpers.h"
|
||||||
|
#include "nvim/eval.h"
|
||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/event/multiqueue.h"
|
#include "nvim/event/multiqueue.h"
|
||||||
#include "nvim/globals.h"
|
#include "nvim/globals.h"
|
||||||
@@ -24,23 +25,71 @@
|
|||||||
#endif
|
#endif
|
||||||
// uncrustify:on
|
// uncrustify:on
|
||||||
|
|
||||||
void ui_client_init(uint64_t chan)
|
uint64_t ui_client_start_server(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
varnumber_T exit_status;
|
||||||
|
char **args = xmalloc(((size_t)(2 + argc)) * sizeof(char *));
|
||||||
|
int args_idx = 0;
|
||||||
|
args[args_idx++] = xstrdup((const char *)get_vim_var_str(VV_PROGPATH));
|
||||||
|
args[args_idx++] = xstrdup("--embed");
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
args[args_idx++] = xstrdup(argv[i]);
|
||||||
|
}
|
||||||
|
args[args_idx++] = NULL;
|
||||||
|
|
||||||
|
Channel *channel = channel_job_start(args, CALLBACK_READER_INIT,
|
||||||
|
CALLBACK_READER_INIT, CALLBACK_NONE,
|
||||||
|
false, true, true, false, kChannelStdinPipe,
|
||||||
|
NULL, 0, 0, NULL, &exit_status);
|
||||||
|
if (ui_client_forward_stdin) {
|
||||||
|
close(0);
|
||||||
|
dup(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ui_client_run(bool remote_ui)
|
||||||
|
FUNC_ATTR_NORETURN
|
||||||
|
{
|
||||||
|
ui_builtin_start();
|
||||||
|
|
||||||
|
loop_poll_events(&main_loop, 1);
|
||||||
|
|
||||||
Array args = ARRAY_DICT_INIT;
|
Array args = ARRAY_DICT_INIT;
|
||||||
int width = Columns;
|
|
||||||
int height = Rows;
|
|
||||||
Dictionary opts = ARRAY_DICT_INIT;
|
Dictionary opts = ARRAY_DICT_INIT;
|
||||||
|
|
||||||
PUT(opts, "rgb", BOOLEAN_OBJ(true));
|
PUT(opts, "rgb", BOOLEAN_OBJ(true));
|
||||||
PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true));
|
PUT(opts, "ext_linegrid", BOOLEAN_OBJ(true));
|
||||||
PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true));
|
PUT(opts, "ext_termcolors", BOOLEAN_OBJ(true));
|
||||||
|
|
||||||
ADD(args, INTEGER_OBJ((int)width));
|
if (ui_client_termname) {
|
||||||
ADD(args, INTEGER_OBJ((int)height));
|
PUT(opts, "term_name", STRING_OBJ(cstr_as_string(ui_client_termname)));
|
||||||
|
}
|
||||||
|
if (ui_client_bg_respose != kNone) {
|
||||||
|
bool is_dark = (ui_client_bg_respose == kTrue);
|
||||||
|
PUT(opts, "term_background", STRING_OBJ(cstr_as_string(is_dark ? "dark" : "light")));
|
||||||
|
}
|
||||||
|
PUT(opts, "term_colors", INTEGER_OBJ(t_colors));
|
||||||
|
if (!remote_ui) {
|
||||||
|
PUT(opts, "stdin_tty", BOOLEAN_OBJ(stdin_isatty));
|
||||||
|
PUT(opts, "stdout_tty", BOOLEAN_OBJ(stdout_isatty));
|
||||||
|
if (ui_client_forward_stdin) {
|
||||||
|
PUT(opts, "stdin_fd", INTEGER_OBJ(UI_CLIENT_STDIN_FD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD(args, INTEGER_OBJ(Columns));
|
||||||
|
ADD(args, INTEGER_OBJ(Rows));
|
||||||
ADD(args, DICTIONARY_OBJ(opts));
|
ADD(args, DICTIONARY_OBJ(opts));
|
||||||
|
|
||||||
rpc_send_event(chan, "nvim_ui_attach", args);
|
rpc_send_event(ui_client_channel_id, "nvim_ui_attach", args);
|
||||||
ui_client_channel_id = chan;
|
ui_client_attached = true;
|
||||||
|
|
||||||
|
// os_exit() will be invoked when the client channel detaches
|
||||||
|
while (true) {
|
||||||
|
LOOP_PROCESS_EVENTS(&main_loop, resize_events, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error)
|
UIClientHandler ui_client_get_redraw_handler(const char *name, size_t name_len, Error *error)
|
||||||
@@ -63,20 +112,6 @@ Object handle_ui_client_redraw(uint64_t channel_id, Array args, Arena *arena, Er
|
|||||||
return NIL;
|
return NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// run the main thread in ui client mode
|
|
||||||
///
|
|
||||||
/// This is just a stub. the full version will handle input, resizing, etc
|
|
||||||
void ui_client_execute(uint64_t chan)
|
|
||||||
FUNC_ATTR_NORETURN
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
loop_poll_events(&main_loop, -1);
|
|
||||||
multiqueue_process_events(resize_events);
|
|
||||||
}
|
|
||||||
|
|
||||||
getout(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
|
static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
|
||||||
{
|
{
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
@@ -85,7 +120,7 @@ static HlAttrs ui_client_dict2hlattrs(Dictionary d, bool rgb)
|
|||||||
// TODO(bfredl): log "err"
|
// TODO(bfredl): log "err"
|
||||||
return HLATTRS_INIT;
|
return HLATTRS_INIT;
|
||||||
}
|
}
|
||||||
return dict2hlattrs(&dict, true, NULL, &err);
|
return dict2hlattrs(&dict, rgb, NULL, &err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ui_client_event_grid_resize(Array args)
|
void ui_client_event_grid_resize(Array args)
|
||||||
|
@@ -17,6 +17,26 @@ EXTERN size_t grid_line_buf_size INIT(= 0);
|
|||||||
EXTERN schar_T *grid_line_buf_char INIT(= NULL);
|
EXTERN schar_T *grid_line_buf_char INIT(= NULL);
|
||||||
EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
|
EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
|
||||||
|
|
||||||
|
// ID of the ui client channel. If zero, the client is not running.
|
||||||
|
EXTERN uint64_t ui_client_channel_id INIT(= 0);
|
||||||
|
|
||||||
|
// TODO(bfredl): the current structure for how tui and ui_client.c communicate is a bit awkward.
|
||||||
|
// This will be restructured as part of The UI Devirtualization Project.
|
||||||
|
|
||||||
|
/// Whether ui client has sent nvim_ui_attach yet
|
||||||
|
EXTERN bool ui_client_attached INIT(= false);
|
||||||
|
|
||||||
|
/// Whether ui client has gotten a response about the bg color of the terminal,
|
||||||
|
/// kTrue=dark, kFalse=light, kNone=no response yet
|
||||||
|
EXTERN TriState ui_client_bg_respose INIT(= kNone);
|
||||||
|
|
||||||
|
/// The ui client should forward its stdin to the nvim process
|
||||||
|
/// by convention, this uses fd=3 (next free number after stdio)
|
||||||
|
EXTERN bool ui_client_forward_stdin INIT(= false);
|
||||||
|
|
||||||
|
EXTERN char *ui_client_termname INIT(= "null");
|
||||||
|
|
||||||
|
#define UI_CLIENT_STDIN_FD 3
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ui_client.h.generated.h"
|
# include "ui_client.h.generated.h"
|
||||||
|
|
||||||
|
@@ -69,11 +69,7 @@ static char *features[] = {
|
|||||||
"-iconv",
|
"-iconv",
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEAT_TUI
|
|
||||||
"+tui",
|
"+tui",
|
||||||
#else
|
|
||||||
"-tui",
|
|
||||||
#endif
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -33,9 +33,17 @@ describe('autoread TUI FocusGained/FocusLost', function()
|
|||||||
|
|
||||||
helpers.write_file(path, '')
|
helpers.write_file(path, '')
|
||||||
lfs.touch(path, os.time() - 10)
|
lfs.touch(path, os.time() - 10)
|
||||||
feed_command('edit '..path)
|
|
||||||
feed_data('\027[O')
|
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
feed_command('edit '..path)
|
||||||
screen:expect{grid=[[
|
screen:expect{grid=[[
|
||||||
{1: } |
|
{1: } |
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
@@ -45,6 +53,17 @@ describe('autoread TUI FocusGained/FocusLost', function()
|
|||||||
:edit xtest-foo |
|
:edit xtest-foo |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]]}
|
]]}
|
||||||
|
feed_data('\027[O')
|
||||||
|
feed_data('\027[O')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:xtest-foo }|
|
||||||
|
:edit xtest-foo |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]], unchanged=true}
|
||||||
|
|
||||||
helpers.write_file(path, expected_addition)
|
helpers.write_file(path, expected_addition)
|
||||||
|
|
||||||
|
@@ -56,7 +56,7 @@ describe('Command-line option', function()
|
|||||||
screen:attach()
|
screen:attach()
|
||||||
local args = {
|
local args = {
|
||||||
nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE',
|
nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE',
|
||||||
'--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
|
'--cmd', '"set noswapfile shortmess+=IFW fileformats=unix"',
|
||||||
'-s', '-'
|
'-s', '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -544,7 +544,7 @@ describe('sysinit', function()
|
|||||||
nvim_exec() |
|
nvim_exec() |
|
||||||
cmd: aunmenu * |
|
cmd: aunmenu * |
|
||||||
> |
|
> |
|
||||||
<" -u NONE -i NONE --cmd "set noruler" -D 1,1 All|
|
<" -u NONE -i NONE --cmd "set noruler" -D 1,0-1 All|
|
||||||
|
|
|
|
||||||
]])
|
]])
|
||||||
command([[call chansend(g:id, "cont\n")]])
|
command([[call chansend(g:id, "cont\n")]])
|
||||||
|
@@ -243,7 +243,7 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
|
|||||||
end
|
end
|
||||||
|
|
||||||
loop_running = true
|
loop_running = true
|
||||||
session:run(on_request, on_notification, on_setup, timeout)
|
lsession:run(on_request, on_notification, on_setup, timeout)
|
||||||
loop_running = false
|
loop_running = false
|
||||||
if last_error then
|
if last_error then
|
||||||
local err = last_error
|
local err = last_error
|
||||||
@@ -251,7 +251,7 @@ function module.run_session(lsession, request_cb, notification_cb, setup_cb, tim
|
|||||||
error(err)
|
error(err)
|
||||||
end
|
end
|
||||||
|
|
||||||
return session.eof_err
|
return lsession.eof_err
|
||||||
end
|
end
|
||||||
|
|
||||||
function module.run(request_cb, notification_cb, setup_cb, timeout)
|
function module.run(request_cb, notification_cb, setup_cb, timeout)
|
||||||
@@ -465,8 +465,14 @@ end
|
|||||||
-- clear('-e')
|
-- clear('-e')
|
||||||
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
|
-- clear{args={'-e'}, args_rm={'-i'}, env={TERM=term}}
|
||||||
function module.clear(...)
|
function module.clear(...)
|
||||||
|
module.set_session(module.spawn_argv(false, ...))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- same params as clear, but does returns the session instead
|
||||||
|
-- of replacing the default session
|
||||||
|
function module.spawn_argv(keep, ...)
|
||||||
local argv, env, io_extra = module.new_argv(...)
|
local argv, env, io_extra = module.new_argv(...)
|
||||||
module.set_session(module.spawn(argv, nil, env, nil, io_extra))
|
return module.spawn(argv, nil, env, keep, io_extra)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Builds an argument list for use in clear().
|
-- Builds an argument list for use in clear().
|
||||||
|
@@ -19,6 +19,16 @@ describe('api', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it("qa! RPC request during insert-mode", function()
|
it("qa! RPC request during insert-mode", function()
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
-- Start the socket from the child nvim.
|
-- Start the socket from the child nvim.
|
||||||
child_session.feed_data(":echo serverstart('"..socket_name.."')\n")
|
child_session.feed_data(":echo serverstart('"..socket_name.."')\n")
|
||||||
|
|
||||||
|
@@ -24,6 +24,11 @@ local funcs = helpers.funcs
|
|||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
local is_ci = helpers.is_ci
|
local is_ci = helpers.is_ci
|
||||||
local is_os = helpers.is_os
|
local is_os = helpers.is_os
|
||||||
|
local new_pipename = helpers.new_pipename
|
||||||
|
local spawn_argv = helpers.spawn_argv
|
||||||
|
local set_session = helpers.set_session
|
||||||
|
local feed = helpers.feed
|
||||||
|
local eval = helpers.eval
|
||||||
|
|
||||||
if helpers.skip(helpers.is_os('win')) then return end
|
if helpers.skip(helpers.is_os('win')) then return end
|
||||||
|
|
||||||
@@ -34,7 +39,7 @@ describe('TUI', function()
|
|||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
clear()
|
clear()
|
||||||
local child_server = helpers.new_pipename()
|
local child_server = new_pipename()
|
||||||
screen = thelpers.screen_setup(0,
|
screen = thelpers.screen_setup(0,
|
||||||
string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
|
string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
|
||||||
nvim_prog, child_server, nvim_set))
|
nvim_prog, child_server, nvim_set))
|
||||||
@@ -1191,6 +1196,15 @@ describe('TUI', function()
|
|||||||
|
|
||||||
it('paste: split "start paste" code', function()
|
it('paste: split "start paste" code', function()
|
||||||
feed_data('i')
|
feed_data('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
{3:-- INSERT --} |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
-- Send split "start paste" sequence.
|
-- Send split "start paste" sequence.
|
||||||
feed_data('\027[2')
|
feed_data('\027[2')
|
||||||
feed_data('00~pasted from terminal\027[201~')
|
feed_data('00~pasted from terminal\027[201~')
|
||||||
@@ -1207,6 +1221,15 @@ describe('TUI', function()
|
|||||||
|
|
||||||
it('paste: split "stop paste" code', function()
|
it('paste: split "stop paste" code', function()
|
||||||
feed_data('i')
|
feed_data('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
{3:-- INSERT --} |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
-- Send split "stop paste" sequence.
|
-- Send split "stop paste" sequence.
|
||||||
feed_data('\027[200~pasted from terminal\027[20')
|
feed_data('\027[200~pasted from terminal\027[20')
|
||||||
feed_data('1~')
|
feed_data('1~')
|
||||||
@@ -1232,6 +1255,15 @@ describe('TUI', function()
|
|||||||
end)(vim.paste)
|
end)(vim.paste)
|
||||||
]], {})
|
]], {})
|
||||||
feed_data('i')
|
feed_data('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
{3:-- INSERT --} |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
feed_data('\027[200~pasted') -- phase 1
|
feed_data('\027[200~pasted') -- phase 1
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
pasted{1: } |
|
pasted{1: } |
|
||||||
@@ -1365,7 +1397,7 @@ describe('TUI', function()
|
|||||||
|
|
|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{5: }|
|
{5: }|
|
||||||
[[['chan', 0], ['height', 6], ['override', v:false|
|
[[['chan', 1], ['height', 6], ['override', v:false|
|
||||||
], ['rgb', v:false], ['width', 50]]] |
|
], ['rgb', v:false], ['width', 50]]] |
|
||||||
{10:Press ENTER or type command to continue}{1: } |
|
{10:Press ENTER or type command to continue}{1: } |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
@@ -1467,6 +1499,15 @@ describe('TUI', function()
|
|||||||
it('<C-h> #10134', function()
|
it('<C-h> #10134', function()
|
||||||
local screen = thelpers.screen_setup(0, '["'..nvim_prog
|
local screen = thelpers.screen_setup(0, '["'..nvim_prog
|
||||||
..[[", "-u", "NONE", "-i", "NONE", "--cmd", "set noruler", "--cmd", ':nnoremap <C-h> :echomsg "\<C-h\>"<CR>']]..']')
|
..[[", "-u", "NONE", "-i", "NONE", "--cmd", "set noruler", "--cmd", ':nnoremap <C-h> :echomsg "\<C-h\>"<CR>']]..']')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
command([[call chansend(b:terminal_job_id, "\<C-h>")]])
|
command([[call chansend(b:terminal_job_id, "\<C-h>")]])
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
@@ -1493,6 +1534,15 @@ describe('TUI UIEnter/UILeave', function()
|
|||||||
..[[, "--cmd", "autocmd VimEnter * :call add(g:evs, 'VimEnter')"]]
|
..[[, "--cmd", "autocmd VimEnter * :call add(g:evs, 'VimEnter')"]]
|
||||||
..']'
|
..']'
|
||||||
)
|
)
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
feed_data(":echo g:evs\n")
|
feed_data(":echo g:evs\n")
|
||||||
screen:expect{grid=[[
|
screen:expect{grid=[[
|
||||||
{1: } |
|
{1: } |
|
||||||
@@ -1513,61 +1563,88 @@ describe('TUI FocusGained/FocusLost', function()
|
|||||||
clear()
|
clear()
|
||||||
screen = thelpers.screen_setup(0, '["'..nvim_prog
|
screen = thelpers.screen_setup(0, '["'..nvim_prog
|
||||||
..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]')
|
..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler"]')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
feed_data(":autocmd FocusGained * echo 'gained'\n")
|
feed_data(":autocmd FocusGained * echo 'gained'\n")
|
||||||
feed_data(":autocmd FocusLost * echo 'lost'\n")
|
feed_data(":autocmd FocusLost * echo 'lost'\n")
|
||||||
feed_data("\034\016") -- CTRL-\ CTRL-N
|
feed_data("\034\016") -- CTRL-\ CTRL-N
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('in normal-mode', function()
|
it('in normal-mode', function()
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
:autocmd FocusLost * echo 'lost' |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
retry(2, 3 * screen.timeout, function()
|
retry(2, 3 * screen.timeout, function()
|
||||||
feed_data('\027[I')
|
feed_data('\027[I')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
{1: } |
|
{1: } |
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{5:[No Name] }|
|
{5:[No Name] }|
|
||||||
gained |
|
gained |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
|
|
||||||
feed_data('\027[O')
|
feed_data('\027[O')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
{1: } |
|
{1: } |
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{5:[No Name] }|
|
{5:[No Name] }|
|
||||||
lost |
|
lost |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('in insert-mode', function()
|
it('in insert-mode', function()
|
||||||
feed_command('set noshowmode')
|
feed_command('set noshowmode')
|
||||||
feed_data('i')
|
feed_data('i')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
:set noshowmode |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
retry(2, 3 * screen.timeout, function()
|
retry(2, 3 * screen.timeout, function()
|
||||||
feed_data('\027[I')
|
feed_data('\027[I')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
{1: } |
|
{1: } |
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{5:[No Name] }|
|
{5:[No Name] }|
|
||||||
gained |
|
gained |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
feed_data('\027[O')
|
feed_data('\027[O')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
{1: } |
|
{1: } |
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{4:~ }|
|
{4:~ }|
|
||||||
{5:[No Name] }|
|
{5:[No Name] }|
|
||||||
lost |
|
lost |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -1604,6 +1681,15 @@ describe('TUI FocusGained/FocusLost', function()
|
|||||||
feed_data(":autocmd!\n")
|
feed_data(":autocmd!\n")
|
||||||
feed_data(":autocmd FocusLost * call append(line('$'), 'lost')\n")
|
feed_data(":autocmd FocusLost * call append(line('$'), 'lost')\n")
|
||||||
feed_data(":autocmd FocusGained * call append(line('$'), 'gained')\n")
|
feed_data(":autocmd FocusGained * call append(line('$'), 'gained')\n")
|
||||||
|
screen:expect{grid=[[
|
||||||
|
{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
retry(2, 3 * screen.timeout, function()
|
retry(2, 3 * screen.timeout, function()
|
||||||
-- Enter cmdline-mode.
|
-- Enter cmdline-mode.
|
||||||
feed_data(':')
|
feed_data(':')
|
||||||
@@ -1662,9 +1748,7 @@ describe('TUI FocusGained/FocusLost', function()
|
|||||||
feed_data(":echom 'msg1'|echom 'msg2'|echom 'msg3'|echom 'msg4'|echom 'msg5'\n")
|
feed_data(":echom 'msg1'|echom 'msg2'|echom 'msg3'|echom 'msg4'|echom 'msg5'\n")
|
||||||
-- Execute :messages to provoke the press-enter prompt.
|
-- Execute :messages to provoke the press-enter prompt.
|
||||||
feed_data(":messages\n")
|
feed_data(":messages\n")
|
||||||
feed_data('\027[I')
|
screen:expect{grid=[[
|
||||||
feed_data('\027[I')
|
|
||||||
screen:expect([[
|
|
||||||
msg1 |
|
msg1 |
|
||||||
msg2 |
|
msg2 |
|
||||||
msg3 |
|
msg3 |
|
||||||
@@ -1672,7 +1756,18 @@ describe('TUI FocusGained/FocusLost', function()
|
|||||||
msg5 |
|
msg5 |
|
||||||
{10:Press ENTER or type command to continue}{1: } |
|
{10:Press ENTER or type command to continue}{1: } |
|
||||||
{3:-- TERMINAL --} |
|
{3:-- TERMINAL --} |
|
||||||
]])
|
]]}
|
||||||
|
feed_data('\027[I')
|
||||||
|
feed_data('\027[I')
|
||||||
|
screen:expect{grid=[[
|
||||||
|
msg1 |
|
||||||
|
msg2 |
|
||||||
|
msg3 |
|
||||||
|
msg4 |
|
||||||
|
msg5 |
|
||||||
|
{10:Press ENTER or type command to continue}{1: } |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]], unchanged=true}
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -2040,7 +2135,7 @@ describe("TUI", function()
|
|||||||
|
|
||||||
retry(nil, 3000, function() -- Wait for log file to be flushed.
|
retry(nil, 3000, function() -- Wait for log file to be flushed.
|
||||||
local log = read_file('Xtest_tui_verbose_log') or ''
|
local log = read_file('Xtest_tui_verbose_log') or ''
|
||||||
eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n'))
|
eq('--- Terminal info --- {{{\n', string.match(log, '%-%-%- Terminal.-\n')) -- }}}
|
||||||
ok(#log > 50)
|
ok(#log > 50)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
@@ -2164,3 +2259,165 @@ describe('TUI bg color', function()
|
|||||||
screen:expect{any='new_bg=dark'}
|
screen:expect{any='new_bg=dark'}
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
-- These tests require `thelpers` because --headless/--embed
|
||||||
|
-- does not initialize the TUI.
|
||||||
|
describe("TUI as a client", function()
|
||||||
|
|
||||||
|
it("connects to remote instance (with its own TUI)", function()
|
||||||
|
local server_super = spawn_argv(false) -- equivalent to clear()
|
||||||
|
local client_super = spawn_argv(true)
|
||||||
|
|
||||||
|
set_session(server_super)
|
||||||
|
local server_pipe = new_pipename()
|
||||||
|
local screen_server = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
|
||||||
|
nvim_prog, server_pipe, nvim_set))
|
||||||
|
|
||||||
|
feed_data("iHello, World")
|
||||||
|
screen_server:expect{grid=[[
|
||||||
|
Hello, World{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
{3:-- INSERT --} |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
feed_data("\027")
|
||||||
|
screen_server:expect{grid=[[
|
||||||
|
Hello, Worl{1:d} |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
set_session(client_super)
|
||||||
|
local screen_client = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
|
||||||
|
nvim_prog, server_pipe))
|
||||||
|
|
||||||
|
screen_client:expect{grid=[[
|
||||||
|
Hello, Worl{1:d} |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
feed_data(":q!\n")
|
||||||
|
|
||||||
|
server_super:close()
|
||||||
|
client_super:close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("connects to remote instance (--headless)", function()
|
||||||
|
local server = helpers.spawn_argv(false) -- equivalent to clear()
|
||||||
|
local client_super = spawn_argv(true)
|
||||||
|
|
||||||
|
set_session(server)
|
||||||
|
local server_pipe = eval'v:servername'
|
||||||
|
feed'iHalloj!<esc>'
|
||||||
|
|
||||||
|
set_session(client_super)
|
||||||
|
local screen = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
|
||||||
|
nvim_prog, server_pipe))
|
||||||
|
|
||||||
|
screen:expect{grid=[[
|
||||||
|
Halloj{1:!} |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
client_super:close()
|
||||||
|
server:close()
|
||||||
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
it("throws error when no server exists", function()
|
||||||
|
clear()
|
||||||
|
local screen = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "127.0.0.1:2436546", "--remote-ui"]]=],
|
||||||
|
nvim_prog))
|
||||||
|
|
||||||
|
screen:try_resize(60, 7)
|
||||||
|
|
||||||
|
screen:expect([[
|
||||||
|
Remote ui failed to start: {MATCH:.*}|
|
||||||
|
|
|
||||||
|
[Process exited 1]{1: } |
|
||||||
|
|
|
||||||
|
|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("exits when server quits", function()
|
||||||
|
local server_super = spawn_argv(false) -- equivalent to clear()
|
||||||
|
local client_super = spawn_argv(true)
|
||||||
|
|
||||||
|
set_session(server_super)
|
||||||
|
local server_pipe = new_pipename()
|
||||||
|
local screen_server = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=],
|
||||||
|
nvim_prog, server_pipe, nvim_set))
|
||||||
|
|
||||||
|
feed_data("iHello, World")
|
||||||
|
screen_server:expect{grid=[[
|
||||||
|
Hello, World{1: } |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
{3:-- INSERT --} |
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
feed_data("\027")
|
||||||
|
screen_server:expect{grid=[[
|
||||||
|
Hello, Worl{1:d} |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
set_session(client_super)
|
||||||
|
local screen_client = thelpers.screen_setup(0,
|
||||||
|
string.format([=[["%s", "-u", "NONE", "-i", "NONE", "--server", "%s", "--remote-ui"]]=],
|
||||||
|
nvim_prog, server_pipe))
|
||||||
|
|
||||||
|
screen_client:expect{grid=[[
|
||||||
|
Hello, Worl{1:d} |
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{4:~ }|
|
||||||
|
{5:[No Name] [+] }|
|
||||||
|
|
|
||||||
|
{3:-- TERMINAL --} |
|
||||||
|
]]}
|
||||||
|
|
||||||
|
-- quitting the server
|
||||||
|
set_session(server_super)
|
||||||
|
feed_data(":q!\n")
|
||||||
|
screen_server:expect({any="Process exited 0"})
|
||||||
|
|
||||||
|
-- assert that client has exited
|
||||||
|
screen_client:expect({any="Process exited 0"})
|
||||||
|
|
||||||
|
server_super:close()
|
||||||
|
client_super:close()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
@@ -1550,7 +1550,8 @@ function Screen:_get_attr_id(attr_state, attrs, hl_id)
|
|||||||
attr_state.modified = true
|
attr_state.modified = true
|
||||||
return id
|
return id
|
||||||
end
|
end
|
||||||
return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][1])
|
local kind = self._options.rgb and 1 or 2
|
||||||
|
return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][kind])
|
||||||
else
|
else
|
||||||
if self:_equal_attrs(attrs, {}) then
|
if self:_equal_attrs(attrs, {}) then
|
||||||
-- ignore this attrs
|
-- ignore this attrs
|
||||||
|
@@ -33,7 +33,7 @@ itp('handle_background_color', function()
|
|||||||
term_input.waiting_for_bg_response = 1
|
term_input.waiting_for_bg_response = 1
|
||||||
eq(kComplete, handle_background_color(term_input))
|
eq(kComplete, handle_background_color(term_input))
|
||||||
eq(0, term_input.waiting_for_bg_response)
|
eq(0, term_input.waiting_for_bg_response)
|
||||||
eq(1, multiqueue.multiqueue_size(events))
|
eq(0, multiqueue.multiqueue_size(events))
|
||||||
|
|
||||||
local event = multiqueue.multiqueue_get(events)
|
local event = multiqueue.multiqueue_get(events)
|
||||||
local bg_event = ffi.cast("Event*", event.argv[1])
|
local bg_event = ffi.cast("Event*", event.argv[1])
|
||||||
|
Reference in New Issue
Block a user