build: selene + stylua

This commit is contained in:
Folke Lemaitre 2021-05-12 11:21:16 +02:00
parent 0f584688c3
commit 135d4e95dd
15 changed files with 924 additions and 799 deletions

View File

@ -1,36 +1,36 @@
local M = {}
local links = {
Error = "LspDiagnosticsDefaultError",
Warning = "LspDiagnosticsDefaultWarning",
Information = "LspDiagnosticsDefaultInformation",
Hint = "LspDiagnosticsDefaultHint",
SignError = "LspDiagnosticsSignError",
SignWarning = "LspDiagnosticsSignWarning",
SignInformation = "LspDiagnosticsSignInformation",
SignHint = "LspDiagnosticsSignHint",
TextError = "LspTroubleText",
TextWarning = "LspTroubleText",
TextInformation = "LspTroubleText",
TextHint = "LspTroubleText",
Text = "Normal",
File = "Directory",
Source = "Comment",
Code = "Comment",
Location = "LineNr",
FoldIcon = "CursorLineNr",
Normal = "Normal",
Count = "TabLineSel",
Preview = "Search",
Indent = "LineNr",
SignOther = "LspTroubleSignInformation"
Error = "LspDiagnosticsDefaultError",
Warning = "LspDiagnosticsDefaultWarning",
Information = "LspDiagnosticsDefaultInformation",
Hint = "LspDiagnosticsDefaultHint",
SignError = "LspDiagnosticsSignError",
SignWarning = "LspDiagnosticsSignWarning",
SignInformation = "LspDiagnosticsSignInformation",
SignHint = "LspDiagnosticsSignHint",
TextError = "LspTroubleText",
TextWarning = "LspTroubleText",
TextInformation = "LspTroubleText",
TextHint = "LspTroubleText",
Text = "Normal",
File = "Directory",
Source = "Comment",
Code = "Comment",
Location = "LineNr",
FoldIcon = "CursorLineNr",
Normal = "Normal",
Count = "TabLineSel",
Preview = "Search",
Indent = "LineNr",
SignOther = "LspTroubleSignInformation",
}
function M.setup()
for k, v in pairs(links) do
vim.api.nvim_command('hi def link LspTrouble' .. k .. ' ' .. v)
vim.api.nvim_command('hi def link Trouble' .. k .. ' LspTrouble' .. k)
end
for k, v in pairs(links) do
vim.api.nvim_command("hi def link LspTrouble" .. k .. " " .. v)
vim.api.nvim_command("hi def link Trouble" .. k .. " LspTrouble" .. k)
end
end
return M

View File

@ -5,45 +5,47 @@ M.namespace = vim.api.nvim_create_namespace("Trouble")
---@class Options
---@field buf number|nil
---@field win number|nil
-- TODO: make some options configurable per mode
-- TODO: make it possible to have multiple trouble lists open at the same time
local defaults = {
debug = false,
position = "bottom", -- position of the list can be: bottom, top, left, right
height = 10, -- height of the trouble list when position is top or bottom
width = 50, -- width of the list when position is left or right
icons = true, -- use devicons for filenames
mode = "lsp_workspace_diagnostics", -- "lsp_workspace_diagnostics", "lsp_document_diagnostics", "quickfix", "lsp_references", "loclist"
fold_open = "", -- icon used for open folds
fold_closed = "", -- icon used for closed folds
action_keys = { -- key mappings for actions in the trouble list
close = "q", -- close the list
cancel = "<esc>", -- cancel the preview and get back to your last window / buffer / cursor
refresh = "r", -- manually refresh
jump = {"<cr>", "<tab>"}, -- jump to the diagnostic or open / close folds
jump_close = {"o"}, -- jump to the diagnostic and close the list
toggle_mode = "m", -- toggle between "workspace" and "document" mode
toggle_preview = "P", -- toggle auto_preview
hover = "K", -- opens a small poup with the full multiline message
preview = "p", -- preview the diagnostic location
close_folds = {"zM", "zm"}, -- close all folds
open_folds = {"zR", "zr"}, -- open all folds
toggle_fold = {"zA", "za"}, -- toggle fold of current file
previous = "k", -- preview item
next = "j" -- next item
},
indent_lines = true, -- add an indent guide below the fold icons
auto_open = false, -- automatically open the list when you have diagnostics
auto_close = false, -- automatically close the list when you have no diagnostics
auto_preview = true, -- automatyically preview the location of the diagnostic. <esc> to close preview and go back to last window
auto_fold = false, -- automatically fold a file trouble list at creation
signs = {
-- icons / text used for a diagnostic
error = "",
warning = "",
hint = "",
information = "",
other = ""
},
use_lsp_diagnostic_signs = false -- enabling this will use the signs defined in your lsp client
debug = false,
position = "bottom", -- position of the list can be: bottom, top, left, right
height = 10, -- height of the trouble list when position is top or bottom
width = 50, -- width of the list when position is left or right
icons = true, -- use devicons for filenames
mode = "lsp_workspace_diagnostics", -- "lsp_workspace_diagnostics", "lsp_document_diagnostics", "quickfix", "lsp_references", "loclist"
fold_open = "", -- icon used for open folds
fold_closed = "", -- icon used for closed folds
action_keys = { -- key mappings for actions in the trouble list
close = "q", -- close the list
cancel = "<esc>", -- cancel the preview and get back to your last window / buffer / cursor
refresh = "r", -- manually refresh
jump = { "<cr>", "<tab>" }, -- jump to the diagnostic or open / close folds
jump_close = { "o" }, -- jump to the diagnostic and close the list
toggle_mode = "m", -- toggle between "workspace" and "document" mode
toggle_preview = "P", -- toggle auto_preview
hover = "K", -- opens a small poup with the full multiline message
preview = "p", -- preview the diagnostic location
close_folds = { "zM", "zm" }, -- close all folds
open_folds = { "zR", "zr" }, -- open all folds
toggle_fold = { "zA", "za" }, -- toggle fold of current file
previous = "k", -- preview item
next = "j", -- next item
},
indent_lines = true, -- add an indent guide below the fold icons
auto_open = false, -- automatically open the list when you have diagnostics
auto_close = false, -- automatically close the list when you have no diagnostics
auto_preview = true, -- automatyically preview the location of the diagnostic. <esc> to close preview and go back to last window
auto_fold = false, -- automatically fold a file trouble list at creation
signs = {
-- icons / text used for a diagnostic
error = "",
warning = "",
hint = "",
information = "",
other = "",
},
use_lsp_diagnostic_signs = false, -- enabling this will use the signs defined in your lsp client
}
---@type Options
@ -51,16 +53,16 @@ M.options = {}
---@return Options
function M.setup(options)
M.options = vim.tbl_deep_extend("force", {}, defaults, options or {})
M.fix_mode(M.options)
M.options = vim.tbl_deep_extend("force", {}, defaults, options or {})
M.fix_mode(M.options)
end
function M.fix_mode(opts)
if opts.mode == "workspace" then
opts.mode = "lsp_workspace_diagnostics"
elseif opts.mode == "document" then
opts.mode = "lsp_document_diagnostics"
end
if opts.mode == "workspace" then
opts.mode = "lsp_workspace_diagnostics"
elseif opts.mode == "document" then
opts.mode = "lsp_document_diagnostics"
end
end
M.setup()

View File

