commit ae7d9bf4b6e7eddb13710d7912e4f2514225aa96 Author: Mariano Z. Date: Sat Dec 21 11:22:16 2024 -0300 batman! diff --git a/.stylua.toml b/.stylua.toml new file mode 100644 index 0000000..9393cf9 --- /dev/null +++ b/.stylua.toml @@ -0,0 +1,5 @@ +column_width = 120 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferDouble" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7569c98 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Mariano Zunino + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..456a634 --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +# My Neovim Configuration + +This is my personal Neovim setup, tailored for a fast and minimal development workflow. + +## Installation + +1. Clone the repo: + ```bash + git clone https://github.com/marianozunino/nvim.git ~/.config/nvim + ``` +2. Open Neovim +![Neovim Setup](pic.jpg) + + +## License + +This configuration is available under the [MIT License](LICENSE). diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..1a891b9 --- /dev/null +++ b/init.lua @@ -0,0 +1,5 @@ +require("config.utils") +require("config.options") +require("config.remap") +require("config.autocomands") +require("config.lazy") diff --git a/lazy-lock.json b/lazy-lock.json new file mode 100644 index 0000000..9993a03 --- /dev/null +++ b/lazy-lock.json @@ -0,0 +1,57 @@ +{ + "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, + "LuaSnip": { "branch": "master", "commit": "33b06d72d220aa56a7ce80a0dd6f06c70cd82b9d" }, + "alpha-nvim": { "branch": "main", "commit": "de72250e054e5e691b9736ee30db72c65d560771" }, + "blink.cmp": { "branch": "main", "commit": "ca05bb33eb071d916d33af08cae8034c3d8002b5" }, + "chezmoi.nvim": { "branch": "main", "commit": "f5614261b77cb17df72ba2c4fdbc31f7ee42bc6c" }, + "chezmoi.vim": { "branch": "main", "commit": "abf37336437867cbd99ce2f8849b717415391cc3" }, + "cloak.nvim": { "branch": "main", "commit": "648aca6d33ec011dc3166e7af3b38820d01a71e4" }, + "codeium.vim": { "branch": "main", "commit": "d152e6cd3d814a44f36447bb0d21d51f6787fe9c" }, + "conform.nvim": { "branch": "master", "commit": "880aa379f91ed36c328806846f7c1eca9b49241e" }, + "dressing.nvim": { "branch": "master", "commit": "8fb86f808420d5a8a1b1bf869b1609b2337dc6e0" }, + "ecolog.nvim": { "branch": "main", "commit": "8e191f639e3c96f7159c5770757aa157292551b9" }, + "flash.nvim": { "branch": "main", "commit": "34c7be146a91fec3555c33fe89c7d643f6ef5cf1" }, + "friendly-snippets": { "branch": "main", "commit": "efff286dd74c22f731cdec26a70b46e5b203c619" }, + "fzf-lua": { "branch": "main", "commit": "34d5e4053a3825ef16a04aa3664dc856c6a13e64" }, + "gitlinker.nvim": { "branch": "master", "commit": "cc59f732f3d043b626c8702cb725c82e54d35c25" }, + "gitsigns.nvim": { "branch": "main", "commit": "5f808b5e4fef30bd8aca1b803b4e555da07fc412" }, + "gopher.nvim": { "branch": "main", "commit": "f55c15ada8e02398000c04a96ef44d986cd01051" }, + "harpoon": { "branch": "harpoon2", "commit": "a84ab829eaf3678b586609888ef52f7779102263" }, + "indent-blankline.nvim": { "branch": "master", "commit": "259357fa4097e232730341fa60988087d189193a" }, + "lazy.nvim": { "branch": "main", "commit": "7e6c863bc7563efbdd757a310d17ebc95166cef3" }, + "lazydev.nvim": { "branch": "main", "commit": "8620f82ee3f59ff2187647167b6b47387a13a018" }, + "lazygit.nvim": { "branch": "main", "commit": "77a0d42943d8265271e6e6beaed72da54eeb17e7" }, + "lualine.nvim": { "branch": "master", "commit": "2a5bae925481f999263d6f5ed8361baef8df4f83" }, + "markdown-preview.nvim": { "branch": "master", "commit": "a923f5fc5ba36a3b17e289dc35dc17f66d0548ee" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "2daa8921b7afdcfa47419a21ea343c3df6d74fa0" }, + "mason.nvim": { "branch": "main", "commit": "e2f7f9044ec30067bc11800a9e266664b88cda22" }, + "mini.nvim": { "branch": "main", "commit": "4228f166ee9db3e910eea1915e2d3683028add47" }, + "neoscroll.nvim": { "branch": "master", "commit": "f957373912e88579e26fdaea4735450ff2ef5c9c" }, + "nightfox.nvim": { "branch": "main", "commit": "7557f26defd093c4e9bc17f28b08403f706f5a44" }, + "noice.nvim": { "branch": "main", "commit": "eaed6cc9c06aa2013b5255349e4f26a6b17ab70f" }, + "nui.nvim": { "branch": "main", "commit": "53e907ffe5eedebdca1cd503b00aa8692068ca46" }, + "nvim-colorizer.lua": { "branch": "master", "commit": "a065833f35a3a7cc3ef137ac88b5381da2ba302e" }, + "nvim-lastplace": { "branch": "main", "commit": "0bb6103c506315044872e0f84b1f736c4172bb20" }, + "nvim-lint": { "branch": "master", "commit": "1fea92f1d9908eaa5eb8bafe08b4293d7aadaa55" }, + "nvim-lspconfig": { "branch": "master", "commit": "040001d85e9190a904d0e35ef5774633e14d8475" }, + "nvim-navic": { "branch": "master", "commit": "8649f694d3e76ee10c19255dece6411c29206a54" }, + "nvim-spectre": { "branch": "master", "commit": "08be31c104df3b4b049607694ebb2b6ced4f928b" }, + "nvim-treesitter": { "branch": "master", "commit": "2a75d8065cff33216e106b651eb0f58b90375717" }, + "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, + "nvim-vtsls": { "branch": "main", "commit": "45c6dfea9f83a126e9bfc5dd63430562b3f8af16" }, + "oil.nvim": { "branch": "master", "commit": "c5f7c56644425e2b77e71904da98cda0331b3342" }, + "plenary.nvim": { "branch": "master", "commit": "2d9b06177a975543726ce5c73fca176cedbffe9d" }, + "pretty-fold.nvim": { "branch": "master", "commit": "1eb18f228972e86b7b8f5ef33ca8091e53fb1e49" }, + "render-markdown.nvim": { "branch": "main", "commit": "0022a579ac7355966be5ade77699b88c76b6a549" }, + "rose-pine": { "branch": "main", "commit": "91548dca53b36dbb9d36c10f114385f759731be1" }, + "schemastore.nvim": { "branch": "main", "commit": "af3f82cd4972520e6ac04c48ed3eed473660efca" }, + "suda.vim": { "branch": "master", "commit": "9adda7d195222d4e2854efb2a88005a120296c47" }, + "todo-comments.nvim": { "branch": "main", "commit": "ae0a2afb47cf7395dc400e5dc4e05274bf4fb9e0" }, + "trouble.nvim": { "branch": "main", "commit": "46cf952fc115f4c2b98d4e208ed1e2dce08c9bf6" }, + "ts-error-translator.nvim": { "branch": "main", "commit": "47e5ba89f71b9e6c72eaaaaa519dd59bd6897df4" }, + "undotree": { "branch": "master", "commit": "78b5241191852ffa9bb5da5ff2ee033160798c3b" }, + "vim-dadbod": { "branch": "master", "commit": "f740950d0703099e0f172016f10e0e39f50fd0ba" }, + "vim-dadbod-completion": { "branch": "master", "commit": "9e354e86fcc67a5ec2c104f312e374ea2f89c799" }, + "vim-dadbod-ui": { "branch": "master", "commit": "0fec59e3e1e619e302198cd491b7d27f8d398b7c" }, + "vim-sleuth": { "branch": "master", "commit": "be69bff86754b1aa5adcbb527d7fcd1635a84080" } +} diff --git a/lua/config/autocomands.lua b/lua/config/autocomands.lua new file mode 100644 index 0000000..068c88b --- /dev/null +++ b/lua/config/autocomands.lua @@ -0,0 +1,71 @@ +-- Create autogroups first +local MZuninoGroup = vim.api.nvim_create_augroup("mzunino", {}) +local yank_group = vim.api.nvim_create_augroup("HighlightYank", {}) +local bigfile_group = vim.api.nvim_create_augroup("bigfile", {}) + +-- Set bigfile size threshold +vim.g.bigfile_size = 1024 * 1024 * 1.5 -- 1.5 MB + +-- Netrw diagnostic disable +vim.api.nvim_create_autocmd("FileType", { + pattern = "netrw", + callback = function() + vim.diagnostic.enable(false) + end, +}) + +-- Template files +vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { + pattern = "*.templ", + command = "set filetype=templ", +}) + +-- Highlight on yank +vim.api.nvim_create_autocmd("TextYankPost", { + group = yank_group, + pattern = "*", + callback = function() + vim.highlight.on_yank({ + higroup = "IncSearch", + timeout = 40, + }) + end, +}) + +-- Remove trailing whitespace on save +vim.api.nvim_create_autocmd("BufWritePre", { + group = MZuninoGroup, + pattern = "*", + command = [[%s/\s\+$//e]], +}) + +-- Auto-apply chezmoi changes +vim.api.nvim_create_autocmd("BufWritePost", { + group = MZuninoGroup, + pattern = "~/.local/share/chezmoi/*", + command = [[silent! !chezmoi apply --source-path "%"]], +}) + +-- Bigfile detection +vim.filetype.add({ + pattern = { + [".*"] = { + function(path, buf) + return vim.bo[buf].filetype ~= "bigfile" and path and vim.fn.getfsize(path) > vim.g.bigfile_size and "bigfile" + or nil + end, + }, + }, +}) + +-- Bigfile handling +vim.api.nvim_create_autocmd("FileType", { + group = bigfile_group, + pattern = "bigfile", + callback = function(ev) + vim.b.minianimate_disable = true + vim.schedule(function() + vim.bo[ev.buf].syntax = vim.filetype.match({ buf = ev.buf }) or "" + end) + end, +}) diff --git a/lua/config/lazy.lua b/lua/config/lazy.lua new file mode 100644 index 0000000..5d69ed5 --- /dev/null +++ b/lua/config/lazy.lua @@ -0,0 +1,53 @@ +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not (vim.uv or vim.loop).fs_stat(lazypath) then + local lazyrepo = "https://github.com/folke/lazy.nvim.git" + local out = vim.fn.system({ + "git", + "clone", + "--filter=blob:none", + "--branch=stable", + lazyrepo, + lazypath, + }) + if vim.v.shell_error ~= 0 then + vim.api.nvim_echo({ + { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, + { out, "WarningMsg" }, + { "\nPress any key to exit..." }, + }, true, {}) + vim.fn.getchar() + os.exit(1) + end +end +vim.opt.rtp:prepend(lazypath) + +require("lazy").setup({ + spec = { + { import = "config.plugins" }, + }, + performance = { + rtp = { + disabled_plugins = { + "gzip", + "netrwPlugin", + "tarPlugin", + "tohtml", + "tutor", + "zipPlugin", + }, + }, + }, + change_detection = { + notify = false, + enable = true, + }, + ui = { + border = "rounded", + size = { + width = 0.8, + height = 0.8, + }, + }, +}) + +nmap("la", ":Lazy", { desc = "Open Lazy" }) diff --git a/lua/config/lsp/gopls.lua b/lua/config/lsp/gopls.lua new file mode 100644 index 0000000..9af8045 --- /dev/null +++ b/lua/config/lsp/gopls.lua @@ -0,0 +1,15 @@ +return { + settings = { + gopls = { + gofumpt = true, -- https://github.com/mvdan/gofumpt a stricter gofmt + completeUnimported = true, + usePlaceholders = true, + analyses = { + unusedparams = true, + }, + }, + }, + flags = { + debounce_text_changes = 150, -- https://github.com/golang/tools/blob/master/gopls/doc/settings.md#change-detection + }, +} diff --git a/lua/config/lsp/html.lua b/lua/config/lsp/html.lua new file mode 100644 index 0000000..c4f1ba2 --- /dev/null +++ b/lua/config/lsp/html.lua @@ -0,0 +1,3 @@ +return { + filetypes = { "html", "templ" }, +} diff --git a/lua/config/lsp/htmx.lua b/lua/config/lsp/htmx.lua new file mode 100644 index 0000000..c4f1ba2 --- /dev/null +++ b/lua/config/lsp/htmx.lua @@ -0,0 +1,3 @@ +return { + filetypes = { "html", "templ" }, +} diff --git a/lua/config/lsp/jsonls.lua b/lua/config/lsp/jsonls.lua new file mode 100644 index 0000000..5a685df --- /dev/null +++ b/lua/config/lsp/jsonls.lua @@ -0,0 +1,16 @@ +return { + settings = { + json = { + schemas = require("schemastore").json.schemas(), + }, + }, + setup = { + commands = { + Format = { + function() + vim.lsp.buf.range_formatting({}, { 0, 0 }, { vim.fn.line("$"), 0 }) + end, + }, + }, + }, +} diff --git a/lua/config/lsp/lua_ls.lua b/lua/config/lsp/lua_ls.lua new file mode 100644 index 0000000..596bf7b --- /dev/null +++ b/lua/config/lsp/lua_ls.lua @@ -0,0 +1,22 @@ +-- With 2-space indentation +return { + settings = { + Lua = { + format = { + enable = false, + }, + hint = { + enable = false, + arrayIndex = "Disable", + await = true, + paramName = "Disable", + paramType = true, + semicolon = "All", + setType = false, + }, + telemetry = { + enable = false, + }, + }, + }, +} diff --git a/lua/config/lsp/omnisharp.lua b/lua/config/lsp/omnisharp.lua new file mode 100644 index 0000000..1362553 --- /dev/null +++ b/lua/config/lsp/omnisharp.lua @@ -0,0 +1,7 @@ +return { + settings = { + enable_roslyn_analyzers = true, + organize_imports_on_format = true, + enable_import_completion = true, + }, +} diff --git a/lua/config/lsp/yamlls.lua b/lua/config/lsp/yamlls.lua new file mode 100644 index 0000000..47b8d97 --- /dev/null +++ b/lua/config/lsp/yamlls.lua @@ -0,0 +1,21 @@ +return { + settings = { + yaml = { + schemaStore = { + -- You must disable built-in schemaStore support if you want to use + -- this plugin and its advanced options like `ignore`. + enable = false, + -- Avoid TypeError: Cannot read properties of undefined (reading 'length') + url = "", + }, + schemas = require("schemastore").yaml.schemas({ + -- additional schemas (not in the catalog) + extra = { + url = "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/argoproj.io/application_v1alpha1.json", + name = "Argo CD Application", + fileMatch = "argocd-application.yaml", + }, + }), + }, + }, +} diff --git a/lua/config/options.lua b/lua/config/options.lua new file mode 100644 index 0000000..3159e46 --- /dev/null +++ b/lua/config/options.lua @@ -0,0 +1,119 @@ +vim.opt.guifont = "monospace:h17" -- the font used in graphical neovim applications + +-- Make line numbers default +vim.opt.number = true +vim.opt.relativenumber = true + +-- Enable mouse mode, can be useful for resizing splits for example! +vim.opt.mouse = "a" + +-- Don't show the mode, since it's already in status line +vim.opt.showmode = false + +-- Sync clipboard between OS and Neovim. +-- Remove this option if you want your OS clipboard to remain independent. +-- See `:help 'clipboard'` +vim.opt.clipboard = "unnamedplus" + +-- Enable break indent +vim.opt.breakindent = true + +-- Save undo history +vim.opt.undofile = true + +-- Case-insensitive searching UNLESS \C or capital in search +vim.opt.ignorecase = true +vim.opt.smartcase = true + +-- Search as characters are entered +vim.opt.incsearch = true + +-- Highlight search +vim.opt.hlsearch = true + +-- Keep signcolumn on by default +vim.opt.signcolumn = "yes" + +-- Decrease update time +vim.opt.updatetime = 250 +vim.opt.timeoutlen = 300 + +-- Configure how new splits should be opened +vim.opt.splitright = true +vim.opt.splitbelow = true + +-- Sets how neovim will display certain whitespace in the editor. +-- See :help 'list' +-- and :help 'listchars' +vim.opt.list = true +vim.opt.listchars = { tab = "» ", trail = "·", nbsp = "␣" } + +-- Preview substitutions live, as you type! +vim.opt.inccommand = "split" + +-- Show which line your cursor is on +vim.opt.cursorline = true + +-- Minimal number of screen lines to keep above and below the cursor. +vim.opt.scrolloff = 10 + +vim.opt.wrap = false +vim.opt.colorcolumn = "120" + +vim.opt.swapfile = false +vim.opt.backup = false +vim.opt.termguicolors = true + +vim.opt.showmatch = true + +vim.opt.signcolumn = "yes" +vim.opt.isfname:append("@-@") + +-- Give more space for displaying messages. +vim.opt.cmdheight = 1 + +-- Don't pass messages to |ins-completion-menu|. +vim.opt.shortmess:append("c") + +vim.opt.completeopt = { "menuone", "noselect" } +vim.opt.pumheight = 10 +vim.opt.pumblend = 10 + +local group = vim.api.nvim_create_augroup("highlight_yank", { clear = true }) + +vim.api.nvim_create_autocmd("TextYankPost", { + callback = function() + vim.highlight.on_yank({ higroup = "IncSearch", timeout = 50 }) + end, + group = group, +}) + +vim.opt.foldmethod = "indent" +vim.opt.foldnestmax = 3 +vim.opt.foldenable = false + +vim.g.netrw_browse_split = 0 +vim.g.netrw_banner = 0 +vim.g.netrw_winsize = 25 +vim.g.netrw_liststyle = 3 +vim.g.netrw_localrmdir = "rm -r" +vim.g.netrw_browse_split = 0 +vim.g.netrw_banner = 0 +vim.g.netrw_winsize = 25 + +vim.filetype.add({ + extension = { + templ = "templ", + njk = "html", + }, +}) + +vim.filetype.add({ + extension = { rasi = "rasi" }, + pattern = { + [".*/waybar/config"] = "jsonc", + [".*/mako/config"] = "dosini", + [".*/kitty/*.conf"] = "bash", + [".*/hypr/.*%.conf"] = "hyprlang", + }, +}) diff --git a/lua/config/plugins/ai.lua b/lua/config/plugins/ai.lua new file mode 100644 index 0000000..18f6d83 --- /dev/null +++ b/lua/config/plugins/ai.lua @@ -0,0 +1,25 @@ +local M = { + "Exafunction/codeium.vim", + cmd = "CodeiumEnable", + keys = { + { "ce", "CodeiumEnable", desc = "Codeium Enable" }, + }, +} + +M.config = function() + vim.g.codeium_disable_bindings = 1 + + imap("", function() + return vim.fn["codeium#Accept"]() + end, { expr = true, silent = true, desc = "[codeium] Accept completion" }) + + imap("", function() + return vim.fn["codeium#CycleCompletions"](1) + end, { expr = true, silent = true, desc = "[codeium] Cycle completions" }) + + imap("", function() + return vim.fn["codeium#CycleCompletions"](-1) + end, { expr = true, silent = true, desc = "[codeium] Cycle completions" }) +end + +return M diff --git a/lua/config/plugins/alpha.lua b/lua/config/plugins/alpha.lua new file mode 100644 index 0000000..f56209c --- /dev/null +++ b/lua/config/plugins/alpha.lua @@ -0,0 +1,15 @@ +local M = { + "goolord/alpha-nvim", +} + +M.config = function() + local startify = require("alpha.themes.startify") + + startify.section.bottom_buttons.val = { + startify.button("q", "Quit", "q "), -- preserve the quit button + } + + require("alpha").setup(startify.config) +end + +return M diff --git a/lua/config/plugins/blankline.lua b/lua/config/plugins/blankline.lua new file mode 100644 index 0000000..0a6d578 --- /dev/null +++ b/lua/config/plugins/blankline.lua @@ -0,0 +1,31 @@ +local M = { + "lukas-reineke/indent-blankline.nvim", + main = "ibl", +} + +M.config = function() + require("ibl").setup({ + indent = { + char = "│", + tab_char = "│", + }, + scope = { enabled = false }, + exclude = { + filetypes = { + "help", + "alpha", + "dashboard", + "neo-tree", + "Trouble", + "trouble", + "lazy", + "mason", + "notify", + "toggleterm", + "lazyterm", + }, + }, + }) +end + +return M diff --git a/lua/config/plugins/chezmoi.lua b/lua/config/plugins/chezmoi.lua new file mode 100644 index 0000000..5727b86 --- /dev/null +++ b/lua/config/plugins/chezmoi.lua @@ -0,0 +1,35 @@ +return { + { + "alker0/chezmoi.vim", + lazy = false, + init = function() + -- This option is required. + vim.g["chezmoi#use_tmp_buffer"] = true + -- add other options here if needed. + end, + }, + { + "xvzc/chezmoi.nvim", + dependencies = { "nvim-lua/plenary.nvim" }, + config = function() + require("chezmoi").setup({ + -- your configurations + edit = { + watch = true, -- Set true to automatically apply on save. + force = true, -- Set true to force apply. Works only when watch = true. + }, + notification = { + on_open = true, -- vim.notify when start editing chezmoi-managed file. + on_apply = true, -- vim.notify on apply. + }, + }) + vim.api.nvim_create_autocmd({ "BufRead", "BufNewFile" }, { + pattern = { os.getenv("HOME") .. "/.local/share/chezmoi/*" }, + callback = function() + -- invoke with vim.schedule() for better startup time + vim.schedule(require("chezmoi.commands.__edit").watch) + end, + }) + end, + }, +} diff --git a/lua/config/plugins/colorizer.lua b/lua/config/plugins/colorizer.lua new file mode 100644 index 0000000..79c8046 --- /dev/null +++ b/lua/config/plugins/colorizer.lua @@ -0,0 +1,31 @@ +local M = { + "norcalli/nvim-colorizer.lua", + branch = "master", +} + +M.config = function() + require("colorizer").setup({ + filetypes = { + "typescript", + "typescriptreact", + "javascript", + "javascriptreact", + "css", + "html", + "astro", + "lua", + "go", + "golang", + "bash", + "sh", + }, + user_default_options = { + names = false, + rgb_fn = true, + hsl_fn = true, + tailwind = "both", + }, + }) +end + +return M diff --git a/lua/config/plugins/colors.lua b/lua/config/plugins/colors.lua new file mode 100644 index 0000000..c6d991a --- /dev/null +++ b/lua/config/plugins/colors.lua @@ -0,0 +1,32 @@ +local M = { + { + "EdenEast/nightfox.nvim", + enabled = true, + priority = 1000, + config = function() + vim.cmd("colorscheme nightfox") + end, + }, + { + "rose-pine/neovim", + name = "rose-pine", + enabled = false, + priority = 1000, + opts = { + variant = "auto", + dark_variant = "main", + groups = { + border = "muted", + panel = "surface", + error = "love", + hint = "iris", + info = "foam", + }, + }, + config = function() + vim.cmd("colorscheme rose-pine") + end, + }, +} + +return M diff --git a/lua/config/plugins/comments.lua b/lua/config/plugins/comments.lua new file mode 100644 index 0000000..b4317fb --- /dev/null +++ b/lua/config/plugins/comments.lua @@ -0,0 +1,38 @@ +local M = { + { + "numToStr/Comment.nvim", + dependencies = { + "JoosepAlviste/nvim-ts-context-commentstring", + event = "VeryLazy", + }, + }, + { "folke/todo-comments.nvim" }, +} + +M.config = function() + vim.g.skip_ts_context_commentstring_module = true + + require("ts_context_commentstring").setup({ + enable_autocmd = false, + }) + + require("Comment").setup({ + pre_hook = require("ts_context_commentstring.integrations.comment_nvim").create_pre_hook(), + opleader = { + line = "gc", + block = "gC", + }, + mappings = { + basic = true, + }, + }) + + require("todo-comments").setup({ + keywords = { + FUCK = { icon = "󰇷 ", color = "error" }, + SHITTY = { icon = "󰇷 ", color = "error" }, + }, + }) +end + +return M diff --git a/lua/config/plugins/completion.lua b/lua/config/plugins/completion.lua new file mode 100644 index 0000000..c9908ef --- /dev/null +++ b/lua/config/plugins/completion.lua @@ -0,0 +1,42 @@ +local M = { + "saghen/blink.cmp", + dependencies = "rafamadriz/friendly-snippets", + version = "v0.*", +} + +M.config = function() + require("blink.cmp").setup({ + keymap = { + [""] = { + "show", + "show_documentation", + "hide_documentation", + }, + [""] = { "hide", "fallback" }, + [""] = { "hide", "fallback" }, + + [""] = { "select_prev", "fallback" }, + [""] = { "select_next", "fallback" }, + }, + + appearance = { + use_nvim_cmp_as_default = true, + nerd_font_variant = "mono", + }, + + signature = { + enabled = true, + }, + + completion = { + accept = { + create_undo_point = true, + auto_brackets = { + enabled = true, + }, + }, + }, + }) +end + +return M diff --git a/lua/config/plugins/db.lua b/lua/config/plugins/db.lua new file mode 100644 index 0000000..94fe64c --- /dev/null +++ b/lua/config/plugins/db.lua @@ -0,0 +1,18 @@ +local M = { + "tpope/vim-dadbod", + cmd = { + "DBUI", + }, + dependencies = { + "kristijanhusak/vim-dadbod-ui", + "kristijanhusak/vim-dadbod-completion", + }, +} + +M.config = function() + vim.g.db_ui_use_nerd_fonts = 1 + -- g:db_ui_save_location + vim.g.db_ui_save_location = "~/Sync/saved_queries" +end + +return M diff --git a/lua/config/plugins/diagnostics.lua b/lua/config/plugins/diagnostics.lua new file mode 100644 index 0000000..e51e9e3 --- /dev/null +++ b/lua/config/plugins/diagnostics.lua @@ -0,0 +1,121 @@ +local M = { + "folke/trouble.nvim", + branch = "main", +} + +local function setup_keymaps(trouble) + -- Diagnostic navigation + nmap("[d", vim.diagnostic.goto_prev, { desc = "Previous diagnostic" }) + nmap("]d", vim.diagnostic.goto_next, { desc = "Next diagnostic" }) + + -- Trouble specific navigation + nmap("", function() + trouble.previous({ skip_groups = true, jump = true }) + end, { desc = "Previous trouble item" }) + nmap("", function() + trouble.next({ skip_groups = true, jump = true }) + end, { desc = "Next trouble item" }) + + -- Trouble mode toggles + nmap("tt", "TroubleToggle", { desc = "Toggle trouble" }) + nmap("tw", "TroubleToggle workspace_diagnostics", { desc = "Workspace diagnostics" }) + nmap("td", "TroubleToggle document_diagnostics", { desc = "Document diagnostics" }) + nmap("tq", "TroubleToggle quickfix", { desc = "Quickfix list" }) + nmap("tl", "TroubleToggle loclist", { desc = "Location list" }) +end + +local function setup_diagnostic_config() + vim.diagnostic.config({ + virtual_text = { + prefix = "●", + suffix = "", + format = function(diagnostic) + local icons = { + [vim.diagnostic.severity.ERROR] = " ", + [vim.diagnostic.severity.WARN] = " ", + [vim.diagnostic.severity.HINT] = " ", + [vim.diagnostic.severity.INFO] = " ", + } + local icon = icons[diagnostic.severity] or "" + return string.format("%s %s", icon, diagnostic.message) + end, + }, + underline = false, + update_in_insert = false, + signs = { + active = true, + text = { + [vim.diagnostic.severity.ERROR] = "", + [vim.diagnostic.severity.WARN] = "", + [vim.diagnostic.severity.HINT] = "", + [vim.diagnostic.severity.INFO] = "", + }, + }, + float = { + focusable = true, + style = "minimal", + border = "rounded", + source = true, + header = "", + prefix = "", + format = function(diagnostic) + local severity = vim.diagnostic.severity[diagnostic.severity] + return string.format("%s: %s", severity:lower(), diagnostic.message) + end, + }, + severity_sort = true, + }) +end + +function M.config() + local trouble = require("trouble") + + trouble.setup({ + position = "bottom", + height = 10, + width = 50, + icons = true, + mode = "workspace_diagnostics", + fold_open = "", + fold_closed = "", + group = true, + padding = true, + action_keys = { + close = "q", -- close the list + cancel = "", -- cancel the preview and get back to your last window / buffer / cursor + refresh = "r", -- manually refresh + jump = { "", "" }, -- jump to the diagnostic or open / close folds + open_split = { "" }, -- open buffer in new split + open_vsplit = { "" }, -- open buffer in new vsplit + open_tab = { "" }, -- open buffer in new tab + toggle_mode = "m", -- toggle between "workspace" and "document" mode + toggle_preview = "P", -- toggle auto_preview + preview = "p", -- preview the diagnostic location + close_folds = { "zM", "zm" }, -- close all folds + open_folds = { "zR", "zr" }, -- open all folds + toggle_fold = { "zA", "za" }, -- toggle fold of current file + previous = "k", -- previous item + next = "j", -- next item + }, + auto_preview = true, + auto_fold = false, + auto_jump = { "lsp_definitions" }, + signs = { + -- Icons / text used for a diagnostic + error = "", + warning = "", + hint = "", + information = "", + other = "", + }, + use_diagnostic_signs = false, + }) + + -- Setup keymaps + setup_keymaps(trouble) + + -- Setup diagnostic configuration + setup_diagnostic_config() +end + +return M diff --git a/lua/config/plugins/env.lua b/lua/config/plugins/env.lua new file mode 100644 index 0000000..8687cc6 --- /dev/null +++ b/lua/config/plugins/env.lua @@ -0,0 +1,59 @@ +local M = { + { + "laytan/cloak.nvim", + config = function() + require("cloak").setup({ + cloak_character = "*", + highlight_group = "Comment", + patterns = { + { + file_pattern = { + ".env*", + "wrangler.toml", + ".dev.vars", + }, + cloak_pattern = "=.+", + }, + }, + }) + + nmap("cc", ":CloakToggle") + end, + }, + { + "philosofonusus/ecolog.nvim", + keys = { + { "ge", "EcologGoto", desc = "Go to env file" }, + { "ep", "EcologPeek", desc = "Ecolog peek variable" }, + { "es", "EcologSelect", desc = "Switch env file" }, + }, + -- Lazy loading is done internally + lazy = false, + opts = { + integrations = { + blink_cmp = true, + }, + -- Enables shelter mode for sensitive values + shelter = { + configuration = { + partial_mode = false, -- false by default, disables partial mode, for more control check out shelter partial mode + mask_char = "*", -- Character used for masking + }, + modules = { + cmp = true, -- Mask values in completion + peek = false, -- Mask values in peek view + files = false, -- Mask values in files + telescope = false, -- Mask values in telescope + }, + }, + -- true by default, enables built-in types (database_url, url, etc.) + types = true, + path = vim.fn.getcwd(), -- Path to search for .env files + preferred_environment = "development", -- Optional: prioritize specific env files + }, + }, +} + +M.config = function() end + +return M diff --git a/lua/config/plugins/flash.lua b/lua/config/plugins/flash.lua new file mode 100644 index 0000000..06b9f0b --- /dev/null +++ b/lua/config/plugins/flash.lua @@ -0,0 +1,22 @@ +local M = { + "folke/flash.nvim", + opts = { + jump = { + autojump = true, + }, + modes = { + char = { + jump_labels = true, + multi_line = false, + }, + }, + }, +} + +M.config = function() + nmap("ff", function() + require("flash").jump() + end, { desc = "Flash" }) +end + +return M diff --git a/lua/config/plugins/fold.lua b/lua/config/plugins/fold.lua new file mode 100644 index 0000000..188859b --- /dev/null +++ b/lua/config/plugins/fold.lua @@ -0,0 +1,53 @@ +return { + "bbjornstad/pretty-fold.nvim", + enabled = true, + config = function() + local global_setup = { + sections = { + left = { "content" }, + right = { + " ", + function() + return ("[%dL]"):format(vim.v.foldend - vim.v.foldstart) + end, + "[", + "percentage", + "]", + }, + }, + matchup_patterns = { + { "{", "}" }, + { "%(", ")" }, -- % to escape lua pattern char + { "%[", "]" }, -- % to escape lua pattern char + }, + -- add_close_pattern = true, + process_comment_signs = ({ "delete", "spaces", false })[2], + } + + local function ft_setup(lang, options) -- {{{ + local opts = vim.tbl_deep_extend("force", global_setup, options) + -- combine global and ft specific matchup_patterns + if opts and opts.matchup_patterns and global_setup.matchup_patterns then + opts.matchup_patterns = vim.list_extend(opts.matchup_patterns, global_setup.matchup_patterns) + end + require("pretty-fold").ft_setup(lang, opts) + end -- }}} + + require("pretty-fold").setup(global_setup) + + ft_setup("lua", { -- {{{ + matchup_patterns = { + { "^%s*do$", "end" }, -- do ... end blocks + { "^%s*if", "end" }, -- if ... end + { "^%s*for", "end" }, -- for + { "function[^%(]*%(", "end" }, -- 'function( or 'function ('' + }, + }) -- }}} + + ft_setup("vim", { -- {{{ + matchup_patterns = { + { "^%s*function!?[^%(]*%(", "endfunction" }, + }, + }) -- }}} + end, +} diff --git a/lua/config/plugins/format.lua b/lua/config/plugins/format.lua new file mode 100644 index 0000000..13f8258 --- /dev/null +++ b/lua/config/plugins/format.lua @@ -0,0 +1,38 @@ +return { + "stevearc/conform.nvim", + event = { + "BufReadPre", + "BufNewFile", + }, + config = function() + require("conform").setup({ + formatters_by_ft = { + graphql = { "prettierd", "prettier" }, + njk = { "prettierd", "prettier" }, + html = { "prettierd", "prettier" }, + typescript = { "prettierd", "prettier" }, + lua = { "stylua" }, + javascript = { "prettierd", "prettier", stop_after_first = true }, + json = { "prettierd", "prettier" }, + sh = { "shfmt" }, + bash = { "shfmt" }, + tex = { "latexindent" }, + go = { "gofumpt", "goimports-reviser", "golines" }, + cs = { "csharpier" }, + templ = { "templ" }, + }, + formatters = { + csharpier = { + command = "dotnet-csharpier", + args = { "--write-stdout" }, + }, + }, + format_on_save = { + timeout_ms = 500, + lsp_fallback = true, + async = false, + }, + notify_on_error = false, + }) + end, +} diff --git a/lua/config/plugins/fzf.lua b/lua/config/plugins/fzf.lua new file mode 100644 index 0000000..0b2684d --- /dev/null +++ b/lua/config/plugins/fzf.lua @@ -0,0 +1,72 @@ +local M = { + "ibhagwan/fzf-lua", +} + +M.config = function() + local config = require("fzf-lua.config") + local actions = require("trouble.sources.fzf").actions + config.defaults.actions.files["ctrl-q"] = actions.open + + local fzf_lua = require("fzf-lua") + + -- Basic fzf-lua setup + fzf_lua.setup({ + layout = "fzf-vim", + keymap = { + fzf = { + ["CTRL-Q"] = "select-all+accept", + }, + }, + grep = { + fzf_opts = { + ["--history"] = vim.fn.stdpath("data") .. "/fzf-lua-grep-history", + }, + }, + }) + + nmap("K", vim.lsp.buf.hover, { desc = "Hover Documentation" }) + nmap("gd", function() + fzf_lua.lsp_definitions({ jump_to_single_result = true }) + end, { desc = "Goto Definition" }) + nmap("gr", function() + fzf_lua.lsp_references({ ignore_current_line = true }) + end, { desc = "Goto References" }) + nmap("gi", function() + fzf_lua.lsp_implementations({ jump_to_single_result = true }) + end, { desc = "Goto Implementation" }) + nmap("D", fzf_lua.lsp_typedefs, { desc = "Type Definition" }) + nmap("ca", fzf_lua.lsp_code_actions, { desc = "Code Action" }) + nmap("ds", fzf_lua.lsp_document_symbols, { desc = "Document Symbols" }) + nmap("ws", fzf_lua.lsp_workspace_symbols, { desc = "Workspace Symbols" }) + nmap("ic", fzf_lua.lsp_incoming_calls, { desc = "Incoming Calls" }) + nmap("oc", fzf_lua.lsp_outgoing_calls, { desc = "Outgoing Calls" }) + + -- keys = { + nmap("/", function() + fzf_lua.files({ + cwd_prompt = false, + silent = true, + }) + end, { desc = "Find Files" }) + nmap(";", fzf_lua.buffers, { desc = "Find Buffers" }) + nmap("gf", fzf_lua.live_grep, { desc = "Find Live Grep" }) + nmap("sb", fzf_lua.grep_curbuf, { desc = "Search Current Buffer" }) + nmap("gw", fzf_lua.grep_cword, { desc = "Search word under cursor" }) + nmap("gW", fzf_lua.grep_cWORD, { desc = "Search WORD under cursor" }) + nmap("sk", fzf_lua.keymaps, { desc = "Search Keymaps" }) + nmap("sh", fzf_lua.help_tags, { desc = "Search help" }) + + -- Automatic sizing of height/width of vim.ui.select + fzf_lua.register_ui_select(function(_, items) + local min_h, max_h = 0.60, 0.80 + local h = (#items + 4) / vim.o.lines + if h < min_h then + h = min_h + elseif h > max_h then + h = max_h + end + return { winopts = { height = h, width = 0.80, row = 0.40 } } + end) +end + +return M diff --git a/lua/config/plugins/git.lua b/lua/config/plugins/git.lua new file mode 100644 index 0000000..6c71b9b --- /dev/null +++ b/lua/config/plugins/git.lua @@ -0,0 +1,47 @@ +local M = { + { "lewis6991/gitsigns.nvim" }, + { + "kdheepak/lazygit.nvim", + cmd = { + "LazyGit", + "LazyGitConfig", + "LazyGitCurrentFile", + "LazyGitFilter", + "LazyGitFilterCurrentFile", + }, + dependencies = { + "nvim-lua/plenary.nvim", + }, + keys = { + { "lg", "LazyGit", desc = "LazyGit" }, + }, + }, + { + + "ruifm/gitlinker.nvim", + }, +} + +M.config = function() + require("gitsigns").setup({ + current_line_blame_formatter = ", - ", + current_line_blame = true, + signs = { + add = { text = icons.ui.BoldLineMiddle }, + change = { text = icons.ui.BoldLineDashedMiddle }, + delete = { text = icons.ui.TriangleShortArrowRight }, + topdelete = { text = icons.ui.TriangleShortArrowRight }, + changedelete = { text = icons.ui.BoldLineMiddle }, + }, + }) + + require("gitlinker").setup({ + message = false, + console_log = false, + }) + + nmap("gy", "lua require('gitlinker').get_buf_range_url('n')") + namp("gY", "lua require('gitlinker').get_buf_range_url('n', 'blame')") +end + +return M diff --git a/lua/config/plugins/harpoon.lua b/lua/config/plugins/harpoon.lua new file mode 100644 index 0000000..532ac64 --- /dev/null +++ b/lua/config/plugins/harpoon.lua @@ -0,0 +1,35 @@ +local M = { + "ThePrimeagen/harpoon", + branch = "harpoon2", + + dependencies = { + "nvim-lua/plenary.nvim", + }, +} + +M.config = function() + local harpoon = require("harpoon") + + harpoon:setup({ + settings = { + save_on_toggle = true, + sync_on_ui_close = true, + }, + }) + + nmap("a", function() + harpoon:list():add() + end, { desc = "Harpoon: Append" }) + + nmap("h", function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = "Harpoon: Toggle Quick Menu" }) + + for i = 1, 4 do + nmap("" .. i, function() + harpoon:list():select(i) + end, { desc = "Harpoon: Select " .. i }) + end +end + +return M diff --git a/lua/config/plugins/lastplace.lua b/lua/config/plugins/lastplace.lua new file mode 100644 index 0000000..0ca0acc --- /dev/null +++ b/lua/config/plugins/lastplace.lua @@ -0,0 +1,3 @@ +local M = { "ethanholz/nvim-lastplace" } + +return M diff --git a/lua/config/plugins/lint.lua b/lua/config/plugins/lint.lua new file mode 100644 index 0000000..376a867 --- /dev/null +++ b/lua/config/plugins/lint.lua @@ -0,0 +1,44 @@ +local M = { + "mfussenegger/nvim-lint", + event = { + "BufReadPre", + "BufNewFile", + }, +} + +M.config = function() + local lint = require("lint") + + lint.linters_by_ft = { + javascript = { + "eslint_d", + }, + typescript = { + "eslint_d", + }, + javascriptreact = { + "eslint_d", + }, + typescriptreact = { + "eslint_d", + }, + -- go = { + -- "revive", + -- }, + } + + local lint_augroup = vim.api.nvim_create_augroup("lint", { clear = true }) + + vim.api.nvim_create_autocmd({ "BufWritePost", "InsertLeave", "BufEnter" }, { + group = lint_augroup, + callback = function() + lint.try_lint() + end, + }) + + nmap("ll", function() + lint.try_lint() + end, { desc = "Trigger linting for current file" }) +end + +return M diff --git a/lua/config/plugins/lsp/extras/gopher.lua b/lua/config/plugins/lsp/extras/gopher.lua new file mode 100644 index 0000000..20ebac6 --- /dev/null +++ b/lua/config/plugins/lsp/extras/gopher.lua @@ -0,0 +1,13 @@ +return { + "olexsmir/gopher.nvim", + ft = "go", + config = function(_, opts) + require("gopher").setup(opts) + vim.keymap.set("n", "gmt", ":GoMod tidy", { + desc = "[Go] Tidy", + }) + end, + build = function() + vim.cmd([[silent! GoInstallDeps]]) + end, +} diff --git a/lua/config/plugins/lsp/extras/lazydev.lua b/lua/config/plugins/lsp/extras/lazydev.lua new file mode 100644 index 0000000..1d80752 --- /dev/null +++ b/lua/config/plugins/lsp/extras/lazydev.lua @@ -0,0 +1,9 @@ +return { + "folke/lazydev.nvim", + ft = "lua", + opts = { + library = { + { path = "${3rd}/luv/library", words = { "vim%.uv" } }, + }, + }, +} diff --git a/lua/config/plugins/lsp/extras/typescript.lua b/lua/config/plugins/lsp/extras/typescript.lua new file mode 100644 index 0000000..84146bb --- /dev/null +++ b/lua/config/plugins/lsp/extras/typescript.lua @@ -0,0 +1,10 @@ +return { + "yioneko/nvim-vtsls", + dependencies = { + "dmmulroy/ts-error-translator.nvim", + }, + config = function() + require("ts-error-translator").setup() + end, + ft = { "typescript", "javascript", "jsx", "tsx", "json" }, +} diff --git a/lua/config/plugins/lsp/init.lua b/lua/config/plugins/lsp/init.lua new file mode 100644 index 0000000..bc3cfd3 --- /dev/null +++ b/lua/config/plugins/lsp/init.lua @@ -0,0 +1,139 @@ +local M = { + "neovim/nvim-lspconfig", + dependencies = { + "saghen/blink.cmp", + "williamboman/mason.nvim", + "williamboman/mason-lspconfig.nvim", + require("config.plugins.lsp.extras.lazydev"), + require("config.plugins.lsp.extras.gopher"), + require("config.plugins.lsp.extras.typescript"), + }, +} + +local function setup_autocommands(client, bufnr) + if client.server_capabilities.documentHighlightProvider then + local group = vim.api.nvim_create_augroup("LSPDocumentHighlight", { clear = true }) + vim.api.nvim_create_autocmd({ "CursorHold", "CursorHoldI" }, { + group = group, + buffer = bufnr, + callback = vim.lsp.buf.document_highlight, + }) + vim.api.nvim_create_autocmd({ "CursorMoved", "CursorMovedI" }, { + group = group, + buffer = bufnr, + callback = vim.lsp.buf.clear_references, + }) + end +end + +local function setup_keymaps(bufnr) + local keymaps = { + { "", vim.lsp.buf.signature_help, "Signature Help" }, + { "cw", vim.lsp.buf.rename, "Rename" }, + { "r", vim.lsp.buf.rename, "Rename" }, + { "vd", vim.diagnostic.open_float, "Open Diagnostics" }, + { "lr", ":LspRestart", "Restart LSP" }, + { "li", ":LspInfo", "LSP Info" }, + } + + for _, map in ipairs(keymaps) do + nmap(map[1], map[2], { + buffer = bufnr, + desc = map[3], + }) + end +end + +local function on_attach(client, bufnr) + setup_autocommands(client, bufnr) + setup_keymaps(bufnr) +end + +local BORDER = "rounded" + +local handlers = { + ["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = BORDER }), + ["textDocument/signatureHelp"] = vim.lsp.with(vim.lsp.handlers.signature_help, { border = BORDER }), +} + +function M.config() + require("mason").setup({ + max_concurrent_installers = 4, + }) + + -- Ensure all tools are installed + local ensure_installed = { + -- LSP servers + "gopls", + "graphql-language-service-cli", + "html-lsp", + "htmx-lsp", + "json-lsp", + "lua-language-server", + "omnisharp", + "vtsls", + "yaml-language-server", + + -- Formatters + "prettierd", + "shfmt", + "stylua", + "latexindent", + + -- Additional tools + "eslint_d", + "templ", + } + + local registry = require("mason-registry") + for _, tool in ipairs(ensure_installed) do + if not registry.is_installed(tool) then + vim.cmd("MasonInstall " .. tool) + end + end + + require("mason-lspconfig").setup({ + automatic_installation = true, + ensure_installed = { + "gopls", + "html", + "htmx", + "jsonls", + "lua_ls", + "omnisharp", + "yamlls", + "graphql", + }, + handlers = { + function(server_name) + local capabilities = require("blink.cmp").get_lsp_capabilities() + + -- Enable folding capabilities + capabilities.textDocument.foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + } + + local base_opts = { + on_attach = on_attach, + capabilities = capabilities, + handlers = handlers, + flags = { + debounce_text_changes = 150, + }, + } + + -- Try to load server-specific configuration + local ok, server_opts = pcall(require, "config.plugins.lsp.servers." .. server_name) + if ok then + base_opts = vim.tbl_deep_extend("force", base_opts, server_opts) + end + + -- Set up the LSP server + require("lspconfig")[server_name].setup(base_opts) + end, + }, + }) +end + +return M diff --git a/lua/config/plugins/lsp/servers/gopls.lua b/lua/config/plugins/lsp/servers/gopls.lua new file mode 100644 index 0000000..9af8045 --- /dev/null +++ b/lua/config/plugins/lsp/servers/gopls.lua @@ -0,0 +1,15 @@ +return { + settings = { + gopls = { + gofumpt = true, -- https://github.com/mvdan/gofumpt a stricter gofmt + completeUnimported = true, + usePlaceholders = true, + analyses = { + unusedparams = true, + }, + }, + }, + flags = { + debounce_text_changes = 150, -- https://github.com/golang/tools/blob/master/gopls/doc/settings.md#change-detection + }, +} diff --git a/lua/config/plugins/lsp/servers/html.lua b/lua/config/plugins/lsp/servers/html.lua new file mode 100644 index 0000000..c4f1ba2 --- /dev/null +++ b/lua/config/plugins/lsp/servers/html.lua @@ -0,0 +1,3 @@ +return { + filetypes = { "html", "templ" }, +} diff --git a/lua/config/plugins/lsp/servers/htmx.lua b/lua/config/plugins/lsp/servers/htmx.lua new file mode 100644 index 0000000..c4f1ba2 --- /dev/null +++ b/lua/config/plugins/lsp/servers/htmx.lua @@ -0,0 +1,3 @@ +return { + filetypes = { "html", "templ" }, +} diff --git a/lua/config/plugins/lsp/servers/jsonls.lua b/lua/config/plugins/lsp/servers/jsonls.lua new file mode 100644 index 0000000..5a685df --- /dev/null +++ b/lua/config/plugins/lsp/servers/jsonls.lua @@ -0,0 +1,16 @@ +return { + settings = { + json = { + schemas = require("schemastore").json.schemas(), + }, + }, + setup = { + commands = { + Format = { + function() + vim.lsp.buf.range_formatting({}, { 0, 0 }, { vim.fn.line("$"), 0 }) + end, + }, + }, + }, +} diff --git a/lua/config/plugins/lsp/servers/lua_ls.lua b/lua/config/plugins/lsp/servers/lua_ls.lua new file mode 100644 index 0000000..45d8e6a --- /dev/null +++ b/lua/config/plugins/lsp/servers/lua_ls.lua @@ -0,0 +1,21 @@ +return { + settings = { + Lua = { + format = { + enable = false, + }, + hint = { + enable = false, + arrayIndex = "Disable", -- "Enable" | "Auto" | "Disable" + await = true, + paramName = "Disable", -- "All" | "Literal" | "Disable" + paramType = true, + semicolon = "All", -- "All" | "SameLine" | "Disable" + setType = false, + }, + telemetry = { + enable = false, + }, + }, + }, +} diff --git a/lua/config/plugins/lsp/servers/omnisharp.lua b/lua/config/plugins/lsp/servers/omnisharp.lua new file mode 100644 index 0000000..1362553 --- /dev/null +++ b/lua/config/plugins/lsp/servers/omnisharp.lua @@ -0,0 +1,7 @@ +return { + settings = { + enable_roslyn_analyzers = true, + organize_imports_on_format = true, + enable_import_completion = true, + }, +} diff --git a/lua/config/plugins/lsp/servers/yamlls.lua b/lua/config/plugins/lsp/servers/yamlls.lua new file mode 100644 index 0000000..47b8d97 --- /dev/null +++ b/lua/config/plugins/lsp/servers/yamlls.lua @@ -0,0 +1,21 @@ +return { + settings = { + yaml = { + schemaStore = { + -- You must disable built-in schemaStore support if you want to use + -- this plugin and its advanced options like `ignore`. + enable = false, + -- Avoid TypeError: Cannot read properties of undefined (reading 'length') + url = "", + }, + schemas = require("schemastore").yaml.schemas({ + -- additional schemas (not in the catalog) + extra = { + url = "https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/argoproj.io/application_v1alpha1.json", + name = "Argo CD Application", + fileMatch = "argocd-application.yaml", + }, + }), + }, + }, +} diff --git a/lua/config/plugins/markdown.lua b/lua/config/plugins/markdown.lua new file mode 100644 index 0000000..292431f --- /dev/null +++ b/lua/config/plugins/markdown.lua @@ -0,0 +1,20 @@ +local M = { + { + "iamcco/markdown-preview.nvim", + cmd = { "MarkdownPreviewToggle", "MarkdownPreview", "MarkdownPreviewStop" }, + build = "cd app && yarn install", + init = function() + vim.g.mkdp_filetypes = { "markdown" } + end, + ft = { "markdown" }, + }, + { + "MeanderingProgrammer/render-markdown.nvim", + dependencies = { "nvim-treesitter/nvim-treesitter", "echasnovski/mini.nvim" }, -- if you use the mini.nvim suite + opts = {}, + }, +} + +M.config = function() end + +return M diff --git a/lua/config/plugins/mini.lua b/lua/config/plugins/mini.lua new file mode 100644 index 0000000..3781553 --- /dev/null +++ b/lua/config/plugins/mini.lua @@ -0,0 +1,24 @@ +return { + "echasnovski/mini.nvim", + config = function() + require("mini.icons").setup() + + -- - va) - [V]isually select [A]round [)]paren + -- - yinq - [Y]ank [I]nside [N]ext [Q]uote + -- - ci' - [C]hange [I]nside [']quote + require("mini.ai").setup({ n_lines = 500 }) + + -- Add/delete/replace surroundings (brackets, quotes, etc.) + -- + -- - saiw) - [S]urround [A]dd [I]nner [W]ord [)]Paren + -- - sd' - [S]urround [D]elete [']quotes + -- - sr)' - [S]urround [R]eplace [)] ['] + require("mini.surround").setup({}) + end, + init = function() + package.preload["nvim-web-devicons"] = function() + require("mini.icons").mock_nvim_web_devicons() + return package.loaded["nvim-web-devicons"] + end + end, +} diff --git a/lua/config/plugins/navic.lua b/lua/config/plugins/navic.lua new file mode 100644 index 0000000..763461c --- /dev/null +++ b/lua/config/plugins/navic.lua @@ -0,0 +1,19 @@ +local M = { + "SmiteshP/nvim-navic", +} + +M.config = function() + require("nvim-navic").setup({ + icons = icons.kind, + highlight = true, + lsp = { + auto_attach = true, + }, + click = true, + separator = " " .. icons.ui.ChevronRight .. " ", + depth_limit = 0, + depth_limit_indicator = "..", + }) +end + +return M diff --git a/lua/config/plugins/noice.lua b/lua/config/plugins/noice.lua new file mode 100644 index 0000000..ea54ddd --- /dev/null +++ b/lua/config/plugins/noice.lua @@ -0,0 +1,93 @@ +local M = { + "folke/noice.nvim", + event = "VeryLazy", + dependencies = { + "MunifTanjim/nui.nvim", + }, +} + +M.config = function() + local noice = require("noice") + + noice.setup({ + routes = { + { + filter = { + event = "msg_show", + any = { + { find = "%d+L, %d+B" }, + { find = "; after #%d+" }, + { find = "; before #%d+" }, + { find = "%d fewer lines" }, + { find = "%d more lines" }, + }, + }, + opts = { skip = true }, + }, + }, + lsp = { + progress = { enabled = true }, + override = { + ["vim.lsp.util.convert_input_to_markdown_lines"] = true, + ["vim.lsp.util.stylize_markdown"] = true, + ["cmp.entry.get_documentation"] = true, + }, + hover = { silent = true }, + signature = { + auto_open = { throttle = vim.api.nvim_get_option_value("updatetime", { scope = "global" }) }, + }, + }, + cmdline = { + format = { + cmdline = { icon = "" }, + search_down = { icon = " " }, + search_up = { icon = " " }, + }, + }, + messages = { + enabled = false, + }, + popupmenu = { enabled = true }, + presets = { + bottom_search = true, + long_message_to_split = true, + lsp_doc_border = true, + }, + throttle = 1000, + views = { + split = { + enter = true, + size = "25%", + win_options = { + signcolumn = "no", + number = false, + relativenumber = false, + list = false, + wrap = false, + }, + }, + popup = { border = { style = "rounded" } }, + hover = { + border = { style = "rounded" }, + position = { row = 2, col = 2 }, + }, + mini = { + timeout = 3000, + position = { row = -2 }, + border = { style = "rounded" }, + win_options = { + winblend = vim.api.nvim_get_option_value("winblend", { scope = "global" }), + }, + }, + cmdline_popup = { border = { style = "rounded" } }, + confirm = { + border = { + style = "rounded", + padding = { 0, 1 }, + }, + }, + }, + }) +end + +return M diff --git a/lua/config/plugins/oil.lua b/lua/config/plugins/oil.lua new file mode 100644 index 0000000..25f0b49 --- /dev/null +++ b/lua/config/plugins/oil.lua @@ -0,0 +1,69 @@ +local M = { + "stevearc/oil.nvim", +} + +M.config = function() + local function max_height() + local height = vim.fn.winheight(0) + if height >= 40 then + return 30 + elseif height >= 30 then + return 20 + else + return 10 + end + end + + require("oil").setup({ + keymaps = { + [""] = false, + ["g?"] = "actions.show_help", + [""] = "actions.select", + [""] = "actions.select_vsplit", + [""] = "actions.select_split", + [""] = "actions.select_tab", + [""] = "actions.close", + [""] = "actions.refresh", + ["-"] = "actions.parent", + ["_"] = "actions.open_cwd", + ["`"] = "actions.cd", + ["~"] = "actions.tcd", + ["g."] = "actions.toggle_hidden", + }, + -- Set to false if you still want to use netrw. + default_file_explorer = true, + delete_to_trash = true, + + -- Skip the confirmation popup for simple operations (:help oil.skip_confirm_for_simple_edits) + skip_confirm_for_simple_edits = true, + + view_options = { + natural_order = true, + -- Show files and directories that start with "." + show_hidden = false, + -- This function defines what is considered a "hidden" file + is_hidden_file = function(name) + local ignore_folders = { "node_modules", "dist", "build", "coverage" } + return vim.startswith(name, ".") or vim.tbl_contains(ignore_folders, name) + end, + wrap = true, + }, + + -- Configuration for the floating window in oil.open_float + float = { + padding = 2, + max_width = 120, + max_height = max_height(), + border = "rounded", + win_options = { + winblend = 0, + }, + }, + }) + + vim.keymap.set("n", "-", function() + require("oil").open() + end) +end + +return M diff --git a/lua/config/plugins/schema.lua b/lua/config/plugins/schema.lua new file mode 100644 index 0000000..14b97e2 --- /dev/null +++ b/lua/config/plugins/schema.lua @@ -0,0 +1,7 @@ +local M = { + "b0o/schemastore.nvim", +} + +M.config = function() end + +return M diff --git a/lua/config/plugins/scroll.lua b/lua/config/plugins/scroll.lua new file mode 100644 index 0000000..79f44d8 --- /dev/null +++ b/lua/config/plugins/scroll.lua @@ -0,0 +1,41 @@ +local M = { + "karb94/neoscroll.nvim", +} + +M.config = function() + require("neoscroll").setup({ + -- All these keys will be mapped to their corresponding default scrolling animation + mappings = { "", "" }, + hide_cursor = true, -- Hide cursor while scrolling + stop_eof = true, -- Stop at when scrolling downwards + respect_scrolloff = false, -- Stop scrolling when the cursor reaches the scrolloff margin of the file + cursor_scrolls_alone = true, -- The cursor will keep on scrolling even if the window cannot scroll further + easing_function = nil, -- Default easing function + pre_hook = nil, -- Function to run before the scrolling animation starts + post_hook = nil, -- Function to run after the scrolling animation ends + performance_mode = false, -- Disable "Performance Mode" on all buffers. + }) + local neoscroll = require("neoscroll") + + local t = { + [""] = function() + neoscroll.ctrl_u({ duration = 50 }) + end, + [""] = function() + neoscroll.ctrl_u({ duration = 50 }) + end, + [""] = function() + neoscroll.ctrl_d({ duration = 50 }) + end, + [""] = function() + neoscroll.ctrl_d({ duration = 50 }) + end, + } + + local modes = { "n", "v", "x" } + for key, func in pairs(t) do + vim.keymap.set(modes, key, func) + end +end + +return M diff --git a/lua/config/plugins/snips.lua b/lua/config/plugins/snips.lua new file mode 100644 index 0000000..930e81f --- /dev/null +++ b/lua/config/plugins/snips.lua @@ -0,0 +1,20 @@ +local M = { + + "L3MON4D3/LuaSnip", + dependencies = { + "rafamadriz/friendly-snippets", + -- "saadparwaiz1/cmp_luasnip", + }, + build = "make install_jsregexp", +} + +M.config = function() + require("luasnip.loaders.from_vscode").lazy_load() + require("luasnip.loaders.from_vscode").lazy_load({ paths = vim.fn.stdpath("config") .. "/lua/plugins/snippets/" }) + require("luasnip").config.set_config({ + history = true, + updateevents = "TextChanged,TextChangedI", + }) +end + +return M diff --git a/lua/config/plugins/spectre.lua b/lua/config/plugins/spectre.lua new file mode 100644 index 0000000..37eca14 --- /dev/null +++ b/lua/config/plugins/spectre.lua @@ -0,0 +1,22 @@ +local M = { + "nvim-pack/nvim-spectre", +} + +M.config = function() + require("spectre").setup() + + nmap("S", 'lua require("spectre").toggle()', { + desc = "Toggle Spectre", + }) + nmap("sw", 'lua require("spectre").open_visual({select_word=true})', { + desc = "[Spectre] Search current word", + }) + nmap("sw", 'lua require("spectre").open_visual()', { + desc = "[Spectre] Search current word", + }) + nmap("sp", 'lua require("spectre").open_file_search({select_word=true})', { + desc = "[Spectre] Search on current file", + }) +end + +return M diff --git a/lua/config/plugins/status.lua b/lua/config/plugins/status.lua new file mode 100644 index 0000000..13866de --- /dev/null +++ b/lua/config/plugins/status.lua @@ -0,0 +1,66 @@ +local M = { + "nvim-lualine/lualine.nvim", +} + +function M.config() + local lualine = require("lualine") + + local mode = "mode" + local filetype = { "filetype", icon_only = true } + + local diagnostics = { + "diagnostics", + sources = { "nvim_diagnostic" }, + sections = { "error", "warn", "info", "hint" }, + symbols = { + error = icons.diagnostics.Error, + hint = icons.diagnostics.Hint, + info = icons.diagnostics.Info, + warn = icons.diagnostics.Warning, + }, + colored = true, + update_in_insert = false, + always_visible = false, + } + + local diff = { + "diff", + source = function() + local gitsigns = vim.b.gitsigns_status_dict + if gitsigns then + return { + added = gitsigns.added, + modified = gitsigns.changed, + removed = gitsigns.removed, + } + end + end, + symbols = { + added = icons.git.LineAdded .. " ", + modified = icons.git.LineModified .. " ", + removed = icons.git.LineRemoved .. " ", + }, + colored = true, + always_visible = false, + } + + lualine.setup({ + options = { + theme = "auto", + globalstatus = true, + section_separators = "", + component_separators = "", + disabled_filetypes = { statusline = { "dashboard", "lazy", "alpha" } }, + }, + sections = { + lualine_a = { mode }, + lualine_b = {}, + lualine_c = { "filename" }, + lualine_x = { diff, diagnostics, filetype }, + lualine_y = {}, + lualine_z = {}, + }, + }) +end + +return M diff --git a/lua/config/plugins/sudo.lua b/lua/config/plugins/sudo.lua new file mode 100644 index 0000000..2c44188 --- /dev/null +++ b/lua/config/plugins/sudo.lua @@ -0,0 +1,4 @@ +return { + "lambdalisue/suda.vim", + lazy = false, +} diff --git a/lua/config/plugins/tpope.lua b/lua/config/plugins/tpope.lua new file mode 100644 index 0000000..c7110c6 --- /dev/null +++ b/lua/config/plugins/tpope.lua @@ -0,0 +1,9 @@ +local M = { + "tpope/vim-sleuth", +} + +M.config = function() + vim.g.sleuth_automatic = 1 +end + +return M diff --git a/lua/config/plugins/treesitter.lua b/lua/config/plugins/treesitter.lua new file mode 100644 index 0000000..9995963 --- /dev/null +++ b/lua/config/plugins/treesitter.lua @@ -0,0 +1,24 @@ +return { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function() + require("nvim-treesitter.configs").setup({ + ensure_installed = { "c", "lua", "typescript", "go", "vim", "vimdoc", "query", "markdown", "markdown_inline" }, + ignore_install = {}, + modules = {}, + sync_install = false, + auto_install = false, + highlight = { + enable = true, + disable = function(_, buf) + local max_filesize = 100 * 1024 + local ok, stats = pcall(vim.uv.fs_stat, vim.api.nvim_buf_get_name(buf)) + if ok and stats and stats.size > max_filesize then + return true + end + end, + additional_vim_regex_highlighting = false, + }, + }) + end, +} diff --git a/lua/config/plugins/ui.lua b/lua/config/plugins/ui.lua new file mode 100644 index 0000000..834805e --- /dev/null +++ b/lua/config/plugins/ui.lua @@ -0,0 +1,9 @@ +local M = { + "stevearc/dressing.nvim", +} + +M.config = function() + require("dressing").setup() +end + +return M diff --git a/lua/config/plugins/undo.lua b/lua/config/plugins/undo.lua new file mode 100644 index 0000000..47311cb --- /dev/null +++ b/lua/config/plugins/undo.lua @@ -0,0 +1,12 @@ +local M = { + "mbbill/undotree", + cmd = { + "UndotreeToggle", + }, +} + +M.config = function() + vim.opt.undodir = vim.fn.expand("~/.config/undodir") +end + +return M diff --git a/lua/config/remap.lua b/lua/config/remap.lua new file mode 100644 index 0000000..24709a5 --- /dev/null +++ b/lua/config/remap.lua @@ -0,0 +1,91 @@ +nmap("", "") + +vim.g.mapleader = " " +vim.g.maplocalleader = " " + +nmap("Y", "yy") +nmap("", "", { desc = "Switch to last buffer" }) +nmap("", ":vertical resize +3", { desc = "Resize window" }) +nmap("", ":vertical resize -3", { desc = "Resize window" }) +nmap("", ":resize +3", { desc = "Resize window" }) +nmap("", ":resize -3", { desc = "Resize window" }) + +vim.cmd([[ + nnoremap + nnoremap +]]) + +nmap("n", "nzz", { desc = "Move to next search match" }) +nmap("N", "Nzz", { desc = "Move to previous search match" }) +nmap("*", "*zz", { desc = "Move to next search match" }) +nmap("#", "#zz", { desc = "Move to previous search match" }) +nmap("g*", "g*zz", { desc = "Move to next search match" }) +nmap("g#", "g#zz", { desc = "Move to previous search match" }) + +-- stay in indent mode +vmap("<", "", ">gv", { desc = "Indent right" }) + +-- keep register after paste +map("x", "p", [["_dP]]) + +map({ "n", "o", "x" }, "", "^", { desc = "Move to first non-blank character" }) +map({ "n", "o", "x" }, "", "g_", { desc = "Move to last non-blank character" }) + +nmap("q", ":q", { desc = "Quit" }) +nmap("Q", function() + vim.api.nvim_command("bd!|qall!") +end, { desc = "Quit all" }) + +-- create a user command to save without formatting :noa w +vim.api.nvim_create_user_command("W", function() + -- if buffer is empty, don't save + if vim.fn.empty(vim.fn.expand("%:t")) == 1 then + return vim.notify("Buffer is empty, not saving", vim.log.levels.ERROR) + end + vim.api.nvim_command("noa w") +end, { nargs = 0, desc = "Save without formatting" }) + +-- move lines +vmap("J", ":m '>+1gv=gv", { desc = "Move lines down" }) +vmap("K", ":m '<-2gv=gv", { desc = "Move lines up" }) + +-- undo tree +nmap("u", vim.cmd.UndotreeToggle) + +-- nnoremap yl :let @" = expand("%:p") +nmap("yl", function() + local file = vim.fn.expand("%:p") + print("Copied path to clipboard: " .. file) + -- copy to os clipboard + vim.fn.setreg("+", file) +end, { desc = "Copy file path to clipboard" }) + +nmap("x", "!chmod +x %", { desc = "Make file executable" }) + +vim.g.user_emmet_leader_key = "," + +nmap("gp", "Git push", { desc = "Git push" }) + +-- reset cmdheight +nmap("ch", function() + vim.o.cmdheight = 1 +end, { desc = "Reset cmdheight" }) + +nmap("w", function() + vim.api.nvim_command("w") +end, { desc = "Save" }) + +nmap("W", function() + vim.api.nvim_command("wa") +end, { desc = "Save all" }) + +-- format json using jq +nmap("jq", ":%!jq '.'", { desc = "Format json using jq" }) +vmap("jq", ":!jq '.'", { desc = "Format json using jq" }) + +-- Navigate quickfix list +nmap("qj", "cnext", { desc = "Next quickfix item" }) +nmap("qk", "cprev", { desc = "Previous quickfix item" }) + +nmap("", "nohlsearch", { desc = "Clear search highlights" }) diff --git a/lua/config/utils.lua b/lua/config/utils.lua new file mode 100644 index 0000000..5432beb --- /dev/null +++ b/lua/config/utils.lua @@ -0,0 +1,177 @@ +_G.map = function(mode, keys, func, opts) + opts = opts or {} + opts.desc = opts.desc or nil + vim.keymap.set(mode, keys, func, opts) +end + +_G.nmap = function(keys, func, opts) + _G.map("n", keys, func, opts) +end + +_G.imap = function(keys, func, opts) + _G.map("i", keys, func, opts) +end + +_G.vmap = function(keys, func, opts) + _G.map("v", keys, func, opts) +end + +_G.icons = { + kind = { + Array = " ", + Boolean = " ", + Class = " ", + Color = " ", + Constant = " ", + Constructor = " ", + Enum = " ", + EnumMember = " ", + Event = " ", + Field = " ", + File = " ", + Folder = "󰉋 ", + Function = " ", + Interface = " ", + Key = " ", + Keyword = " ", + Method = " ", + -- Module = " ", + Module = " ", + Namespace = " ", + Null = "󰟢 ", + Number = " ", + Object = " ", + Operator = " ", + Package = " ", + Property = " ", + Reference = " ", + Snippet = " ", + String = " ", + Struct = " ", + Text = " ", + TypeParameter = " ", + Unit = " ", + Value = " ", + Variable = " ", + }, + git = { + LineAdded = " ", + LineModified = " ", + LineRemoved = " ", + FileDeleted = " ", + FileIgnored = "◌", + FileRenamed = " ", + FileStaged = "S", + FileUnmerged = "", + FileUnstaged = "", + FileUntracked = "U", + Diff = " ", + Repo = " ", + Octoface = " ", + Copilot = " ", + Branch = "", + }, + ui = { + Message = "󰍡 ", + ArrowCircleDown = "", + ArrowCircleLeft = "", + ArrowCircleRight = "", + ArrowCircleUp = "", + BoldArrowDown = "", + BoldArrowLeft = "", + BoldArrowRight = "", + BoldArrowUp = "", + BoldClose = "", + BoldDividerLeft = "", + BoldDividerRight = "", + BoldLineLeft = "▎", + BoldLineMiddle = "┃", + BoldLineDashedMiddle = "┋", + BookMark = "", + BoxChecked = " ", + Bug = " ", + Stacks = "", + Scopes = "", + Watches = "󰂥", + DebugConsole = " ", + Calendar = " ", + Check = "", + ChevronRight = "", + ChevronShortDown = "", + ChevronShortLeft = "", + ChevronShortRight = "", + ChevronShortUp = "", + Circle = " ", + Close = "󰅖", + CloudDownload = " ", + Code = "", + Comment = "", + Dashboard = "", + DividerLeft = "", + DividerRight = "", + DoubleChevronRight = "»", + Ellipsis = "", + EmptyFolder = " ", + EmptyFolderOpen = " ", + File = " ", + FileSymlink = "", + Files = " ", + FindFile = "󰈞", + FindText = "󰊄", + Fire = "", + Folder = "󰉋 ", + FolderOpen = " ", + FolderSymlink = " ", + Forward = " ", + Gear = " ", + History = " ", + Lightbulb = " ", + LineLeft = "▏", + LineMiddle = "│", + List = " ", + Lock = " ", + NewFile = " ", + Note = " ", + Package = " ", + Pencil = "󰏫 ", + Plus = " ", + Project = " ", + Search = " ", + SignIn = " ", + SignOut = " ", + Tab = "󰌒 ", + Table = " ", + Target = "󰀘 ", + Telescope = "🔭", + Text = " ", + Tree = "", + Triangle = "󰐊", + TriangleShortArrowDown = "", + TriangleShortArrowLeft = "", + TriangleShortArrowRight = "", + TriangleShortArrowUp = "", + }, + diagnostics = { + BoldError = "", + Error = "", + BoldWarning = "", + Warning = "", + BoldInformation = "", + Information = "", + BoldQuestion = "", + Question = "", + BoldHint = "", + Hint = "󰌶", + Debug = "", + Trace = "✎", + }, + misc = { + Robot = "󰚩 ", + Squirrel = " ", + Tag = " ", + Watch = "", + Smiley = " ", + Package = " ", + CircuitBoard = " ", + }, +} diff --git a/pic.jpg b/pic.jpg new file mode 100644 index 0000000..3c4dca5 Binary files /dev/null and b/pic.jpg differ