Diff
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e033bc6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+lazy-lock.json
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1779e97
--- /dev/null
+++ b/README.md
@@ -0,0 +1,117 @@
+# Neovim Keybinds Documentation
+
+This document provides a simple and organized overview of all the custom keybinds defined in my Neovim configuration.
+
+## General Keybinds
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `n` | `<leader>cd` | Open Ex mode (`:Ex`) |
+| `n` | `J` | Join lines while keeping the cursor in place |
+| `n` | `<C-d>` | Scroll half-page down and keep the cursor centered |
+| `n` | `<C-u>` | Scroll half-page up and keep the cursor centered |
+| `n` | `n` | Move to next search result and keep it centered |
+| `n` | `N` | Move to previous search result and keep it centered |
+| `n` | `Q` | Disable Ex mode |
+| `n` | `<C-k>` | Jump to next quickfix entry and keep it centered |
+| `n` | `<C-j>` | Jump to previous quickfix entry and keep it centered |
+| `n` | `<leader>k` | Jump to next location entry and keep it centered |
+| `n` | `<leader>j` | Jump to previous location entry and keep it centered |
+| `i` | `<C-c>` | Exit insert mode (acts like `Esc`) |
+| `n` | `<leader>x` | Make current file executable (`chmod +x`) |
+| `n` | `<leader>u` | Toggle Undotree |
+| `n` | `<leader>rl` | Reload the Neovim config (`~/.config/nvim/init.lua`) |
+| `n` | `<leader><leader>` | Source the current file (`:so`) |
+
+---
+
+## Visual Mode Keybinds
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `v` | `J` | Move selected block down |
+| `v` | `K` | Move selected block up |
+| `x` | `<leader>p` | Paste without overwriting clipboard |
+| `v` | `<leader>y` | Yank into system clipboard (even on SSH) |
+
+---
+
+## Linting and Formatting
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `n` | `<leader>cc` | Run `php-cs-fixer` to lint and format PHP files |
+| `n` | `<F3>` | Format code (`LSP`) |
+
+---
+
+## Telescope Keybinds
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `n` | `<leader>ff` | Find files |
+| `n` | `<leader>fg` | Find git-tracked files |
+| `n` | `<leader>fo` | Open recent files |
+| `n` | `<leader>fq` | Open quickfix list |
+| `n` | `<leader>fh` | Open help tags |
+| `n` | `<leader>fb` | Open buffer list |
+| `n` | `<leader>fs` | Grep current string |
+| `n` | `<leader>fc` | Grep instances of the current file name without the extension |
+| `n` | `<leader>fi` | Find files in Neovim configuration directory (`~/.config/nvim/`) |
+
+---
+
+## Harpoon Integration
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `n` | `<leader>a` | Add current file to Harpoon list |
+| `n` | `<C-e>` | Toggle Harpoon quick menu |
+| `n` | `<leader>fl` | Open Harpoon window with Telescope |
+| `n` | `<C-p>` | Go to previous Harpoon mark |
+| `n` | `<C-n>` | Go to next Harpoon mark |
+
+---
+
+## LSP Keybinds
+
+| Mode | Key | Action |
+|-----------|------------|---------------------------------------------------------------------------------------------|
+| `n` | `K` | Show hover information |
+| `n` | `gd` | Go to definition |
+| `n` | `gD` | Go to declaration |
+| `n` | `gi` | Go to implementation |
+| `n` | `go` | Go to type definition |
+| `n` | `gr` | Show references |
+| `n` | `gs` | Show signature help |
+| `n` | `gl` | Show diagnostics in a floating window |
+| `n` | `<F2>` | Rename symbol |
+| `n`, `x` | `<F3>` | Format code asynchronously |
+| `n` | `<F4>` | Show code actions |
+
+---
+
+## Miscellaneous
+
+| Mode | Key | Action |
+|------|-----------------|---------------------------------------------------------------------------------------------|
+| `n` | `<leader>dg` | Run `DogeGenerate` (comment documentation generation) |
+| `n` | `<leader>s` | Replace all instances of the word under the cursor on the current line |
+
+---
+
+# LSP servers:
+
+I am migrating my lsp config to /lua/plugins/lsp.lua because nvim v0.11 allows a very minimal debloated way to setup language server protocols.
+
+Below is a running list of what and how to install the lsp's that are going to be configured in this build. I will avoid mason for now because I think its better to have full control over your system, and not outsource it to mason. Just uncommonet `return {` in /plugins/lsp.lua from the original lspconfig if you want to go that route.
+
+1. { lua-language-server }
+ - refer to distro ( pacman -Ss lua-language-server )
+2. { css-language-server --studio, html-language-server }
+ - npm install -g vscode-langservers-extracted
+3. { intelephense }
+ - npm install -g intelephense
+4. { typescript-language-server }
+ - npm install -g typescript-language-server typescript
+
diff --git a/after/ftplugin/goon.lua b/after/ftplugin/goon.lua
new file mode 100644
index 0000000..2059624
--- /dev/null
+++ b/after/ftplugin/goon.lua
@@ -0,0 +1,2 @@
+vim.bo.commentstring = "// %s"
+vim.bo.comments = "s:/*,m: *,ex:*/,://"
diff --git a/after/ftplugin/jsonc.lua b/after/ftplugin/jsonc.lua
new file mode 100644
index 0000000..e1833b8
--- /dev/null
+++ b/after/ftplugin/jsonc.lua
@@ -0,0 +1,9 @@
+local set = vim.opt_local
+
+set.shiftwidth = 2
+set.tabstop = 2
+set.softtabstop = 2
+set.expandtab = true
+
+set.number = true
+set.relativenumber = true
diff --git a/after/ftplugin/man.lua b/after/ftplugin/man.lua
new file mode 100644
index 0000000..f21d675
--- /dev/null
+++ b/after/ftplugin/man.lua
@@ -0,0 +1,8 @@
+local set = vim.opt_local
+set.number = true
+set.relativenumber = true
+set.number = true
+set.relativenumber = false
+set.wrap = true
+set.linebreak = true
+set.conceallevel = 0
diff --git a/after/ftplugin/nix.lua b/after/ftplugin/nix.lua
new file mode 100644
index 0000000..6425eda
--- /dev/null
+++ b/after/ftplugin/nix.lua
@@ -0,0 +1,8 @@
+local set = vim.opt_local
+
+set.shiftwidth = 2
+set.tabstop = 2
+set.softtabstop = 2
+set.expandtab = true
+set.number = true
+set.relativenumber = true
diff --git a/after/plugin/colors.lua b/after/plugin/colors.lua
new file mode 100644
index 0000000..e9af665
--- /dev/null
+++ b/after/plugin/colors.lua
@@ -0,0 +1,6 @@
+vim.cmd.colorscheme("tokyonight")
+vim.cmd("hi Directory guibg=NONE")
+vim.cmd("hi SignColumn guibg=NONE")
+vim.api.nvim_set_hl(0, "Normal", { bg = "none" })
+vim.api.nvim_set_hl(0, "NormalFloat", { bg = "none" })
+vim.api.nvim_set_hl(0, "LineNr", { bg = "none" })
diff --git a/after/plugin/completion.lua b/after/plugin/completion.lua
new file mode 100644
index 0000000..da77d42
--- /dev/null
+++ b/after/plugin/completion.lua
@@ -0,0 +1,27 @@
+local cmp = require("cmp")
+
+cmp.setup({
+ preselect = cmp.PreselectMode.Item,
+ completion = { completeopt = "menu,menuone,noinsert" },
+ window = { documentation = cmp.config.window.bordered() },
+ mapping = cmp.mapping.preset.insert({
+ ["<CR>"] = cmp.mapping.confirm({ select = false }),
+ ["<C-e>"] = cmp.mapping.abort(),
+ ["<C-Space>"] = cmp.mapping.complete(),
+ ["<C-n>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }),
+ ["<C-p>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }),
+ ["<C-f>"] = cmp.mapping.scroll_docs(4),
+ ["<C-u>"] = cmp.mapping.scroll_docs(-4),
+ ["<Tab>"] = cmp.mapping(function(fallback)
+ if cmp.visible() then cmp.select_next_item() else fallback() end
+ end, { "i", "s" }),
+ ["<S-Tab>"] = cmp.mapping(function()
+ if cmp.visible() then cmp.select_prev_item() end
+ end, { "i", "s" }),
+ }),
+ sources = {
+ { name = "nvim_lsp" },
+ { name = "path" },
+ { name = "buffer", keyword_length = 3 },
+ },
+})
diff --git a/after/plugin/harpoon.lua b/after/plugin/harpoon.lua
new file mode 100644
index 0000000..cb0ddfc
--- /dev/null
+++ b/after/plugin/harpoon.lua
@@ -0,0 +1,21 @@
+local harpoon = require("harpoon")
+harpoon:setup()
+
+vim.keymap.set("n", "<leader>a", function() harpoon:list():add() end)
+vim.keymap.set("n", "<C-e>", function() harpoon.ui:toggle_quick_menu(harpoon:list()) end)
+vim.keymap.set("n", "<C-p>", function() harpoon:list():prev() end)
+vim.keymap.set("n", "<C-n>", function() harpoon:list():next() end)
+
+vim.keymap.set("n", "<leader>fl", function()
+ local conf = require("telescope.config").values
+ local themes = require("telescope.themes")
+ local file_paths = {}
+ for _, item in ipairs(harpoon:list().items) do
+ table.insert(file_paths, item.value)
+ end
+ require("telescope.pickers").new(themes.get_ivy({ prompt_title = "Working List" }), {
+ finder = require("telescope.finders").new_table({ results = file_paths }),
+ previewer = conf.file_previewer({}),
+ sorter = conf.generic_sorter({}),
+ }):find()
+end, { desc = "Open harpoon window" })
diff --git a/after/plugin/one-liners.lua b/after/plugin/one-liners.lua
new file mode 100644
index 0000000..63ed17e
--- /dev/null
+++ b/after/plugin/one-liners.lua
@@ -0,0 +1,6 @@
+require("lualine").setup({ options = { theme = "tokyonight" } })
+require("nvim-highlight-colors").setup({})
+require("orgmode").setup({
+ org_agenda_files = "~/orgfiles/**/*",
+ org_default_notes_file = "~/orgfiles/refile.org",
+})
diff --git a/after/plugin/telescope.lua b/after/plugin/telescope.lua
new file mode 100644
index 0000000..081311c
--- /dev/null
+++ b/after/plugin/telescope.lua
@@ -0,0 +1,31 @@
+local actions = require("telescope.actions")
+require("telescope").setup({
+ defaults = {
+ mappings = {
+ i = {
+ ["<C-k>"] = actions.move_selection_previous,
+ ["<C-j>"] = actions.move_selection_next,
+ ["<C-q>"] = actions.smart_send_to_qflist + actions.open_qflist,
+ },
+ },
+ },
+})
+
+local builtin = require("telescope.builtin")
+vim.keymap.set("n", "<leader>ff", builtin.find_files)
+vim.keymap.set("n", "<leader>fo", builtin.oldfiles)
+vim.keymap.set("n", "<leader>fq", builtin.quickfix)
+vim.keymap.set("n", "<leader>fh", builtin.help_tags, { desc = "Telescope help tags" })
+vim.keymap.set("n", "<leader>fb", builtin.buffers, { desc = "Telescope buffers" })
+vim.keymap.set("n", "<leader>fg", function()
+ builtin.grep_string({ search = vim.fn.input("Grep > ") })
+end)
+vim.keymap.set("n", "<leader>fc", function()
+ builtin.grep_string({ search = vim.fn.expand("%:t:r") })
+end, { desc = "Find current file" })
+vim.keymap.set("n", "<leader>fs", function()
+ builtin.grep_string({})
+end, { desc = "Find current string" })
+vim.keymap.set("n", "<leader>fi", function()
+ builtin.find_files({ cwd = "~/.config/nvim/" })
+end)
diff --git a/after/plugin/treesitter.lua b/after/plugin/treesitter.lua
new file mode 100644
index 0000000..495ac8c
--- /dev/null
+++ b/after/plugin/treesitter.lua
@@ -0,0 +1,49 @@
+vim.filetype.add({ extension = { goon = "goon" } })
+
+require("nvim-treesitter.parsers").goon = {
+ install_info = {
+ path = "/home/tony/repos/tree-sitter-goon",
+ files = { "src/parser.c" },
+ queries = "queries",
+ },
+ filetype = "goon",
+}
+
+local ts = require("nvim-treesitter")
+
+ts.setup({
+ install_dir = vim.fn.stdpath("data") .. "/site",
+})
+
+local parsers = {
+ "json", "python", "ron", "javascript", "haskell", "d", "query",
+ "typescript", "tsx", "rust", "zig", "php", "yaml", "html", "css",
+ "markdown", "markdown_inline", "bash", "lua", "vim", "vimdoc", "c",
+ "dockerfile", "gitignore", "astro",
+}
+
+vim.api.nvim_create_user_command("TSInstallAll", function()
+ ts.install(parsers)
+end, {})
+
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function()
+ pcall(vim.treesitter.start)
+ end,
+})
+
+require("nvim-treesitter-textobjects").setup({
+ select = {
+ lookahead = true,
+ },
+})
+
+vim.keymap.set({ "x", "o" }, "af", function()
+ require("nvim-treesitter-textobjects.select").select_textobject("@function.outer", "textobjects")
+end)
+vim.keymap.set({ "x", "o" }, "if", function()
+ require("nvim-treesitter-textobjects.select").select_textobject("@function.inner", "textobjects")
+end)
+
+require("treesitter-context").setup({})
diff --git a/init.lua b/init.lua
new file mode 100644
index 0000000..36a1d08
--- /dev/null
+++ b/init.lua
@@ -0,0 +1,3 @@
+require("config.options")
+require("config.keybinds")
+require("manage").setup()
diff --git a/lua/config/keybinds.lua b/lua/config/keybinds.lua
new file mode 100644
index 0000000..a2979b8
--- /dev/null
+++ b/lua/config/keybinds.lua
@@ -0,0 +1,66 @@
+-- KEYBINDS
+vim.g.mapleader = " "
+vim.keymap.set("n", "<leader>cd", vim.cmd.Ex)
+
+vim.keymap.set("v", "J", ":m '>+1<CR>gv=gv") -- Alt Up/Down in vscode
+vim.keymap.set("v", "K", ":m '<-2<CR>gv=gv")
+
+vim.keymap.set("n", "J", "mzJ`z") -- Remap joining lines
+vim.keymap.set("n", "<C-d>", "<C-d>zz") -- Keep cursor in place while moving up/down page
+vim.keymap.set("n", "<C-u>", "<C-u>zz")
+vim.keymap.set("n", "n", "nzzzv") -- center screen when looping search results
+vim.keymap.set("n", "N", "Nzzzv")
+
+-- paste and don't replace clipboard over deleted text
+vim.keymap.set("x", "<leader>p", [["_dP]])
+vim.keymap.set({ "n", "v" }, "<leader>d", [["_d]])
+
+
+-- sometimes in insert mode, control-c doesn't exactly work like escape
+vim.keymap.set("i", "<C-c>", "<Esc>")
+
+-- add binds for Control J/K to scroll thru quickfix list
+vim.keymap.set("n", "<C-j>", "<cmd>cnext<CR>zz")
+vim.keymap.set("n", "<C-k>", "<cmd>cprev<CR>zz")
+
+-- What the heck is Ex mode?
+vim.keymap.set("n", "Q", "<nop>")
+
+vim.keymap.set("n", "<leader>k", "<cmd>lnext<CR>zz")
+vim.keymap.set("n", "<leader>j", "<cmd>lprev<CR>zz")
+
+-- getting Alex off my back :)
+vim.keymap.set("n", "<leader>dg", "<cmd>DogeGenerate<cr>")
+
+-- lint / format php files for LC
+vim.keymap.set("n", "<leader>cc", "<cmd>!php-cs-fixer fix % --using-cache=no<cr>")
+
+-- Replace all instances of whatever is under cursor (on line)
+vim.keymap.set("n", "<leader>s", [[:s/\<<C-r><C-w>\>//gI<Left><Left><Left>]])
+
+-- make file executable
+vim.keymap.set("n", "<leader>x", "<cmd>!chmod +x %<CR>", { silent = true })
+
+-- yank into clipboard even if on ssh
+vim.keymap.set('n', '<leader>y', '<Plug>OSCYankOperator')
+vim.keymap.set('v', '<leader>y', '<Plug>OSCYankVisual')
+
+-- reload without exiting vim
+vim.keymap.set("n", "<leader>rl", "<cmd>source ~/.config/nvim/init.lua<cr>")
+
+vim.keymap.set("n", "<leader>u", vim.cmd.UndotreeToggle)
+
+-- Quickfix list stuff
+vim.keymap.set("n", "<leader>cl", ":cclose<CR>", { silent = true })
+vim.keymap.set("n", "<leader>co", ":copen<CR>", { silent = true })
+vim.keymap.set("n", "<leader>cn", ":cnext<CR>zz")
+vim.keymap.set("n", "<leader>cp", ":cprev<CR>zz")
+vim.keymap.set("n", "<leader>li", ":checkhealth vim.lsp<CR>", { desc = "LSP Info" })
+
+-- run make in current working directory
+vim.keymap.set("n", "<leader>mm", "<cmd>make<CR>")
+
+-- source file
+vim.keymap.set("n", "<leader><leader>", function()
+ vim.cmd("so")
+end)
diff --git a/lua/config/options.lua b/lua/config/options.lua
new file mode 100644
index 0000000..52de0a7
--- /dev/null
+++ b/lua/config/options.lua
@@ -0,0 +1,55 @@
+-- OPTIONS
+local set = vim.opt
+
+--line nums
+set.relativenumber = true
+set.number = true
+
+-- indentation and tabs
+set.tabstop = 4
+set.shiftwidth = 4
+set.autoindent = true
+set.expandtab = true
+
+-- search settings
+set.ignorecase = true
+set.smartcase = true
+
+-- appearance
+set.termguicolors = true
+set.background = "dark"
+set.signcolumn = "yes"
+
+-- cursor line
+set.cursorline = true
+
+-- 80th column
+set.colorcolumn = "80"
+
+-- clipboard
+set.clipboard:append("unnamedplus")
+
+-- backspace
+set.backspace = "indent,eol,start"
+
+-- split windows
+set.splitbelow = true
+set.splitright = true
+
+-- dw/diw/ciw works on full-word
+set.iskeyword:append("-")
+
+-- keep cursor at least 8 rows from top/bot
+set.scrolloff = 8
+
+-- undo dir settings
+set.swapfile = false
+set.backup = false
+set.undodir = os.getenv("HOME") .. "/.vim/undodir"
+set.undofile = true
+
+-- incremental search
+set.incsearch = true
+
+-- faster cursor hold
+set.updatetime = 50
diff --git a/lua/manage.lua b/lua/manage.lua
new file mode 100644
index 0000000..50989f3
--- /dev/null
+++ b/lua/manage.lua
@@ -0,0 +1,86 @@
+---@diagnostic disable: undefined-field
+local M = {}
+local plug_dir = vim.fn.stdpath("data") .. "/plugins"
+
+local function ensure(spec)
+ local repo = type(spec) == "string" and spec or spec[1]
+ local name = repo:match(".+/(.+)$")
+ local path = plug_dir .. "/" .. name
+
+ if not vim.uv.fs_stat(path) then
+ vim.fn.mkdir(plug_dir, "p")
+ local cmd = { "git", "clone", "--depth=1" }
+ if spec.branch then
+ table.insert(cmd, "-b")
+ table.insert(cmd, spec.branch)
+ end
+ table.insert(cmd, "https://github.com/" .. repo)
+ table.insert(cmd, path)
+ print("Installing " .. name .. "...")
+ vim.fn.system(cmd)
+ end
+
+ vim.opt.rtp:append(path)
+ local lua_path = path .. "/lua"
+ if vim.uv.fs_stat(lua_path) then
+ package.path = package.path .. ";" .. lua_path .. "/?.lua;" .. lua_path .. "/?/init.lua"
+ end
+end
+
+function M.setup()
+ for _, spec in ipairs(require("plugin-list")) do
+ ensure(spec)
+ end
+end
+
+function M.update()
+ local handle = vim.uv.fs_scandir(plug_dir)
+ if not handle then return print("No plugins installed") end
+ while true do
+ local name, t = vim.uv.fs_scandir_next(handle)
+ if not name then break end
+ if t == "directory" then
+ print("Updating " .. name .. "...")
+ vim.fn.system({ "git", "-C", plug_dir .. "/" .. name, "pull", "--ff-only" })
+ end
+ end
+ print("Done!")
+end
+
+function M.list()
+ local handle = vim.uv.fs_scandir(plug_dir)
+ if not handle then return print("No plugins installed") end
+ while true do
+ local name, t = vim.uv.fs_scandir_next(handle)
+ if not name then break end
+ if t == "directory" then print(name) end
+ end
+end
+
+function M.clean()
+ local installed = {}
+ local handle = vim.uv.fs_scandir(plug_dir)
+ if handle then
+ while true do
+ local name, t = vim.uv.fs_scandir_next(handle)
+ if not name then break end
+ if t == "directory" then installed[name] = true end
+ end
+ end
+
+ local wanted = {}
+ for _, spec in ipairs(require("plugin-list")) do
+ local repo = type(spec) == "string" and spec or spec[1]
+ wanted[repo:match(".+/(.+)$")] = true
+ end
+
+ for name in pairs(installed) do
+ if not wanted[name] then
+ print("Removing " .. name .. "...")
+ vim.fn.delete(plug_dir .. "/" .. name, "rf")
+ end
+ end
+ print("Done!")
+end
+
+return M
diff --git a/lua/plugin-list.lua b/lua/plugin-list.lua
new file mode 100644
index 0000000..02f3925
--- /dev/null
+++ b/lua/plugin-list.lua
@@ -0,0 +1,22 @@
+return {
+ "nvim-lua/plenary.nvim",
+ "nvim-tree/nvim-web-devicons",
+ { "nvim-treesitter/nvim-treesitter", branch = "main" },
+ { "nvim-treesitter/nvim-treesitter-textobjects", branch = "main" },
+ "nvim-treesitter/nvim-treesitter-context",
+ "hrsh7th/nvim-cmp",
+ "hrsh7th/cmp-nvim-lsp",
+ "hrsh7th/cmp-path",
+ "hrsh7th/cmp-buffer",
+ "nvim-telescope/telescope.nvim",
+ { "ThePrimeagen/harpoon", branch = "harpoon2" },
+ "folke/tokyonight.nvim",
+ "nvim-lualine/lualine.nvim",
+ "brenoprata10/nvim-highlight-colors",
+ "nvim-orgmode/orgmode",
+ "tpope/vim-fugitive",
+ "mbbill/undotree",
+ "ojroques/vim-oscyank",
+ "kkoomen/vim-doge",
+ "captbaritone/better-indent-support-for-php-with-html",
+}
diff --git a/plugin/flterm.lua b/plugin/flterm.lua
new file mode 100644
index 0000000..d766181
--- /dev/null
+++ b/plugin/flterm.lua
@@ -0,0 +1,55 @@
+-- Remap leaving 'terminal mode' to double tap esc
+vim.keymap.set("t", "<esc><esc>", "<c-\\><c-n>")
+
+local state = {
+ floating = {
+ buf = -1,
+ win = -1,
+ }
+}
+
+local function open_floating_terminal(opts)
+ opts = opts or {}
+ local width = opts.width or math.floor(vim.o.columns * 0.8)
+ local height = opts.height or math.floor(vim.o.lines * 0.8)
+
+ local row = math.floor((vim.o.lines - height) / 2)
+ local col = math.floor((vim.o.columns - width) / 2)
+
+ local buf = nil
+ if vim.api.nvim_buf_is_valid(opts.buf) then
+ buf = opts.buf
+ else
+ buf = vim.api.nvim_create_buf(false, true)
+ end
+ if not buf then
+ error("Failed to create buffer")
+ end
+
+ local win = vim.api.nvim_open_win(buf, true, {
+ relative = 'editor',
+ width = width,
+ height = height,
+ row = row,
+ col = col,
+ style = 'minimal',
+ border = 'rounded',
+ })
+
+ return { buf = buf, win = win }
+end
+
+local toggle_terminal = function()
+ if not vim.api.nvim_win_is_valid(state.floating.win) then
+ state.floating = open_floating_terminal({ buf = state.floating.buf });
+ if vim.bo[state.floating.buf].buftype ~= "terminal" then
+ vim.cmd.terminal()
+ vim.cmd("startinsert!")
+ end
+ else
+ vim.api.nvim_win_hide(state.floating.win)
+ end
+end
+
+vim.api.nvim_create_user_command("Flterm", toggle_terminal, {})
+vim.api.nvim_set_keymap('n', '<leader>ft', [[:Flterm<CR>]], { noremap = true, silent = true })
diff --git a/plugin/lsp.lua b/plugin/lsp.lua
new file mode 100644
index 0000000..8bbc49e
--- /dev/null
+++ b/plugin/lsp.lua
@@ -0,0 +1,270 @@
+vim.lsp.config('*', {
+ root_markers = { '.git' },
+})
+
+vim.diagnostic.config({
+ virtual_text = true,
+ severity_sort = true,
+ float = {
+ style = 'minimal',
+ border = 'rounded',
+ source = 'if_many',
+ header = '',
+ prefix = '',
+ },
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = '✘',
+ [vim.diagnostic.severity.WARN] = '▲',
+ [vim.diagnostic.severity.HINT] = '⚑',
+ [vim.diagnostic.severity.INFO] = '»',
+ },
+ },
+})
+
+local orig = vim.lsp.util.open_floating_preview
+---@diagnostic disable-next-line: duplicate-set-field
+function vim.lsp.util.open_floating_preview(contents, syntax, opts, ...)
+ opts = opts or {}
+ opts.border = opts.border or 'rounded'
+ opts.max_width = opts.max_width or 80
+ opts.max_height = opts.max_height or 24
+ opts.wrap = opts.wrap ~= false
+ return orig(contents, syntax, opts, ...)
+end
+
+vim.api.nvim_create_autocmd('LspAttach', {
+ group = vim.api.nvim_create_augroup('my.lsp', {}),
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ local buf = args.buf
+ local map = function(mode, lhs, rhs) vim.keymap.set(mode, lhs, rhs, { buffer = buf }) end
+
+ map('n', 'K', vim.lsp.buf.hover)
+ map('n', 'gd', vim.lsp.buf.definition)
+ map('n', 'gD', vim.lsp.buf.declaration)
+ map('n', 'gi', vim.lsp.buf.implementation)
+ map('n', 'go', vim.lsp.buf.type_definition)
+ map('n', 'gr', vim.lsp.buf.references)
+ map('n', 'gs', vim.lsp.buf.signature_help)
+ map('n', 'gl', vim.diagnostic.open_float)
+ map('n', '<F2>', vim.lsp.buf.rename)
+ map({ 'n', 'x' }, '<F3>', function() vim.lsp.buf.format({ async = true }) end)
+ map('n', '<F4>', vim.lsp.buf.code_action)
+
+ if client:supports_method('textDocument/documentHighlight') then
+ local highlight_augroup = vim.api.nvim_create_augroup('my.lsp.highlight', { clear = false })
+ vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
+ buffer = buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.document_highlight,
+ })
+ vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
+ buffer = buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.clear_references,
+ })
+ end
+
+ local excluded_filetypes = { php = true, c = true, cpp = true }
+ if not client:supports_method('textDocument/willSaveWaitUntil')
+ and client:supports_method('textDocument/formatting')
+ and not excluded_filetypes[vim.bo[buf].filetype]
+ then
+ vim.api.nvim_create_autocmd('BufWritePre', {
+ group = vim.api.nvim_create_augroup('my.lsp.format', { clear = false }),
+ buffer = buf,
+ callback = function()
+ vim.lsp.buf.format({ bufnr = buf, id = client.id, timeout_ms = 1000 })
+ end,
+ })
+ end
+ end,
+})
+local caps = require("cmp_nvim_lsp").default_capabilities()
+vim.lsp.config['luals'] = {
+ cmd = { 'lua-language-server' },
+ filetypes = { 'lua' },
+ root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
+ capabilities = caps,
+ settings = {
+ Lua = {
+ runtime = { version = 'LuaJIT' },
+ diagnostics = { globals = { 'vim' } },
+ workspace = {
+ checkThirdParty = false,
+ library = vim.api.nvim_get_runtime_file('', true),
+ },
+ telemetry = { enable = false },
+ },
+ },
+}
+
+vim.lsp.config['cssls'] = {
+ cmd = { 'vscode-css-language-server', '--stdio' },
+ filetypes = { 'css', 'scss', 'less' },
+ root_markers = { 'package.json', '.git' },
+ capabilities = caps,
+ settings = {
+ css = { validate = true },
+ scss = { validate = true },
+ less = { validate = true },
+ },
+}
+
+vim.lsp.config['phpls'] = {
+ cmd = { 'intelephense', '--stdio' },
+ filetypes = { 'php' },
+ root_markers = { 'composer.json', '.git' },
+ capabilities = caps,
+ settings = {
+ intelephense = {
+ files = {
+ maxSize = 5000000, -- default 5MB
+ },
+ },
+ },
+}
+
+vim.lsp.config['ts_ls'] = {
+ cmd = { 'typescript-language-server', '--stdio' },
+ filetypes = {
+ 'javascript', 'javascriptreact', 'javascript.jsx',
+ 'typescript', 'typescriptreact', 'typescript.tsx',
+ },
+ root_markers = { 'package.json', 'tsconfig.json', 'jsconfig.json', '.git' },
+ capabilities = caps,
+ settings = {
+ completions = {
+ completeFunctionCalls = true,
+ },
+ },
+}
+
+vim.lsp.config['zls'] = {
+ cmd = { 'zls' },
+ filetypes = { 'zig', 'zir' },
+ root_markers = { 'zls.json', 'build.zig', '.git' },
+ capabilities = caps,
+ settings = {
+ zls = {
+ enable_build_on_save = true,
+ build_on_save_step = "install",
+ warn_style = false,
+ enable_snippets = true,
+ }
+ }
+}
+
+vim.lsp.config['nil_ls'] = {
+ cmd = { 'nil' },
+ filetypes = { 'nix' },
+ root_markers = { 'flake.nix', 'default.nix', '.git' },
+ capabilities = caps,
+ settings = {
+ ['nil'] = {
+ formatting = {
+ command = { "alejandra" }
+ }
+ }
+ }
+}
+
+vim.lsp.config['rust_analyzer'] = {
+ cmd = { 'rust-analyzer' },
+ filetypes = { 'rust' },
+ root_markers = { 'Cargo.toml', 'rust-project.json', '.git' },
+ capabilities = caps,
+ settings = {
+ ['rust-analyzer'] = {
+ cargo = { allFeatures = true },
+ formatting = {
+ command = { "rustfmt" }
+ },
+ },
+ },
+}
+
+-- C / C++ via clangd
+vim.lsp.config['clangd'] = {
+ cmd = {
+ 'clangd',
+ '--background-index',
+ '--clang-tidy',
+ '--header-insertion=never',
+ '--completion-style=detailed',
+ '--query-driver=/nix/store/*-gcc-*/bin/gcc*,/nix/store/*-clang-*/bin/clang*,/run/current-system/sw/bin/cc*',
+ },
+ filetypes = { 'c', 'cpp', 'objc', 'objcpp' },
+ root_markers = { 'compile_commands.json', '.clangd', 'configure.ac', 'Makefile', '.git' },
+ capabilities = caps,
+ init_options = {
+ fallbackFlags = { '-std=c23' }, -- Default to C23
+ },
+}
+
+vim.lsp.config['c3lsp'] = {
+ cmd = { 'c3-lsp' },
+ filetypes = { 'c3' },
+ root_markers = { 'project.json', '.git' },
+ capabilities = caps,
+}
+
+vim.lsp.config['serve_d'] = {
+ cmd = { 'serve-d' },
+ filetypes = { 'd' },
+ root_markers = { 'dub.sdl', 'dub.json', '.git' },
+ capabilities = caps,
+}
+
+vim.lsp.config['jsonls'] = {
+ cmd = { 'vscode-json-languageserver', '--stdio' },
+ filetypes = { 'json', 'jsonc' },
+ root_markers = { 'package.json', '.git', 'config.jsonc' },
+ capabilities = caps,
+}
+
+vim.lsp.config['hls'] = {
+ cmd = { 'haskell-language-server-wrapper', '--lsp' },
+ filetypes = { 'haskell', 'lhaskell' },
+ root_markers = { 'stack.yaml', 'cabal.project', 'package.yaml', '*.cabal', 'hie.yaml', '.git' },
+ capabilities = caps,
+ settings = {
+ haskell = {
+ formattingProvider = 'fourmolu',
+ plugin = {
+ semanticTokens = { globalOn = false }
+ },
+ },
+ },
+}
+
+vim.lsp.config['gopls'] = {
+ cmd = { 'gopls' },
+ filetypes = { 'go', 'gomod', 'gowork', 'gotmpl' },
+ root_markers = { 'go.mod', 'go.work', '.git' },
+ capabilities = caps,
+ settings = {
+ gopls = {
+ analyses = {
+ unusedparams = true,
+ },
+ staticcheck = true,
+ },
+ },
+}
+
+vim.filetype.add({
+ extension = {
+ h = 'c',
+ c3 = 'c3',
+ d = 'd',
+ },
+})
+
+---@diagnostic disable-next-line: invisible
+for name, _ in pairs(vim.lsp.config._configs) do
+ if name ~= '*' then -- Skip the wildcard config
+ vim.lsp.enable(name)
+ end
+end
diff --git a/plugin/quickformat.lua b/plugin/quickformat.lua
new file mode 100644
index 0000000..038dadf
--- /dev/null
+++ b/plugin/quickformat.lua
@@ -0,0 +1,40 @@
+local function reformat_parenthesized_content()
+ local bufnr = vim.api.nvim_get_current_buf()
+ local row = vim.api.nvim_win_get_cursor(0)[1]
+ local line = vim.api.nvim_buf_get_lines(bufnr, row - 1, row, false)[1]
+
+ local inside = line:match("%((.-)%)")
+ if not inside then
+ vim.notify(
+ "No content found inside parentheses",
+ vim.log.levels.ERROR
+ )
+ return
+ end
+
+ local prefix = line:match("^(.-)%(") or ""
+ local suffix = line:match("%)(.*)$") or ""
+
+ local parts = vim.split(inside, ",%s*")
+ if #parts == 0 then
+ vim.notify("No comma-separated content found", vim.log.levels.ERROR)
+ return
+ end
+
+ local new_lines = {}
+ table.insert(new_lines, prefix .. "(")
+ for i, part in ipairs(parts) do
+ if i < #parts then
+ table.insert(new_lines, " " .. part .. ",")
+ else
+ table.insert(new_lines, " " .. part)
+ end
+ end
+ table.insert(new_lines, " )" .. suffix)
+
+ vim.api.nvim_buf_set_lines(bufnr, row - 1, row, false, new_lines)
+end
+
+vim.keymap.set("n", "<leader>qq", function()
+ reformat_parenthesized_content()
+end)
diff --git a/queries/goon/highlights.scm b/queries/goon/highlights.scm
new file mode 100644
index 0000000..893c012
--- /dev/null
+++ b/queries/goon/highlights.scm
@@ -0,0 +1,55 @@
+; Keywords
+"let" @keyword
+"if" @keyword.conditional
+"then" @keyword.conditional
+"else" @keyword.conditional
+"import" @keyword.import
+
+; Literals
+(string) @string
+(string_content) @string
+(escape_sequence) @string.escape
+(number) @number
+(boolean) @boolean
+
+; Identifiers
+(identifier) @variable
+
+; Types
+(type_annotation) @type
+
+; Fields
+(record_field
+ key: (identifier) @property)
+
+(field_access
+ (identifier) @property)
+
+; Functions
+(call_expression
+ function: (identifier) @function.call)
+
+; Operators
+"=" @operator
+":" @punctuation.delimiter
+"?" @operator
+"..." @operator
+"." @punctuation.delimiter
+
+; Punctuation
+"{" @punctuation.bracket
+"}" @punctuation.bracket
+"[" @punctuation.bracket
+"]" @punctuation.bracket
+"(" @punctuation.bracket
+")" @punctuation.bracket
+";" @punctuation.delimiter
+"," @punctuation.delimiter
+
+; Interpolation
+(interpolation
+ "${" @punctuation.special
+ "}" @punctuation.special)
+
+; Comments
+(comment) @comment