@ -5,14 +5,20 @@ local M = {}
M.folded = {}
function M.is_folded(filename)
local fold = M.folded[filename]
return (fold == nil and config.options.auto_fold == true) or (fold == true)
local fold = M.folded[filename]
return (fold == nil and config.options.auto_fold == true) or (fold == true)
end
function M.toggle(filename) M.folded[filename] = not M.is_folded(filename) end
function M.toggle(filename)
M.folded[filename] = not M.is_folded(filename)
end
function M.close(filename) M.folded[filename] = true end
function M.close(filename)
M.folded[filename] = true
end
function M.open(filename) M.folded[filename] = false end
function M.open(filename)
M.folded[filename] = false
end
return M

View File

@ -9,150 +9,191 @@ local Trouble = {}
local view
local function is_open() return view and view:is_valid() end
function Trouble.setup(options)
config.setup(options)
colors.setup()
local function is_open()
return view and view:is_valid()
end
function Trouble.close() if is_open() then view:close() end end
function Trouble.setup(options)
config.setup(options)
colors.setup()
end
function Trouble.close()
if is_open() then
view:close()
end
end
local function get_opts(opts)
opts = opts or {}
if type(opts) == "string" then opts = {mode = opts} end
config.fix_mode(opts)
return opts
opts = opts or {}
if type(opts) == "string" then
opts = { mode = opts }
end
config.fix_mode(opts)
return opts
end
function Trouble.open(opts)
opts = get_opts(opts)
if opts.mode and (opts.mode ~= config.options.mode) then
config.options.mode = opts.mode
end
opts.focus = true
opts = get_opts(opts)
if opts.mode and (opts.mode ~= config.options.mode) then
config.options.mode = opts.mode
end
opts.focus = true
if is_open() then
Trouble.refresh(opts)
else
view = View.create(opts)
end
if is_open() then
Trouble.refresh(opts)
else
view = View.create(opts)
end
end
function Trouble.toggle(opts)
opts = get_opts(opts)
opts = get_opts(opts)
if opts.mode and (opts.mode ~= config.options.mode) then
config.options.mode = opts.mode
Trouble.open()
return
end
if opts.mode and (opts.mode ~= config.options.mode) then
config.options.mode = opts.mode
Trouble.open()
return
end
if is_open() then
Trouble.close()
else
Trouble.open()
end
if is_open() then
Trouble.close()
else
Trouble.open()
end
end
function Trouble.help()
local lines = {"# Key Bindings"}
local height = 1
for command, key in pairs(config.options.action_keys) do
if type(key) == "table" then key = table.concat(key, " | ") end
table.insert(lines, " * **" .. key .. "** " .. command:gsub("_", " "))
height = height + 1
local lines = { "# Key Bindings" }
local height = 1
for command, key in pairs(config.options.action_keys) do
if type(key) == "table" then
key = table.concat(key, " | ")
end
-- help
vim.lsp.util.open_floating_preview(lines, "markdown", {
border = "single",
height = 20,
offset_y = -2,
offset_x = 2
})
table.insert(lines, " * **" .. key .. "** " .. command:gsub("_", " "))
height = height + 1
end
-- help
vim.lsp.util.open_floating_preview(lines, "markdown", {
border = "single",
height = 20,
offset_y = -2,
offset_x = 2,
})
end
local updater = util.debounce(100, function()
util.debug("refresh: auto")
view:update({auto = true})
util.debug("refresh: auto")
view:update({ auto = true })
end)
function Trouble.refresh(opts)
opts = opts or {}
opts = opts or {}
-- dont do an update if this is an automated refresh from a different provider
-- dont do an update if this is an automated refresh from a different provider
if opts.auto then
if opts.provider == "diagnostics" and config.options.mode == "lsp_document_diagnostics" then
opts.provider = "lsp_document_diagnostics"
elseif opts.provider == "diagnostics" and config.options.mode == "lsp_workspace_diagnostics" then
opts.provider = "lsp_workspace_diagnostics"
elseif opts.provider == "qf" and config.options.mode == "quickfix" then
opts.provider = "quickfix"
elseif opts.provider == "qf" and config.options.mode == "loclist" then
opts.provider = "loclist"
end
if opts.provider ~= config.options.mode then
return
end
end
if is_open() then
if opts.auto then
if opts.provider == "diagnostics" and config.options.mode ==
"lsp_document_diagnostics" then
opts.provider = "lsp_document_diagnostics"
elseif opts.provider == "diagnostics" and config.options.mode ==
"lsp_workspace_diagnostics" then
opts.provider = "lsp_workspace_diagnostics"
elseif opts.provider == "qf" and config.options.mode == "quickfix" then
opts.provider = "quickfix"
elseif opts.provider == "qf" and config.options.mode == "loclist" then
opts.provider = "loclist"
end
if opts.provider ~= config.options.mode then return end
updater()
else
util.debug("refresh")
view:update(opts)
end
if is_open() then
if opts.auto then
updater()
else
util.debug("refresh")
view:update(opts)
end
elseif opts.auto and config.options.auto_open and opts.mode ==
config.options.mode then
local items = require("trouble.providers").get(
vim.api.nvim_get_current_win(),
vim.api.nvim_get_current_buf(), config.options)
if #items > 0 then Trouble.open(opts) end
elseif opts.auto and config.options.auto_open and opts.mode == config.options.mode then
local items = require("trouble.providers").get(
vim.api.nvim_get_current_win(),
vim.api.nvim_get_current_buf(),
config.options
)
if #items > 0 then
Trouble.open(opts)
end
end
end
function Trouble.action(action)
if action == "toggle_mode" then
if config.options.mode == "lsp_document_diagnostics" then
config.options.mode = "lsp_workspace_diagnostics"
elseif config.options.mode == "lsp_workspace_diagnostics" then
config.options.mode = "lsp_document_diagnostics"
end
action = "refresh"
if action == "toggle_mode" then
if config.options.mode == "lsp_document_diagnostics" then
config.options.mode = "lsp_workspace_diagnostics"
elseif config.options.mode == "lsp_workspace_diagnostics" then
config.options.mode = "lsp_document_diagnostics"
end
action = "refresh"
end
if view and action == "on_win_enter" then view:on_win_enter() end
if not is_open() then return end
if action == "hover" then view:hover() end
if action == "jump" then view:jump() end
if action == "jump_close" then
view:jump()
Trouble.close()
end
if action == "open_folds" then Trouble.refresh({open_folds = true}) end
if action == "close_folds" then Trouble.refresh({close_folds = true}) end
if action == "toggle_fold" then view:toggle_fold() end
if action == "on_enter" then view:on_enter() end
if action == "on_leave" then view:on_leave() end
if action == "cancel" then view:switch_to_parent() end
if action == "next" then view:next_item() end
if action == "previous" then view:previous_item() end
if view and action == "on_win_enter" then
view:on_win_enter()
end
if not is_open() then
return
end
if action == "hover" then
view:hover()
end
if action == "jump" then
view:jump()
end
if action == "jump_close" then
view:jump()
Trouble.close()
end
if action == "open_folds" then
Trouble.refresh({ open_folds = true })
end
if action == "close_folds" then
Trouble.refresh({ close_folds = true })
end
if action == "toggle_fold" then
view:toggle_fold()
end
if action == "on_enter" then
view:on_enter()
end
if action == "on_leave" then
view:on_leave()
end
if action == "cancel" then
view:switch_to_parent()
end
if action == "next" then
view:next_item()
end
if action == "previous" then
view:previous_item()
end
if action == "toggle_preview" then
config.options.auto_preview = not config.options.auto_preview
if not config.options.auto_preview then
view:close_preview()
else
action = "preview"
end
if action == "toggle_preview" then
config.options.auto_preview = not config.options.auto_preview
if not config.options.auto_preview then
view:close_preview()
else
action = "preview"
end
if action == "auto_preview" and config.options.auto_preview then
action = "preview"
end
if action == "preview" then view:preview() end
end
if action == "auto_preview" and config.options.auto_preview then
action = "preview"
end
if action == "preview" then
view:preview()
end
if Trouble[action] then Trouble[action]() end
if Trouble[action] then
Trouble[action]()
end
end
return Trouble

