mirror of
https://github.com/neovim/neovim.git
synced 2025-09-12 22:38:16 +00:00
Merge remote-tracking branch 'origin/master' into qftf
This commit is contained in:
@@ -423,10 +423,6 @@ function! s:check_python(version) abort
|
|||||||
\ ' This could lead to confusing error messages.')
|
\ ' This could lead to confusing error messages.')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if a:version == 3 && str2float(pyversion) < 3.3
|
|
||||||
call health#report_warn('Python 3.3+ is recommended.')
|
|
||||||
endif
|
|
||||||
|
|
||||||
call health#report_info('Python version: ' . pyversion)
|
call health#report_info('Python version: ' . pyversion)
|
||||||
|
|
||||||
if s:is_bad_response(status)
|
if s:is_bad_response(status)
|
||||||
|
@@ -297,26 +297,30 @@ local function pick_call_hierarchy_item(call_hierarchy_items)
|
|||||||
return choice
|
return choice
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function call_hierarchy(method)
|
||||||
|
local params = util.make_position_params()
|
||||||
|
request('textDocument/prepareCallHierarchy', params, function(err, _, result)
|
||||||
|
if err then
|
||||||
|
vim.notify(err.message, vim.log.levels.WARN)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
||||||
|
vim.lsp.buf_request(0, method, { item = call_hierarchy_item })
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
--- Lists all the call sites of the symbol under the cursor in the
|
--- Lists all the call sites of the symbol under the cursor in the
|
||||||
--- |quickfix| window. If the symbol can resolve to multiple
|
--- |quickfix| window. If the symbol can resolve to multiple
|
||||||
--- items, the user can pick one in the |inputlist|.
|
--- items, the user can pick one in the |inputlist|.
|
||||||
function M.incoming_calls()
|
function M.incoming_calls()
|
||||||
local params = util.make_position_params()
|
call_hierarchy('callHierarchy/incomingCalls')
|
||||||
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
|
|
||||||
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
|
||||||
vim.lsp.buf_request(0, 'callHierarchy/incomingCalls', { item = call_hierarchy_item })
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Lists all the items that are called by the symbol under the
|
--- Lists all the items that are called by the symbol under the
|
||||||
--- cursor in the |quickfix| window. If the symbol can resolve to
|
--- cursor in the |quickfix| window. If the symbol can resolve to
|
||||||
--- multiple items, the user can pick one in the |inputlist|.
|
--- multiple items, the user can pick one in the |inputlist|.
|
||||||
function M.outgoing_calls()
|
function M.outgoing_calls()
|
||||||
local params = util.make_position_params()
|
call_hierarchy('callHierarchy/outgoingCalls')
|
||||||
request('textDocument/prepareCallHierarchy', params, function(_, _, result)
|
|
||||||
local call_hierarchy_item = pick_call_hierarchy_item(result)
|
|
||||||
vim.lsp.buf_request(0, 'callHierarchy/outgoingCalls', { item = call_hierarchy_item })
|
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- List workspace folders.
|
--- List workspace folders.
|
||||||
|
@@ -566,13 +566,15 @@ end
|
|||||||
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
--@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion
|
||||||
local function get_completion_word(item)
|
local function get_completion_word(item)
|
||||||
if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
|
if item.textEdit ~= nil and item.textEdit.newText ~= nil and item.textEdit.newText ~= "" then
|
||||||
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
|
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
|
||||||
|
if insert_text_format == "PlainText" or insert_text_format == nil then
|
||||||
return item.textEdit.newText
|
return item.textEdit.newText
|
||||||
else
|
else
|
||||||
return M.parse_snippet(item.textEdit.newText)
|
return M.parse_snippet(item.textEdit.newText)
|
||||||
end
|
end
|
||||||
elseif item.insertText ~= nil and item.insertText ~= "" then
|
elseif item.insertText ~= nil and item.insertText ~= "" then
|
||||||
if protocol.InsertTextFormat[item.insertTextFormat] == "PlainText" then
|
local insert_text_format = protocol.InsertTextFormat[item.insertTextFormat]
|
||||||
|
if insert_text_format == "PlainText" or insert_text_format == nil then
|
||||||
return item.insertText
|
return item.insertText
|
||||||
else
|
else
|
||||||
return M.parse_snippet(item.insertText)
|
return M.parse_snippet(item.insertText)
|
||||||
@@ -914,23 +916,6 @@ function M.make_floating_popup_options(width, height, opts)
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local function _should_add_to_tagstack(new_item)
|
|
||||||
local stack = vim.fn.gettagstack()
|
|
||||||
|
|
||||||
-- Check if we're at the bottom of the tagstack.
|
|
||||||
if stack.curidx <= 1 then return true end
|
|
||||||
|
|
||||||
local top_item = stack.items[stack.curidx-1]
|
|
||||||
|
|
||||||
-- Check if the item at the top of the tagstack is exactly the
|
|
||||||
-- same as the one we want to push.
|
|
||||||
if top_item.tagname ~= new_item.tagname then return true end
|
|
||||||
for i, v in ipairs(top_item.from) do
|
|
||||||
if v ~= new_item.from[i] then return true end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Jumps to a location.
|
--- Jumps to a location.
|
||||||
---
|
---
|
||||||
--@param location (`Location`|`LocationLink`)
|
--@param location (`Location`|`LocationLink`)
|
||||||
@@ -939,36 +924,22 @@ function M.jump_to_location(location)
|
|||||||
-- location may be Location or LocationLink
|
-- location may be Location or LocationLink
|
||||||
local uri = location.uri or location.targetUri
|
local uri = location.uri or location.targetUri
|
||||||
if uri == nil then return end
|
if uri == nil then return end
|
||||||
|
local bufnr = vim.uri_to_bufnr(uri)
|
||||||
local from_bufnr = vim.fn.bufnr('%')
|
|
||||||
local from = {from_bufnr, vim.fn.line('.'), vim.fn.col('.'), 0}
|
|
||||||
local item = {tagname=vim.fn.expand('<cword>'), from=from}
|
|
||||||
|
|
||||||
-- Save position in jumplist
|
-- Save position in jumplist
|
||||||
vim.cmd("mark '")
|
vim.cmd "normal! m'"
|
||||||
|
|
||||||
|
-- Push a new item into tagstack
|
||||||
|
local from = {vim.fn.bufnr('%'), vim.fn.line('.'), vim.fn.col('.'), 0}
|
||||||
|
local items = {{tagname=vim.fn.expand('<cword>'), from=from}}
|
||||||
|
vim.fn.settagstack(vim.fn.win_getid(), {items=items}, 't')
|
||||||
|
|
||||||
--- Jump to new location (adjusting for UTF-16 encoding of characters)
|
--- Jump to new location (adjusting for UTF-16 encoding of characters)
|
||||||
local bufnr = vim.uri_to_bufnr(uri)
|
|
||||||
api.nvim_set_current_buf(bufnr)
|
api.nvim_set_current_buf(bufnr)
|
||||||
api.nvim_buf_set_option(0, 'buflisted', true)
|
api.nvim_buf_set_option(0, 'buflisted', true)
|
||||||
local range = location.range or location.targetSelectionRange
|
local range = location.range or location.targetSelectionRange
|
||||||
local row = range.start.line
|
local row = range.start.line
|
||||||
local col = get_line_byte_from_position(0, range.start)
|
local col = get_line_byte_from_position(0, range.start)
|
||||||
-- This prevents the tagstack to be filled with items that provide
|
|
||||||
-- no motion when CTRL-T is pressed because they're both the source
|
|
||||||
-- and the destination.
|
|
||||||
local motionless =
|
|
||||||
bufnr == from_bufnr and
|
|
||||||
row+1 == from[2] and col+1 == from[3]
|
|
||||||
if not motionless and _should_add_to_tagstack(item) then
|
|
||||||
local winid = vim.fn.win_getid()
|
|
||||||
local items = {item}
|
|
||||||
vim.fn.settagstack(winid, {items=items}, 't')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Jump to new location
|
|
||||||
api.nvim_win_set_cursor(0, {row + 1, col})
|
api.nvim_win_set_cursor(0, {row + 1, col})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -578,7 +578,7 @@ list_missing_previous_vimpatches_for_patch() {
|
|||||||
|
|
||||||
list_missing_previous_vimpatches_for_patch() {
|
list_missing_previous_vimpatches_for_patch() {
|
||||||
local for_vim_patch="${1}"
|
local for_vim_patch="${1}"
|
||||||
local vim_commit vim_tag
|
local vim_commit vim_tag
|
||||||
assign_commit_details "${for_vim_patch}"
|
assign_commit_details "${for_vim_patch}"
|
||||||
|
|
||||||
local file
|
local file
|
||||||
@@ -593,18 +593,20 @@ list_missing_previous_vimpatches_for_patch() {
|
|||||||
if [[ -z "${vim_tag}" ]]; then
|
if [[ -z "${vim_tag}" ]]; then
|
||||||
printf 'NOTE: "%s" is not a Vim tag - listing all oldest missing patches\n' "${for_vim_patch}" >&2
|
printf 'NOTE: "%s" is not a Vim tag - listing all oldest missing patches\n' "${for_vim_patch}" >&2
|
||||||
fi
|
fi
|
||||||
for fname in "${fnames[@]}"; do
|
for fname in "${fnames[@]}"; do
|
||||||
i=$(( i+1 ))
|
i=$(( i+1 ))
|
||||||
printf '[%.*d/%d] %s: ' "${#n}" "$i" "$n" "$fname"
|
printf '[%.*d/%d] %s: ' "${#n}" "$i" "$n" "$fname"
|
||||||
|
|
||||||
local -a missing_vim_patches=()
|
|
||||||
_set_missing_vimpatches 1 -- "${fname}"
|
|
||||||
|
|
||||||
set +u # Avoid "unbound variable" with bash < 4.4 below.
|
|
||||||
|
|
||||||
if [[ -z "${missing_vim_commit_info}" ]]; then
|
local -a missing_vim_patches=()
|
||||||
|
_set_missing_vimpatches 1 -- "${fname}"
|
||||||
|
|
||||||
|
set +u # Avoid "unbound variable" with bash < 4.4 below.
|
||||||
|
for missing_vim_commit_info in "${missing_vim_patches[@]}"; do
|
||||||
|
if [[ -z "${missing_vim_commit_info}" ]]; then
|
||||||
|
printf -- "-\r"
|
||||||
|
else
|
||||||
printf -- "-\r"
|
printf -- "-\r"
|
||||||
else
|
local missing_vim_commit="${missing_vim_commit_info%%:*}"
|
||||||
if [[ -z "${vim_tag}" ]] || [[ "${missing_vim_commit}" < "${vim_tag}" ]]; then
|
if [[ -z "${vim_tag}" ]] || [[ "${missing_vim_commit}" < "${vim_tag}" ]]; then
|
||||||
printf -- "%s\n" "$missing_vim_commit_info"
|
printf -- "%s\n" "$missing_vim_commit_info"
|
||||||
missing_list+=("$missing_vim_commit_info")
|
missing_list+=("$missing_vim_commit_info")
|
||||||
|
@@ -177,6 +177,29 @@ Object dict_get_value(dict_T *dict, String key, Error *err)
|
|||||||
return vim_to_object(&di->di_tv);
|
return vim_to_object(&di->di_tv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dictitem_T *dict_check_writable(dict_T *dict, String key, bool del, Error *err)
|
||||||
|
{
|
||||||
|
dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
|
||||||
|
|
||||||
|
if (di != NULL) {
|
||||||
|
if (di->di_flags & DI_FLAGS_RO) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Key is read-only: %s", key.data);
|
||||||
|
} else if (di->di_flags & DI_FLAGS_LOCK) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Key is locked: %s", key.data);
|
||||||
|
} else if (del && (di->di_flags & DI_FLAGS_FIX)) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Key is fixed: %s", key.data);
|
||||||
|
}
|
||||||
|
} else if (dict->dv_lock) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Dictionary is locked");
|
||||||
|
} else if (key.size == 0) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Key name is empty");
|
||||||
|
} else if (key.size > INT_MAX) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Key name is too long");
|
||||||
|
}
|
||||||
|
|
||||||
|
return di;
|
||||||
|
}
|
||||||
|
|
||||||
/// Set a value in a scope dict. Objects are recursively expanded into their
|
/// Set a value in a scope dict. Objects are recursively expanded into their
|
||||||
/// vimscript equivalents.
|
/// vimscript equivalents.
|
||||||
///
|
///
|
||||||
@@ -192,27 +215,9 @@ Object dict_set_var(dict_T *dict, String key, Object value, bool del,
|
|||||||
bool retval, Error *err)
|
bool retval, Error *err)
|
||||||
{
|
{
|
||||||
Object rv = OBJECT_INIT;
|
Object rv = OBJECT_INIT;
|
||||||
dictitem_T *di = tv_dict_find(dict, key.data, (ptrdiff_t)key.size);
|
dictitem_T *di = dict_check_writable(dict, key, del, err);
|
||||||
|
|
||||||
if (di != NULL) {
|
if (ERROR_SET(err)) {
|
||||||
if (di->di_flags & DI_FLAGS_RO) {
|
|
||||||
api_set_error(err, kErrorTypeException, "Key is read-only: %s", key.data);
|
|
||||||
return rv;
|
|
||||||
} else if (di->di_flags & DI_FLAGS_LOCK) {
|
|
||||||
api_set_error(err, kErrorTypeException, "Key is locked: %s", key.data);
|
|
||||||
return rv;
|
|
||||||
} else if (del && (di->di_flags & DI_FLAGS_FIX)) {
|
|
||||||
api_set_error(err, kErrorTypeException, "Key is fixed: %s", key.data);
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
} else if (dict->dv_lock) {
|
|
||||||
api_set_error(err, kErrorTypeException, "Dictionary is locked");
|
|
||||||
return rv;
|
|
||||||
} else if (key.size == 0) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "Key name is empty");
|
|
||||||
return rv;
|
|
||||||
} else if (key.size > INT_MAX) {
|
|
||||||
api_set_error(err, kErrorTypeValidation, "Key name is too long");
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1619,7 +1619,7 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty,
|
|||||||
char buf[IOSIZE];
|
char buf[IOSIZE];
|
||||||
|
|
||||||
// apply :filter /pat/ to variable name
|
// apply :filter /pat/ to variable name
|
||||||
xstrlcpy(buf, prefix, IOSIZE - 1);
|
xstrlcpy(buf, prefix, IOSIZE);
|
||||||
xstrlcat(buf, (char *)di->di_key, IOSIZE);
|
xstrlcat(buf, (char *)di->di_key, IOSIZE);
|
||||||
if (message_filtered((char_u *)buf)) {
|
if (message_filtered((char_u *)buf)) {
|
||||||
continue;
|
continue;
|
||||||
|
@@ -1850,15 +1850,30 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr)
|
|||||||
ptrdiff_t len = end - str;
|
ptrdiff_t len = end - str;
|
||||||
assert(len > 0);
|
assert(len > 0);
|
||||||
const char * value = str + len + 1;
|
const char * value = str + len + 1;
|
||||||
if (tv_dict_find(rettv->vval.v_dict, str, len) != NULL) {
|
|
||||||
|
char c = env[i][len];
|
||||||
|
env[i][len] = NUL;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
// Upper-case all the keys for Windows so we can detect duplicates
|
||||||
|
char *const key = strcase_save(str, true);
|
||||||
|
#else
|
||||||
|
char *const key = xstrdup(str);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
env[i][len] = c;
|
||||||
|
|
||||||
|
if (tv_dict_find(rettv->vval.v_dict, key, len) != NULL) {
|
||||||
// Since we're traversing from the end of the env block to the front, any
|
// Since we're traversing from the end of the env block to the front, any
|
||||||
// duplicate names encountered should be ignored. This preserves the
|
// duplicate names encountered should be ignored. This preserves the
|
||||||
// semantics of env vars defined later in the env block taking precedence.
|
// semantics of env vars defined later in the env block taking precedence.
|
||||||
|
xfree(key);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
tv_dict_add_str(rettv->vval.v_dict,
|
tv_dict_add_str(rettv->vval.v_dict,
|
||||||
str, len,
|
key, len,
|
||||||
value);
|
value);
|
||||||
|
xfree(key);
|
||||||
}
|
}
|
||||||
os_free_fullenv(env);
|
os_free_fullenv(env);
|
||||||
}
|
}
|
||||||
@@ -5096,7 +5111,21 @@ static dict_T *create_environment(const dictitem_T *job_env,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (job_env) {
|
if (job_env) {
|
||||||
|
#ifdef WIN32
|
||||||
|
TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, {
|
||||||
|
// Always use upper-case keys for Windows so we detect duplicate keys
|
||||||
|
char *const key = strcase_save((const char *)var->di_key, true);
|
||||||
|
size_t len = strlen(key);
|
||||||
|
dictitem_T *dv = tv_dict_find(env, key, len);
|
||||||
|
if (dv) {
|
||||||
|
tv_dict_item_remove(env, dv);
|
||||||
|
}
|
||||||
|
tv_dict_add_str(env, key, len, tv_get_string(&var->di_tv));
|
||||||
|
xfree(key);
|
||||||
|
});
|
||||||
|
#else
|
||||||
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
|
tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pty) {
|
if (pty) {
|
||||||
|
@@ -1772,7 +1772,7 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|
|||||||
// count, it's a buffer name.
|
// count, it's a buffer name.
|
||||||
///
|
///
|
||||||
if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg)
|
if ((ea.argt & EX_COUNT) && ascii_isdigit(*ea.arg)
|
||||||
&& (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg)) == NUL
|
&& (!(ea.argt & EX_BUFNAME) || *(p = skipdigits(ea.arg + 1)) == NUL
|
||||||
|| ascii_iswhite(*p))) {
|
|| ascii_iswhite(*p))) {
|
||||||
n = getdigits_long(&ea.arg, false, -1);
|
n = getdigits_long(&ea.arg, false, -1);
|
||||||
ea.arg = skipwhite(ea.arg);
|
ea.arg = skipwhite(ea.arg);
|
||||||
@@ -2790,15 +2790,18 @@ static struct cmdmod {
|
|||||||
*/
|
*/
|
||||||
int modifier_len(char_u *cmd)
|
int modifier_len(char_u *cmd)
|
||||||
{
|
{
|
||||||
int i, j;
|
|
||||||
char_u *p = cmd;
|
char_u *p = cmd;
|
||||||
|
|
||||||
if (ascii_isdigit(*cmd))
|
if (ascii_isdigit(*cmd)) {
|
||||||
p = skipwhite(skipdigits(cmd));
|
p = skipwhite(skipdigits(cmd + 1));
|
||||||
for (i = 0; i < (int)ARRAY_SIZE(cmdmods); ++i) {
|
}
|
||||||
for (j = 0; p[j] != NUL; ++j)
|
for (int i = 0; i < (int)ARRAY_SIZE(cmdmods); i++) {
|
||||||
if (p[j] != cmdmods[i].name[j])
|
int j;
|
||||||
|
for (j = 0; p[j] != NUL; j++) {
|
||||||
|
if (p[j] != cmdmods[i].name[j]) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (j >= cmdmods[i].minlen
|
if (j >= cmdmods[i].minlen
|
||||||
&& !ASCII_ISALPHA(p[j])
|
&& !ASCII_ISALPHA(p[j])
|
||||||
&& (p == cmd || cmdmods[i].has_count)) {
|
&& (p == cmd || cmdmods[i].has_count)) {
|
||||||
|
@@ -471,6 +471,15 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL
|
|||||||
lua_pushcfunction(lstate, &nlua_wait);
|
lua_pushcfunction(lstate, &nlua_wait);
|
||||||
lua_setfield(lstate, -2, "wait");
|
lua_setfield(lstate, -2, "wait");
|
||||||
|
|
||||||
|
// _getvar
|
||||||
|
lua_pushcfunction(lstate, &nlua_getvar);
|
||||||
|
lua_setfield(lstate, -2, "_getvar");
|
||||||
|
|
||||||
|
// _setvar
|
||||||
|
lua_pushcfunction(lstate, &nlua_setvar);
|
||||||
|
lua_setfield(lstate, -2, "_setvar");
|
||||||
|
|
||||||
|
|
||||||
// vim.loop
|
// vim.loop
|
||||||
luv_set_loop(lstate, &main_loop.uv);
|
luv_set_loop(lstate, &main_loop.uv);
|
||||||
luv_set_callback(lstate, nlua_luv_cfpcall);
|
luv_set_callback(lstate, nlua_luv_cfpcall);
|
||||||
@@ -870,6 +879,109 @@ check_err:
|
|||||||
return request ? 1 : 0;
|
return request ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static dict_T *nlua_get_var_scope(lua_State *lstate) {
|
||||||
|
const char *scope = luaL_checkstring(lstate, 1);
|
||||||
|
handle_T handle = (handle_T)luaL_checkinteger(lstate, 2);
|
||||||
|
dict_T *dict = NULL;
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
if (strequal(scope, "g")) {
|
||||||
|
dict = &globvardict;
|
||||||
|
} else if (strequal(scope, "v")) {
|
||||||
|
dict = &vimvardict;
|
||||||
|
} else if (strequal(scope, "b")) {
|
||||||
|
buf_T *buf = find_buffer_by_handle(handle, &err);
|
||||||
|
if (buf) {
|
||||||
|
dict = buf->b_vars;
|
||||||
|
}
|
||||||
|
} else if (strequal(scope, "w")) {
|
||||||
|
win_T *win = find_window_by_handle(handle, &err);
|
||||||
|
if (win) {
|
||||||
|
dict = win->w_vars;
|
||||||
|
}
|
||||||
|
} else if (strequal(scope, "t")) {
|
||||||
|
tabpage_T *tabpage = find_tab_by_handle(handle, &err);
|
||||||
|
if (tabpage) {
|
||||||
|
dict = tabpage->tp_vars;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
luaL_error(lstate, "invalid scope", err.msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ERROR_SET(&err)) {
|
||||||
|
luaL_error(lstate, "FAIL: %s", err.msg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int nlua_getvar(lua_State *lstate)
|
||||||
|
{
|
||||||
|
// non-local return if not found
|
||||||
|
dict_T *dict = nlua_get_var_scope(lstate);
|
||||||
|
size_t len;
|
||||||
|
const char *name = luaL_checklstring(lstate, 3, &len);
|
||||||
|
|
||||||
|
dictitem_T *di = tv_dict_find(dict, name, (ptrdiff_t)len);
|
||||||
|
if (di == NULL) {
|
||||||
|
return 0; // nil
|
||||||
|
}
|
||||||
|
nlua_push_typval(lstate, &di->di_tv, false);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nlua_setvar(lua_State *lstate)
|
||||||
|
{
|
||||||
|
// non-local return if not found
|
||||||
|
dict_T *dict = nlua_get_var_scope(lstate);
|
||||||
|
String key;
|
||||||
|
key.data = (char *)luaL_checklstring(lstate, 3, &key.size);
|
||||||
|
|
||||||
|
bool del = (lua_gettop(lstate) < 4) || lua_isnil(lstate, 4);
|
||||||
|
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
dictitem_T *di = dict_check_writable(dict, key, del, &err);
|
||||||
|
if (ERROR_SET(&err)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (del) {
|
||||||
|
// Delete the key
|
||||||
|
if (di == NULL) {
|
||||||
|
// Doesn't exist, nothing to do
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
// Delete the entry
|
||||||
|
tv_dict_item_remove(dict, di);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Update the key
|
||||||
|
typval_T tv;
|
||||||
|
|
||||||
|
// Convert the lua value to a vimscript type in the temporary variable
|
||||||
|
lua_pushvalue(lstate, 4);
|
||||||
|
if (!nlua_pop_typval(lstate, &tv)) {
|
||||||
|
return luaL_error(lstate, "Couldn't convert lua value");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (di == NULL) {
|
||||||
|
// Need to create an entry
|
||||||
|
di = tv_dict_item_alloc_len(key.data, key.size);
|
||||||
|
tv_dict_add(dict, di);
|
||||||
|
} else {
|
||||||
|
// Clear the old value
|
||||||
|
tv_clear(&di->di_tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the value
|
||||||
|
tv_copy(&tv, &di->di_tv);
|
||||||
|
// Clear the temporary variable
|
||||||
|
tv_clear(&tv);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int nlua_nil_tostring(lua_State *lstate)
|
static int nlua_nil_tostring(lua_State *lstate)
|
||||||
{
|
{
|
||||||
lua_pushstring(lstate, "vim.NIL");
|
lua_pushstring(lstate, "vim.NIL");
|
||||||
|
@@ -341,32 +341,24 @@ do
|
|||||||
end
|
end
|
||||||
return setmetatable({}, mt)
|
return setmetatable({}, mt)
|
||||||
end
|
end
|
||||||
local function pcall_ret(status, ...)
|
local function make_dict_accessor(scope)
|
||||||
if status then return ... end
|
validate {
|
||||||
end
|
scope = {scope, 's'};
|
||||||
local function nil_wrap(fn)
|
}
|
||||||
return function(...)
|
local mt = {}
|
||||||
return pcall_ret(pcall(fn, ...))
|
function mt:__newindex(k, v)
|
||||||
|
return vim._setvar(scope, 0, k, v)
|
||||||
end
|
end
|
||||||
|
function mt:__index(k)
|
||||||
|
return vim._getvar(scope, 0, k)
|
||||||
|
end
|
||||||
|
return setmetatable({}, mt)
|
||||||
end
|
end
|
||||||
|
vim.g = make_dict_accessor('g')
|
||||||
vim.b = make_meta_accessor(
|
vim.v = make_dict_accessor('v')
|
||||||
nil_wrap(function(v) return a.nvim_buf_get_var(0, v) end),
|
vim.b = make_dict_accessor('b')
|
||||||
function(v, k) return a.nvim_buf_set_var(0, v, k) end,
|
vim.w = make_dict_accessor('w')
|
||||||
function(v) return a.nvim_buf_del_var(0, v) end
|
vim.t = make_dict_accessor('t')
|
||||||
)
|
|
||||||
vim.w = make_meta_accessor(
|
|
||||||
nil_wrap(function(v) return a.nvim_win_get_var(0, v) end),
|
|
||||||
function(v, k) return a.nvim_win_set_var(0, v, k) end,
|
|
||||||
function(v) return a.nvim_win_del_var(0, v) end
|
|
||||||
)
|
|
||||||
vim.t = make_meta_accessor(
|
|
||||||
nil_wrap(function(v) return a.nvim_tabpage_get_var(0, v) end),
|
|
||||||
function(v, k) return a.nvim_tabpage_set_var(0, v, k) end,
|
|
||||||
function(v) return a.nvim_tabpage_del_var(0, v) end
|
|
||||||
)
|
|
||||||
vim.g = make_meta_accessor(nil_wrap(a.nvim_get_var), a.nvim_set_var, a.nvim_del_var)
|
|
||||||
vim.v = make_meta_accessor(nil_wrap(a.nvim_get_vvar), a.nvim_set_vvar)
|
|
||||||
vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
|
vim.o = make_meta_accessor(a.nvim_get_option, a.nvim_set_option)
|
||||||
|
|
||||||
local function getenv(k)
|
local function getenv(k)
|
||||||
|
@@ -1069,7 +1069,7 @@ char_u *get_menu_names(expand_T *xp, int idx)
|
|||||||
#define TBUFFER_LEN 256
|
#define TBUFFER_LEN 256
|
||||||
static char_u tbuffer[TBUFFER_LEN]; /*hack*/
|
static char_u tbuffer[TBUFFER_LEN]; /*hack*/
|
||||||
char_u *str;
|
char_u *str;
|
||||||
static int should_advance = FALSE;
|
static bool should_advance = false;
|
||||||
|
|
||||||
if (idx == 0) { /* first call: start at first item */
|
if (idx == 0) { /* first call: start at first item */
|
||||||
menu = expand_menu;
|
menu = expand_menu;
|
||||||
@@ -1089,12 +1089,13 @@ char_u *get_menu_names(expand_T *xp, int idx)
|
|||||||
|
|
||||||
if (menu->modes & expand_modes) {
|
if (menu->modes & expand_modes) {
|
||||||
if (menu->children != NULL) {
|
if (menu->children != NULL) {
|
||||||
if (should_advance)
|
if (should_advance) {
|
||||||
STRLCPY(tbuffer, menu->en_dname, TBUFFER_LEN - 1);
|
STRLCPY(tbuffer, menu->en_dname, TBUFFER_LEN);
|
||||||
else {
|
} else {
|
||||||
STRLCPY(tbuffer, menu->dname, TBUFFER_LEN - 1);
|
STRLCPY(tbuffer, menu->dname, TBUFFER_LEN);
|
||||||
if (menu->en_dname == NULL)
|
if (menu->en_dname == NULL) {
|
||||||
should_advance = TRUE;
|
should_advance = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* hack on menu separators: use a 'magic' char for the separator
|
/* hack on menu separators: use a 'magic' char for the separator
|
||||||
* so that '.' in names gets escaped properly */
|
* so that '.' in names gets escaped properly */
|
||||||
@@ -1105,8 +1106,9 @@ char_u *get_menu_names(expand_T *xp, int idx)
|
|||||||
str = menu->en_dname;
|
str = menu->en_dname;
|
||||||
else {
|
else {
|
||||||
str = menu->dname;
|
str = menu->dname;
|
||||||
if (menu->en_dname == NULL)
|
if (menu->en_dname == NULL) {
|
||||||
should_advance = TRUE;
|
should_advance = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
|
@@ -1942,10 +1942,12 @@ void do_pending_operator(cmdarg_T *cap, int old_col, bool gui_yank)
|
|||||||
case OP_FORMAT:
|
case OP_FORMAT:
|
||||||
if (*curbuf->b_p_fex != NUL) {
|
if (*curbuf->b_p_fex != NUL) {
|
||||||
op_formatexpr(oap); // use expression
|
op_formatexpr(oap); // use expression
|
||||||
} else if (*p_fp != NUL || *curbuf->b_p_fp != NUL) {
|
|
||||||
op_colon(oap); // use external command
|
|
||||||
} else {
|
} else {
|
||||||
op_format(oap, false); // use internal function
|
if (*p_fp != NUL || *curbuf->b_p_fp != NUL) {
|
||||||
|
op_colon(oap); // use external command
|
||||||
|
} else {
|
||||||
|
op_format(oap, false); // use internal function
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@@ -65,7 +65,7 @@ FileComparison path_full_compare(char_u *const s1, char_u *const s2,
|
|||||||
if (expandenv) {
|
if (expandenv) {
|
||||||
expand_env(s1, exp1, MAXPATHL);
|
expand_env(s1, exp1, MAXPATHL);
|
||||||
} else {
|
} else {
|
||||||
xstrlcpy((char *)exp1, (const char *)s1, MAXPATHL - 1);
|
xstrlcpy((char *)exp1, (const char *)s1, MAXPATHL);
|
||||||
}
|
}
|
||||||
bool id_ok_1 = os_fileid((char *)exp1, &file_id_1);
|
bool id_ok_1 = os_fileid((char *)exp1, &file_id_1);
|
||||||
bool id_ok_2 = os_fileid((char *)s2, &file_id_2);
|
bool id_ok_2 = os_fileid((char *)s2, &file_id_2);
|
||||||
|
@@ -3010,7 +3010,7 @@ static int find_extra(char_u **pp)
|
|||||||
// Repeat for addresses separated with ';'
|
// Repeat for addresses separated with ';'
|
||||||
for (;; ) {
|
for (;; ) {
|
||||||
if (ascii_isdigit(*str)) {
|
if (ascii_isdigit(*str)) {
|
||||||
str = skipdigits(str);
|
str = skipdigits(str + 1);
|
||||||
} else if (*str == '/' || *str == '?') {
|
} else if (*str == '/' || *str == '?') {
|
||||||
str = skip_regexp(str + 1, *str, false, NULL);
|
str = skip_regexp(str + 1, *str, false, NULL);
|
||||||
if (*str != first_char) {
|
if (*str != first_char) {
|
||||||
|
@@ -781,17 +781,66 @@ func Test_diff_lastline()
|
|||||||
bwipe!
|
bwipe!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func WriteDiffFiles(buf, list1, list2)
|
||||||
|
call writefile(a:list1, 'Xfile1')
|
||||||
|
call writefile(a:list2, 'Xfile2')
|
||||||
|
if a:buf
|
||||||
|
call term_sendkeys(a:buf, ":checktime\<CR>")
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
" Verify a screendump with both the internal and external diff.
|
||||||
|
func VerifyBoth(buf, dumpfile, extra)
|
||||||
|
" trailing : for leaving the cursor on the command line
|
||||||
|
for cmd in [":set diffopt=filler" . a:extra . "\<CR>:", ":set diffopt+=internal\<CR>:"]
|
||||||
|
call term_sendkeys(a:buf, cmd)
|
||||||
|
if VerifyScreenDump(a:buf, a:dumpfile, {}, cmd =~ 'internal' ? 'internal' : 'external')
|
||||||
|
break " don't let the next iteration overwrite the "failed" file.
|
||||||
|
" don't let the next iteration overwrite the "failed" file.
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
" also test unified diff
|
||||||
|
call term_sendkeys(a:buf, ":call SetupUnified()\<CR>:")
|
||||||
|
call VerifyScreenDump(a:buf, a:dumpfile, {}, 'unified')
|
||||||
|
call term_sendkeys(a:buf, ":call StopUnified()\<CR>:")
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" Verify a screendump with the internal diff only.
|
||||||
|
func VerifyInternal(buf, dumpfile, extra)
|
||||||
|
call term_sendkeys(a:buf, ":diffupdate!\<CR>")
|
||||||
|
" trailing : for leaving the cursor on the command line
|
||||||
|
call term_sendkeys(a:buf, ":set diffopt=internal,filler" . a:extra . "\<CR>:")
|
||||||
|
call TermWait(a:buf)
|
||||||
|
call VerifyScreenDump(a:buf, a:dumpfile, {})
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_diff_screen()
|
func Test_diff_screen()
|
||||||
CheckScreendump
|
CheckScreendump
|
||||||
CheckFeature menu
|
CheckFeature menu
|
||||||
|
|
||||||
|
let lines =<< trim END
|
||||||
|
func UnifiedDiffExpr()
|
||||||
|
" Prepend some text to check diff type detection
|
||||||
|
call writefile(['warning', ' message'], v:fname_out)
|
||||||
|
silent exe '!diff -u ' .. v:fname_in .. ' ' .. v:fname_new .. '>>' .. v:fname_out
|
||||||
|
endfunc
|
||||||
|
func SetupUnified()
|
||||||
|
set diffexpr=UnifiedDiffExpr()
|
||||||
|
endfunc
|
||||||
|
func StopUnified()
|
||||||
|
set diffexpr=
|
||||||
|
endfunc
|
||||||
|
END
|
||||||
|
call writefile(lines, 'XdiffSetup')
|
||||||
|
|
||||||
" clean up already existing swap files, just in case
|
" clean up already existing swap files, just in case
|
||||||
call delete('.Xfile1.swp')
|
call delete('.Xfile1.swp')
|
||||||
call delete('.Xfile2.swp')
|
call delete('.Xfile2.swp')
|
||||||
|
|
||||||
" Test 1: Add a line in beginning of file 2
|
" Test 1: Add a line in beginning of file 2
|
||||||
call WriteDiffFiles(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
call WriteDiffFiles(0, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||||||
let buf = RunVimInTerminal('-d Xfile1 Xfile2', {})
|
let buf = RunVimInTerminal('-d -S XdiffSetup Xfile1 Xfile2', {})
|
||||||
" Set autoread mode, so that Vim won't complain once we re-write the test
|
" Set autoread mode, so that Vim won't complain once we re-write the test
|
||||||
" files
|
" files
|
||||||
call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
|
call term_sendkeys(buf, ":set autoread\<CR>\<c-w>w:set autoread\<CR>\<c-w>w")
|
||||||
@@ -911,6 +960,7 @@ func Test_diff_screen()
|
|||||||
call StopVimInTerminal(buf)
|
call StopVimInTerminal(buf)
|
||||||
call delete('Xfile1')
|
call delete('Xfile1')
|
||||||
call delete('Xfile2')
|
call delete('Xfile2')
|
||||||
|
call delete('XdiffSetup')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func Test_diff_with_cursorline()
|
func Test_diff_with_cursorline()
|
||||||
|
@@ -118,6 +118,32 @@ describe('jobs', function()
|
|||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('handles case-insensitively matching #env vars', function()
|
||||||
|
nvim('command', "let $TOTO = 'abc'")
|
||||||
|
-- Since $Toto is being set in the job, it should take precedence over the
|
||||||
|
-- global $TOTO on Windows
|
||||||
|
nvim('command', "let g:job_opts = {'env': {'Toto': 'def'}, 'stdout_buffered': v:true}")
|
||||||
|
if iswin() then
|
||||||
|
nvim('command', [[let j = jobstart('set | find /I "toto="', g:job_opts)]])
|
||||||
|
else
|
||||||
|
nvim('command', [[let j = jobstart('env | grep -i toto=', g:job_opts)]])
|
||||||
|
end
|
||||||
|
nvim('command', "call jobwait([j])")
|
||||||
|
nvim('command', "let g:output = Normalize(g:job_opts.stdout)")
|
||||||
|
local actual = eval('g:output')
|
||||||
|
local expected
|
||||||
|
if iswin() then
|
||||||
|
-- Toto is normalized to TOTO so we can detect duplicates, and because
|
||||||
|
-- Windows doesn't care about case
|
||||||
|
expected = {'TOTO=def', ''}
|
||||||
|
else
|
||||||
|
expected = {'TOTO=abc', 'Toto=def', ''}
|
||||||
|
end
|
||||||
|
table.sort(actual)
|
||||||
|
table.sort(expected)
|
||||||
|
eq(expected, actual)
|
||||||
|
end)
|
||||||
|
|
||||||
it('uses &shell and &shellcmdflag if passed a string', function()
|
it('uses &shell and &shellcmdflag if passed a string', function()
|
||||||
nvim('command', "let $VAR = 'abc'")
|
nvim('command', "let $VAR = 'abc'")
|
||||||
if iswin() then
|
if iswin() then
|
||||||
|
@@ -945,12 +945,20 @@ describe('lua stdlib', function()
|
|||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.api.nvim_set_var("testing", "hi")
|
vim.api.nvim_set_var("testing", "hi")
|
||||||
vim.api.nvim_set_var("other", 123)
|
vim.api.nvim_set_var("other", 123)
|
||||||
|
vim.api.nvim_set_var("floaty", 5120.1)
|
||||||
|
vim.api.nvim_set_var("nullvar", vim.NIL)
|
||||||
vim.api.nvim_set_var("to_delete", {hello="world"})
|
vim.api.nvim_set_var("to_delete", {hello="world"})
|
||||||
]]
|
]]
|
||||||
|
|
||||||
eq('hi', funcs.luaeval "vim.g.testing")
|
eq('hi', funcs.luaeval "vim.g.testing")
|
||||||
eq(123, funcs.luaeval "vim.g.other")
|
eq(123, funcs.luaeval "vim.g.other")
|
||||||
|
eq(5120.1, funcs.luaeval "vim.g.floaty")
|
||||||
eq(NIL, funcs.luaeval "vim.g.nonexistant")
|
eq(NIL, funcs.luaeval "vim.g.nonexistant")
|
||||||
|
eq(NIL, funcs.luaeval "vim.g.nullvar")
|
||||||
|
-- lost over RPC, so test locally:
|
||||||
|
eq({false, true}, exec_lua [[
|
||||||
|
return {vim.g.nonexistant == vim.NIL, vim.g.nullvar == vim.NIL}
|
||||||
|
]])
|
||||||
|
|
||||||
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
|
eq({hello="world"}, funcs.luaeval "vim.g.to_delete")
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
@@ -963,12 +971,20 @@ describe('lua stdlib', function()
|
|||||||
exec_lua [[
|
exec_lua [[
|
||||||
vim.api.nvim_buf_set_var(0, "testing", "hi")
|
vim.api.nvim_buf_set_var(0, "testing", "hi")
|
||||||
vim.api.nvim_buf_set_var(0, "other", 123)
|
vim.api.nvim_buf_set_var(0, "other", 123)
|
||||||
|
vim.api.nvim_buf_set_var(0, "floaty", 5120.1)
|
||||||
|
vim.api.nvim_buf_set_var(0, "nullvar", vim.NIL)
|
||||||
vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
|
vim.api.nvim_buf_set_var(0, "to_delete", {hello="world"})
|
||||||
]]
|
]]
|
||||||
|
|
||||||
eq('hi', funcs.luaeval "vim.b.testing")
|
eq('hi', funcs.luaeval "vim.b.testing")
|
||||||
eq(123, funcs.luaeval "vim.b.other")
|
eq(123, funcs.luaeval "vim.b.other")
|
||||||
|
eq(5120.1, funcs.luaeval "vim.b.floaty")
|
||||||
eq(NIL, funcs.luaeval "vim.b.nonexistant")
|
eq(NIL, funcs.luaeval "vim.b.nonexistant")
|
||||||
|
eq(NIL, funcs.luaeval "vim.b.nullvar")
|
||||||
|
-- lost over RPC, so test locally:
|
||||||
|
eq({false, true}, exec_lua [[
|
||||||
|
return {vim.b.nonexistant == vim.NIL, vim.b.nullvar == vim.NIL}
|
||||||
|
]])
|
||||||
|
|
||||||
eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
|
eq({hello="world"}, funcs.luaeval "vim.b.to_delete")
|
||||||
exec_lua [[
|
exec_lua [[
|
||||||
|
@@ -9,6 +9,7 @@ local eq = helpers.eq
|
|||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
local pesc = helpers.pesc
|
local pesc = helpers.pesc
|
||||||
local insert = helpers.insert
|
local insert = helpers.insert
|
||||||
|
local funcs = helpers.funcs
|
||||||
local retry = helpers.retry
|
local retry = helpers.retry
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
local read_file = require('test.helpers').read_file
|
local read_file = require('test.helpers').read_file
|
||||||
@@ -1392,10 +1393,10 @@ describe('LSP', function()
|
|||||||
{ label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} },
|
{ label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} },
|
||||||
{ label='foocar', sortText="f", textEdit={newText='foobar'} },
|
{ label='foocar', sortText="f", textEdit={newText='foobar'} },
|
||||||
-- real-world snippet text
|
-- real-world snippet text
|
||||||
{ label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} },
|
{ label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} },
|
||||||
{ label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} },
|
{ label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} },
|
||||||
-- nested snippet tokens
|
-- nested snippet tokens
|
||||||
{ label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} },
|
{ label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} },
|
||||||
-- plain text
|
-- plain text
|
||||||
{ label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} },
|
{ label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} },
|
||||||
}
|
}
|
||||||
@@ -1407,9 +1408,9 @@ describe('LSP', function()
|
|||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="d", insertText='foobar', textEdit={} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="d", insertText='foobar', textEdit={} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="e", insertText='foodar', textEdit={newText='foobar'} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="f", textEdit={newText='foobar'} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foobar(place holder, more ...holder{})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="g", insertText='foodar', insertTextFormat=2, textEdit={newText='foobar(${1:place holder}, ${2:more ...holder{\\}})'} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', textEdit={} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ1, var2 *typ2) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="h", insertText='foodar(${1:var1} typ1, ${2:var2} *typ2) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', textEdit={} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(var1 typ2,typ3 tail) {}', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="i", insertText='foodar(${1:var1 ${2|typ2,typ3|} ${3:tail}}) {$0\\}', insertTextFormat=2, textEdit={} } } } } },
|
||||||
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } },
|
{ abbr = 'foocar', dup = 1, empty = 1, icase = 1, info = ' ', kind = 'Unknown', menu = '', word = 'foodar(${1:var1})', user_data = { nvim = { lsp = { completion_item = { label='foocar', sortText="j", insertText='foodar(${1:var1})', insertTextFormat=1, textEdit={} } } } } },
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1820,36 +1821,20 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
describe('lsp.util.jump_to_location', function()
|
describe('lsp.util.jump_to_location', function()
|
||||||
local default_target_bufnr
|
local target_bufnr
|
||||||
local default_target_uri = 'file://fake/uri'
|
|
||||||
|
|
||||||
local create_buf = function(uri, lines)
|
|
||||||
for i, line in ipairs(lines) do
|
|
||||||
lines[i] = '"' .. line .. '"'
|
|
||||||
end
|
|
||||||
lines = table.concat(lines, ", ")
|
|
||||||
|
|
||||||
-- Let's set "hidden" to true in order to avoid errors when switching
|
|
||||||
-- between buffers in test.
|
|
||||||
local code = string.format([[
|
|
||||||
vim.api.nvim_set_option('hidden', true)
|
|
||||||
|
|
||||||
local bufnr = vim.uri_to_bufnr("%s")
|
|
||||||
local lines = {%s}
|
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
|
||||||
return bufnr
|
|
||||||
]], uri, lines)
|
|
||||||
|
|
||||||
return exec_lua(code)
|
|
||||||
end
|
|
||||||
|
|
||||||
before_each(function()
|
before_each(function()
|
||||||
default_target_bufnr = create_buf(default_target_uri, {'1st line of text', 'å å ɧ 汉语 ↥ 🤦 🦄'})
|
target_bufnr = exec_lua [[
|
||||||
|
local bufnr = vim.uri_to_bufnr("file://fake/uri")
|
||||||
|
local lines = {"1st line of text", "å å ɧ 汉语 ↥ 🤦 🦄"}
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, 1, false, lines)
|
||||||
|
return bufnr
|
||||||
|
]]
|
||||||
end)
|
end)
|
||||||
|
|
||||||
local location = function(uri, start_line, start_char, end_line, end_char)
|
local location = function(start_line, start_char, end_line, end_char)
|
||||||
return {
|
return {
|
||||||
uri = uri,
|
uri = "file://fake/uri",
|
||||||
range = {
|
range = {
|
||||||
start = { line = start_line, character = start_char },
|
start = { line = start_line, character = start_char },
|
||||||
["end"] = { line = end_line, character = end_char },
|
["end"] = { line = end_line, character = end_char },
|
||||||
@@ -1857,9 +1842,9 @@ describe('LSP', function()
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
local jump = function(bufnr, msg)
|
local jump = function(msg)
|
||||||
eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg))
|
eq(true, exec_lua('return vim.lsp.util.jump_to_location(...)', msg))
|
||||||
eq(bufnr, exec_lua[[return vim.fn.bufnr('%')]])
|
eq(target_bufnr, exec_lua[[return vim.fn.bufnr('%')]])
|
||||||
return {
|
return {
|
||||||
line = exec_lua[[return vim.fn.line('.')]],
|
line = exec_lua[[return vim.fn.line('.')]],
|
||||||
col = exec_lua[[return vim.fn.col('.')]],
|
col = exec_lua[[return vim.fn.col('.')]],
|
||||||
@@ -1867,13 +1852,13 @@ describe('LSP', function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
it('jumps to a Location', function()
|
it('jumps to a Location', function()
|
||||||
local pos = jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9))
|
local pos = jump(location(0, 9, 0, 9))
|
||||||
eq(1, pos.line)
|
eq(1, pos.line)
|
||||||
eq(10, pos.col)
|
eq(10, pos.col)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('jumps to a LocationLink', function()
|
it('jumps to a LocationLink', function()
|
||||||
local pos = jump(default_target_bufnr, {
|
local pos = jump({
|
||||||
targetUri = "file://fake/uri",
|
targetUri = "file://fake/uri",
|
||||||
targetSelectionRange = {
|
targetSelectionRange = {
|
||||||
start = { line = 0, character = 4 },
|
start = { line = 0, character = 4 },
|
||||||
@@ -1889,104 +1874,22 @@ describe('LSP', function()
|
|||||||
end)
|
end)
|
||||||
|
|
||||||
it('jumps to the correct multibyte column', function()
|
it('jumps to the correct multibyte column', function()
|
||||||
local pos = jump(default_target_bufnr, location(default_target_uri, 1, 2, 1, 2))
|
local pos = jump(location(1, 2, 1, 2))
|
||||||
eq(2, pos.line)
|
eq(2, pos.line)
|
||||||
eq(4, pos.col)
|
eq(4, pos.col)
|
||||||
eq('å', exec_lua[[return vim.fn.expand('<cword>')]])
|
eq('å', exec_lua[[return vim.fn.expand('<cword>')]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('adds current position to jumplist before jumping', function()
|
it('adds current position to jumplist before jumping', function()
|
||||||
exec_lua([[
|
funcs.nvim_win_set_buf(0, target_bufnr)
|
||||||
vim.api.nvim_win_set_buf(0, ...)
|
local mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
|
||||||
vim.api.nvim_win_set_cursor(0, {2, 0})
|
eq({ 1, 0 }, mark)
|
||||||
]], default_target_bufnr)
|
|
||||||
jump(default_target_bufnr, location(default_target_uri, 0, 9, 0, 9))
|
|
||||||
|
|
||||||
local mark = exec_lua([[return vim.inspect(vim.api.nvim_buf_get_mark(..., "'"))]], default_target_bufnr)
|
funcs.nvim_win_set_cursor(0, {2, 3})
|
||||||
eq('{ 2, 0 }', mark)
|
jump(location(0, 9, 0, 9))
|
||||||
end)
|
|
||||||
|
|
||||||
it('should not push item to tagstack if destination is the same as source', function()
|
mark = funcs.nvim_buf_get_mark(target_bufnr, "'")
|
||||||
-- Set cursor at the 2nd line, 1st character. This is the source position
|
eq({ 2, 3 }, mark)
|
||||||
-- for the test, and will also be the destination one, making the cursor
|
|
||||||
-- "motionless", thus not triggering a push to the tagstack.
|
|
||||||
exec_lua(string.format([[
|
|
||||||
vim.api.nvim_win_set_buf(0, %d)
|
|
||||||
vim.api.nvim_win_set_cursor(0, {2, 0})
|
|
||||||
]], default_target_bufnr))
|
|
||||||
|
|
||||||
-- Jump to 'f' in 'foobar', at the 2nd line.
|
|
||||||
jump(default_target_bufnr, location(default_target_uri, 1, 0, 1, 0))
|
|
||||||
|
|
||||||
local stack = exec_lua[[return vim.fn.gettagstack()]]
|
|
||||||
eq(0, stack.length)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('should not push the same item from same buffer twice to tagstack', function()
|
|
||||||
-- Set cursor at the 2nd line, 5th character.
|
|
||||||
exec_lua(string.format([[
|
|
||||||
vim.api.nvim_win_set_buf(0, %d)
|
|
||||||
vim.api.nvim_win_set_cursor(0, {2, 4})
|
|
||||||
]], default_target_bufnr))
|
|
||||||
|
|
||||||
local stack
|
|
||||||
|
|
||||||
-- Jump to 1st line, 1st column.
|
|
||||||
jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0))
|
|
||||||
|
|
||||||
stack = exec_lua[[return vim.fn.gettagstack()]]
|
|
||||||
eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from)
|
|
||||||
|
|
||||||
-- Go back to 5th character at 2nd line, which is currently at the top of
|
|
||||||
-- the tagstack.
|
|
||||||
exec_lua(string.format([[
|
|
||||||
vim.api.nvim_win_set_cursor(0, {2, 4})
|
|
||||||
]], default_target_bufnr))
|
|
||||||
|
|
||||||
-- Jump again to 1st line, 1st column. Since we're jumping from the same
|
|
||||||
-- position we have just jumped from, this jump shouldn't be pushed to
|
|
||||||
-- the tagstack.
|
|
||||||
jump(default_target_bufnr, location(default_target_uri, 0, 0, 0, 0))
|
|
||||||
|
|
||||||
stack = exec_lua[[return vim.fn.gettagstack()]]
|
|
||||||
eq({default_target_bufnr, 2, 5, 0}, stack.items[1].from)
|
|
||||||
eq(1, stack.length)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it('should not push the same item from another buffer twice to tagstack', function()
|
|
||||||
local target_uri = 'file://foo/bar'
|
|
||||||
local target_bufnr = create_buf(target_uri, {'this is a line', 'foobar'})
|
|
||||||
|
|
||||||
-- Set cursor at the 1st line, 3rd character of the default test buffer.
|
|
||||||
exec_lua(string.format([[
|
|
||||||
vim.api.nvim_win_set_buf(0, %d)
|
|
||||||
vim.api.nvim_win_set_cursor(0, {1, 2})
|
|
||||||
]], default_target_bufnr))
|
|
||||||
|
|
||||||
local stack
|
|
||||||
|
|
||||||
-- Jump to 1st line, 1st column of a different buffer from the source
|
|
||||||
-- position.
|
|
||||||
jump(target_bufnr, location(target_uri, 0, 0, 0, 0))
|
|
||||||
|
|
||||||
stack = exec_lua[[return vim.fn.gettagstack()]]
|
|
||||||
eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from)
|
|
||||||
|
|
||||||
-- Go back to 3rd character at 1st line of the default test buffer, which
|
|
||||||
-- is currently at the top of the tagstack.
|
|
||||||
exec_lua(string.format([[
|
|
||||||
vim.api.nvim_win_set_buf(0, %d)
|
|
||||||
vim.api.nvim_win_set_cursor(0, {1, 2})
|
|
||||||
]], default_target_bufnr))
|
|
||||||
|
|
||||||
-- Jump again to 1st line, 1st column of the different buffer. Since
|
|
||||||
-- we're jumping from the same position we have just jumped from, this
|
|
||||||
-- jump shouldn't be pushed to the tagstack.
|
|
||||||
jump(target_bufnr, location(target_uri, 0, 0, 0, 0))
|
|
||||||
|
|
||||||
stack = exec_lua[[return vim.fn.gettagstack()]]
|
|
||||||
eq({default_target_bufnr, 1, 3, 0}, stack.items[1].from)
|
|
||||||
eq(1, stack.length)
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user