diff --git a/nvim/init.vim b/nvim-old/init.vim similarity index 100% rename from nvim/init.vim rename to nvim-old/init.vim diff --git a/nvim/.gitignore b/nvim/.gitignore new file mode 100644 index 0000000..d7ad043 --- /dev/null +++ b/nvim/.gitignore @@ -0,0 +1 @@ +plugin/ diff --git a/nvim/init.lua b/nvim/init.lua new file mode 100644 index 0000000..76c8e05 --- /dev/null +++ b/nvim/init.lua @@ -0,0 +1,17 @@ +require("thomas.plugins-setup") +require("thomas.core.options") +require("thomas.core.keymaps") +require("thomas.core.colourscheme") +require("thomas.plugins.lualine") +require("thomas.plugins.telescope") +require("thomas.plugins.nvim-cmp") +require("thomas.plugins.lsp.mason") +require("thomas.plugins.lsp.lspsaga") +require("thomas.plugins.lsp.lspconfig") +require("thomas.plugins.lsp.null-ls") +require("thomas.plugins.autopairs") +require("thomas.plugins.treesitter") +require("thomas.plugins.gitsigns") +require("thomas.plugins.copilot_plugin") +require("thomas.plugins.nvim-tree") +require("thomas.plugins.rainbow-delimiters") diff --git a/nvim/lua/thomas/core/colourscheme.lua b/nvim/lua/thomas/core/colourscheme.lua new file mode 100644 index 0000000..581bd23 --- /dev/null +++ b/nvim/lua/thomas/core/colourscheme.lua @@ -0,0 +1,16 @@ +-- set colorscheme to onedark with protected call +-- in case it isn't installed +local status, _ = pcall(vim.cmd, "colorscheme onedark") +if not status then + print("Colorscheme not found!") -- print error if colorscheme not installed + return +end + +-- helper function +local function update_hl(group, tbl) + local old_hl = vim.api.nvim_get_hl_by_name(group, true) + local new_hl = vim.tbl_extend("force", old_hl, tbl) + vim.api.nvim_set_hl(0, group, new_hl) +end + +update_hl("Function", { bold = true }) diff --git a/nvim/lua/thomas/core/keymaps.lua b/nvim/lua/thomas/core/keymaps.lua new file mode 100644 index 0000000..3a5b073 --- /dev/null +++ b/nvim/lua/thomas/core/keymaps.lua @@ -0,0 +1,34 @@ +vim.g.mapleader = ";" +local keymap = vim.keymap + +-- schema = keymap.set([vim mode: n (normal), i (insert)], [key sequence], [command]) + +-- Search +keymap.set("n", "nh", ":nohl") -- clear search highlights + +-- Windows +keymap.set("n", "sv", "v") -- split vertically +keymap.set("n", "sh", "s") -- split horizontally +keymap.set("n", "se", "=") -- make split window equal width +keymap.set("n", "sx", ":close") -- close current split window +keymap.set("n", "mm", ":MaximizerToggle") -- toggle fullscreen of current window + +-- Navigate windows +keymap.set("n", "", "h") -- move left +keymap.set("n", "", "j") -- move down +keymap.set("n", "", "k") -- move up +keymap.set("n", "", "l") -- move left + +-- Telescope +keymap.set("n", "ff", "Telescope find_files hidden=true") +keymap.set("n", "bb", "Telescope buffers") +keymap.set("n", "tt", "Telescope file_browser hidden=true") +keymap.set("n", "gg", "Telescope live_grep") +keymap.set("n", "fh", "Telescope help_tags") + +-- Nvim Tree +keymap.set("n", "fb", ":NvimTreeToggle") + +-- Git +keymap.set("n", "lg", ":LazyGit") +keymap.set("n", "gr", ":GV") -- Git graph view diff --git a/nvim/lua/thomas/core/options.lua b/nvim/lua/thomas/core/options.lua new file mode 100644 index 0000000..feb541b --- /dev/null +++ b/nvim/lua/thomas/core/options.lua @@ -0,0 +1,26 @@ +vim.g.onedark_terminal_italics = 1 + +local opt = vim.opt +opt.number = true +opt.showmatch = true +opt.ignorecase = true +opt.smartcase = true +opt.hlsearch = true +opt.incsearch = true +opt.tabstop = 2 +opt.softtabstop = 4 +opt.shiftwidth = 4 +opt.textwidth = 100 +-- opt.clipboard = "unnamedplus" +opt.clipboard:append("unnamedplus") +opt.cursorline = true +opt.background = "dark" +opt.mouse = "" +opt.ttyfast = true +opt.laststatus = 2 +opt.signcolumn = "yes" +opt.swapfile = false +opt.showmode = false +opt.termguicolors = true +opt.wrap = false +opt.backspace = "indent,eol,start" diff --git a/nvim/lua/thomas/plugins-setup.lua b/nvim/lua/thomas/plugins-setup.lua new file mode 100644 index 0000000..8785e98 --- /dev/null +++ b/nvim/lua/thomas/plugins-setup.lua @@ -0,0 +1,122 @@ +-- auto install packer if not installed +local ensure_packer = function() + local fn = vim.fn + local install_path = fn.stdpath("data") .. "/site/pack/packer/start/packer.nvim" + if fn.empty(fn.glob(install_path)) > 0 then + fn.system({ "git", "clone", "--depth", "1", "https://github.com/wbthomason/packer.nvim", install_path }) + vim.cmd([[packadd packer.nvim]]) + return true + end + return false +end +local packer_bootstrap = ensure_packer() -- true if packer was just installed + +-- autocommand that reloads neovim and installs/updates/removes plugins +-- when file is saved +vim.cmd([[ + augroup packer_user_config + autocmd! + autocmd BufWritePost plugins-setup.lua source | PackerSync + augroup end +]]) + +-- import packer safely +local status, packer = pcall(require, "packer") +if not status then + return +end + +-- plugins to install +return packer.startup(function(use) + -- packer can manage itself + use("wbthomason/packer.nvim") + use("joshdick/onedark.vim") + use("junegunn/vim-easy-align") + use("junegunn/rainbow_parentheses.vim") + use("HiPhish/rainbow-delimiters.nvim") + use("tpope/vim-fugitive") + use("junegunn/gv.vim") + use("ap/vim-css-color") + use("wakatime/vim-wakatime") + use("psliwka/vim-smoothie") + use("kdheepak/lazygit.nvim") + use("nvim-lua/plenary.nvim") + use("szw/vim-maximizer") + use("tpope/vim-commentary") + use("tpope/vim-surround") + use("vim-scripts/ReplaceWithRegister") + use("nvim-tree/nvim-tree.lua") + use("nvim-lualine/lualine.nvim") + use({ "nvim-telescope/telescope-fzf-native.nvim", run = "make" }) + use({ "nvim-telescope/telescope.nvim", branch = "0.1.x" }) + use({ + "nvim-telescope/telescope-file-browser.nvim", + requires = { "nvim-telescope/telescope.nvim", "nvim-lua/plenary.nvim" }, + }) + -- startup page + use({ + "goolord/alpha-nvim", + requires = { "nvim-tree/nvim-web-devicons" }, + config = function() + require("alpha").setup(require("alpha.themes.startify").config) + end, + }) + + -- autocompletion and snippets + use("hrsh7th/nvim-cmp") -- completion plugin + use("hrsh7th/cmp-buffer") -- source for text in buffer + use("hrsh7th/cmp-path") -- source for file system paths + use("L3MON4D3/LuaSnip") -- snippet engine + use("saadparwaiz1/cmp_luasnip") -- for autocompletion + use("rafamadriz/friendly-snippets") -- useful snippets + + -- managing & installing lsp servers, linters & formatters + use("williamboman/mason.nvim") -- in charge of managing lsp servers, linters & formatters + use("williamboman/mason-lspconfig.nvim") -- bridges gap b/w mason & lspconfig + + -- configuring lsp servers + use("neovim/nvim-lspconfig") -- easily configure language servers + use("hrsh7th/cmp-nvim-lsp") -- for autocompletion + use({ "glepnir/lspsaga.nvim", branch = "main" }) + + use("jose-elias-alvarez/typescript.nvim") -- additional functionality for typescript server (e.g. rename file & update imports) + use("onsails/lspkind.nvim") -- vs-code like icons for autocompletion + + -- formatting and linting + use("jose-elias-alvarez/null-ls.nvim") -- configure formatters & linters + use("jayp0521/mason-null-ls.nvim") -- bridges gap b/w mason & null-ls + + use({ + "nvim-treesitter/nvim-treesitter", + run = ":TSUpdate", + }) + + -- auto closing + use("windwp/nvim-autopairs") -- autoclose parens, brackets, quotes, etc... + use({ "windwp/nvim-ts-autotag", after = "nvim-treesitter" }) -- autoclose tags + + -- git integration + use("lewis6991/gitsigns.nvim") -- show line modifications on left hand side + + -- GitHub Copilot + + use({ + "zbirenbaum/copilot.lua", + cmd = "Copilot", + event = "InsertEnter", + config = function() + require("copilot").setup({}) + end, + }) + + use({ + "zbirenbaum/copilot-cmp", + after = { "copilot.lua" }, + config = function() + require("copilot_cmp").setup() + end, + }) + if packer_bootstrap then + require("packer").sync() + end +end) diff --git a/nvim/lua/thomas/plugins/autopairs.lua b/nvim/lua/thomas/plugins/autopairs.lua new file mode 100644 index 0000000..25539ba --- /dev/null +++ b/nvim/lua/thomas/plugins/autopairs.lua @@ -0,0 +1,29 @@ +-- import nvim-autopairs safely +local autopairs_setup, autopairs = pcall(require, "nvim-autopairs") +if not autopairs_setup then + return +end + +-- configure autopairs +autopairs.setup({ + check_ts = true, -- enable treesitter + ts_config = { + lua = { "string" }, -- don't add pairs in lua string treesitter nodes + javascript = { "template_string" }, -- don't add pairs in javscript template_string treesitter nodes + }, +}) + +-- import nvim-autopairs completion functionality safely +local cmp_autopairs_setup, cmp_autopairs = pcall(require, "nvim-autopairs.completion.cmp") +if not cmp_autopairs_setup then + return +end + +-- import nvim-cmp plugin safely (completions plugin) +local cmp_setup, cmp = pcall(require, "cmp") +if not cmp_setup then + return +end + +-- make autopairs and completion work together +cmp.event:on("confirm_done", cmp_autopairs.on_confirm_done()) diff --git a/nvim/lua/thomas/plugins/copilot_plugin.lua b/nvim/lua/thomas/plugins/copilot_plugin.lua new file mode 100644 index 0000000..9782149 --- /dev/null +++ b/nvim/lua/thomas/plugins/copilot_plugin.lua @@ -0,0 +1,17 @@ +local status, copilot = pcall(require, "copilot") +if not status then + return +end + +copilot.setup({ + suggestion = { + enabled = false, + }, + panel = { + enabled = false, + }, + filetypes = { + yaml = false, + markdown = false, + }, +}) diff --git a/nvim/lua/thomas/plugins/gitsigns.lua b/nvim/lua/thomas/plugins/gitsigns.lua new file mode 100644 index 0000000..22f7ba6 --- /dev/null +++ b/nvim/lua/thomas/plugins/gitsigns.lua @@ -0,0 +1,8 @@ +-- import gitsigns plugin safely +local setup, gitsigns = pcall(require, "gitsigns") +if not setup then + return +end + +-- configure/enable gitsigns +gitsigns.setup() diff --git a/nvim/lua/thomas/plugins/lsp/lspconfig.lua b/nvim/lua/thomas/plugins/lsp/lspconfig.lua new file mode 100644 index 0000000..d0d562d --- /dev/null +++ b/nvim/lua/thomas/plugins/lsp/lspconfig.lua @@ -0,0 +1,81 @@ +-- import lspconfig plugin safely +local lspconfig_status, lspconfig = pcall(require, "lspconfig") +if not lspconfig_status then + return +end + +-- import cmp-nvim-lsp plugin safely +local cmp_nvim_lsp_status, cmp_nvim_lsp = pcall(require, "cmp_nvim_lsp") +if not cmp_nvim_lsp_status then + return +end + +-- import typescript plugin safely +local typescript_setup, typescript = pcall(require, "typescript") +if not typescript_setup then + return +end + +local keymap = vim.keymap + +-- enable keybinds only for when lsp server available +local on_attach = function(client, bufnr) + -- keybind options + local opts = { noremap = true, silent = true, buffer = bufnr } + + -- set keybinds + keymap.set("n", "gf", "Lspsaga lsp_finder", opts) -- show definition + keymap.set("n", "gd", "Lspsaga peek_definition", opts) -- see definition and make edits in window + keymap.set("n", "gi", "lua vim.lsp.buf.implementation()", opts) -- go to implementation + keymap.set("n", "ca", "Lspsaga code_action", opts) -- see available code actions + keymap.set("n", "rn", "Lspsaga rename", opts) -- smart rename + keymap.set("n", "b", "Lspsaga show_line_diagnostics", opts) -- show diagnostics for line + keymap.set("n", "d", "Lspsaga show_cursor_diagnostics", opts) -- show diagnostics for cursor + keymap.set("n", "y", "Lspsaga finder", opts) -- find refs elsewhere in project +end +-- used to enable autocompletion (assign to every lsp server config) +local capabilities = cmp_nvim_lsp.default_capabilities() + +-- Change the Diagnostic symbols in the sign column (gutter) +local signs = { Error = " ", Warn = " ", Hint = "ﴞ ", Info = " " } +for type, icon in pairs(signs) do + local hl = "DiagnosticSign" .. type + vim.fn.sign_define(hl, { text = icon, texthl = hl, numhl = "" }) +end + +-- Hide diagnostic virtual text by default, but toggle visibility with `lead:lh` command +vim.diagnostic.config({ + virtual_text = false, +}) + +vim.keymap.set("n", "lh", function() + local config = vim.diagnostic.config() + vim.diagnostic.config({ virtual_text = not config.virtual_text }) + print("Toggling LSP diagnostics") +end) + +-- configure html server +lspconfig["html"].setup({ + capabilities = capabilities, + on_attach = on_attach, +}) + +-- configure typescript server with plugin +typescript.setup({ + server = { + capabilities = capabilities, + on_attach = on_attach, + }, +}) + +-- configure css server +lspconfig["cssls"].setup({ + capabilities = capabilities, + on_attach = on_attach, +}) + +-- configure tailwindcss server +lspconfig["tailwindcss"].setup({ + capabilities = capabilities, + on_attach = on_attach, +}) diff --git a/nvim/lua/thomas/plugins/lsp/lspsaga.lua b/nvim/lua/thomas/plugins/lsp/lspsaga.lua new file mode 100644 index 0000000..90f5d57 --- /dev/null +++ b/nvim/lua/thomas/plugins/lsp/lspsaga.lua @@ -0,0 +1,19 @@ +-- import lspsaga safely +local saga_status, saga = pcall(require, "lspsaga") +if not saga_status then + return +end + +saga.setup({ + -- keybinds for navigation in lspsaga window + scroll_preview = { scroll_down = "", scroll_up = "" }, + -- use enter to open file with definition preview + definition = { + edit = "", + }, + ui = { + colors = { + normal_bg = "#022746", + }, + }, +}) diff --git a/nvim/lua/thomas/plugins/lsp/mason.lua b/nvim/lua/thomas/plugins/lsp/mason.lua new file mode 100644 index 0000000..15b1e36 --- /dev/null +++ b/nvim/lua/thomas/plugins/lsp/mason.lua @@ -0,0 +1,51 @@ +-- import mason plugin safely +local mason_status, mason = pcall(require, "mason") +if not mason_status then + return +end + +-- import mason-lspconfig plugin safely +local mason_lspconfig_status, mason_lspconfig = pcall(require, "mason-lspconfig") +if not mason_lspconfig_status then + return +end + +-- import mason-null-ls plugin safely +local mason_null_ls_status, mason_null_ls = pcall(require, "mason-null-ls") +if not mason_null_ls_status then + return +end + +mason.setup() + +mason_lspconfig.setup({ + -- list of servers for mason to install + ensure_installed = { + "tsserver", + "html", + "cssls", + "bashls", + "dockerls", + "eslint", + "pyre", + "tailwindcss", + "lua_ls", + "jsonls", + "yamlls", + }, + -- auto-install configured servers (with lspconfig) + automatic_installation = true, -- not the same as ensure_installed +}) + +mason_null_ls.setup({ + -- list of formatters & linters for mason to install + ensure_installed = { + "prettier", -- ts/js formatter + "stylua", -- lua formatter + "eslint_d", -- ts/js linter + "black", + "pylint", + }, + + automatic_installation = true, +}) diff --git a/nvim/lua/thomas/plugins/lsp/null-ls.lua b/nvim/lua/thomas/plugins/lsp/null-ls.lua new file mode 100644 index 0000000..b77ea7d --- /dev/null +++ b/nvim/lua/thomas/plugins/lsp/null-ls.lua @@ -0,0 +1,49 @@ +-- import null-ls plugin safely +local setup, null_ls = pcall(require, "null-ls") + +if not setup then + return +end + +-- for conciseness +local formatting = null_ls.builtins.formatting -- to setup formatters +local diagnostics = null_ls.builtins.diagnostics -- to setup linters + +-- to setup format on save +local augroup = vim.api.nvim_create_augroup("LspFormatting", {}) + +null_ls.setup({ + sources = { + formatting.prettier.with({ + extra_filetypes = { "markdown", "md" }, + }), -- js/ts formatter + formatting.stylua, -- lua formatter + formatting.black, + diagnostics.ruff, + -- diagnostics.pylint, + diagnostics.eslint_d.with({ -- js/ts linter + condition = function(utils) + return utils.root_has_file(".eslintrc.js") -- change file extension if you use something else + end, + }), + }, + -- configure format on save + on_attach = function(current_client, bufnr) + if current_client.supports_method("textDocument/formatting") then + vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr }) + vim.api.nvim_create_autocmd("BufWritePre", { + group = augroup, + buffer = bufnr, + callback = function() + vim.lsp.buf.format({ + filter = function(client) + -- only use null-ls for formatting instead of lsp server + return client.name == "null-ls" + end, + bufnr = bufnr, + }) + end, + }) + end + end, +}) diff --git a/nvim/lua/thomas/plugins/lualine.lua b/nvim/lua/thomas/plugins/lualine.lua new file mode 100644 index 0000000..22449c9 --- /dev/null +++ b/nvim/lua/thomas/plugins/lualine.lua @@ -0,0 +1,33 @@ +local status, lualine = pcall(require, "lualine") +if not status then + return +end + +local lualine_onedark = require("lualine.themes.onedark") + +-- lualine_onedark.normal.c.bg = "#3c3836" + +lualine.setup({ + options = { + theme = lualine_onedark, + component_separators = { left = "|", right = "|" }, + section_separators = { left = " ", right = " " }, + }, + sections = { + lualine_c = { + { + "buffers", + show_filename_only = true, + show_modified_status = true, + mode = 3, + filetype_names = { + TelescopePrompt = "Telescope", + }, + }, + { + "filename", + path = 3, + }, + }, + }, +}) diff --git a/nvim/lua/thomas/plugins/nvim-cmp.lua b/nvim/lua/thomas/plugins/nvim-cmp.lua new file mode 100644 index 0000000..855f97a --- /dev/null +++ b/nvim/lua/thomas/plugins/nvim-cmp.lua @@ -0,0 +1,56 @@ +-- import nvim-cmp plugin safely +local cmp_status, cmp = pcall(require, "cmp") +if not cmp_status then + return +end + +-- import luasnip plugin safely +local luasnip_status, luasnip = pcall(require, "luasnip") +if not luasnip_status then + return +end + +-- import lspkind plugin safely +local lspkind_status, lspkind = pcall(require, "lspkind") +if not lspkind_status then + return +end + +-- load vs-code like snippets from plugins (e.g. friendly-snippets) +require("luasnip/loaders/from_vscode").lazy_load() + +vim.opt.completeopt = "menu,menuone,noselect" + +cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.select_prev_item(), -- previous suggestion + [""] = cmp.mapping.select_next_item(), -- next suggestion + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete(), -- show completion suggestions + [""] = cmp.mapping.abort(), -- close completion window + [""] = cmp.mapping.confirm({ select = false }), + }), + -- sources for autocompletion + sources = cmp.config.sources({ + { name = "copilot", group_index = 2 }, + { name = "nvim_lsp" }, -- lsp + { name = "luasnip" }, -- snippets + { name = "buffer" }, -- text within current buffer + { name = "path" }, -- file system paths + }), + + -- configure lspkind for vs-code like icons + formatting = { + format = lspkind.cmp_format({ + maxwidth = 150, + ellipsis_char = "...", + symbol_map = { Copilot = "" }, + }), + }, +}) diff --git a/nvim/lua/thomas/plugins/nvim-tree.lua b/nvim/lua/thomas/plugins/nvim-tree.lua new file mode 100644 index 0000000..c41d822 --- /dev/null +++ b/nvim/lua/thomas/plugins/nvim-tree.lua @@ -0,0 +1,17 @@ +local setup, nvimtree = pcall(require, "nvim-tree") +if not setup then + return +end + +vim.g.loaded = 1 +vim.g.loaded_netrw = 1 + +nvimtree.setup({ + actions = { + open_file = { + window_picker = { + enable = false, + }, + }, + }, +}) diff --git a/nvim/lua/thomas/plugins/rainbow-delimiters.lua b/nvim/lua/thomas/plugins/rainbow-delimiters.lua new file mode 100644 index 0000000..78e6f6c --- /dev/null +++ b/nvim/lua/thomas/plugins/rainbow-delimiters.lua @@ -0,0 +1,20 @@ +-- import rainbow-delimeters safely +local setup, rd = pcall(require, "rainbow-delimiters.setup") +if not setup then + return +end + +rd.setup({ + -- change the default colour order + -- only use three colours, to match One Dark theme in VSCode + highlight = { + "RainbowDelimiterYellow", + "RainbowDelimiterViolet", + "RainbowDelimiterCyan", + }, +}) + +-- change colour values to match One Dark +vim.api.nvim_set_hl(0, "RainbowDelimiterViolet", { foreground = "#C678DD", bold = true }) +vim.api.nvim_set_hl(0, "RainbowDelimiterYellow", { foreground = "#E5C07B", bold = true }) +vim.api.nvim_set_hl(0, "RainbowDelimiterCyan", { foreground = "#56B6C2", bold = true }) diff --git a/nvim/lua/thomas/plugins/telescope.lua b/nvim/lua/thomas/plugins/telescope.lua new file mode 100644 index 0000000..a31b652 --- /dev/null +++ b/nvim/lua/thomas/plugins/telescope.lua @@ -0,0 +1,27 @@ +local telescope_setup, telescope = pcall(require, "telescope") +if not telescope_setup then + return +end + +local actions_setup, actions = pcall(require, "telescope.actions") +if not actions_setup then + return +end + +telescope.load_extension("fzf") +telescope.load_extension("file_browser") +telescope.setup({ + defaults = { + mappings = { + i = { + [""] = actions.move_selection_previous, -- move to prev result + [""] = actions.move_selection_next, -- move to next result + [""] = actions.send_selected_to_qflist + actions.open_qflist, -- send selected to quickfixlist + }, + }, + file_ignore_patterns = { + "node_modules", + ".git", + }, + }, +}) diff --git a/nvim/lua/thomas/plugins/treesitter.lua b/nvim/lua/thomas/plugins/treesitter.lua new file mode 100644 index 0000000..ab3f1e2 --- /dev/null +++ b/nvim/lua/thomas/plugins/treesitter.lua @@ -0,0 +1,39 @@ +-- import nvim-treesitter plugin safely +local status, treesitter = pcall(require, "nvim-treesitter.configs") +if not status then + return +end + +-- configure treesitter +treesitter.setup({ + -- enable syntax highlighting + highlight = { + enable = true, + }, + -- enable indentation + indent = { enable = true }, + -- enable autotagging (w/ nvim-ts-autotag plugin) + autotag = { enable = true }, + -- ensure these language parsers are installed + ensure_installed = { + "json", + "javascript", + "typescript", + "tsx", + "yaml", + "html", + "css", + "scss", + "markdown", + "markdown_inline", + "graphql", + "bash", + "lua", + "vim", + "dockerfile", + "gitignore", + "python", + }, + -- auto install above language parsers + auto_install = true, +})