aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--init.lua131
1 files changed, 131 insertions, 0 deletions
diff --git a/init.lua b/init.lua
index 29a4ed2..6e81f14 100644
--- a/init.lua
+++ b/init.lua
@@ -723,6 +723,14 @@ function LanguageServer:call_text_document_method(method, params, win, ctx)
self:request_diagnostics(win)
end
+--- Call the textDocument/symbol method of the server if provided
+function LanguageServer:request_symbols(win, ctx)
+ if self.capabilities['documentSymbolProvider'] then
+ local params = {textDocument = {uri = path_to_uri(win.file.path)}}
+ self:call_text_document_method('documentSymbol', params, win, ctx)
+ end
+end
+
local function lspc_handle_goto_method_response(req, result)
if not result or type(result) ~= 'table' or next(result) == nil then
lspc:warn(req.method .. ' found no results')
@@ -960,6 +968,12 @@ local function lspc_handle_initialize_response(ls, result)
vis.events.emit(lspc.events.LS_INITIALIZED, ls)
end
+local function lspc_handle_symbol_method_response(result, ctx)
+ if ctx.callback and type(ctx.callback) == 'function' then
+ ctx.callback(result, ctx)
+ end
+end
+
--- Dispatch method response from the server
-- @param method_response the response send from the server
-- @param req the request causing this response
@@ -1009,6 +1023,9 @@ function LanguageServer:handle_method_response(method_response, req)
elseif method == 'textDocument/diagnostic' then
lspc_handle_diagnostic_method_response(self, result, req.ctx)
+ elseif method == 'textDocument/documentSymbol' then
+ lspc_handle_symbol_method_response(result, req.ctx)
+
elseif method == 'shutdown' then
self:send_notification('exit')
self.fd:close()
@@ -1522,6 +1539,107 @@ local function lspc_show_diagnostic(win, line)
end
end
+local lsp_symbol_kinds = {
+ 'File',
+ 'Module',
+ 'Namespace',
+ 'Package',
+ 'Class',
+ 'Method',
+ 'Property',
+ 'Field',
+ 'Constructor',
+ 'Enum',
+ 'Interface',
+ 'Function',
+ 'Variable',
+ 'Constant',
+ 'String',
+ 'Number',
+ 'Boolean',
+ 'Array',
+ 'Object',
+ 'Key',
+ 'Null',
+ 'EnumMember',
+ 'Struct',
+ 'Event',
+ 'Operator',
+ 'TypeParameter',
+}
+
+local function recursive_bfs_symbols_flatten(symbols, indent)
+ local ranges = {}
+ local str = ''
+ for _, s in ipairs(symbols) do
+ local range = s.selectionRange or s.location.range
+ table.insert(ranges, range)
+ str = str ..
+ string.format('%s%s %s %d:%d\n', indent, lsp_symbol_kinds[s.kind], s.name,
+ range.start.line + 1, range.start.character + 1)
+ -- Remember DocumentSymbol.selectionRange or SymbolInformation.location.range
+ if s.children then
+ local cstr, cranges = recursive_bfs_symbols_flatten(s.children, indent .. ' ')
+ str = str .. cstr
+ for _, v in ipairs(cranges) do
+ table.insert(ranges, v)
+ end
+ end
+ end
+ return str, ranges
+end
+
+local function lspc_navwin_from_symbols(symbols, ctx)
+ local symbols_outline, ranges = recursive_bfs_symbols_flatten(symbols, '')
+
+ vis.ui.layout = vis.ui.layouts.VERTICAL
+ vis:message('')
+ local navwin = vis.win
+
+ -- Prepare message window to be our symbol navigation window.
+ navwin.file = 'symbols'
+ local navfile = navwin.file
+ assert(navwin and navwin.file)
+ assert(navfile:delete(0, navfile.size))
+ assert(navfile:insert(0, symbols_outline))
+
+ -- Create event handlers to connect the position in the navigation window to
+ -- the original window.
+ local function nav_win_win_highlight_handler()
+ if vis.win ~= navwin then
+ return
+ end
+ local line = navwin.selection.line
+ if line > #ranges then
+ return
+ end
+ local sel = lsp_pos_to_vis_sel(ranges[navwin.selection.line].start)
+ vis_open_new_doc_pos({
+ file = ctx.win.file.path,
+ line = sel.line,
+ col = sel.col,
+ }, 'e', ctx.win)
+ vis.win = navwin
+ end
+ vis.events.subscribe(vis.events.WIN_HIGHLIGHT, nav_win_win_highlight_handler)
+
+ -- Remove the event handlers.
+ local nav_win_close_handler
+ nav_win_close_handler = function(win)
+ if win ~= navwin or win ~= ctx.win then
+ return
+ end
+ vis.events.unsubscribe(vis.events.UI_DRAW, nav_win_win_highlight_handler)
+ vis.events.unsubscribe(vis.events.WIN_CLOSE, nav_win_close_handler)
+ end
+ vis.events.subscribe(vis.events.WIN_CLOSE, nav_win_close_handler)
+end
+
+local function lspc_nav_symbols(ls, win)
+ local ctx = {win = win, callback = lspc_navwin_from_symbols}
+ ls:request_symbols(win, ctx)
+end
+
-- vis-lspc commands
vis:command_register('lspc-back', function()
@@ -1748,6 +1866,19 @@ vis:command_register('lspc-show-diagnostics', function(argv, _, win)
end
end)
+vis:command_register('lspc-navigate-symbols', function(argv, _, win)
+ local ls, err = lspc_get_usable_ls(win, argv[1])
+ if err then
+ lspc:err(err)
+ return
+ end
+ assert(ls)
+ if not ls:is_file_opened(win.file.path) then
+ lspc_open(ls, win, win.file)
+ end
+ lspc_nav_symbols(ls, win)
+end, 'navigate the file by its symbols')
+
-- vis-lspc event hooks
vis.events.subscribe(vis.events.WIN_OPEN, function(win)
if lspc.autostart and win.syntax then