mirror of
https://github.com/hyzendust/KickestEnd.nvim.git
synced 2026-07-01 10:22:23 +02:00
Compare commits
11 Commits
fbf75ac3ed
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e0d7b2eec | ||
|
|
38afffe094 | ||
|
|
a33b196aca | ||
|
|
af8e71910c | ||
|
|
7df4ce3c03 | ||
|
|
602735a0f4 | ||
|
|
0e08afd339 | ||
|
|
25f8a5cd15 | ||
|
|
cc449aec74 | ||
|
|
5f8e087d8a | ||
|
|
bbc14c3c9d |
18
init.lua
18
init.lua
@@ -8,9 +8,6 @@ vim.g.loaded_netrwPlugin = 1
|
|||||||
-- keymaps.lua
|
-- keymaps.lua
|
||||||
require 'keymaps'
|
require 'keymaps'
|
||||||
|
|
||||||
-- Copy custom snippets from custom_friendly_snippets folder
|
|
||||||
require 'replace_with_custom_snippets'
|
|
||||||
|
|
||||||
-- :UpdateKickestEnd command to safely update KickestEnd.nvim config from origin/master
|
-- :UpdateKickestEnd command to safely update KickestEnd.nvim config from origin/master
|
||||||
require 'update_kickestend'
|
require 'update_kickestend'
|
||||||
|
|
||||||
@@ -39,14 +36,14 @@ vim.opt.rtp:prepend(lazypath)
|
|||||||
-- You can also configure plugins after the setup call,
|
-- You can also configure plugins after the setup call,
|
||||||
-- as they will be available in your neovim runtime.
|
-- as they will be available in your neovim runtime.
|
||||||
|
|
||||||
-- Disables which-key healthcheck notifications
|
-- Disables which-key healthcheck notification
|
||||||
do
|
do
|
||||||
local orig_notify = vim.notify
|
local orig_notify = vim.notify
|
||||||
vim.notify = function(msg, ...)
|
vim.notify = function(msg, level, ...)
|
||||||
if type(msg) == 'string' and msg:match 'which%-key' then
|
if type(msg) == 'string' and msg:match 'issues reported with your' and msg:match 'which%-key' then
|
||||||
return -- ignore WhichKey health messages
|
return
|
||||||
end
|
end
|
||||||
return orig_notify(msg, ...)
|
return orig_notify(msg, level, ...)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -138,8 +135,7 @@ require('lazy').setup({
|
|||||||
event = 'InsertEnter',
|
event = 'InsertEnter',
|
||||||
dependencies = {
|
dependencies = {
|
||||||
-- Snippet Engine & its associated nvim-cmp source
|
-- Snippet Engine & its associated nvim-cmp source
|
||||||
'L3MON4D3/LuaSnip',
|
{ 'L3MON4D3/LuaSnip', dependencies = { 'rafamadriz/friendly-snippets' } },
|
||||||
dependencies = { 'rafamadriz/friendly-snippets' },
|
|
||||||
'saadparwaiz1/cmp_luasnip',
|
'saadparwaiz1/cmp_luasnip',
|
||||||
-- Adds LSP completion capabilities
|
-- Adds LSP completion capabilities
|
||||||
'hrsh7th/cmp-nvim-lsp',
|
'hrsh7th/cmp-nvim-lsp',
|
||||||
@@ -326,7 +322,7 @@ vim.o.completeopt = 'menuone,noselect'
|
|||||||
vim.o.termguicolors = true
|
vim.o.termguicolors = true
|
||||||
|
|
||||||
-- Tabs and indentation
|
-- Tabs and indentation
|
||||||
vim.o.expandtab = false -- use spaces instead of tabs
|
vim.o.expandtab = false -- use tabs instead of spaces
|
||||||
vim.o.shiftwidth = 4 -- number of spaces for autoindent
|
vim.o.shiftwidth = 4 -- number of spaces for autoindent
|
||||||
vim.o.softtabstop = 0 -- number of spaces per Tab in insert mode
|
vim.o.softtabstop = 0 -- number of spaces per Tab in insert mode
|
||||||
vim.o.tabstop = 4 -- number of spaces a Tab counts for
|
vim.o.tabstop = 4 -- number of spaces a Tab counts for
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
-- Manages Alpha refresh when switching and preserves ASCII art
|
-- Manages Alpha refresh when switching and preserves ASCII art
|
||||||
|
-- Temporarily unlist other buffers so alpha.start(true) isn't skipped
|
||||||
|
-- when other tabs have real files open, then restore them after.
|
||||||
|
-- Also bypass argc()>0, which alpha's own should_skip_alpha() checks
|
||||||
|
-- first -- this stays true for the whole session if nvim was launched
|
||||||
|
-- as `nvim file.txt`, blocking alpha.start(true) everywhere afterward.
|
||||||
|
|
||||||
_G.alpha_tab_ascii = {}
|
_G.alpha_tab_ascii = {}
|
||||||
|
|
||||||
-- Temporarily unlist other buffers so alpha.start(true) isn't skipped
|
|
||||||
-- when other tabs have real files open, then restore them after
|
|
||||||
local function force_alpha_start()
|
local function force_alpha_start()
|
||||||
local alpha_loaded, alpha = pcall(require, 'alpha')
|
local alpha_loaded, alpha = pcall(require, 'alpha')
|
||||||
if not alpha_loaded then
|
if not alpha_loaded then
|
||||||
@@ -19,7 +22,15 @@ local function force_alpha_start()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
alpha.start(true)
|
local orig_argc = vim.fn.argc
|
||||||
|
vim.fn.argc = function(...)
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
local ok = pcall(alpha.start, true)
|
||||||
|
vim.fn.argc = orig_argc
|
||||||
|
if not ok then
|
||||||
|
vim.notify('Failed to start Alpha', vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
|
||||||
for _, buf in ipairs(restore) do
|
for _, buf in ipairs(restore) do
|
||||||
if vim.api.nvim_buf_is_valid(buf) then
|
if vim.api.nvim_buf_is_valid(buf) then
|
||||||
@@ -91,9 +102,6 @@ vim.api.nvim_create_autocmd('TabEnter', {
|
|||||||
})
|
})
|
||||||
|
|
||||||
-- Clean
|
-- Clean
|
||||||
-- <afile> here is the tab's position number, not its handle, so we
|
|
||||||
-- can't use it to key into alpha_tab_ascii directly. Prune any cached
|
|
||||||
-- handle that's no longer a live tabpage instead.
|
|
||||||
vim.api.nvim_create_autocmd('TabClosed', {
|
vim.api.nvim_create_autocmd('TabClosed', {
|
||||||
pattern = '*',
|
pattern = '*',
|
||||||
callback = function()
|
callback = function()
|
||||||
|
|||||||
@@ -5,6 +5,19 @@ local M = {}
|
|||||||
-- List of buffer names or filetypes to skip (UndoTree, Neo-tree, etc.)
|
-- List of buffer names or filetypes to skip (UndoTree, Neo-tree, etc.)
|
||||||
local skip_buffers = { 'undotree', 'neo-tree' }
|
local skip_buffers = { 'undotree', 'neo-tree' }
|
||||||
|
|
||||||
|
-- getftime() returns -1 for both never-existed and deleted files
|
||||||
|
local confirmed_existed = {}
|
||||||
|
vim.api.nvim_create_autocmd({ 'BufReadPost', 'BufWritePost' }, {
|
||||||
|
callback = function(args)
|
||||||
|
confirmed_existed[args.buf] = true
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
vim.api.nvim_create_autocmd('BufDelete', {
|
||||||
|
callback = function(args)
|
||||||
|
confirmed_existed[args.buf] = nil
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
-- Helper function to determine if a buffer should be skipped in future
|
-- Helper function to determine if a buffer should be skipped in future
|
||||||
local function should_skip(buf)
|
local function should_skip(buf)
|
||||||
if not vim.api.nvim_buf_is_valid(buf) then
|
if not vim.api.nvim_buf_is_valid(buf) then
|
||||||
@@ -44,10 +57,9 @@ local function rename_deleted_buffers()
|
|||||||
local bufname = vim.api.nvim_buf_get_name(buf)
|
local bufname = vim.api.nvim_buf_get_name(buf)
|
||||||
|
|
||||||
if bufname ~= '' and vim.fn.filereadable(bufname) == 0 then
|
if bufname ~= '' and vim.fn.filereadable(bufname) == 0 then
|
||||||
-- Skip buffers for files that have never been written (new files),
|
-- Skip never-written new files, but not deleted ones
|
||||||
-- regardless of whether they have unsaved (modified) content
|
|
||||||
local ftime = vim.fn.getftime(bufname)
|
local ftime = vim.fn.getftime(bufname)
|
||||||
if ftime == -1 then
|
if ftime == -1 and not confirmed_existed[buf] then
|
||||||
goto continue
|
goto continue
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -70,6 +82,9 @@ local function rename_deleted_buffers()
|
|||||||
vim.api.nvim_buf_set_option(buf, 'buflisted', false)
|
vim.api.nvim_buf_set_option(buf, 'buflisted', false)
|
||||||
vim.api.nvim_buf_set_name(buf, new_name)
|
vim.api.nvim_buf_set_name(buf, new_name)
|
||||||
vim.api.nvim_buf_set_option(buf, 'buflisted', true)
|
vim.api.nvim_buf_set_option(buf, 'buflisted', true)
|
||||||
|
-- Prevent accidental :w from creating this file on disk
|
||||||
|
vim.api.nvim_buf_set_option(buf, 'buftype', 'nofile')
|
||||||
|
vim.api.nvim_buf_set_option(buf, 'modifiable', false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -85,8 +100,8 @@ vim.api.nvim_create_autocmd({ 'FocusGained', 'BufEnter' }, {
|
|||||||
if should_skip(buf) then
|
if should_skip(buf) then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
-- Force Neovim to check external changes
|
-- Restrict to current buffer; checktime with no arg checks all
|
||||||
vim.cmd 'checktime'
|
vim.cmd('checktime ' .. buf)
|
||||||
rename_deleted_buffers()
|
rename_deleted_buffers()
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -23,10 +23,13 @@ function M.tabline()
|
|||||||
local label = i .. ': ' .. vim.fn.fnamemodify(bufname, ':t') .. modified
|
local label = i .. ': ' .. vim.fn.fnamemodify(bufname, ':t') .. modified
|
||||||
|
|
||||||
-- truncate/pad to fixed tab_width
|
-- truncate/pad to fixed tab_width
|
||||||
if #label > tab_width - 2 then
|
if vim.fn.strwidth(label) > tab_width - 2 then
|
||||||
label = label:sub(1, tab_width - 3) .. '…'
|
while vim.fn.strwidth(label) > tab_width - 3 and #label > 0 do
|
||||||
|
label = label:sub(1, -2)
|
||||||
end
|
end
|
||||||
label = label .. string.rep(' ', tab_width - #label)
|
label = label .. '…'
|
||||||
|
end
|
||||||
|
label = label .. string.rep(' ', tab_width - vim.fn.strwidth(label))
|
||||||
|
|
||||||
tabs[i] = (i == current_tab and '%#TabLineSel#' or '%#TabLine#') .. label
|
tabs[i] = (i == current_tab and '%#TabLineSel#' or '%#TabLine#') .. label
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -242,14 +242,12 @@ local function smart_save(force_save_as)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- Preserve cursor position and undo history
|
-- Preserve cursor position
|
||||||
local old_buf = vim.api.nvim_get_current_buf()
|
local old_buf = vim.api.nvim_get_current_buf()
|
||||||
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
||||||
local undo_history = vim.fn.getbufinfo(old_buf)[1].changedtick
|
|
||||||
vim.cmd('edit ' .. vim.fn.fnameescape(filename))
|
vim.cmd('edit ' .. vim.fn.fnameescape(filename))
|
||||||
vim.cmd 'filetype detect'
|
vim.cmd 'filetype detect'
|
||||||
vim.api.nvim_win_set_cursor(0, cursor_pos)
|
vim.api.nvim_win_set_cursor(0, cursor_pos)
|
||||||
vim.cmd 'undojoin'
|
|
||||||
vim.api.nvim_buf_delete(old_buf, { force = true })
|
vim.api.nvim_buf_delete(old_buf, { force = true })
|
||||||
end
|
end
|
||||||
print('Saved as ' .. filename)
|
print('Saved as ' .. filename)
|
||||||
@@ -744,4 +742,17 @@ for i = 32, 126 do
|
|||||||
end, { noremap = true, expr = true, silent = true })
|
end, { noremap = true, expr = true, silent = true })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
vim.keymap.set({ 'i', 'v', 't' }, 'q', q_double_escape, { noremap = true, expr = true, silent = true })
|
-- Insert/terminal mode:
|
||||||
|
vim.keymap.set({ 'i', 't' }, 'q', q_double_escape, { noremap = true, expr = true, silent = true })
|
||||||
|
-- Visual mode:
|
||||||
|
vim.keymap.set('v', 'q', function()
|
||||||
|
local ok, nr = pcall(vim.fn.getchar)
|
||||||
|
if not ok then
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
local nextchar = type(nr) == 'number' and vim.fn.nr2char(nr) or nr
|
||||||
|
if nextchar == 'q' then
|
||||||
|
return vim.api.nvim_replace_termcodes('<Esc>', true, false, true)
|
||||||
|
end
|
||||||
|
return 'q' .. nextchar
|
||||||
|
end, { noremap = true, expr = true, silent = true })
|
||||||
|
|||||||
@@ -121,7 +121,45 @@ return {
|
|||||||
local cmp = require 'cmp'
|
local cmp = require 'cmp'
|
||||||
local luasnip = require 'luasnip'
|
local luasnip = require 'luasnip'
|
||||||
require('luasnip.loaders.from_vscode').lazy_load()
|
require('luasnip.loaders.from_vscode').lazy_load()
|
||||||
--require('luasnip.loaders.from_vscode').lazy_load({ paths = { "~/.config/nvim/my_snippets" } })
|
do
|
||||||
|
local parser = require 'luasnip.util.parser'
|
||||||
|
local custom_dir = vim.fn.stdpath 'config' .. '/custom_friendly_snippets'
|
||||||
|
local handle = vim.loop.fs_scandir(custom_dir)
|
||||||
|
if handle then
|
||||||
|
while true do
|
||||||
|
local name, ftype = vim.loop.fs_scandir_next(handle)
|
||||||
|
if not name then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
if ftype == 'file' and name:match '%.json$' then
|
||||||
|
local filetype = name:gsub('%.json$', '')
|
||||||
|
local lines_ok, lines = pcall(vim.fn.readfile, custom_dir .. '/' .. name)
|
||||||
|
if lines_ok then
|
||||||
|
local decode_ok, data = pcall(vim.json.decode, table.concat(lines, '\n'))
|
||||||
|
if decode_ok and type(data) == 'table' then
|
||||||
|
local snippets = {}
|
||||||
|
for snip_name, snip in pairs(data) do
|
||||||
|
local prefixes = type(snip.prefix) == 'table' and snip.prefix or { snip.prefix }
|
||||||
|
local body = type(snip.body) == 'table' and table.concat(snip.body, '\n') or snip.body
|
||||||
|
for _, prefix in ipairs(prefixes) do
|
||||||
|
local parse_ok, parsed =
|
||||||
|
pcall(parser.parse_snippet, { trig = prefix, name = snip_name, desc = snip.description }, body)
|
||||||
|
if parse_ok then
|
||||||
|
table.insert(snippets, parsed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
luasnip.add_snippets(filetype, snippets, { default_priority = 2000 })
|
||||||
|
else
|
||||||
|
vim.notify('Failed to decode custom snippet file: ' .. name, vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
vim.notify('Failed to read custom snippet file: ' .. name, vim.log.levels.ERROR)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
luasnip.config.setup {}
|
luasnip.config.setup {}
|
||||||
cmp.setup {
|
cmp.setup {
|
||||||
snippet = {
|
snippet = {
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
local uv = vim.loop
|
|
||||||
local home = uv.os_homedir()
|
|
||||||
-- Detect OS and base path of friendly-snippets
|
|
||||||
local function get_friendly_snippets_base_path()
|
|
||||||
local os_name = uv.os_uname().sysname
|
|
||||||
if os_name == 'Windows_NT' then
|
|
||||||
return home .. '\\.local\\share\\nvim\\lazy\\friendly-snippets\\snippets\\'
|
|
||||||
else
|
|
||||||
return home .. '/.local/share/nvim/lazy/friendly-snippets/snippets/'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local target_base = get_friendly_snippets_base_path()
|
|
||||||
local custom_snippets_dir = vim.fn.stdpath 'config' .. '/custom_friendly_snippets/'
|
|
||||||
-- Read all files in custom_snippets_dir
|
|
||||||
local function get_custom_snippet_files()
|
|
||||||
local handle = uv.fs_scandir(custom_snippets_dir)
|
|
||||||
if not handle then
|
|
||||||
vim.notify('Custom snippets folder not found: ' .. custom_snippets_dir, vim.log.levels.ERROR)
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
local files = {}
|
|
||||||
while true do
|
|
||||||
local name, type = uv.fs_scandir_next(handle)
|
|
||||||
if not name then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
if type == 'file' then
|
|
||||||
table.insert(files, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return files
|
|
||||||
end
|
|
||||||
-- Replace each snippet file
|
|
||||||
local function replace_snippets()
|
|
||||||
local files = get_custom_snippet_files()
|
|
||||||
for _, filename in ipairs(files) do
|
|
||||||
local source_path = custom_snippets_dir .. filename
|
|
||||||
local target_path = target_base .. filename
|
|
||||||
local source_file = io.open(source_path, 'r')
|
|
||||||
if not source_file then
|
|
||||||
vim.notify('Failed to read: ' .. source_path, vim.log.levels.ERROR)
|
|
||||||
goto continue
|
|
||||||
end
|
|
||||||
local source_content = source_file:read '*a'
|
|
||||||
source_file:close()
|
|
||||||
-- Read target content (if exists)
|
|
||||||
local target_content = ''
|
|
||||||
local target_file = io.open(target_path, 'r')
|
|
||||||
if target_file then
|
|
||||||
target_content = target_file:read '*a'
|
|
||||||
target_file:close()
|
|
||||||
end
|
|
||||||
-- Only update if different
|
|
||||||
if source_content ~= target_content then
|
|
||||||
-- Create backup if not exists
|
|
||||||
local backup_path = target_path .. '.bak'
|
|
||||||
if not uv.fs_stat(backup_path) and target_content ~= '' then
|
|
||||||
local backup_file = io.open(backup_path, 'w')
|
|
||||||
if backup_file then
|
|
||||||
backup_file:write(target_content)
|
|
||||||
backup_file:close()
|
|
||||||
else
|
|
||||||
vim.notify('Failed to create backup: ' .. backup_path, vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local output = io.open(target_path, 'w')
|
|
||||||
if output then
|
|
||||||
output:write(source_content)
|
|
||||||
output:close()
|
|
||||||
vim.notify('Updated snippet: ' .. filename, vim.log.levels.INFO)
|
|
||||||
else
|
|
||||||
vim.notify('Failed to write: ' .. target_path, vim.log.levels.ERROR)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
::continue::
|
|
||||||
end
|
|
||||||
end
|
|
||||||
replace_snippets()
|
|
||||||
@@ -27,15 +27,21 @@ vim.api.nvim_create_user_command('UpdateKickestEnd', function()
|
|||||||
local origin_master = vim.fn.systemlist({ 'git', '-C', config_path, 'rev-parse', 'origin/master' })[1]
|
local origin_master = vim.fn.systemlist({ 'git', '-C', config_path, 'rev-parse', 'origin/master' })[1]
|
||||||
local is_uptodate = (head == origin_master)
|
local is_uptodate = (head == origin_master)
|
||||||
|
|
||||||
|
-- Check for local commits not yet pushed to origin/master -- a
|
||||||
|
local ahead_count = tonumber(vim.fn.systemlist({ 'git', '-C', config_path, 'rev-list', '--count', 'origin/master..HEAD' })[1]) or 0
|
||||||
|
local has_unpushed_commits = ahead_count > 0
|
||||||
|
|
||||||
-- Skip update if nothing to do
|
-- Skip update if nothing to do
|
||||||
if not dirty and is_uptodate then
|
if not dirty and is_uptodate then
|
||||||
vim.notify('KickestEnd.nvim is already up-to-date with origin/master.', vim.log.levels.INFO)
|
vim.notify('KickestEnd.nvim is already up-to-date with origin/master.', vim.log.levels.INFO)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If there are local changes or untracked files, confirm overwrite
|
-- If there are local changes, untracked files, or unpushed commits, confirm overwrite
|
||||||
if dirty then
|
if dirty or has_unpushed_commits then
|
||||||
local answer = vim.fn.input 'Local changes or new files detected! Overwrite and delete them? (y/N): '
|
local prompt = dirty and 'Local changes or new files detected! Overwrite and delete them? (y/N): '
|
||||||
|
or string.format('%d local commit(s) not on origin/master will be lost! Continue? (y/N): ', ahead_count)
|
||||||
|
local answer = vim.fn.input(prompt)
|
||||||
if answer:lower() ~= 'y' then
|
if answer:lower() ~= 'y' then
|
||||||
vim.notify('Update cancelled to preserve your modifications.', vim.log.levels.WARN)
|
vim.notify('Update cancelled to preserve your modifications.', vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user