From db2b2d364f7b6139bd63557439c5a6e9b59150cb Mon Sep 17 00:00:00 2001 From: Zacharias-Brohn Date: Wed, 25 Feb 2026 11:30:39 +0100 Subject: [PATCH] cmp is back baby --- autoload/neoformat/formatters/qml.vim | 13 ++ ftplugin/qml.lua | 12 +- init.lua | 2 +- lua/autocmd.lua | 55 +++++++ lua/coc-settings.lua | 152 ++++++++++++------- lua/config/catppuccin.lua | 8 +- lua/config/codex.lua | 18 +++ lua/config/load-colorscheme.lua | 2 + lua/config/lspconfig.lua | 85 ++++++++++- lua/config/notify.lua | 145 +++++++----------- lua/config/render-markdown.lua | 55 +++++++ lua/config/smart-splits.lua | 22 +++ lua/config/snacks.lua | 61 +++++++- lua/functions/smart_jump.lua | 202 ++++++++++++++++++++++++++ lua/mappings.lua | 18 +++ lua/plugins/colorschemes.lua | 7 +- lua/plugins/init.lua | 102 ++++++++----- lua/plugins/neoformat.vim | 1 + 18 files changed, 760 insertions(+), 200 deletions(-) create mode 100644 autoload/neoformat/formatters/qml.vim create mode 100644 lua/config/codex.lua create mode 100644 lua/config/load-colorscheme.lua create mode 100644 lua/config/render-markdown.lua create mode 100644 lua/config/smart-splits.lua create mode 100644 lua/functions/smart_jump.lua diff --git a/autoload/neoformat/formatters/qml.vim b/autoload/neoformat/formatters/qml.vim new file mode 100644 index 0000000..4f09f0a --- /dev/null +++ b/autoload/neoformat/formatters/qml.vim @@ -0,0 +1,13 @@ +function! neoformat#formatters#qml#enabled() abort + return ['qmlformat'] +endfunction + +function! neoformat#formatters#qml#qmlformat() abort + return { + \ 'exe': '/usr/lib/qt6/bin/qmlformat', + \ 'args': ['-t', '-n', '--objects-spacing', '--functions-spacing', '-i'], + \ 'replace': 1, + \ 'stderr': 1, + \ 'stdin': 0 + \ } +endfunction diff --git a/ftplugin/qml.lua b/ftplugin/qml.lua index b645400..b90964e 100644 --- a/ftplugin/qml.lua +++ b/ftplugin/qml.lua @@ -1,12 +1,16 @@ local gen_hook = MiniSplitjoin.gen_hook -local curly = { brackets = { '%b{}' }, separator = ';' } -local curly_semi = { brackets = { '%b{}' }, separator = ';' } +local curly = { brackets = { "%b{}" }, separator = ";" } +local curly_semi = { brackets = { "%b{}" }, separator = ";" } local add_semicolon_curly = gen_hook.add_trailing_separator(curly) local remove_semicolon_curly = gen_hook.del_trailing_separator(curly) local pad_curly = gen_hook.pad_brackets(curly_semi) vim.b.minisplitjoin_config = { - split = { hooks_post = { remove_semicolon_curly }}, - join = { hooks_pre = { add_semicolon_curly }, hooks_post = { pad_curly }}, + split = { hooks_post = { remove_semicolon_curly } }, + join = { hooks_pre = { add_semicolon_curly }, hooks_post = { pad_curly } }, } + +vim.keymap.set("n", "gd", function() + require("functions.smart_jump").smart_qml_jump() +end, { buffer = bufnr, silent = true, desc = "Smart QML jump" }) diff --git a/init.lua b/init.lua index 953b0b2..218245a 100644 --- a/init.lua +++ b/init.lua @@ -40,4 +40,4 @@ vim.filetype.add { }, } -vim.cmd "colorscheme tokyonight" +require "config.load-colorscheme" diff --git a/lua/autocmd.lua b/lua/autocmd.lua index b0d4e41..cdfadaa 100644 --- a/lua/autocmd.lua +++ b/lua/autocmd.lua @@ -27,3 +27,58 @@ autocmd({ "InsertLeave" }, { require("lint").try_lint() end, }) + +local progress = vim.defaulttable() +vim.api.nvim_create_autocmd("LspProgress", { + ---@param ev {data: {client_id: integer, params: lsp.ProgressParams}} + callback = function(ev) + local client = vim.lsp.get_client_by_id(ev.data.client_id) + local value = ev.data.params.value --[[@as {percentage?: number, title?: string, message?: string, kind: "begin" | "report" | "end"}]] + if not client or type(value) ~= "table" then + return + end + local p = progress[client.id] + + for i = 1, #p + 1 do + if i == #p + 1 or p[i].token == ev.data.params.token then + p[i] = { + token = ev.data.params.token, + msg = ("[%3d%%] %s%s"):format( + value.kind == "end" and 100 or value.percentage or 100, + value.title or "", + value.message and (" **%s**"):format(value.message) + or "" + ), + done = value.kind == "end", + } + break + end + end + + local msg = {} ---@type string[] + progress[client.id] = vim.tbl_filter(function(v) + return table.insert(msg, v.msg) or not v.done + end, p) + + local spinner = { + "⠋", + "⠙", + "⠹", + "⠸", + "⠼", + "⠴", + "⠦", + "⠧", + "⠇", + "⠏", + } + vim.notify(table.concat(msg, "\n"), "info", { + id = "lsp_progress", + title = client.name, + opts = function(notif) + notif.icon = #progress[client.id] == 0 and " " + or spinner[math.floor(vim.uv.hrtime() / (1e6 * 80)) % #spinner + 1] + end, + }) + end, +}) diff --git a/lua/coc-settings.lua b/lua/coc-settings.lua index 3812e41..10c8e95 100644 --- a/lua/coc-settings.lua +++ b/lua/coc-settings.lua @@ -1,4 +1,4 @@ -vimiopt.backup = false +vim.opt.backup = false vim.opt.writebackup = false vim.opt.completeopt = "menuone,menu,noinsert,noselect,popup" vim.opt.pumheight = 10 @@ -9,8 +9,8 @@ vim.opt.signcolumn = "yes" local keyset = vim.keymap.set function _G.check_back_space() - local col = vim.fn.col('.') - 1 - return col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') ~= nil + local col = vim.fn.col "." - 1 + return col == 0 or vim.fn.getline("."):sub(col, col):match "%s" ~= nil end -- Use Tab for trigger completion with characters ahead and navigate @@ -18,75 +18,81 @@ end -- no select by setting `"suggest.noselect": true` in your configuration file -- NOTE: Use command ':verbose imap ' to make sure Tab is not mapped by -- other plugins before putting this into your config -local opts = {silent = true, noremap = true, expr = true, replace_keycodes = false} -keyset("i", "", 'coc#pum#visible() ? coc#pum#next(0) : v:lua.check_back_space() ? "" : coc#refresh()', opts) +local opts = + { silent = true, noremap = true, expr = true, replace_keycodes = false } +keyset( + "i", + "", + 'coc#pum#visible() ? coc#pum#next(0) : v:lua.check_back_space() ? "" : coc#refresh()', + opts +) keyset("i", "", [[coc#pum#visible() ? coc#pum#prev(0) : "\"]], opts) -- Make to accept selected completion item or notify coc.nvim to format -- u breaks current undo, please make your own choice -keyset("i", "", [[coc#pum#visible() ? coc#pum#confirm() : "\u\\=coc#on_enter()\"]], opts) +keyset( + "i", + "", + [[coc#pum#visible() ? coc#pum#confirm() : "\u\\=coc#on_enter()\"]], + opts +) -- Use to trigger snippets keyset("i", "", "(coc-snippets-expand-jump)") -- Use to trigger completion -keyset("i", "", "coc#refresh()", {silent = true, expr = true}) +keyset("i", "", "coc#refresh()", { silent = true, expr = true }) -- Use `[g` and `]g` to navigate diagnostics -- Use `:CocDiagnostics` to get all diagnostics of current buffer in location list -keyset("n", "[g", "(coc-diagnostic-prev)", {silent = true}) -keyset("n", "]g", "(coc-diagnostic-next)", {silent = true}) +keyset("n", "[g", "(coc-diagnostic-prev)", { silent = true }) +keyset("n", "]g", "(coc-diagnostic-next)", { silent = true }) -- GoTo code navigation -keyset("n", "gd", "(coc-definition)", {silent = true}) -keyset("n", "gy", "(coc-type-definition)", {silent = true}) -keyset("n", "gi", "(coc-implementation)", {silent = true}) -keyset("n", "gr", "(coc-references)", {silent = true}) - +keyset("n", "gd", "(coc-definition)", { silent = true }) +keyset("n", "gy", "(coc-type-definition)", { silent = true }) +keyset("n", "gi", "(coc-implementation)", { silent = true }) +keyset("n", "gr", "(coc-references)", { silent = true }) -- Use K to show documentation in preview window function _G.show_docs() - local cw = vim.fn.expand('') - if vim.fn.index({'vim', 'help'}, vim.bo.filetype) >= 0 then - vim.api.nvim_command('h ' .. cw) - elseif vim.api.nvim_eval('coc#rpc#ready()') then - vim.fn.CocActionAsync('doHover') + local cw = vim.fn.expand "" + if vim.fn.index({ "vim", "help" }, vim.bo.filetype) >= 0 then + vim.api.nvim_command("h " .. cw) + elseif vim.api.nvim_eval "coc#rpc#ready()" then + vim.fn.CocActionAsync "doHover" else - vim.api.nvim_command('!' .. vim.o.keywordprg .. ' ' .. cw) + vim.api.nvim_command("!" .. vim.o.keywordprg .. " " .. cw) end end -keyset("n", "K", 'lua _G.show_docs()', {silent = true}) - +keyset("n", "K", "lua _G.show_docs()", { silent = true }) -- Highlight the symbol and its references on a CursorHold event(cursor is idle) vim.api.nvim_create_augroup("CocGroup", {}) vim.api.nvim_create_autocmd("CursorHold", { group = "CocGroup", command = "silent call CocActionAsync('highlight')", - desc = "Highlight symbol under cursor on CursorHold" + desc = "Highlight symbol under cursor on CursorHold", }) - -- Symbol renaming -keyset("n", "rn", "(coc-rename)", {silent = true}) - +keyset("n", "rn", "(coc-rename)", { silent = true }) -- Formatting selected code -keyset("x", "f", "(coc-format-selected)", {silent = true}) -keyset("n", "f", "(coc-format-selected)", {silent = true}) - +keyset("x", "f", "(coc-format-selected)", { silent = true }) +keyset("n", "f", "(coc-format-selected)", { silent = true }) -- Setup formatexpr specified filetype(s) vim.api.nvim_create_autocmd("FileType", { group = "CocGroup", pattern = "typescript,json", command = "setl formatexpr=CocAction('formatSelected')", - desc = "Setup formatexpr specified filetype(s)." + desc = "Setup formatexpr specified filetype(s).", }) -- Apply codeAction to the selected region -- Example: `aap` for current paragraph -local opts = {silent = true, nowait = true} +local opts = { silent = true, nowait = true } keyset("x", "a", "(coc-codeaction-selected)", opts) keyset("n", "a", "(coc-codeaction-selected)", opts) @@ -99,13 +105,22 @@ keyset("n", "qf", "(coc-fix-current)", opts) -- Remap keys for apply refactor code actions. keyset("n", "re", "(coc-codeaction-refactor)", { silent = true }) -keyset("x", "r", "(coc-codeaction-refactor-selected)", { silent = true }) -keyset("n", "r", "(coc-codeaction-refactor-selected)", { silent = true }) +keyset( + "x", + "r", + "(coc-codeaction-refactor-selected)", + { silent = true } +) +keyset( + "n", + "r", + "(coc-codeaction-refactor-selected)", + { silent = true } +) -- Run the Code Lens actions on the current line keyset("n", "cl", "(coc-codelens-action)", opts) - -- Map function and class text objects -- NOTE: Requires 'textDocument.documentSymbol' support from the language server keyset("x", "if", "(coc-funcobj-i)", opts) @@ -117,44 +132,77 @@ keyset("o", "ic", "(coc-classobj-i)", opts) keyset("x", "ac", "(coc-classobj-a)", opts) keyset("o", "ac", "(coc-classobj-a)", opts) - -- Remap and to scroll float windows/popups ---@diagnostic disable-next-line: redefined-local -local opts = {silent = true, nowait = true, expr = true} -keyset("n", "", 'coc#float#has_scroll() ? coc#float#scroll(1) : ""', opts) -keyset("n", "", 'coc#float#has_scroll() ? coc#float#scroll(0) : ""', opts) -keyset("i", "", - 'coc#float#has_scroll() ? "=coc#float#scroll(1)" : ""', opts) -keyset("i", "", - 'coc#float#has_scroll() ? "=coc#float#scroll(0)" : ""', opts) -keyset("v", "", 'coc#float#has_scroll() ? coc#float#scroll(1) : ""', opts) -keyset("v", "", 'coc#float#has_scroll() ? coc#float#scroll(0) : ""', opts) - +local opts = { silent = true, nowait = true, expr = true } +keyset( + "n", + "", + 'coc#float#has_scroll() ? coc#float#scroll(1) : ""', + opts +) +keyset( + "n", + "", + 'coc#float#has_scroll() ? coc#float#scroll(0) : ""', + opts +) +keyset( + "i", + "", + 'coc#float#has_scroll() ? "=coc#float#scroll(1)" : ""', + opts +) +keyset( + "i", + "", + 'coc#float#has_scroll() ? "=coc#float#scroll(0)" : ""', + opts +) +keyset( + "v", + "", + 'coc#float#has_scroll() ? coc#float#scroll(1) : ""', + opts +) +keyset( + "v", + "", + 'coc#float#has_scroll() ? coc#float#scroll(0) : ""', + opts +) -- Use CTRL-S for selections ranges -- Requires 'textDocument/selectionRange' support of language server -keyset("n", "", "(coc-range-select)", {silent = true}) -keyset("x", "", "(coc-range-select)", {silent = true}) - +keyset("n", "", "(coc-range-select)", { silent = true }) +keyset("x", "", "(coc-range-select)", { silent = true }) -- Add `:Format` command to format current buffer vim.api.nvim_create_user_command("Format", "call CocAction('format')", {}) -- " Add `:Fold` command to fold current buffer -vim.api.nvim_create_user_command("Fold", "call CocAction('fold', )", {nargs = '?'}) +vim.api.nvim_create_user_command( + "Fold", + "call CocAction('fold', )", + { nargs = "?" } +) -- Add `:OR` command for organize imports of the current buffer -vim.api.nvim_create_user_command("OR", "call CocActionAsync('runCommand', 'editor.action.organizeImport')", {}) +vim.api.nvim_create_user_command( + "OR", + "call CocActionAsync('runCommand', 'editor.action.organizeImport')", + {} +) -- Add (Neo)Vim's native statusline support -- NOTE: Please see `:h coc-status` for integrations with external plugins that -- provide custom statusline: lightline.vim, vim-airline -vim.opt.statusline:prepend("%{coc#status()}%{get(b:,'coc_current_function','')}") +vim.opt.statusline:prepend "%{coc#status()}%{get(b:,'coc_current_function','')}" -- Mappings for CoCList -- code actions and coc stuff ---@diagnostic disable-next-line: redefined-local -local opts = {silent = true, nowait = true} +local opts = { silent = true, nowait = true } -- Show all diagnostics keyset("n", "a", ":CocList diagnostics", opts) -- Manage extensions diff --git a/lua/config/catppuccin.lua b/lua/config/catppuccin.lua index 087a557..0eb7e7d 100644 --- a/lua/config/catppuccin.lua +++ b/lua/config/catppuccin.lua @@ -1,9 +1,9 @@ ---@diagnostic disable: redundant-parameter -require("catppuccin").setup({ - flavour = "auto", +require("catppuccin").setup { + flavour = "latte", background = { light = "latte", - dark = "mocha", + dark = "latte", }, transparent_background = false, float = { @@ -68,4 +68,4 @@ require("catppuccin").setup({ indentscope_color = "", }, }, -}) +} diff --git a/lua/config/codex.lua b/lua/config/codex.lua new file mode 100644 index 0000000..bec0250 --- /dev/null +++ b/lua/config/codex.lua @@ -0,0 +1,18 @@ +local M = {} + +M = { + keymaps = { + toggle = "", + quit = nil, + }, + + border = "rounded", + width = "0.8", + height = "0.8", + model = "gpt-5.2", + autoinstall = false, + panel = false, + use_buffer = false, +} + +return M diff --git a/lua/config/load-colorscheme.lua b/lua/config/load-colorscheme.lua new file mode 100644 index 0000000..8b63e37 --- /dev/null +++ b/lua/config/load-colorscheme.lua @@ -0,0 +1,2 @@ +vim.cmd.colorscheme "tokyodark" +vim.o.background = "dark" diff --git a/lua/config/lspconfig.lua b/lua/config/lspconfig.lua index 81e2799..f26ecc3 100644 --- a/lua/config/lspconfig.lua +++ b/lua/config/lspconfig.lua @@ -1,3 +1,7 @@ +local cmp = require "cmp" +local cmp_lsp = require "cmp_nvim_lsp" +local cmp_kinds = require("assets.icons").icons.kinds + local function flatten_to_array(t) local res = {} local function _flatten(tbl) @@ -17,7 +21,7 @@ local capabilities = vim.tbl_deep_extend( "force", {}, vim.lsp.protocol.make_client_capabilities(), - require("blink.cmp").get_lsp_capabilities() + cmp_lsp.default_capabilities() ) require("fidget").setup {} @@ -94,6 +98,74 @@ require("mason-lspconfig").setup { }, } +cmp.setup { + preselect = "None", + formatting = { + fields = { "kind", "abbr" }, + format = function(entry, vim_item) + vim_item.kind = cmp_kinds[vim_item.kind] or "" + if entry.completion_item.detail then + vim_item.menu = entry.completion_item.detail + end + return vim_item + end, + }, + completion = { completeopt = "menu,menuone" }, + snippet = { + expand = function(args) + require("luasnip").lsp_expand(args.body) + end, + }, + + window = { + completion = cmp.config.window.bordered(), + documentation = cmp.config.window.bordered(), + }, + + view = { + entries = "custom", + }, + + mapping = { + [""] = cmp.mapping.select_prev_item(), + [""] = cmp.mapping.select_next_item(), + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete(), + [""] = cmp.mapping.close(), + [""] = cmp.mapping.confirm { + behavior = cmp.ConfirmBehavior.Insert, + select = true, + }, + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif require("luasnip").expand_or_jumpable() then + require("luasnip").expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif require("luasnip").jumpable(-1) then + require("luasnip").jump(-1) + else + fallback() + end + end, { "i", "s" }), + }, + + sources = cmp.config.sources { + { name = "path" }, + { name = "nvim_lsp" }, + { name = "luasnip" }, + { name = "buffer" }, + { name = "nvim_lua" }, + }, +} + vim.diagnostic.config { virtual_text = false, virtual_lines = false, @@ -165,6 +237,12 @@ lspconfig("texlab", { lspconfig("qmlls", { cmd = { "qmlls6" }, + filetypes = { "qml" }, + single_file_support = true, + root_dir = function(bufnr, on_dir) + local root = vim.fs.root(bufnr, ".git") + on_dir(root) + end, }) lspconfig("jsonls", { @@ -210,11 +288,6 @@ for _, server in ipairs(flat_servers) do if client.name == "typos_lsp" then return end - - require("workspace-diagnostics").populate_workspace_diagnostics( - client, - bufnr - ) end, capabilities = capabilities, }) diff --git a/lua/config/notify.lua b/lua/config/notify.lua index ec231b0..8df5d4c 100644 --- a/lua/config/notify.lua +++ b/lua/config/notify.lua @@ -1,7 +1,7 @@ -vim.notify = require('notify') -local dap = require('dap') +vim.notify = require "notify" +local dap = require "dap" -require('notify').setup({ +require("notify").setup { render = "wrapped-default", timeout = 6000, max_width = 50, @@ -17,9 +17,9 @@ require('notify').setup({ }, on_open = function(win) -- vim.api.nvim_win_set_option(win, 'wrap', true) - vim.api.nvim_win_set_option(win, 'breakat', ' ') + vim.api.nvim_win_set_option(win, "breakat", " ") end, -}) +} -- Utility functions shared between progress reports for LSP and DAP @@ -37,8 +37,8 @@ local function get_notif_data(client_id, token) return client_notifs[client_id][token] end - -local spinner_frames = { "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" } +local spinner_frames = + { "⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷" } local function update_spinner(client_id, token) local notif_data = get_notif_data(client_id, token) @@ -67,85 +67,52 @@ local function format_message(message, percentage) return (percentage and percentage .. "%\t" or "") .. (message or "") end --- vim.lsp.handlers["$/progress"] = function(_, result, ctx) --- local client_id = ctx.client_id --- --- local val = result.value --- --- if not val.kind then --- return --- end --- --- local notif_data = get_notif_data(client_id, result.token) --- --- if val.kind == "begin" then --- local message = format_message(val.message, val.percentage) --- --- notif_data.notification = vim.notify(message, "info", { --- title = format_title(val.title, vim.lsp.get_client_by_id(client_id).name), --- icon = spinner_frames[1], --- timeout = false, --- hide_from_history = false, --- }) --- --- notif_data.spinner = 1 --- update_spinner(client_id, result.token) --- elseif val.kind == "report" and notif_data then --- notif_data.notification = vim.notify(format_message(val.message, val.percentage), "info", { --- replace = notif_data.notification, --- hide_from_history = false, --- }) --- elseif val.kind == "end" and notif_data then --- notif_data.notification = --- vim.notify(val.message and format_message(val.message) or "Complete", "info", { --- icon = "", --- replace = notif_data.notification, --- timeout = 6000, --- }) --- --- notif_data.spinner = nil --- end --- end --- --- local severity = { --- "error", --- "warn", --- "info", --- "info", -- map both hint and info to info? --- } --- vim.lsp.handlers["window/showMessage"] = function(err, method, params, client_id) --- vim.notify(method.message, severity[params.type]) --- end --- --- dap.listeners.before['event_progressStart']['progress-notifications'] = function(session, body) --- local notif_data = get_notif_data("dap", body.progressId) --- --- local message = format_message(body.message, body.percentage) --- notif_data.notification = vim.notify(message, "info", { --- title = format_title(body.title, session.config.type), --- icon = spinner_frames[1], --- timeout = false, --- hide_from_history = false, --- }) --- --- notif_data.notification.spinner = 1, --- update_spinner("dap", body.progressId) --- end --- --- dap.listeners.before['event_progressUpdate']['progress-notifications'] = function(session, body) --- local notif_data = get_notif_data("dap", body.progressId) --- notif_data.notification = vim.notify(format_message(body.message, body.percentage), "info", { --- replace = notif_data.notification, --- hide_from_history = false, --- }) --- end --- --- dap.listeners.before['event_progressEnd']['progress-notifications'] = function(session, body) --- local notif_data = client_notifs["dap"][body.progressId] --- notif_data.notification = vim.notify(body.message and format_message(body.message) or "Complete", "info", { --- icon = "", --- replace = notif_data.notification, --- timeout = 6000 --- }) --- notif_data.spinner = nil --- end +-- LSP integration +-- Make sure to also have the snippet with the common helper functions in your config! + +vim.lsp.handlers["$/progress"] = function(_, result, ctx) + local client_id = ctx.client_id + + local val = result.value + + if not val.kind then + return + end + + local notif_data = get_notif_data(client_id, result.token) + + if val.kind == "begin" then + local message = format_message(val.message, val.percentage) + + notif_data.notification = vim.notify(message, "info", { + title = format_title( + val.title, + vim.lsp.get_client_by_id(client_id).name + ), + icon = spinner_frames[1], + timeout = false, + hide_from_history = false, + }) + + notif_data.spinner = 1 + update_spinner(client_id, result.token) + elseif val.kind == "report" and notif_data then + notif_data.notification = + vim.notify(format_message(val.message, val.percentage), "info", { + replace = notif_data.notification, + hide_from_history = false, + }) + elseif val.kind == "end" and notif_data then + notif_data.notification = vim.notify( + val.message and format_message(val.message) or "Complete", + "info", + { + icon = "", + replace = notif_data.notification, + timeout = 3000, + } + ) + + notif_data.spinner = nil + end +end diff --git a/lua/config/render-markdown.lua b/lua/config/render-markdown.lua new file mode 100644 index 0000000..01fe932 --- /dev/null +++ b/lua/config/render-markdown.lua @@ -0,0 +1,55 @@ +local opts = {} + +opts = { + file_types = { "markdown" }, + completions = { + lsp = { + enabled = true, + }, + }, + render_modes = true, + + anti_conceal = { + enabled = true, + above = 1, + below = 1, + }, + + code = { + enabled = true, + render_modes = false, + sign = true, + conceal_delimiters = true, + language = true, + position = "left", + language_icon = true, + language_name = true, + language_info = true, + language_pad = 0, + width = "full", + left_margin = 0, + left_pad = 2, + right_pad = 0, + min_width = 40, + border = "thick", + language_border = "█", + language_left = "", + language_right = "", + above = "▄", + below = "▀", + inline = true, + inline_left = "", + inline_right = "", + inline_pad = 0, + priority = 140, + highlight = "RenderMarkdownCode", + highlight_info = "RenderMarkdownCodeInfo", + highlight_language = nil, + highlight_border = "RenderMarkdownCodeBorder", + highlight_fallback = "RenderMarkdownCodeFallback", + highlight_inline = "RenderMarkdownCodeInline", + style = "full", + }, +} + +return opts diff --git a/lua/config/smart-splits.lua b/lua/config/smart-splits.lua new file mode 100644 index 0000000..669145b --- /dev/null +++ b/lua/config/smart-splits.lua @@ -0,0 +1,22 @@ +require("smart-splits").setup { + ignored_buftypes = { + "nofile", + "quickfix", + "prompt", + }, + ignored_filetypes = { "NvimTree" }, + default_amount = 3, + at_edge = "wrap", + float_win_behavior = "previous", + move_cursor_same_row = false, + cursor_follows_swapped_bufs = false, + ignored_events = { + "BufEnter", + "WinEnter", + }, + multiplexer_integration = nil, + disable_multiplexer_nav_when_zoomed = true, + kitty_password = nil, + zellij_move_focus_or_tab = false, + log_level = "error", +} diff --git a/lua/config/snacks.lua b/lua/config/snacks.lua index 93f619c..5be8aa7 100644 --- a/lua/config/snacks.lua +++ b/lua/config/snacks.lua @@ -57,7 +57,33 @@ return { max_height = 80, }, }, - notifier = { enabled = true }, + notifier = { + timeout = 3000, + width = { min = 40, max = 0.4 }, + height = { min = 1, max = 0.6 }, + margin = { top = 0, right = 1, bottom = 0 }, + padding = true, + gap = 0, + sort = { "level", "added" }, + level = vim.log.levels.TRACE, + icons = { + error = " ", + warn = " ", + info = " ", + debug = " ", + trace = " ", + }, + keep = function(notif) + return vim.fn.getcmdpos() > 0 + end, + ---@type snacks.notifier.style + style = "fancy", + top_down = true, + date_format = "%R", + ---@type string|boolean + more_format = " ↓ %d lines ", + refresh = 50, + }, quickfile = { enabled = true }, scope = { enabled = true }, scroll = { enabled = false }, @@ -75,10 +101,35 @@ return { function() Snacks.terminal.toggle() end, - mode = "t" - } - } - } + mode = "t", + }, + }, + }, + + notification = { + border = true, + zindex = 100, + ft = "markdown", + wo = { + winblend = 0, + wrap = true, + conceallevel = 2, + colorcolumn = "", + }, + bo = { filetype = "snacks_notif" }, + }, + + notification_history = { + border = true, + zindex = 100, + minimal = false, + title = " Notifications ", + title_pos = "center", + ft = "markdown", + bo = { filetype = "snacks_notif_history", modifiable = false }, + wo = { winhighlight = "Normal:SnacksNotifierHistory" }, + keys = { q = "close" }, + }, }, gitbrowse = { enabled = true, diff --git a/lua/functions/smart_jump.lua b/lua/functions/smart_jump.lua new file mode 100644 index 0000000..07ea1cb --- /dev/null +++ b/lua/functions/smart_jump.lua @@ -0,0 +1,202 @@ +local M = {} + +local function get_qmlls_client(bufnr) + for _, c in ipairs(vim.lsp.get_clients { bufnr = bufnr }) do + if c.name == "qmlls" then + return c + end + end +end + +local function first_client_encoding(bufnr) + local c = vim.lsp.get_clients({ bufnr = bufnr })[1] + return (c and c.offset_encoding) or "utf-16" +end + +local function normalize_locations(result) + if not result then + return {} + end + -- Single Location / LocationLink + if result.uri or result.targetUri then + return { result } + end + -- Array of Location / LocationLink + if vim.islist(result) then + return result + end + return {} +end + +local function request_locations(bufnr, method, timeout_ms) + local enc = first_client_encoding(bufnr) + local params = vim.lsp.util.make_position_params(0, enc) + local responses = + vim.lsp.buf_request_sync(bufnr, method, params, timeout_ms or 1000) + + if not responses then + return {} + end + + local all = {} + for client_id, resp in pairs(responses) do + local result = resp and resp.result + local locs = normalize_locations(result) + for _, loc in ipairs(locs) do + table.insert(all, { client_id = client_id, loc = loc }) + end + end + return all +end + +local function jump_to_first_location(bufnr, matches) + if #matches == 0 then + return false + end + + local m = matches[1] + local client = vim.lsp.get_client_by_id(m.client_id) + local enc = (client and client.offset_encoding) + or first_client_encoding(bufnr) + + local loc = m.loc + -- Convert LocationLink -> Location-ish target for jump_to_location + if loc.targetUri then + loc = { + uri = loc.targetUri, + range = loc.targetSelectionRange or loc.targetRange, + } + end + + vim.lsp.util.jump_to_location(loc, enc) + return true +end + +local function get_cursor_context(bufnr) + local pos = vim.api.nvim_win_get_cursor(0) + local row = pos[1] - 1 + local col = pos[2] + local line = vim.api.nvim_buf_get_lines(bufnr, row, row + 1, false)[1] or "" + local cword = vim.fn.expand "" + return row, col, line, cword +end + +-- Tries to detect something like Config.save() and returns ("Config", "save") +local function detect_singleton_member_call(bufnr) + local _, col, line, cword = get_cursor_context(bufnr) + local before = line:sub(1, col + 1) + + -- If cursor is on "save" in Config.save(), this usually works. + local singleton = before:match "([%a_][%w_]*)%s*%.[%w_]*$" + if not singleton then + -- If cursor is on "Config", this can also catch it. + singleton = before:match "([%a_][%w_]*)%s*$" + if singleton and not line:sub(col + 1):match "^%s*%." then + singleton = nil + end + end + + if not singleton then + return nil + end + + -- Basic heuristic: singleton names are often capitalized in QML + -- (you can remove this check if your singletons are lowercase) + if not singleton:match "^[A-Z_]" then + return nil + end + + return singleton, cword +end + +local function find_project_root(bufnr) + local qmlls = get_qmlls_client(bufnr) + if qmlls and qmlls.config and qmlls.config.root_dir then + return qmlls.config.root_dir + end + return vim.fs.root(bufnr, { ".qmlls.ini", ".git" }) or vim.fn.getcwd() +end + +local function singleton_fallback_jump(bufnr) + local singleton, member = detect_singleton_member_call(bufnr) + if not singleton then + return false + end + + local root = find_project_root(bufnr) + local target_name = singleton .. ".qml" + + local matches = vim.fs.find(target_name, { + path = root, + type = "file", + limit = math.huge, + }) + + if not matches or vim.tbl_isempty(matches) then + return false + end + + local pick = nil + if #matches == 1 then + pick = matches[1] + else + -- Prefer a path containing "/Config/" for Config.qml, etc. + for _, p in ipairs(matches) do + if + p:match("/" .. singleton .. "/") + or p:match("/" .. target_name .. "$") + then + pick = p + break + end + end + pick = pick or matches[1] + end + + vim.cmd.edit(vim.fn.fnameescape(pick)) + + -- Optional: try to jump near the member inside the singleton file. + if member and member ~= singleton then + local escaped = vim.fn.escape(member, [[\/]]) + -- Try function save(...) first, then any "save" occurrence. + local found = + vim.fn.search("\\", "W") + end + end + + vim.notify( + ("LSP returned no locations; fell back to %s.qml"):format(singleton), + vim.log.levels.INFO + ) + return true +end + +function M.smart_qml_jump() + local bufnr = vim.api.nvim_get_current_buf() + + -- 1) Try normal definition + local defs = request_locations(bufnr, "textDocument/definition", 1200) + if jump_to_first_location(bufnr, defs) then + return + end + + -- 2) Try type definition + local tdefs = request_locations(bufnr, "textDocument/typeDefinition", 1200) + if jump_to_first_location(bufnr, tdefs) then + return + end + + -- 3) Singleton fallback: Config.save() -> Config.qml + if singleton_fallback_jump(bufnr) then + return + end + + vim.notify( + "No locations found (definition/typeDefinition/fallback)", + vim.log.levels.WARN + ) +end + +return M diff --git a/lua/mappings.lua b/lua/mappings.lua index 9c54358..bcb2928 100644 --- a/lua/mappings.lua +++ b/lua/mappings.lua @@ -14,6 +14,16 @@ map("n", "", vim.cmd.CopilotChatToggle) map("i", "", vim.cmd.CopilotChatToggle) map("v", "", vim.cmd.CopilotChatToggle) +-- Splits +vim.keymap.set("n", "", require("smart-splits").resize_left) +vim.keymap.set("n", "", require("smart-splits").resize_down) +vim.keymap.set("n", "", require("smart-splits").resize_up) +vim.keymap.set("n", "", require("smart-splits").resize_right) +vim.keymap.set("n", "", require("smart-splits").move_cursor_left) +vim.keymap.set("n", "", require("smart-splits").move_cursor_down) +vim.keymap.set("n", "", require("smart-splits").move_cursor_up) +vim.keymap.set("n", "", require("smart-splits").move_cursor_right) + -- Explorer and Undotree map("n", "e", function() Snacks.explorer() @@ -39,6 +49,14 @@ map( { desc = "Telescope find files" } ) +-- LSP +map( + "n", + "gd", + vim.lsp.buf.definition, + { noremap = true, silent = true, buffer = bufnr } +) + map("n", "", ":bdelete") -- map("n", "", ":BufferRestore") diff --git a/lua/plugins/colorschemes.lua b/lua/plugins/colorschemes.lua index cb7606c..a4dcc14 100644 --- a/lua/plugins/colorschemes.lua +++ b/lua/plugins/colorschemes.lua @@ -18,9 +18,10 @@ return { { "catppuccin/nvim", name = "catppuccin", - -- config = function() - -- require("config.catppuccin") - -- end, + priority = 1000, + config = function() + require "config.catppuccin" + end, }, { "rebelot/kanagawa.nvim", diff --git a/lua/plugins/init.lua b/lua/plugins/init.lua index f6a4c90..ecda714 100644 --- a/lua/plugins/init.lua +++ b/lua/plugins/init.lua @@ -80,12 +80,6 @@ return { { "tpope/vim-fugitive", }, - { - "rcarriga/nvim-notify", - config = function() - require "config.notify" - end, - }, { "zbirenbaum/copilot.lua", lazy = true, @@ -128,11 +122,15 @@ return { dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", + "hrsh7th/cmp-nvim-lsp", + "hrsh7th/cmp-buffer", + "hrsh7th/cmp-path", + "hrsh7th/cmp-cmdline", + "hrsh7th/nvim-cmp", "L3MON4D3/LuaSnip", - -- "saadparwaiz1/cmp_luasnip", + "saadparwaiz1/cmp_luasnip", "j-hui/fidget.nvim", "b0o/schemastore.nvim", - "saghen/blink.cmp", }, config = function() require "config.lspconfig" @@ -211,28 +209,28 @@ return { vim.g.minimal_italic_functions = true end, }, - { - "propet/colorscheme-persist.nvim", - dependencies = { - "nvim-telescope/telescope.nvim", - }, - lazy = false, - config = true, - keys = { - { - "sp", - function() - require("colorscheme-persist").picker() - end, - mode = "n", - }, - }, - opts = { - picker_opts = require("telescope.themes").get_dropdown { - enable_preview = true, - }, - }, - }, + -- { + -- "propet/colorscheme-persist.nvim", + -- dependencies = { + -- "nvim-telescope/telescope.nvim", + -- }, + -- lazy = false, + -- config = true, + -- keys = { + -- { + -- "sp", + -- function() + -- require("colorscheme-persist").picker() + -- end, + -- mode = "n", + -- }, + -- }, + -- opts = { + -- picker_opts = require("telescope.themes").get_dropdown { + -- enable_preview = true, + -- }, + -- }, + -- }, { "aznhe21/actions-preview.nvim", config = function() @@ -254,9 +252,9 @@ return { { "MunifTanjim/nui.nvim", }, - { - "artemave/workspace-diagnostics.nvim", - }, + -- { + -- "artemave/workspace-diagnostics.nvim", + -- }, { require "config.dev-tools", }, @@ -272,9 +270,6 @@ return { { require "config.dropbar", }, - { - require "config.blink", - }, { "lewis6991/gitsigns.nvim", config = function() @@ -299,8 +294,43 @@ return { }, { "Zacharias-Brohn/zterm-navigator.nvim", + enabled = false, config = function() require "config.zterm-navigator" end, }, + { + "MeanderingProgrammer/render-markdown.nvim", + dependencies = { + "nvim-treesitter/nvim-treesitter", + "nvim-mini/mini.nvim", + }, + ---@module 'render-markdown' + ---@type render.md.UserConfig + opts = require "config.render-markdown", + }, + { + "mrjones2014/smart-splits.nvim", + config = function() + require "config.smart-splits" + end, + }, + { + "kkrampis/codex.nvim", + lazy = true, + cmd = { "Codex", "CodexToggle" }, + keys = { + -- { + -- nil, + -- function() + -- require("codex").toggle() + -- end, + -- desc = "Toggle Codex", + -- mode = { "n", "t" }, + -- }, + }, + opts = function() + require "config.codex" + end, + }, } diff --git a/lua/plugins/neoformat.vim b/lua/plugins/neoformat.vim index 1709702..9b24ae5 100644 --- a/lua/plugins/neoformat.vim +++ b/lua/plugins/neoformat.vim @@ -8,3 +8,4 @@ let g:neoformat_enabled_lua = ['stylua'] let g:neoformat_enabled_rust = ['rustfmt'] let g:neoformat_enabled_cpp = ['uncrustify'] let g:neoformat_enabled_markdown = ['prettier'] +let g:neoformat_enabled_qml = ['qmlformat']