diff options
-rw-r--r-- | CMakeLists.txt | 4 | ||||
-rw-r--r-- | scripts/gendispatch.lua | 13 | ||||
-rwxr-xr-x | scripts/release.sh | 9 | ||||
-rw-r--r-- | src/nvim/api/buffer.c | 17 | ||||
-rw-r--r-- | src/nvim/api/tabpage.c | 7 | ||||
-rw-r--r-- | src/nvim/api/ui.c | 8 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 42 | ||||
-rw-r--r-- | src/nvim/api/window.c | 16 | ||||
-rw-r--r-- | src/nvim/func_attr.h | 1 | ||||
-rw-r--r-- | test/functional/api/version_spec.lua | 134 | ||||
-rw-r--r-- | test/functional/fixtures/api_level_1.mpack | bin | 0 -> 16695 bytes |
11 files changed, 206 insertions, 45 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2fd5209396..1a058f2bff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,9 +68,9 @@ set(NVIM_VERSION_PATCH 0) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers # API level -set(NVIM_API_LEVEL 1) # Bump this after any API change. +set(NVIM_API_LEVEL 2) # Bump this after any API change. set(NVIM_API_LEVEL_COMPAT 0) # Adjust this after a _breaking_ API change. -set(NVIM_API_PRERELEASE false) +set(NVIM_API_PRERELEASE true) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) diff --git a/scripts/gendispatch.lua b/scripts/gendispatch.lua index 397ccc9aaf..45a4f4a4de 100644 --- a/scripts/gendispatch.lua +++ b/scripts/gendispatch.lua @@ -9,7 +9,8 @@ C, Ct, Cc, Cg = lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cg any = P(1) -- (consume one character) letter = R('az', 'AZ') + S('_$') -alpha = letter + R('09') +num = R('09') +alpha = letter + num nl = P('\r\n') + P('\n') not_nl = any - nl ws = S(' \t') + nl @@ -35,6 +36,7 @@ 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_ASYNC') * Cc(true)), 'async') ^ -1) * (fill * Cg((P('FUNC_API_NOEXPORT') * Cc(true)), 'noexport') ^ -1) * (fill * Cg((P('FUNC_API_NOEVAL') * Cc(true)), 'noeval') ^ -1) * @@ -52,6 +54,7 @@ package.path = nvimsrcdir .. '/?.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[#arg-2] -- output h file with packed metadata @@ -114,9 +117,11 @@ local deprecated_aliases = require("api.dispatch_deprecated") for i,f in ipairs(shallowcopy(functions)) do local ismethod = false if startswith(f.name, "nvim_") then - -- TODO(bfredl) after 0.1.6 allow method definitions - -- to specify the since and deprecated_since field - f.since = 1 + if f.since == nil then + print("Function "..f.name.." lacks since field.\n") + os.exit(1) + end + f.since = tonumber(f.since) if startswith(f.name, "nvim_buf_") then ismethod = true elseif startswith(f.name, "nvim_win_") then diff --git a/scripts/release.sh b/scripts/release.sh index 93f9fa3d35..dac5e9b177 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -31,6 +31,8 @@ __VERSION_MINOR=$(grep 'set(NVIM_VERSION_MINOR' CMakeLists.txt\ __VERSION_PATCH=$(grep 'set(NVIM_VERSION_PATCH' CMakeLists.txt\ |$__sed 's/.*NVIM_VERSION_PATCH ([[:digit:]]).*/\1/') __VERSION="${__VERSION_MAJOR}.${__VERSION_MINOR}.${__VERSION_PATCH}" +__API_LEVEL=$(grep 'set(NVIM_API_LEVEL ' CMakeLists.txt\ + |$__sed 's/.*NVIM_API_LEVEL ([[:digit:]]).*/\1/') { [ -z "$__VERSION_MAJOR" ] || [ -z "$__VERSION_MINOR" ] || [ -z "$__VERSION_PATCH" ]; } \ && { echo "ERROR: version parse failed: '${__VERSION}'"; exit 1; } __RELEASE_MSG="NVIM v${__VERSION} @@ -47,7 +49,12 @@ __BUMP_MSG="version bump" echo "Most recent tag: ${__LAST_TAG}" echo "Release version: ${__VERSION}" $__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt -$__sed -i.bk 's/(NVIM_API_PRERELEASE) true/\1 false/' CMakeLists.txt +if grep '(NVIM_API_PRERELEASE true)' CMakeLists.txt > /dev/null; then + $__sed -i.bk 's/(NVIM_API_PRERELEASE) true/\1 false/' CMakeLists.txt + cp build/funcs_data.mpack test/functional/fixtures/api_level_$__API_LEVEL.mpack + git add test/functional/fixtures/api_level_$__API_LEVEL.mpack +fi + echo "Building changelog since ${__LAST_TAG}..." __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')" diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index c7e535228a..b75a2c7211 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -32,6 +32,7 @@ /// @param[out] err Error details, if any /// @return Line count Integer nvim_buf_line_count(Buffer buffer, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -132,7 +133,6 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, return nvim_buf_get_lines(0, buffer, start , end, false, err); } - /// Retrieves a line range from the buffer /// /// Indexing is zero-based, end-exclusive. Negative indices are interpreted @@ -154,6 +154,7 @@ ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Integer end, Boolean strict_indexing, Error *err) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -266,6 +267,7 @@ void nvim_buf_set_lines(uint64_t channel_id, Boolean strict_indexing, ArrayOf(String) replacement, // NOLINT Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -416,6 +418,7 @@ end: /// @param[out] err Error details, if any /// @return Variable value Object nvim_buf_get_var(Buffer buffer, String name, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -432,6 +435,7 @@ Object nvim_buf_get_var(Buffer buffer, String name, Error *err) /// /// @return `b:changedtick` value. Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) + FUNC_API_SINCE(2) { const buf_T *const buf = find_buffer_by_handle(buffer, err); @@ -449,6 +453,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) /// @param value Variable value /// @param[out] err Error details, if any void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -465,6 +470,7 @@ void nvim_buf_set_var(Buffer buffer, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any void nvim_buf_del_var(Buffer buffer, String name, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -525,6 +531,7 @@ Object buffer_del_var(Buffer buffer, String name, Error *err) /// @param[out] err Error details, if any /// @return Option value Object nvim_buf_get_option(Buffer buffer, String name, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -543,6 +550,7 @@ Object nvim_buf_get_option(Buffer buffer, String name, Error *err) /// @param value Option value /// @param[out] err Error details, if any void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -559,6 +567,7 @@ void nvim_buf_set_option(Buffer buffer, String name, Object value, Error *err) /// @param[out] err Error details, if any /// @return Buffer number Integer nvim_buf_get_number(Buffer buffer, Error *err) + FUNC_API_SINCE(1) { Integer rv = 0; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -576,6 +585,7 @@ Integer nvim_buf_get_number(Buffer buffer, Error *err) /// @param[out] err Error details, if any /// @return Buffer name String nvim_buf_get_name(Buffer buffer, Error *err) + FUNC_API_SINCE(1) { String rv = STRING_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -593,6 +603,7 @@ String nvim_buf_get_name(Buffer buffer, Error *err) /// @param name Buffer name /// @param[out] err Error details, if any void nvim_buf_set_name(Buffer buffer, String name, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -622,6 +633,7 @@ void nvim_buf_set_name(Buffer buffer, String name, Error *err) /// @param buffer Buffer handle /// @return true if the buffer is valid, false otherwise Boolean nvim_buf_is_valid(Buffer buffer) + FUNC_API_SINCE(1) { Error stub = ERROR_INIT; return find_buffer_by_handle(buffer, &stub) != NULL; @@ -653,6 +665,7 @@ void buffer_insert(Buffer buffer, /// @param[out] err Error details, if any /// @return (row, col) tuple ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; buf_T *buf = find_buffer_by_handle(buffer, err); @@ -727,6 +740,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer col_start, Integer col_end, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -767,6 +781,7 @@ void nvim_buf_clear_highlight(Buffer buffer, Integer line_start, Integer line_end, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { diff --git a/src/nvim/api/tabpage.c b/src/nvim/api/tabpage.c index d24250d032..0f0c33f621 100644 --- a/src/nvim/api/tabpage.c +++ b/src/nvim/api/tabpage.c @@ -15,6 +15,7 @@ /// @param[out] err Error details, if any /// @return List of windows in `tabpage` ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -44,6 +45,7 @@ ArrayOf(Window) nvim_tabpage_list_wins(Tabpage tabpage, Error *err) /// @param[out] err Error details, if any /// @return Variable value Object nvim_tabpage_get_var(Tabpage tabpage, String name, Error *err) + FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -64,6 +66,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, String name, Object value, Error *err) + FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -80,6 +83,7 @@ void nvim_tabpage_set_var(Tabpage tabpage, /// @param name Variable name /// @param[out] err Error details, if any void nvim_tabpage_del_var(Tabpage tabpage, String name, Error *err) + FUNC_API_SINCE(1) { tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -138,6 +142,7 @@ Object tabpage_del_var(Tabpage tabpage, String name, Error *err) /// @param[out] err Error details, if any /// @return Window handle Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) + FUNC_API_SINCE(1) { Window rv = 0; tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -165,6 +170,7 @@ Window nvim_tabpage_get_win(Tabpage tabpage, Error *err) /// @param[out] err Error details, if any /// @return Tabpage number Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err) + FUNC_API_SINCE(1) { Integer rv = 0; tabpage_T *tab = find_tab_by_handle(tabpage, err); @@ -181,6 +187,7 @@ Integer nvim_tabpage_get_number(Tabpage tabpage, Error *err) /// @param tabpage Tabpage handle /// @return true if the tabpage is valid, false otherwise Boolean nvim_tabpage_is_valid(Tabpage tabpage) + FUNC_API_SINCE(1) { Error stub = ERROR_INIT; return find_tab_by_handle(tabpage, &stub) != NULL; diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 9178538110..625bcc6b4b 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -48,7 +48,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { if (pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, Exception, _("UI already attached for channel")); @@ -118,7 +118,7 @@ void ui_attach(uint64_t channel_id, Integer width, Integer height, } void nvim_ui_detach(uint64_t channel_id, Error *err) - FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, Exception, _("UI is not attached for channel")); @@ -130,7 +130,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(err, Exception, _("UI is not attached for channel")); @@ -151,7 +151,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { if (!pmap_has(uint64_t)(connected_uis, channel_id)) { api_set_error(error, Exception, _("UI is not attached for channel")); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index bf11795d9e..413456c615 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -39,6 +39,7 @@ /// @param command Ex-command string /// @param[out] err Error details (including actual VimL error), if any void nvim_command(String command, Error *err) + FUNC_API_SINCE(1) { // Run the command try_start(); @@ -56,6 +57,7 @@ void nvim_command(String command, Error *err) /// @see feedkeys() /// @see vim_strsave_escape_csi void nvim_feedkeys(String keys, String mode, Boolean escape_csi) + FUNC_API_SINCE(1) { bool remap = true; bool insert = false; @@ -122,7 +124,7 @@ void nvim_feedkeys(String keys, String mode, Boolean escape_csi) /// @return Number of bytes actually written (can be fewer than /// requested if the buffer becomes full). Integer nvim_input(String keys) - FUNC_API_ASYNC + FUNC_API_SINCE(1) FUNC_API_ASYNC { return (Integer)input_enqueue(keys); } @@ -133,6 +135,7 @@ Integer nvim_input(String keys) /// @see cpoptions String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, Boolean special) + FUNC_API_SINCE(1) { if (str.size == 0) { // Empty string @@ -152,6 +155,7 @@ String nvim_replace_termcodes(String str, Boolean from_part, Boolean do_lt, } String nvim_command_output(String str, Error *err) + FUNC_API_SINCE(1) { do_cmdline_cmd("redir => v:command_output"); nvim_command(str, err); @@ -172,6 +176,7 @@ String nvim_command_output(String str, Error *err) /// @param[out] err Error details, if any /// @return Evaluation result or expanded object Object nvim_eval(String expr, Error *err) + FUNC_API_SINCE(1) { Object rv = OBJECT_INIT; // Evaluate the expression @@ -200,6 +205,7 @@ Object nvim_eval(String expr, Error *err) /// @param[out] err Error details, if any /// @return Result of the function call Object nvim_call_function(String fname, Array args, Error *err) + FUNC_API_SINCE(1) { Object rv = OBJECT_INIT; if (args.size > MAX_FUNC_ARGS) { @@ -248,6 +254,7 @@ free_vim_args: /// @param[out] err Error details, if any /// @return Number of cells Integer nvim_strwidth(String str, Error *err) + FUNC_API_SINCE(1) { if (str.size > INT_MAX) { api_set_error(err, Validation, _("String length is too high")); @@ -261,6 +268,7 @@ Integer nvim_strwidth(String str, Error *err) /// /// @return List of paths ArrayOf(String) nvim_list_runtime_paths(void) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; uint8_t *rtp = p_rtp; @@ -302,6 +310,7 @@ ArrayOf(String) nvim_list_runtime_paths(void) /// @param dir Directory path /// @param[out] err Error details, if any void nvim_set_current_dir(String dir, Error *err) + FUNC_API_SINCE(1) { if (dir.size >= MAXPATHL) { api_set_error(err, Validation, _("Directory string is too long")); @@ -330,6 +339,7 @@ void nvim_set_current_dir(String dir, Error *err) /// @param[out] err Error details, if any /// @return Current line string String nvim_get_current_line(Error *err) + FUNC_API_SINCE(1) { return buffer_get_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } @@ -339,6 +349,7 @@ String nvim_get_current_line(Error *err) /// @param line Line contents /// @param[out] err Error details, if any void nvim_set_current_line(String line, Error *err) + FUNC_API_SINCE(1) { buffer_set_line(curbuf->handle, curwin->w_cursor.lnum - 1, line, err); } @@ -347,6 +358,7 @@ void nvim_set_current_line(String line, Error *err) /// /// @param[out] err Error details, if any void nvim_del_current_line(Error *err) + FUNC_API_SINCE(1) { buffer_del_line(curbuf->handle, curwin->w_cursor.lnum - 1, err); } @@ -357,6 +369,7 @@ void nvim_del_current_line(Error *err) /// @param[out] err Error details, if any /// @return Variable value Object nvim_get_var(String name, Error *err) + FUNC_API_SINCE(1) { return dict_get_value(&globvardict, name, err); } @@ -367,6 +380,7 @@ Object nvim_get_var(String name, Error *err) /// @param value Variable value /// @param[out] err Error details, if any void nvim_set_var(String name, Object value, Error *err) + FUNC_API_SINCE(1) { dict_set_var(&globvardict, name, value, false, false, err); } @@ -376,6 +390,7 @@ void nvim_set_var(String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any void nvim_del_var(String name, Error *err) + FUNC_API_SINCE(1) { dict_set_var(&globvardict, name, NIL, true, false, err); } @@ -414,6 +429,7 @@ Object vim_del_var(String name, Error *err) /// @param[out] err Error details, if any /// @return Variable value Object nvim_get_vvar(String name, Error *err) + FUNC_API_SINCE(1) { return dict_get_value(&vimvardict, name, err); } @@ -424,6 +440,7 @@ Object nvim_get_vvar(String name, Error *err) /// @param[out] err Error details, if any /// @return Option value Object nvim_get_option(String name, Error *err) + FUNC_API_SINCE(1) { return get_option_from(NULL, SREQ_GLOBAL, name, err); } @@ -434,6 +451,7 @@ Object nvim_get_option(String name, Error *err) /// @param value New option value /// @param[out] err Error details, if any void nvim_set_option(String name, Object value, Error *err) + FUNC_API_SINCE(1) { set_option_to(NULL, SREQ_GLOBAL, name, value, err); } @@ -442,6 +460,7 @@ void nvim_set_option(String name, Object value, Error *err) /// /// @param str Message void nvim_out_write(String str) + FUNC_API_SINCE(1) { write_msg(str, false); } @@ -450,6 +469,7 @@ void nvim_out_write(String str) /// /// @param str Message void nvim_err_write(String str) + FUNC_API_SINCE(1) { write_msg(str, true); } @@ -460,6 +480,7 @@ void nvim_err_write(String str) /// @param str Message /// @see nvim_err_write() void nvim_err_writeln(String str) + FUNC_API_SINCE(1) { nvim_err_write(str); nvim_err_write((String) { .data = "\n", .size = 1 }); @@ -469,6 +490,7 @@ void nvim_err_writeln(String str) /// /// @return List of buffer handles ArrayOf(Buffer) nvim_list_bufs(void) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -490,6 +512,7 @@ ArrayOf(Buffer) nvim_list_bufs(void) /// /// @return Buffer handle Buffer nvim_get_current_buf(void) + FUNC_API_SINCE(1) { return curbuf->handle; } @@ -499,6 +522,7 @@ Buffer nvim_get_current_buf(void) /// @param id Buffer handle /// @param[out] err Error details, if any void nvim_set_current_buf(Buffer buffer, Error *err) + FUNC_API_SINCE(1) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -520,6 +544,7 @@ void nvim_set_current_buf(Buffer buffer, Error *err) /// /// @return List of window handles ArrayOf(Window) nvim_list_wins(void) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -541,6 +566,7 @@ ArrayOf(Window) nvim_list_wins(void) /// /// @return Window handle Window nvim_get_current_win(void) + FUNC_API_SINCE(1) { return curwin->handle; } @@ -549,6 +575,7 @@ Window nvim_get_current_win(void) /// /// @param handle Window handle void nvim_set_current_win(Window window, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -570,6 +597,7 @@ void nvim_set_current_win(Window window, Error *err) /// /// @return List of tabpage handles ArrayOf(Tabpage) nvim_list_tabpages(void) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; @@ -591,6 +619,7 @@ ArrayOf(Tabpage) nvim_list_tabpages(void) /// /// @return Tabpage handle Tabpage nvim_get_current_tabpage(void) + FUNC_API_SINCE(1) { return curtab->handle; } @@ -600,6 +629,7 @@ Tabpage nvim_get_current_tabpage(void) /// @param handle Tabpage handle /// @param[out] err Error details, if any void nvim_set_current_tabpage(Tabpage tabpage, Error *err) + FUNC_API_SINCE(1) { tabpage_T *tp = find_tab_by_handle(tabpage, err); @@ -622,7 +652,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { size_t length = (event.size < METHOD_MAXLEN ? event.size : METHOD_MAXLEN); char e[METHOD_MAXLEN + 1]; @@ -636,7 +666,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { size_t length = (event.size < METHOD_MAXLEN ? event.size : @@ -648,11 +678,13 @@ void nvim_unsubscribe(uint64_t channel_id, String event) } Integer nvim_get_color_by_name(String name) + FUNC_API_SINCE(1) { return name_to_color((uint8_t *)name.data); } Dictionary nvim_get_color_map(void) + FUNC_API_SINCE(1) { Dictionary colors = ARRAY_DICT_INIT; @@ -665,7 +697,7 @@ Dictionary nvim_get_color_map(void) Array nvim_get_api_info(uint64_t channel_id) - FUNC_API_ASYNC FUNC_API_NOEVAL + FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_NOEVAL { Array rv = ARRAY_DICT_INIT; @@ -698,7 +730,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_NOEVAL + FUNC_API_SINCE(1) FUNC_API_NOEVAL { Array rv = ARRAY_DICT_INIT; Array results = ARRAY_DICT_INIT; diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 13294e6bf2..3c564ada99 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -19,6 +19,7 @@ /// @param[out] err Error details, if any /// @return Buffer handle Buffer nvim_win_get_buf(Window window, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -35,6 +36,7 @@ Buffer nvim_win_get_buf(Window window, Error *err) /// @param[out] err Error details, if any /// @return (row, col) tuple ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); @@ -53,6 +55,7 @@ ArrayOf(Integer, 2) nvim_win_get_cursor(Window window, Error *err) /// @param pos (row, col) tuple representing the new position /// @param[out] err Error details, if any void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -99,6 +102,7 @@ void nvim_win_set_cursor(Window window, ArrayOf(Integer, 2) pos, Error *err) /// @param[out] err Error details, if any /// @return Height as a count of rows Integer nvim_win_get_height(Window window, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -116,6 +120,7 @@ Integer nvim_win_get_height(Window window, Error *err) /// @param height Height as a count of rows /// @param[out] err Error details, if any void nvim_win_set_height(Window window, Integer height, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -142,6 +147,7 @@ void nvim_win_set_height(Window window, Integer height, Error *err) /// @param[out] err Error details, if any /// @return Width as a count of columns Integer nvim_win_get_width(Window window, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -159,6 +165,7 @@ Integer nvim_win_get_width(Window window, Error *err) /// @param width Width as a count of columns /// @param[out] err Error details, if any void nvim_win_set_width(Window window, Integer width, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -186,6 +193,7 @@ void nvim_win_set_width(Window window, Integer width, Error *err) /// @param[out] err Error details, if any /// @return Variable value Object nvim_win_get_var(Window window, String name, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -203,6 +211,7 @@ Object nvim_win_get_var(Window window, String name, Error *err) /// @param value Variable value /// @param[out] err Error details, if any void nvim_win_set_var(Window window, String name, Object value, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -219,6 +228,7 @@ void nvim_win_set_var(Window window, String name, Object value, Error *err) /// @param name Variable name /// @param[out] err Error details, if any void nvim_win_del_var(Window window, String name, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -278,6 +288,7 @@ Object window_del_var(Window window, String name, Error *err) /// @param[out] err Error details, if any /// @return Option value Object nvim_win_get_option(Window window, String name, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -296,6 +307,7 @@ Object nvim_win_get_option(Window window, String name, Error *err) /// @param value Option value /// @param[out] err Error details, if any void nvim_win_set_option(Window window, String name, Object value, Error *err) + FUNC_API_SINCE(1) { win_T *win = find_window_by_handle(window, err); @@ -312,6 +324,7 @@ void nvim_win_set_option(Window window, String name, Object value, Error *err) /// @param[out] err Error details, if any /// @return (row, col) tuple with the window position ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) + FUNC_API_SINCE(1) { Array rv = ARRAY_DICT_INIT; win_T *win = find_window_by_handle(window, err); @@ -330,6 +343,7 @@ ArrayOf(Integer, 2) nvim_win_get_position(Window window, Error *err) /// @param[out] err Error details, if any /// @return Tabpage that contains the window Tabpage nvim_win_get_tabpage(Window window, Error *err) + FUNC_API_SINCE(1) { Tabpage rv = 0; win_T *win = find_window_by_handle(window, err); @@ -347,6 +361,7 @@ Tabpage nvim_win_get_tabpage(Window window, Error *err) /// @param[out] err Error details, if any /// @return Window number Integer nvim_win_get_number(Window window, Error *err) + FUNC_API_SINCE(1) { int rv = 0; win_T *win = find_window_by_handle(window, err); @@ -366,6 +381,7 @@ Integer nvim_win_get_number(Window window, Error *err) /// @param window Window handle /// @return true if the window is valid, false otherwise Boolean nvim_win_is_valid(Window window) + FUNC_API_SINCE(1) { Error stub = ERROR_INIT; return find_window_by_handle(window, &stub) != NULL; diff --git a/src/nvim/func_attr.h b/src/nvim/func_attr.h index d98fe5b22b..18410445e1 100644 --- a/src/nvim/func_attr.h +++ b/src/nvim/func_attr.h @@ -186,6 +186,7 @@ # define FUNC_API_ASYNC # define FUNC_API_NOEXPORT # define FUNC_API_NOEVAL +# define FUNC_API_SINCE(X) # define FUNC_ATTR_MALLOC REAL_FATTR_MALLOC # define FUNC_ATTR_ALLOC_SIZE(x) REAL_FATTR_ALLOC_SIZE(x) # define FUNC_ATTR_ALLOC_SIZE_PROD(x, y) REAL_FATTR_ALLOC_SIZE_PROD(x, y) diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua index 3efd00ddbe..d23f058f69 100644 --- a/test/functional/api/version_spec.lua +++ b/test/functional/api/version_spec.lua @@ -4,26 +4,16 @@ local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq local function read_mpack_file(fname) local fd = io.open(fname, 'rb') + if fd == nil then + return nil + end + local data = fd:read('*a') fd:close() local unpack = mpack.Unpacker() return unpack(data) end --- Remove metadata that is not essential to backwards-compatibility. -local function remove_function_metadata(fspec) - fspec['can_fail'] = nil - fspec['async'] = nil - fspec['method'] = nil - fspec['since'] = nil - fspec['deprecated_since'] = nil - fspec['receives_channel_id'] = nil - for idx, _ in ipairs(fspec['parameters']) do - fspec['parameters'][idx][2] = '' -- Remove parameter name. - end - return fspec -end - describe("api_info()['version']", function() before_each(clear) @@ -49,23 +39,111 @@ describe("api_info()['version']", function() eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch)) eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch)) end) +end) + + +describe("api functions", function() + before_each(clear) + + local function func_table(metadata) + local functions = {} + for _,f in ipairs(metadata.functions) do + functions[f.name] = f + end + return functions + end + + -- Remove metadata that is not essential to backwards-compatibility. + local function filter_function_metadata(f) + f.deprecated_since = nil + for idx, _ in ipairs(f.parameters) do + f.parameters[idx][2] = '' -- Remove parameter name. + end + + if string.sub(f.name, 1, 4) ~= "nvim" then + f.method = nil + end + return f + end - it("api_compatible level is valid", function() - local api = helpers.call('api_info') - local compat = api['version']['api_compatible'] - local path = 'test/functional/fixtures/api_level_' - ..tostring(compat)..'.mpack' - - -- Verify that the current API function signatures match those of the API - -- level for which we claim compatibility. - local old_api = read_mpack_file(path) - for _, fn_old in ipairs(old_api['functions']) do - for _, fn_new in ipairs(api['functions']) do - if fn_old['name'] == fn_new['name'] then - eq(remove_function_metadata(fn_old), - remove_function_metadata(fn_new)) + -- Level 0 represents methods from 0.1.5 and earlier, when 'since' was not + -- yet defined, and metadata was not filtered of internal keys like 'async'. + local function clean_level_0(metadata) + for _, f in ipairs(metadata.functions) do + f.can_fail = nil + f.async = nil + f.receives_channel_id = nil + f.since = 0 + end + end + + it("are compatible with old metadata or have new level", function() + local api = helpers.call('api_info') + local compat = api.version.api_compatible + local api_level = api.version.api_level + local stable + if api.version.api_prerelease then + stable = api_level-1 + else + stable = api_level + end + + local funcs_new = func_table(api) + local funcs_compat = {} + for level = compat, stable do + local path = ('test/functional/fixtures/api_level_'.. + tostring(level)..'.mpack') + local old_api = read_mpack_file(path) + if old_api == nil then + local errstr = "missing metadata fixture for stable level "..level..". " + if level == api_level and not api.version.api_prerelease then + errstr = (errstr.."If NVIM_API_CURRENT was bumped, ".. + "don't forget to set NVIM_API_PRERELEASE to true.") + end + error(errstr) + end + + if level == 0 then + clean_level_0(old_api) + end + + for _,f in ipairs(old_api.functions) do + if funcs_new[f.name] == nil then + if f.since >= compat then + error('function '..f.name..' was removed but exists in level '.. + f.since..' which nvim should be compatible with') + end + else + eq(filter_function_metadata(f), + filter_function_metadata(funcs_new[f.name])) + end + end + + funcs_compat[level] = func_table(old_api) + end + + for _,f in ipairs(api.functions) do + if f.since <= stable then + local f_old = funcs_compat[f.since][f.name] + if f_old == nil then + if string.sub(f.name, 1, 4) == "nvim" then + local errstr = ("function "..f.name.." has too low since value. ".. + "For new functions set it to "..(stable+1)..".") + if not api.version.api_prerelease then + errstr = (errstr.." Also bump NVIM_API_CURRENT and set ".. + "NVIM_API_PRERELEASE to true in CMakeLists.txt.") + end + error(errstr) + else + error("function name '"..f.name.."' doesn't begin with 'nvim_'") + end end + elseif f.since > api_level then + error("function "..f.name.." has since value > api_level. ".. + "Please bump NVIM_API_CURRENT and set ".. + "NVIM_API_PRERELEASE to true in CMakeLists.txt.") end end end) + end) diff --git a/test/functional/fixtures/api_level_1.mpack b/test/functional/fixtures/api_level_1.mpack Binary files differnew file mode 100644 index 0000000000..66ea9c2e5d --- /dev/null +++ b/test/functional/fixtures/api_level_1.mpack |