View File

@ -6,54 +6,56 @@ local lsp = require("trouble.providers.lsp")
local M = {}
M.providers = {
lsp_workspace_diagnostics = lsp.diagnostics,
lsp_document_diagnostics = lsp.diagnostics,
lsp_references = lsp.references,
lsp_definitions = lsp.definitions,
quickfix = qf.qflist,
loclist = qf.loclist,
telescope = telescope.telescope
lsp_workspace_diagnostics = lsp.diagnostics,
lsp_document_diagnostics = lsp.diagnostics,
lsp_references = lsp.references,
lsp_definitions = lsp.definitions,
quickfix = qf.qflist,
loclist = qf.loclist,
telescope = telescope.telescope,
}
---@param options Options
function M.get(win, buf, cb, options)
local name = options.mode
local provider = M.providers[name]
local name = options.mode
local provider = M.providers[name]
if not provider then
local ok, mod = pcall(require, "trouble.providers." .. name)
if ok then
M.providers[name] = mod
provider = mod
end
if not provider then
local ok, mod = pcall(require, "trouble.providers." .. name)
if ok then
M.providers[name] = mod
provider = mod
end
end
if not provider then
util.error(("invalid provider %q"):format(name))
return {}
end
if not provider then
util.error(("invalid provider %q"):format(name))
return {}
end
provider(win, buf, function(items)
table.sort(items, function(a, b)
if a.severity == b.severity then
return a.lnum < b.lnum
else
return a.severity < b.severity
end
end)
cb(items)
end, options)
provider(win, buf, function(items)
table.sort(items, function(a, b)
if a.severity == b.severity then
return a.lnum < b.lnum
else
return a.severity < b.severity
end
end)
cb(items)
end, options)
end
---@param items Item[]
---@return table<string, Item[]>
function M.group(items)
local ret = {}
for _, item in ipairs(items) do
if ret[item.filename] == nil then ret[item.filename] = {} end
table.insert(ret[item.filename], item)
local ret = {}
for _, item in ipairs(items) do
if ret[item.filename] == nil then
ret[item.filename] = {}
end
return ret
table.insert(ret[item.filename], item)
end
return ret
end
return M

View File

@ -6,72 +6,74 @@ local M = {}
---@param options Options
---@return Item[]
function M.diagnostics(win, buf, cb, options)
if options.mode == "lsp_workspace_diagnostics" then buf = nil end
local buffer_diags = buf and {[buf] = vim.lsp.diagnostic.get(buf, nil)} or
vim.lsp.diagnostic.get_all()
function M.diagnostics(_win, buf, cb, options)
if options.mode == "lsp_workspace_diagnostics" then
buf = nil
end
local buffer_diags = buf and { [buf] = vim.lsp.diagnostic.get(buf, nil) } or vim.lsp.diagnostic.get_all()
local items = util.locations_to_items(buffer_diags, 1)
if #items == 0 then util.warn("no diagnostics found") end
cb(items)
local items = util.locations_to_items(buffer_diags, 1)
if #items == 0 then
util.warn("no diagnostics found")
end
cb(items)
end
---@return Item[]
function M.references(win, buf, cb, options)
local method = "textDocument/references"
local params = util.make_position_params(win, buf)
params.context = {includeDeclaration = true}
lsp.buf_request(buf, method, params,
function(err, method, result, client_id, bufnr, config)
if err then
util.error("an error happened getting references: " .. err)
return cb({})
end
if result == nil or #result == 0 then
util.warn("No referenes found")
return cb({})
end
local ret = util.locations_to_items({result}, 0)
cb(ret)
end)
function M.references(win, buf, cb, _options)
local method = "textDocument/references"
local params = util.make_position_params(win, buf)
params.context = { includeDeclaration = true }
lsp.buf_request(buf, method, params, function(err, _method, result, _client_id, _bufnr, _config)
if err then
util.error("an error happened getting references: " .. err)
return cb({})
end
if result == nil or #result == 0 then
util.warn("No referenes found")
return cb({})
end
local ret = util.locations_to_items({ result }, 0)
cb(ret)
end)
end
---@return Item[]
function M.definitions(win, buf, cb, options)
local method = "textDocument/definition"
local params = util.make_position_params(win, buf)
params.context = {includeDeclaration = true}
lsp.buf_request(buf, method, params,
function(err, method, result, client_id, bufnr, config)
if err then
util.error("an error happened getting definitions: " .. err)
return cb({})
end
if result == nil or #result == 0 then
util.warn("No definitions found")
return cb({})
end
for _, value in ipairs(result) do
value.uri = value.targetUri
value.range = value.targetSelectionRange
end
local ret = util.locations_to_items({result}, 0)
cb(ret)
end)
function M.definitions(win, buf, cb, _options)
local method = "textDocument/definition"
local params = util.make_position_params(win, buf)
params.context = { includeDeclaration = true }
lsp.buf_request(buf, method, params, function(err, _method, result, _client_id, _bufnr, _config)
if err then
util.error("an error happened getting definitions: " .. err)
return cb({})
end
if result == nil or #result == 0 then
util.warn("No definitions found")
return cb({})
end
for _, value in ipairs(result) do
value.uri = value.targetUri
value.range = value.targetSelectionRange
end
local ret = util.locations_to_items({ result }, 0)
cb(ret)
end)
end
function M.get_signs()
local signs = {}
for _, v in pairs(util.severity) do
-- pcall to catch entirely unbound or cleared out sign hl group
local status, sign = pcall(function()
return vim.trim(vim.fn.sign_getdefined("LspDiagnosticsSign" .. v)[1]
.text)
end)
if not status then sign = v:sub(1, 1) end
signs[string.lower(v)] = sign
local signs = {}
for _, v in pairs(util.severity) do
-- pcall to catch entirely unbound or cleared out sign hl group
local status, sign = pcall(function()
return vim.trim(vim.fn.sign_getdefined("LspDiagnosticsSign" .. v)[1].text)
end)
if not status then
sign = v:sub(1, 1)
end
return signs
signs[string.lower(v)] = sign
end
return signs
end
return M

View File

@ -2,38 +2,41 @@ local util = require("trouble.util")
local M = {}
local severities = {E = 1, W = 2, I = 3, H = 4}
local severities = { E = 1, W = 2, I = 3, H = 4 }
function M.get_list(winid)
local list = winid == nil and vim.fn.getqflist({all = true}) or
vim.fn.getloclist(winid, {all = true})
local list = winid == nil and vim.fn.getqflist({ all = true }) or vim.fn.getloclist(winid, { all = true })
local ret = {}
for _, item in pairs(list.items) do
local row = (item.lnum == 0 and 1 or item.lnum) - 1
local col = (item.col == 0 and 1 or item.col) - 1
local ret = {}
for _, item in pairs(list.items) do
local row = (item.lnum == 0 and 1 or item.lnum) - 1
local col = (item.col == 0 and 1 or item.col) - 1
local pitem = {
row = row,
col = col,
message = item.text,
severity = severities[item.type] or 0,
range = {
start = {line = row, character = col},
["end"] = {line = row, character = -1}
}
}
local pitem = {
row = row,
col = col,
message = item.text,
severity = severities[item.type] or 0,
range = {
start = { line = row, character = col },
["end"] = { line = row, character = -1 },
},
}
table.insert(ret, util.process_item(pitem, item.bufnr))
end
if #ret == 0 then
util.warn("the " .. (winid and "loclist" or "qflist") .. " is empty")
end
return ret
table.insert(ret, util.process_item(pitem, item.bufnr))
end
if #ret == 0 then
util.warn("the " .. (winid and "loclist" or "qflist") .. " is empty")
end
return ret
end
function M.loclist(win, buf, cb, options) return cb(M.get_list(win)) end
function M.loclist(win, _buf, cb, _options)
return cb(M.get_list(win))
end
function M.qflist(win, buf, cb, options) return cb(M.get_list()) end
function M.qflist(_win, _buf, cb, _options)
return cb(M.get_list())
end
return M

