aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--config/versiondef.h.in4
-rw-r--r--runtime/doc/api.txt7
-rw-r--r--runtime/doc/msgpack_rpc.txt2
-rwxr-xr-xscripts/release.sh2
-rw-r--r--src/nvim/api/private/helpers.c2
-rw-r--r--src/nvim/version.c12
-rw-r--r--test/functional/api/version_spec.lua71
-rw-r--r--test/functional/eval/api_functions_spec.lua3
-rw-r--r--test/functional/eval/msgpack_functions_spec.lua2
-rw-r--r--test/functional/fixtures/api_level_0.mpackbin0 -> 7873 bytes
11 files changed, 105 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4aa6a2de32..75c0bd0ecd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -59,12 +59,17 @@ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY
STRINGS "Debug" "Dev" "Release" "MinSizeRel" "RelWithDebInfo")
# If not in a git repo (e.g., a tarball) these tokens define the complete
-# version string, else it is combined with the result of `git describe`.
+# version string, else they are combined with the result of `git describe`.
set(NVIM_VERSION_MAJOR 0)
set(NVIM_VERSION_MINOR 1)
set(NVIM_VERSION_PATCH 6)
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_COMPAT 0) # Adjust this after a _breaking_ API change.
+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..b9565735b3 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_LEVEL @NVIM_API_LEVEL@
+#define NVIM_API_LEVEL_COMPAT @NVIM_API_LEVEL_COMPAT@
+#define NVIM_API_PRERELEASE @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..976b852bdb 100644
--- a/runtime/doc/api.txt
+++ b/runtime/doc/api.txt
@@ -51,9 +51,10 @@ Tabpage -> enum value kObjectTypeTabpage
Nvim exposes metadata about the API as a Dictionary with the following keys:
-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.
+version Nvim version, API level/compatibility
+functions API function signatures
+types Custom handle types defined by Nvim
+error_types Possible error types returned by API functions
This metadata is mostly useful for external programs accessing the API via
RPC, see |rpc-api|.
diff --git a/runtime/doc/msgpack_rpc.txt b/runtime/doc/msgpack_rpc.txt
index b3fed9e756..757f5574d4 100644
--- a/runtime/doc/msgpack_rpc.txt
+++ b/runtime/doc/msgpack_rpc.txt
@@ -168,6 +168,8 @@ 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 "version" key contains the Nvim version, API level, and API
+ backwards-compatibility level.
- 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..208c3b53c8 100644
--- a/src/nvim/api/private/helpers.c
+++ b/src/nvim/api/private/helpers.c
@@ -18,6 +18,7 @@
#include "nvim/map.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
+#include "nvim/version.h"
#include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h"
@@ -763,6 +764,7 @@ Dictionary api_metadata(void)
static Dictionary metadata = ARRAY_DICT_INIT;
if (!metadata.size) {
+ PUT(metadata, "version", DICTIONARY_OBJ(version_dict()));
init_function_metadata(&metadata);
init_error_type_metadata(&metadata);
init_type_metadata(&metadata);
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 498afa9656..7ab8c84569 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -7,6 +7,7 @@
#include <assert.h>
#include <limits.h>
+#include "nvim/api/private/helpers.h"
#include "nvim/vim.h"
#include "nvim/ascii.h"
#include "nvim/iconv.h"
@@ -2514,6 +2515,17 @@ bool has_vim_patch(int n)
return false;
}
+Dictionary version_dict(void) {
+ Dictionary d = ARRAY_DICT_INIT;
+ PUT(d, "major", INTEGER_OBJ(NVIM_VERSION_MAJOR));
+ PUT(d, "minor", INTEGER_OBJ(NVIM_VERSION_MINOR));
+ PUT(d, "patch", INTEGER_OBJ(NVIM_VERSION_PATCH));
+ PUT(d, "api_level", INTEGER_OBJ(NVIM_API_LEVEL));
+ PUT(d, "api_compatible", INTEGER_OBJ(NVIM_API_LEVEL_COMPAT));
+ PUT(d, "api_prerelease", BOOLEAN_OBJ(NVIM_API_PRERELEASE));
+ return d;
+}
+
void ex_version(exarg_T *eap)
{
// Ignore a ":version 9.99" command.
diff --git a/test/functional/api/version_spec.lua b/test/functional/api/version_spec.lua
new file mode 100644
index 0000000000..3efd00ddbe
--- /dev/null
+++ b/test/functional/api/version_spec.lua
@@ -0,0 +1,71 @@
+local helpers = require('test.functional.helpers')(after_each)
+local mpack = require('mpack')
+local clear, funcs, eq = helpers.clear, helpers.funcs, helpers.eq
+
+local function read_mpack_file(fname)
+ local fd = io.open(fname, 'rb')
+ 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)
+
+ it("returns API level", function()
+ local version = helpers.call('api_info')['version']
+ local current = version['api_level']
+ local compat = version['api_compatible']
+ eq("number", type(current))
+ eq("number", type(compat))
+ assert(current >= compat)
+ end)
+
+ it("returns Nvim version", function()
+ local version = helpers.call('api_info')['version']
+ local major = version['major']
+ local minor = version['minor']
+ local patch = version['patch']
+ eq("number", type(major))
+ eq("number", type(minor))
+ eq("number", type(patch))
+ eq(1, funcs.has("nvim-"..major.."."..minor.."."..patch))
+ eq(0, funcs.has("nvim-"..major.."."..minor.."."..(patch + 1)))
+ eq(0, funcs.has("nvim-"..major.."."..(minor + 1).."."..patch))
+ eq(0, funcs.has("nvim-"..(major + 1).."."..minor.."."..patch))
+ 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))
+ 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..21dd228145 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({'error_types', 'functions', 'types', 'version'}, 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..f11c08de05 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({'error_types', 'functions', 'types', 'version'}, api_info)
end)
it('fails when called with no arguments', function()
diff --git a/test/functional/fixtures/api_level_0.mpack b/test/functional/fixtures/api_level_0.mpack
new file mode 100644
index 0000000000..75b236a3c1
--- /dev/null
+++ b/test/functional/fixtures/api_level_0.mpack
Binary files differ