diff options
-rw-r--r-- | CMakeLists.txt | 6 | ||||
-rw-r--r-- | config/versiondef.h.in | 4 | ||||
-rw-r--r-- | runtime/doc/api.txt | 1 | ||||
-rw-r--r-- | runtime/doc/msgpack_rpc.txt | 3 | ||||
-rwxr-xr-x | scripts/release.sh | 2 | ||||
-rw-r--r-- | src/nvim/api/private/helpers.c | 13 | ||||
-rw-r--r-- | test/functional/api/compatibility_spec.lua | 65 | ||||
-rw-r--r-- | test/functional/eval/api_functions_spec.lua | 3 | ||||
-rw-r--r-- | test/functional/eval/msgpack_functions_spec.lua | 2 | ||||
-rw-r--r-- | test/functional/fixtures/api-info/0.mpack | bin | 0 -> 7873 bytes |
10 files changed, 96 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aa6a2de32..3b139025d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,6 +65,12 @@ set(NVIM_VERSION_MINOR 1) set(NVIM_VERSION_PATCH 6) set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers +# Neovim API version. When changing the API, bump CURRENT if +# PRERELEASE is false, and set PRERELEASE as true +set(NVIM_API_CURRENT 1) +set(NVIM_API_COMPATIBILITY 0) +set(NVIM_API_PRERELEASE true) + file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR) include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT) diff --git a/config/versiondef.h.in b/config/versiondef.h.in index c91bb29c90..465d90b674 100644 --- a/config/versiondef.h.in +++ b/config/versiondef.h.in @@ -7,6 +7,10 @@ #define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" #cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@" +#define NVIM_API_CURRENT @NVIM_API_CURRENT@ +#define NVIM_API_COMPATIBILITY @NVIM_API_COMPATIBILITY@ +#cmakedefine NVIM_API_PRERELEASE + #define NVIM_VERSION_CFLAGS "@NVIM_VERSION_CFLAGS@" #define NVIM_VERSION_BUILD_TYPE "@NVIM_VERSION_BUILD_TYPE@" diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c3d7fdb35b..dd48a0c2a5 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -51,6 +51,7 @@ Tabpage -> enum value kObjectTypeTabpage Nvim exposes metadata about the API as a Dictionary with the following keys: +api_level API version compatibility information functions calling signature of the API functions types The custom handle types defined by Nvim error_types The possible kinds of errors an API function can exit with. diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt index b3fed9e756..bfd4100f15 100644 --- a/runtime/doc/msgpack_rpc.txt +++ b/runtime/doc/msgpack_rpc.txt @@ -168,6 +168,9 @@ API metadata object ~ API clients exist to hide msgpack-rpc details. The API metadata object contains information that makes this task easier (see also |rpc-types|): + - The "api_level" key contais API compatibility information. The "current" + key holds the API version supported Neovim. The "compatibility" key holds + the oldest supported API version. - The "functions" key contains a list of metadata objects for individual functions. - Each function metadata object has |rpc-types| information about the return diff --git a/scripts/release.sh b/scripts/release.sh index 5a5b5a6498..0dd840074a 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -5,6 +5,7 @@ # Steps: # Create the "release" commit: # - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE +# - CMakeLists.txt: Unset NVIM_API_PRERELEASE # - Tag the commit. # Create the "version bump" commit: # - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev" @@ -46,6 +47,7 @@ __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 echo "Building changelog since ${__LAST_TAG}..." __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')" diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index c0ee735d1a..7fda2802c8 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -20,6 +20,7 @@ #include "nvim/option_defs.h" #include "nvim/eval/typval_encode.h" #include "nvim/lib/kvec.h" +#include "auto/versiondef.h" /// Helper structure for vim_to_object typedef struct { @@ -766,6 +767,7 @@ Dictionary api_metadata(void) init_function_metadata(&metadata); init_error_type_metadata(&metadata); init_type_metadata(&metadata); + init_api_level_metadata(&metadata); } return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; @@ -825,6 +827,17 @@ static void init_type_metadata(Dictionary *metadata) PUT(*metadata, "types", DICTIONARY_OBJ(types)); } +static void init_api_level_metadata(Dictionary *metadata) +{ + Dictionary version = ARRAY_DICT_INIT; + PUT(version, "current", INTEGER_OBJ(NVIM_API_CURRENT)); + PUT(version, "compatibility", INTEGER_OBJ(NVIM_API_COMPATIBILITY)); +#ifdef NVIM_API_PRERELEASE + PUT(version, "prerelease", BOOLEAN_OBJ(true)); +#endif + + PUT(*metadata, "api_level", DICTIONARY_OBJ(version)); +} /// Creates a deep clone of an object Object copy_object(Object obj) diff --git a/test/functional/api/compatibility_spec.lua b/test/functional/api/compatibility_spec.lua new file mode 100644 index 0000000000..e0fc625b46 --- /dev/null +++ b/test/functional/api/compatibility_spec.lua @@ -0,0 +1,65 @@ + +local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') +local mpack = require('mpack') +local clear, eq, neq = helpers.clear, helpers.eq, helpers.neq + +local read_mpack_file = function(fname) + local fd = io.open(fname, 'rb') + local data = fd:read('*a') + fd:close() + local unpack = mpack.Unpacker() + return unpack(data) +end + +-- ignore metadata in API function spec +local remove_function_metadata = function(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] = '' + end +end + +clear() +local api_level = helpers.call('api_info')['api_level'] + +describe('api compatibility', function() + before_each(clear) + + it("version metadata is sane", function() + local info = helpers.call('api_info') + local current = info['api_level']['current'] + local compatibility = info['api_level']['compatibility'] + neq(current, nil) + neq(compatibility, nil) + assert(current >= compatibility) + end) + + for ver = api_level['compatibility'], api_level['current'] do + local path = 'test/functional/fixtures/api-info/' .. tostring(ver) .. '.mpack' + it('are backwards compatible with api level '..ver, function() + if lfs.attributes(path,"mode") ~= "file" then + pending("No fixture found, skipping test") + return + end + + local old_api = read_mpack_file(path) + local api = helpers.call('api_info') + + for _, fspec in ipairs(old_api['functions']) do + remove_function_metadata(fspec) + for _, fspec_new in ipairs(api['functions']) do + if fspec['name'] == fspec_new['name'] then + remove_function_metadata(fspec_new) + eq(fspec, fspec_new) + end + end + end + end) + end +end) diff --git a/test/functional/eval/api_functions_spec.lua b/test/functional/eval/api_functions_spec.lua index 2ef67e7808..5345c9782d 100644 --- a/test/functional/eval/api_functions_spec.lua +++ b/test/functional/eval/api_functions_spec.lua @@ -106,7 +106,7 @@ describe('api functions', function() it('have metadata accessible with api_info()', function() local api_keys = eval("sort(keys(api_info()))") - eq({'error_types', 'functions', 'types'}, api_keys) + eq({'api_level', 'error_types', 'functions', 'types'}, api_keys) end) it('are highlighted by vim.vim syntax file', function() @@ -144,5 +144,4 @@ describe('api functions', function() ]]) screen:detach() end) - end) diff --git a/test/functional/eval/msgpack_functions_spec.lua b/test/functional/eval/msgpack_functions_spec.lua index 5b87b05652..ae7e1b5609 100644 --- a/test/functional/eval/msgpack_functions_spec.lua +++ b/test/functional/eval/msgpack_functions_spec.lua @@ -460,7 +460,7 @@ describe('msgpackparse() function', function() eval(cmd) eval(cmd) -- do it again (try to force segfault) local api_info = eval(cmd) -- do it again - eq({'error_types', 'functions', 'types'}, api_info) + eq({'api_level', 'error_types', 'functions', 'types'}, api_info) end) it('fails when called with no arguments', function() diff --git a/test/functional/fixtures/api-info/0.mpack b/test/functional/fixtures/api-info/0.mpack Binary files differnew file mode 100644 index 0000000000..75b236a3c1 --- /dev/null +++ b/test/functional/fixtures/api-info/0.mpack |