View File

@ -3,45 +3,44 @@ local M = {}
M.results = {}
function M.open_with_trouble(prompt_bufnr, mode)
local action_state = require('telescope.actions.state')
local actions = require('telescope.actions')
local picker = action_state.get_current_picker(prompt_bufnr)
local manager = picker.manager
function M.open_with_trouble(prompt_bufnr, _mode)
local action_state = require("telescope.actions.state")
local actions = require("telescope.actions")
local picker = action_state.get_current_picker(prompt_bufnr)
local manager = picker.manager
M.results = {}
for item in manager:iter() do
local row = (item.lnum or 1) - 1
local col = (item.col or 1) - 1
M.results = {}
for item in manager:iter() do
local row = (item.lnum or 1) - 1
local col = (item.col or 1) - 1
if not item.bufnr then
item.bufnr = vim.fn.bufnr(item.filename, true)
end
local pitem = {
row = row,
col = col,
message = item.text,
severity = 0,
range = {
start = {line = row, character = col},
["end"] = {line = row, character = -1}
}
}
table.insert(M.results, util.process_item(pitem, item.bufnr))
if not item.bufnr then
item.bufnr = vim.fn.bufnr(item.filename, true)
end
actions.close(prompt_bufnr)
require("trouble").open({mode = "telescope"})
local pitem = {
row = row,
col = col,
message = item.text,
severity = 0,
range = {
start = { line = row, character = col },
["end"] = { line = row, character = -1 },
},
}
table.insert(M.results, util.process_item(pitem, item.bufnr))
end
actions.close(prompt_bufnr)
require("trouble").open({ mode = "telescope" })
end
function M.telescope(win, buf, cb, options)
if #M.results == 0 then
util.warn(
"No Telescope results found. Open Telescopen and send results to Trouble first. Refer to the documentation for more info.")
end
cb(M.results)
function M.telescope(_win, _buf, cb, _options)
if #M.results == 0 then
util.warn("No Telescope results found. Open Telescopen and send results to Trouble first. Refer to the documentation for more info.")
end
cb(M.results)
end
return M

View File

@ -10,59 +10,64 @@ local renderer = {}
local signs = {}
local function get_icon(file)
local ok, icons = pcall(require, "nvim-web-devicons")
if not ok then
util.warn(
"'nvim-web-devicons' is not installed. Install it, or set icons=false in your configuration to disable this message")
return ""
end
local fname = vim.fn.fnamemodify(file, ":t")
local ext = vim.fn.fnamemodify(file, ":e")
return icons.get_icon(fname, ext, {default = true})
local ok, icons = pcall(require, "nvim-web-devicons")
if not ok then
util.warn("'nvim-web-devicons' is not installed. Install it, or set icons=false in your configuration to disable this message")
return ""
end
local fname = vim.fn.fnamemodify(file, ":t")
local ext = vim.fn.fnamemodify(file, ":e")
return icons.get_icon(fname, ext, { default = true })
end
local function update_signs()
signs = config.options.signs
if config.options.use_lsp_diagnostic_signs then
local lsp_signs = require("trouble.providers.lsp").get_signs()
signs = vim.tbl_deep_extend("force", {}, signs, lsp_signs)
end
signs = config.options.signs
if config.options.use_lsp_diagnostic_signs then
local lsp_signs = require("trouble.providers.lsp").get_signs()
signs = vim.tbl_deep_extend("force", {}, signs, lsp_signs)
end
end
---@param view View
function renderer.render(view, opts)
opts = opts or {}
local buf = vim.api.nvim_win_get_buf(view.parent)
providers.get(view.parent, buf, function(items)
local grouped = providers.group(items)
local count = util.count(grouped)
opts = opts or {}
local buf = vim.api.nvim_win_get_buf(view.parent)
providers.get(view.parent, buf, function(items)
local grouped = providers.group(items)
local count = util.count(grouped)
-- check for auto close
if opts.auto and config.options.auto_close then
if count == 0 then
view:close()
return
end
end
-- check for auto close
if opts.auto and config.options.auto_close then
if count == 0 then
view:close()
return
end
end
-- Update lsp signs
update_signs()
-- Update lsp signs
update_signs()
local text = Text:new()
view.items = {}
local text = Text:new()
view.items = {}
text:nl()
text:nl()
-- render file groups
for filename, group_items in pairs(grouped) do
if opts.open_folds then folds.open(filename) end
if opts.close_folds then folds.close(filename) end
renderer.render_file(view, text, filename, group_items)
end
-- render file groups
for filename, group_items in pairs(grouped) do
if opts.open_folds then
folds.open(filename)
end
if opts.close_folds then
folds.close(filename)
end
renderer.render_file(view, text, filename, group_items)
end
view:render(text)
if opts.focus then view:focus() end
end, config.options)
view:render(text)
if opts.focus then
view:focus()
end
end, config.options)
end
---@param view View
@ -70,62 +75,68 @@ end
---@param items Item[]
---@param filename string
function renderer.render_file(view, text, filename, items)
view.items[text.lineNr + 1] = {filename = filename, is_file = true}
view.items[text.lineNr + 1] = { filename = filename, is_file = true }
local count = util.count(items)
local count = util.count(items)
text:render(" ")
text:render(" ")
if folds.is_folded(filename) then
text:render(config.options.fold_closed, "FoldIcon", " ")
else
text:render(config.options.fold_open, "FoldIcon", " ")
end
if folds.is_folded(filename) then
text:render(config.options.fold_closed, "FoldIcon", " ")
else
text:render(config.options.fold_open, "FoldIcon", " ")
end
if config.options.icons then
local icon, icon_hl = get_icon(filename)
text:render(icon, icon_hl, {exact = true, append = " "})
end
if config.options.icons then
local icon, icon_hl = get_icon(filename)
text:render(icon, icon_hl, { exact = true, append = " " })
end
text:render(vim.fn.fnamemodify(filename, ":p:."), "File", " ")
text:render(" " .. count .. " ", "Count")
text:nl()
text:render(vim.fn.fnamemodify(filename, ":p:."), "File", " ")
text:render(" " .. count .. " ", "Count")
text:nl()
if not folds.is_folded(filename) then
renderer.render_diagnostics(view, text, items)
end
if not folds.is_folded(filename) then
renderer.render_diagnostics(view, text, items)
end
end
---@param view View
---@param text Text
---@param items Item[]
function renderer.render_diagnostics(view, text, items)
for _, diag in ipairs(items) do
view.items[text.lineNr + 1] = diag
for _, diag in ipairs(items) do
view.items[text.lineNr + 1] = diag
local sign = diag.sign or signs[string.lower(diag.type)]
if not sign then sign = diag.type end
local indent = " "
if config.options.indent_lines then indent = "" end
local sign_hl = diag.sign_hl or ("TroubleSign" .. diag.type)
text:render(indent, "Indent")
text:render(sign .. " ", sign_hl, {exact = true})
text:render(diag.text, "Text" .. diag.type, " ")
-- text:render(diag.type, diag.type, " ")
if diag.source then text:render(diag.source, "Source") end
if diag.code and diag.code ~= vim.NIL then
text:render("(" .. diag.code .. ")", "Code")
end
text:render(" ")
text:render("[" .. diag.lnum .. ", " .. diag.col .. "]", "Location")
text:nl()
local sign = diag.sign or signs[string.lower(diag.type)]
if not sign then
sign = diag.type
end
local indent = " "
if config.options.indent_lines then
indent = ""
end
local sign_hl = diag.sign_hl or ("TroubleSign" .. diag.type)
text:render(indent, "Indent")
text:render(sign .. " ", sign_hl, { exact = true })
text:render(diag.text, "Text" .. diag.type, " ")
-- text:render(diag.type, diag.type, " ")
if diag.source then
text:render(diag.source, "Source")
end
if diag.code and diag.code ~= vim.NIL then
text:render("(" .. diag.code .. ")", "Code")
end
text:render(" ")
text:render("[" .. diag.lnum .. ", " .. diag.col .. "]", "Location")
text:nl()
end
end
return renderer

