aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile10
-rw-r--r--runtime/doc/lsp.txt8
-rw-r--r--runtime/doc/ui.txt4
-rw-r--r--runtime/lua/vim/lsp.lua1
-rw-r--r--runtime/lua/vim/lsp/buf.lua6
-rw-r--r--runtime/lua/vim/lsp/callbacks.lua4
-rw-r--r--runtime/lua/vim/lsp/protocol.lua28
-rw-r--r--runtime/lua/vim/lsp/util.lua25
-rw-r--r--src/nvim/option.c65
-rw-r--r--src/nvim/testdir/test_mksession.vim30
-rw-r--r--src/nvim/version.c16
-rw-r--r--test/functional/plugin/lsp_spec.lua251
12 files changed, 412 insertions, 36 deletions
diff --git a/Makefile b/Makefile
index 07dad26d7b..39f42739ff 100644
--- a/Makefile
+++ b/Makefile
@@ -58,11 +58,15 @@ endif
BUILD_CMD = $(BUILD_TOOL)
-ifneq ($(VERBOSE),)
- # Only need to handle Ninja here. Make will inherit the VERBOSE variable.
- ifeq ($(BUILD_TYPE),Ninja)
+# Only need to handle Ninja here. Make will inherit the VERBOSE variable, and the -j and -n flags.
+ifeq ($(BUILD_TYPE),Ninja)
+ ifneq ($(VERBOSE),)
BUILD_CMD += -v
endif
+ BUILD_CMD += $(shell printf '%s' '$(MAKEFLAGS)' | grep -o -- '-j[0-9]\+')
+ ifeq (n,$(findstring n,$(firstword -$(MAKEFLAGS))))
+ BUILD_CMD += -n
+ endif
endif
DEPS_CMAKE_FLAGS ?=
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 8140b6a15e..249136f32f 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -49,6 +49,7 @@ go-to-definition, hover, etc. Example config: >
nnoremap <silent> 1gD <cmd>lua vim.lsp.buf.type_definition()<CR>
nnoremap <silent> gr <cmd>lua vim.lsp.buf.references()<CR>
nnoremap <silent> g0 <cmd>lua vim.lsp.buf.document_symbol()<CR>
+ nnoremap <silent> gW <cmd>lua vim.lsp.buf.workspace_symbol()<CR>
Nvim provides the |vim.lsp.omnifunc| 'omnifunc' handler which allows
|i_CTRL-X_CTRL-O| to consume LSP completion. Example config (note the use of
@@ -783,6 +784,13 @@ signature_help() *vim.lsp.buf.signature_help()*
type_definition() *vim.lsp.buf.type_definition()*
TODO: Documentation
+workspace_symbol({query}) *vim.lsp.buf.workspace_symbol()*
+ Lists all symbols in the current workspace in the quickfix
+ window. The list is filtered against the optional argument
+ {query}; if the argument is omitted from the call, the user
+ is prompted to enter a string on the command line. An empty
+ string means no filtering is done.
+
==============================================================================
Lua module: vim.lsp.callbacks *lsp-callbacks*
diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt
index b243d9ba50..2817c1015b 100644
--- a/runtime/doc/ui.txt
+++ b/runtime/doc/ui.txt
@@ -161,7 +161,9 @@ the editor.
`cursor_shape`: "block", "horizontal", "vertical"
`cell_percentage`: Cell % occupied by the cursor.
`blinkwait`, `blinkon`, `blinkoff`: See |cursor-blinking|.
- `attr_id`: Cursor attribute id (defined by `hl_attr_define`)
+ `attr_id`: Cursor attribute id (defined by `hl_attr_define`).
+ When attr_id is 0, the background and foreground
+ colors should be swapped.
`attr_id_lm`: Cursor attribute id for when 'langmap' is active.
`short_name`: Mode code name, see 'guicursor'.
`name`: Mode descriptive name.
diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua
index 4c1c52c796..61da2130c8 100644
--- a/runtime/lua/vim/lsp.lua
+++ b/runtime/lua/vim/lsp.lua
@@ -533,6 +533,7 @@ function lsp.start_client(config)
or (not client.resolved_capabilities.goto_definition and method == 'textDocument/definition')
or (not client.resolved_capabilities.implementation and method == 'textDocument/implementation')
or (not client.resolved_capabilities.document_symbol and method == 'textDocument/documentSymbol')
+ or (not client.resolved_capabilities.workspace_symbol and method == 'textDocument/workspaceSymbol')
then
callback(unsupported_method(method), method, nil, client_id, bufnr)
return
diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua
index 587d1f52e9..0b45951a56 100644
--- a/runtime/lua/vim/lsp/buf.lua
+++ b/runtime/lua/vim/lsp/buf.lua
@@ -137,6 +137,12 @@ function M.document_symbol()
request('textDocument/documentSymbol', params)
end
+function M.workspace_symbol(query)
+ query = query or npcall(vfn.input, "Query: ")
+ local params = {query = query}
+ request('workspace/symbol', params)
+end
+
--- Send request to server to resolve document highlights for the
--- current text document position. This request can be associated
--- to key mapping or to events such as `CursorHold`, eg:
diff --git a/runtime/lua/vim/lsp/callbacks.lua b/runtime/lua/vim/lsp/callbacks.lua
index bd2cbf1ea7..70d21be8e7 100644
--- a/runtime/lua/vim/lsp/callbacks.lua
+++ b/runtime/lua/vim/lsp/callbacks.lua
@@ -54,13 +54,15 @@ M['textDocument/references'] = function(_, _, result)
api.nvim_command("wincmd p")
end
-M['textDocument/documentSymbol'] = function(_, _, result, _, bufnr)
+local symbol_callback = function(_, _, result, _, bufnr)
if not result or vim.tbl_isempty(result) then return end
util.set_qflist(util.symbols_to_items(result, bufnr))
api.nvim_command("copen")
api.nvim_command("wincmd p")
end
+M['textDocument/documentSymbol'] = symbol_callback
+M['workspace/symbol'] = symbol_callback
M['textDocument/rename'] = function(_, _, result)
if not result then return end
diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua
index 41e8119c8c..76817e3a4a 100644
--- a/runtime/lua/vim/lsp/protocol.lua
+++ b/runtime/lua/vim/lsp/protocol.lua
@@ -644,6 +644,18 @@ function protocol.make_client_capabilities()
-- TODO(tjdevries): Implement this
contextSupport = false;
};
+ declaration = {
+ linkSupport = true;
+ };
+ definition = {
+ linkSupport = true;
+ };
+ implementation = {
+ linkSupport = true;
+ };
+ typeDefinition = {
+ linkSupport = true;
+ };
hover = {
dynamicRegistration = false;
contentFormat = { protocol.MarkupKind.Markdown; protocol.MarkupKind.PlainText };
@@ -677,7 +689,21 @@ function protocol.make_client_capabilities()
hierarchicalDocumentSymbolSupport = true;
};
};
- workspace = nil;
+ workspace = {
+ symbol = {
+ dynamicRegistration = false;
+ symbolKind = {
+ valueSet = (function()
+ local res = {}
+ for k in pairs(protocol.SymbolKind) do
+ if type(k) == 'number' then table.insert(res, k) end
+ end
+ return res
+ end)();
+ };
+ hierarchicalWorkspaceSymbolSupport = true;
+ };
+ };
experimental = nil;
}
end
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 82b9a0b3aa..6a1e799489 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -424,8 +424,10 @@ function M.make_floating_popup_options(width, height, opts)
end
function M.jump_to_location(location)
- if location.uri == nil then return end
- local bufnr = vim.uri_to_bufnr(location.uri)
+ -- location may be Location or LocationLink
+ local uri = location.uri or location.targetUri
+ if uri == nil then return end
+ local bufnr = vim.uri_to_bufnr(uri)
-- Save position in jumplist
vim.cmd "normal! m'"
@@ -436,8 +438,9 @@ function M.jump_to_location(location)
--- Jump to new location
api.nvim_set_current_buf(bufnr)
- local row = location.range.start.line
- local col = location.range.start.character
+ local range = location.range or location.targetSelectionRange
+ local row = range.start.line
+ local col = range.start.character
local line = api.nvim_buf_get_lines(0, row, row+1, true)[1]
col = vim.str_byteindex(line, col)
api.nvim_win_set_cursor(0, {row + 1, col})
@@ -876,9 +879,11 @@ function M.locations_to_items(locations)
end;
})
for _, d in ipairs(locations) do
- local start = d.range.start
- local fname = assert(vim.uri_to_fname(d.uri))
- table.insert(grouped[fname], {start = start})
+ -- locations may be Location or LocationLink
+ local uri = d.uri or d.targetUri
+ local fname = assert(vim.uri_to_fname(uri))
+ local range = d.range or d.targetSelectionRange
+ table.insert(grouped[fname], {start = range.start})
end
@@ -956,10 +961,8 @@ function M.symbols_to_items(symbols, bufnr)
text = '['..kind..'] '..symbol.name
})
if symbol.children then
- for _, child in ipairs(symbol) do
- for _, v in ipairs(_symbols_to_items(child, _items, _bufnr)) do
- vim.list_extend(_items, v)
- end
+ for _, v in ipairs(_symbols_to_items(symbol.children, _items, _bufnr)) do
+ vim.list_extend(_items, v)
end
end
end
diff --git a/src/nvim/option.c b/src/nvim/option.c
index eae52ff260..d7675f48d7 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -5402,8 +5402,9 @@ int makeset(FILE *fd, int opt_flags, int local_only)
do_endif = true;
}
if (put_setstring(fd, cmd, p->fullname, (char_u **)varp,
- (p->flags & P_EXPAND) != 0) == FAIL)
+ p->flags) == FAIL) {
return FAIL;
+ }
if (do_endif) {
if (put_line(fd, "endif") == FAIL) {
return FAIL;
@@ -5421,12 +5422,12 @@ int makeset(FILE *fd, int opt_flags, int local_only)
/// 'sessionoptions' or 'viewoptions' contains "folds" but not "options".
int makefoldset(FILE *fd)
{
- if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, false) == FAIL
- || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, false)
+ if (put_setstring(fd, "setlocal", "fdm", &curwin->w_p_fdm, 0) == FAIL
+ || put_setstring(fd, "setlocal", "fde", &curwin->w_p_fde, 0)
== FAIL
- || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, false)
+ || put_setstring(fd, "setlocal", "fmr", &curwin->w_p_fmr, 0)
== FAIL
- || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, false)
+ || put_setstring(fd, "setlocal", "fdi", &curwin->w_p_fdi, 0)
== FAIL
|| put_setnum(fd, "setlocal", "fdl", &curwin->w_p_fdl) == FAIL
|| put_setnum(fd, "setlocal", "fml", &curwin->w_p_fml) == FAIL
@@ -5439,10 +5440,13 @@ int makefoldset(FILE *fd)
return OK;
}
-static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int expand)
+static int put_setstring(FILE *fd, char *cmd, char *name,
+ char_u **valuep, uint64_t flags)
{
char_u *s;
- char_u *buf;
+ char_u *buf = NULL;
+ char_u *part = NULL;
+ char_u *p;
if (fprintf(fd, "%s %s=", cmd, name) < 0) {
return FAIL;
@@ -5460,9 +5464,46 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
return FAIL;
}
}
- } else if (expand) {
- buf = xmalloc(MAXPATHL);
- home_replace(NULL, *valuep, buf, MAXPATHL, false);
+ } else if ((flags & P_EXPAND) != 0) {
+ size_t size = (size_t)STRLEN(*valuep) + 1;
+
+ // replace home directory in the whole option value into "buf"
+ buf = xmalloc(size);
+ if (buf == NULL) {
+ goto fail;
+ }
+ home_replace(NULL, *valuep, buf, size, false);
+
+ // If the option value is longer than MAXPATHL, we need to append
+ // earch comma separated part of the option sperately, so that it
+ // can be expanded when read back.
+ if (size >= MAXPATHL && (flags & P_COMMA) != 0
+ && vim_strchr(*valuep, ',') != NULL) {
+ part = xmalloc(size);
+ if (part == NULL) {
+ goto fail;
+ }
+
+ // write line break to clear the option, e.g. ':set rtp='
+ if (put_eol(fd) == FAIL) {
+ goto fail;
+ }
+ p = buf;
+ while (*p != NUL) {
+ // for each comma seperated option part, append value to
+ // the option, :set rtp+=value
+ if (fprintf(fd, "%s %s+=", cmd, name) < 0) {
+ goto fail;
+ }
+ (void)copy_option_part(&p, part, size, ",");
+ if (put_escstr(fd, part, 2) == FAIL || put_eol(fd) == FAIL) {
+ goto fail;
+ }
+ }
+ xfree(buf);
+ xfree(part);
+ return OK;
+ }
if (put_escstr(fd, buf, 2) == FAIL) {
xfree(buf);
return FAIL;
@@ -5476,6 +5517,10 @@ static int put_setstring(FILE *fd, char *cmd, char *name, char_u **valuep, int e
return FAIL;
}
return OK;
+fail:
+ xfree(buf);
+ xfree(part);
+ return FAIL;
}
static int put_setnum(FILE *fd, char *cmd, char *name, long *valuep)
diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim
index b7169444d1..0a8d52242a 100644
--- a/src/nvim/testdir/test_mksession.vim
+++ b/src/nvim/testdir/test_mksession.vim
@@ -2,7 +2,7 @@
scriptencoding latin1
-if !has('multi_byte') || !has('mksession')
+if !has('mksession')
finish
endif
@@ -122,6 +122,34 @@ func Test_mksession_large_winheight()
call delete('Xtest_mks_winheight.out')
endfunc
+func Test_mksession_rtp()
+ if has('win32')
+ " TODO: fix problem with backslashes
+ return
+ endif
+ new
+ set sessionoptions+=options
+ let _rtp=&rtp
+ " Make a real long (invalid) runtimepath value,
+ " that should exceed PATH_MAX (hopefully)
+ let newrtp=&rtp.',~'.repeat('/foobar', 1000)
+ let newrtp.=",".expand("$HOME")."/.vim"
+ let &rtp=newrtp
+
+ " determine expected value
+ let expected=split(&rtp, ',')
+ let expected = map(expected, '"set runtimepath+=".v:val')
+ let expected = ['set runtimepath='] + expected
+ let expected = map(expected, {v,w -> substitute(w, $HOME, "~", "g")})
+
+ mksession! Xtest_mks.out
+ let &rtp=_rtp
+ let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "runtimepath"')
+ call assert_equal(expected, li)
+
+ call delete('Xtest_mks.out')
+endfunc
+
" Verify that arglist is stored correctly to the session file.
func Test_mksession_arglist()
argdel *
diff --git a/src/nvim/version.c b/src/nvim/version.c
index 15a9713c7c..e9f2b37e6b 100644
--- a/src/nvim/version.c
+++ b/src/nvim/version.c
@@ -267,7 +267,7 @@ static const int included_patches[] = {
1654,
1653,
1652,
- // 1651,
+ 1651,
1650,
1649,
1648,
@@ -543,7 +543,7 @@ static const int included_patches[] = {
1378,
1377,
1376,
- // 1375,
+ 1375,
1374,
1373,
1372,
@@ -584,7 +584,7 @@ static const int included_patches[] = {
1337,
1336,
// 1335,
- // 1334,
+ 1334,
1333,
1332,
1331,
@@ -626,7 +626,7 @@ static const int included_patches[] = {
1295,
1294,
1293,
- // 1292,
+ 1292,
1291,
1290,
1289,
@@ -776,10 +776,10 @@ static const int included_patches[] = {
1145,
1144,
1143,
- // 1142,
+ 1142,
1141,
1140,
- // 1139,
+ 1139,
1138,
1137,
1136,
@@ -793,9 +793,9 @@ static const int included_patches[] = {
1128,
1127,
1126,
- // 1125,
+ 1125,
1124,
- // 1123,
+ 1123,
1122,
1121,
1120,
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 63188a9b09..79f6ef9dd2 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -988,4 +988,255 @@ describe('LSP', function()
]])
end)
end)
+ describe('lsp.util.symbols_to_items', function()
+ describe('convert DocumentSymbol[] to items', function()
+ it('DocumentSymbol has children', function()
+ local expected = {
+ {
+ col = 1,
+ filename = '',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Module',
+ lnum = 4,
+ text = '[Module] TestB'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Namespace',
+ lnum = 6,
+ text = '[Namespace] TestC'
+ }
+ }
+ eq(expected, exec_lua [[
+ local doc_syms = {
+ {
+ deprecated = false,
+ detail = "A",
+ kind = 1,
+ name = "TestA",
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 4,
+ line = 1
+ }
+ },
+ children = {
+ {
+ children = {},
+ deprecated = false,
+ detail = "B",
+ kind = 2,
+ name = "TestB",
+ range = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 0,
+ line = 4
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 4,
+ line = 3
+ }
+ }
+ }
+ }
+ },
+ {
+ deprecated = false,
+ detail = "C",
+ kind = 3,
+ name = "TestC",
+ range = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 0,
+ line = 6
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 4,
+ line = 5
+ }
+ }
+ }
+ }
+ return vim.lsp.util.symbols_to_items(doc_syms, nil)
+ ]])
+ end)
+ it('DocumentSymbol has no children', function()
+ local expected = {
+ {
+ col = 1,
+ filename = '',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = '',
+ kind = 'Namespace',
+ lnum = 6,
+ text = '[Namespace] TestC'
+ }
+ }
+ eq(expected, exec_lua [[
+ local doc_syms = {
+ {
+ deprecated = false,
+ detail = "A",
+ kind = 1,
+ name = "TestA",
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 4,
+ line = 1
+ }
+ },
+ },
+ {
+ deprecated = false,
+ detail = "C",
+ kind = 3,
+ name = "TestC",
+ range = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 0,
+ line = 6
+ }
+ },
+ selectionRange = {
+ start = {
+ character = 0,
+ line = 5
+ },
+ ["end"] = {
+ character = 4,
+ line = 5
+ }
+ }
+ }
+ }
+ return vim.lsp.util.symbols_to_items(doc_syms, nil)
+ ]])
+ end)
+ end)
+ describe('convert SymbolInformation[] to items', function()
+ local expected = {
+ {
+ col = 1,
+ filename = 'test_a',
+ kind = 'File',
+ lnum = 2,
+ text = '[File] TestA'
+ },
+ {
+ col = 1,
+ filename = 'test_b',
+ kind = 'Module',
+ lnum = 4,
+ text = '[Module] TestB'
+ }
+ }
+ eq(expected, exec_lua [[
+ local sym_info = {
+ {
+ deprecated = false,
+ kind = 1,
+ name = "TestA",
+ location = {
+ range = {
+ start = {
+ character = 0,
+ line = 1
+ },
+ ["end"] = {
+ character = 0,
+ line = 2
+ }
+ },
+ uri = "file://test_a"
+ },
+ contanerName = "TestAContainer"
+ },
+ {
+ deprecated = false,
+ kind = 2,
+ name = "TestB",
+ location = {
+ range = {
+ start = {
+ character = 0,
+ line = 3
+ },
+ ["end"] = {
+ character = 0,
+ line = 4
+ }
+ },
+ uri = "file://test_b"
+ },
+ contanerName = "TestBContainer"
+ }
+ }
+ return vim.lsp.util.symbols_to_items(sym_info, nil)
+ ]])
+ end)
+ end)
end)