init commit

neovim_0_5
ray-x 3 years ago
parent b080fa4010
commit 9d5f2efdbd

@ -0,0 +1,88 @@
# Navigator
GUI for neovim lsp with a collection of most used LSP/treesitter functions.
Easy code navigation. Based on LSP.
# Features:
- LSP easy setup. Support some of the most commonly used lsp client setup
- GUI
- fzy search
- Better navigation for diagnostic errors, Navigate through files that contain errors/warnings
- Group references/implementation/incomming/outgoing based on file names.
# Why a new plugin
After installed a handful of lsp plugins, I still got ~500 loc for lsp and still increasing. Reason is that I need
to tune the plugins to fit my requirements.
# Similar projects / special mentions:
- [nvim-lsputils](https://github.com/RishabhRD/nvim-lsputils)
- [nvim-fzy](https://github.com/mfussenegger/nvim-fzy.git)
- [fuzzy](https://github.com/amirrezaask/fuzzy.nvim)
- [lspsaga](https://github.com/glepnir/lspsaga.nvim)
# Install
You can remove your lspconfig setup and use this plugin.
The plugin depends on [guihua.lua](https://github.com/ray-x/guihua.lua), which provides gui and fzy support.
```vim
Plug 'ray-x/guihua.lua', {'do': 'cd lua/fzy && make' }
Plug 'ray-x/navigator.lua'
```
Packer
```lua
use {'ray-x/navigator.lua', requires = {'ray-x/guihua.lua', run = 'cd lua/fzy && make'}}
```
## Setup
```lua
lua require'navigator'.setup()
```
## Screenshots
### Reference
![reference](https://github.com/ray-x/files/blob/master/img/navigator/ref.gif?raw=true)
### Diagnostic
![diagnostic](https://github.com/ray-x/files/blob/master/img/navigator/diag.jpg?raw=true)
### Implementation
![implementation](https://github.com/ray-x/files/blob/master/img/navigator/implemention.jpg?raw=true)
### Fzy search in reference
![fzy_reference](https://github.com/ray-x/files/blob/master/img/navigator/fzy_reference.jpg?raw=true)
### Code actions
![code actions](https://github.com/ray-x/files/blob/master/img/navigator/codeaction.jpg?raw=true)
### Code preview with highlight
![code preview](https://github.com/ray-x/files/blob/master/img/navigator/preview_with_hl.jpg?raw=true)
### Call hierarchy (incomming/outgoing)
![incomming](https://github.com/ray-x/files/blob/master/img/navigator/incomming.jpg?raw=true)
# Todo
- Early phase, bugs expected
- Async (some of the requests is slow on large codebase and might be good to use co-rountine)
- More clients. I use go, python, js/ts, java, c/cpp, lua most of the time. Do not test other languages (e.g rust, swift etc)
```
```

1
lua/.gitignore vendored

@ -0,0 +1 @@
TODO.txt

@ -0,0 +1,52 @@
local M = {}
M.config_values ={
debug = false, -- log output
code_action_icon = '',
code_action_prompt = {
enable = true,
sign = true,
sign_priority = 40,
virtual_text = true,
},
}
vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()")
vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
local extend_config = function(opts)
opts = opts or {}
if next(opts) == nil then return end
for key,value in pairs(opts) do
if M.config_values[key] == nil then
error(string.format('[] Key %s not valid',key))
return
end
if type(M.config_values[key]) == 'table' then
for k,v in pairs(value) do
M.config_values[key][k] = v
end
else
M.config_values[key] = value
end
end
end
M.setup = function(cfg)
extend_config(cfg)
-- print("loading navigator")
require('navigator.lspclient').setup(M.config_values)
require('navigator.reference')
require('navigator.definition')
require('navigator.hierarchy')
require('navigator.implementation')
print("navigator loader")
if M.config_values.code_action_prompt.enable then
vim.cmd [[autocmd CursorHold,CursorHoldI * lua require'navigator.codeAction'.code_action_prompt()]]
end
end
return M

@ -0,0 +1,209 @@
local util = require "navigator.util"
local log = util.log
local code_action = {}
local gui = require "navigator.gui"
local config = require("navigator").config_values
local api = vim.api
function code_action.code_action_handler(err, _, actions, num, _, _, customSelectionHandler)
-- log(actions)
if actions == nil or vim.tbl_isempty(actions) then
print("No code actions available")
return
end
local data = {" Auto Fix <C-o> Apply <C-e> Exit"}
for i, action in ipairs(actions) do
local title = action.title:gsub("\r\n", "\\r\\n")
title = title:gsub("\n", "\\n")
title = string.format("[%d] %s", i, title)
table.insert(data, title)
end
local width = 0
for _, str in ipairs(data) do
if #str > width then
width = #str
end
end
-- log(data)
local function apply_action(idx)
local action_chosen = actions[idx - 1]
if action_chosen.edit or type(action_chosen.command) == "table" then
if action_chosen.edit then
vim.lsp.util.apply_workspace_edit(action_chosen.edit)
end
if type(action_chosen.command) == "table" then
vim.lsp.buf.execute_command(action_chosen.command)
end
else
vim.lsp.buf.execute_command(action_chosen)
end
end
gui.new_list_view {
items = data,
width = width + 4,
loc = "top_center",
relative = "cursor",
rawdata = true,
data = data,
on_confirm = function(pos)
if pos < 2 then
pos = 2
end
apply_action(pos)
end,
on_move = function(pos)
if pos < 2 then
pos = 2
end
local l = data[pos]
-- log("on move", l)
end
}
end
-- https://github.com/glepnir/lspsaga.nvim/blob/main/lua/lspsaga/codeaction.lua
-- lspsaga has a clever design to inject code action indicator
local sign_group = "nvcodeaction"
local get_namespace = function()
return api.nvim_create_namespace(sign_group)
end
local get_current_winid = function()
return api.nvim_get_current_win()
end
local sign_name = "NavigatorLightBulb"
if vim.tbl_isempty(vim.fn.sign_getdefined(sign_name)) then
vim.fn.sign_define(sign_name, {text = config.code_action_icon, texthl = "LspDiagnosticsSignHint"})
end
local function _update_virtual_text(line)
local namespace = get_namespace()
api.nvim_buf_clear_namespace(0, namespace, 0, -1)
if line then
local icon_with_indent = " " .. config.code_action_icon
-- log("updat text", line, icon_with_indent)
api.nvim_buf_set_extmark(
0,
namespace,
line,
-1,
{
virt_text = {{icon_with_indent, "LspDiagnosticsSignHint"}},
virt_text_pos = "overlay",
hl_mode = "combine"
}
)
end
end
local function _update_sign(line)
local winid = get_current_winid()
if code_action[winid] and code_action[winid].lightbulb_line ~= 0 then
vim.fn.sign_unplace(sign_group, {id = code_action[winid].lightbulb_line, buffer = "%"})
end
if line then
--log("updatasign", line, sign_group, sign_name)
vim.fn.sign_place(
line,
sign_group,
sign_name,
"%",
{lnum = line + 1, priority = config.code_action_prompt.sign_priority}
)
code_action[winid].lightbulb_line = line
end
end
local need_check_diagnostic = {
["go"] = true,
["python"] = true
}
function code_action:render_action_virtual_text(line, diagnostics)
return function(_, _, actions)
if actions == nil or type(actions) ~= "table" or vim.tbl_isempty(actions) then
if config.code_action_prompt.virtual_text then
_update_virtual_text(nil)
end
if config.code_action_prompt.sign then
_update_sign(nil)
end
else
if config.code_action_prompt.sign then
if need_check_diagnostic[vim.bo.filetype] then
if next(diagnostics) == nil then
_update_sign(nil)
else
_update_sign(line)
end
else
_update_sign(line)
end
end
if config.code_action_prompt.virtual_text then
if need_check_diagnostic[vim.bo.filetype] then
if next(diagnostics) == nil then
_update_virtual_text(nil)
else
_update_virtual_text(line)
end
else
_update_virtual_text(line)
end
end
end
end
end
local special_buffers = {
["LspSagaCodecode_action"] = true,
["lspsagafinder"] = true,
["NvimTree"] = true,
["vist"] = true,
["lspinfo"] = true,
["markdown"] = true,
["text"] = true
}
-- local action_call_back = function (_,_)
-- return Action:action_callback()
-- end
local action_vritual_call_back = function (line,diagnostics)
return code_action:render_action_virtual_text(line,diagnostics)
end
local code_action_req = function(_call_back_fn, diagnostics)
local context = {diagnostics = diagnostics}
local params = vim.lsp.util.make_range_params()
params.context = context
local line = params.range.start.line
local callback = _call_back_fn(line, diagnostics)
vim.lsp.buf_request(0, "textDocument/codeAction", params, callback)
end
-- code_action.code_action = function()
-- local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
-- code_action_req(action_call_back, diagnostics)
-- end
code_action.code_action_prompt = function()
if special_buffers[vim.bo.filetype] then
return
end
local diagnostics = vim.lsp.diagnostic.get_line_diagnostics()
local winid = get_current_winid()
code_action[winid] = code_action[winid] or {}
code_action[winid].lightbulb_line = code_action[winid].lightbulb_line or 0
code_action_req(action_vritual_call_back, diagnostics)
end
return code_action

@ -0,0 +1,101 @@
local util = require "navigator.util"
local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items
local gui = require "navigator.gui"
local log = util.log
local TextView = require("guihua.textview")
-- callback for lsp definition, implementation and declaration handler
local function definition_hdlr(_, _, locations, _, bufnr)
-- log(locations)
if locations == nil or vim.tbl_isempty(locations) then
print "Definition not found"
return
end
if vim.tbl_islist(locations) then
if #locations > 1 then
local items = locations_to_items(locations)
gui.new_list_view({items = items, api = 'Definition'})
else
vim.lsp.util.jump_to_location(locations[1])
end
else
vim.lsp.util.jump_to_location(locations)
end
end
local function def_preview(timeout_ms)
local method = "textDocument/definition"
local params = vim.lsp.util.make_position_params()
local result = vim.lsp.buf_request_sync(0, method, params, timeout_ms or 2000)
if result == nil or vim.tbl_isempty(result) then
print("No result found: " .. method)
return nil
end
local data = {}
-- result = {vim.tbl_deep_extend("force", {}, unpack(result))}
-- log("def-preview", result)
for key, value in pairs(result) do
if result[key] ~= nil then
table.insert(data, result[key].result[1])
end
end
local range = data[1].targetRange or data[1].range
local row = range.start.line
-- in case there are comments
row = math.max(row - 3, 1)
local delta = range.start.line - row + 1
local uri = data[1].uri or data[1].targetUri
if not uri then
return
end
local bufnr = vim.uri_to_bufnr(uri)
if not vim.api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
local definition = vim.api.nvim_buf_get_lines(bufnr, row, range["end"].line + 12, false)
local def_line = vim.api.nvim_buf_get_lines(bufnr, range.start.line, range.start.line + 1, false)
for _ = 1, math.min(3, #definition), 1 do
if #definition[1] < 2 then
table.remove(definition, 1)
delta = delta - 1
else
break
end
end
definition = vim.list_extend({"" .. "Definition: "}, definition)
local filetype = vim.api.nvim_buf_get_option(bufnr, "filetype")
-- TODO multiple resuts?
local opts = {
relative = "cursor",
style = "minimal",
ft = filetype,
data = definition,
enter = true
}
TextView:new(opts)
delta = delta + 1 -- header
local cmd = "normal! " .. tostring(delta) .. "G"
vim.cmd(cmd)
vim.cmd('set cursorline')
if #def_line > 0 then
local niddle = require('guihua.util').add_escape(def_line[1])
log(def_line[1], niddle)
vim.fn.matchadd("Search", niddle)
end
-- TODO:
-- https://github.com/oblitum/goyo.vim/blob/master/autoload/goyo.vim#L108-L135
end
vim.lsp.handlers["textDocument/definition"] = definition_hdlr
return {
definition_handler = definition_hdlr,
definition_preview = def_preview,
declaration_handler = definition_hdlr,
typeDefinition_handler = definition_hdlr
}

@ -0,0 +1,83 @@
local gui = require "navigator.gui"
local diagnostic_list = {}
local log = require "navigator.util".log
diagnostic_list[vim.bo.filetype] = {}
local diag_hdlr = function(err, method, result, client_id, br, config)
-- log(result)
vim.lsp.diagnostic.on_publish_diagnostics(err, method, result, client_id, br, config)
if err ~= nil then log(err, config) end
local cwd = vim.fn.getcwd(0)
local ft = vim.bo.filetype
if diagnostic_list[ft] == nil then
diagnostic_list[vim.bo.filetype] = {}
end
-- vim.lsp.diagnostic.clear(vim.fn.bufnr(), client.id, nil, nil)
local uri = result.uri
if result and result.diagnostics then
local item_list = {}
for _, v in ipairs(result.diagnostics) do
local item = v
item.filename = assert(vim.uri_to_fname(uri))
item.display_filename = item.filename:gsub(cwd .. "/", "./", 1)
item.lnum = v.range.start.line + 1
item.col = v.range.start.character + 1
item.uri = uri
local head = ""
if v.severity > 1 then
head = ""
end
local bufnr = vim.uri_to_bufnr(uri)
vim.fn.bufload(bufnr)
local pos = v.range.start
local row = pos.line
local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1]
item.text = head .. tostring(item.lnum) .. ": " .. line .. "" .. v.message
table.insert(item_list, item)
end
-- local old_items = vim.fn.getqflist()
diagnostic_list[ft][uri] = item_list
end
end
local M = {}
-- vim.lsp.handlers["textDocument/publishDiagnostics"] =
M.diagnostic_handler =
vim.lsp.with(
diag_hdlr,
{
-- Enable underline, use default values
underline = true,
-- Enable virtual text, override spacing to 0
virtual_text = {
spacing = 0,
prefix = "" --' ,  
},
-- Use a function to dynamically turn signs off
-- and on, using buffer local variables
signs = true,
-- Disable a feature
update_in_insert = false
}
)
M.show_diagnostic = function()
if diagnostic_list[vim.bo.filetype] ~= nil then
log(diagnostic_list[vim.bo.filetype])
-- vim.fn.setqflist({}, " ", {title = "LSP", items = diagnostic_list[vim.bo.filetype]})
local results = diagnostic_list[vim.bo.filetype]
local display_items = {}
for _, items in pairs(results) do
for _, it in pairs(items) do
table.insert(display_items, it)
end
end
log(display_items)
if #display_items > 0 then
gui.new_list_view({items = display_items, api = 'Diagnostic'})
end
end
end
return M

@ -0,0 +1,149 @@
local M = {}
local ListView = require "guihua.listview"
local TextView = require "guihua.textview"
local View = require "guihua.view"
local util = require "navigator.util"
local log = require "navigator.util".log
function M.new_preview(opts)
return TextView:new(
{
loc = "top_center",
rect = {
height = #opts.items + 4,
width = opts.width or 90,
pos_x = opts.pos_x or 0,
pos_y = opts.pos_y or 4
},
-- data = display_data,
relative = opts.relative,
data = opts.items,
syntax = opts.syntax,
enter = opts.enter or false
}
)
end
function M._preview_location(location, width, pos_x, pos_y)
local api = vim.api
local uri = location.targetUri or location.uri
if uri == nil then
log("invalid uri ", location)
return
end
local bufnr = vim.uri_to_bufnr(uri)
if not api.nvim_buf_is_loaded(bufnr) then
vim.fn.bufload(bufnr)
end
--
local range = location.targetRange or location.range
local contents = api.nvim_buf_get_lines(bufnr, range.start.line, range["end"].line + 1, false)
--
local syntax = api.nvim_buf_get_option(bufnr, "syntax")
if syntax == nil or #syntax < 1 then
syntax = api.nvim_buf_get_option(bufnr, "ft")
end
log(syntax, contents)
local opts = {syntax = syntax, width = width, pos_x = pos_x or 0, pos_y = pos_y or 10}
opts.items = contents
log("syntax", opts.syntax)
return M.new_preview(opts)
end
-- local bufnr, winnr =lsp.util.open_floating_preview(contents, syntax, {offset_x=30, offset_y=20})
--
-- vim.api.nvim_buf_set_var(bufnr, "lsp_floating", true)
-- return bufnr, winnr
--
function M.preview_file(filename, width, line, col, offset_x, offset_y)
log("file", filename, line, offset_x, offset_y)
if line >= 2 then
line = line - 2
end
local loc = {uri = "file:///" .. filename, targetRange = {start = {line = line}}}
offset_x = offset_x or 0
offset_y = offset_y or 6
loc.targetRange["end"] = {line = line + 4}
return M._preview_location(loc, width, offset_x, offset_y)
end
function M.preview_uri(uri, width, line, col, offset_x, offset_y)
log("uri", uri, line, offset_x, offset_y)
if line >= 2 then
line = line - 2
end
offset_x = offset_x or 0
offset_y = offset_y or 6
local loc = {uri = uri, targetRange = {start = {line = line}}}
loc.targetRange["end"] = {line = line + 4}
return M._preview_location(loc, width, offset_x, offset_y)
end
function M.new_list_view(opts)
local items = opts.items
local data = {}
if opts.rawdata then
data = items
else
data = require "guihua.util".aggregate_filename(items, opts)
end
local wwidth = vim.api.nvim_get_option("columns")
local width = opts.width or math.floor(wwidth * 0.8)
local wheight = math.floor(vim.api.nvim_get_option("lines") * 0.8)
local prompt = opts.prompt or false
if data and not vim.tbl_isempty(data) then
-- replace
if #data > 10 and opts.prompt == nil then
prompt = true
end
local height = math.min(#data, math.floor(wheight / 2))
local offset_y = height
if prompt then
offset_y = offset_y + 1
end
return ListView:new(
{
loc = "top_center",
prompt = prompt,
relative = opts.relative,
style = opts.style,
api = opts.api,
rect = {
height = height,
width = width,
pos_x = 0,
pos_y = 0
},
-- data = display_data,
data = data,
on_confirm = opts.on_confirm or function(pos)
if pos == 0 then
pos = 1
end
local l = data[pos]
if l.filename ~= nil then
util.open_file_at(l.filename, l.lnum)
end
end,
on_move = opts.on_move or function(pos)
if pos == 0 then
pos = 1
end
local l = data[pos]
log("on move", pos, l.text or l, l.uri, l.filename)
-- todo fix
if l.uri ~= nil then
return M.preview_uri(l.uri, width, l.lnum, l.col, 0, offset_y)
else
return M.preview_file(l.filename, width, l.lnum, l.col, 0, offset_y)
end
end
}
)
end
end
return M

@ -0,0 +1,85 @@
local gui = require "navigator.gui"
local util = require "navigator.util"
local log = util.log
local partial = util.partial
local lsphelper = require "navigator.lspwrapper"
local cwd = vim.fn.getcwd(0)
local M = {}
local function call_hierarchy_handler(direction, err, _, result, _, _, error_message)
-- log('call_hierarchy')
if err ~= nil then
print("ERROR: " .. error_message)
return
end
-- log("dir", direction, "result", result)
local items = {}
for _, call_hierarchy_call in pairs(result) do
local call_hierarchy_item = call_hierarchy_call[direction]
local kind = ''
if call_hierarchy_item.kind then
kind = require'navigator.lspclient.lspkind'.kind(call_hierarchy_item.kind) .. ' '
end
for _, range in pairs(call_hierarchy_call.fromRanges) do
local filename = assert(vim.uri_to_fname(call_hierarchy_item.uri))
local display_filename = filename:gsub(cwd .. "/", "./", 1)
table.insert(
items,
{
uri = call_hierarchy_item.uri,
filename = filename,
-- display_filename = filename:gsub(cwd .. "/", "./", 1),
display_filename = call_hierarchy_item.detail or display_filename,
text = kind .. call_hierarchy_item.name,
range = range,
lnum = range.start.line,
col = range.start.character
}
)
end
end
return items
end
local call_hierarchy_handler_from = partial(call_hierarchy_handler, "from")
local call_hierarchy_handler_to = partial(call_hierarchy_handler, "to")
local function incoming_calls_handler(bang, err, method, result, client_id, bufnr)
local results = call_hierarchy_handler_from(err, method, result, client_id, bufnr, "Incoming calls not found")
gui.new_list_view({items = results, api = 'incomming'})
end
local function outgoing_calls_handler(bang, err, method, result, client_id, bufnr)
local results = call_hierarchy_handler_to(err, method, result, client_id, bufnr, "Outgoing calls not found")
gui.new_list_view({items =results, api = 'outgoing'})
--fzf_locations(bang, "", "Outgoing Calls", results, false)
end
function M.incoming_calls(bang, opts)
if not lsphelper.check_capabilities("call_hierarchy") then
return
end
local params = vim.lsp.util.make_position_params()
util.call_sync("callHierarchy/incomingCalls", params, opts, partial(incoming_calls_handler, bang))
end
function M.outgoing_calls(bang, opts)
if not lsphelper.check_capabilities("call_hierarchy") then
return
end
local params = vim.lsp.util.make_position_params()
util.call_sync("callHierarchy/outgoingCalls", params, opts, partial(outgoing_calls_handler, bang))
end
M.incoming_calls_call = partial(M.incoming_calls, 0)
M.outgoing_calls_call = partial(M.outgoing_calls, 0)
M.incoming_calls_handler = partial(incoming_calls_handler, 0)
M.outgoing_calls_handler = partial(outgoing_calls_handler, 0)
return M

@ -0,0 +1,23 @@
-- TODO: change background and use TextView?
local lsp = require("vim.lsp")
return { hover_handler = function(_, method, result)
vim.lsp.util.focusable_float(
method,
function()
if not (result and result.contents) then
return
end
local markdown_lines = lsp.util.convert_input_to_markdown_lines(result.contents)
markdown_lines = lsp.util.trim_empty_lines(markdown_lines)
if vim.tbl_isempty(markdown_lines) then
return
end
local bnr, contents_winid, _, border_winid = vim.lsp.util.fancy_floating_markdown(markdown_lines)
lsp.util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, contents_winid)
lsp.util.close_preview_autocmd({"CursorMoved", "BufHidden", "InsertCharPre"}, border_winid)
return bnr, contents_winid
end
)
end
}

@ -0,0 +1,38 @@
local util = require "navigator.util"
local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local M = {}
local location = require("guihua.location")
local partial = util.partial
local locations_to_items = lsphelper.locations_to_items
local log = util.log
-- dataformat should be same as reference
local function location_handler(err, _, locations, _, bufnr, error_message)
if err ~= nil then
print("ERROR: " .. tostring(err) .. error_message)
return
end
return locations_to_items(locations)
end
local function implementation_handler(bang, err, method, result, client_id, bufnr)
local results = location_handler(err, method, result, client_id, bufnr, "Implementation not found")
gui.new_list_view({items =results, api = 'Implementation'})
end
function M.implementation(bang, opts)
if not lsphelper.check_capabilities("implementation") then
return
end
local params = vim.lsp.util.make_position_params()
util.call_sync("textDocument/implementation", params, opts, partial(implementation_handler, bang))
end
M.implementation_call = partial(M.implementation, 0)
M.implementation_handler = partial(implementation_handler, 0)
return M

@ -0,0 +1,74 @@
local vim, api = vim, vim.api
local lsp = require("vim.lsp")
local util = require "navigator.util"
local log = util.log
if not packer_plugins["nvim-lua/lsp-status.nvim"] or not packer_plugins["lsp-status.nvim"].loaded then
vim.cmd [[packadd lsp-status.nvim]]
end
local lsp_status = require("lsp-status")
local diagnostic_map = function(bufnr)
local opts = {noremap = true, silent = true}
api.nvim_buf_set_keymap(bufnr, "n", "]O", ":lua vim.lsp.diagnostic.set_loclist()<CR>", opts)
end
local M = {}
local function documentHighlight()
api.nvim_exec(
[[
hi LspReferenceRead cterm=bold gui=Bold ctermbg=yellow guibg=DarkOrchid3
hi LspReferenceText cterm=bold gui=Bold ctermbg=red guibg=gray27
hi LspReferenceWrite cterm=bold gui=Bold,Italic ctermbg=red guibg=MistyRose
augroup lsp_document_highlight
autocmd! * <buffer>
autocmd CursorHold <buffer> lua vim.lsp.buf.document_highlight()
autocmd CursorMoved <buffer> lua vim.lsp.buf.clear_references()
augroup END
]],
false
)
vim.lsp.handlers["textDocument/documentHighlight"] = function(_, _, result, _)
if not result then
return
end
bufnr = api.nvim_get_current_buf()
vim.lsp.util.buf_clear_references(bufnr)
vim.lsp.util.buf_highlight_references(bufnr, result)
end
end
M.on_attach = function(client, bufnr)
log("attaching")
if lsp_status ~= nil then
lsp_status.on_attach(client, bufnr)
end
require "lsp_signature".on_attach()
diagnostic_map(bufnr)
-- lspsaga
require "utils.highlight".add_highlight()
api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")
-- https://github.com/fsouza
if client.resolved_capabilities.document_highlight then
documentHighlight()
end
require("navigator.lspclient.mapping").setup({client = client, bufnr = bufnr, cap = client.resolved_capabilities})
vim.cmd [[packadd vim-illuminate]]
require "illuminate".on_attach(client)
require "utils.lspkind".init()
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true
end
M.setup = function(cfg)
return M
end
return M

@ -0,0 +1,219 @@
-- todo allow config passed in
local lspconfig = nil
local lsp_status = nil
if not packer_plugins["nvim-lua/lsp-status.nvim"] or not packer_plugins["lsp-status.nvim"].loaded then
vim.cmd [[packadd lsp-status.nvim]]
lsp_status = require("lsp-status")
-- if lazyloading
vim.cmd [[packadd nvim-lspconfig]]
lspconfig = require "lspconfig"
end
local cap = vim.lsp.protocol.make_client_capabilities()
local on_attach = require("navigator.lspclient.attach").on_attach
local lsp_status_cfg = {
status_symbol = "",
indicator_errors = "", --'',
indicator_warnings = "", --'',
indicator_info = "",
--'',
indicator_hint = "💡",
indicator_ok = "",
--'✔️',
spinner_frames = {"", "", "", "", "", "", "", ""},
select_symbol = function(cursor_pos, symbol)
if symbol.valuerange then
local value_range = {
["start"] = {
character = 0,
line = vim.fn.byte2line(symbol.valuerange[1])
},
["end"] = {
character = 0,
line = vim.fn.byte2line(symbol.valuerange[2])
}
}
return require("lsp-status.util").in_range(cursor_pos, value_range)
end
end
}
-- local gopls = {}
-- gopls["ui.completion.usePlaceholders"] = true
local golang_setup = {
on_attach = on_attach,
capabilities = cap,
-- init_options = {
-- useplaceholders = true,
-- completeunimported = true
-- },
message_level = vim.lsp.protocol.MessageType.Error,
cmd = {
"gopls"
-- share the gopls instance if there is one already
-- "-remote=auto",
--[[ debug options ]]
--
--"-logfile=auto",
--"-debug=:0",
--"-remote.debug=:0",
--"-rpc.trace",
},
settings = {},
root_dir = function(fname)
local util = require("lspconfig").util
return util.root_pattern("go.mod", ".git")(fname) or util.path.dirname(fname)
end
}
local clang_cfg = {
cmd = {
"clangd",
"--background-index",
"--suggest-missing-includes",
"--clang-tidy",
"--header-insertion=iwyu"
},
on_attach = function(client)
client.resolved_capabilities.document_formatting = true
on_attach(client)
end
}
local sqls_cfg = {
on_attach = function(client, bufnr)
client.resolved_capabilities.execute_command = true
lsp_status.on_attach(client, bufnr)
require "utils.highlight".diagnositc_config_sign()
require "sqls".setup {picker = "telescope"} -- or default
end,
settings = {
cmd = {"sqls", "-config", "$HOME/.config/sqls/config.yml"},
-- alterantively:
-- connections = {
-- {
-- driver = 'postgresql',
-- datasourcename = 'host=127.0.0.1 port=5432 user=postgres password=password dbname=user_db sslmode=disable',
-- },
-- },
workspace = {
library = {
-- this loads the `lua` files from nvim into the runtime.
[vim.fn.expand("$vimruntime/lua")] = true,
[vim.fn.expand("~/repos/nvim/lua")] = true
}
}
}
}
-- lua setup
local sumneko_root_path = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server"
local sumneko_binary = vim.fn.expand("$HOME") .. "/github/sumneko/lua-language-server/bin/macOS/lua-language-server"
local lua_cfg = {
cmd = {sumneko_binary, "-E", sumneko_root_path .. "/main.lua"},
on_attach = on_attach,
settings = {
Lua = {
runtime = {
-- Tell the language server which version of Lua you're using (most likely LuaJIT in the case of Neovim)
version = "LuaJIT",
-- Setup your lua path
path = vim.split(package.path, ";")
},
diagnostics = {
enable = true,
-- Get the language server to recognize the `vim` global
globals = {
"vim",
"describe",
"it",
"before_each",
"after_each",
"teardown",
"pending"
}
},
workspace = {
-- Make the server aware of Neovim runtime files
library = {
[vim.fn.expand("$VIMRUNTIME/lua")] = true,
[vim.fn.expand("$VIMRUNTIME/lua/vim")] = true,
[vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true,
-- [vim.fn.expand("~/repos/nvim/lua")] = true
}
}
}
}
}
local function lsp_status_setup()
local servers = {
"gopls",
"tsserver",
"flow",
"bashls",
"dockerls",
"pyls",
"sumneko_lua",
"vimls",
"html",
"jsonls",
"cssls",
"yamlls",
"clangd",
"sqls"
}
for _, lspclient in ipairs(servers) do
if lsp_status ~= nl then
lsp_status.register_progress()
lsp_status.config(lsp_status_cfg)
end
require "utils.highlight".diagnositc_config_sign()
require "utils.highlight".add_highlight()
end
end
local function setup(user_opts)
if lspconfig == nil then
print("lsp-config need installed and enabled")
return
end
lsp_status_setup()
for _, lspclient in ipairs({"tsserver", "bashls", "flow", "dockerls", "vimls", "html", "jsonls", "cssls", "yamlls"}) do
lspconfig[lspclient].setup {
message_level = vim.lsp.protocol.MessageType.error,
log_level = vim.lsp.protocol.MessageType.error,
on_attach = on_attach,
capabilities = lsp_status.capabilities
}
end
lspconfig.gopls.setup(golang_setup)
lspconfig.sqls.setup(sqls_cfg)
require "lspconfig".sumneko_lua.setup(lua_cfg)
lspconfig.clangd.setup(clang_cfg)
servers = {
"dockerls",
"bashls",
"rust_analyzer",
"pyls"
}
for _, server in ipairs(servers) do
lspconfig[server].setup {
on_attach = on_attach
}
end
end
return {setup = setup, cap = cap}

@ -0,0 +1,29 @@
local lsp = require("vim.lsp")
vim.cmd [[packadd lspsaga.nvim]]
local saga = require "lspsaga"
saga.init_lsp_saga()
M={}
local capabilities = vim.lsp.protocol.make_client_capabilities()
capabilities.textDocument.completion.completionItem.snippetSupport = true
function M.reload_lsp()
vim.lsp.stop_client(vim.lsp.get_active_clients())
vim.cmd [[edit]]
end
function M.open_lsp_log()
local path = vim.lsp.get_log_path()
vim.cmd("edit " .. path)
end
vim.cmd("command! -nargs=0 LspLog call v:lua.open_lsp_log()")
vim.cmd("command! -nargs=0 LspRestart call v:lua.reload_lsp()")
print("loading lsp client")
local cfg = {}
require('lsp.clients').setup(cfg)
require('lsp.mappings').setup(cfg)
return M

@ -0,0 +1,48 @@
local M = {}
local api = vim.api
-- lsp sign          ﮻         ﯭ        ﳀ  
function M.diagnositc_config_sign()
vim.fn.sign_define('LspDiagnosticsSignError', {text='', texthl='LspDiagnosticsSignError',linehl='', numhl=''})
vim.fn.sign_define('LspDiagnosticsSignWarning', {text='', texthl='LspDiagnosticsSignWarning', linehl='', numhl=''})
vim.fn.sign_define('LspDiagnosticsSignInformation', {text='', texthl='LspDiagnosticsSignInformation', linehl='', numhl=''})
vim.fn.sign_define('LspDiagnosticsSignHint', {text='💡', texthl='LspDiagnosticsSignHint', linehl='', numhl=''})
end
function M.add_highlight()
-- lsp system default
api.nvim_command("hi! link LspDiagnosticsUnderlineError SpellBad")
api.nvim_command("hi! link LspDiagnosticsUnderlineWarning SpellRare")
api.nvim_command("hi! link LspDiagnosticsUnderlineInformation SpellRare")
api.nvim_command("hi! link LspDiagnosticsUnderlineHint SpellRare")
-- lspsaga
api.nvim_command("hi LspFloatWinBorder guifg=black")
api.nvim_command("hi def link TargetWord Error")
api.nvim_command("hi def link ReferencesCount Title")
api.nvim_command("hi def link DefinitionCount Title")
api.nvim_command("hi def link TargetFileName Comment")
api.nvim_command("hi def link DefinitionIcon Special")
api.nvim_command("hi def link ReferencesIcon Special")
api.nvim_command("hi def link HelpTitle Comment")
api.nvim_command("hi def link HelpItem Comment")
-- diagnostic for lspsaga, overwrite if needed
-- api.nvim_command("hi DiagnosticTruncateLine guifg=#6699cc gui=bold")
-- api.nvim_command("hi def link DiagnosticError Error")
-- api.nvim_command("hi def link DiagnosticWarning WarningMsg")
-- api.nvim_command("hi DiagnosticInformation guifg=#6699cc gui=bold")
-- api.nvim_command("hi DiagnosticHint guifg=#56b6c2 gui=bold")
-- for nvim version 0.5 2020-11
-- api.nvim_command("sign define LspDiagnosticsErrorSign text= texthl=LspDiagnosticsError linehl= numhl=")
-- api.nvim_command("sign define LspDiagnosticsWarningSign text= texthl=LspDiagnosticsWarning linehl= numhl=")
api.nvim_command("hi def link DefinitionPreviewTitle Title")
end
return M

@ -0,0 +1,7 @@
local M = {}
M.setup = function(cfg)
cfg = cfg or {}
require('navigator.lspclient.clients').setup(cfg)
end
return M

@ -0,0 +1,38 @@
local kind_symbols = {
Text = '',
Method = 'ƒ',
Function = '',
Constructor = '',
Field = '',
Variable = '',
Class = '',
Interface = '',
Module = '',
Property = '',
Unit = '',
Value = '',
Enum = '',
Keyword = '',
Snippet = '',
Color = '',
File = '',
Reference = '',
Folder = '',
EnumMember = '',
Constant = '',
Struct = '',
Event = '',
Operator ='',
TypeParameter = '',
Default = '',
}
local CompletionItemKind = {'', 'ƒ', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''}
function lspkind.kind(kind)
-- require('vim.lsp.protocol').CompletionItemKind = {'', 'ƒ', '', '', 'ﴲ', '', '', 'ﰮ', '', '', '', '', '了', '', '﬌', '', '', '', '', '', '', '', 'ﳅ', '', '', ''}
return CompletionItemKind[kind]
end
return lspkind

@ -0,0 +1,142 @@
local log = require "navigator.util".log
local function set_keymap(...)
vim.api.nvim_set_keymap(...)
end
local event_hdlrs = {
{ev = "BufWritePre", func = "diagnostic.set_loclist({open_loclist = false})"},
{ev = "CursorHold", func = "document_highlight()"},
{ev = "CursorHoldI", func = "document_highlight()"},
{ev = "CursorMoved", func = "clear_references()"}
}
local key_maps = {
{key = "gr", func = "references()"},
{mode = "i", key = "<M-k>", func = "signature_help()"},
{key = "gs", func = "signature_help()"},
{key = "g0", func = "document_symbol()"},
{key = "gW", func = "workspace_symbol()"},
{key = "<c-]>", func = "definition()"},
{key = "gD", func = "declaration()"},
{key = "gp", func = "require('navigator.definition').definition_preview()"},
{key = "K", func = "hover()"},
{key = "ga", mode = 'n', func = "code_action()"},
{key = "ca", mode = 'v', func = "range_code_action()"},
{key = "<Leader>re", func = "rename()"},
{key = "<Leader>gi", func = "incoming_calls()"},
{key = "<Leader>go", func = "outgoing_calls()"},
{key = "gi", func = "implementation()"},
{key = "gt", func = "type_definition()"},
{key = "gL", func = "diagnostic.show_line_diagnostics()"},
{key = "gG", func = "require('navigator.diagnostics').show_diagnostic()"},
{key = "]d", func = "diagnostic.goto_next()"},
{key = "[d", func = "diagnostic.goto_prev()"},
{key = "<C-LeftMouse>", func = "definition()"},
{key = "g<LeftMouse>", func = "implementation()"}
}
local function set_mapping(user_opts)
local opts = {noremap = true, silent = true}
user_opts = user_opts or {}
local user_key = user_opts.keymaps or {}
local bufnr = user_opts.bufnr or 0
local opts = { noremap=true, silent=true }
local function buf_set_keymap(...)
vim.api.nvim_buf_set_keymap(bufnr, ...)
end
-- local function buf_set_option(...)
-- vim.api.nvim_buf_set_option(bufnr, ...)
-- end
for _, v in pairs(user_key) do
local exists = false
for _, default in pairs(key_maps) do
if v.func == default.func then
default.key, exists = v.key, true
break
end
end
if not exists then
table.insert(key_maps, v)
end
end
-- local key_opts = {vim.tbl_deep_extend("force", key_maps, unpack(result))}
for _, value in pairs(key_maps) do
local f = "<Cmd>lua vim.lsp.buf." .. value.func .. "<CR>"
if string.find(value.func, 'require') then
f = "<Cmd>lua " .. value.func .. "<CR>"
else if string.find(value.func, 'diagnostic') then
f = "<Cmd>lua vim.lsp." .. value.func .. "<CR>"
end
local k = value.key
local m = value.mode or "n"
buf_set_keymap(m, k, f, opts)
end
-- format setup
if user_opts.cap.document_formatting then
buf_set_keymap("n", "<space>f", "<cmd>lua vim.lsp.buf.formatting()<CR>", opts)
vim.cmd([[autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting()]])
end
if user_opts.cap.document_range_formatting then
buf_set_keymap("v", "<space>f", "<cmd>lua vim.lsp.buf.range_formatting()<CR>", opts)
end
end
local function set_event_handler(user_opts)
user_opts = user_opts or {}
local file_types = "c,cpp,h,go,python,vim,sh,javascript,html,css,lua,typescript,rust"
-- local format_files = "c,cpp,h,go,python,vim,javascript,typescript" --html,css,
vim.api.nvim_command [[augroup nvim_lsp_autos]]
vim.api.nvim_command [[autocmd!]]
for _, value in pairs(event_hdlrs) do
local f = ""
if string.find(value.func, "diagnostic") then
f = "lua vim.lsp." .. value.func
else
f = "lua vim.lsp.buf." .. value.func
end
local cmd = "autocmd FileType " .. file_types .. " autocmd nvim_lsp_autos " .. value.ev .. " <buffer> silent! " .. f
vim.api.nvim_command(cmd)
end
vim.api.nvim_command([[augroup END]])
end
local M = {}
function M.setup(user_opts)
set_mapping(user_opts)
set_event_handler(user_opts)
local cap = user_opts.cap or {}
if cap.call_hierarchy then
vim.lsp.handlers["callHierarchy/incomingCalls"] = require "navigator.hierarchy".incoming_calls_handler
vim.lsp.handlers["callHierarchy/outgoingCalls"] = require "navigator.hierarchy".outgoing_calls_handler
end
vim.lsp.handlers["textDocument/references"] = require "navigator.reference".reference_handler
vim.lsp.handlers["textDocument/codeAction"] = require "navigator.codeAction".code_action_handler
vim.lsp.handlers["textDocument/definition"] = require "navigator.definition".definition_handler
if cap.declaration then
vim.lsp.handlers["textDocument/declaration"] = require "navigator.definition".declaration_handler
end
vim.lsp.handlers["textDocument/typeDefinition"] = require "navigator.definition".typeDefinition_handler
vim.lsp.handlers["textDocument/implementation"] = require "navigator.implementation".implementation_handler
vim.lsp.handlers["textDocument/documentSymbol"] = require "navigator.symbols".symbol_handler
vim.lsp.handlers["workspace/symbol"] = require "navigator.symbols".symbol_handler
vim.lsp.handlers["textDocument/publishDiagnostics"] = require'navigator.diagnostics'.diagnostic_handler
-- vim.lsp.handlers["textDocument/hover"] = require 'navigator.hover'.hover_handler
end
return M

@ -0,0 +1,128 @@
local M = {}
local util = require "navigator.util"
local lsp = require "vim.lsp"
local log = require "navigator.util".log
function M.lines_from_locations(locations, include_filename)
local fnamemodify = (function(filename)
if include_filename then
return vim.fn.fnamemodify(filename, ":~:.") .. ":"
else
return ""
end
end)
local lines = {}
for _, loc in ipairs(locations) do
table.insert(
lines,
(fnamemodify(loc["filename"]) .. loc["lnum"] .. ":" .. loc["col"] .. ": " .. vim.trim(loc["text"]))
)
end
return lines
end
local function extract_result(results_lsp)
if results_lsp then
local results = {}
for _, server_results in pairs(results_lsp) do
if server_results.result then
vim.list_extend(results, server_results.result)
end
end
return results
end
end
function M.check_capabilities(feature, client_id)
local clients = lsp.buf_get_clients(client_id or 0)
local supported_client = false
for _, client in pairs(clients) do
supported_client = client.resolved_capabilities[feature]
if supported_client then
goto continue
end
end
::continue::
if supported_client then
return true
else
if #clients == 0 then
print("LSP: no client attached")
else
print("LSP: server does not support " .. feature)
end
return false
end
end
function M.call_sync(method, params, opts, handler)
params = params or {}
opts = opts or {}
local results_lsp, err = lsp.buf_request_sync(0, method, params, opts.timeout or vim.g.navtator_timeout)
handler(err, method, extract_result(results_lsp), nil, nil)
end
function M.call_async(method, params, handler)
params = params or {}
local callback = function(...)
util.show(...)
handler(...)
end
local results_lsp, canceller = lsp.buf_request(0, method, params, callback)
return results_lsp, canceller
-- handler(err, method, extract_result(results_lsp), nil, nil)
end
function M.locations_to_items(locations)
local cwd = vim.fn.getcwd(0)
if not locations or vim.tbl_isempty(locations) then
print("list not avalible")
return
end
local items = {} -- lsp.util.locations_to_items(locations)
-- items and locations may not matching
table.sort(
locations,
function(i, j)
if i.uri == j.uri then
if i.range and i.range.start then
return i.range.start.line < j.range.start.line
end
return false
else
return i.uri < j.uri
end
end
)
for i, loc in ipairs(locations) do
local item = lsp.util.locations_to_items({loc})[1]
item.uri = locations[i].uri
item.range = locations[i].range
item.filename = assert(vim.uri_to_fname(item.uri))
local filename = item.filename:gsub(cwd .. "/", "./", 1)
item.display_filename = filename or item.filename
item.rpath = util.get_relative_path(cwd, item.filename)
table.insert(items, item)
end
-- insert data into lines and loc
-- for i, loc in ipairs(items) do
-- log(items[i], locations[i])
-- local filename = loc.filename:gsub(cwd .. "/", "./", 1)
-- items[i].uri = locations[i].uri
-- items[i].range = locations[i].range
-- items[i].filename = assert(vim.uri_to_fname(loc.uri))
-- items[i].display_filename = filename or items[i].filename
-- items[i].rpath = util.get_relative_path(cwd, loc.filename)
-- log(items[i], locations[i])
-- end
return items
end
return M

@ -0,0 +1,453 @@
--[[ -- incomming/outgoing
dir from result { {
from = {
detail = "command-line-arguments • interface.go",
kind = 12,
name = "m2",
range = {
end = {
character = 7,
line = 39
},
start = {
character = 5,
line = 39
}
},
selectionRange = {
end = {
character = 7,
line = 39
},
start = {
character = 5,
line = 39
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
fromRanges = { {
end = {
character = 8,
line = 40
},
start = {
character = 1,
line = 40
}
} }
}, {
from = {
detail = "command-line-arguments • interface.go",
kind = 12,
name = "main",
range = {
end = {
character = 9,
line = 43
},
start = {
character = 5,
line = 43
}
},
selectionRange = {
end = {
character = 9,
line = 43
},
start = {
character = 5,
line = 43
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
fromRanges = { {
end = {
character = 11,
line = 47
},
start = {
character = 4,
line = 47
}
}, {
end = {
character = 11,
line = 48
},
start = {
character = 4,
line = 48
}
} }
} }
--]]
-- [[ locations/reference from lsp
{ {
range = {
["end"] = {
character = 20,
line = 26
},
start = {
character = 16,
line = 26
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
}, {
range = {
["end"] = {
character = 22,
line = 35
},
start = {
character = 18,
line = 35
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
} }
--]]
-- definition
definition.lua:9: { {
range = {
end = {
character = 12,
line = 33
},
start = {
character = 5,
line = 33
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
} }
-- def preview
def-preview {
[3] = {
result = { {
range = {
end = {
character = 12,
line = 33
},
start = {
character = 5,
line = 33
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
} }
}
}
-- symbol
{ {
containerName = "command-line-arguments",
kind = 11,
location = {
range = {
end = {
character = 13,
line = 7
},
start = {
character = 5,
line = 7
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
name = "command-line-arguments.geometry"
}, {
containerName = "command-line-arguments",
kind = 23,
location = {
range = {
end = {
character = 9,
line = 12
},
start = {
character = 5,
line = 12
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
name = "command-line-arguments.rect"
}, {
containerName = "command-line-arguments",
kind = 12,
location = {
range = {
end = {
character = 9,
line = 43
},
start = {
character = 5,
line = 43
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
name = "command-line-arguments.main"
}, {
containerName = "command-line-arguments",
kind = 6,
location = {
range = {
end = {
character = 8,
line = 8
},
start = {
character = 4,
line = 8
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
name = "command-line-arguments.geometry.area"
}, {
containerName = "command-line-arguments",
kind = 8,
location = {
range = {
end = {
character = 9,
line = 13
},
start = {
character = 4,
line = 13
}
},
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
},
name = "command-line-arguments.rect.width"
}}
{
diagnostics = { {
code = "UnusedVar",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal#UnusedVar"
},
message = "d declared but not used",
range = {
end = {
character = 5,
line = 46
},
start = {
character = 4,
line = 46
}
},
severity = 1,
source = "compiler"
}, {
code = "WrongArgCount",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal#WrongArgCount"
},
message = "missing argument in conversion to circle",
range = {
end = {
character = 17,
line = 46
},
start = {
character = 9,
line = 46
}
},
severity = 1,
source = "compiler"
} },
uri = "file:///Users/ray.xu/lsp-test/go/interface.go"
}
-- range actions
{ {
edit = {
documentChanges = { {
edits = { {
newText = '\nimport (\n\t"fmt"\n\t"log"\n)\n',
range = {
end = {
character = 0,
line = 1
},
start = {
character = 0,
line = 1
}
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
version = 0
}
} }
},
kind = "source.organizeImports",
title = "Organize Imports"
}, {
command = {
arguments = { {
Fix = "undeclared_name",
Range = {
end = {
character = 20,
line = 4
},
start = {
character = 16,
line = 4
}
},
URI = "file:///Users/ray.xu/lsp_test/go/ref.go"
} },
command = "gopls.apply_fix",
title = "undeclared name: rect"
},
diagnostics = { {
code = "UndeclaredName",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal#UndeclaredName"
},
message = "undeclared name: rect",
range = {
end = {
character = 20,
line = 4
},
start = {
character = 16,
line = 4
}
},
severity = 1,
source = "compiler",
tags = { 1 }
} },
edit = {},
kind = "quickfix",
title = "undeclared name: rect"
}, {
command = {
arguments = { {
Fix = "extract_function",
Range = {
end = {
character = 16,
line = 6
},
start = {
character = 0,
line = 4
}
},
URI = "file:///Users/ray.xu/lsp_test/go/ref.go"
} },
command = "gopls.apply_fix",
title = "Extract to function"
},
edit = {},
kind = "refactor.extract",
title = "Extract to function"
} }
-- code action
{ {
diagnostics = { {
code = "UndeclaredName",
codeDescription = {
href = "https://pkg.go.dev/golang.org/x/tools/internal/typesinternal#UndeclaredName"
},
message = "undeclared name: log",
range = {
end = {
character = 4,
line = 6
},
start = {
character = 1,
line = 6
}
},
severity = 1,
source = "compiler"
} },
edit = {
documentChanges = { {
edits = { {
newText = '\nimport "log"\n',
range = {
end = {
character = 0,
line = 1
},
start = {
character = 0,
line = 1
}
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
version = 0
}
} }
},
kind = "quickfix",
title = 'Add import: "log"'
}, {
edit = {
documentChanges = { {
edits = { {
newText = '\nimport (\n\t"fmt"\n\t"log"\n)\n',
range = {
end = {
character = 0,
line = 1
},
start = {
character = 0,
line = 1
}
}
} },
textDocument = {
uri = "file:///Users/ray.xu/lsp_test/go/ref.go",
version = 0
}
} }
},
kind = "source.organizeImports",
title = "Organize Imports"
} }

@ -0,0 +1,32 @@
local util = require "navigator.util"
local lsphelper = require "navigator.lspwrapper"
local gui = require "navigator.gui"
local log = require "navigator.util".log
-- local log = util.log
-- local partial = util.partial
-- local cwd = vim.fn.getcwd(0)
-- local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items
--vim.api.nvim_set_option("navtator_options", {width = 90, height = 60, location = require "navigator.location".center})
-- local options = vim.g.navtator_options or {width = 60, height = 40, location = location.center}
local function ref_hdlr(arg1, api, locations, num, bufnr)
local opts = {}
-- log("arg1", arg1)
-- log(api)
-- log("num", num)
-- log("bfnr", bufnr)
if locations == nil or vim.tbl_isempty(locations) then
print "References not found"
return
end
log(locations)
local items = locations_to_items(locations)
gui.new_list_view({items = items, api = 'Reference'})
end
return { reference_handler = ref_hdlr }

@ -0,0 +1,115 @@
local gui = require "navigator.gui"
local M = {}
local log = require "navigator.util".log
local lsphelper = require "navigator.lspwrapper"
local locations_to_items = lsphelper.locations_to_items
function M.document_symbols(opts)
opts = opts or {}
local params = vim.lsp.util.make_position_params()
params.context = {includeDeclaration = true}
params.query = ""
local results_lsp = vim.lsp.buf_request_sync(0, "textDocument/documentSymbol", params, opts.timeout or 10000)
local locations = {}
log(results_lsp)
for _, server_results in pairs(results_lsp) do
if server_results.result then
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result) or {})
end
end
local lines = {}
for _, loc in ipairs(locations) do
table.insert(lines, string.format("%s:%s:%s", loc.filename, loc.lnum, loc.text))
end
local cmd = table.concat(lines, "\n")
if #lines > 0 then
gui.new_list_view({data = lines})
else
print("symbols not found")
end
end
function M.workspace_symbols(opts)
opts = opts or {}
local params = vim.lsp.util.make_position_params()
params.context = {includeDeclaration = true}
params.query = ""
local results_lsp = vim.lsp.buf_request_sync(0, "workspace/symbol", params, opts.timeout or 10000)
log(results_lsp)
local locations = {}
for _, server_results in pairs(results_lsp) do
if server_results.result then
vim.list_extend(locations, vim.lsp.util.symbols_to_items(server_results.result) or {})
end
end
local lines = {}
for _, loc in ipairs(locations) do
table.insert(lines, string.format("%s:%s:%s", loc.filename, loc.lnum, loc.text))
end
if #lines > 0 then
gui.new_list_view({data = lines})
else
print("symbols not found")
end
end
function M.symbol_handler(_, _, result, _, bufnr)
if not result or vim.tbl_isempty(result) then
print("symbol not found")
return
end
-- log(result)
local locations = {}
for i = 1, #result do
local item = result[i].location
item.kind = result[i].kind
item.containerName = result[i].containerName
item.name = result[i].name
item.text = result[i].name
if #item.containerName > 0 then
item.text = item.text:gsub(item.containerName, '', 1)
end
table.insert(locations, item)
end
local items = locations_to_items(locations)
gui.new_list_view({items = items, prompt = true})
-- if locations == nil or vim.tbl_isempty(locations) then
-- print "References not found"
-- return
-- end
-- local items = locations_to_items(locations)
-- gui.new_list_view({items = items})
-- local filename = vim.api.nvim_buf_get_name(bufnr)
-- local items = vim.lsp.util.symbols_to_items(result, bufnr)
-- local data = {}
-- for i, item in pairs(action.items) do
-- data[i] = item.text
-- if filename ~= item.filename then
-- local cwd = vim.fn.getcwd(0) .. "/"
-- local add = util.get_relative_path(cwd, item.filename)
-- data[i] = data[i] .. " - " .. add
-- end
-- item.text = nil
-- end
-- opts.data = data
-- action.popup = popfix:new(opts)
-- if not action.popup then
-- action.items = nil
-- end
-- if action.popup.list then
-- util.setFiletype(action.popup.list.buffer, "lsputil_symbols_list")
-- end
-- if action.popup.preview then
-- util.setFiletype(action.popup.preview.buffer, "lsputil_symbols_preview")
-- end
-- if action.popup.prompt then
-- util.setFiletype(action.popup.prompt.buffer, "lsputil_symbols_prompt")
-- end
-- opts.data = nil
end
return M

@ -0,0 +1,231 @@
-- retreives data form file
-- and line to highlight
-- Some of function copied from https://github.com/RishabhRD/nvim-lsputils
local M = {
log_path = vim.lsp.get_log_path()
}
function M.get_data_from_file(filename, startLine)
local displayLine
if startLine < 3 then
displayLine = startLine
startLine = 0
else
startLine = startLine - 2
displayLine = 2
end
local uri = "file:///" .. filename
local bufnr = vim.uri_to_bufnr(uri)
vim.fn.bufload(bufnr)
local data = vim.api.nvim_buf_get_lines(bufnr, startLine, startLine + 8, false)
if data == nil or vim.tbl_isempty(data) then
startLine = nil
else
local len = #data
startLine = startLine + 1
for i = 1, len, 1 do
data[i] = startLine .. " " .. data[i]
startLine = startLine + 1
end
end
return {
data = data,
line = displayLine
}
end
function M.get_base(path)
local len = #path
for i = len, 1, -1 do
if path:sub(i, i) == "/" then
local ret = path:sub(i + 1, len)
return ret
end
end
end
local function getDir(path)
local data = {}
local len = #path
if len <= 1 then return nil end
local last_index = 1
for i = 2, len do
local cur_char = path:sub(i,i)
if cur_char == '/' then
local my_data = path:sub(last_index + 1, i - 1)
table.insert(data, my_data)
last_index = i
end
end
return data
end
function M.get_relative_path(base_path, my_path)
local base_data = getDir(base_path)
local my_data = getDir(my_path)
local base_len = #base_data
local my_len = #my_data
if base_len > my_len then
return my_path
end
if base_data[1] ~= my_data[1] then
return my_path
end
local cur = 0
for i = 1, base_len do
if base_data[i] ~= my_data[i] then
break
end
cur = i
end
local data = ""
for i = cur + 1, my_len do
data = data .. my_data[i] .. "/"
end
data = data .. M.get_base(my_path)
return data
end
local default_config = {
plugin = "navigator",
use_console = false,
use_file = true,
level = "info"
}
M._log = require('guihua.log').new({level='debug'}, true)
-- add log to you lsp.log
M.log = M._log.info
M.verbose = M._log.debug
function M.fmt(...)
M._log.fmt_info(...)
end
function M.split(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t = {}
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
table.insert(t, str)
end
return t
end
function M.trim_space(s)
return s:match("^%s*(.-)%s*$")
end
function M.quickfix_extract(line)
-- check if it is a line of file pos been selected
local split = M.split
line = M.trim_space(line)
local sep = split(line, " ")
if #sep < 2 then
M.log(line)
return nil
end
sep = split(sep[1], ":")
if #sep < 3 then
M.log(line)
return nil
end
local location = {uri = "file:///" .. sep[1], range = {start = {line = sep[2] - 3 > 0 and sep[2] - 3 or 1}}}
location.range["end"] = {line = sep[2] + 15}
return location
end
function M.getArgs(inputstr)
local sep = "%s"
local t = {}
local cmd
for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do
if not cmd then
cmd = str
else
table.insert(t, str)
end
end
return cmd, t
end
function M.p(t)
print(vim.inspect(t))
end
function M.printError(msg)
vim.cmd("echohl ErrorMsg")
vim.cmd(string.format([[echomsg '%s']], msg))
vim.cmd("echohl None")
end
function M.reload()
vim.lsp.stop_client(vim.lsp.get_active_clients())
vim.cmd [[edit]]
end
function M.open_log()
local path = vim.lsp.get_log_path()
vim.cmd("edit " .. path)
end
function table.pack(...)
return {n = select("#", ...), ...}
end
function M.show(...)
local string = ""
local args = table.pack(...)
for i = 1, args.n do
string = string .. tostring(args[i]) .. "\t"
end
return string .. "\n"
end
function M.split(s, sep)
local fields = {}
local sep = sep or " "
local pattern = string.format("([^%s]+)", sep)
string.gsub(
s,
pattern,
function(c)
fields[#fields + 1] = c
end
)
return fields
end
M.open_file = function(filename)
vim.api.nvim_command(string.format("e! %s", filename))
end
M.open_file_at = function(filename, line)
vim.api.nvim_command(string.format("e! +%s %s", line, filename))
end
function M.exists(var)
for k, _ in pairs(_G) do
if k == var then
return true
end
end
end
function M.partial(func, arg)
return (function(...)
return func(arg, ...)
end)
end
return M

@ -0,0 +1,24 @@
" built upon popfix api(https://github.com/RishabhRD/popfix)
" for parameter references see popfix readme.
if exists('g:loaded_navigator_lsp') | finish | endif
let s:save_cpo = &cpo
set cpo&vim
if ! exists('g:navigator_lsp_location_opts')
let g:navigator_lsp_location_opts = v:null
endif
if ! exists('g:navigator_lsp_symbols_opts')
let g:navigator_lsp_symbols_opts = v:null
endif
if ! exists('g:navigator_lsp_codeaction_opts')
let g:navigator_lsp_codeaction_opts = v:null
endif
let &cpo = s:save_cpo
unlet s:save_cpo
let g:loaded_navigator_lsp = 1
Loading…
Cancel
Save