mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-01-24 09:40:06 +08:00
233 lines
6.7 KiB
Lua
233 lines
6.7 KiB
Lua
local context_manager = require "plenary.context_manager"
|
||
local ts_utils = require "telescope.utils"
|
||
local strings = require "plenary.strings"
|
||
local conf = require("telescope.config").values
|
||
|
||
local has_ts, _ = pcall(require, "nvim-treesitter")
|
||
local _, ts_configs = pcall(require, "nvim-treesitter.configs")
|
||
local _, ts_parsers = pcall(require, "nvim-treesitter.parsers")
|
||
|
||
local Job = require "plenary.job"
|
||
|
||
local utils = {}
|
||
|
||
utils.with_preview_window = function(status, bufnr, callable)
|
||
if bufnr and vim.api.nvim_buf_call and false then
|
||
vim.api.nvim_buf_call(bufnr, callable)
|
||
else
|
||
return context_manager.with(function()
|
||
vim.cmd(string.format("noautocmd call nvim_set_current_win(%s)", status.preview_win))
|
||
coroutine.yield()
|
||
vim.cmd(string.format("noautocmd call nvim_set_current_win(%s)", status.prompt_win))
|
||
end, callable)
|
||
end
|
||
end
|
||
|
||
-- API helper functions for buffer previewer
|
||
--- Job maker for buffer previewer
|
||
utils.job_maker = function(cmd, bufnr, opts)
|
||
opts = opts or {}
|
||
opts.mode = opts.mode or "insert"
|
||
-- bufname and value are optional
|
||
-- if passed, they will be use as the cache key
|
||
-- if any of them are missing, cache will be skipped
|
||
if opts.bufname ~= opts.value or not opts.bufname or not opts.value then
|
||
local command = table.remove(cmd, 1)
|
||
local writer = (function()
|
||
if opts.writer ~= nil then
|
||
local wcommand = table.remove(opts.writer, 1)
|
||
return Job:new {
|
||
command = wcommand,
|
||
args = opts.writer,
|
||
env = opts.env,
|
||
cwd = opts.cwd,
|
||
}
|
||
end
|
||
end)()
|
||
|
||
Job:new({
|
||
command = command,
|
||
args = cmd,
|
||
env = opts.env,
|
||
cwd = opts.cwd,
|
||
writer = writer,
|
||
on_exit = vim.schedule_wrap(function(j)
|
||
if not vim.api.nvim_buf_is_valid(bufnr) then
|
||
return
|
||
end
|
||
if opts.mode == "append" then
|
||
vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, j:result())
|
||
elseif opts.mode == "insert" then
|
||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, j:result())
|
||
end
|
||
if opts.callback then
|
||
opts.callback(bufnr, j:result())
|
||
end
|
||
end),
|
||
}):start()
|
||
else
|
||
if opts.callback then
|
||
opts.callback(bufnr)
|
||
end
|
||
end
|
||
end
|
||
|
||
local function has_filetype(ft)
|
||
return ft and ft ~= ""
|
||
end
|
||
|
||
--- Attach default highlighter which will choose between regex and ts
|
||
utils.highlighter = function(bufnr, ft, opts)
|
||
opts = vim.F.if_nil(opts, {})
|
||
opts.preview = vim.F.if_nil(opts.preview, {})
|
||
opts.preview.treesitter = (function()
|
||
if type(opts.preview) == "table" and opts.preview.treesitter then
|
||
return opts.preview.treesitter
|
||
end
|
||
if type(conf.preview) == "table" and conf.preview.treesitter then
|
||
return conf.preview.treesitter
|
||
end
|
||
if type(conf.preview) == "boolean" then
|
||
return conf.preview
|
||
end
|
||
-- We should never get here
|
||
return false
|
||
end)()
|
||
|
||
if type(opts.preview.treesitter) == "boolean" then
|
||
local temp = { enable = opts.preview.treesitter }
|
||
opts.preview.treesitter = temp
|
||
end
|
||
|
||
local ts_highlighting = (function()
|
||
if type(opts.preview.treesitter.enable) == "table" then
|
||
if vim.tbl_contains(opts.preview.treesitter.enable, ft) then
|
||
return true
|
||
end
|
||
return false
|
||
end
|
||
|
||
if vim.tbl_contains(vim.F.if_nil(opts.preview.treesitter.disable, {}), ft) then
|
||
return false
|
||
end
|
||
|
||
return opts.preview.treesitter.enable == nil or opts.preview.treesitter.enable == true
|
||
end)()
|
||
|
||
local ts_success
|
||
if ts_highlighting then
|
||
ts_success = utils.ts_highlighter(bufnr, ft)
|
||
end
|
||
if not ts_highlighting or ts_success == false then
|
||
utils.regex_highlighter(bufnr, ft)
|
||
end
|
||
end
|
||
|
||
--- Attach regex highlighter
|
||
utils.regex_highlighter = function(bufnr, ft)
|
||
if has_filetype(ft) then
|
||
return pcall(vim.api.nvim_buf_set_option, bufnr, "syntax", ft)
|
||
end
|
||
return false
|
||
end
|
||
|
||
local treesitter_attach = function(bufnr, ft)
|
||
local lang = ts_parsers.ft_to_lang(ft)
|
||
if not ts_configs.is_enabled("highlight", lang, bufnr) then
|
||
return false
|
||
end
|
||
|
||
local config = ts_configs.get_module "highlight"
|
||
vim.treesitter.highlighter.new(ts_parsers.get_parser(bufnr, lang))
|
||
local is_table = type(config.additional_vim_regex_highlighting) == "table"
|
||
if
|
||
config.additional_vim_regex_highlighting
|
||
and (not is_table or vim.tbl_contains(config.additional_vim_regex_highlighting, lang))
|
||
then
|
||
vim.api.nvim_buf_set_option(bufnr, "syntax", ft)
|
||
end
|
||
return true
|
||
end
|
||
|
||
-- Attach ts highlighter
|
||
utils.ts_highlighter = function(bufnr, ft)
|
||
if not has_ts then
|
||
has_ts, _ = pcall(require, "nvim-treesitter")
|
||
if has_ts then
|
||
_, ts_configs = pcall(require, "nvim-treesitter.configs")
|
||
_, ts_parsers = pcall(require, "nvim-treesitter.parsers")
|
||
end
|
||
end
|
||
|
||
if has_ts and has_filetype(ft) then
|
||
return treesitter_attach(bufnr, ft)
|
||
end
|
||
return false
|
||
end
|
||
|
||
utils.set_preview_message = function(bufnr, winid, message, fillchar)
|
||
fillchar = vim.F.if_nil(fillchar, "╱")
|
||
local height = vim.api.nvim_win_get_height(winid)
|
||
local width = vim.api.nvim_win_get_width(winid)
|
||
vim.api.nvim_buf_set_lines(
|
||
bufnr,
|
||
0,
|
||
-1,
|
||
false,
|
||
ts_utils.repeated_table(height, table.concat(ts_utils.repeated_table(width, fillchar), ""))
|
||
)
|
||
local anon_ns = vim.api.nvim_create_namespace ""
|
||
local padding = table.concat(ts_utils.repeated_table(#message + 4, " "), "")
|
||
local formatted_message = " " .. message .. " "
|
||
-- Populate lines table based on height
|
||
local lines = {}
|
||
if height == 1 then
|
||
lines[1] = formatted_message
|
||
else
|
||
for i = 1, math.min(height, 3), 1 do
|
||
if i % 2 == 0 then
|
||
lines[i] = formatted_message
|
||
else
|
||
lines[i] = padding
|
||
end
|
||
end
|
||
end
|
||
vim.api.nvim_buf_set_extmark(
|
||
bufnr,
|
||
anon_ns,
|
||
0,
|
||
0,
|
||
{ end_line = height, hl_group = "TelescopePreviewMessageFillchar" }
|
||
)
|
||
local col = math.floor((width - strings.strdisplaywidth(formatted_message)) / 2)
|
||
for i, line in ipairs(lines) do
|
||
local line_pos = math.floor(height / 2) - 2 + i
|
||
vim.api.nvim_buf_set_extmark(
|
||
bufnr,
|
||
anon_ns,
|
||
math.max(line_pos, 0),
|
||
0,
|
||
{ virt_text = { { line, "TelescopePreviewMessage" } }, virt_text_pos = "overlay", virt_text_win_col = col }
|
||
)
|
||
end
|
||
end
|
||
|
||
--- Check if mime type is binary.
|
||
--- NOT an exhaustive check, may get false negatives. Ideally should check
|
||
--- filetype with `vim.filetype.match` or `filetype_detect` first for filetype
|
||
--- info.
|
||
---@param mime_type string
|
||
---@return boolean
|
||
utils.binary_mime_type = function(mime_type)
|
||
local type_, subtype = unpack(vim.split(mime_type, "/"))
|
||
if vim.tbl_contains({ "text", "inode" }, type_) then
|
||
return false
|
||
end
|
||
if vim.tbl_contains({ "json", "javascript" }, subtype) then
|
||
return false
|
||
end
|
||
return true
|
||
end
|
||
|
||
return utils
|