add emacs compile mode and find file
This commit is contained in:
parent
eeb2afc1ff
commit
a64612c293
4 changed files with 1078 additions and 181 deletions
|
|
@ -4,3 +4,4 @@ indent_type = "Spaces"
|
|||
indent_width = 2
|
||||
quote_style = "AutoPreferSingle"
|
||||
call_parentheses = "None"
|
||||
collapse_simple_statement = "Always"
|
||||
|
|
|
|||
|
|
@ -21,44 +21,47 @@ vim.opt.autoread = true
|
|||
vim.opt.cursorline = true
|
||||
vim.opt.scrolloff = 10
|
||||
|
||||
local path_package = vim.fn.stdpath('data') .. '/site/'
|
||||
local path_package = vim.fn.stdpath 'data' .. '/site/'
|
||||
local mini_path = path_package .. 'pack/deps/start/mini.nvim'
|
||||
if not vim.uv.fs_stat(mini_path) then
|
||||
vim.cmd('echo "Installing mini.nvim" | redraw')
|
||||
vim.fn.system({
|
||||
'git', 'clone', '--filter=blob:none',
|
||||
'https://github.com/echasnovski/mini.nvim', mini_path
|
||||
})
|
||||
vim.cmd('packadd mini.nvim | helptags ALL')
|
||||
vim.cmd 'echo "Installing mini.nvim" | redraw'
|
||||
vim.fn.system {
|
||||
'git',
|
||||
'clone',
|
||||
'--filter=blob:none',
|
||||
'https://github.com/echasnovski/mini.nvim',
|
||||
mini_path,
|
||||
}
|
||||
vim.cmd 'packadd mini.nvim | helptags ALL'
|
||||
end
|
||||
|
||||
require('mini.deps').setup({ path = { package = path_package } })
|
||||
require('mini.deps').setup { path = { package = path_package } }
|
||||
|
||||
local add, now, later = MiniDeps.add, MiniDeps.now, MiniDeps.later
|
||||
|
||||
add('folke/snacks.nvim')
|
||||
add('stevearc/quicker.nvim')
|
||||
add('stevearc/oil.nvim')
|
||||
add('A7Lavinraj/fyler.nvim')
|
||||
add('EdenEast/nightfox.nvim')
|
||||
add('williamboman/mason.nvim')
|
||||
add('williamboman/mason-lspconfig.nvim')
|
||||
add('neovim/nvim-lspconfig')
|
||||
add('folke/lazydev.nvim')
|
||||
add('Bilal2453/luvit-meta')
|
||||
add('NeogitOrg/neogit')
|
||||
add('nvim-lua/plenary.nvim')
|
||||
add('sindrets/diffview.nvim')
|
||||
add('folke/which-key.nvim')
|
||||
add({
|
||||
add 'folke/snacks.nvim'
|
||||
add 'stevearc/quicker.nvim'
|
||||
add 'stevearc/oil.nvim'
|
||||
add 'A7Lavinraj/fyler.nvim'
|
||||
add 'EdenEast/nightfox.nvim'
|
||||
add 'williamboman/mason.nvim'
|
||||
add 'williamboman/mason-lspconfig.nvim'
|
||||
add 'neovim/nvim-lspconfig'
|
||||
add 'folke/lazydev.nvim'
|
||||
add 'Bilal2453/luvit-meta'
|
||||
add 'NeogitOrg/neogit'
|
||||
add 'nvim-lua/plenary.nvim'
|
||||
add 'sindrets/diffview.nvim'
|
||||
add 'folke/which-key.nvim'
|
||||
add {
|
||||
source = 'saghen/blink.cmp',
|
||||
checkout = 'v1.8.0',
|
||||
})
|
||||
add('stevearc/conform.nvim')
|
||||
add('coder/claudecode.nvim')
|
||||
}
|
||||
add 'stevearc/conform.nvim'
|
||||
add 'coder/claudecode.nvim'
|
||||
|
||||
now(function()
|
||||
require('mini.icons').setup({
|
||||
require('mini.icons').setup {
|
||||
extension = {
|
||||
lua = { glyph = '', hl = 'MiniIconsAzure' },
|
||||
},
|
||||
|
|
@ -69,13 +72,13 @@ now(function()
|
|||
file = { glyph = '', hl = 'MiniIconsGrey' },
|
||||
directory = { glyph = '', hl = 'MiniIconsBlue' },
|
||||
},
|
||||
})
|
||||
require('nightfox').setup({})
|
||||
vim.cmd('colorscheme nightfox')
|
||||
}
|
||||
require('nightfox').setup {}
|
||||
vim.cmd 'colorscheme nightfox'
|
||||
end)
|
||||
|
||||
later(function()
|
||||
require('snacks').setup({
|
||||
require('snacks').setup {
|
||||
picker = {
|
||||
prompt = ' ',
|
||||
icons = {
|
||||
|
|
@ -89,13 +92,13 @@ later(function()
|
|||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('quicker').setup({
|
||||
require('quicker').setup {
|
||||
keys = {
|
||||
{
|
||||
'>',
|
||||
function() require('quicker').expand({ before = 2, after = 2, add_to_existing = true }) end,
|
||||
function() require('quicker').expand { before = 2, after = 2, add_to_existing = true } end,
|
||||
desc = 'Expand quickfix context',
|
||||
},
|
||||
{
|
||||
|
|
@ -104,7 +107,7 @@ later(function()
|
|||
desc = 'Collapse quickfix context',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
-- Function to show current directory in oil winbar
|
||||
_G.get_oil_winbar = function()
|
||||
local bufnr = vim.api.nvim_win_get_buf(vim.g.statusline_winid or 0)
|
||||
|
|
@ -116,7 +119,7 @@ later(function()
|
|||
end
|
||||
end
|
||||
|
||||
require('oil').setup({
|
||||
require('oil').setup {
|
||||
view_options = { show_hidden = true },
|
||||
columns = {
|
||||
'icon',
|
||||
|
|
@ -154,7 +157,7 @@ later(function()
|
|||
-- Copy current directory path to clipboard
|
||||
['gy'] = {
|
||||
callback = function()
|
||||
local oil = require('oil')
|
||||
local oil = require 'oil'
|
||||
local dir = oil.get_current_dir()
|
||||
if dir then
|
||||
vim.fn.setreg('+', dir)
|
||||
|
|
@ -164,36 +167,41 @@ later(function()
|
|||
desc = 'Copy current directory path',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('fyler').setup({
|
||||
require('fyler').setup {
|
||||
views = {
|
||||
finder = {
|
||||
confirm_simple = true,
|
||||
close_on_select = false,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('emacs-find-file').setup({
|
||||
require('find-file').setup {
|
||||
max_matches = 8,
|
||||
on_directory_enter = function(dir)
|
||||
require('oil').open(dir)
|
||||
end,
|
||||
})
|
||||
on_directory_enter = function(dir) require('oil').open(dir) end,
|
||||
}
|
||||
|
||||
require('neogit').setup({
|
||||
require('compile-mode').setup {
|
||||
-- split_mode = "vertical-right",
|
||||
on_exit = function(qf_list, _)
|
||||
if #qf_list > 0 then require('quicker').open { focus = false } end
|
||||
end,
|
||||
}
|
||||
|
||||
require('neogit').setup {
|
||||
integrations = {
|
||||
diffview = true,
|
||||
snacks = true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('diffview').setup({
|
||||
require('diffview').setup {
|
||||
enhanced_diff_hl = true,
|
||||
})
|
||||
}
|
||||
|
||||
require('which-key').setup({
|
||||
require('which-key').setup {
|
||||
preset = 'modern',
|
||||
icons = {
|
||||
separator = '→',
|
||||
|
|
@ -202,11 +210,13 @@ later(function()
|
|||
win = {
|
||||
border = 'rounded',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('claudecode').setup({})
|
||||
require('claudecode').setup {}
|
||||
end)
|
||||
|
||||
require('conform').setup({
|
||||
later(function()
|
||||
require('conform').setup {
|
||||
formatters_by_ft = {
|
||||
lua = { 'stylua' },
|
||||
python = { 'isort', 'black' },
|
||||
|
|
@ -223,25 +233,24 @@ later(function()
|
|||
c = { 'clang_format' },
|
||||
cpp = { 'clang_format' },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
-- Format command
|
||||
vim.api.nvim_create_user_command('Format', function(_)
|
||||
require('conform').format({ async = true, lsp_format = 'fallback' })
|
||||
end, { desc = 'Format current buffer' })
|
||||
vim.api.nvim_create_user_command(
|
||||
'Format',
|
||||
function(_) require('conform').format { async = true, lsp_format = 'fallback' } end,
|
||||
{ desc = 'Format current buffer' }
|
||||
)
|
||||
end)
|
||||
|
||||
later(function()
|
||||
local has_words_before = function()
|
||||
local col = vim.api.nvim_win_get_cursor(0)[2]
|
||||
if col == 0 then
|
||||
return false
|
||||
end
|
||||
if col == 0 then return false end
|
||||
local line = vim.api.nvim_get_current_line()
|
||||
return line:sub(col, col):match("%s") == nil
|
||||
return line:sub(col, col):match '%s' == nil
|
||||
end
|
||||
|
||||
require('blink.cmp').setup({
|
||||
require('blink.cmp').setup {
|
||||
keymap = {
|
||||
preset = 'none',
|
||||
['<C-space>'] = { 'show', 'show_documentation', 'hide_documentation' },
|
||||
|
|
@ -249,9 +258,7 @@ later(function()
|
|||
['<CR>'] = { 'accept', 'fallback' },
|
||||
['<Tab>'] = {
|
||||
function(cmp)
|
||||
if has_words_before() then
|
||||
return cmp.insert_next()
|
||||
end
|
||||
if has_words_before() then return cmp.insert_next() end
|
||||
end,
|
||||
'fallback',
|
||||
},
|
||||
|
|
@ -293,11 +300,11 @@ later(function()
|
|||
border = 'rounded',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
end)
|
||||
|
||||
later(function()
|
||||
require('mason').setup({
|
||||
require('mason').setup {
|
||||
ui = {
|
||||
border = 'rounded',
|
||||
icons = {
|
||||
|
|
@ -306,9 +313,9 @@ later(function()
|
|||
package_uninstalled = '✗',
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
require('mason-lspconfig').setup({
|
||||
require('mason-lspconfig').setup {
|
||||
ensure_installed = {
|
||||
'lua_ls', -- Lua
|
||||
'pyright', -- Python
|
||||
|
|
@ -316,7 +323,18 @@ later(function()
|
|||
'clangd', -- C/C++
|
||||
},
|
||||
automatic_installation = true,
|
||||
})
|
||||
}
|
||||
|
||||
-- Ensure formatters are installed
|
||||
local mason_registry = require 'mason-registry'
|
||||
local formatters = {
|
||||
'stylua',
|
||||
}
|
||||
|
||||
for _, formatter in ipairs(formatters) do
|
||||
local package = mason_registry.get_package(formatter)
|
||||
if not package:is_installed() then package:install() end
|
||||
end
|
||||
|
||||
-- LSP keybindings and options
|
||||
vim.api.nvim_create_autocmd('LspAttach', {
|
||||
|
|
@ -327,7 +345,12 @@ later(function()
|
|||
vim.keymap.set('n', 'gD', function() require('snacks').picker.lsp_declarations() end, vim.tbl_extend('force', opts, { desc = 'Goto Declaration' }))
|
||||
vim.keymap.set('n', 'gr', function() require('snacks').picker.lsp_references() end, vim.tbl_extend('force', opts, { desc = 'References', nowait = true }))
|
||||
vim.keymap.set('n', 'gI', function() require('snacks').picker.lsp_implementations() end, vim.tbl_extend('force', opts, { desc = 'Goto Implementation' }))
|
||||
vim.keymap.set('n', 'gy', function() require('snacks').picker.lsp_type_definitions() end, vim.tbl_extend('force', opts, { desc = 'Goto T[y]pe Definition' }))
|
||||
vim.keymap.set(
|
||||
'n',
|
||||
'gy',
|
||||
function() require('snacks').picker.lsp_type_definitions() end,
|
||||
vim.tbl_extend('force', opts, { desc = 'Goto T[y]pe Definition' })
|
||||
)
|
||||
vim.keymap.set('n', 'gai', function() require('snacks').picker.lsp_incoming_calls() end, vim.tbl_extend('force', opts, { desc = 'C[a]lls Incoming' }))
|
||||
vim.keymap.set('n', 'gao', function() require('snacks').picker.lsp_outgoing_calls() end, vim.tbl_extend('force', opts, { desc = 'C[a]lls Outgoing' }))
|
||||
vim.keymap.set('n', 'K', vim.lsp.buf.hover, vim.tbl_extend('force', opts, { desc = 'Hover documentation (press K again to focus)' }))
|
||||
|
|
@ -341,7 +364,7 @@ later(function()
|
|||
})
|
||||
|
||||
-- Diagnostic UI improvements
|
||||
vim.diagnostic.config({
|
||||
vim.diagnostic.config {
|
||||
virtual_text = true,
|
||||
signs = true,
|
||||
underline = true,
|
||||
|
|
@ -351,7 +374,7 @@ later(function()
|
|||
border = 'rounded',
|
||||
source = 'always',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
-- Add borders to hover windows
|
||||
local orig_util_open_floating_preview = vim.lsp.util.open_floating_preview
|
||||
|
|
@ -362,11 +385,11 @@ later(function()
|
|||
end
|
||||
|
||||
-- Setup lazydev for better plugin type support
|
||||
require('lazydev').setup({
|
||||
require('lazydev').setup {
|
||||
library = {
|
||||
{ path = 'luvit-meta/library', words = { 'vim%.uv' } },
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
-- Setup language servers using vim.lsp.config
|
||||
-- Lua
|
||||
|
|
@ -390,7 +413,7 @@ later(function()
|
|||
library = {
|
||||
vim.env.VIMRUNTIME,
|
||||
'${3rd}/luv/library',
|
||||
vim.fn.stdpath('data') .. '/site/pack/deps/start',
|
||||
vim.fn.stdpath 'data' .. '/site/pack/deps/start',
|
||||
},
|
||||
checkThirdParty = false,
|
||||
},
|
||||
|
|
@ -427,18 +450,17 @@ later(function()
|
|||
}
|
||||
|
||||
-- Enable language servers
|
||||
vim.lsp.enable({ 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd' })
|
||||
vim.lsp.enable { 'lua_ls', 'ts_ls', 'pyright', 'rust_analyzer', 'clangd' }
|
||||
end)
|
||||
|
||||
|
||||
local map = function(mode, lhs, rhs, opts)
|
||||
if type(opts) == 'string' then opts = { desc = opts } end
|
||||
require('snacks').keymap.set(mode, lhs, rhs, opts)
|
||||
end
|
||||
|
||||
map('n', '<leader>ex', function() require('fyler').toggle({ kind = 'split_left_most' }) end, 'Toggle file tree')
|
||||
map('n', '<leader>ex', function() require('fyler').toggle { kind = 'split_left_most' } end, 'Toggle file tree')
|
||||
map('n', '<leader>fs', '<cmd>w<cr>', 'Save file')
|
||||
map('n', '<leader>ff', function() require('emacs-find-file').open() end, 'Find file (emacs-style)')
|
||||
map('n', '<leader>ff', function() require('find-file').open() end, 'Find file (emacs-style)')
|
||||
map('n', '<leader>bb', function() require('snacks').picker.buffers() end, 'List buffers')
|
||||
map('n', '<leader>bd', function() require('snacks').bufdelete() end, 'Buffer delete')
|
||||
map('n', '<leader>.', function() require('snacks').scratch() end, 'Scratch buffer')
|
||||
|
|
@ -449,8 +471,10 @@ map('n', '<leader>ls', function() require('snacks').picker.lsp_symbols() end, 'L
|
|||
map('n', '<leader>lS', function() require('snacks').picker.lsp_workspace_symbols() end, 'LSP Workspace Symbols')
|
||||
map('n', '<leader>zz', function() require('snacks').zen() end, 'Toggle zen mode')
|
||||
map('n', '<leader>zm', function() require('snacks').zen.zoom() end, 'Toggle Zoom')
|
||||
map('n', '<leader>qq', function() require('quicker').open({ focus = true }) end, 'Open/focus quickfix')
|
||||
map('n', '<leader>ql', function() require('quicker').toggle({ loclist = true }) end, 'Toggle loclist')
|
||||
map('n', '<leader>qq', function() require('quicker').open { focus = true } end, 'Open/focus quickfix')
|
||||
map('n', '<leader>ql', function() require('quicker').toggle { loclist = true } end, 'Toggle loclist')
|
||||
map('n', '<leader>cc', function() require('compile-mode').compile() end, 'Compile')
|
||||
map('n', '<leader>cC', function() require('compile-mode').recompile() end, 'Recompile')
|
||||
map('n', '<leader>cr', '<cmd>source $MYVIMRC<cr>', 'Reload config')
|
||||
map('n', '<leader>ce', '<cmd>edit $MYVIMRC<cr>', 'Edit config')
|
||||
map('n', '<leader>ch', '<cmd>checkhealth<cr>', 'Check health')
|
||||
|
|
@ -472,9 +496,7 @@ map('n', '<leader>ad', '<cmd>ClaudeCodeDiffDeny<cr>', 'Deny diff')
|
|||
-- File tree specific keymap for Claude Code
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = { 'oil' },
|
||||
callback = function()
|
||||
vim.keymap.set('n', '<leader>as', '<cmd>ClaudeCodeTreeAdd<cr>', { buffer = true, desc = 'Add file to Claude' })
|
||||
end,
|
||||
callback = function() vim.keymap.set('n', '<leader>as', '<cmd>ClaudeCodeTreeAdd<cr>', { buffer = true, desc = 'Add file to Claude' }) end,
|
||||
})
|
||||
|
||||
map('n', '<leader>wh', '<C-w>h', 'Window left')
|
||||
|
|
@ -485,13 +507,19 @@ map('n', '<leader>ws', '<cmd>split<cr>', 'Split window horizontally')
|
|||
map('n', '<leader>wv', '<cmd>vsplit<cr>', 'Split window vertically')
|
||||
map('n', '<leader>wq', '<C-w>q', 'Close window')
|
||||
|
||||
-- Terminal mode keymap to unfocus and return to previous window
|
||||
vim.keymap.set('t', '<C-q>', '<C-\\><C-n><C-w>p', { desc = 'Unfocus terminal and return to editor' })
|
||||
|
||||
vim.api.nvim_create_autocmd('QuickFixCmdPost', {
|
||||
callback = function()
|
||||
vim.cmd('redraw!')
|
||||
vim.schedule(function()
|
||||
require('quicker').open({ focus = true })
|
||||
end)
|
||||
vim.cmd 'redraw!'
|
||||
vim.schedule(function() require('quicker').open { focus = true } end)
|
||||
end,
|
||||
})
|
||||
|
||||
vim.opt.shortmess:append('c')
|
||||
vim.opt.shortmess:append 'c'
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'FocusGained', 'BufEnter' }, {
|
||||
command = "if mode() != 'c' | checktime | endif",
|
||||
pattern = '*',
|
||||
})
|
||||
|
|
|
|||
879
home/neovim/config/lua/compile-mode.lua
Normal file
879
home/neovim/config/lua/compile-mode.lua
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
--- Emacs-style compile mode for Neovim
|
||||
--- @class CompileMode
|
||||
local M = {}
|
||||
|
||||
--- @type string?
|
||||
local last_command = nil
|
||||
|
||||
--- @type string[]
|
||||
local command_history = {}
|
||||
|
||||
local HISTORY_FILE = vim.fn.stdpath 'data' .. '/compile_history'
|
||||
local MAX_HISTORY_SIZE = 200
|
||||
|
||||
--- @class CompileConfig
|
||||
--- @field on_success? fun(qf_list: table)
|
||||
--- @field on_error? fun(qf_list: table, exit_code: number)
|
||||
--- @field on_exit? fun(qf_list: table, exit_code: number)
|
||||
--- @field keymaps? table<string, string|false>
|
||||
--- @field split_mode? 'auto'|'horizontal'|'vertical-right'|'vertical-left'
|
||||
--- @field min_width_for_vertical? number
|
||||
--- @field prompt? string
|
||||
|
||||
--- @type CompileConfig
|
||||
local config = {
|
||||
on_success = nil,
|
||||
on_error = nil,
|
||||
on_exit = nil,
|
||||
keymaps = {
|
||||
close = 'q',
|
||||
recompile = 'g',
|
||||
},
|
||||
split_mode = 'auto',
|
||||
min_width_for_vertical = 160,
|
||||
prompt = 'Compile command',
|
||||
}
|
||||
|
||||
local function load_history()
|
||||
local file = io.open(HISTORY_FILE, 'r')
|
||||
if not file then return end
|
||||
|
||||
command_history = {}
|
||||
for line in file:lines() do
|
||||
if line ~= '' then table.insert(command_history, line) end
|
||||
end
|
||||
file:close()
|
||||
end
|
||||
|
||||
local function save_history()
|
||||
local seen = {}
|
||||
local deduplicated = {}
|
||||
|
||||
for i = #command_history, 1, -1 do
|
||||
local cmd = command_history[i]
|
||||
if not seen[cmd] then
|
||||
seen[cmd] = true
|
||||
table.insert(deduplicated, 1, cmd)
|
||||
end
|
||||
end
|
||||
|
||||
local start_idx = math.max(1, #deduplicated - MAX_HISTORY_SIZE + 1)
|
||||
local trimmed = {}
|
||||
for i = start_idx, #deduplicated do
|
||||
table.insert(trimmed, deduplicated[i])
|
||||
end
|
||||
|
||||
local file = io.open(HISTORY_FILE, 'w')
|
||||
if not file then
|
||||
vim.notify('Failed to save compile history', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
for _, cmd in ipairs(trimmed) do
|
||||
file:write(cmd .. '\n')
|
||||
end
|
||||
file:close()
|
||||
|
||||
command_history = trimmed
|
||||
end
|
||||
|
||||
--- @param cmd string
|
||||
local function append_to_history(cmd)
|
||||
if not cmd or cmd == '' then return end
|
||||
|
||||
table.insert(command_history, cmd)
|
||||
save_history()
|
||||
end
|
||||
|
||||
--- Determine if we should use vertical split layout
|
||||
--- @return boolean
|
||||
local function should_use_vertical_split()
|
||||
if config.split_mode == 'vertical-right' or config.split_mode == 'vertical-left' then
|
||||
return true
|
||||
elseif config.split_mode == 'horizontal' then
|
||||
return false
|
||||
else -- 'auto'
|
||||
return vim.o.columns >= (config.min_width_for_vertical or 160)
|
||||
end
|
||||
end
|
||||
|
||||
--- Create a vertical split on left or right side with two stacked windows
|
||||
--- @param side 'left'|'right'
|
||||
--- @param qf_bufnr number
|
||||
--- @param comp_bufnr number
|
||||
--- @return number qf_win, number comp_win
|
||||
local function create_stacked_vertical_split(side, qf_bufnr, comp_bufnr)
|
||||
local cmd = side == 'left' and 'topleft vertical split' or 'botright vertical split'
|
||||
|
||||
vim.cmd(cmd)
|
||||
local qf_win = vim.api.nvim_get_current_win()
|
||||
|
||||
vim.api.nvim_win_set_buf(qf_win, qf_bufnr)
|
||||
vim.bo[qf_bufnr].buftype = 'quickfix'
|
||||
|
||||
vim.cmd 'belowright split'
|
||||
local comp_win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(comp_win, comp_bufnr)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = comp_win })
|
||||
|
||||
local qf_height = vim.api.nvim_win_get_height(qf_win)
|
||||
local comp_height = vim.api.nvim_win_get_height(comp_win)
|
||||
local total_height = qf_height + comp_height
|
||||
local half_height = math.floor(total_height / 2)
|
||||
|
||||
vim.api.nvim_win_set_height(qf_win, half_height)
|
||||
vim.api.nvim_win_set_height(comp_win, half_height)
|
||||
|
||||
vim.api.nvim_set_current_win(qf_win)
|
||||
|
||||
return qf_win, comp_win
|
||||
end
|
||||
|
||||
--- Create compilation window on left or right side
|
||||
--- @param side 'left'|'right'
|
||||
--- @param bufnr number
|
||||
--- @param qf_winid number?
|
||||
--- @return number win
|
||||
local function create_vertical_compilation_window(side, bufnr, qf_winid)
|
||||
local has_qf = qf_winid and qf_winid ~= 0 and vim.api.nvim_win_is_valid(qf_winid)
|
||||
|
||||
if has_qf and qf_winid then
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
vim.cmd 'belowright split'
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(win, bufnr)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = win })
|
||||
return win
|
||||
else
|
||||
local cmd = side == 'left' and 'topleft vertical split' or 'botright vertical split'
|
||||
vim.cmd(cmd)
|
||||
local win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(win, bufnr)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = win })
|
||||
return win
|
||||
end
|
||||
end
|
||||
|
||||
--- @param cmd string
|
||||
local function run_compile(cmd)
|
||||
if not cmd or cmd == '' then
|
||||
vim.notify('No compile command provided', vim.log.levels.WARN)
|
||||
return
|
||||
end
|
||||
|
||||
last_command = cmd
|
||||
vim.fn.setqflist({}, 'r')
|
||||
|
||||
local bufnr = vim.fn.bufnr '*compilation*'
|
||||
if bufnr == -1 then
|
||||
bufnr = vim.api.nvim_create_buf(false, true)
|
||||
vim.api.nvim_buf_set_name(bufnr, '*compilation*')
|
||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = bufnr })
|
||||
vim.api.nvim_set_option_value('bufhidden', 'hide', { buf = bufnr })
|
||||
vim.api.nvim_set_option_value('swapfile', false, { buf = bufnr })
|
||||
vim.api.nvim_set_option_value('buflisted', false, { buf = bufnr })
|
||||
vim.api.nvim_set_option_value('filetype', 'compilation', { buf = bufnr })
|
||||
end
|
||||
|
||||
vim.api.nvim_set_option_value('modifiable', true, { buf = bufnr })
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {})
|
||||
|
||||
local use_vertical = should_use_vertical_split()
|
||||
|
||||
local win = vim.fn.bufwinid(bufnr)
|
||||
if win == -1 then
|
||||
local qf_info = vim.fn.getqflist { winid = 0 }
|
||||
local qf_winid = qf_info.winid
|
||||
local has_qf = qf_winid ~= 0 and vim.api.nvim_win_is_valid(qf_winid)
|
||||
|
||||
if config.split_mode == 'vertical-right' then
|
||||
win = create_vertical_compilation_window('right', bufnr, qf_winid)
|
||||
elseif config.split_mode == 'vertical-left' then
|
||||
win = create_vertical_compilation_window('left', bufnr, qf_winid)
|
||||
elseif has_qf and use_vertical then
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
vim.cmd 'rightbelow vertical split'
|
||||
win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(win, bufnr)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = win })
|
||||
else
|
||||
vim.cmd 'botright split'
|
||||
win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(win, bufnr)
|
||||
vim.api.nvim_win_set_height(win, 15)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = win })
|
||||
end
|
||||
end
|
||||
|
||||
vim.api.nvim_set_current_win(win)
|
||||
|
||||
local start_time = vim.uv.hrtime()
|
||||
local start_ms = math.floor((start_time % 1000000000) / 1000000)
|
||||
local header = {
|
||||
'-*- mode: compilation; default-directory: "' .. vim.fn.getcwd() .. '" -*-',
|
||||
'Compilation started at ' .. os.date '%Y-%m-%d %H:%M:%S' .. '.' .. string.format('%03d', start_ms),
|
||||
'',
|
||||
cmd,
|
||||
'',
|
||||
}
|
||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, header)
|
||||
|
||||
local output_lines = {}
|
||||
local line_to_qf = {}
|
||||
|
||||
local function append_output(data)
|
||||
if not data then return end
|
||||
for _, line in ipairs(data) do
|
||||
if line ~= '' then
|
||||
table.insert(output_lines, line)
|
||||
vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, { line })
|
||||
local buf_line = vim.api.nvim_buf_line_count(bufnr)
|
||||
line_to_qf[buf_line] = #output_lines
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local job_id = vim.fn.jobstart(cmd, {
|
||||
stdout_buffered = false,
|
||||
stderr_buffered = false,
|
||||
on_stdout = function(_, data) append_output(data) end,
|
||||
on_stderr = function(_, data) append_output(data) end,
|
||||
on_exit = function(_, exit_code)
|
||||
local end_time = vim.uv.hrtime()
|
||||
local end_ms = math.floor((end_time % 1000000000) / 1000000)
|
||||
local footer = {
|
||||
'',
|
||||
'Compilation '
|
||||
.. (exit_code == 0 and 'finished' or 'exited abnormally with code ' .. exit_code)
|
||||
.. ' at '
|
||||
.. os.date '%Y-%m-%d %H:%M:%S'
|
||||
.. '.'
|
||||
.. string.format('%03d', end_ms),
|
||||
}
|
||||
vim.api.nvim_buf_set_lines(bufnr, -1, -1, false, footer)
|
||||
vim.api.nvim_set_option_value('modifiable', false, { buf = bufnr })
|
||||
|
||||
local valid_entries = {}
|
||||
if #output_lines > 0 then
|
||||
vim.fn.setqflist({}, 'r', {
|
||||
lines = output_lines,
|
||||
})
|
||||
local qf_list = vim.fn.getqflist()
|
||||
valid_entries = vim.tbl_filter(function(item) return item.valid == 1 end, qf_list)
|
||||
|
||||
vim.b[bufnr].line_to_qf = line_to_qf
|
||||
|
||||
vim.schedule(function()
|
||||
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if vim.api.nvim_buf_is_valid(buf) and vim.bo[buf].buftype == 'quickfix' then
|
||||
local function qf_jump()
|
||||
local line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local qf_entries = vim.fn.getqflist()
|
||||
|
||||
if line <= #qf_entries then
|
||||
local entry = qf_entries[line]
|
||||
|
||||
if entry.valid == 1 and entry.bufnr > 0 then
|
||||
vim.cmd('cc ' .. line)
|
||||
else
|
||||
for i = line - 1, 1, -1 do
|
||||
if qf_entries[i].valid == 1 and qf_entries[i].bufnr > 0 then
|
||||
vim.cmd('cc ' .. i)
|
||||
return
|
||||
end
|
||||
end
|
||||
for i = line + 1, #qf_entries do
|
||||
if qf_entries[i].valid == 1 and qf_entries[i].bufnr > 0 then
|
||||
vim.cmd('cc ' .. i)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vim.keymap.set('n', '<CR>', qf_jump, { buffer = buf, desc = 'Jump to error location' })
|
||||
vim.keymap.set('n', '<2-LeftMouse>', qf_jump, { buffer = buf, desc = 'Jump to error location' })
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
vim.schedule(function()
|
||||
if exit_code == 0 then
|
||||
vim.notify('Compilation finished successfully', vim.log.levels.INFO)
|
||||
if config.on_success then config.on_success(valid_entries) end
|
||||
else
|
||||
vim.notify('Compilation failed with code ' .. exit_code, vim.log.levels.ERROR)
|
||||
if config.on_error then config.on_error(valid_entries, exit_code) end
|
||||
end
|
||||
|
||||
if config.on_exit then config.on_exit(valid_entries, exit_code) end
|
||||
|
||||
vim.schedule(function()
|
||||
local comp_win = vim.fn.bufwinid(bufnr)
|
||||
local qf_info = vim.fn.getqflist { winid = 0 }
|
||||
local qf_winid = qf_info.winid
|
||||
local has_qf = qf_winid ~= 0 and vim.api.nvim_win_is_valid(qf_winid)
|
||||
local has_both = comp_win ~= -1 and has_qf
|
||||
|
||||
if exit_code == 0 and has_qf then
|
||||
vim.cmd 'cclose'
|
||||
elseif has_both then
|
||||
if config.split_mode == 'vertical-right' or config.split_mode == 'vertical-left' then
|
||||
local side = config.split_mode == 'vertical-right' and 'right' or 'left'
|
||||
local qf_bufnr = vim.fn.getqflist({ qfbufnr = 0 }).qfbufnr
|
||||
|
||||
vim.api.nvim_win_close(comp_win, false)
|
||||
vim.api.nvim_win_close(qf_winid, false)
|
||||
|
||||
create_stacked_vertical_split(side, qf_bufnr, bufnr)
|
||||
elseif should_use_vertical_split() then
|
||||
local comp_config = vim.api.nvim_win_get_config(comp_win)
|
||||
local qf_config = vim.api.nvim_win_get_config(qf_winid)
|
||||
if comp_config.relative == '' and qf_config.relative == '' then
|
||||
vim.api.nvim_win_close(comp_win, false)
|
||||
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
vim.cmd 'rightbelow vertical split'
|
||||
local new_comp_win = vim.api.nvim_get_current_win()
|
||||
vim.api.nvim_win_set_buf(new_comp_win, bufnr)
|
||||
vim.api.nvim_set_option_value('winfixbuf', true, { win = new_comp_win })
|
||||
|
||||
local target_height = math.max(20, math.floor(vim.o.lines / 3))
|
||||
vim.api.nvim_win_set_height(qf_winid, target_height)
|
||||
vim.api.nvim_win_set_height(new_comp_win, target_height)
|
||||
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
end
|
||||
else
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
end
|
||||
elseif has_qf then
|
||||
vim.api.nvim_set_current_win(qf_winid)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
if job_id <= 0 then vim.notify('Failed to start compilation', vim.log.levels.ERROR) end
|
||||
end
|
||||
|
||||
--- Get matches for autocomplete (commands and files)
|
||||
--- @param input string
|
||||
--- @return string[]
|
||||
local function get_matches(input)
|
||||
local words = vim.split(input, '%s+')
|
||||
local last_word = words[#words] or ''
|
||||
|
||||
if last_word == '' then return {} end
|
||||
|
||||
local matches = {}
|
||||
|
||||
local is_path = last_word:match '[/.]' or last_word:match '^~'
|
||||
|
||||
if is_path then
|
||||
local expanded = vim.fn.expand(last_word)
|
||||
local dir = vim.fn.fnamemodify(expanded, ':h')
|
||||
local filter = vim.fn.fnamemodify(expanded, ':t')
|
||||
|
||||
if vim.fn.isdirectory(dir) == 1 then
|
||||
local handle = vim.uv.fs_scandir(dir)
|
||||
if handle then
|
||||
while true do
|
||||
local name, type = vim.uv.fs_scandir_next(handle)
|
||||
if not name then break end
|
||||
|
||||
if name ~= '.' and name ~= '..' then
|
||||
if filter == '' or name:lower():find(filter:lower(), 1, true) then
|
||||
local sep = dir:match '/$' and '' or '/'
|
||||
local full_path = dir .. sep .. name
|
||||
if type == 'directory' then full_path = full_path .. '/' end
|
||||
table.insert(matches, full_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local handle = vim.uv.fs_scandir '.'
|
||||
if handle then
|
||||
while true do
|
||||
local name, type = vim.uv.fs_scandir_next(handle)
|
||||
if not name then break end
|
||||
|
||||
if name ~= '.' and name ~= '..' then
|
||||
if name:lower():find(last_word:lower(), 1, true) == 1 then
|
||||
if type == 'directory' then
|
||||
table.insert(matches, name .. '/')
|
||||
else
|
||||
table.insert(matches, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #words == 1 then
|
||||
local path = os.getenv 'PATH' or ''
|
||||
local path_dirs = vim.split(path, ':')
|
||||
local seen = {}
|
||||
|
||||
for _, match in ipairs(matches) do
|
||||
seen[match] = true
|
||||
end
|
||||
|
||||
for _, dir in ipairs(path_dirs) do
|
||||
local path_handle = vim.uv.fs_scandir(dir)
|
||||
if path_handle then
|
||||
while true do
|
||||
local name, type = vim.uv.fs_scandir_next(path_handle)
|
||||
if not name then break end
|
||||
|
||||
if type == 'file' and not seen[name] then
|
||||
if name:lower():find(last_word:lower(), 1, true) == 1 then
|
||||
local filepath = dir .. '/' .. name
|
||||
local stat = vim.uv.fs_stat(filepath)
|
||||
if stat and stat.mode then
|
||||
local is_executable = bit.band(stat.mode, tonumber('111', 8)) ~= 0
|
||||
if is_executable then
|
||||
table.insert(matches, name)
|
||||
seen[name] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(matches, function(a, b)
|
||||
local a_is_dir = a:match '/$'
|
||||
local b_is_dir = b:match '/$'
|
||||
if a_is_dir == b_is_dir then return a < b end
|
||||
return a_is_dir
|
||||
end)
|
||||
|
||||
return matches
|
||||
end
|
||||
|
||||
function M.compile()
|
||||
local prev_win = vim.api.nvim_get_current_win()
|
||||
local input_buf = vim.api.nvim_create_buf(false, true)
|
||||
|
||||
vim.b[input_buf].completion = false
|
||||
|
||||
local prompt_text = config.prompt .. ': '
|
||||
local prompt_len = #prompt_text
|
||||
|
||||
---@return table
|
||||
local function get_win_config()
|
||||
local width = vim.o.columns
|
||||
local max_height = 5
|
||||
local row = vim.o.lines - vim.o.cmdheight - max_height - 1
|
||||
|
||||
return {
|
||||
relative = 'editor',
|
||||
width = width,
|
||||
height = max_height,
|
||||
row = row,
|
||||
col = 0,
|
||||
style = 'minimal',
|
||||
border = 'none',
|
||||
}
|
||||
end
|
||||
|
||||
local input_win = vim.api.nvim_open_win(input_buf, true, get_win_config())
|
||||
|
||||
vim.api.nvim_set_hl(0, 'CompileModePrompt', { link = 'Title', default = true })
|
||||
vim.api.nvim_set_hl(0, 'CompileModeMatch', { link = 'Normal', default = true })
|
||||
vim.api.nvim_set_hl(0, 'CompileModeMatchSelected', { link = 'CursorLine', default = true })
|
||||
vim.api.nvim_set_hl(0, 'CompileModeMatchChar', { link = 'IncSearch', default = true })
|
||||
|
||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = input_buf })
|
||||
vim.api.nvim_set_option_value('bufhidden', 'wipe', { buf = input_buf })
|
||||
vim.api.nvim_set_option_value('modifiable', true, { buf = input_buf })
|
||||
vim.api.nvim_set_option_value('wrap', true, { win = input_win })
|
||||
vim.api.nvim_set_option_value('linebreak', true, { win = input_win })
|
||||
|
||||
local default_cmd = last_command or 'make -k'
|
||||
local current_input = default_cmd
|
||||
local matches = {}
|
||||
local match_index = 0
|
||||
local lock = false
|
||||
local cycling = false
|
||||
local history_index = 0
|
||||
|
||||
local ns_id = vim.api.nvim_create_namespace 'compile_mode_prompt'
|
||||
|
||||
local function get_input()
|
||||
local line = vim.api.nvim_buf_get_lines(input_buf, 0, 1, false)[1] or ''
|
||||
local pattern = '^' .. vim.pesc(prompt_text)
|
||||
if line:match(pattern) then return line:sub(prompt_len + 1) end
|
||||
return ''
|
||||
end
|
||||
|
||||
local function update_display()
|
||||
local words = vim.split(current_input, '%s+')
|
||||
local last_word = words[#words] or ''
|
||||
|
||||
local parts = { prompt_text, current_input }
|
||||
local match_highlights = {}
|
||||
|
||||
if #matches > 0 and match_index > 0 then
|
||||
local pos = #table.concat(parts, '')
|
||||
local max_display = 8
|
||||
|
||||
for i, match in ipairs(matches) do
|
||||
if i > max_display then break end
|
||||
|
||||
if i > 1 then
|
||||
table.insert(parts, ' | ')
|
||||
pos = pos + 3
|
||||
else
|
||||
table.insert(parts, ' | ')
|
||||
pos = pos + 3
|
||||
end
|
||||
|
||||
local filter_start, filter_end = nil, nil
|
||||
if last_word ~= '' then
|
||||
filter_start, filter_end = match:lower():find(last_word:lower(), 1, true)
|
||||
end
|
||||
|
||||
table.insert(match_highlights, {
|
||||
start = pos,
|
||||
finish = pos + #match,
|
||||
selected = i == match_index,
|
||||
filter_start = filter_start,
|
||||
filter_end = filter_end,
|
||||
})
|
||||
|
||||
table.insert(parts, match)
|
||||
pos = pos + #match
|
||||
end
|
||||
|
||||
if #matches > max_display then table.insert(parts, ' | ...') end
|
||||
end
|
||||
|
||||
local full_text = table.concat(parts, '')
|
||||
|
||||
lock = true
|
||||
cycling = true
|
||||
vim.api.nvim_buf_set_lines(input_buf, 0, -1, false, { full_text })
|
||||
|
||||
local width = vim.o.columns
|
||||
local required_lines = math.ceil(#full_text / width)
|
||||
local needed_height = math.min(required_lines, 5)
|
||||
|
||||
local current_config = vim.api.nvim_win_get_config(input_win)
|
||||
if current_config.height ~= needed_height then
|
||||
local new_config = get_win_config()
|
||||
new_config.height = needed_height
|
||||
new_config.row = vim.o.lines - vim.o.cmdheight - needed_height - 1
|
||||
vim.api.nvim_win_set_config(input_win, new_config)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_clear_namespace(input_buf, ns_id, 0, -1)
|
||||
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, 0, {
|
||||
end_col = prompt_len,
|
||||
hl_group = 'CompileModePrompt',
|
||||
})
|
||||
|
||||
if match_index == 0 then
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, prompt_len, {
|
||||
end_col = prompt_len + #current_input,
|
||||
hl_group = 'CompileModeMatchSelected',
|
||||
})
|
||||
end
|
||||
|
||||
for _, hl in ipairs(match_highlights) do
|
||||
if hl.selected then
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, hl.start, {
|
||||
end_col = hl.finish,
|
||||
hl_group = 'CompileModeMatchSelected',
|
||||
})
|
||||
else
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, hl.start, {
|
||||
end_col = hl.finish,
|
||||
hl_group = 'CompileModeMatch',
|
||||
})
|
||||
end
|
||||
|
||||
if hl.filter_start and hl.filter_end then
|
||||
local match_start = hl.start + hl.filter_start - 1
|
||||
local match_end = hl.start + hl.filter_end
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, match_start, {
|
||||
end_col = match_end,
|
||||
hl_group = 'CompileModeMatchChar',
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local cursor_col = prompt_len + #current_input
|
||||
vim.api.nvim_win_set_cursor(input_win, { 1, cursor_col })
|
||||
|
||||
vim.schedule(function()
|
||||
lock = false
|
||||
cycling = false
|
||||
end)
|
||||
end
|
||||
|
||||
local function close()
|
||||
if vim.api.nvim_win_is_valid(input_win) then vim.api.nvim_win_close(input_win, true) end
|
||||
if vim.api.nvim_win_is_valid(prev_win) then vim.api.nvim_set_current_win(prev_win) end
|
||||
vim.cmd 'stopinsert'
|
||||
end
|
||||
|
||||
update_display()
|
||||
vim.schedule(function() vim.api.nvim_win_set_cursor(input_win, { 1, prompt_len + #current_input }) end)
|
||||
|
||||
local last_input = current_input
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'TextChanged', 'TextChangedI' }, {
|
||||
buffer = input_buf,
|
||||
callback = function()
|
||||
if lock or cycling then return end
|
||||
|
||||
local line = vim.api.nvim_buf_get_lines(input_buf, 0, 1, false)[1] or ''
|
||||
|
||||
local pattern = '^' .. vim.pesc(prompt_text)
|
||||
if not line:match(pattern) then
|
||||
lock = true
|
||||
vim.api.nvim_buf_set_lines(input_buf, 0, -1, false, { prompt_text .. current_input })
|
||||
vim.api.nvim_buf_clear_namespace(input_buf, ns_id, 0, -1)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, 0, {
|
||||
end_col = prompt_len,
|
||||
hl_group = 'CompileModePrompt',
|
||||
})
|
||||
vim.api.nvim_win_set_cursor(input_win, { 1, prompt_len + #current_input })
|
||||
vim.schedule(function() lock = false end)
|
||||
return
|
||||
end
|
||||
|
||||
local input_part = line:sub(prompt_len + 1)
|
||||
local pipe_pos = input_part:find ' | '
|
||||
if pipe_pos then input_part = input_part:sub(1, pipe_pos - 1) end
|
||||
|
||||
if input_part ~= last_input then
|
||||
current_input = input_part
|
||||
last_input = input_part
|
||||
match_index = 0
|
||||
matches = {}
|
||||
|
||||
vim.schedule(update_display)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
|
||||
buffer = input_buf,
|
||||
callback = function()
|
||||
if lock or cycling then return end
|
||||
local cursor = vim.api.nvim_win_get_cursor(input_win)
|
||||
|
||||
if cursor[2] < prompt_len then vim.api.nvim_win_set_cursor(input_win, { 1, prompt_len }) end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'VimResized' }, {
|
||||
callback = function()
|
||||
if vim.api.nvim_win_is_valid(input_win) then vim.schedule(update_display) end
|
||||
end,
|
||||
})
|
||||
|
||||
local opts = { buffer = input_buf, nowait = true, silent = true }
|
||||
|
||||
vim.keymap.set('i', '<Tab>', function()
|
||||
if #matches == 0 then
|
||||
matches = get_matches(current_input)
|
||||
if #matches == 0 then return end
|
||||
end
|
||||
|
||||
match_index = (match_index % #matches) + 1
|
||||
local words = vim.split(current_input, '%s+')
|
||||
words[#words] = matches[match_index]
|
||||
current_input = table.concat(words, ' ')
|
||||
last_input = current_input
|
||||
vim.schedule(update_display)
|
||||
end, opts)
|
||||
|
||||
vim.keymap.set('i', '<S-Tab>', function()
|
||||
if #matches == 0 then
|
||||
matches = get_matches(current_input)
|
||||
if #matches == 0 then return end
|
||||
end
|
||||
|
||||
match_index = match_index - 1
|
||||
if match_index < 1 then match_index = #matches end
|
||||
local words = vim.split(current_input, '%s+')
|
||||
words[#words] = matches[match_index]
|
||||
current_input = table.concat(words, ' ')
|
||||
last_input = current_input
|
||||
vim.schedule(update_display)
|
||||
end, opts)
|
||||
|
||||
vim.keymap.set('i', '<C-p>', function()
|
||||
if #command_history > 0 then
|
||||
if history_index == 0 then
|
||||
current_input = get_input()
|
||||
last_input = current_input
|
||||
end
|
||||
if history_index < #command_history then
|
||||
history_index = history_index + 1
|
||||
current_input = command_history[#command_history - history_index + 1]
|
||||
last_input = current_input
|
||||
vim.schedule(update_display)
|
||||
end
|
||||
end
|
||||
return ''
|
||||
end, vim.tbl_extend('force', opts, { expr = true }))
|
||||
|
||||
vim.keymap.set('i', '<C-n>', function()
|
||||
if history_index > 0 then
|
||||
history_index = history_index - 1
|
||||
if history_index == 0 then
|
||||
current_input = get_input()
|
||||
else
|
||||
current_input = command_history[#command_history - history_index + 1]
|
||||
end
|
||||
last_input = current_input
|
||||
vim.schedule(update_display)
|
||||
end
|
||||
return ''
|
||||
end, vim.tbl_extend('force', opts, { expr = true }))
|
||||
|
||||
vim.keymap.set('i', '<CR>', function()
|
||||
if current_input and current_input ~= '' then
|
||||
append_to_history(current_input)
|
||||
vim.schedule(function()
|
||||
close()
|
||||
run_compile(current_input)
|
||||
end)
|
||||
end
|
||||
return ''
|
||||
end, vim.tbl_extend('force', opts, { expr = true }))
|
||||
|
||||
vim.keymap.set({ 'i', 'n' }, '<Esc>', function() vim.schedule(close) end, opts)
|
||||
vim.keymap.set({ 'i', 'n' }, '<C-c>', function() vim.schedule(close) end, opts)
|
||||
|
||||
vim.keymap.set('i', '<M-BS>', function()
|
||||
local new_pos = 0
|
||||
local found_non_sep = false
|
||||
for i = #current_input, 1, -1 do
|
||||
local char = current_input:sub(i, i)
|
||||
local is_sep = char:match '[/%._%- ]'
|
||||
|
||||
if not found_non_sep and not is_sep then
|
||||
found_non_sep = true
|
||||
elseif found_non_sep and is_sep then
|
||||
new_pos = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
current_input = current_input:sub(1, new_pos)
|
||||
last_input = current_input
|
||||
vim.schedule(update_display)
|
||||
return ''
|
||||
end, vim.tbl_extend('force', opts, { expr = true }))
|
||||
|
||||
vim.cmd 'startinsert!'
|
||||
end
|
||||
|
||||
function M.recompile()
|
||||
if last_command then
|
||||
run_compile(last_command)
|
||||
else
|
||||
M.compile()
|
||||
end
|
||||
end
|
||||
|
||||
--- @param opts? CompileConfig
|
||||
function M.setup(opts)
|
||||
config = vim.tbl_deep_extend('force', config, opts or {})
|
||||
|
||||
load_history()
|
||||
|
||||
vim.api.nvim_create_user_command('Compile', function(cmd_opts)
|
||||
if cmd_opts.args and cmd_opts.args ~= '' then
|
||||
run_compile(cmd_opts.args)
|
||||
else
|
||||
M.compile()
|
||||
end
|
||||
end, { nargs = '?', desc = 'Run compile command' })
|
||||
|
||||
vim.api.nvim_create_user_command('Recompile', M.recompile, { desc = 'Rerun last compile command' })
|
||||
|
||||
vim.api.nvim_create_autocmd('FileType', {
|
||||
pattern = 'compilation',
|
||||
callback = function()
|
||||
if config.keymaps.close then vim.keymap.set('n', config.keymaps.close, '<cmd>close<cr>', { buffer = true, desc = 'Close compilation buffer' }) end
|
||||
if config.keymaps.recompile then vim.keymap.set('n', config.keymaps.recompile, M.recompile, { buffer = true, desc = 'Recompile' }) end
|
||||
|
||||
local function jump_to_error()
|
||||
local bufnr = vim.api.nvim_get_current_buf()
|
||||
local line = vim.api.nvim_win_get_cursor(0)[1]
|
||||
local line_to_qf = vim.b[bufnr].line_to_qf
|
||||
|
||||
if not line_to_qf then return end
|
||||
|
||||
local output_idx = line_to_qf[line]
|
||||
if not output_idx then return end
|
||||
|
||||
local qf_list = vim.fn.getqflist()
|
||||
|
||||
if output_idx > 0 and output_idx <= #qf_list then
|
||||
local entry = qf_list[output_idx]
|
||||
|
||||
local valid_idx = nil
|
||||
if entry.valid == 1 and entry.bufnr > 0 then
|
||||
valid_idx = output_idx
|
||||
else
|
||||
for i = output_idx - 1, 1, -1 do
|
||||
if qf_list[i].valid == 1 and qf_list[i].bufnr > 0 then
|
||||
valid_idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if not valid_idx then
|
||||
for i = output_idx + 1, #qf_list do
|
||||
if qf_list[i].valid == 1 and qf_list[i].bufnr > 0 then
|
||||
valid_idx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if valid_idx then
|
||||
local target_win = nil
|
||||
for _, win in ipairs(vim.api.nvim_list_wins()) do
|
||||
local win_buf = vim.api.nvim_win_get_buf(win)
|
||||
local bt = vim.bo[win_buf].buftype
|
||||
if bt == '' or bt == 'acwrite' then
|
||||
target_win = win
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if target_win then
|
||||
vim.api.nvim_set_current_win(target_win)
|
||||
vim.cmd('cc ' .. valid_idx)
|
||||
else
|
||||
vim.cmd('cc ' .. valid_idx)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
vim.keymap.set('n', '<CR>', jump_to_error, { buffer = true, desc = 'Jump to error location' })
|
||||
vim.keymap.set('n', '<2-LeftMouse>', jump_to_error, { buffer = true, desc = 'Jump to error location' })
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---@class EmacsFindFileConfig
|
||||
---@class FindFileConfig
|
||||
---@field max_matches? number Maximum number of matches to display
|
||||
---@field auto_cd? boolean Automatically change directory when entering a directory
|
||||
---@field respect_gitignore? boolean Respect .gitignore files when listing matches (default: true)
|
||||
---@field highlights? table Highlight configuration (will be set dynamically based on theme)
|
||||
---@field on_directory_enter? function Optional callback when entering a directory without selecting a file
|
||||
---@field keymaps? EmacsFindFileKeymaps Keymap configuration
|
||||
---@field keymaps? FindFileKeymaps Keymap configuration
|
||||
|
||||
---@class EmacsFindFileKeymaps
|
||||
---@class FindFileKeymaps
|
||||
---@field next string Cycle to next candidate
|
||||
---@field prev string Cycle to previous candidate
|
||||
---@field complete string Complete to selected candidate
|
||||
|
|
@ -23,12 +22,11 @@
|
|||
|
||||
local M = {}
|
||||
|
||||
---@type EmacsFindFileConfig
|
||||
---@type FindFileConfig
|
||||
local config = {
|
||||
max_matches = 8,
|
||||
auto_cd = true,
|
||||
respect_gitignore = true,
|
||||
highlights = nil,
|
||||
on_directory_enter = nil,
|
||||
keymaps = {
|
||||
next = '<C-n>',
|
||||
|
|
@ -43,10 +41,8 @@ local config = {
|
|||
}
|
||||
|
||||
---Setup emacs-find-file with custom configuration
|
||||
---@param opts EmacsFindFileConfig|nil Configuration options
|
||||
function M.setup(opts)
|
||||
config = vim.tbl_deep_extend('force', config, opts or {})
|
||||
end
|
||||
---@param opts FindFileConfig|nil Configuration options
|
||||
function M.setup(opts) config = vim.tbl_deep_extend('force', config, opts or {}) end
|
||||
|
||||
---Check if a path should be ignored based on gitignore
|
||||
---@param path string Full path to check
|
||||
|
|
@ -60,23 +56,17 @@ local function is_gitignored(path, override_respect)
|
|||
should_respect = config.respect_gitignore
|
||||
end
|
||||
|
||||
if not should_respect then
|
||||
return false
|
||||
end
|
||||
if not should_respect then return false end
|
||||
|
||||
-- Extract just the filename from the path
|
||||
local name = vim.fn.fnamemodify(path, ':t')
|
||||
|
||||
-- Filter out .git directory when respecting gitignore
|
||||
if name == '.git' then
|
||||
return true
|
||||
end
|
||||
if name == '.git' then return true end
|
||||
|
||||
-- Use git check-ignore to determine if path is ignored
|
||||
local handle = io.popen('git check-ignore -q "' .. path:gsub('"', '\\"') .. '" 2>/dev/null')
|
||||
if not handle then
|
||||
return false
|
||||
end
|
||||
if not handle then return false end
|
||||
|
||||
local _, _, exit_code = handle:close()
|
||||
-- git check-ignore returns 0 if path is ignored, 1 if not ignored
|
||||
|
|
@ -91,14 +81,10 @@ end
|
|||
local function parse_path(input)
|
||||
local expanded = vim.fn.expand(input)
|
||||
|
||||
if expanded == '' or expanded == '~' then
|
||||
return vim.fn.expand('~'), ''
|
||||
end
|
||||
if expanded == '' or expanded == '~' then return vim.fn.expand '~', '' end
|
||||
|
||||
if input:match('/$') then
|
||||
if vim.fn.isdirectory(expanded) == 1 then
|
||||
return expanded, ''
|
||||
end
|
||||
if input:match '/$' then
|
||||
if vim.fn.isdirectory(expanded) == 1 then return expanded, '' end
|
||||
end
|
||||
|
||||
local dir = vim.fn.fnamemodify(expanded, ':h')
|
||||
|
|
@ -122,7 +108,7 @@ local function get_matches(dir, filter, override_respect)
|
|||
|
||||
if name ~= '.' and name ~= '..' then
|
||||
if filter == '' or name:lower():find(filter:lower(), 1, true) then
|
||||
local sep = dir:match('/$') and '' or '/'
|
||||
local sep = dir:match '/$' and '' or '/'
|
||||
local full_path = dir .. sep .. name
|
||||
|
||||
-- Skip if gitignored
|
||||
|
|
@ -138,9 +124,7 @@ local function get_matches(dir, filter, override_respect)
|
|||
end
|
||||
|
||||
table.sort(matches, function(a, b)
|
||||
if a.type == b.type then
|
||||
return a.name < b.name
|
||||
end
|
||||
if a.type == b.type then return a.name < b.name end
|
||||
return a.type == 'directory'
|
||||
end)
|
||||
|
||||
|
|
@ -172,11 +156,11 @@ function M.open_dir(start_path)
|
|||
|
||||
local input_win = vim.api.nvim_open_win(input_buf, true, get_win_config())
|
||||
|
||||
vim.api.nvim_set_hl(0, 'EmacsPrompt', { link = 'Title', default = true })
|
||||
vim.api.nvim_set_hl(0, 'EmacsSep', { link = 'Comment', default = true })
|
||||
vim.api.nvim_set_hl(0, 'EmacsMatch', { link = 'Normal', default = true })
|
||||
vim.api.nvim_set_hl(0, 'EmacsMatchSelected', { link = 'CursorLine', default = true })
|
||||
vim.api.nvim_set_hl(0, 'EmacsMatchChar', { link = 'IncSearch', default = true })
|
||||
vim.api.nvim_set_hl(0, 'FindFilePrompt', { link = 'Title', default = true })
|
||||
vim.api.nvim_set_hl(0, 'FindFileSep', { link = 'Comment', default = true })
|
||||
vim.api.nvim_set_hl(0, 'FindFileMatch', { link = 'Normal', default = true })
|
||||
vim.api.nvim_set_hl(0, 'FindFileMatchSelected', { link = 'CursorLine', default = true })
|
||||
vim.api.nvim_set_hl(0, 'FindFileMatchChar', { link = 'IncSearch', default = true })
|
||||
|
||||
vim.api.nvim_set_option_value('buftype', 'nofile', { buf = input_buf })
|
||||
vim.api.nvim_set_option_value('bufhidden', 'wipe', { buf = input_buf })
|
||||
|
|
@ -193,9 +177,13 @@ function M.open_dir(start_path)
|
|||
local matches = {}
|
||||
local selected = 0
|
||||
local lock = false
|
||||
local cycling = false
|
||||
local user_selected = false
|
||||
local show_hidden = false
|
||||
|
||||
-- Namespace for highlights
|
||||
local ns_id = vim.api.nvim_create_namespace 'find_file'
|
||||
|
||||
local function update_display()
|
||||
local dir, filter = parse_path(current_input)
|
||||
matches = get_matches(dir, filter, not show_hidden)
|
||||
|
|
@ -206,7 +194,7 @@ function M.open_dir(start_path)
|
|||
local parts = { 'Find file: ', current_input }
|
||||
|
||||
local match_highlights = {}
|
||||
local show_matches = current_input:match('/$') or (filter ~= '' and #matches >= 1)
|
||||
local show_matches = current_input:match '/$' or (filter ~= '' and #matches >= 1)
|
||||
|
||||
if #matches > 0 and show_matches then
|
||||
local pos = #table.concat(parts, '')
|
||||
|
|
@ -243,9 +231,7 @@ function M.open_dir(start_path)
|
|||
pos = pos + #item
|
||||
end
|
||||
|
||||
if #matches > config.max_matches then
|
||||
table.insert(parts, ' | ...')
|
||||
end
|
||||
if #matches > config.max_matches then table.insert(parts, ' | ...') end
|
||||
end
|
||||
|
||||
local full_text = table.concat(parts, '')
|
||||
|
|
@ -266,30 +252,54 @@ function M.open_dir(start_path)
|
|||
vim.api.nvim_win_set_config(input_win, new_config)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_clear_namespace(input_buf, -1, 0, -1)
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsPrompt', 0, 0, 11)
|
||||
vim.api.nvim_buf_clear_namespace(input_buf, ns_id, 0, -1)
|
||||
|
||||
-- Highlight the prompt
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, 0, {
|
||||
end_col = 11,
|
||||
hl_group = 'FindFilePrompt',
|
||||
})
|
||||
|
||||
-- Highlight input if no match is selected
|
||||
if selected == 0 or not user_selected then
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsMatchSelected', 0, 11, 11 + #current_input)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, 11, {
|
||||
end_col = 11 + #current_input,
|
||||
hl_group = 'FindFileMatchSelected',
|
||||
})
|
||||
end
|
||||
|
||||
-- Highlight path separators
|
||||
for i = 1, #current_input do
|
||||
if current_input:sub(i, i) == '/' then
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsSep', 0, 11 + i - 1, 11 + i)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, 11 + i - 1, {
|
||||
end_col = 11 + i,
|
||||
hl_group = 'FindFileSep',
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- Highlight matches
|
||||
for _, hl in ipairs(match_highlights) do
|
||||
if hl.selected then
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsMatchSelected', 0, hl.start, hl.finish)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, hl.start, {
|
||||
end_col = hl.finish,
|
||||
hl_group = 'FindFileMatchSelected',
|
||||
})
|
||||
else
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsMatch', 0, hl.start, hl.finish)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, hl.start, {
|
||||
end_col = hl.finish,
|
||||
hl_group = 'FindFileMatch',
|
||||
})
|
||||
end
|
||||
|
||||
-- Highlight the matching characters
|
||||
if hl.filter_start and hl.filter_end then
|
||||
local match_start = hl.start + hl.filter_start - 1
|
||||
local match_end = hl.start + hl.filter_end
|
||||
vim.api.nvim_buf_add_highlight(input_buf, -1, 'EmacsMatchChar', 0, match_start, match_end)
|
||||
vim.api.nvim_buf_set_extmark(input_buf, ns_id, 0, match_start, {
|
||||
end_col = match_end,
|
||||
hl_group = 'FindFileMatchChar',
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -304,18 +314,13 @@ function M.open_dir(start_path)
|
|||
|
||||
local function close()
|
||||
if vim.api.nvim_win_is_valid(input_win) then vim.api.nvim_win_close(input_win, true) end
|
||||
if vim.api.nvim_win_is_valid(prev_win) then
|
||||
vim.api.nvim_set_current_win(prev_win)
|
||||
end
|
||||
vim.cmd('stopinsert')
|
||||
if vim.api.nvim_win_is_valid(prev_win) then vim.api.nvim_set_current_win(prev_win) end
|
||||
vim.cmd 'stopinsert'
|
||||
end
|
||||
|
||||
update_display()
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_win_set_cursor(input_win, { 1, 11 + #current_input })
|
||||
end)
|
||||
vim.schedule(function() vim.api.nvim_win_set_cursor(input_win, { 1, 11 + #current_input }) end)
|
||||
|
||||
local cycling = false
|
||||
local last_input = current_input
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'TextChangedI' }, {
|
||||
|
|
@ -324,13 +329,11 @@ function M.open_dir(start_path)
|
|||
if lock or cycling then return end
|
||||
|
||||
local line = vim.api.nvim_buf_get_lines(input_buf, 0, 1, false)[1] or ''
|
||||
if not line:match('^Find file: ') then return end
|
||||
if not line:match '^Find file: ' then return end
|
||||
|
||||
local path_part = line:sub(12)
|
||||
local pipe_pos = path_part:find(' | ')
|
||||
if pipe_pos then
|
||||
path_part = path_part:sub(1, pipe_pos - 1)
|
||||
end
|
||||
local pipe_pos = path_part:find ' | '
|
||||
if pipe_pos then path_part = path_part:sub(1, pipe_pos - 1) end
|
||||
|
||||
if path_part ~= last_input then
|
||||
current_input = path_part
|
||||
|
|
@ -350,17 +353,13 @@ function M.open_dir(start_path)
|
|||
local cursor = vim.api.nvim_win_get_cursor(input_win)
|
||||
local max_col = 11 + #current_input
|
||||
|
||||
if cursor[2] > max_col then
|
||||
vim.api.nvim_win_set_cursor(input_win, { 1, max_col })
|
||||
end
|
||||
if cursor[2] > max_col then vim.api.nvim_win_set_cursor(input_win, { 1, max_col }) end
|
||||
end,
|
||||
})
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'VimResized' }, {
|
||||
callback = function()
|
||||
if vim.api.nvim_win_is_valid(input_win) then
|
||||
vim.api.nvim_win_set_config(input_win, get_win_config())
|
||||
end
|
||||
if vim.api.nvim_win_is_valid(input_win) then vim.api.nvim_win_set_config(input_win, get_win_config()) end
|
||||
end,
|
||||
})
|
||||
|
||||
|
|
@ -368,9 +367,7 @@ function M.open_dir(start_path)
|
|||
|
||||
vim.keymap.set('i', config.keymaps.next, function()
|
||||
selected = selected + 1
|
||||
if selected > #matches then
|
||||
selected = 0
|
||||
end
|
||||
if selected > #matches then selected = 0 end
|
||||
user_selected = true
|
||||
vim.schedule(update_display)
|
||||
return ''
|
||||
|
|
@ -378,9 +375,7 @@ function M.open_dir(start_path)
|
|||
|
||||
vim.keymap.set('i', config.keymaps.prev, function()
|
||||
selected = selected - 1
|
||||
if selected < 0 then
|
||||
selected = #matches
|
||||
end
|
||||
if selected < 0 then selected = #matches end
|
||||
user_selected = true
|
||||
vim.schedule(update_display)
|
||||
return ''
|
||||
|
|
@ -390,9 +385,7 @@ function M.open_dir(start_path)
|
|||
if #matches == 0 then return '' end
|
||||
local match = selected > 0 and matches[selected] or matches[1]
|
||||
current_input = match.path
|
||||
if match.type == 'directory' then
|
||||
current_input = current_input .. '/'
|
||||
end
|
||||
if match.type == 'directory' then current_input = current_input .. '/' end
|
||||
last_input = current_input
|
||||
selected = 0
|
||||
user_selected = false
|
||||
|
|
@ -401,7 +394,7 @@ function M.open_dir(start_path)
|
|||
end, vim.tbl_extend('force', opts, { expr = true, desc = 'Complete to selected candidate' }))
|
||||
|
||||
vim.keymap.set('i', config.keymaps.confirm, function()
|
||||
local in_directory_mode = current_input:match('/$')
|
||||
local in_directory_mode = current_input:match '/$'
|
||||
local dir, filter = parse_path(current_input)
|
||||
|
||||
if user_selected and selected > 0 and #matches > 0 then
|
||||
|
|
@ -411,7 +404,7 @@ function M.open_dir(start_path)
|
|||
selected = 0
|
||||
user_selected = false
|
||||
vim.schedule(update_display)
|
||||
vim.cmd('startinsert!')
|
||||
vim.cmd 'startinsert!'
|
||||
return ''
|
||||
else
|
||||
local file = matches[selected].path
|
||||
|
|
@ -426,9 +419,7 @@ function M.open_dir(start_path)
|
|||
local dir_path = vim.fn.expand(current_input)
|
||||
vim.schedule(function()
|
||||
close()
|
||||
if config.auto_cd then
|
||||
vim.cmd('cd ' .. vim.fn.fnameescape(dir_path))
|
||||
end
|
||||
if config.auto_cd then vim.cmd('cd ' .. vim.fn.fnameescape(dir_path)) end
|
||||
if config.on_directory_enter then
|
||||
config.on_directory_enter(dir_path)
|
||||
else
|
||||
|
|
@ -455,12 +446,10 @@ function M.open_dir(start_path)
|
|||
return ''
|
||||
end, { buffer = input_buf, nowait = true, expr = true, desc = 'Open file or directory' })
|
||||
|
||||
-- Handle close keymaps (can be a single key or a list)
|
||||
local close_keys = type(config.keymaps.close) == 'table' and config.keymaps.close or { config.keymaps.close }
|
||||
---@type string[]
|
||||
local close_keys = type(config.keymaps.close) == 'table' and config.keymaps.close --[[@as string[] ]] or { config.keymaps.close }
|
||||
for _, key in ipairs(close_keys) do
|
||||
vim.keymap.set({ 'n', 'i' }, key, function()
|
||||
vim.schedule(close)
|
||||
end, vim.tbl_extend('force', opts, { desc = 'Close find-file' }))
|
||||
vim.keymap.set({ 'n', 'i' }, key, function() vim.schedule(close) end, vim.tbl_extend('force', opts, { desc = 'Close find-file' }))
|
||||
end
|
||||
|
||||
vim.keymap.set('i', config.keymaps.delete_component, function()
|
||||
|
|
@ -468,7 +457,7 @@ function M.open_dir(start_path)
|
|||
local found_non_sep = false
|
||||
for i = #current_input, 1, -1 do
|
||||
local char = current_input:sub(i, i)
|
||||
local is_sep = char:match('[/%._%- ]')
|
||||
local is_sep = char:match '[/%._%- ]'
|
||||
|
||||
if not found_non_sep and not is_sep then
|
||||
found_non_sep = true
|
||||
|
|
@ -487,7 +476,7 @@ function M.open_dir(start_path)
|
|||
|
||||
vim.keymap.set('i', config.keymaps.parent_dir, function()
|
||||
-- Go to parent directory
|
||||
local dir = current_input:match('/$') and current_input or vim.fn.fnamemodify(current_input, ':h')
|
||||
local dir = current_input:match '/$' and current_input or vim.fn.fnamemodify(current_input, ':h')
|
||||
local parent = vim.fn.fnamemodify(dir, ':h')
|
||||
if parent ~= '' and parent ~= '/' then
|
||||
current_input = parent .. '/'
|
||||
|
|
@ -509,7 +498,7 @@ function M.open_dir(start_path)
|
|||
return ''
|
||||
end, vim.tbl_extend('force', opts, { expr = true, desc = 'Toggle showing ignored/hidden files' }))
|
||||
|
||||
vim.cmd('startinsert!')
|
||||
vim.cmd 'startinsert!'
|
||||
end
|
||||
|
||||
---Open emacs-style find-file interface from current buffer's directory
|
||||
Loading…
Reference in a new issue