Neovim-Configuration/init.lua

607 lines
22 KiB
Lua

-- 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 <C-x> <Space>")
vim.cmd("map <C-g> <Esc>")
vim.cmd("imap <C-g> <Esc>")
vim.cmd("lmap <C-g> <Esc>")
vim.cmd("map <C-c> <Nop>")
-- Tab Size
local set = vim.opt -- set options
set.tabstop = 8
set.softtabstop = 8
set.shiftwidth = 8
-- Terminal Config
-- Exit terminal mode with Esc
-- vim.cmd("tnoremap <Esc> <C-\\><C-n>")
vim.cmd("tnoremap <C-g> <C-\\><C-n>")
-- 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", "<Esc><Esc>", "<Cmd>call firenvim#focus_page()<CR>", {})
-- 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", "<S-Enter>", saveAndEnter, {})
vim.keymap.set("i", "<S-Enter>", 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 = "<C-e>" -- (DROPPED SUPPORT, see [here](#cycle-between-annotations) !) The keymap in order to jump in the annotation fields (in insert mode)
}
-- 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 })
vim.cmd("set tabstop=8")
vim.cmd("set shiftwidth=8")
vim.cmd("set softtabstop=8")
end)
-- LSP Autocompletion settings
vim.keymap.set('i', '<c-space>', function()
vim.lsp.completion.get()
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<string, string>
includeLanguages = {},
--- @type string[]
excludeLanguages = {},
--- @type string[]
extensionsPath = {},
--- @type table<string, any> [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<string, any> [Emmet Docs](https://docs.emmet.io/customization/syntax-profiles/)
syntaxProfiles = {},
--- @type table<string, string> [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', '<D-s>', ':w<CR>') -- Save
vim.keymap.set('v', '<D-c>', '"+y') -- Copy
vim.keymap.set('n', '<D-v>', '"+P') -- Paste normal mode
vim.keymap.set('v', '<D-v>', '"+P') -- Paste visual mode
vim.keymap.set('c', '<D-v>', '<C-R>+') -- Paste command mode
vim.keymap.set('i', '<D-v>', '<ESC>l"+Pli') -- Paste insert mode
vim.keymap.set({ "n", "v" }, "<C-=>", ":lua vim.g.neovide_scale_factor = vim.g.neovide_scale_factor + 0.1<CR>")
vim.keymap.set({ "n", "v" }, "<C-->", ":lua vim.g.neovide_scale_factor = vim.g.neovide_scale_factor - 0.1<CR>")
vim.keymap.set({ "n" , "v" }, "<C-0>", ":lua vim.g.neovide_scale_factor = 1<CR>")
end
-- Allow clipboard copy paste in neovim
vim.api.nvim_set_keymap('', '<D-v>', '+p<CR>', { noremap = true, silent = true})
vim.api.nvim_set_keymap('!', '<D-v>', '<C-R>+', { noremap = true, silent = true})
vim.api.nvim_set_keymap('t', '<D-v>', '<C-R>+', { noremap = true, silent = true})
vim.api.nvim_set_keymap('v', '<D-v>', '<C-R>+', { 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"}, "<C-s>")
-- vim.keymap.set({"n", "v", "o"}, "<C-n>", "<C-c>ln")
-- vim.keymap.set({"n", "v", "o"}, "<C-i>", "<C-c>ln")
-- vim.keymap.set({"n", "v", "o"}, "<C-r>", "<C-c>ln")
-- vim.keymap.set({"n", "v", "o"}, "<C-s>", "<C-c>ln")
vim.cmd("unmap <C-s>")
vim.cmd("map <C-n> <C-c>dn")
vim.cmd("map <C-i> <C-c>di")
vim.cmd("map <C-r> <C-c>dr")
vim.cmd("map <C-s> <C-c>ds")
print("Initialised debugger successfully")
end
local function dapCleanup()
dap.repl.close()
dapwidgetscope.close()
daprunning = false
vim.cmd("unmap <C-n>")
vim.cmd("unmap <C-i>")
vim.cmd("unmap <C-r>")
vim.cmd("unmap <C-s>")
vim.keymap.set({"n", "v", "o"}, '<C-s>', '<cmd>Telescope live_grep<cr>')
-- vim.keymap.del({"n", "v", "o"}, "<C-n>")
-- vim.keymap.del({"n", "v", "o"}, "<C-i>")
-- vim.keymap.del({"n", "v", "o"}, "<C-r>")
-- vim.keymap.del({"n", "v", "o"}, "<C-s>")
-- vim.keymap.set({"n", "v", "o"}, '<C-s>', '<cmd>Telescope live_grep<cr>')
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(<filename>, 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 ({
--{ "<C-s>", "<cmd>Telescope live_grep<cr>" , desc = "Swiper", mode = "n" },
{ "/", function () Snacks.picker.lines() end , desc = "Swiper", mode = "n" },
{ "<C-s>", 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.
--{ "<leader><C-f>", "<cmd>Telescope find_files find_command=rg,--ignore,--hidden,--files<cr>" , desc = "Find File", mode = "n" },
{ "<leader><C-f>", function() Snacks.picker.explorer({
layout = "ivy",
focus = "input",
auto_close = true,
win = {
input = {
keys = {
["<C-g>"] = { "focus_list", mode = { "i", "n" } },
["<Esc>"] = { "focus_list", mode = { "i", "n" } },
}
},
},
}) end, desc = "Find File", mode = "n" },
{ "<leader>f", function () Snacks.picker.files() end , desc = "Find File", mode = "n" },
--{ "<leader>f", "<cmd>Telescope file_browser<cr>" , 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
-- { "<leader>b", "<cmd>Telescope oldfiles<cr>" , desc = "Recently opened files", mode = "n" },
-- { "<leader><C-b>", "<cmd>Telescope buffers<cr>" , desc = "Buffers", mode = "n" },
-- SNACKS NOW GOOD ENOUGH TO PROVIDE EXACT BEHAVIOUR
{ "<leader>b", function () Snacks.picker.smart({multi = { "buffers", "recent" }, matcher = { cwd_bonus = false, frecency = true, sort_empty = true }}) end , desc = "Recently opened files", mode = "n" },
{ "<leader>k", "<cmd>bp|bd#<cr>" , desc = "Close current buffer", mode = "n" },
{ "<leader>0", "<cmd>close<cr>", desc = "Close focused window" },
{ "<leader>2", "<cmd>split<cr>", desc = "Split window horizontally" },
{ "<leader>3", "<cmd>vs<cr>", desc = "Split window vertically" },
{ "<leader>o", "<cmd>wincmd w<cr>", 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
{ "<leader><C-c>", "<cmd>qa<cr>", desc = "Quit" },
{ "<leader><C-s>", "<cmd>w<cr>", desc = "Write" },
},
-- LSP/DAP Options
{
mode = { "n", "v" }, -- NORMAL and VISUAL mode
{ "<C-c>db", function() dap.toggle_breakpoint() end, desc = "Debugger: Toggle breakpoint" },
{ "<C-c>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" },
{ "<C-c>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" },
{ "<C-c>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" },
{ "<C-c>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" },
{ "<C-c>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" },
{ "<C-c>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 = '<author>, <author_time:%R> - <summary>',
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
},
}