diff options
Diffstat (limited to 'src')
32 files changed, 2030 insertions, 664 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 8deeff4654..a5d4170062 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -11,31 +11,38 @@ endif() endif() set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches) +set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators) set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) -set(MSGPACK_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genmsgpack.lua) +set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua) +set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) set(MSGPACK_LUA_C_BINDINGS ${GENERATED_DIR}/msgpack_lua_c_bindings.generated.c) -set(HEADER_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gendeclarations.lua) +set(HEADER_GENERATOR ${GENERATOR_DIR}/gen_declarations.lua) set(GENERATED_INCLUDES_DIR ${PROJECT_BINARY_DIR}/include) set(GENERATED_API_DISPATCH ${GENERATED_DIR}/api/private/dispatch_wrappers.generated.h) set(GENERATED_FUNCS_METADATA ${GENERATED_DIR}/api/private/funcs_metadata.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_REMOTE ${GENERATED_DIR}/ui_events_remote.generated.h) +set(GENERATED_UI_EVENTS_BRIDGE ${GENERATED_DIR}/ui_events_bridge.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_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) -set(EX_CMDS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genex_cmds.lua) -set(FUNCS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/geneval.lua) -set(EVENTS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gen_events.lua) -set(OPTIONS_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genoptions.lua) -set(UNICODE_TABLES_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/genunicodetables.lua) +set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) +set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) +set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua) +set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) +set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) set(GENERATED_UNICODE_TABLES ${GENERATED_DIR}/unicode_tables.generated.h) set(VIM_MODULE_FILE ${GENERATED_DIR}/lua/vim_module.generated.h) set(VIM_MODULE_SOURCE ${PROJECT_SOURCE_DIR}/src/nvim/lua/vim.lua) -set(CHAR_BLOB_GENERATOR ${PROJECT_SOURCE_DIR}/scripts/gencharblob.lua) +set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua) set(LINT_SUPPRESS_FILE ${PROJECT_BINARY_DIR}/errors.json) set(LINT_SUPPRESS_URL_BASE "https://raw.githubusercontent.com/neovim/doc/gh-pages/reports/clint") set(LINT_SUPPRESS_URL "${LINT_SUPPRESS_URL_BASE}/errors.json") @@ -49,6 +56,7 @@ set(LINT_SUPPRESSES_INSTALL_SCRIPT "${PROJECT_SOURCE_DIR}/cmake/InstallClintErro file(GLOB UNICODE_FILES ${UNICODE_DIR}/*.txt) file(GLOB API_HEADERS api/*.h) +list(REMOVE_ITEM API_HEADERS ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h) file(GLOB MSGPACK_RPC_HEADERS msgpack_rpc/*.h) include_directories(${GENERATED_DIR}) @@ -185,7 +193,11 @@ endfunction() # These lists must be mutually exclusive. foreach(sfile ${NVIM_SOURCES} "${CMAKE_CURRENT_LIST_DIR}/regexp_nfa.c" - ${GENERATED_API_DISPATCH}) + ${GENERATED_API_DISPATCH} + "${GENERATED_UI_EVENTS_CALL}" + "${GENERATED_UI_EVENTS_REMOTE}" + "${GENERATED_UI_EVENTS_BRIDGE}" + ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") if(${d} MATCHES "^[.][.]|auto/") @@ -228,7 +240,7 @@ add_custom_command(OUTPUT ${GENERATED_UNICODE_TABLES} add_custom_command( OUTPUT ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${API_METADATA} ${MSGPACK_LUA_C_BINDINGS} - COMMAND ${LUA_PRG} ${MSGPACK_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} + COMMAND ${LUA_PRG} ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_API_DISPATCH} ${GENERATED_FUNCS_METADATA} ${API_METADATA} ${MSGPACK_LUA_C_BINDINGS} @@ -236,7 +248,7 @@ add_custom_command( DEPENDS ${API_HEADERS} ${MSGPACK_RPC_HEADERS} - ${MSGPACK_GENERATOR} + ${API_DISPATCH_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/api/dispatch_deprecated.lua ) @@ -253,6 +265,24 @@ list(APPEND NVIM_GENERATED_SOURCES "${MSGPACK_LUA_C_BINDINGS}" ) +add_custom_command( + OUTPUT ${GENERATED_UI_EVENTS} + ${GENERATED_UI_EVENTS_CALL} + ${GENERATED_UI_EVENTS_REMOTE} + ${GENERATED_UI_EVENTS_BRIDGE} + ${GENERATED_UI_EVENTS_METADATA} + COMMAND ${LUA_PRG} ${API_UI_EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} + ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h + ${GENERATED_UI_EVENTS} + ${GENERATED_UI_EVENTS_CALL} + ${GENERATED_UI_EVENTS_REMOTE} + ${GENERATED_UI_EVENTS_BRIDGE} + ${GENERATED_UI_EVENTS_METADATA} + DEPENDS + ${API_UI_EVENTS_GENERATOR} + ${CMAKE_CURRENT_LIST_DIR}/api/ui_events.in.h +) + list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EVENTS_ENUM}" @@ -518,7 +548,7 @@ set(NO_SINGLE_CHECK_HEADERS foreach(hfile ${NVIM_HEADERS}) get_test_target(test-includes "${hfile}" relative_path texe) - if(NOT ${hfile} MATCHES "[.]c[.]h$") + if(NOT ${hfile} MATCHES "[.](c|in)[.]h$") set(tsource "${GENERATED_DIR}/${relative_path}.test-include.c") write_file("${tsource}" "#include \"${hfile}\"\nint main(int argc, char **argv) { return 0; }") add_executable( diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 4cc4b600d5..1d7b305da3 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -33,6 +33,7 @@ typedef struct { #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/private/helpers.c.generated.h" # include "api/private/funcs_metadata.generated.h" +# include "api/private/ui_events_metadata.generated.h" #endif /// Start block that may cause vimscript exceptions @@ -820,6 +821,7 @@ Dictionary api_metadata(void) if (!metadata.size) { PUT(metadata, "version", DICTIONARY_OBJ(version_dict())); init_function_metadata(&metadata); + init_ui_event_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); } @@ -843,6 +845,22 @@ static void init_function_metadata(Dictionary *metadata) PUT(*metadata, "functions", functions); } +static void init_ui_event_metadata(Dictionary *metadata) +{ + msgpack_unpacked unpacked; + msgpack_unpacked_init(&unpacked); + if (msgpack_unpack_next(&unpacked, + (const char *)ui_events_metadata, + sizeof(ui_events_metadata), + NULL) != MSGPACK_UNPACK_SUCCESS) { + abort(); + } + Object ui_events; + msgpack_rpc_to_object(&unpacked.data, &ui_events); + msgpack_unpacked_destroy(&unpacked); + PUT(*metadata, "ui_events", ui_events); +} + static void init_error_type_metadata(Dictionary *metadata) { Dictionary types = ARRAY_DICT_INIT; @@ -885,6 +903,24 @@ static void init_type_metadata(Dictionary *metadata) PUT(*metadata, "types", DICTIONARY_OBJ(types)); } +String copy_string(String str) +{ + if (str.data != NULL) { + return (String){ .data = xmemdupz(str.data, str.size), .size = str.size }; + } else { + return (String)STRING_INIT; + } +} + +Array copy_array(Array array) +{ + Array rv = ARRAY_DICT_INIT; + for (size_t i = 0; i < array.size; i++) { + ADD(rv, copy_object(array.items[i])); + } + return rv; +} + /// Creates a deep clone of an object Object copy_object(Object obj) { @@ -896,15 +932,10 @@ Object copy_object(Object obj) return obj; case kObjectTypeString: - return STRING_OBJ(cstr_to_string(obj.data.string.data)); + return STRING_OBJ(copy_string(obj.data.string)); - case kObjectTypeArray: { - Array rv = ARRAY_DICT_INIT; - for (size_t i = 0; i < obj.data.array.size; i++) { - ADD(rv, copy_object(obj.data.array.items[i])); - } - return ARRAY_OBJ(rv); - } + case kObjectTypeArray: + return ARRAY_OBJ(copy_array(obj.data.array)); case kObjectTypeDictionary: { Dictionary rv = ARRAY_DICT_INIT; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 3c0e8bc049..1b29f2fc78 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -19,6 +19,7 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/ui.c.generated.h" +# include "ui_events_remote.generated.h" #endif typedef struct { @@ -52,7 +53,7 @@ void remote_ui_disconnect(uint64_t channel_id) void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, Dictionary options, Error *err) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI already attached for channel"); @@ -124,7 +125,7 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, } void nvim_ui_detach(uint64_t channel_id, Error *err) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI is not attached for channel"); @@ -136,7 +137,7 @@ void nvim_ui_detach(uint64_t channel_id, Error *err) void nvim_ui_try_resize(uint64_t channel_id, Integer width, Integer height, Error *err) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, kErrorTypeException, "UI is not attached for channel"); @@ -157,7 +158,7 @@ void nvim_ui_try_resize(uint64_t channel_id, Integer width, void nvim_ui_set_option(uint64_t channel_id, String name, Object value, Error *error) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(error, kErrorTypeException, "UI is not attached for channel"); @@ -236,100 +237,6 @@ static void push_call(UI *ui, char *name, Array args) kv_A(data->buffer, kv_size(data->buffer) - 1).data.array = call; } -static void remote_ui_resize(UI *ui, int width, int height) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(width)); - ADD(args, INTEGER_OBJ(height)); - push_call(ui, "resize", args); -} - -static void remote_ui_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "clear", args); -} - -static void remote_ui_eol_clear(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "eol_clear", args); -} - -static void remote_ui_cursor_goto(UI *ui, int row, int col) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); - push_call(ui, "cursor_goto", args); -} - -static void remote_ui_update_menu(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "update_menu", args); -} - -static void remote_ui_busy_start(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_start", args); -} - -static void remote_ui_busy_stop(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "busy_stop", args); -} - -static void remote_ui_mouse_on(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_on", args); -} - -static void remote_ui_mouse_off(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "mouse_off", args); -} - -static void remote_ui_mode_change(UI *ui, int mode_idx) -{ - Array args = ARRAY_DICT_INIT; - - char *full_name = shape_table[mode_idx].full_name; - ADD(args, STRING_OBJ(cstr_to_string(full_name))); - - ADD(args, INTEGER_OBJ(mode_idx)); - push_call(ui, "mode_change", args); -} - -static void remote_ui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(top)); - ADD(args, INTEGER_OBJ(bot)); - ADD(args, INTEGER_OBJ(left)); - ADD(args, INTEGER_OBJ(right)); - push_call(ui, "set_scroll_region", args); -} - -static void remote_ui_scroll(UI *ui, int count) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(count)); - push_call(ui, "scroll", args); -} - -static void remote_ui_mode_info_set(UI *ui, bool guicursor_enabled, Array data) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, BOOLEAN_OBJ(guicursor_enabled)); - ADD(args, copy_object(ARRAY_OBJ(data))); - push_call(ui, "mode_info_set", args); -} static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) { @@ -372,47 +279,6 @@ static void remote_ui_highlight_set(UI *ui, HlAttrs attrs) push_call(ui, "highlight_set", args); } -static void remote_ui_put(UI *ui, uint8_t *data, size_t size) -{ - Array args = ARRAY_DICT_INIT; - String str = { .data = xmemdupz(data, size), .size = size }; - ADD(args, STRING_OBJ(str)); - push_call(ui, "put", args); -} - -static void remote_ui_bell(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "bell", args); -} - -static void remote_ui_visual_bell(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "visual_bell", args); -} - -static void remote_ui_update_fg(UI *ui, int fg) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(fg)); - push_call(ui, "update_fg", args); -} - -static void remote_ui_update_bg(UI *ui, int bg) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(bg)); - push_call(ui, "update_bg", args); -} - -static void remote_ui_update_sp(UI *ui, int sp) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, INTEGER_OBJ(sp)); - push_call(ui, "update_sp", args); -} - static void remote_ui_flush(UI *ui) { UIData *data = ui->data; @@ -422,26 +288,6 @@ static void remote_ui_flush(UI *ui) } } -static void remote_ui_suspend(UI *ui) -{ - Array args = ARRAY_DICT_INIT; - push_call(ui, "suspend", args); -} - -static void remote_ui_set_title(UI *ui, char *title) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(title))); - push_call(ui, "set_title", args); -} - -static void remote_ui_set_icon(UI *ui, char *icon) -{ - Array args = ARRAY_DICT_INIT; - ADD(args, STRING_OBJ(cstr_to_string(icon))); - push_call(ui, "set_icon", args); -} - static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) { Array my_args = ARRAY_DICT_INIT; diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h new file mode 100644 index 0000000000..45d04335e4 --- /dev/null +++ b/src/nvim/api/ui_events.in.h @@ -0,0 +1,71 @@ +#ifndef NVIM_API_UI_EVENTS_IN_H +#define NVIM_API_UI_EVENTS_IN_H + +// This file is not compiled, just parsed for definitons +#ifdef INCLUDE_GENERATED_DECLARATIONS +# error "don't include this file, include nvim/ui.h" +#endif + +#include "nvim/api/private/defs.h" +#include "nvim/func_attr.h" +#include "nvim/ui.h" + +void resize(Integer rows, Integer columns) + FUNC_API_SINCE(3); +void clear(void) + FUNC_API_SINCE(3); +void eol_clear(void) + FUNC_API_SINCE(3); +void cursor_goto(Integer row, Integer col) + FUNC_API_SINCE(3); +void mode_info_set(Boolean enabled, Array cursor_styles) + FUNC_API_SINCE(3); +void update_menu(void) + FUNC_API_SINCE(3); +void busy_start(void) + FUNC_API_SINCE(3); +void busy_stop(void) + FUNC_API_SINCE(3); +void mouse_on(void) + FUNC_API_SINCE(3); +void mouse_off(void) + FUNC_API_SINCE(3); +void mode_change(String mode, Integer mode_idx) + FUNC_API_SINCE(3); +void set_scroll_region(Integer top, Integer bot, Integer left, Integer right) + FUNC_API_SINCE(3); +void scroll(Integer count) + FUNC_API_SINCE(3); +void highlight_set(HlAttrs attrs) + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL FUNC_API_BRIDGE_IMPL; +void put(String str) + FUNC_API_SINCE(3); +void bell(void) + FUNC_API_SINCE(3); +void visual_bell(void) + FUNC_API_SINCE(3); +void flush(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_IMPL; +void update_fg(Integer fg) + FUNC_API_SINCE(3); +void update_bg(Integer bg) + FUNC_API_SINCE(3); +void update_sp(Integer sp) + FUNC_API_SINCE(3); +void suspend(void) + FUNC_API_SINCE(3) FUNC_API_BRIDGE_IMPL; +void set_title(String title) + FUNC_API_SINCE(3); +void set_icon(String icon) + FUNC_API_SINCE(3); + +void popupmenu_show(Array items, Integer selected, Integer row, Integer col) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void popupmenu_hide(void) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void popupmenu_select(Integer selected) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; +void tabline_update(Tabpage current, Array tabs) + FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; + +#endif // NVIM_API_UI_EVENTS_IN_H diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 12e8194c15..d0bb840b8d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -659,7 +659,7 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) /// @param channel_id Channel id (passed automatically by the dispatcher) /// @param event Event type string void nvim_subscribe(uint64_t channel_id, String event) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { size_t length = (event.size < METHOD_MAXLEN ? event.size : METHOD_MAXLEN); char e[METHOD_MAXLEN + 1]; @@ -673,7 +673,7 @@ void nvim_subscribe(uint64_t channel_id, String event) /// @param channel_id Channel id (passed automatically by the dispatcher) /// @param event Event type string void nvim_unsubscribe(uint64_t channel_id, String event) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { size_t length = (event.size < METHOD_MAXLEN ? event.size : @@ -722,7 +722,7 @@ Dictionary nvim_get_mode(void) } Array nvim_get_api_info(uint64_t channel_id) - FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_REMOTE_ONLY { Array rv = ARRAY_DICT_INIT; @@ -755,7 +755,7 @@ Array nvim_get_api_info(uint64_t channel_id) /// which resulted in an error, the error type and the error message. If an /// error ocurred, the values from all preceding calls will still be returned. Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err) - FUNC_API_SINCE(1) FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_REMOTE_ONLY { Array rv = ARRAY_DICT_INIT; Array results = ARRAY_DICT_INIT; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 4ed75d8a2d..f54979eb1e 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -24,6 +24,7 @@ #include <inttypes.h> #include "nvim/api/private/handle.h" +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/assert.h" #include "nvim/vim.h" @@ -3001,13 +3002,13 @@ static bool ti_change(char_u *str, char_u **last) return false; } -/* - * Put current window title back (used after calling a shell) - */ + +/// Set current window title void resettitle(void) { - ui_set_title((char *)lasttitle); - ui_set_icon((char *)lasticon); + ui_call_set_title(cstr_as_string((char *)lasttitle)); + ui_call_set_icon(cstr_as_string((char *)lasticon)); + ui_flush(); } # if defined(EXITFREE) diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index cb02befeaf..a0406cf418 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6285,13 +6285,14 @@ static void ex_stop(exarg_T *eap) autowrite_all(); } ui_cursor_goto((int)Rows - 1, 0); - ui_putc('\n'); + ui_linefeed(); + ui_flush(); + ui_call_suspend(); // call machine specific function ui_flush(); - ui_suspend(); /* call machine specific function */ maketitle(); - resettitle(); /* force updating the title */ + resettitle(); // force updating the title redraw_later_clear(); - ui_refresh(); /* may have resized window */ + ui_refresh(); // may have resized window } } diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index 1049b5b7a6..9146a1a8ea 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -181,7 +181,7 @@ #ifdef DEFINE_FUNC_ATTRIBUTES # define FUNC_API_ASYNC # define FUNC_API_NOEXPORT -# define FUNC_API_NOEVAL +# define FUNC_API_REMOTE_ONLY # define FUNC_API_SINCE(X) # define FUNC_API_DEPRECATED_SINCE(X) # define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua new file mode 100644 index 0000000000..d3047e1a9c --- /dev/null +++ b/src/nvim/generators/c_grammar.lua @@ -0,0 +1,50 @@ +lpeg = require('lpeg') + +-- lpeg grammar for building api metadata from a set of header files. It +-- ignores comments and preprocessor commands and parses a very small subset +-- of C prototypes with a limited set of types +local P, R, S = lpeg.P, lpeg.R, lpeg.S +local C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg + +local any = P(1) -- (consume one character) +local letter = R('az', 'AZ') + S('_$') +local num = R('09') +local alpha = letter + num +local nl = P('\r\n') + P('\n') +local not_nl = any - nl +local ws = S(' \t') + nl +local fill = ws ^ 0 +local c_comment = P('//') * (not_nl ^ 0) +local c_preproc = P('#') * (not_nl ^ 0) +local typed_container = + (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') +local c_id = ( + typed_container + + (letter * (alpha ^ 0)) +) +local c_void = P('void') +local c_param_type = ( + ((P('Error') * fill * P('*') * fill) * Cc('error')) + + (C(c_id) * (ws ^ 1)) + ) +local c_type = (C(c_void) * (ws ^ 1)) + c_param_type +local c_param = Ct(c_param_type * C(c_id)) +local c_param_list = c_param * (fill * (P(',') * fill * c_param) ^ 0) +local c_params = Ct(c_void + c_param_list) +local c_proto = Ct( + Cg(c_type, 'return_type') * Cg(c_id, 'name') * + fill * P('(') * fill * Cg(c_params, 'parameters') * fill * P(')') * + Cg(Cc(false), 'async') * + (fill * Cg((P('FUNC_API_SINCE(') * C(num ^ 1)) * P(')'), 'since') ^ -1) * + (fill * Cg((P('FUNC_API_DEPRECATED_SINCE(') * C(num ^ 1)) * P(')'), + 'deprecated_since') ^ -1) * + (fill * Cg((P('FUNC_API_ASYNC') * Cc(true)), 'async') ^ -1) * + (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * + (fill * Cg((P('FUNC_API_REMOTE_ONLY') * Cc(true)), 'remote_only') ^ -1) * + (fill * Cg((P('FUNC_API_REMOTE_IMPL') * Cc(true)), 'remote_impl') ^ -1) * + (fill * Cg((P('FUNC_API_BRIDGE_IMPL') * Cc(true)), 'bridge_impl') ^ -1) * + fill * P(';') + ) + +local grammar = Ct((c_proto + c_comment + c_preproc + ws) ^ 1) +return {grammar=grammar, typed_container=typed_container} diff --git a/src/nvim/generators/dump_bin_array.lua b/src/nvim/generators/dump_bin_array.lua new file mode 100644 index 0000000000..bee5aba73f --- /dev/null +++ b/src/nvim/generators/dump_bin_array.lua @@ -0,0 +1,17 @@ +local function dump_bin_array(output, name, data) + output:write([[ + static const uint8_t ]]..name..[[[] = { +]]) + + for i = 1, #data do + output:write(string.byte(data, i)..', ') + if i % 10 == 0 then + output:write('\n ') + end + end + output:write([[ +}; +]]) +end + +return dump_bin_array diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua new file mode 100644 index 0000000000..b01321e713 --- /dev/null +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -0,0 +1,458 @@ +mpack = require('mpack') + +-- we need at least 4 arguments since the last two are output files +if arg[1] == '--help' then + print('Usage: genmsgpack.lua args') + print('Args: 1: source directory') + print(' 2: dispatch output file (dispatch_wrappers.generated.h)') + print(' 3: functions metadata output file (funcs_metadata.generated.h)') + print(' 4: API metadata output file (api_metadata.mpack)') + print(' 5: lua C bindings output file (msgpack_lua_c_bindings.generated.c)') + print(' rest: C files where API functions are defined') +end +assert(#arg >= 4) +functions = {} + +local nvimdir = arg[1] +package.path = nvimdir .. '/?.lua;' .. package.path + +-- names of all headers relative to the source root (for inclusion in the +-- generated file) +headers = {} + +-- output h file with generated dispatch functions +dispatch_outputf = arg[2] +-- output h file with packed metadata +funcs_metadata_outputf = arg[3] +-- output metadata mpack file, for use by other build scripts +mpack_outputf = arg[4] +lua_c_bindings_outputf = arg[5] + +-- set of function names, used to detect duplicates +function_names = {} + +c_grammar = require('generators.c_grammar') + +-- read each input file, parse and append to the api metadata +for i = 6, #arg do + local full_path = arg[i] + local parts = {} + for part in string.gmatch(full_path, '[^/]+') do + parts[#parts + 1] = part + end + headers[#headers + 1] = parts[#parts - 1]..'/'..parts[#parts] + + local input = io.open(full_path, 'rb') + + local tmp = c_grammar.grammar:match(input:read('*all')) + for i = 1, #tmp do + local fn = tmp[i] + if not fn.noexport then + functions[#functions + 1] = tmp[i] + function_names[fn.name] = true + if #fn.parameters ~= 0 and fn.parameters[1][2] == 'channel_id' then + -- this function should receive the channel id + fn.receives_channel_id = true + -- remove the parameter since it won't be passed by the api client + table.remove(fn.parameters, 1) + end + if #fn.parameters ~= 0 and fn.parameters[#fn.parameters][1] == 'error' then + -- function can fail if the last parameter type is 'Error' + fn.can_fail = true + -- remove the error parameter, msgpack has it's own special field + -- for specifying errors + fn.parameters[#fn.parameters] = nil + end + end + end + input:close() +end + +local function shallowcopy(orig) + local copy = {} + for orig_key, orig_value in pairs(orig) do + copy[orig_key] = orig_value + end + return copy +end + +local function startswith(String,Start) + return string.sub(String,1,string.len(Start))==Start +end + +-- Export functions under older deprecated names. +-- These will be removed eventually. +local deprecated_aliases = require("api.dispatch_deprecated") +for i,f in ipairs(shallowcopy(functions)) do + local ismethod = false + if startswith(f.name, "nvim_") then + if startswith(f.name, "nvim__") then + f.since = -1 + elseif f.since == nil then + print("Function "..f.name.." lacks since field.\n") + os.exit(1) + end + f.since = tonumber(f.since) + if f.deprecated_since ~= nil then + f.deprecated_since = tonumber(f.deprecated_since) + end + + if startswith(f.name, "nvim_buf_") then + ismethod = true + elseif startswith(f.name, "nvim_win_") then + ismethod = true + elseif startswith(f.name, "nvim_tabpage_") then + ismethod = true + end + else + f.remote_only = true + f.since = 0 + f.deprecated_since = 1 + end + f.method = ismethod + local newname = deprecated_aliases[f.name] + if newname ~= nil then + if function_names[newname] then + -- duplicate + print("Function "..f.name.." has deprecated alias\n" + ..newname.." which has a separate implementation.\n".. + "Please remove it from src/nvim/api/dispatch_deprecated.lua") + os.exit(1) + end + local newf = shallowcopy(f) + newf.name = newname + if newname == "ui_try_resize" then + -- The return type was incorrectly set to Object in 0.1.5. + -- Keep it that way for clients that rely on this. + newf.return_type = "Object" + end + newf.impl_name = f.name + newf.remote_only = true + newf.since = 0 + newf.deprecated_since = 1 + functions[#functions+1] = newf + end +end + +-- don't expose internal attributes like "impl_name" in public metadata +exported_attributes = {'name', 'parameters', 'return_type', 'method', + 'since', 'deprecated_since'} +exported_functions = {} +for _,f in ipairs(functions) do + if not startswith(f.name, "nvim__") then + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = f[attr] + end + exported_functions[#exported_functions+1] = f_exported + end +end + + +-- serialize the API metadata using msgpack and embed into the resulting +-- binary for easy querying by clients +funcs_metadata_output = io.open(funcs_metadata_outputf, 'wb') +packed = mpack.pack(exported_functions) +dump_bin_array = require("generators.dump_bin_array") +dump_bin_array(funcs_metadata_output, 'funcs_metadata', packed) +funcs_metadata_output:close() + +-- start building the dispatch wrapper output +output = io.open(dispatch_outputf, 'wb') + +local function real_type(type) + local rv = type + if c_grammar.typed_container:match(rv) then + if rv:match('Array') then + rv = 'Array' + else + rv = 'Dictionary' + end + end + return rv +end + +local function attr_name(rt) + if rt == 'Float' then + return 'floating' + else + return rt:lower() + end +end + +-- start the handler functions. Visit each function metadata to build the +-- handler function with code generated for validating arguments and calling to +-- the real API. +for i = 1, #functions do + local fn = functions[i] + if fn.impl_name == nil then + local args = {} + + output:write('Object handle_'..fn.name..'(uint64_t channel_id, Array args, Error *error)') + output:write('\n{') + output:write('\n Object ret = NIL;') + -- Declare/initialize variables that will hold converted arguments + for j = 1, #fn.parameters do + local param = fn.parameters[j] + local converted = 'arg_'..j + output:write('\n '..param[1]..' '..converted..';') + end + output:write('\n') + output:write('\n if (args.size != '..#fn.parameters..') {') + output:write('\n api_set_error(error, kErrorTypeException, "Wrong number of arguments: expecting '..#fn.parameters..' but got %zu", args.size);') + output:write('\n goto cleanup;') + output:write('\n }\n') + + -- Validation/conversion for each argument + for j = 1, #fn.parameters do + local converted, convert_arg, param, arg + param = fn.parameters[j] + converted = 'arg_'..j + local rt = real_type(param[1]) + if rt ~= 'Object' then + if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then + -- Buffer, Window, and Tabpage have a specific type, but are stored in integer + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..' && args.items['..(j - 1)..'].data.integer >= 0) {') + output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + else + output:write('\n if (args.items['..(j - 1)..'].type == kObjectType'..rt..') {') + output:write('\n '..converted..' = args.items['..(j - 1)..'].data.'..attr_name(rt)..';') + end + if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') or rt:match('^Boolean$') then + -- accept nonnegative integers for Booleans, Buffers, Windows and Tabpages + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeInteger && args.items['..(j - 1)..'].data.integer >= 0) {') + output:write('\n '..converted..' = (handle_T)args.items['..(j - 1)..'].data.integer;') + end + output:write('\n } else {') + output:write('\n api_set_error(error, kErrorTypeException, "Wrong type for argument '..j..', expecting '..param[1]..'");') + output:write('\n goto cleanup;') + output:write('\n }\n') + else + output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + end + + args[#args + 1] = converted + end + + -- function call + local call_args = table.concat(args, ', ') + output:write('\n ') + if fn.return_type ~= 'void' then + -- has a return value, prefix the call with a declaration + output:write(fn.return_type..' rv = ') + end + + -- write the function name and the opening parenthesis + output:write(fn.name..'(') + + if fn.receives_channel_id then + -- if the function receives the channel id, pass it as first argument + if #args > 0 or fn.can_fail then + output:write('channel_id, '..call_args) + else + output:write('channel_id') + end + else + output:write(call_args) + end + + if fn.can_fail then + -- if the function can fail, also pass a pointer to the local error object + if #args > 0 then + output:write(', error);\n') + else + output:write('error);\n') + end + -- and check for the error + output:write('\n if (ERROR_SET(error)) {') + output:write('\n goto cleanup;') + output:write('\n }\n') + else + output:write(');\n') + end + + if fn.return_type ~= 'void' then + output:write('\n ret = '..string.upper(real_type(fn.return_type))..'_OBJ(rv);') + end + output:write('\n\ncleanup:'); + + output:write('\n return ret;\n}\n\n'); + end +end + +-- Generate a function that initializes method names with handler functions +output:write([[ +void msgpack_rpc_init_method_table(void) +{ + methods = map_new(String, MsgpackRpcRequestHandler)(); + +]]) + +for i = 1, #functions do + local fn = functions[i] + output:write(' msgpack_rpc_add_method_handler('.. + '(String) {.data = "'..fn.name..'", '.. + '.size = sizeof("'..fn.name..'") - 1}, '.. + '(MsgpackRpcRequestHandler) {.fn = handle_'.. (fn.impl_name or fn.name).. + ', .async = '..tostring(fn.async)..'});\n') + +end + +output:write('\n}\n\n') +output:close() + +mpack_output = io.open(mpack_outputf, 'wb') +mpack_output:write(mpack.pack(functions)) +mpack_output:close() + +local function include_headers(output, headers) + for i = 1, #headers do + if headers[i]:sub(-12) ~= '.generated.h' then + output:write('\n#include "nvim/'..headers[i]..'"') + end + end +end + +local function write_shifted_output(output, str) + str = str:gsub('\n ', '\n') + str = str:gsub('^ ', '') + str = str:gsub(' +$', '') + output:write(str) +end + +-- start building lua output +output = io.open(lua_c_bindings_outputf, 'wb') + +output:write([[ +#include <lua.h> +#include <lualib.h> +#include <lauxlib.h> + +#include "nvim/func_attr.h" +#include "nvim/api/private/defs.h" +#include "nvim/api/private/helpers.h" +#include "nvim/lua/converter.h" +]]) +include_headers(output, headers) +output:write('\n') + +lua_c_functions = {} + +local function process_function(fn) + lua_c_function_name = ('nlua_msgpack_%s'):format(fn.name) + write_shifted_output(output, string.format([[ + + static int %s(lua_State *lstate) + { + Error err = ERROR_INIT; + if (lua_gettop(lstate) != %i) { + api_set_error(&err, kErrorTypeValidation, "Expected %i argument%s"); + goto exit_0; + } + ]], lua_c_function_name, #fn.parameters, #fn.parameters, + (#fn.parameters == 1) and '' or 's')) + lua_c_functions[#lua_c_functions + 1] = { + binding=lua_c_function_name, + api=fn.name + } + local cparams = '' + local free_code = {} + for j = #fn.parameters,1,-1 do + param = fn.parameters[j] + cparam = string.format('arg%u', j) + param_type = real_type(param[1]) + lc_param_type = param_type:lower() + write_shifted_output(output, string.format([[ + const %s %s = nlua_pop_%s(lstate, &err); + + if (ERROR_SET(&err)) { + goto exit_%u; + } + ]], param[1], cparam, param_type, #fn.parameters - j)) + free_code[#free_code + 1] = ('api_free_%s(%s);'):format( + lc_param_type, cparam) + cparams = cparam .. ', ' .. cparams + end + if fn.receives_channel_id then + cparams = 'LUA_INTERNAL_CALL, ' .. cparams + end + if fn.can_fail then + cparams = cparams .. '&err' + else + cparams = cparams:gsub(', $', '') + end + local free_at_exit_code = '' + for i = 1, #free_code do + local rev_i = #free_code - i + 1 + local code = free_code[rev_i] + if i == 1 then + free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) + else + free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format( + rev_i, code) + end + end + local err_throw_code = [[ + + exit_0: + if (ERROR_SET(&err)) { + luaL_where(lstate, 1); + lua_pushstring(lstate, err.msg); + api_clear_error(&err); + lua_concat(lstate, 2); + return lua_error(lstate); + } + ]] + if fn.return_type ~= 'void' then + if fn.return_type:match('^ArrayOf') then + return_type = 'Array' + else + return_type = fn.return_type + end + write_shifted_output(output, string.format([[ + const %s ret = %s(%s); + nlua_push_%s(lstate, ret); + api_free_%s(ret); + %s + %s + return 1; + ]], fn.return_type, fn.name, cparams, return_type, return_type:lower(), + free_at_exit_code, err_throw_code)) + else + write_shifted_output(output, string.format([[ + %s(%s); + %s + %s + return 0; + ]], fn.name, cparams, free_at_exit_code, err_throw_code)) + end + write_shifted_output(output, [[ + } + ]]) +end + +for _, fn in ipairs(functions) do + if not fn.remote_only or fn.name:sub(1, 4) == '_vim' then + process_function(fn) + end +end + +output:write(string.format([[ +void nlua_add_api_functions(lua_State *lstate) + FUNC_ATTR_NONNULL_ALL +{ + lua_createtable(lstate, 0, %u); +]], #lua_c_functions)) +for _, func in ipairs(lua_c_functions) do + output:write(string.format([[ + + lua_pushcfunction(lstate, &%s); + lua_setfield(lstate, -2, "%s");]], func.binding, func.api)) +end +output:write([[ + + lua_setfield(lstate, -2, "api"); +} +]]) + +output:close() diff --git a/src/nvim/generators/gen_api_ui_events.lua b/src/nvim/generators/gen_api_ui_events.lua new file mode 100644 index 0000000000..6f76eb5aab --- /dev/null +++ b/src/nvim/generators/gen_api_ui_events.lua @@ -0,0 +1,160 @@ +mpack = require('mpack') + +local nvimdir = arg[1] +package.path = nvimdir .. '/?.lua;' .. package.path + +assert(#arg == 7) +input = io.open(arg[2], 'rb') +proto_output = io.open(arg[3], 'wb') +call_output = io.open(arg[4], 'wb') +remote_output = io.open(arg[5], 'wb') +bridge_output = io.open(arg[6], 'wb') +metadata_output = io.open(arg[7], 'wb') + +c_grammar = require('generators.c_grammar') +local events = c_grammar.grammar:match(input:read('*all')) + +function write_signature(output, ev, prefix, notype) + output:write('('..prefix) + if prefix == "" and #ev.parameters == 0 then + output:write('void') + end + for j = 1, #ev.parameters do + if j > 1 or prefix ~= '' then + output:write(', ') + end + local param = ev.parameters[j] + if not notype then + output:write(param[1]..' ') + end + output:write(param[2]) + end + output:write(')') +end + +function write_arglist(output, ev, need_copy) + output:write(' Array args = ARRAY_DICT_INIT;\n') + for j = 1, #ev.parameters do + local param = ev.parameters[j] + local kind = string.upper(param[1]) + local do_copy = need_copy and (kind == "ARRAY" or kind == "DICTIONARY" or kind == "STRING") + output:write(' ADD(args, ') + if do_copy then + output:write('copy_object(') + end + output:write(kind..'_OBJ('..param[2]..')') + if do_copy then + output:write(')') + end + output:write(');\n') + end +end + +for i = 1, #events do + ev = events[i] + assert(ev.return_type == 'void') + + if ev.since == nil then + print("Ui event "..ev.name.." lacks since field.\n") + os.exit(1) + end + ev.since = tonumber(ev.since) + + if not ev.remote_only then + proto_output:write(' void (*'..ev.name..')') + write_signature(proto_output, ev, 'UI *ui') + proto_output:write(';\n') + + if not ev.remote_impl then + remote_output:write('static void remote_ui_'..ev.name) + write_signature(remote_output, ev, 'UI *ui') + remote_output:write('\n{\n') + write_arglist(remote_output, ev, true) + remote_output:write(' push_call(ui, "'..ev.name..'", args);\n') + remote_output:write('}\n\n') + end + + if not ev.bridge_impl then + + send, argv, recv, recv_argv, recv_cleanup = '', '', '', '', '' + argc = 1 + for j = 1, #ev.parameters do + local param = ev.parameters[j] + copy = 'copy_'..param[2] + if param[1] == 'String' then + send = send..' String copy_'..param[2]..' = copy_string('..param[2]..');\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_'..param[2]..' = copy_array('..param[2]..');\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] == '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_CALL(ui, '..ev.name..', '..argc..', ui'..argv..');\n}\n') + end + end + + call_output:write('void ui_call_'..ev.name) + write_signature(call_output, ev, '') + call_output:write('\n{\n') + if ev.remote_only then + write_arglist(call_output, ev, false) + call_output:write(' ui_event("'..ev.name..'", args);\n') + else + call_output:write(' UI_CALL') + write_signature(call_output, ev, ev.name, true) + call_output:write(";\n") + end + call_output:write("}\n\n") + +end + +proto_output:close() +call_output:close() +remote_output:close() + +-- don't expose internal attributes like "impl_name" in public metadata +exported_attributes = {'name', 'parameters', + 'since', 'deprecated_since'} +exported_events = {} +for _,ev in ipairs(events) do + local f_exported = {} + for _,attr in ipairs(exported_attributes) do + f_exported[attr] = ev[attr] + end + exported_events[#exported_events+1] = f_exported +end + +packed = mpack.pack(exported_events) +dump_bin_array = require("generators.dump_bin_array") +dump_bin_array(metadata_output, 'ui_events_metadata', packed) +metadata_output:close() diff --git a/src/nvim/generators/gen_char_blob.lua b/src/nvim/generators/gen_char_blob.lua new file mode 100644 index 0000000000..d860375e26 --- /dev/null +++ b/src/nvim/generators/gen_char_blob.lua @@ -0,0 +1,48 @@ +if arg[1] == '--help' then + print('Usage:') + print(' gencharblob.lua source target varname') + print('') + print('Generates C file with big uint8_t blob.') + print('Blob will be stored in a static const array named varname.') + os.exit() +end + +assert(#arg == 3) + +local source_file = arg[1] +local target_file = arg[2] +local varname = arg[3] + +source = io.open(source_file, 'r') +target = io.open(target_file, 'w') + +target:write('#include <stdint.h>\n\n') +target:write(('static const uint8_t %s[] = {\n'):format(varname)) + +num_bytes = 0 +MAX_NUM_BYTES = 15 -- 78 / 5: maximum number of bytes on one line +target:write(' ') + +increase_num_bytes = function() + num_bytes = num_bytes + 1 + if num_bytes == MAX_NUM_BYTES then + num_bytes = 0 + target:write('\n ') + end +end + +for line in source:lines() do + for i = 1,string.len(line) do + byte = string.byte(line, i) + assert(byte ~= 0) + target:write(string.format(' %3u,', byte)) + increase_num_bytes() + end + target:write(string.format(' %3u,', string.byte('\n', 1))) + increase_num_bytes() +end + +target:write(' 0};\n') + +source:close() +target:close() diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua new file mode 100755 index 0000000000..e999e53e4a --- /dev/null +++ b/src/nvim/generators/gen_declarations.lua @@ -0,0 +1,287 @@ +#!/usr/bin/lua + +local fname = arg[1] +local static_fname = arg[2] +local non_static_fname = arg[3] +local preproc_fname = arg[4] + + +local lpeg = require('lpeg') + +local fold = function (func, ...) + local result = nil + for i, v in ipairs({...}) do + if result == nil then + result = v + else + result = func(result, v) + end + end + return result +end + +local folder = function (func) + return function (...) + return fold(func, ...) + end +end + +local lit = lpeg.P +local set = function(...) + return lpeg.S(fold(function (a, b) return a .. b end, ...)) +end +local any_character = lpeg.P(1) +local rng = function(s, e) return lpeg.R(s .. e) end +local concat = folder(function (a, b) return a * b end) +local branch = folder(function (a, b) return a + b end) +local one_or_more = function(v) return v ^ 1 end +local two_or_more = function(v) return v ^ 2 end +local any_amount = function(v) return v ^ 0 end +local one_or_no = function(v) return v ^ -1 end +local look_behind = lpeg.B +local look_ahead = function(v) return #v end +local neg_look_ahead = function(v) return -v end +local neg_look_behind = function(v) return -look_behind(v) end + +local w = branch( + rng('a', 'z'), + rng('A', 'Z'), + lit('_') +) +local aw = branch( + w, + rng('0', '9') +) +local s = set(' ', '\n', '\t') +local raw_word = concat(w, any_amount(aw)) +local right_word = concat( + raw_word, + neg_look_ahead(aw) +) +local word = branch( + concat( + branch(lit('ArrayOf('), lit('DictionaryOf(')), -- typed container macro + one_or_more(any_character - lit(')')), + lit(')') + ), + concat( + neg_look_behind(aw), + right_word + ) +) +local inline_comment = concat( + lit('/*'), + any_amount(concat( + neg_look_ahead(lit('*/')), + any_character + )), + lit('*/') +) +local spaces = any_amount(branch( + s, + -- Comments are really handled by preprocessor, so the following is not needed + inline_comment, + concat( + lit('//'), + any_amount(concat( + neg_look_ahead(lit('\n')), + any_character + )), + lit('\n') + ), + -- Linemarker inserted by preprocessor + concat( + lit('# '), + any_amount(concat( + neg_look_ahead(lit('\n')), + any_character + )), + lit('\n') + ) +)) +local typ_part = concat( + word, + any_amount(concat( + spaces, + lit('*') + )), + spaces +) +local typ = one_or_more(typ_part) +local typ_id = two_or_more(typ_part) +local arg = typ_id -- argument name is swallowed by typ +local pattern = concat( + any_amount(branch(set(' ', '\t'), inline_comment)), + typ_id, -- return type with function name + spaces, + lit('('), + spaces, + one_or_no(branch( -- function arguments + concat( + arg, -- first argument, does not require comma + any_amount(concat( -- following arguments, start with a comma + spaces, + lit(','), + spaces, + arg, + any_amount(concat( + lit('['), + spaces, + any_amount(aw), + spaces, + lit(']') + )) + )), + one_or_no(concat( + spaces, + lit(','), + spaces, + lit('...') + )) + ), + lit('void') -- also accepts just void + )), + spaces, + lit(')'), + any_amount(concat( -- optional attributes + spaces, + lit('FUNC_'), + any_amount(aw), + one_or_no(concat( -- attribute argument + spaces, + lit('('), + any_amount(concat( + neg_look_ahead(lit(')')), + any_character + )), + lit(')') + )) + )), + look_ahead(concat( -- definition must be followed by "{" + spaces, + lit('{') + )) +) + +if fname == '--help' then + print'Usage:' + print() + print' gendeclarations.lua definitions.c static.h non-static.h preprocessor.i' + os.exit() +end + +local preproc_f = io.open(preproc_fname) +local text = preproc_f:read("*all") +preproc_f:close() + + +local header = [[ +#ifndef DEFINE_FUNC_ATTRIBUTES +# define DEFINE_FUNC_ATTRIBUTES +#endif +#include "nvim/func_attr.h" +#undef DEFINE_FUNC_ATTRIBUTES +]] + +local footer = [[ +#include "nvim/func_attr.h" +]] + +local non_static = header +local static = header + +local filepattern = '^#%a* (%d+) "([^"]-)/?([^"/]+)"' +local curfile + +local init = 0 +local curfile = nil +local neededfile = fname:match('[^/]+$') +local declline = 0 +local declendpos = 0 +local curdir = nil +local is_needed_file = false +while init ~= nil do + init = text:find('[\n;}]', init) + if init == nil then + break + end + local init_is_nl = text:sub(init, init) == '\n' + init = init + 1 + if init_is_nl and is_needed_file then + declline = declline + 1 + end + if init_is_nl and text:sub(init, init) == '#' then + local line, dir, file = text:match(filepattern, init) + if file ~= nil then + curfile = file + is_needed_file = (curfile == neededfile) + declline = tonumber(line) - 1 + local curdir_start = dir:find('src/nvim/') + if curdir_start ~= nil then + curdir = dir:sub(curdir_start + #('src/nvim/')) + else + curdir = dir + end + else + declline = declline - 1 + end + elseif init < declendpos then + -- Skipping over declaration + elseif is_needed_file then + s = init + e = pattern:match(text, init) + if e ~= nil then + local declaration = text:sub(s, e - 1) + -- Comments are really handled by preprocessor, so the following is not + -- needed + declaration = declaration:gsub('/%*.-%*/', '') + declaration = declaration:gsub('//.-\n', '\n') + + declaration = declaration:gsub('# .-\n', '') + + declaration = declaration:gsub('\n', ' ') + declaration = declaration:gsub('%s+', ' ') + declaration = declaration:gsub(' ?%( ?', '(') + -- declaration = declaration:gsub(' ?%) ?', ')') + declaration = declaration:gsub(' ?, ?', ', ') + declaration = declaration:gsub(' ?(%*+) ?', ' %1') + declaration = declaration:gsub(' ?(FUNC_ATTR_)', ' %1') + declaration = declaration:gsub(' $', '') + declaration = declaration:gsub('^ ', '') + declaration = declaration .. ';' + declaration = declaration .. (' // %s/%s:%u'):format( + curdir, curfile, declline) + declaration = declaration .. '\n' + if declaration:sub(1, 6) == 'static' then + static = static .. declaration + else + non_static = non_static .. declaration + end + declendpos = e + end + end +end + +non_static = non_static .. footer +static = static .. footer + +local F +F = io.open(static_fname, 'w') +F:write(static) +F:close() + +-- Before generating the non-static headers, check if the current file(if +-- exists) is different from the new one. If they are the same, we won't touch +-- the current version to avoid triggering an unnecessary rebuilds of modules +-- that depend on this one +F = io.open(non_static_fname, 'r') +if F ~= nil then + if F:read('*a') == non_static then + os.exit(0) + end + io.close(F) +end + +F = io.open(non_static_fname, 'w') +F:write(non_static) +F:close() diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua new file mode 100644 index 0000000000..23435a1d0b --- /dev/null +++ b/src/nvim/generators/gen_eval.lua @@ -0,0 +1,67 @@ +mpack = require('mpack') + +local nvimsrcdir = arg[1] +local autodir = arg[2] +local metadata_file = arg[3] +local funcs_file = arg[4] + +if nvimsrcdir == '--help' then + print([[ +Usage: + lua geneval.lua src/nvim build/src/nvim/auto + +Will generate build/src/nvim/auto/funcs.generated.h with definition of functions +static const array. +]]) + os.exit(0) +end + +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +local funcsfname = autodir .. '/funcs.generated.h' + +local gperfpipe = io.open(funcsfname .. '.gperf', 'wb') + +local funcs = require('eval').funcs +local metadata = mpack.unpack(io.open(arg[3], 'rb'):read("*all")) +for i,fun in ipairs(metadata) do + if not fun.remote_only then + funcs[fun.name] = { + args=#fun.parameters, + func='api_wrapper', + data='&handle_'..fun.name, + } + end +end + +local funcsdata = io.open(funcs_file, 'w') +funcsdata:write(mpack.pack(funcs)) +funcsdata:close() + +gperfpipe:write([[ +%language=ANSI-C +%global-table +%readonly-tables +%define initializer-suffix ,0,0,NULL,NULL +%define word-array-name functions +%define hash-function-name hash_internal_func_gperf +%define lookup-function-name find_internal_func_gperf +%omit-struct-type +%struct-type +VimLFuncDef; +%% +]]) + +for name, def in pairs(funcs) do + args = def.args or 0 + if type(args) == 'number' then + args = {args, args} + elseif #args == 1 then + args[2] = 'MAX_FUNC_ARGS' + end + func = def.func or ('f_' .. name) + data = def.data or "NULL" + gperfpipe:write(('%s, %s, %s, &%s, (FunPtr)%s\n') + :format(name, args[1], args[2], func, data)) +end +gperfpipe:close() diff --git a/src/nvim/generators/gen_events.lua b/src/nvim/generators/gen_events.lua new file mode 100644 index 0000000000..75e0b3da3a --- /dev/null +++ b/src/nvim/generators/gen_events.lua @@ -0,0 +1,65 @@ +if arg[1] == '--help' then + print('Usage: gen_events.lua src/nvim enum_file event_names_file') + os.exit(0) +end + +local nvimsrcdir = arg[1] +local fileio_enum_file = arg[2] +local names_file = arg[3] + +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +local auevents = require('auevents') +local events = auevents.events +local aliases = auevents.aliases + +enum_tgt = io.open(fileio_enum_file, 'w') +names_tgt = io.open(names_file, 'w') + +enum_tgt:write('typedef enum auto_event {') +names_tgt:write([[ +static const struct event_name { + size_t len; + char *name; + event_T event; +} event_names[] = {]]) + +for i, event in ipairs(events) do + if i > 1 then + comma = ',\n' + else + comma = '\n' + end + enum_tgt:write(('%s EVENT_%s = %u'):format(comma, event:upper(), i - 1)) + names_tgt:write(('%s {%u, "%s", EVENT_%s}'):format(comma, #event, event, event:upper())) +end + +for alias, event in pairs(aliases) do + names_tgt:write((',\n {%u, "%s", EVENT_%s}'):format(#alias, alias, event:upper())) +end + +names_tgt:write(',\n {0, NULL, (event_T)0}') + +enum_tgt:write('\n} event_T;\n') +names_tgt:write('\n};\n') + +enum_tgt:write(('\n#define NUM_EVENTS %u\n'):format(#events)) +names_tgt:write('\nstatic AutoPat *first_autopat[NUM_EVENTS] = {\n ') +line_len = 1 +for i = 1,((#events) - 1) do + line_len = line_len + #(' NULL,') + if line_len > 80 then + names_tgt:write('\n ') + line_len = 1 + #(' NULL,') + end + names_tgt:write(' NULL,') +end +if line_len + #(' NULL') > 80 then + names_tgt:write('\n NULL') +else + names_tgt:write(' NULL') +end +names_tgt:write('\n};\n') + +enum_tgt:close() +names_tgt:close() diff --git a/src/nvim/generators/gen_ex_cmds.lua b/src/nvim/generators/gen_ex_cmds.lua new file mode 100644 index 0000000000..cb566d46ca --- /dev/null +++ b/src/nvim/generators/gen_ex_cmds.lua @@ -0,0 +1,88 @@ +local nvimsrcdir = arg[1] +local includedir = arg[2] +local autodir = arg[3] + +if nvimsrcdir == '--help' then + print ([[ +Usage: + lua genex_cmds.lua src/nvim build/include build/src/nvim/auto + +Will generate files build/include/ex_cmds_enum.generated.h with cmdidx_T +enum and build/src/nvim/auto/ex_cmds_defs.generated.h with main Ex commands +definitions. +]]) + os.exit(0) +end + +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +local enumfname = includedir .. '/ex_cmds_enum.generated.h' +local defsfname = autodir .. '/ex_cmds_defs.generated.h' + +local enumfile = io.open(enumfname, 'w') +local defsfile = io.open(defsfname, 'w') + +local defs = require('ex_cmds') +local lastchar = nil + +local i +local cmd +local first = true +local prevfirstchar = nil + +local byte_a = string.byte('a') +local byte_z = string.byte('z') + +local cmdidxs = string.format([[ +static const cmdidx_T cmdidxs[%u] = { +]], byte_z - byte_a + 2) + +enumfile:write([[ +typedef enum CMD_index { +]]) +defsfile:write(string.format([[ +static CommandDefinition cmdnames[%u] = { +]], #defs)) +for i, cmd in ipairs(defs) do + local enumname = cmd.enum or ('CMD_' .. cmd.command) + firstchar = string.byte(cmd.command) + if firstchar ~= prevfirstchar then + if (not prevfirstchar + or (byte_a <= firstchar and firstchar <= byte_z) + or (byte_a <= prevfirstchar and prevfirstchar <= byte_z)) then + if not first then + cmdidxs = cmdidxs .. ',\n' + end + cmdidxs = cmdidxs .. ' ' .. enumname + end + prevfirstchar = firstchar + end + if first then + first = false + else + defsfile:write(',\n') + end + enumfile:write(' ' .. enumname .. ',\n') + defsfile:write(string.format([[ + [%s] = { + .cmd_name = (char_u *) "%s", + .cmd_func = (ex_func_T)&%s, + .cmd_argt = %uL, + .cmd_addr_type = %i + }]], enumname, cmd.command, cmd.func, cmd.flags, cmd.addr_type)) +end +defsfile:write([[ + +}; +]]) +enumfile:write([[ + CMD_SIZE, + CMD_USER = -1, + CMD_USER_BUF = -2 +} cmdidx_T; +]]) +cmdidxs = cmdidxs .. [[ + +}; +]] +defsfile:write(cmdidxs) diff --git a/src/nvim/generators/gen_options.lua b/src/nvim/generators/gen_options.lua new file mode 100644 index 0000000000..9d7f235a3b --- /dev/null +++ b/src/nvim/generators/gen_options.lua @@ -0,0 +1,190 @@ +if arg[1] == '--help' then + print('Usage: genoptions.lua src/nvim options_file') + os.exit(0) +end + +local nvimsrcdir = arg[1] +local options_file = arg[2] + +package.path = nvimsrcdir .. '/?.lua;' .. package.path + +local opt_fd = io.open(options_file, 'w') + +local w = function(s) + if s:match('^ %.') then + opt_fd:write(s .. ',\n') + else + opt_fd:write(s .. '\n') + end +end + +local options = require('options') + +cstr = options.cstr + +local type_flags={ + bool='P_BOOL', + number='P_NUM', + string='P_STRING', +} + +local redraw_flags={ + statuslines='P_RSTAT', + current_window='P_RWIN', + current_window_only='P_RWINONLY', + current_buffer='P_RBUF', + all_windows='P_RALL', + everything='P_RCLR', + curswant='P_CURSWANT', +} + +local list_flags={ + comma='P_COMMA', + onecomma='P_ONECOMMA', + flags='P_FLAGLIST', + flagscomma='P_COMMA|P_FLAGLIST', +} + +local get_flags = function(o) + local ret = {type_flags[o.type]} + local add_flag = function(f) + ret[1] = ret[1] .. '|' .. f + end + if o.list then + add_flag(list_flags[o.list]) + end + if o.redraw then + for _, r_flag in ipairs(o.redraw) do + add_flag(redraw_flags[r_flag]) + end + end + if o.expand then + add_flag('P_EXPAND') + if o.expand == 'nodefault' then + add_flag('P_NO_DEF_EXP') + end + end + for _, flag_desc in ipairs({ + {'alloced'}, + {'nodefault'}, + {'no_mkrc'}, + {'vi_def'}, + {'vim'}, + {'secure'}, + {'gettext'}, + {'noglob'}, + {'normal_fname_chars', 'P_NFNAME'}, + {'pri_mkrc'}, + {'deny_in_modelines', 'P_NO_ML'}, + {'deny_duplicates', 'P_NODUP'}, + }) do + local key_name = flag_desc[1] + local def_name = flag_desc[2] or ('P_' .. key_name:upper()) + if o[key_name] then + add_flag(def_name) + end + end + return ret[1] +end + +local get_cond +get_cond = function(c, base_string) + local cond_string = base_string or '#if ' + if type(c) == 'table' then + cond_string = cond_string .. get_cond(c[1], '') + for i, subc in ipairs(c) do + if i > 1 then + cond_string = cond_string .. ' && ' .. get_cond(subc, '') + end + end + elseif c:sub(1, 1) == '!' then + cond_string = cond_string .. '!defined(' .. c:sub(2) .. ')' + else + cond_string = cond_string .. 'defined(' .. c .. ')' + end + return cond_string +end + +value_dumpers = { + ['function']=function(v) return v() end, + string=cstr, + boolean=function(v) return v and 'true' or 'false' end, + number=function(v) return ('%iL'):format(v) end, + ['nil']=function(v) return '0L' end, +} + +local get_value = function(v) + return '(char_u *) ' .. value_dumpers[type(v)](v) +end + +local get_defaults = function(d) + return '{' .. get_value(d.vi) .. ', ' .. get_value(d.vim) .. '}' +end + +local defines = {} + +local dump_option = function(i, o) + w(' [' .. ('%u'):format(i - 1) .. ']={') + w(' .fullname=' .. cstr(o.full_name)) + if o.abbreviation then + w(' .shortname=' .. cstr(o.abbreviation)) + end + w(' .flags=' .. get_flags(o)) + if o.enable_if then + w(get_cond(o.enable_if)) + end + if o.varname then + w(' .var=(char_u *)&' .. o.varname) + elseif #o.scope == 1 and o.scope[1] == 'window' then + w(' .var=VAR_WIN') + end + if o.enable_if then + w('#endif') + end + if #o.scope == 1 and o.scope[1] == 'global' then + w(' .indir=PV_NONE') + else + assert (#o.scope == 1 or #o.scope == 2) + assert (#o.scope == 1 or o.scope[1] == 'global') + local min_scope = o.scope[#o.scope] + local varname = o.pv_name or o.varname or ( + 'p_' .. (o.abbreviation or o.full_name)) + local pv_name = ( + 'OPT_' .. min_scope:sub(1, 3):upper() .. '(' .. ( + min_scope:sub(1, 1):upper() .. 'V_' .. varname:sub(3):upper() + ) .. ')' + ) + if #o.scope == 2 then + pv_name = 'OPT_BOTH(' .. pv_name .. ')' + end + defines['PV_' .. varname:sub(3):upper()] = pv_name + w(' .indir=' .. pv_name) + end + if o.defaults then + if o.defaults.condition then + w(get_cond(o.defaults.condition)) + end + w(' .def_val=' .. get_defaults(o.defaults.if_true)) + if o.defaults.condition then + if o.defaults.if_false then + w('#else') + w(' .def_val=' .. get_defaults(o.defaults.if_false)) + end + w('#endif') + end + end + w(' },') +end + +w('static vimoption_T options[] = {') +for i, o in ipairs(options.options) do + dump_option(i, o) +end +w(' [' .. ('%u'):format(#options.options) .. ']={.fullname=NULL}') +w('};') +w('') + +for k, v in pairs(defines) do + w('#define ' .. k .. ' ' .. v) +end +opt_fd:close() diff --git a/src/nvim/generators/gen_unicode_tables.lua b/src/nvim/generators/gen_unicode_tables.lua new file mode 100644 index 0000000000..66430ba26e --- /dev/null +++ b/src/nvim/generators/gen_unicode_tables.lua @@ -0,0 +1,327 @@ +-- Script creates the following tables in unicode_tables.generated.h: +-- +-- 1. doublewidth and ambiguous tables: sorted list of non-overlapping closed +-- intervals. Codepoints in these intervals have double (W or F) or ambiguous +-- (A) east asian width respectively. +-- 2. combining table: same as the above, but characters inside are combining +-- characters (i.e. have general categories equal to Mn, Mc or Me). +-- 3. foldCase, toLower and toUpper tables used to convert characters to +-- folded/lower/upper variants. In these tables first two values are +-- character ranges: like in previous tables they are sorted and must be +-- non-overlapping. Third value means step inside the range: e.g. if it is +-- 2 then interval applies only to first, third, fifth, … character in range. +-- Fourth value is number that should be added to the codepoint to yield +-- folded/lower/upper codepoint. +-- 4. emoji_width and emoji_all tables: sorted lists of non-overlapping closed +-- intervals of Emoji characters. emoji_width contains all the characters +-- which don't have ambiguous or double width, and emoji_all has all Emojis. +if arg[1] == '--help' then + print('Usage:') + print(' genunicodetables.lua unicode/ unicode_tables.generated.h') + os.exit(0) +end + +local basedir = arg[1] +local pathsep = package.config:sub(1, 1) +local get_path = function(fname) + return basedir .. pathsep .. fname +end + +local unicodedata_fname = get_path('UnicodeData.txt') +local casefolding_fname = get_path('CaseFolding.txt') +local eastasianwidth_fname = get_path('EastAsianWidth.txt') +local emoji_fname = get_path('emoji-data.txt') + +local utf_tables_fname = arg[2] + +local split_on_semicolons = function(s) + local ret = {} + local idx = 1 + while idx <= #s + 1 do + item = s:match('^[^;]*', idx) + idx = idx + #item + 1 + if idx <= #s + 1 then + assert(s:sub(idx - 1, idx - 1) == ';') + end + item = item:gsub('^%s*', '') + item = item:gsub('%s*$', '') + table.insert(ret, item) + end + return ret +end + +local fp_lines_to_lists = function(fp, n, has_comments) + local ret = {} + local line + local i = 0 + while true do + i = i + 1 + line = fp:read('*l') + if not line then + break + end + if (not has_comments + or (line:sub(1, 1) ~= '#' and not line:match('^%s*$'))) then + local l = split_on_semicolons(line) + if #l ~= n then + io.stderr:write(('Found %s items in line %u, expected %u\n'):format( + #l, i, n)) + io.stderr:write('Line: ' .. line .. '\n') + return nil + end + table.insert(ret, l) + end + end + return ret +end + +local parse_data_to_props = function(ud_fp) + return fp_lines_to_lists(ud_fp, 15, false) +end + +local parse_fold_props = function(cf_fp) + return fp_lines_to_lists(cf_fp, 4, true) +end + +local parse_width_props = function(eaw_fp) + return fp_lines_to_lists(eaw_fp, 2, true) +end + +local parse_emoji_props = function(emoji_fp) + return fp_lines_to_lists(emoji_fp, 2, true) +end + +local make_range = function(start, end_, step, add) + if step and add then + return (' {0x%x, 0x%x, %d, %d},\n'):format( + start, end_, step == 0 and -1 or step, add) + else + return (' {0x%04x, 0x%04x},\n'):format(start, end_) + end +end + +local build_convert_table = function(ut_fp, props, cond_func, nl_index, + table_name) + ut_fp:write('static const convertStruct ' .. table_name .. '[] = {\n') + local start = -1 + local end_ = -1 + local step = 0 + local add = -1 + for _, p in ipairs(props) do + if cond_func(p) then + local n = tonumber(p[1], 16) + local nl = tonumber(p[nl_index], 16) + if start >= 0 and add == (nl - n) and (step == 0 or n - end_ == step) then + -- Continue with the same range. + step = n - end_ + end_ = n + else + if start >= 0 then + -- Produce previous range. + ut_fp:write(make_range(start, end_, step, add)) + end + start = n + end_ = n + step = 0 + add = nl - n + end + end + end + if start >= 0 then + ut_fp:write(make_range(start, end_, step, add)) + end + ut_fp:write('};\n') +end + +local build_case_table = function(ut_fp, dataprops, table_name, index) + local cond_func = function(p) + return p[index] ~= '' + end + return build_convert_table(ut_fp, dataprops, cond_func, index, + 'to' .. table_name) +end + +local build_fold_table = function(ut_fp, foldprops) + local cond_func = function(p) + return (p[2] == 'C' or p[2] == 'S') + end + return build_convert_table(ut_fp, foldprops, cond_func, 3, 'foldCase') +end + +local build_combining_table = function(ut_fp, dataprops) + ut_fp:write('static const struct interval combining[] = {\n') + local start = -1 + local end_ = -1 + for _, p in ipairs(dataprops) do + if (({Mn=true, Mc=true, Me=true})[p[3]]) then + local n = tonumber(p[1], 16) + if start >= 0 and end_ + 1 == n then + -- Continue with the same range. + end_ = n + else + if start >= 0 then + -- Produce previous range. + ut_fp:write(make_range(start, end_)) + end + start = n + end_ = n + end + end + end + if start >= 0 then + ut_fp:write(make_range(start, end_)) + end + ut_fp:write('};\n') +end + +local build_width_table = function(ut_fp, dataprops, widthprops, widths, + table_name) + ut_fp:write('static const struct interval ' .. table_name .. '[] = {\n') + local start = -1 + local end_ = -1 + local dataidx = 1 + local ret = {} + for _, p in ipairs(widthprops) do + if widths[p[2]:sub(1, 1)] then + local rng_start, rng_end = p[1]:find('%.%.') + local n, n_last + if rng_start then + -- It is a range. We don’t check for composing char then. + n = tonumber(p[1]:sub(1, rng_start - 1), 16) + n_last = tonumber(p[1]:sub(rng_end + 1), 16) + else + n = tonumber(p[1], 16) + n_last = n + end + local dn + while true do + dn = tonumber(dataprops[dataidx][1], 16) + if dn >= n then + break + end + dataidx = dataidx + 1 + end + if dn ~= n and n_last == n then + io.stderr:write('Cannot find character ' .. n .. ' in data table.\n') + end + -- Only use the char when it’s not a composing char. + -- But use all chars from a range. + local dp = dataprops[dataidx] + if (n_last > n) or (not (({Mn=true, Mc=true, Me=true})[dp[3]])) then + if start >= 0 and end_ + 1 == n then + -- Continue with the same range. + else + if start >= 0 then + ut_fp:write(make_range(start, end_)) + table.insert(ret, {start, end_}) + end + start = n + end + end_ = n_last + end + end + end + if start >= 0 then + ut_fp:write(make_range(start, end_)) + table.insert(ret, {start, end_}) + end + ut_fp:write('};\n') + return ret +end + +local build_emoji_table = function(ut_fp, emojiprops, doublewidth, ambiwidth) + local emojiwidth = {} + local emoji = {} + for _, p in ipairs(emojiprops) do + if p[2]:match('Emoji%s+#') then + local rng_start, rng_end = p[1]:find('%.%.') + if rng_start then + n = tonumber(p[1]:sub(1, rng_start - 1), 16) + n_last = tonumber(p[1]:sub(rng_end + 1), 16) + else + n = tonumber(p[1], 16) + n_last = n + end + if #emoji > 0 and n - 1 == emoji[#emoji][2] then + emoji[#emoji][2] = n_last + else + table.insert(emoji, { n, n_last }) + end + + -- Characters below 1F000 may be considered single width traditionally, + -- making them double width causes problems. + if n >= 0x1f000 then + -- exclude characters that are in the ambiguous/doublewidth table + for _, ambi in ipairs(ambiwidth) do + if n >= ambi[1] and n <= ambi[2] then + n = ambi[2] + 1 + end + if n_last >= ambi[1] and n_last <= ambi[2] then + n_last = ambi[1] - 1 + end + end + for _, double in ipairs(doublewidth) do + if n >= double[1] and n <= double[2] then + n = double[2] + 1 + end + if n_last >= double[1] and n_last <= double[2] then + n_last = double[1] - 1 + end + end + + if n <= n_last then + if #emojiwidth > 0 and n - 1 == emojiwidth[#emojiwidth][2] then + emojiwidth[#emojiwidth][2] = n_last + else + table.insert(emojiwidth, { n, n_last }) + end + end + end + end + end + + ut_fp:write('static const struct interval emoji_all[] = {\n') + for _, p in ipairs(emoji) do + ut_fp:write(make_range(p[1], p[2])) + end + ut_fp:write('};\n') + + ut_fp:write('static const struct interval emoji_width[] = {\n') + for _, p in ipairs(emojiwidth) do + ut_fp:write(make_range(p[1], p[2])) + end + ut_fp:write('};\n') +end + +local ud_fp = io.open(unicodedata_fname, 'r') +local dataprops = parse_data_to_props(ud_fp) +ud_fp:close() + +local ut_fp = io.open(utf_tables_fname, 'w') + +build_case_table(ut_fp, dataprops, 'Lower', 14) +build_case_table(ut_fp, dataprops, 'Upper', 13) +build_combining_table(ut_fp, dataprops) + +local cf_fp = io.open(casefolding_fname, 'r') +local foldprops = parse_fold_props(cf_fp) +cf_fp:close() + +build_fold_table(ut_fp, foldprops) + +local eaw_fp = io.open(eastasianwidth_fname, 'r') +local widthprops = parse_width_props(eaw_fp) +eaw_fp:close() + +local doublewidth = build_width_table(ut_fp, dataprops, widthprops, + {W=true, F=true}, 'doublewidth') +local ambiwidth = build_width_table(ut_fp, dataprops, widthprops, + {A=true}, 'ambiguous') + +local emoji_fp = io.open(emoji_fname, 'r') +local emojiprops = parse_emoji_props(emoji_fp) +emoji_fp:close() + +build_emoji_table(ut_fp, emojiprops, doublewidth, ambiwidth) + +ut_fp:close() diff --git a/src/nvim/main.c b/src/nvim/main.c index 1095d4d3b5..40b553e93c 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1303,8 +1303,8 @@ static void handle_quickfix(mparm_T *paramp) set_string_option_direct((char_u *)"ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef); - if (qf_init(NULL, p_ef, p_efm, TRUE, IObuff) < 0) { - ui_putc('\n'); + if (qf_init(NULL, p_ef, p_efm, true, IObuff) < 0) { + ui_linefeed(); mch_exit(3); } TIME_MSG("reading errorfile"); diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 4e621b49c1..7e9e9e9e5c 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -247,7 +247,7 @@ ex_menu ( xfree(map_buf); } - ui_update_menu(); + ui_call_update_menu(); theend: ; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6de74fddf2..9630656f3f 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -2536,9 +2536,9 @@ void vim_beep(unsigned val) if (emsg_silent == 0) { if (!((bo_flags & val) || (bo_flags & BO_ALL))) { if (p_vb) { - ui_visual_bell(); + ui_call_visual_bell(); } else { - ui_putc(BELL); + ui_call_bell(); } } @@ -2691,7 +2691,7 @@ int call_shell(char_u *cmd, ShellOpts opts, char_u *extra_shell_arg) if (p_verbose > 3) { verbose_enter(); smsg(_("Calling shell to execute: \"%s\""), cmd == NULL ? p_sh : cmd); - ui_putc('\n'); + ui_linefeed(); verbose_leave(); } diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index b1c146d406..d908a022f1 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -479,9 +479,9 @@ void setmouse(void) checkfor = MOUSE_NORMAL; /* assume normal mode */ if (mouse_has(checkfor)) { - ui_mouse_on(); + ui_call_mouse_on(); } else { - ui_mouse_off(); + ui_call_mouse_off(); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index ad09b936e0..9b4cd0924b 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1952,15 +1952,7 @@ did_set_title ( { if (starting != NO_SCREEN) { maketitle(); - if (icon) { - if (!p_icon) { - ui_set_icon(NULL); - } - } else { - if (!p_title) { - ui_set_title(NULL); - } - } + resettitle(); } } @@ -3282,7 +3274,7 @@ did_set_string_option ( if (varp == &p_mouse) { if (*p_mouse == NUL) { - ui_mouse_off(); + ui_call_mouse_off(); } else { setmouse(); // in case 'mouse' changed } diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index fc8ab7dc8f..1a40309655 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -464,9 +464,10 @@ static void out_data_append_to_screen(char *output, size_t remaining, continue; } - // Translate NUL to SOH - if (output[off] == NUL) { - output[off] = 1; + // TODO(bfredl): using msg_puts would be better until + // terminal emulation is implemented. + if (output[off] < 0x20) { + output[off] = ' '; } off++; diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index b8650d8c62..9f3abfcb89 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -98,7 +98,6 @@ redo: } if (pum_external) { - Array args = ARRAY_DICT_INIT; if (array_changed) { Array arr = ARRAY_DICT_INIT; for (i = 0; i < size; i++) { @@ -109,14 +108,9 @@ redo: ADD(item, STRING_OBJ(cstr_to_string((char *)array[i].pum_info))); ADD(arr, ARRAY_OBJ(item)); } - ADD(args, ARRAY_OBJ(arr)); - ADD(args, INTEGER_OBJ(selected)); - ADD(args, INTEGER_OBJ(row)); - ADD(args, INTEGER_OBJ(col)); - ui_event("popupmenu_show", args); + ui_call_popupmenu_show(arr, selected, row, col); } else { - ADD(args, INTEGER_OBJ(selected)); - ui_event("popupmenu_select", args); + ui_call_popupmenu_select(selected); } return; } @@ -713,8 +707,7 @@ void pum_undisplay(void) pum_array = NULL; if (pum_external) { - Array args = ARRAY_DICT_INIT; - ui_event("popupmenu_hide", args); + ui_call_popupmenu_hide(); } else { redraw_all_later(SOME_VALID); redraw_tabline = true; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 238b3f07c8..4f4363d121 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -5956,7 +5956,7 @@ void screen_fill(int start_row, int end_row, int start_col, int end_col, int c1, col = off - LineOffset[row]; screen_stop_highlight(); ui_cursor_goto(row, col); // clear rest of this screen line - ui_eol_clear(); + ui_call_eol_clear(); col = end_col - col; while (col--) { /* clear chars in ScreenLines */ ScreenLines[off] = ' '; @@ -6345,10 +6345,10 @@ static void screenclear2(void) LineWraps[i] = FALSE; } - ui_clear(); // clear the display - clear_cmdline = FALSE; - mode_displayed = FALSE; - screen_cleared = TRUE; /* can use contents of ScreenLines now */ + ui_call_clear(); // clear the display + clear_cmdline = false; + mode_displayed = false; + screen_cleared = true; // can use contents of ScreenLines now win_rest_invalid(firstwin); redraw_cmdline = TRUE; @@ -6647,7 +6647,7 @@ int screen_ins_lines ( } } - ui_append_lines(line_count); + ui_call_scroll(-line_count); return OK; } @@ -6702,7 +6702,7 @@ int screen_del_lines ( } } - ui_delete_lines(line_count); + ui_call_scroll(line_count); return OK; } @@ -7087,8 +7087,6 @@ static void draw_tabline(void) void ui_ext_tabline_update(void) { - Array args = ARRAY_DICT_INIT; - ADD(args, TABPAGE_OBJ(curtab->handle)); Array tabs = ARRAY_DICT_INIT; FOR_ALL_TABS(tp) { Dictionary tab_info = ARRAY_DICT_INIT; @@ -7100,9 +7098,7 @@ void ui_ext_tabline_update(void) ADD(tabs, DICTIONARY_OBJ(tab_info)); } - ADD(args, ARRAY_OBJ(tabs)); - - ui_event("tabline_update", args); + ui_call_tabline_update(curtab->handle, tabs); } /* diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index cc1de583d3..b8b7085c5e 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -683,7 +683,7 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) static int term_bell(void *data) { - ui_putc('\x07'); + ui_call_bell(); return 1; } diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 399d6c74ca..4a81b32199 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -187,7 +187,7 @@ static void terminfo_stop(UI *ui) { TUIData *data = ui->data; // Destroy output stuff - tui_mode_change(ui, SHAPE_IDX_N); + tui_mode_change(ui, (String)STRING_INIT, SHAPE_IDX_N); tui_mouse_off(ui); unibi_out(ui, unibi_exit_attribute_mode); // cursor should be set to normal before exiting alternate screen @@ -417,14 +417,14 @@ static void clear_region(UI *ui, int top, int bot, int left, int right) unibi_goto(ui, grid->row, grid->col); } -static void tui_resize(UI *ui, int width, int height) +static void tui_resize(UI *ui, Integer width, Integer height) { TUIData *data = ui->data; - ugrid_resize(&data->grid, width, height); + ugrid_resize(&data->grid, (int)width, (int)height); if (!got_winch) { // Try to resize the terminal window. char r[16]; // enough for 9999x9999 - snprintf(r, sizeof(r), "\x1b[8;%d;%dt", height, width); + snprintf(r, sizeof(r), "\x1b[8;%d;%dt", (int)height, (int)width); out(ui, r, strlen(r)); } else { // Already handled the SIGWINCH signal; avoid double-resize. got_winch = false; @@ -447,11 +447,11 @@ static void tui_eol_clear(UI *ui) clear_region(ui, grid->row, grid->row, grid->col, grid->right); } -static void tui_cursor_goto(UI *ui, int row, int col) +static void tui_cursor_goto(UI *ui, Integer row, Integer col) { TUIData *data = ui->data; - ugrid_goto(&data->grid, row, col); - unibi_goto(ui, row, col); + ugrid_goto(&data->grid, (int)row, (int)col); + unibi_goto(ui, (int)row, (int)col); } CursorShape tui_cursor_decode_shape(const char *shape_str) @@ -599,30 +599,31 @@ static void tui_set_mode(UI *ui, ModeShape mode) } /// @param mode editor mode -static void tui_mode_change(UI *ui, int mode_idx) +static void tui_mode_change(UI *ui, String mode, Integer mode_idx) { TUIData *data = ui->data; tui_set_mode(ui, (ModeShape)mode_idx); data->showing_mode = (ModeShape)mode_idx; } -static void tui_set_scroll_region(UI *ui, int top, int bot, int left, - int right) +static void tui_set_scroll_region(UI *ui, Integer top, Integer bot, + Integer left, Integer right) { TUIData *data = ui->data; - ugrid_set_scroll_region(&data->grid, top, bot, left, right); + ugrid_set_scroll_region(&data->grid, (int)top, (int)bot, + (int)left, (int)right); data->can_use_terminal_scroll = left == 0 && right == ui->width - 1 && ((top == 0 && bot == ui->height - 1) || unibi_get_str(data->ut, unibi_change_scroll_region)); } -static void tui_scroll(UI *ui, int count) +static void tui_scroll(UI *ui, Integer count) { TUIData *data = ui->data; UGrid *grid = &data->grid; int clear_top, clear_bot; - ugrid_scroll(grid, count, &clear_top, &clear_bot); + ugrid_scroll(grid, (int)count, &clear_top, &clear_bot); if (data->can_use_terminal_scroll) { // Change terminal scroll region and move cursor to the top @@ -642,7 +643,7 @@ static void tui_scroll(UI *ui, int count) if (count == 1) { unibi_out(ui, unibi_delete_line); } else { - data->params[0].i = count; + data->params[0].i = (int)count; unibi_out(ui, unibi_parm_delete_line); } } @@ -652,7 +653,7 @@ static void tui_scroll(UI *ui, int count) if (count == -1) { unibi_out(ui, unibi_insert_line); } else { - data->params[0].i = -count; + data->params[0].i = -(int)count; unibi_out(ui, unibi_parm_insert_line); } } @@ -683,10 +684,10 @@ static void tui_highlight_set(UI *ui, HlAttrs attrs) ((TUIData *)ui->data)->grid.attrs = attrs; } -static void tui_put(UI *ui, uint8_t *text, size_t size) +static void tui_put(UI *ui, String text) { TUIData *data = ui->data; - print_cell(ui, ugrid_put(&data->grid, text, size)); + print_cell(ui, ugrid_put(&data->grid, (uint8_t *)text.data, text.size)); } static void tui_bell(UI *ui) @@ -699,17 +700,17 @@ static void tui_visual_bell(UI *ui) unibi_out(ui, unibi_flash_screen); } -static void tui_update_fg(UI *ui, int fg) +static void tui_update_fg(UI *ui, Integer fg) { - ((TUIData *)ui->data)->grid.fg = fg; + ((TUIData *)ui->data)->grid.fg = (int)fg; } -static void tui_update_bg(UI *ui, int bg) +static void tui_update_bg(UI *ui, Integer bg) { - ((TUIData *)ui->data)->grid.bg = bg; + ((TUIData *)ui->data)->grid.bg = (int)bg; } -static void tui_update_sp(UI *ui, int sp) +static void tui_update_sp(UI *ui, Integer sp) { // Do nothing; 'special' color is for GUI only } @@ -784,19 +785,19 @@ static void tui_suspend(UI *ui) #endif } -static void tui_set_title(UI *ui, char *title) +static void tui_set_title(UI *ui, String title) { TUIData *data = ui->data; - if (!(title && unibi_get_str(data->ut, unibi_to_status_line) + if (!(title.data && unibi_get_str(data->ut, unibi_to_status_line) && unibi_get_str(data->ut, unibi_from_status_line))) { return; } unibi_out(ui, unibi_to_status_line); - out(ui, title, strlen(title)); + out(ui, title.data, title.size); unibi_out(ui, unibi_from_status_line); } -static void tui_set_icon(UI *ui, char *icon) +static void tui_set_icon(UI *ui, String icon) { } diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 924a4192bc..0a2154438f 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -89,6 +89,10 @@ static int old_mode_idx = -1; #define UI_CALL_MORE(method, ...) if (ui->method) ui->method(ui, __VA_ARGS__) #define UI_CALL_ZERO(method) if (ui->method) ui->method(ui) +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ui_events_call.generated.h" +#endif + void ui_builtin_start(void) { #ifdef FEAT_TUI @@ -128,24 +132,6 @@ bool ui_active(void) return ui_count != 0; } -void ui_suspend(void) -{ - UI_CALL(suspend); - UI_CALL(flush); -} - -void ui_set_title(char *title) -{ - UI_CALL(set_title, title); - UI_CALL(flush); -} - -void ui_set_icon(char *icon) -{ - UI_CALL(set_icon, icon); - UI_CALL(flush); -} - void ui_event(char *name, Array args) { bool args_consumed = false; @@ -214,33 +200,23 @@ void ui_resize(int new_width, int new_height) sr.bot = height - 1; sr.left = 0; sr.right = width - 1; - UI_CALL(resize, width, height); + ui_call_resize(width, height); } void ui_busy_start(void) { if (!(busy++)) { - UI_CALL(busy_start); + ui_call_busy_start(); } } void ui_busy_stop(void) { if (!(--busy)) { - UI_CALL(busy_stop); + ui_call_busy_stop(); } } -void ui_mouse_on(void) -{ - UI_CALL(mouse_on); -} - -void ui_mouse_off(void) -{ - UI_CALL(mouse_off); -} - void ui_attach_impl(UI *ui) { if (ui_count == MAX_UI_COUNT) { @@ -281,11 +257,6 @@ void ui_detach_impl(UI *ui) } } -void ui_clear(void) -{ - UI_CALL(clear); -} - // Set scrolling region for window 'wp'. // The region starts 'off' lines from the start of the window. // Also set the vertical scroll region for a vertically split window. Always @@ -300,7 +271,7 @@ void ui_set_scroll_region(win_T *wp, int off) sr.right = wp->w_wincol + wp->w_width - 1; } - UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); + ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right); } // Reset scrolling region to the whole screen. @@ -310,22 +281,7 @@ void ui_reset_scroll_region(void) sr.bot = (int)Rows - 1; sr.left = 0; sr.right = (int)Columns - 1; - UI_CALL(set_scroll_region, sr.top, sr.bot, sr.left, sr.right); -} - -void ui_append_lines(int count) -{ - UI_CALL(scroll, -count); -} - -void ui_delete_lines(int count) -{ - UI_CALL(scroll, count); -} - -void ui_eol_clear(void) -{ - UI_CALL(eol_clear); + ui_call_set_scroll_region(sr.top, sr.bot, sr.left, sr.right); } void ui_start_highlight(int attr_code) @@ -350,23 +306,31 @@ void ui_stop_highlight(void) set_highlight_args(current_attr_code); } -void ui_visual_bell(void) -{ - UI_CALL(visual_bell); -} - void ui_puts(uint8_t *str) { - uint8_t *ptr = str; + uint8_t *p = str; uint8_t c; - while ((c = *ptr)) { + while ((c = *p)) { if (c < 0x20) { - parse_control_character(c); - ptr++; - } else { - send_output(&ptr); + abort(); + } + + size_t clen = (size_t)mb_ptr2len(p); + ui_call_put((String){ .data = (char *)p, .size = clen }); + col++; + if (mb_ptr2cells(p) > 1) { + // double cell character, blank the next cell + ui_call_put((String)STRING_INIT); + col++; } + if (utf_ambiguous_width(utf_ptr2char(p))) { + pending_cursor_update = true; + } + if (col >= width) { + ui_linefeed(); + } + p += clen; } } @@ -390,15 +354,10 @@ void ui_mode_info_set(void) { Array style = mode_style_array(); bool enabled = (*p_guicursor != NUL); - UI_CALL(mode_info_set, enabled, style); + ui_call_mode_info_set(enabled, style); api_free_array(style); } -void ui_update_menu(void) -{ - UI_CALL(update_menu); -} - int ui_current_row(void) { return row; @@ -411,47 +370,7 @@ int ui_current_col(void) void ui_flush(void) { - UI_CALL(flush); -} - -static void send_output(uint8_t **ptr) -{ - uint8_t *p = *ptr; - - while (*p >= 0x20) { - size_t clen = (size_t)mb_ptr2len(p); - UI_CALL(put, p, (size_t)clen); - col++; - if (mb_ptr2cells(p) > 1) { - // double cell character, blank the next cell - UI_CALL(put, NULL, 0); - col++; - } - if (utf_ambiguous_width(utf_ptr2char(p))) { - pending_cursor_update = true; - } - if (col >= width) { - ui_linefeed(); - } - p += clen; - } - - *ptr = p; -} - -static void parse_control_character(uint8_t c) -{ - if (c == '\n') { - ui_linefeed(); - } else if (c == '\r') { - ui_carriage_return(); - } else if (c == '\b') { - ui_cursor_left(); - } else if (c == Ctrl_L) { - ui_cursor_right(); - } else if (c == Ctrl_G) { - UI_CALL(bell); - } + ui_call_flush(); } static void set_highlight_args(int attr_code) @@ -509,43 +428,23 @@ end: UI_CALL(highlight_set, (ui->rgb ? rgb_attrs : cterm_attrs)); } -static void ui_linefeed(void) +void ui_linefeed(void) { int new_col = 0; int new_row = row; if (new_row < sr.bot) { new_row++; } else { - UI_CALL(scroll, 1); + ui_call_scroll(1); } ui_cursor_goto(new_row, new_col); } -static void ui_carriage_return(void) -{ - int new_col = 0; - ui_cursor_goto(row, new_col); -} - -static void ui_cursor_left(void) -{ - int new_col = col - 1; - assert(new_col >= 0); - ui_cursor_goto(row, new_col); -} - -static void ui_cursor_right(void) -{ - int new_col = col + 1; - assert(new_col < width); - ui_cursor_goto(row, new_col); -} - static void flush_cursor_update(void) { if (pending_cursor_update) { pending_cursor_update = false; - UI_CALL(cursor_goto, row, col); + ui_call_cursor_goto(row, col); } } @@ -560,7 +459,8 @@ void ui_cursor_shape(void) if (old_mode_idx != mode_idx) { old_mode_idx = mode_idx; - UI_CALL(mode_change, mode_idx); + char *full_name = shape_table[mode_idx].full_name; + ui_call_mode_change(cstr_as_string(full_name), mode_idx); } conceal_check_cursur_line(); } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 9338ab3ea3..064f77fee1 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -28,35 +28,17 @@ struct ui_t { bool ui_ext[UI_WIDGETS]; ///< Externalized widgets int width, height; void *data; - void (*resize)(UI *ui, int rows, int columns); - void (*clear)(UI *ui); - void (*eol_clear)(UI *ui); - void (*cursor_goto)(UI *ui, int row, int col); - void (*mode_info_set)(UI *ui, bool enabled, Array cursor_styles); - void (*update_menu)(UI *ui); - void (*busy_start)(UI *ui); - void (*busy_stop)(UI *ui); - void (*mouse_on)(UI *ui); - void (*mouse_off)(UI *ui); - void (*mode_change)(UI *ui, int mode_idx); - void (*set_scroll_region)(UI *ui, int top, int bot, int left, int right); - void (*scroll)(UI *ui, int count); - void (*highlight_set)(UI *ui, HlAttrs attrs); - void (*put)(UI *ui, uint8_t *str, size_t len); - void (*bell)(UI *ui); - void (*visual_bell)(UI *ui); - void (*flush)(UI *ui); - void (*update_fg)(UI *ui, int fg); - void (*update_bg)(UI *ui, int bg); - void (*update_sp)(UI *ui, int sp); - void (*suspend)(UI *ui); - void (*set_title)(UI *ui, char *title); - void (*set_icon)(UI *ui, char *icon); + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "ui_events.generated.h" +#endif + void (*event)(UI *ui, char *name, Array args, bool *args_consumed); void (*stop)(UI *ui); }; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui.h.generated.h" +# include "ui_events_call.h.generated.h" #endif #endif // NVIM_UI_H diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index d790770892..0165db7c0c 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -49,8 +49,12 @@ static argv_callback uilog_event = NULL; event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) #endif -#define INT2PTR(i) ((void *)(uintptr_t)i) -#define PTR2INT(p) ((int)(uintptr_t)p) +#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) { @@ -146,148 +150,6 @@ static void ui_bridge_stop_event(void **argv) ui->stop(ui); } -static void ui_bridge_resize(UI *b, int width, int height) -{ - UI_CALL(b, resize, 3, b, INT2PTR(width), INT2PTR(height)); -} -static void ui_bridge_resize_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->resize(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); -} - -static void ui_bridge_clear(UI *b) -{ - UI_CALL(b, clear, 1, b); -} -static void ui_bridge_clear_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->clear(ui); -} - -static void ui_bridge_eol_clear(UI *b) -{ - UI_CALL(b, eol_clear, 1, b); -} -static void ui_bridge_eol_clear_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->eol_clear(ui); -} - -static void ui_bridge_cursor_goto(UI *b, int row, int col) -{ - UI_CALL(b, cursor_goto, 3, b, INT2PTR(row), INT2PTR(col)); -} -static void ui_bridge_cursor_goto_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->cursor_goto(ui, PTR2INT(argv[1]), PTR2INT(argv[2])); -} - -static void ui_bridge_mode_info_set(UI *b, bool enabled, Array modes) -{ - bool *enabledp = xmalloc(sizeof(*enabledp)); - Object *modesp = xmalloc(sizeof(*modesp)); - *enabledp = enabled; - *modesp = copy_object(ARRAY_OBJ(modes)); - UI_CALL(b, mode_info_set, 3, b, enabledp, modesp); -} -static void ui_bridge_mode_info_set_event(void **argv) -{ - UI *ui = UI(argv[0]); - bool *enabled = argv[1]; - Object *modes = argv[2]; - ui->mode_info_set(ui, *enabled, modes->data.array); - xfree(enabled); - api_free_object(*modes); - xfree(modes); -} - -static void ui_bridge_update_menu(UI *b) -{ - UI_CALL(b, update_menu, 1, b); -} -static void ui_bridge_update_menu_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->update_menu(ui); -} - -static void ui_bridge_busy_start(UI *b) -{ - UI_CALL(b, busy_start, 1, b); -} -static void ui_bridge_busy_start_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->busy_start(ui); -} - -static void ui_bridge_busy_stop(UI *b) -{ - UI_CALL(b, busy_stop, 1, b); -} -static void ui_bridge_busy_stop_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->busy_stop(ui); -} - -static void ui_bridge_mouse_on(UI *b) -{ - UI_CALL(b, mouse_on, 1, b); -} -static void ui_bridge_mouse_on_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->mouse_on(ui); -} - -static void ui_bridge_mouse_off(UI *b) -{ - UI_CALL(b, mouse_off, 1, b); -} -static void ui_bridge_mouse_off_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->mouse_off(ui); -} - -static void ui_bridge_mode_change(UI *b, int mode_idx) -{ - UI_CALL(b, mode_change, 2, b, INT2PTR(mode_idx)); -} -static void ui_bridge_mode_change_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->mode_change(ui, PTR2INT(argv[1])); -} - -static void ui_bridge_set_scroll_region(UI *b, int top, int bot, int left, - int right) -{ - UI_CALL(b, set_scroll_region, 5, b, INT2PTR(top), INT2PTR(bot), - INT2PTR(left), INT2PTR(right)); -} -static void ui_bridge_set_scroll_region_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->set_scroll_region(ui, PTR2INT(argv[1]), PTR2INT(argv[2]), - PTR2INT(argv[3]), PTR2INT(argv[4])); -} - -static void ui_bridge_scroll(UI *b, int count) -{ - UI_CALL(b, scroll, 2, b, INT2PTR(count)); -} -static void ui_bridge_scroll_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->scroll(ui, PTR2INT(argv[1])); -} - static void ui_bridge_highlight_set(UI *b, HlAttrs attrs) { HlAttrs *a = xmalloc(sizeof(HlAttrs)); @@ -301,82 +163,6 @@ static void ui_bridge_highlight_set_event(void **argv) xfree(argv[1]); } -static void ui_bridge_put(UI *b, uint8_t *text, size_t size) -{ - uint8_t *t = NULL; - if (text) { - t = xmalloc(sizeof(((UCell *)0)->data)); - memcpy(t, text, size); - } - UI_CALL(b, put, 3, b, t, INT2PTR(size)); -} -static void ui_bridge_put_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->put(ui, (uint8_t *)argv[1], (size_t)(uintptr_t)argv[2]); - xfree(argv[1]); -} - -static void ui_bridge_bell(UI *b) -{ - UI_CALL(b, bell, 1, b); -} -static void ui_bridge_bell_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->bell(ui); -} - -static void ui_bridge_visual_bell(UI *b) -{ - UI_CALL(b, visual_bell, 1, b); -} -static void ui_bridge_visual_bell_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->visual_bell(ui); -} - -static void ui_bridge_update_fg(UI *b, int fg) -{ - UI_CALL(b, update_fg, 2, b, INT2PTR(fg)); -} -static void ui_bridge_update_fg_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->update_fg(ui, PTR2INT(argv[1])); -} - -static void ui_bridge_update_bg(UI *b, int bg) -{ - UI_CALL(b, update_bg, 2, b, INT2PTR(bg)); -} -static void ui_bridge_update_bg_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->update_bg(ui, PTR2INT(argv[1])); -} - -static void ui_bridge_update_sp(UI *b, int sp) -{ - UI_CALL(b, update_sp, 2, b, INT2PTR(sp)); -} -static void ui_bridge_update_sp_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->update_sp(ui, PTR2INT(argv[1])); -} - -static void ui_bridge_flush(UI *b) -{ - UI_CALL(b, flush, 1, b); -} -static void ui_bridge_flush_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->flush(ui); -} - static void ui_bridge_suspend(UI *b) { UIBridgeData *data = (UIBridgeData *)b; @@ -394,25 +180,3 @@ static void ui_bridge_suspend_event(void **argv) UI *ui = UI(argv[0]); ui->suspend(ui); } - -static void ui_bridge_set_title(UI *b, char *title) -{ - UI_CALL(b, set_title, 2, b, title ? xstrdup(title) : NULL); -} -static void ui_bridge_set_title_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->set_title(ui, argv[1]); - xfree(argv[1]); -} - -static void ui_bridge_set_icon(UI *b, char *icon) -{ - UI_CALL(b, set_icon, 2, b, icon ? xstrdup(icon) : NULL); -} -static void ui_bridge_set_icon_event(void **argv) -{ - UI *ui = UI(argv[0]); - ui->set_icon(ui, argv[1]); - xfree(argv[1]); -} |