View File

@ -7,38 +7,46 @@ local Text = {}
Text.__index = Text
function Text:new()
local this = {lines = {}, hl = {}, lineNr = 0, current = ""}
setmetatable(this, self)
return this
local this = { lines = {}, hl = {}, lineNr = 0, current = "" }
setmetatable(this, self)
return this
end
function Text:nl()
table.insert(self.lines, self.current)
self.current = ""
self.lineNr = self.lineNr + 1
table.insert(self.lines, self.current)
self.current = ""
self.lineNr = self.lineNr + 1
end
function Text:render(str, group, opts)
str = str:gsub("[\n]", " ")
if type(opts) == "string" then opts = {append = opts} end
opts = opts or {}
str = str:gsub("[\n]", " ")
if type(opts) == "string" then
opts = { append = opts }
end
opts = opts or {}
if group then
if opts.exact ~= true then group = "Trouble" .. group end
local from = string.len(self.current)
---@class Highlight
local hl
hl = {
line = self.lineNr,
from = from,
to = from + string.len(str),
group = group
}
table.insert(self.hl, hl)
if group then
if opts.exact ~= true then
group = "Trouble" .. group
end
self.current = self.current .. str
if opts.append then self.current = self.current .. opts.append end
if opts.nl then self:nl() end
local from = string.len(self.current)
---@class Highlight
local hl
hl = {
line = self.lineNr,
from = from,
to = from + string.len(str),
group = group,
}
table.insert(self.hl, hl)
end
self.current = self.current .. str
if opts.append then
self.current = self.current .. opts.append
end
if opts.nl then
self:nl()
end
end
return Text

View File

