-- Map leader "Global Key" to space, so you can use it optionally like spacemacs etc. vim.g.mapleader = " " vim.g.maplocalleader = " " -- Beautifully cursed, let's rebind C-x to the leader key and other respective emacs keybindings vim.cmd("map ") vim.cmd("map ") vim.cmd("imap ") vim.cmd("lmap ") vim.cmd("map ") -- Ignore case sensitivity in general (Primarily for tab autocomplete) vim.o.ignorecase = true -- Tab Size local set = vim.opt -- set options set.tabstop = 8 set.softtabstop = 8 set.shiftwidth = 8 -- Disable tab settings being overriden vim.cmd("filetype plugin indent off") -- Terminal Config -- Exit terminal mode with Esc -- vim.cmd("tnoremap ") vim.cmd("tnoremap ") -- Autostart in Insert mode -- vim.cmd("startinsert") -- Autostart terminal buffers in insert/terminal mode vim.api.nvim_create_autocmd({ 'VimEnter' }, { pattern = 'bash', command = 'startinsert', }) -- Enable line numbers -- vim.cmd("set number") -- Relative and Absolute line numbers. Hybrid. vim.opt.nu = false vim.opt.relativenumber = false -- vim.o.statuscolumn = "%s %l %r " -- Both displayed on their own seperate lines -- Highlight current cursor line vim.opt.cursorline = true -- Link system and neovim clipboard vim.api.nvim_set_option("clipboard", "unnamedplus") -- Lazy plugin manager require("config.lazy") -- Turn off highlighting after search completion vim.cmd("set nohlsearch") -- Colourscheme vim.o.background = "light" -- or "dark" for dark mode vim.cmd("colorscheme gruvbox") -- Telescope setup (Like emacs ivy-mode) NOW USING SNACKS PICKERS INSTEAD --require('telescope').setup{ -- defaults = { -- -- ... -- }, -- pickers = { -- find_files = { -- theme = "ivy", -- } -- }, -- extensions = { -- -- ... -- } --} -- To get telescope-file-browser loaded and working with telescope, -- you need to call load_extension, somewhere after setup function: --require("telescope").load_extension "file_browser" -- Custom statusline (modeline) require('mini.statusline').setup() -- Custom filetypes configuration vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { pattern = '*ansible*.yml', command = 'set filetype=yaml.ansible', }) vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { pattern = '*playbook*.yml', command = 'set filetype=yaml.ansible', }) vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { pattern = '*server*.yml', command = 'set filetype=yaml.ansible', }) vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { pattern = '*service*.yml', command = 'set filetype=yaml.ansible', }) vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { pattern = '*compose*.yml', command = 'set filetype=yaml.docker-compose', }) -- Vim Suda Setup (Read/Write files with sudo) vim.g.suda_smart_edit = 1 -- Spooky thing that will attempt to add any relative to current working directory node_modules binaries to PATH on startup node_modules_path_original = vim.env.PATH -- Refresh PATH on new file vim.api.nvim_create_autocmd({ 'BufEnter', 'BufNewFile' }, { callback = function () vim.env.PATH = node_modules_path_original currentPWD = vim.loop.cwd() node_modules_path = vim.fn.system("printf `if [[ -d " .. currentPWD .. "/node_modules ]]; then find ./node_modules -maxdepth 2 -regex '.*/bin' | xargs realpath | paste -s -d ':'; else realpath " .. currentPWD.. "/node_modules; fi`") vim.env.PATH = vim.env.PATH .. ":" .. node_modules_path end, }) -- Spooky thing that will change the vim working directory based on what we cd into via the :terminal to keep them in sync, uses an external wrapper script called cd-nvim which overrides the cd command to send the signal alongside with the new working directory to Neovim -- See :help terminal-events, we can use this to implement cd commands etc changing the working directory of the nvim host and other terminal app to nvim host ipc. -- sleep 5 && printf "\033]7;file://${PWD}/foo/bar\033\\" -- \033]7 AKA \x1b]7 enclosed by another \033\\ is used as a custom signal to denote which is a valid request to handle I'm asssuming, but technically everything in the terminal is being listened to vim.api.nvim_create_autocmd({ 'TermRequest' }, { desc = 'Handles OSC 7 dir change requests', callback = function(ev) if string.sub(vim.v.termrequest, 1, 4) == '\x1b]7;' then local dir = string.gsub(vim.v.termrequest, '\x1b]7;file://[^/.]*', '') vim.cmd("cd " .. dir) end end }) -- CME Configuration -- Basically compile mode from emacs for neovim vim.g.cme = { -- Preferred user shell. Accepts any executor that supports -c. shell = vim.o.shell, -- Expand '%' to the current file name. shell_expand = true, } -- Firenvim Configuration vim.g.firenvim_config = { globalSettings = { alt = "all" }, localSettings = { [".*"] = { cmdline = "firenvim", content = "text", priority = 0, selector = "textarea", takeover = "always" } } } vim.api.nvim_create_autocmd({'BufEnter'}, { pattern = "github.com_*.txt", command = "set filetype=markdown" }) -- Only settings that will be configured if started by firenvim if vim.g.started_by_firenvim == true then -- Press escape 2 times to leave editor vim.keymap.set("n", "", "call firenvim#focus_page()", {}) -- Press shift enter to save and attempt to submit form on page local function saveAndEnter() vim.fn['firenvim#eval_js']('setTimeout (function() { document.activeElement.closest("form").submit(); }, 100)', 'MyFunction') --vim.fn['firenvim#eval_js']('setTimeout (function() { const event = new KeyboardEvent("keydown", { key: "Enter", code: "Enter", which: 13, keyCode: 13,}); document.activeElement.dispatchEvent(event); }, 100)', 'MyFunction') vim.cmd("wq") end vim.keymap.set("n", "", saveAndEnter, {}) vim.keymap.set("i", "", saveAndEnter, {}) -- Automatically save changes back to textbox every 1 second on detected write vim.api.nvim_create_autocmd({'TextChanged', 'TextChangedI'}, { callback = function(e) if vim.g.timer_started == true then return end vim.g.timer_started = true vim.fn.timer_start(1000, function() vim.g.timer_started = false vim.cmd('silent write') end) end }) vim.api.nvim_create_autocmd({'VimEnter'}, { pattern = "*", command = "startinsert" }) --vim.api.nvim_create_autocmd({'VimLeave'}, { -- pattern = "*", -- callback = function () -- vim.fn['firenvim#focus_page']() -- end --}) end -- Neogen Configuration require('neogen').setup { enabled = true, --if you want to disable Neogen input_after_comment = true, -- (default: true) automatic jump (with insert mode) on inserted annotation -- jump_map = "" -- (DROPPED SUPPORT, see [here](#cycle-between-annotations) !) The keymap in order to jump in the annotation fields (in insert mode) } -- Built in tabcomplete/autocomplete setup function check_for_whitespace() -- Get the current cursor position (row and column) local row, col = unpack(vim.api.nvim_win_get_cursor(0)) -- Get the text of the current line local linetext = vim.api.nvim_get_current_line() -- Neovim's column is 0-based, Lua's string indices are 1-based. -- Return true if we are at the start of the line (col == 0) -- OR if the character before the cursor is a whitespace character (%s) -- linetext:sub(col, col) gets the character at the column index return col == 0 or string.match(linetext:sub(col, col), '%s') ~= nil end -- Tabcomplete: vim.api.nvim_set_keymap('i', '', "pumvisible() ? '' : v:lua.check_for_whitespace() ? '' : ''", { expr = true, silent = true }) -- Autocomplete: -- vim.api.nvim_create_autocmd("InsertCharPre", { -- callback = function(ev) -- if vim.bo[ev.buf].buftype == '' and not (vim.fn.pumvisible() == 1) then -- vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), 'i', false) -- end -- end, -- }) -- LSP Configuration local lsp_zero = require('lsp-zero') -- https://lsp-zero.netlify.app/v3.x/language-server-configuration.html#default-keybindings -- https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md lsp_zero.on_attach(function(client, bufnr) lsp_zero.default_keymaps({ buffer = bufnr, preserve_mappings = false }) vim.lsp.completion.enable(true, client.id, bufnr, { autotrigger = false, convert = function(item) return { abbr = item.label:gsub('%b()', '') } end, }) vim.lsp.inlay_hint.enable(true, { 0 }) -- Override default omnifunc completer with LSP completion for LSP buffer -- Tabcomplete: -- vim.api.nvim_set_keymap('i', '', "pumvisible() ? '' : v:lua.check_for_whitespace() ? '' : 'lua vim.lsp.completion.get()'", { expr = true, silent = true }) end) --local lspconfig = require("lspconfig") vim.lsp.enable("lua_ls") vim.lsp.enable("bashls") --vim.lsp.config("ccls", { -- init_options = { -- compilationDatabaseDirectory = "build"; -- index = { -- threads = 0; -- }; -- clang = { -- excludeArgs = { "-frounding-math"} ; -- }; -- } --}) --vim.lsp.enable('ccls') vim.lsp.enable("clangd") vim.lsp.config("clangd", { init_options = { fallbackFlags = { "--compile-commands-dir=.\\compile_commands.json" }, }, }) vim.lsp.enable("gopls") vim.lsp.enable("jdtls") vim.lsp.enable("eslint") vim.lsp.enable("ts_ls", { settings = { implicitProjectConfiguration = { checkJs = true }, } }) vim.lsp.enable("jedi_language_server") vim.lsp.enable("nixd") vim.lsp.enable("nginx_language_server") vim.lsp.enable("docker_compose_language_service") vim.lsp.enable("ansiblels") vim.lsp.enable("sourcekit", { filetypes = { "swift" }, }) vim.lsp.enable("rust_analyzer", { settings = { ['rust-analyzer'] = { diagnostics = { enable = false; } } }, filetypes = { "rust" } }) -- vim.lsp.enable("yamlls", { -- settings = { -- yaml = { -- schemas = { -- ["https://json.schemastore.org/github-workflow.json"] = "/.github/workflows/*", -- ["https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/ansible.json#/$defs/playbook"] = "**/*playbook*.yaml", -- ["https://raw.githubusercontent.com/ansible/ansible-lint/main/src/ansiblelint/schemas/inventory.json"] = "**/*hosts*.yaml", -- }, -- }, -- } -- }) vim.lsp.enable("emmet_language_server" ,{ filetypes = { "css", "eruby", "html", "javascript", "javascriptreact", "less", "sass", "scss", "pug", "typescriptreact" }, -- Read more about this options in the [vscode docs](https://code.visualstudio.com/docs/editor/emmet#_emmet-configuration). -- **Note:** only the options listed in the table are supported. init_options = { ---@type table includeLanguages = {}, --- @type string[] excludeLanguages = {}, --- @type string[] extensionsPath = {}, --- @type table [Emmet Docs](https://docs.emmet.io/customization/preferences/) preferences = {}, --- @type boolean Defaults to `true` showAbbreviationSuggestions = true, --- @type "always" | "never" Defaults to `"always"` showExpandedAbbreviation = "always", --- @type boolean Defaults to `false` showSuggestionsAsSnippets = false, --- @type table [Emmet Docs](https://docs.emmet.io/customization/syntax-profiles/) syntaxProfiles = {}, --- @type table [Emmet Docs](https://docs.emmet.io/customization/snippets/#variables) variables = {}, }, }) --Enable (broadcasting) snippet capability for completion local capabilities = vim.lsp.protocol.make_client_capabilities() capabilities.textDocument.completion.completionItem.snippetSupport = true vim.lsp.enable("cssls", { capabilities = capabilities, }) vim.lsp.enable("jsonls", { capabilities = capabilities, }) -- LSP Inline diagnostics require('tiny-inline-diagnostic').setup() vim.diagnostic.config({ virtual_text = false }) -- Only if needed in your configuration, if you already have native LSP diagnostics -- Neovide Configuration if vim.g.neovide then vim.keymap.set('n', '', ':w') -- Save vim.keymap.set('v', '', '"+y') -- Copy vim.keymap.set('n', '', '"+P') -- Paste normal mode vim.keymap.set('v', '', '"+P') -- Paste visual mode vim.keymap.set('c', '', '+') -- Paste command mode vim.keymap.set('i', '', 'l"+Pli') -- Paste insert mode vim.keymap.set({ "n", "v" }, "", ":lua vim.g.neovide_scale_factor = vim.g.neovide_scale_factor + 0.1") vim.keymap.set({ "n", "v" }, "", ":lua vim.g.neovide_scale_factor = vim.g.neovide_scale_factor - 0.1") vim.keymap.set({ "n" , "v" }, "", ":lua vim.g.neovide_scale_factor = 1") end -- Allow clipboard copy paste in neovim vim.api.nvim_set_keymap('', '', '+p', { noremap = true, silent = true}) vim.api.nvim_set_keymap('!', '', '+', { noremap = true, silent = true}) vim.api.nvim_set_keymap('t', '', '+', { noremap = true, silent = true}) vim.api.nvim_set_keymap('v', '', '+', { noremap = true, silent = true}) -- DAP Configuration local dap = require("dap") local dapwidgets = require("dap.ui.widgets") local dapwidgetscope = dapwidgets.sidebar(dapwidgets.scopes) local daprunning = false -- Functions to initialise Dap, open UI widgets and set helper keybindings local function dapInit() dap.repl.open() dapwidgetscope.open() daprunning = true -- Can't get these working! -- vim.keymap.del({"n", "v", "o"}, "") -- vim.keymap.set({"n", "v", "o"}, "", "ln") -- vim.keymap.set({"n", "v", "o"}, "", "ln") -- vim.keymap.set({"n", "v", "o"}, "", "ln") -- vim.keymap.set({"n", "v", "o"}, "", "ln") vim.cmd("unmap ") vim.cmd("map dn") vim.cmd("map di") vim.cmd("map dr") vim.cmd("map ds") print("Initialised debugger successfully") end local function dapCleanup() dap.repl.close() dapwidgetscope.close() daprunning = false vim.cmd("unmap ") vim.cmd("unmap ") vim.cmd("unmap ") vim.cmd("unmap ") vim.keymap.set({"n", "v", "o"}, '', 'Telescope live_grep') -- vim.keymap.del({"n", "v", "o"}, "") -- vim.keymap.del({"n", "v", "o"}, "") -- vim.keymap.del({"n", "v", "o"}, "") -- vim.keymap.del({"n", "v", "o"}, "") -- vim.keymap.set({"n", "v", "o"}, '', 'Telescope live_grep') print("Cleaned up debugger successfully") end -- DAP Server configurations -- C DAP Configuration dap.adapters.gdb = { type = "executable", command = "gdb", args = { "-i", "dap" } } dap.configurations.c = { { name = "Launch", type = "gdb", request = "launch", program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, cwd = "${workspaceFolder}", stopAtBeginningOfMainSubprogram = false, }, } -- Discord Rich Presence -- The setup config table shows all available config options with their default values: -- require("neocord").setup({ -- -- General options -- logo = "auto", -- "auto" or url -- logo_tooltip = nil, -- nil or string -- main_image = "language", -- "language" or "logo" -- client_id = "1157438221865717891", -- Use your own Discord application client id (not recommended) -- log_level = "debug", -- Log messages at or above this level (one of the following: "debug", "info", "warn", "error") -- debounce_timeout = 10, -- Number of seconds to debounce events (or calls to `:lua package.loaded.presence:update(, true)`) -- blacklist = {}, -- A list of strings or Lua patterns that disable Rich Presence if the current file name, path, or workspace matches -- file_assets = {}, -- Custom file asset definitions keyed by file names and extensions (see default config at `lua/presence/file_assets.lua` for reference) -- show_time = true, -- Show the timer -- global_timer = false, -- if set true, timer won't update when any event are triggered -- -- -- Rich Presence text options -- editing_text = "Editing %s", -- Format string rendered when an editable file is loaded in the buffer (either string or function(filename: string): string) -- file_explorer_text = "Browsing %s", -- Format string rendered when browsing a file explorer (either string or function(file_explorer_name: string): string) -- git_commit_text = "Committing changes", -- Format string rendered when committing changes in git (either string or function(filename: string): string) -- plugin_manager_text = "Managing plugins", -- Format string rendered when managing plugins (either string or function(plugin_manager_name: string): string) -- reading_text = "Reading %s", -- Format string rendered when a read-only or unmodifiable file is loaded in the buffer (either string or function(filename: string): string) -- workspace_text = "Working on %s", -- Format string rendered when in a git repository (either string or function(project_name: string|nil, filename: string): string) -- line_number_text = "Line %s out of %s", -- Format string rendered when `enable_line_number` is set to true (either string or function(line_number: number, line_count: number): string) -- terminal_text = "Using Terminal", -- Format string rendered when in terminal mode. -- }) -- Remake of emacs which-key mode, I'll add all custom keybinds here -- TODO: The DAP related functions should be moved into their own respective file/modularised better! require("which-key").add ({ --{ "", "Telescope live_grep" , desc = "Swiper", mode = "n" }, { "/", function () Snacks.picker.lines() end , desc = "Swiper", mode = "n" }, { "", function () Snacks.picker.grep() end , desc = "Swiper", mode = "n" }, -- This does not allow you to make a new file only search existing, but good enough for now. --{ "", "Telescope find_files find_command=rg,--ignore,--hidden,--files" , desc = "Find File", mode = "n" }, { "", function() Snacks.picker.explorer({ layout = "ivy", focus = "input", auto_close = true, win = { input = { keys = { [""] = { "focus_list", mode = { "i", "n" } }, [""] = { "focus_list", mode = { "i", "n" } }, } }, }, }) end, desc = "Find File", mode = "n" }, { "f", function () Snacks.picker.files() end , desc = "Find File", mode = "n" }, --{ "f", "Telescope file_browser" , desc = "File Browser", mode = "n" }, -- To properly remake the Emacs ivy buffer command, we should figure out how to combine Telescope buffers and Telescope oldfiles -- This is good enough for now -- { "b", "Telescope oldfiles" , desc = "Recently opened files", mode = "n" }, -- { "", "Telescope buffers" , desc = "Buffers", mode = "n" }, -- SNACKS NOW GOOD ENOUGH TO PROVIDE EXACT BEHAVIOUR { "b", function () Snacks.picker.smart({multi = { "buffers", "recent" }, matcher = { cwd_bonus = false, frecency = true, sort_empty = true }}) end , desc = "Recently opened files", mode = "n" }, { "k", "bp|bd#" , desc = "Close current buffer", mode = "n" }, { "0", "close", desc = "Close focused window" }, { "2", "split", desc = "Split window horizontally" }, { "3", "vs", desc = "Split window vertically" }, { "o", "wincmd w", desc = "Switch to next window" }, { -- Nested mappings are allowed and can be added in any order -- Most attributes can be inherited or overridden on any level -- There's no limit to the depth of nesting mode = { "n", "v" }, -- NORMAL and VISUAL mode { "", "qa", desc = "Quit" }, { "", "w", desc = "Write" }, }, -- LSP/DAP Options { mode = { "n", "v" }, -- NORMAL and VISUAL mode { "db", function() dap.toggle_breakpoint() end, desc = "Debugger: Toggle breakpoint" }, { "dd", function() if daprunning == false then if pcall(function() dap.continue() end) then dapInit() else print("Debugger failed to start!") daprunning = false end else print("Debugger already running!") end end, desc = "Debugger: Run" }, { "dD", function() if daprunning == false then if pcall(function() dap.run_last() end) then dapInit() print("Started debugger from previous configuration successfully") else print("Debugger failed to start!") daprunning = false end else print("Debugger already running!") end end, desc = "Debugger: Run last configuration again" }, { "ds", function() if daprunning == true then if pcall(function() dap.terminate() end) then dapCleanup() print("Stopped debugger successfully") else daprunning = true end else print("Debugger is not running!") end end, desc = "Debugger: Stop" }, { "dr", function() if daprunning == true then if not pcall(function() dap.continue() end) then dapCleanup() end else print("Debugger is not running! Please run debugger first") end end, desc = "Debugger: Resume execution" }, { "dn", function() if daprunning == true then if not pcall(function() dap.step_over() end) then dapCleanup() end else print("Debugger is not running! Please run debugger first") end end, desc = "Debugger: Step over" }, { "di", function() if daprunning == true then if not pcall(function() dap.step_into() end) then dapCleanup() print("Debugger failed to continue!") end else print("Debugger is not running! Please run debugger first") end end, desc = "Debugger: Step into" }, } }) require('gitsigns').setup { signs = { add = { text = '┃' }, change = { text = '┃' }, delete = { text = '_' }, topdelete = { text = '‾' }, changedelete = { text = '~' }, untracked = { text = '┆' }, }, signs_staged = { add = { text = '┃' }, change = { text = '┃' }, delete = { text = '_' }, topdelete = { text = '‾' }, changedelete = { text = '~' }, untracked = { text = '┆' }, }, signs_staged_enable = true, signcolumn = true, -- Toggle with `:Gitsigns toggle_signs` numhl = false, -- Toggle with `:Gitsigns toggle_numhl` linehl = false, -- Toggle with `:Gitsigns toggle_linehl` word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff` watch_gitdir = { follow_files = true }, auto_attach = true, attach_to_untracked = false, current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame` current_line_blame_opts = { virt_text = true, virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align' delay = 1000, ignore_whitespace = false, virt_text_priority = 100, use_focus = true, }, current_line_blame_formatter = ', - ', sign_priority = 6, update_debounce = 100, status_formatter = nil, -- Use default max_file_length = 40000, -- Disable if file is longer than this (in lines) preview_config = { -- Options passed to nvim_open_win style = 'minimal', relative = 'cursor', row = 0, col = 1 }, }