mirror of
https://github.com/hyzendust/KickestEnd.nvim.git
synced 2026-02-15 00:21:15 +01:00
Update: Modularizing some features and customizations.
This commit is contained in:
230
init.lua
230
init.lua
@@ -347,235 +347,19 @@ vim.api.nvim_create_autocmd('TextYankPost', {
|
||||
require('kanagawa').setup { transparent = true }
|
||||
vim.cmd [[colorscheme kanagawa]]
|
||||
|
||||
-- Custom native-looking tabline that matches Kanagawa theme
|
||||
-- Custom native-looking tabline
|
||||
vim.o.showtabline = 1
|
||||
_G.tab_offset = 1
|
||||
local max_visible_tabs = 8 -- maximum number of tabs visible at once
|
||||
|
||||
function _G.Tabline()
|
||||
local current_tab = vim.fn.tabpagenr()
|
||||
local tab_count = vim.fn.tabpagenr '$'
|
||||
local win_width = vim.o.columns
|
||||
local tab_width = math.floor(win_width / max_visible_tabs) -- FIXED width
|
||||
|
||||
local tabs = {}
|
||||
for i = 1, tab_count do
|
||||
local buflist = vim.fn.tabpagebuflist(i)
|
||||
local winnr = vim.fn.tabpagewinnr(i)
|
||||
local bufname = vim.fn.bufname(buflist[winnr])
|
||||
if bufname == '' then
|
||||
bufname = '[No Name]'
|
||||
end
|
||||
local modified = vim.fn.getbufvar(buflist[winnr], '&mod') == 1 and ' ●' or ''
|
||||
local label = i .. ': ' .. vim.fn.fnamemodify(bufname, ':t') .. modified
|
||||
|
||||
-- truncate/pad to fixed tab_width
|
||||
if #label > tab_width - 2 then
|
||||
label = label:sub(1, tab_width - 3) .. '…'
|
||||
end
|
||||
label = label .. string.rep(' ', tab_width - #label)
|
||||
|
||||
tabs[i] = (i == current_tab and '%#TabLineSel#' or '%#TabLine#') .. label
|
||||
end
|
||||
|
||||
-- scrolling logic to keep active tab visible
|
||||
local start_index = _G.tab_offset
|
||||
if current_tab < start_index then
|
||||
start_index = current_tab
|
||||
elseif current_tab >= start_index + max_visible_tabs then
|
||||
start_index = current_tab - max_visible_tabs + 1
|
||||
end
|
||||
_G.tab_offset = start_index
|
||||
|
||||
-- select visible tabs
|
||||
local visible_tabs = {}
|
||||
for i = start_index, math.min(start_index + max_visible_tabs - 1, tab_count) do
|
||||
table.insert(visible_tabs, tabs[i])
|
||||
end
|
||||
|
||||
-- scrolling arrows
|
||||
local left_arrow = start_index > 1 and '< ' or ' '
|
||||
local right_arrow = start_index + max_visible_tabs - 1 < tab_count and ' >' or ' '
|
||||
|
||||
return '%#TabLine#' .. left_arrow .. table.concat(visible_tabs, '') .. right_arrow .. '%#TabLineFill#'
|
||||
end
|
||||
|
||||
vim.o.tabline = '%!v:lua.Tabline()'
|
||||
|
||||
-- adjust offset if tabs are closed
|
||||
vim.api.nvim_create_autocmd({ 'TabClosed' }, {
|
||||
callback = function()
|
||||
local tab_count = vim.fn.tabpagenr '$'
|
||||
if _G.tab_offset > tab_count then
|
||||
_G.tab_offset = math.max(tab_count - max_visible_tabs + 1, 1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
local custom_tabline = require 'custom_tabline'
|
||||
vim.o.tabline = '%!v:lua.require("custom_tabline").tabline()'
|
||||
|
||||
-- Auto-create config files for formatters (cross-platform)
|
||||
local uv = vim.loop
|
||||
local home = uv.os_homedir()
|
||||
local os_name = uv.os_uname().sysname
|
||||
local appdata = os.getenv 'APPDATA' or (home .. '/AppData/Roaming')
|
||||
require('formatters_auto_config').setup()
|
||||
|
||||
-- List of formatter configs
|
||||
local formatters = {
|
||||
{
|
||||
name = 'clang-format',
|
||||
path = (os_name:match 'Windows' and home .. '\\.clang-format' or home .. '/.clang-format'),
|
||||
content = [[
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'prettier',
|
||||
path = (os_name:match 'Windows' and appdata .. '\\Prettier\\.prettierrc' or home .. '/.prettierrc'),
|
||||
content = [[
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100
|
||||
}
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'yapf',
|
||||
path = (os_name:match 'Windows' and home .. '\\.style.yapf' or home .. '/.style.yapf'),
|
||||
content = [[
|
||||
[style]
|
||||
based_on_style = pep8
|
||||
indent_width = 4
|
||||
use_tabs = True
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'isort',
|
||||
path = (os_name:match 'Windows' and home .. '\\.isort.cfg' or home .. '/.isort.cfg'),
|
||||
content = [[
|
||||
[settings]
|
||||
profile = black
|
||||
force_single_line = true
|
||||
]],
|
||||
},
|
||||
}
|
||||
|
||||
-- Helper to create file if it doesn't exist
|
||||
local function ensure_file(path, content)
|
||||
if not path then
|
||||
print 'Invalid path, skipping file creation'
|
||||
return
|
||||
end
|
||||
|
||||
local stat = uv.fs_stat(path)
|
||||
if not stat then
|
||||
-- Make parent directory if needed
|
||||
local dir = vim.fn.fnamemodify(path, ':h')
|
||||
if vim.fn.isdirectory(dir) == 0 then
|
||||
vim.fn.mkdir(dir, 'p') -- recursively create directories
|
||||
print('Created directory: ' .. dir)
|
||||
end
|
||||
|
||||
-- Write the file
|
||||
local fd = uv.fs_open(path, 'w', 420) -- 0644
|
||||
if fd then
|
||||
uv.fs_write(fd, content, -1)
|
||||
uv.fs_close(fd)
|
||||
print('Created file: ' .. path)
|
||||
else
|
||||
print('Failed to create file: ' .. path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Loop through all formatter configs
|
||||
for _, fmt in ipairs(formatters) do
|
||||
ensure_file(fmt.path, fmt.content)
|
||||
end
|
||||
|
||||
-- Auto-clear messages on most user actions
|
||||
local clear_msg_grp = vim.api.nvim_create_augroup('AutoClearMessages', { clear = true })
|
||||
|
||||
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI', 'InsertEnter', 'InsertLeave', 'TextChanged', 'TextChangedI' }, {
|
||||
group = clear_msg_grp,
|
||||
callback = function()
|
||||
vim.cmd 'echo ""' -- Clear the message/command line
|
||||
end,
|
||||
})
|
||||
|
||||
-- Auto-clear messages on pressing ESC
|
||||
vim.keymap.set('n', '<Esc>', '<Esc><Cmd>echo ""<CR>', { noremap = true, silent = true })
|
||||
vim.keymap.set('v', '<Esc>', '<Esc><Cmd>echo ""<CR>', { noremap = true, silent = true })
|
||||
-- Auto-clear command line messages on most user actions and on pressing ESC
|
||||
require('message_auto_clear').setup()
|
||||
|
||||
-- When a file is deleted externally, rename all its buffers to "[file]: file removed"
|
||||
-- List of buffer names or filetypes to skip (UndoTree, Neo-tree, etc.)
|
||||
local skip_buffers = { 'undotree', 'neo-tree' }
|
||||
-- Helper function to determine if a buffer should be skipped in future
|
||||
local function should_skip(buf)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return true
|
||||
end
|
||||
local ft = vim.api.nvim_buf_get_option(buf, 'filetype')
|
||||
local bufname = vim.api.nvim_buf_get_name(buf)
|
||||
if bufname == '' then
|
||||
return true
|
||||
end
|
||||
for _, v in ipairs(skip_buffers) do
|
||||
if ft == v or bufname:match(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost' }, {
|
||||
callback = function()
|
||||
local current_buf = vim.api.nvim_get_current_buf()
|
||||
if should_skip(current_buf) then
|
||||
return
|
||||
end
|
||||
local bufname = vim.api.nvim_buf_get_name(current_buf)
|
||||
-- If this file no longer exists, mark all buffers showing it
|
||||
if vim.fn.filereadable(bufname) == 0 then
|
||||
local filename = vim.fn.fnamemodify(bufname, ':t')
|
||||
local new_name = string.format('[%s]: file removed', filename)
|
||||
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
local name = vim.api.nvim_buf_get_name(buf)
|
||||
if name == bufname and not name:match 'file removed' then
|
||||
-- Temporarily unlist so renaming works cleanly
|
||||
vim.api.nvim_buf_set_option(buf, 'buflisted', false)
|
||||
vim.api.nvim_buf_set_name(buf, new_name)
|
||||
vim.api.nvim_buf_set_option(buf, 'buflisted', true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
-- Automatically wipe "file removed" buffers when they are closed
|
||||
vim.api.nvim_create_autocmd('BufWinLeave', {
|
||||
callback = function(args)
|
||||
local buf = args.buf
|
||||
if should_skip(buf) then
|
||||
return
|
||||
end
|
||||
local bufname = vim.api.nvim_buf_get_name(buf)
|
||||
-- If the buffer was marked for wipe or name indicates it's deleted
|
||||
local marked = pcall(vim.api.nvim_buf_get_var, buf, 'marked_for_wipe') and vim.api.nvim_buf_get_var(buf, 'marked_for_wipe')
|
||||
if marked or (bufname ~= '' and bufname:match 'file removed') then
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
vim.cmd('bwipeout! ' .. buf)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end,
|
||||
})
|
||||
require 'buffer_deleted'
|
||||
|
||||
-- The line beneath this is called `modeline`. See `:help modeline`
|
||||
-- vim: ts=2 sts=2 sw=2 et
|
||||
|
||||
73
lua/buffer_deleted.lua
Normal file
73
lua/buffer_deleted.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
-- When a file is deleted externally, rename all its buffers to "[file]: file removed"
|
||||
|
||||
local M = {}
|
||||
|
||||
-- List of buffer names or filetypes to skip (UndoTree, Neo-tree, etc.)
|
||||
local skip_buffers = { 'undotree', 'neo-tree' }
|
||||
|
||||
-- Helper function to determine if a buffer should be skipped in future
|
||||
local function should_skip(buf)
|
||||
if not vim.api.nvim_buf_is_valid(buf) then
|
||||
return true
|
||||
end
|
||||
local ft = vim.api.nvim_buf_get_option(buf, 'filetype')
|
||||
local bufname = vim.api.nvim_buf_get_name(buf)
|
||||
if bufname == '' then
|
||||
return true
|
||||
end
|
||||
for _, v in ipairs(skip_buffers) do
|
||||
if ft == v or bufname:match(v) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Detect deleted files and rename their buffers
|
||||
vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost' }, {
|
||||
callback = function()
|
||||
local current_buf = vim.api.nvim_get_current_buf()
|
||||
if should_skip(current_buf) then
|
||||
return
|
||||
end
|
||||
local bufname = vim.api.nvim_buf_get_name(current_buf)
|
||||
-- If this file no longer exists, mark all buffers showing it
|
||||
if vim.fn.filereadable(bufname) == 0 then
|
||||
local filename = vim.fn.fnamemodify(bufname, ':t')
|
||||
local new_name = string.format('[%s]: file removed', filename)
|
||||
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
local name = vim.api.nvim_buf_get_name(buf)
|
||||
if name == bufname and not name:match 'file removed' then
|
||||
-- Temporarily unlist so renaming works cleanly
|
||||
vim.api.nvim_buf_set_option(buf, 'buflisted', false)
|
||||
vim.api.nvim_buf_set_name(buf, new_name)
|
||||
vim.api.nvim_buf_set_option(buf, 'buflisted', true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- Automatically wipe "file removed" buffers when they are closed
|
||||
vim.api.nvim_create_autocmd('BufWinLeave', {
|
||||
callback = function(args)
|
||||
local buf = args.buf
|
||||
if should_skip(buf) then
|
||||
return
|
||||
end
|
||||
local bufname = vim.api.nvim_buf_get_name(buf)
|
||||
-- If the buffer was marked for wipe or name indicates it's deleted
|
||||
local marked = pcall(vim.api.nvim_buf_get_var, buf, 'marked_for_wipe') and vim.api.nvim_buf_get_var(buf, 'marked_for_wipe')
|
||||
if marked or (bufname ~= '' and bufname:match 'file removed') then
|
||||
vim.schedule(function()
|
||||
if vim.api.nvim_buf_is_valid(buf) then
|
||||
vim.cmd('bwipeout! ' .. buf)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return M
|
||||
66
lua/custom_tabline.lua
Normal file
66
lua/custom_tabline.lua
Normal file
@@ -0,0 +1,66 @@
|
||||
-- Custom native-looking tabline that matches Kanagawa theme
|
||||
-- This module creates a fixed-width, scrollable tabline that
|
||||
local M = {}
|
||||
|
||||
local max_visible_tabs = 8 -- maximum number of tabs visible at once
|
||||
local tab_offset = 1
|
||||
|
||||
function M.tabline()
|
||||
local current_tab = vim.fn.tabpagenr()
|
||||
local tab_count = vim.fn.tabpagenr '$'
|
||||
local win_width = vim.o.columns
|
||||
local tab_width = math.floor(win_width / max_visible_tabs) -- FIXED width
|
||||
|
||||
local tabs = {}
|
||||
for i = 1, tab_count do
|
||||
local buflist = vim.fn.tabpagebuflist(i)
|
||||
local winnr = vim.fn.tabpagewinnr(i)
|
||||
local bufname = vim.fn.bufname(buflist[winnr])
|
||||
if bufname == '' then
|
||||
bufname = '[No Name]'
|
||||
end
|
||||
local modified = vim.fn.getbufvar(buflist[winnr], '&mod') == 1 and ' ●' or ''
|
||||
local label = i .. ': ' .. vim.fn.fnamemodify(bufname, ':t') .. modified
|
||||
|
||||
-- truncate/pad to fixed tab_width
|
||||
if #label > tab_width - 2 then
|
||||
label = label:sub(1, tab_width - 3) .. '…'
|
||||
end
|
||||
label = label .. string.rep(' ', tab_width - #label)
|
||||
|
||||
tabs[i] = (i == current_tab and '%#TabLineSel#' or '%#TabLine#') .. label
|
||||
end
|
||||
|
||||
-- scrolling logic to keep active tab visible
|
||||
local start_index = tab_offset
|
||||
if current_tab < start_index then
|
||||
start_index = current_tab
|
||||
elseif current_tab >= start_index + max_visible_tabs then
|
||||
start_index = current_tab - max_visible_tabs + 1
|
||||
end
|
||||
tab_offset = start_index
|
||||
|
||||
-- select visible tabs
|
||||
local visible_tabs = {}
|
||||
for i = start_index, math.min(start_index + max_visible_tabs - 1, tab_count) do
|
||||
table.insert(visible_tabs, tabs[i])
|
||||
end
|
||||
|
||||
-- scrolling arrows
|
||||
local left_arrow = start_index > 1 and '< ' or ' '
|
||||
local right_arrow = start_index + max_visible_tabs - 1 < tab_count and ' >' or ' '
|
||||
|
||||
return '%#TabLine#' .. left_arrow .. table.concat(visible_tabs, '') .. right_arrow .. '%#TabLineFill#'
|
||||
end
|
||||
|
||||
-- adjust offset if tabs are closed
|
||||
vim.api.nvim_create_autocmd({ 'TabClosed' }, {
|
||||
callback = function()
|
||||
local tab_count = vim.fn.tabpagenr '$'
|
||||
if tab_offset > tab_count then
|
||||
tab_offset = math.max(tab_count - max_visible_tabs + 1, 1)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return M
|
||||
93
lua/formatters_auto_config.lua
Normal file
93
lua/formatters_auto_config.lua
Normal file
@@ -0,0 +1,93 @@
|
||||
-- Auto-create config files for formatters (cross-platform)
|
||||
|
||||
local M = {}
|
||||
local uv = vim.loop
|
||||
|
||||
-- Determine OS paths
|
||||
local home = uv.os_homedir()
|
||||
local os_name = uv.os_uname().sysname
|
||||
local appdata = os.getenv 'APPDATA' or (home .. '/AppData/Roaming')
|
||||
|
||||
-- List of formatter configs
|
||||
local formatters = {
|
||||
{
|
||||
name = 'clang-format',
|
||||
path = (os_name:match 'Windows' and home .. '\\.clang-format' or home .. '/.clang-format'),
|
||||
content = [[
|
||||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'prettier',
|
||||
path = (os_name:match 'Windows' and appdata .. '\\Prettier\\.prettierrc' or home .. '/.prettierrc'),
|
||||
content = [[
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"printWidth": 100
|
||||
}
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'yapf',
|
||||
path = (os_name:match 'Windows' and home .. '\\.style.yapf' or home .. '/.style.yapf'),
|
||||
content = [[
|
||||
[style]
|
||||
based_on_style = pep8
|
||||
indent_width = 4
|
||||
use_tabs = True
|
||||
]],
|
||||
},
|
||||
{
|
||||
name = 'isort',
|
||||
path = (os_name:match 'Windows' and home .. '\\.isort.cfg' or home .. '/.isort.cfg'),
|
||||
content = [[
|
||||
[settings]
|
||||
profile = black
|
||||
force_single_line = true
|
||||
]],
|
||||
},
|
||||
}
|
||||
|
||||
-- Helper function to create a file if it doesn't exist
|
||||
local function ensure_file(path, content)
|
||||
if not path then
|
||||
print 'Invalid path, skipping file creation'
|
||||
return
|
||||
end
|
||||
|
||||
local stat = uv.fs_stat(path)
|
||||
if not stat then
|
||||
-- Make parent directory if needed
|
||||
local dir = vim.fn.fnamemodify(path, ':h')
|
||||
if vim.fn.isdirectory(dir) == 0 then
|
||||
vim.fn.mkdir(dir, 'p') -- recursively create directories
|
||||
print('Created directory: ' .. dir)
|
||||
end
|
||||
|
||||
-- Write the file
|
||||
local fd = uv.fs_open(path, 'w', 420) -- 0644
|
||||
if fd then
|
||||
uv.fs_write(fd, content, -1)
|
||||
uv.fs_close(fd)
|
||||
print('Created file: ' .. path)
|
||||
else
|
||||
print('Failed to create file: ' .. path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Function to run the auto-creation for all formatters
|
||||
function M.setup()
|
||||
for _, fmt in ipairs(formatters) do
|
||||
ensure_file(fmt.path, fmt.content)
|
||||
end
|
||||
end
|
||||
|
||||
return M
|
||||
22
lua/message_auto_clear.lua
Normal file
22
lua/message_auto_clear.lua
Normal file
@@ -0,0 +1,22 @@
|
||||
-- Auto-clear command line messages on most user actions and on pressing ESC
|
||||
|
||||
local M = {}
|
||||
|
||||
function M.setup()
|
||||
-- Create an augroup for auto-clearing messages
|
||||
local clear_msg_grp = vim.api.nvim_create_augroup('AutoClearMessages', { clear = true })
|
||||
|
||||
-- Clear messages on common user actions
|
||||
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI', 'InsertEnter', 'InsertLeave', 'TextChanged', 'TextChangedI' }, {
|
||||
group = clear_msg_grp,
|
||||
callback = function()
|
||||
vim.cmd 'echo ""' -- Clear the message/command line
|
||||
end,
|
||||
})
|
||||
|
||||
-- Clear messages when pressing ESC in normal and visual modes
|
||||
vim.keymap.set('n', '<Esc>', '<Esc><Cmd>echo ""<CR>', { noremap = true, silent = true })
|
||||
vim.keymap.set('v', '<Esc>', '<Esc><Cmd>echo ""<CR>', { noremap = true, silent = true })
|
||||
end
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user