api: Nvim version, API level #5386

The API level is disconnected from the NVIM version. The API metadata
holds the current API level, and the lowest backwards-compatible level
supported by this instance.

Release 0.1.6 will be the first release reporting the Nvim version and
API level.

    metadata['version'] = {
      major: 0,
      minor: 1,
      patch: 6,
      prerelease: true,
      api_level: 1,
      api_compatible: 0,
    }

The API level may remain unchanged across Neovim releases if the API has
not changed.

When changing the API the CMake variable NVIM_API_PRERELEASE is set to
true, and  NVIM_API_CURRENT/NVIM_API_COMPATIBILITY are incremented
accordingly.

The functional tests check the API table against fixtures of past
versions of Neovim. It compares all the functions in the old table with
the new one, it does ignore some metadata attributes that do not alter
the function signature or were removed since 0.1.5.  Currently the only
fixture is 0.mpack, generated from Neovim 0.1.5 with nvim --api-info.
This commit is contained in:
Rui Abreu Ferreira 2016-09-25 18:46:37 +01:00 committed by Justin M. Keyes
parent 90bf31c742
commit f25797f869
10 changed files with 96 additions and 3 deletions

View File

@ -65,6 +65,12 @@ set(NVIM_VERSION_MINOR 1)
set(NVIM_VERSION_PATCH 6) set(NVIM_VERSION_PATCH 6)
set(NVIM_VERSION_PRERELEASE "-dev") # for package maintainers 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) file(TO_CMAKE_PATH ${CMAKE_CURRENT_LIST_DIR}/.git FORCED_GIT_DIR)
include(GetGitRevisionDescription) include(GetGitRevisionDescription)
get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT) get_git_head_revision(GIT_REFSPEC NVIM_VERSION_COMMIT)

View File

@ -7,6 +7,10 @@
#define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@" #define NVIM_VERSION_PRERELEASE "@NVIM_VERSION_PRERELEASE@"
#cmakedefine NVIM_VERSION_MEDIUM "@NVIM_VERSION_MEDIUM@" #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_CFLAGS "@NVIM_VERSION_CFLAGS@"
#define NVIM_VERSION_BUILD_TYPE "@NVIM_VERSION_BUILD_TYPE@" #define NVIM_VERSION_BUILD_TYPE "@NVIM_VERSION_BUILD_TYPE@"

View File

@ -51,6 +51,7 @@ Tabpage -> enum value kObjectTypeTabpage
Nvim exposes metadata about the API as a Dictionary with the following keys: 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 functions calling signature of the API functions
types The custom handle types defined by Nvim types The custom handle types defined by Nvim
error_types The possible kinds of errors an API function can exit with. error_types The possible kinds of errors an API function can exit with.

View File

@ -168,6 +168,9 @@ API metadata object ~
API clients exist to hide msgpack-rpc details. The 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|): 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 - The "functions" key contains a list of metadata objects for individual
functions. functions.
- Each function metadata object has |rpc-types| information about the return - Each function metadata object has |rpc-types| information about the return

View File

@ -5,6 +5,7 @@
# Steps: # Steps:
# Create the "release" commit: # Create the "release" commit:
# - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE # - CMakeLists.txt: Unset NVIM_VERSION_PRERELEASE
# - CMakeLists.txt: Unset NVIM_API_PRERELEASE
# - Tag the commit. # - Tag the commit.
# Create the "version bump" commit: # Create the "version bump" commit:
# - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev" # - CMakeLists.txt: Set NVIM_VERSION_PRERELEASE to "-dev"
@ -46,6 +47,7 @@ __BUMP_MSG="version bump"
echo "Most recent tag: ${__LAST_TAG}" echo "Most recent tag: ${__LAST_TAG}"
echo "Release version: ${__VERSION}" echo "Release version: ${__VERSION}"
$__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) "-dev"/\1 ""/' CMakeLists.txt $__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}..." echo "Building changelog since ${__LAST_TAG}..."
__CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')" __CHANGELOG="$(./scripts/git-log-pretty-since.sh "$__LAST_TAG" 'vim-patch:\S')"

View File

@ -20,6 +20,7 @@
#include "nvim/option_defs.h" #include "nvim/option_defs.h"
#include "nvim/eval/typval_encode.h" #include "nvim/eval/typval_encode.h"
#include "nvim/lib/kvec.h" #include "nvim/lib/kvec.h"
#include "auto/versiondef.h"
/// Helper structure for vim_to_object /// Helper structure for vim_to_object
typedef struct { typedef struct {
@ -766,6 +767,7 @@ Dictionary api_metadata(void)
init_function_metadata(&metadata); init_function_metadata(&metadata);
init_error_type_metadata(&metadata); init_error_type_metadata(&metadata);
init_type_metadata(&metadata); init_type_metadata(&metadata);
init_api_level_metadata(&metadata);
} }
return copy_object(DICTIONARY_OBJ(metadata)).data.dictionary; 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)); 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 /// Creates a deep clone of an object
Object copy_object(Object obj) Object copy_object(Object obj)

View File

@ -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)

View File

@ -106,7 +106,7 @@ describe('api functions', function()
it('have metadata accessible with api_info()', function() it('have metadata accessible with api_info()', function()
local api_keys = eval("sort(keys(api_info()))") 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) end)
it('are highlighted by vim.vim syntax file', function() it('are highlighted by vim.vim syntax file', function()
@ -144,5 +144,4 @@ describe('api functions', function()
]]) ]])
screen:detach() screen:detach()
end) end)
end) end)

View File

@ -460,7 +460,7 @@ describe('msgpackparse() function', function()
eval(cmd) eval(cmd)
eval(cmd) -- do it again (try to force segfault) eval(cmd) -- do it again (try to force segfault)
local api_info = eval(cmd) -- do it again 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) end)
it('fails when called with no arguments', function() it('fails when called with no arguments', function()

Binary file not shown.