@ -3,136 +3,149 @@ local config = require("trouble.config")
local M = {}
function M.count(tab)
local count = 0
for _ in pairs(tab) do count = count + 1 end
return count
local count = 0
for _ in pairs(tab) do
count = count + 1
end
return count
end
function M.log(msg, hl)
hl = hl or "MsgArea"
vim.api.nvim_echo({{'[Trouble] ', hl}, {msg}}, true, {})
hl = hl or "MsgArea"
vim.api.nvim_echo({ { "[Trouble] ", hl }, { msg } }, true, {})
end
function M.warn(msg) M.log(msg, "WarningMsg") end
function M.warn(msg)
M.log(msg, "WarningMsg")
end
function M.error(msg) M.log(msg, "Error") end
function M.error(msg)
M.log(msg, "Error")
end
function M.debug(msg) if config.options.debug then M.log(msg) end end
function M.debug(msg)
if config.options.debug then
M.log(msg)
end
end
function M.debounce(ms, fn)
local timer = vim.loop.new_timer()
return function(...)
local argv = {...}
timer:start(ms, 0, function()
timer:stop()
vim.schedule_wrap(fn)(unpack(argv))
end)
end
local timer = vim.loop.new_timer()
return function(...)
local argv = { ... }
timer:start(ms, 0, function()
timer:stop()
vim.schedule_wrap(fn)(unpack(argv))
end)
end
end
function M.throttle(ms, fn)
local timer = vim.loop.new_timer()
local running = false
return function(...)
if not running then
local argv = {...}
local argc = select('#', ...)
local timer = vim.loop.new_timer()
local running = false
return function(...)
if not running then
local argv = { ... }
local argc = select("#", ...)
timer:start(ms, 0, function()
running = false
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
end)
running = true
end
timer:start(ms, 0, function()
running = false
pcall(vim.schedule_wrap(fn), unpack(argv, 1, argc))
end)
running = true
end
end
end
M.severity = {
[0] = "Other",
[1] = "Error",
[2] = "Warning",
[3] = "Information",
[4] = "Hint"
[0] = "Other",
[1] = "Error",
[2] = "Warning",
[3] = "Information",
[4] = "Hint",
}
-- based on the Telescope diagnostics code
-- see https://github.com/nvim-telescope/telescope.nvim/blob/0d6cd47990781ea760dd3db578015c140c7b9fa7/lua/telescope/utils.lua#L85
function M.process_item(item, bufnr)
local filename = vim.api.nvim_buf_get_name(bufnr)
local start = item.range["start"]
local finish = item.range["end"]
local row = start.line
local col = start.character
local line =
(vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or {""})[1]
item.message = item.message or line or ""
local filename = vim.api.nvim_buf_get_name(bufnr)
local start = item.range["start"]
local finish = item.range["end"]
local row = start.line
local col = start.character
local line = (vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
item.message = item.message or line or ""
---@class Item
---@field is_file boolean
---@field fixed boolean
local ret
ret = {
bufnr = bufnr,
filename = filename,
lnum = row + 1,
col = col + 1,
start = start,
finish = finish,
sign = item.sign,
sign_hl = item.sign_hl,
-- remove line break to avoid display issues
text = vim.trim(item.message:gsub("[\n]", "")):sub(0, vim.o.columns),
line = line,
full_text = vim.trim(item.message),
type = M.severity[item.severity] or M.severity[0],
code = item.code,
source = item.source,
severity = item.severity or 0
}
return ret
---@class Item
---@field is_file boolean
---@field fixed boolean
local ret
ret = {
bufnr = bufnr,
filename = filename,
lnum = row + 1,
col = col + 1,
start = start,
finish = finish,
sign = item.sign,
sign_hl = item.sign_hl,
-- remove line break to avoid display issues
text = vim.trim(item.message:gsub("[\n]", "")):sub(0, vim.o.columns),
line = line,
full_text = vim.trim(item.message),
type = M.severity[item.severity] or M.severity[0],
code = item.code,
source = item.source,
severity = item.severity or 0,
}
return ret
end
-- takes either a table indexed by bufnr, or an lsp result with uri
---@return Item[]
function M.locations_to_items(results, default_severity)
default_severity = default_severity or 0
local ret = {}
for bufnr, locs in pairs(results or {}) do
for _, loc in pairs(locs.result or locs) do
if not vim.tbl_isempty(loc) then
local buf = loc.uri and vim.uri_to_bufnr(loc.uri) or bufnr
loc.severity = loc.severity or default_severity
-- load the buffer when needed
vim.fn.bufload(buf)
table.insert(ret, M.process_item(loc, buf))
end
end
default_severity = default_severity or 0
local ret = {}
for bufnr, locs in pairs(results or {}) do
for _, loc in pairs(locs.result or locs) do
if not vim.tbl_isempty(loc) then
local buf = loc.uri and vim.uri_to_bufnr(loc.uri) or bufnr
loc.severity = loc.severity or default_severity
-- load the buffer when needed
vim.fn.bufload(buf)
table.insert(ret, M.process_item(loc, buf))
end
end
return ret
end
return ret
end
-- @private
local function make_position_param(win, buf)
local row, col = unpack(vim.api.nvim_win_get_cursor(win))
row = row - 1
local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then return {line = 0, character = 0} end
col = vim.str_utfindex(line, col)
return {line = row, character = col}
local row, col = unpack(vim.api.nvim_win_get_cursor(win))
row = row - 1
local line = vim.api.nvim_buf_get_lines(buf, row, row + 1, true)[1]
if not line then
return { line = 0, character = 0 }
end
col = vim.str_utfindex(line, col)
return { line = row, character = col }
end
function M.make_text_document_params(buf) return {uri = vim.uri_from_bufnr(buf)} end
function M.make_text_document_params(buf)
return { uri = vim.uri_from_bufnr(buf) }
end
--- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position.
---
-- @returns `TextDocumentPositionParams` object
-- @see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams
function M.make_position_params(win, buf)
return {
textDocument = M.make_text_document_params(buf),
position = make_position_param(win, buf)
}
return {
textDocument = M.make_text_document_params(buf),
position = make_position_param(win, buf),
}
end
return M

View File

@ -20,413 +20,445 @@ View.__index = View
local hl_bufs = {}
local function clear_hl(bufnr)
if vim.api.nvim_buf_is_valid(bufnr) then
vim.api.nvim_buf_clear_namespace(bufnr, config.namespace, 0, -1)
end
if vim.api.nvim_buf_is_valid(bufnr) then
vim.api.nvim_buf_clear_namespace(bufnr, config.namespace, 0, -1)
end
end
---Find a rogue Trouble buffer that might have been spawned by i.e. a session.
local function find_rogue_buffer()
for _, v in ipairs(vim.api.nvim_list_bufs()) do
if vim.fn.bufname(v) == "Trouble" then return v end
for _, v in ipairs(vim.api.nvim_list_bufs()) do
if vim.fn.bufname(v) == "Trouble" then
return v
end
return nil
end
return nil
end
---Find pre-existing Trouble buffer, delete its windows then wipe it.
---@private
local function wipe_rogue_buffer()
local bn = find_rogue_buffer()
if bn then
local win_ids = vim.fn.win_findbuf(bn)
for _, id in ipairs(win_ids) do
if vim.fn.win_gettype(id) ~= "autocmd" then
vim.api.nvim_win_close(id, true)
end
end
vim.api.nvim_buf_set_name(bn, "")
vim.schedule(function() pcall(vim.api.nvim_buf_delete, bn, {}) end)
local bn = find_rogue_buffer()
if bn then
local win_ids = vim.fn.win_findbuf(bn)
for _, id in ipairs(win_ids) do
if vim.fn.win_gettype(id) ~= "autocmd" then
vim.api.nvim_win_close(id, true)
end
end
vim.api.nvim_buf_set_name(bn, "")
vim.schedule(function()
pcall(vim.api.nvim_buf_delete, bn, {})
end)
end
end
function View:new(opts)
opts = opts or {}
local this = {
buf = vim.api.nvim_get_current_buf(),
win = opts.win or vim.api.nvim_get_current_win(),
parent = opts.parent,
items = {}
}
setmetatable(this, self)
return this
opts = opts or {}
local this = {
buf = vim.api.nvim_get_current_buf(),
win = opts.win or vim.api.nvim_get_current_win(),
parent = opts.parent,
items = {},
}
setmetatable(this, self)
return this
end
function View:set_option(name, value, win)
if win then
return vim.api.nvim_win_set_option(self.win, name, value)
else
return vim.api.nvim_buf_set_option(self.buf, name, value)
end
if win then
return vim.api.nvim_win_set_option(self.win, name, value)
else
return vim.api.nvim_buf_set_option(self.buf, name, value)
end
end
---@param text Text
function View:render(text)
self:unlock()
self:set_lines(text.lines)
self:lock()
clear_hl(self.buf)
for _, data in ipairs(text.hl) do
highlight(self.buf, config.namespace, data.group, data.line, data.from,
data.to)
end
self:unlock()
self:set_lines(text.lines)
self:lock()
clear_hl(self.buf)
for _, data in ipairs(text.hl) do
highlight(self.buf, config.namespace, data.group, data.line, data.from, data.to)
end
end
function View:clear()
return vim.api.nvim_buf_set_lines(self.buf, 0, -1, false, {})
return vim.api.nvim_buf_set_lines(self.buf, 0, -1, false, {})
end
function View:unlock()
self:set_option("modifiable", true)
self:set_option("readonly", false)
self:set_option("modifiable", true)
self:set_option("readonly", false)
end
function View:lock()
self:set_option("readonly", true)
self:set_option("modifiable", false)
self:set_option("readonly", true)
self:set_option("modifiable", false)
end
function View:set_lines(lines, first, last, strict)
first = first or 0
last = last or -1
strict = strict or false
return vim.api.nvim_buf_set_lines(self.buf, first, last, strict, lines)
first = first or 0
last = last or -1
strict = strict or false
return vim.api.nvim_buf_set_lines(self.buf, first, last, strict, lines)
end
function View:is_valid()
return vim.api.nvim_buf_is_valid(self.buf) and
vim.api.nvim_buf_is_loaded(self.buf)
return vim.api.nvim_buf_is_valid(self.buf) and vim.api.nvim_buf_is_loaded(self.buf)
end
function View:update(opts)
util.debug("update")
renderer.render(self, opts)
util.debug("update")
renderer.render(self, opts)
end
function View:setup(opts)
util.debug("setup")
opts = opts or {}
vim.cmd("setlocal nonu")
vim.cmd("setlocal nornu")
if not pcall(vim.api.nvim_buf_set_name, self.buf, 'Trouble') then
wipe_rogue_buffer()
vim.api.nvim_buf_set_name(self.buf, 'Trouble')
end
self:set_option("bufhidden", "wipe")
self:set_option("filetype", "Trouble")
self:set_option("buftype", "nofile")
self:set_option("swapfile", false)
self:set_option("buflisted", false)
self:set_option("winfixwidth", true, true)
self:set_option("spell", false, true)
self:set_option("list", false, true)
self:set_option("winfixheight", true, true)
self:set_option("signcolumn", "no", true)
self:set_option("foldmethod", "manual", true)
self:set_option("foldcolumn", "0", true)
self:set_option("foldlevel", 3, true)
self:set_option("foldenable", false, true)
self:set_option("winhighlight",
"Normal:TroubleNormal,EndOfBuffer:TroubleNormal,SignColumn:TroubleNormal",
true)
self:set_option("fcs", "eob: ", true)
util.debug("setup")
opts = opts or {}
vim.cmd("setlocal nonu")
vim.cmd("setlocal nornu")
if not pcall(vim.api.nvim_buf_set_name, self.buf, "Trouble") then
wipe_rogue_buffer()
vim.api.nvim_buf_set_name(self.buf, "Trouble")
end
self:set_option("bufhidden", "wipe")
self:set_option("filetype", "Trouble")
self:set_option("buftype", "nofile")
self:set_option("swapfile", false)
self:set_option("buflisted", false)
self:set_option("winfixwidth", true, true)
self:set_option("spell", false, true)
self:set_option("list", false, true)
self:set_option("winfixheight", true, true)
self:set_option("signcolumn", "no", true)
self:set_option("foldmethod", "manual", true)
self:set_option("foldcolumn", "0", true)
self:set_option("foldlevel", 3, true)
self:set_option("foldenable", false, true)
self:set_option("winhighlight", "Normal:TroubleNormal,EndOfBuffer:TroubleNormal,SignColumn:TroubleNormal", true)
self:set_option("fcs", "eob: ", true)
for action, keys in pairs(config.options.action_keys) do
if type(keys) == "string" then keys = {keys} end
for _, key in pairs(keys) do
vim.api.nvim_buf_set_keymap(self.buf, "n", key,
[[<cmd>lua require("trouble").action("]] ..
action .. [[")<cr>]],
{silent = true, noremap = true})
end
for action, keys in pairs(config.options.action_keys) do
if type(keys) == "string" then
keys = { keys }
end
if config.options.position == "top" or config.options.position == "bottom" then
vim.api.nvim_win_set_height(self.win, config.options.height)
else
vim.api.nvim_win_set_width(self.win, config.options.width)
for _, key in pairs(keys) do
vim.api.nvim_buf_set_keymap(self.buf, "n", key, [[<cmd>lua require("trouble").action("]] .. action .. [[")<cr>]], {
silent = true,
noremap = true,
})
end
end
vim.api.nvim_exec([[
if config.options.position == "top" or config.options.position == "bottom" then
vim.api.nvim_win_set_height(self.win, config.options.height)
else
vim.api.nvim_win_set_width(self.win, config.options.width)
end
vim.api.nvim_exec(
[[
augroup TroubleHighlights
autocmd! * <buffer>
autocmd BufEnter <buffer> lua require("trouble").action("on_enter")
autocmd CursorMoved <buffer> lua require("trouble").action("auto_preview")
autocmd BufLeave <buffer> lua require("trouble").action("on_leave")
augroup END
]], false)
]],
false
)
if not opts.parent then self:on_enter() end
self:lock()
self:update(opts)
if not opts.parent then
self:on_enter()
end
self:lock()
self:update(opts)
end
function View:on_enter()
util.debug("on_enter")
util.debug("on_enter")
self.parent = self.parent or vim.fn.win_getid(vim.fn.winnr('#'))
self.parent = self.parent or vim.fn.win_getid(vim.fn.winnr("#"))
if (not self:is_valid_parent(self.parent)) or self.parent == self.win then
for _, win in pairs(vim.api.nvim_list_wins()) do
if self:is_valid_parent(win) and win ~= self.win then
self.parent = win
break
end
end
if (not self:is_valid_parent(self.parent)) or self.parent == self.win then
for _, win in pairs(vim.api.nvim_list_wins()) do
if self:is_valid_parent(win) and win ~= self.win then
self.parent = win
break
end
end
end
if not vim.api.nvim_win_is_valid(self.parent) then return self:close() end
if not vim.api.nvim_win_is_valid(self.parent) then
return self:close()
end
self.parent_state = {
buf = vim.api.nvim_win_get_buf(self.parent),
cursor = vim.api.nvim_win_get_cursor(self.parent)
}
self.parent_state = {
buf = vim.api.nvim_win_get_buf(self.parent),
cursor = vim.api.nvim_win_get_cursor(self.parent),
}
end
function View:on_leave()
util.debug("on_leave")
self:close_preview()
util.debug("on_leave")
self:close_preview()
end
function View:close_preview()
-- Clear preview highlights
for buf, _ in pairs(hl_bufs) do
clear_hl(buf)
end
hl_bufs = {}
-- Clear preview highlights
for buf, _ in pairs(hl_bufs) do clear_hl(buf) end
hl_bufs = {}
-- Reset parent state
local valid_win = vim.api.nvim_win_is_valid(self.parent)
local valid_buf = vim.api.nvim_buf_is_valid(self.parent_state.buf)
-- Reset parent state
local valid_win = vim.api.nvim_win_is_valid(self.parent)
local valid_buf = vim.api.nvim_buf_is_valid(self.parent_state.buf)
if self.parent_state and valid_buf and valid_win then
vim.api.nvim_win_set_buf(self.parent, self.parent_state.buf)
vim.api.nvim_win_set_cursor(self.parent, self.parent_state.cursor)
end
if self.parent_state and valid_buf and valid_win then
vim.api.nvim_win_set_buf(self.parent, self.parent_state.buf)
vim.api.nvim_win_set_cursor(self.parent, self.parent_state.cursor)
end
self.parent_state = nil
self.parent_state = nil
end
function View:is_float(win)
local opts = vim.api.nvim_win_get_config(win)
return opts and opts.relative and opts.relative ~= ""
local opts = vim.api.nvim_win_get_config(win)
return opts and opts.relative and opts.relative ~= ""
end
function View:is_valid_parent(win)
if not vim.api.nvim_win_is_valid(win) then return false end
-- dont do anything for floating windows
if View:is_float(win) then return false end
local buf = vim.api.nvim_win_get_buf(win)
-- Skip special buffers
if vim.api.nvim_buf_get_option(buf, "buftype") ~= "" then return false end
if not vim.api.nvim_win_is_valid(win) then
return false
end
-- dont do anything for floating windows
if View:is_float(win) then
return false
end
local buf = vim.api.nvim_win_get_buf(win)
-- Skip special buffers
if vim.api.nvim_buf_get_option(buf, "buftype") ~= "" then
return false
end
return true
return true
end
function View:on_win_enter()
util.debug("on_win_enter")
util.debug("on_win_enter")
local current_win = vim.api.nvim_get_current_win()
local current_win = vim.api.nvim_get_current_win()
if vim.fn.winnr('$') == 1 and current_win == self.win then
vim.cmd [[q]]
return
if vim.fn.winnr("$") == 1 and current_win == self.win then
vim.cmd([[q]])
return
end
if not self:is_valid_parent(current_win) then
return
end
local current_buf = vim.api.nvim_get_current_buf()
-- update parent when needed
if current_win ~= self.parent and current_win ~= self.win then
self.parent = current_win
-- update diagnostics to match the window we are viewing
if self:is_valid() then
vim.defer_fn(function()
util.debug("update_on_win_enter")
self:update()
end, 100)
end
end
if not self:is_valid_parent(current_win) then return end
local current_buf = vim.api.nvim_get_current_buf()
-- update parent when needed
if current_win ~= self.parent and current_win ~= self.win then
self.parent = current_win
-- update diagnostics to match the window we are viewing
if self:is_valid() then
vim.defer_fn(function()
util.debug("update_on_win_enter")
self:update()
end, 100)
end
end
-- check if another buffer took over our window
local parent = self.parent
if current_win == self.win and current_buf ~= self.buf then
-- open the buffer in the parent
vim.api.nvim_win_set_buf(parent, current_buf)
-- HACK: somw window local settings need to be reset
vim.api.nvim_win_set_option(parent, "winhl", "")
-- close the current trouble window
vim.api.nvim_win_close(self.win, false)
-- open a new trouble window
require("trouble").open()
-- switch back to the opened window / buffer
View.switch_to(parent, current_buf)
-- util.warn("win_enter pro")
end
-- check if another buffer took over our window
local parent = self.parent
if current_win == self.win and current_buf ~= self.buf then
-- open the buffer in the parent
vim.api.nvim_win_set_buf(parent, current_buf)
-- HACK: some window local settings need to be reset
vim.api.nvim_win_set_option(parent, "winhl", "")
-- close the current trouble window
vim.api.nvim_win_close(self.win, false)
-- open a new trouble window
require("trouble").open()
-- switch back to the opened window / buffer
View.switch_to(parent, current_buf)
-- util.warn("win_enter pro")
end
end
function View:focus()
View.switch_to(self.win, self.buf)
local line = self:get_line()
if line == 1 then
self:next_item()
self:next_item()
end
View.switch_to(self.win, self.buf)
local line = self:get_line()
if line == 1 then
self:next_item()
self:next_item()
end
end
function View.switch_to(win, buf)
if win then
vim.api.nvim_set_current_win(win)
if buf then vim.api.nvim_win_set_buf(win, buf) end
if win then
vim.api.nvim_set_current_win(win)
if buf then
vim.api.nvim_win_set_buf(win, buf)
end
end
end
function View:switch_to_parent()
-- vim.cmd("wincmd p")
View.switch_to(self.parent)
-- vim.cmd("wincmd p")
View.switch_to(self.parent)
end
function View:close()
util.debug("close")
if vim.api.nvim_win_is_valid(self.win) then
vim.api.nvim_win_close(self.win, {})
end
if vim.api.nvim_buf_is_valid(self.buf) then
vim.api.nvim_buf_delete(self.buf, {})
end
util.debug("close")
if vim.api.nvim_win_is_valid(self.win) then
vim.api.nvim_win_close(self.win, {})
end
if vim.api.nvim_buf_is_valid(self.buf) then
vim.api.nvim_buf_delete(self.buf, {})
end
end
function View.create(opts)
opts = opts or {}
if opts.win then
View.switch_to(opts.win)
vim.cmd("enew")
else
vim.cmd("below new")
local pos = {bottom = "J", top = "K", left = "H", right = "L"}
vim.cmd("wincmd " .. (pos[config.options.position] or "K"))
end
local buffer = View:new(opts)
buffer:setup(opts)
opts = opts or {}
if opts.win then
View.switch_to(opts.win)
vim.cmd("enew")
else
vim.cmd("below new")
local pos = { bottom = "J", top = "K", left = "H", right = "L" }
vim.cmd("wincmd " .. (pos[config.options.position] or "K"))
end
local buffer = View:new(opts)
buffer:setup(opts)
if opts and opts.auto then buffer:switch_to_parent() end
return buffer
if opts and opts.auto then
buffer:switch_to_parent()
end
return buffer
end
function View:get_cursor() return vim.api.nvim_win_get_cursor(self.win) end
function View:get_line() return self:get_cursor()[1] end
function View:get_col() return self:get_cursor()[2] end
function View:get_cursor()
return vim.api.nvim_win_get_cursor(self.win)
end
function View:get_line()
return self:get_cursor()[1]
end
function View:get_col()
return self:get_cursor()[2]
end
function View:current_item()
local line = self:get_line()
local item = self.items[line]
return item
local line = self:get_line()
local item = self.items[line]
return item
end
function View:next_item()
local line = self:get_line()
for i = line + 1, vim.api.nvim_buf_line_count(self.buf), 1 do
if self.items[i] then
vim.api.nvim_win_set_cursor(self.win, {i, self:get_col()})
return
end
local line = self:get_line()
for i = line + 1, vim.api.nvim_buf_line_count(self.buf), 1 do
if self.items[i] then
vim.api.nvim_win_set_cursor(self.win, { i, self:get_col() })
return
end
end
end
function View:previous_item()
local line = self:get_line()
for i = line - 1, 0, -1 do
if self.items[i] then
vim.api.nvim_win_set_cursor(self.win, {i, self:get_col()})
return
end
local line = self:get_line()
for i = line - 1, 0, -1 do
if self.items[i] then
vim.api.nvim_win_set_cursor(self.win, { i, self:get_col() })
return
end
end
end
function View:hover(opts)
opts = opts or {}
local item = opts.item or self:current_item()
if not (item and item.full_text) then return end
opts = opts or {}
local item = opts.item or self:current_item()
if not (item and item.full_text) then
return
end
local lines = {}
for line in item.full_text:gmatch("([^\n]*)\n?") do
table.insert(lines, line)
end
local lines = {}
for line in item.full_text:gmatch("([^\n]*)\n?") do
table.insert(lines, line)
end
vim.lsp.util.open_floating_preview(lines, "plaintext", {border = "single"})
vim.lsp.util.open_floating_preview(lines, "plaintext", { border = "single" })
end
function View:jump(opts)
opts = opts or {}
local item = opts.item or self:current_item()
if not item then return end
opts = opts or {}
local item = opts.item or self:current_item()
if not item then
return
end
if item.is_file == true then
folds.toggle(item.filename)
self:update()
if item.is_file == true then
folds.toggle(item.filename)
self:update()
else
View.switch_to(opts.win or self.parent)
if vim.api.nvim_buf_get_option(item.bufnr, "buflisted") == false then
vim.cmd("edit #" .. item.bufnr)
else
View.switch_to(opts.win or self.parent)
if vim.api.nvim_buf_get_option(item.bufnr, "buflisted") == false then
vim.cmd("edit #" .. item.bufnr)
else
vim.cmd("buffer " .. item.bufnr)
end
vim.api.nvim_win_set_cursor(self.parent,
{item.start.line + 1, item.start.character})
vim.cmd("buffer " .. item.bufnr)
end
vim.api.nvim_win_set_cursor(self.parent, { item.start.line + 1, item.start.character })
end
end
function View:toggle_fold()
folds.toggle(self:current_item().filename)
self:update()
folds.toggle(self:current_item().filename)
self:update()
end
function View:_preview()
if not vim.api.nvim_win_is_valid(self.parent) then return end
util.debug("preview")
if not vim.api.nvim_win_is_valid(self.parent) then
return
end
util.debug("preview")
local item = self:current_item()
if not item then return end
local item = self:current_item()
if not item then
return
end
if item.is_file ~= true then
if item.is_file ~= true then
vim.api.nvim_win_set_buf(self.parent, item.bufnr)
vim.api.nvim_win_set_cursor(self.parent, { item.start.line + 1, item.start.character })
vim.api.nvim_win_set_buf(self.parent, item.bufnr)
vim.api.nvim_win_set_cursor(self.parent,
{item.start.line + 1, item.start.character})
vim.api.nvim_buf_call(item.bufnr, function()
-- Center preview line on screen and open enough folds to show it
vim.cmd("norm! zz zv")
if vim.api.nvim_buf_get_option(item.bufnr, "filetype") == "" then
vim.cmd("do BufRead")
end
end)
vim.api.nvim_buf_call(item.bufnr, function()
-- Center preview line on screen and open enough folds to show it
vim.cmd("norm! zz zv")
if vim.api.nvim_buf_get_option(item.bufnr, "filetype") == "" then
vim.cmd("do BufRead")
end
end)
clear_hl(item.bufnr)
hl_bufs[item.bufnr] = true
for row = item.start.line, item.finish.line, 1 do
local col_start = 0
local col_end = -1
if row == item.start.line then
col_start = item.start.character
end
if row == item.finish.line then
col_end = item.finish.character
end
highlight(item.bufnr, config.namespace, "TroublePreview", row,
col_start, col_end)
end
clear_hl(item.bufnr)
hl_bufs[item.bufnr] = true
for row = item.start.line, item.finish.line, 1 do
local col_start = 0
local col_end = -1
if row == item.start.line then
col_start = item.start.character
end
if row == item.finish.line then
col_end = item.finish.character
end
highlight(item.bufnr, config.namespace, "TroublePreview", row, col_start, col_end)
end
end
end
-- View.preview = View._preview

1
selene.toml Normal file
View File

@ -0,0 +1 @@
std="lua51+vim"

3
stylua.toml Normal file
View File

@ -0,0 +1,3 @@
indent_type = "Spaces"
indent_width = 2
column_width = 120

2
vim.toml Normal file
View File

@ -0,0 +1,2 @@
[vim]
any = true