├── .gitignore ├── lua ├── core │ ├── init.lua │ ├── integrations │ │ ├── comment_highlights.lua │ │ └── init.lua │ ├── hooks.lua │ ├── option_wrappers │ │ ├── undofile.lua │ │ └── init.lua │ ├── colorscheme.lua │ └── languages.lua ├── utils.lua └── config │ ├── utilities.lua │ ├── setup.lua │ └── environment.lua ├── user ├── plugins │ ├── presence.lua │ ├── gitsigns.lua │ ├── neogen.lua │ ├── toggleterm.lua │ ├── telescope.lua │ ├── neogit.lua │ └── neorg.lua └── init.lua ├── README.md ├── README.norg └── init.lua /.gitignore: -------------------------------------------------------------------------------- 1 | plugin/packer_compiled.lua 2 | -------------------------------------------------------------------------------- /lua/core/init.lua: -------------------------------------------------------------------------------- 1 | import "core.option_wrappers" { 2 | "undofile" 3 | } 4 | 5 | import "core.languages" 6 | import "core.hooks" 7 | import "core.colorscheme" 8 | -------------------------------------------------------------------------------- /lua/core/integrations/comment_highlights.lua: -------------------------------------------------------------------------------- 1 | return vim.schedule_wrap(function() 2 | vim.cmd([[ 3 | syntax match NeorgDevSectionComment /^\-\+>\s\+\zs\(\w\|\s\)\+\ze\s\+<\-\+$/ 4 | syntax match NeorgDevAnnotationComment /^\-\+>\s\+\zs[^<]\+$/ 5 | ]]) 6 | end) 7 | -------------------------------------------------------------------------------- /lua/core/hooks.lua: -------------------------------------------------------------------------------- 1 | return { 2 | new_hook = function(events, hook) 3 | events = (type(events) == "string" and { events } or events) 4 | 5 | for _, event in ipairs(events or {}) do 6 | table.insert(hooks[event], hook) 7 | end 8 | end, 9 | } 10 | -------------------------------------------------------------------------------- /user/plugins/presence.lua: -------------------------------------------------------------------------------- 1 | return { 2 | presence = function(config) 3 | plugin "presence.nvim" { 4 | "andweeb/presence.nvim", 5 | config = function() 6 | require("presence"):setup(config) 7 | end, 8 | } 9 | end, 10 | } 11 | -------------------------------------------------------------------------------- /user/plugins/gitsigns.lua: -------------------------------------------------------------------------------- 1 | return { 2 | gitsigns = function(config) 3 | plugin "gitsigns.nvim" { 4 | "lewis6991/gitsigns.nvim", 5 | config = function() 6 | require('gitsigns').setup(config) 7 | end, 8 | } 9 | end, 10 | } 11 | -------------------------------------------------------------------------------- /user/plugins/neogen.lua: -------------------------------------------------------------------------------- 1 | return { 2 | neogen = function(config) 3 | plugin "neogen" { 4 | "danymat/neogen", 5 | cmd = "Neogen", 6 | config = function() 7 | require("neogen").setup(config) 8 | end, 9 | } 10 | end, 11 | } 12 | -------------------------------------------------------------------------------- /lua/core/option_wrappers/undofile.lua: -------------------------------------------------------------------------------- 1 | local undofile_module = {} 2 | 3 | function undofile_module.undofile(location) 4 | require "core.option_wrappers".opt.undodir = location 5 | 6 | new_hook("post", function() 7 | vim.api.nvim_buf_set_option(0, "undofile", true) 8 | end) 9 | end 10 | 11 | return undofile_module 12 | -------------------------------------------------------------------------------- /lua/core/colorscheme.lua: -------------------------------------------------------------------------------- 1 | local colorscheme_manager = {} 2 | 3 | colorscheme_manager.colorscheme = function(path, name) 4 | plugin(name) { 5 | path, 6 | config = function() 7 | vim.cmd("colorscheme " .. name) 8 | end, 9 | } 10 | end 11 | 12 | colorscheme_manager.colourscheme = colorscheme_manager.colorscheme 13 | 14 | return colorscheme_manager 15 | -------------------------------------------------------------------------------- /user/plugins/toggleterm.lua: -------------------------------------------------------------------------------- 1 | return { 2 | toggleterm = function(config) 3 | plugin "toggleterm.nvim" { 4 | "akinsho/toggleterm.nvim", 5 | module = "toggleterm", 6 | cmd = { "ToggleTerm", "TermExec" }, 7 | 8 | config = function() 9 | require("toggleterm").setup(config) 10 | end, 11 | } 12 | end, 13 | } 14 | -------------------------------------------------------------------------------- /lua/utils.lua: -------------------------------------------------------------------------------- 1 | local utils = {} 2 | 3 | function utils.packer_command_chain(command, ...) 4 | if not command then 5 | return 6 | end 7 | 8 | local args = { ... } 9 | 10 | vim.api.nvim_create_autocmd("User", { 11 | pattern = { "PackerComplete", "PackerCompileDone" }, 12 | once = true, 13 | 14 | callback = function() 15 | utils.packer_command_chain(unpack(args)) 16 | end, 17 | }) 18 | 19 | command() 20 | end 21 | 22 | return utils 23 | -------------------------------------------------------------------------------- /user/plugins/telescope.lua: -------------------------------------------------------------------------------- 1 | return { 2 | telescope = function(config) 3 | local plenary = plugin "plenary.nvim" 4 | 5 | if not plenary.active then 6 | plenary { 7 | "nvim-lua/plenary.nvim", 8 | module = "plenary", 9 | } 10 | end 11 | 12 | plugin "telescope.nvim" { 13 | "nvim-telescope/telescope.nvim", 14 | cmd = "Telescope", 15 | config = function() 16 | require("telescope").setup(config) 17 | end, 18 | } 19 | end, 20 | } 21 | -------------------------------------------------------------------------------- /lua/core/option_wrappers/init.lua: -------------------------------------------------------------------------------- 1 | local variables = {} 2 | 3 | function variables.set(what) 4 | if vim.startswith(what, "no") then 5 | vim.opt[what:sub(3)] = false 6 | else 7 | vim.opt[what] = true 8 | end 9 | end 10 | 11 | -- TODO: make these proper wrappers with proper errors 12 | variables.opt = setmetatable({}, { 13 | __index = vim.opt, 14 | __newindex = vim.opt, 15 | 16 | __call = function(_, options) 17 | for key, value in pairs(options) do 18 | vim.opt[key] = value 19 | end 20 | end 21 | }) 22 | 23 | variables.g = vim.g 24 | variables.b = vim.opt_local 25 | 26 | return variables 27 | -------------------------------------------------------------------------------- /user/plugins/neogit.lua: -------------------------------------------------------------------------------- 1 | return { 2 | neogit = function(config) 3 | -- TODO: Make a try_make_plugin or try_plugin function 4 | -- to abstract this boilerplate 5 | 6 | local plenary = plugin "plenary.nvim" 7 | 8 | if not plenary.active then 9 | plenary { 10 | "nvim-lua/plenary.nvim", 11 | module = "plenary", 12 | } 13 | end 14 | 15 | plugin "neogit" { 16 | "TimUntersberger/neogit", 17 | requires = { "plenary.nvim" }, 18 | cmd = "Neogit", 19 | module = "neogit", 20 | config = function() 21 | require("neogit").setup(config or {}) 22 | end, 23 | } 24 | end, 25 | } 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # \[WIP\] Neovim Configuration for Neorg Developers 2 | This configuration serves as a stable way to develop for Neorg. 3 | ## Features 4 | Honestly nothing special, it's just a Neovim configuration after all - Neovim configs 5 | all boil down to plugins in the end. 6 | One thing that _does_ set `neorg-dev` aside from typical configurations is that it's not 7 | scared to abstract things away - although some may consider this bad, in the current era of 8 | Neovim plugins where things love to break having abstractions helps the user a lot. 9 | Aside from abstraction `neorg-dev` operates on a kind of client-server model, where the user 10 | is provided with a `user/init.lua` (the client) and may interact with the server (`neorg-dev`) 11 | APIs if it so chooses. 12 | ## Quirks to Note 13 | **TODO** -------------------------------------------------------------------------------- /README.norg: -------------------------------------------------------------------------------- 1 | * \[WIP\] Neovim Configuration for Neorg Developers 2 | This configuration serves as a stable way to develop for Neorg. 3 | 4 | ** Features 5 | Honestly nothing special, it's just a Neovim configuration after all - Neovim configs 6 | all boil down to plugins in the end. 7 | 8 | One thing that /does/ set `neorg-dev` aside from typical configurations is that it's not 9 | scared to abstract things away - although some may consider this bad, in the current era of 10 | Neovim plugins where things love to break having abstractions helps the user a lot. 11 | 12 | Aside from abstraction `neorg-dev` operates on a kind of client-server model, where the user 13 | is provided with a `user/init.lua` (the client) and may interact with the server (`neorg-dev`) 14 | APIs if it so chooses. 15 | 16 | ** Quirks to Note 17 | *TODO* 18 | -------------------------------------------------------------------------------- /lua/core/integrations/init.lua: -------------------------------------------------------------------------------- 1 | local highlight_comments = require "core.integrations.comment_highlights" 2 | 3 | return { 4 | integrations = function(integrations) 5 | if integrations == "all" then 6 | integrations = { "highlights" } 7 | end 8 | 9 | if vim.tbl_contains(integrations, "highlights") then 10 | new_hook({ "config_open", "post" }, function() 11 | highlight_comments() 12 | end) 13 | end 14 | end, 15 | 16 | integrations_highlights = function(highlights) 17 | vim.api.nvim_set_hl(0, "NeorgDevSectionComment", { 18 | link = highlights.sections or "TSUnderline", 19 | }) 20 | 21 | vim.api.nvim_set_hl(0, "NeorgDevAnnotationComment", { 22 | link = highlights.annotations or "TSStrong", 23 | }) 24 | end, 25 | } 26 | -------------------------------------------------------------------------------- /lua/core/languages.lua: -------------------------------------------------------------------------------- 1 | local module = {} 2 | 3 | function module.languages(language_list) 4 | local treesitter_languages = {} 5 | 6 | for _, language in ipairs(language_list) do 7 | if type(language) == "string" then 8 | table.insert(treesitter_languages, language) 9 | end 10 | end 11 | 12 | if not vim.tbl_isempty(treesitter_languages) then 13 | plugin "nvim-treesitter" { 14 | "nvim-treesitter/nvim-treesitter", 15 | run = ":silent! TSUpdate", 16 | config = function() 17 | require("nvim-treesitter.configs").setup({ 18 | ensure_installed = treesitter_languages, 19 | 20 | highlight = { 21 | enable = true, 22 | }, 23 | }) 24 | end, 25 | } 26 | end 27 | end 28 | 29 | return module 30 | -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | --> Set up plugins if they don't exist 2 | local is_fresh_install = false 3 | local config_path = vim.fn.stdpath("config") 4 | 5 | do 6 | local install_dir = vim.fn.stdpath("data") .. "/site/pack/packer/" 7 | 8 | if vim.fn.isdirectory(install_dir) == 0 then 9 | vim.notify("Packer isn't installed! Setting it up...") 10 | 11 | vim.fn.delete(config_path .. "/plugin/packer_compiled.lua") 12 | vim.fn.system({"git", "clone", "--depth", "1", "https://github.com/wbthomason/packer.nvim", install_dir .. "/opt/packer.nvim"}) 13 | 14 | if vim.v.shell_error == 0 then 15 | is_fresh_install = true 16 | vim.notify("Packer set up successfully!") 17 | else 18 | vim.fn.delete(install_dir, "rf") 19 | vim.notify("Failed to install packer (make this error message better in the future)") 20 | return -1 21 | end 22 | end 23 | end 24 | 25 | --> Set up functions specifically made for the user configuration 26 | 27 | -- Make `user/` part of the lua path 28 | package.path = package.path .. ";" .. config_path .. "/user/?.lua;" .. config_path .. "/user/?/init.lua;" 29 | 30 | --> Configure the isolated environment 31 | require("config.setup")(is_fresh_install) 32 | -------------------------------------------------------------------------------- /user/plugins/neorg.lua: -------------------------------------------------------------------------------- 1 | return { 2 | -- TODO: Change function name to "neorg" once the `neorg` global is removed 3 | neorg_setup = function(options) 4 | -- Declare the plenary plugin 5 | local plenary = plugin "plenary.nvim" 6 | 7 | -- If the plugin is already defined then don't override its data. 8 | if not plenary.active then 9 | plenary { 10 | "nvim-lua/plenary.nvim", 11 | module = "plenary", 12 | } 13 | end 14 | 15 | -- Declare the neorg plugin 16 | local neorg = plugin "neorg" 17 | 18 | options = options or {} 19 | options.modules = options.modules or { 20 | ["core.defaults"] = {}, 21 | } 22 | 23 | if options.workspaces then 24 | options.modules["core.norg.dirman"] = vim.tbl_deep_extend("keep", options.modules["core.norg.dirman"] or {}, { 25 | config = { 26 | workspaces = options.workspaces, 27 | } 28 | }) 29 | end 30 | 31 | if options.treesitter then 32 | options.modules["core.integrations.treesitter"] = { 33 | config = { 34 | parser_configs = options.treesitter, 35 | } 36 | } 37 | end 38 | 39 | -- Send the packaged plugin to be managed by packer 40 | neorg { 41 | options.path or "nvim-neorg/neorg", 42 | after = "nvim-treesitter", 43 | requires = { "plenary.nvim" }, 44 | config = function() 45 | require("neorg").setup({ 46 | load = options.modules 47 | }) 48 | end, 49 | } 50 | end, 51 | } 52 | -------------------------------------------------------------------------------- /lua/config/utilities.lua: -------------------------------------------------------------------------------- 1 | local utilities = {} 2 | 3 | --- Returns the item that matches the first item in statements 4 | ---@param value any #The value to compare against 5 | ---@param compare? function #A custom comparison function 6 | ---@return function #A function to invoke with a table of potential matches 7 | utilities.match = function(value, compare) 8 | -- Returning a function allows for such syntax: 9 | -- match(something) { ..matches.. } 10 | return function(statements) 11 | if value == nil then 12 | return 13 | end 14 | 15 | -- Set the comparison function 16 | -- A comparison function may be required for more complex 17 | -- data types that need to be compared against another static value. 18 | -- The default comparison function compares booleans as strings to ensure 19 | -- that boolean comparisons work as intended. 20 | compare = compare 21 | or function(lhs, rhs) 22 | if type(lhs) == "boolean" then 23 | return tostring(lhs) == rhs 24 | end 25 | 26 | return lhs == rhs 27 | end 28 | 29 | -- Go through every statement, compare it, and perform the desired action 30 | -- if the comparison was successful 31 | for case, action in pairs(statements) do 32 | if compare(value, case) then 33 | -- The action can be a function, in which case it is invoked 34 | -- and the return value of that function is returned instead. 35 | if type(action) == "function" then 36 | return action(value) 37 | end 38 | 39 | return action 40 | end 41 | end 42 | 43 | -- If we've fallen through all statements to check and haven't found 44 | -- a single match then see if we can fall back to a `_` clause instead. 45 | if statements._ then 46 | local action = statements._ 47 | 48 | if type(action) == "function" then 49 | return action(value) 50 | end 51 | 52 | return action 53 | end 54 | end 55 | end 56 | 57 | --- Wraps a function in a callback 58 | ---@param function_pointer function #The function to wrap 59 | ---@vararg ... #The arguments to pass to the wrapped function 60 | ---@return function #The wrapped function in a callback 61 | utilities.wrap = function(function_pointer, ...) 62 | local params = { ... } 63 | 64 | if type(function_pointer) ~= "function" then 65 | local prev = function_pointer 66 | 67 | -- luacheck: push ignore 68 | function_pointer = function(...) 69 | return prev 70 | end 71 | -- luacheck: pop 72 | end 73 | 74 | return function() 75 | return function_pointer(unpack(params)) 76 | end 77 | end 78 | 79 | --- Maps a function to every element of a table 80 | -- The function can return a value, in which case that specific element will be assigned 81 | -- the return value of that function. 82 | ---@param tbl table #The table to iterate over 83 | ---@param callback function #The callback that should be invoked on every iteration 84 | ---@return table #A modified version of the original `tbl`. 85 | utilities.map = function(tbl, callback) 86 | local copy = vim.deepcopy(tbl) 87 | 88 | for k, v in pairs(tbl) do 89 | local cb = callback(k, v, tbl) 90 | 91 | if cb then 92 | copy[k] = cb 93 | end 94 | end 95 | 96 | return copy 97 | end 98 | 99 | return utilities 100 | -------------------------------------------------------------------------------- /lua/config/setup.lua: -------------------------------------------------------------------------------- 1 | return function(is_fresh_install) 2 | local config_path = vim.fn.stdpath("config") 3 | 4 | package.loaded["config.environment"] = nil 5 | 6 | local isolated_environment = require("config.environment") 7 | 8 | -- TODO: Document why we leak this out into the global namespace 9 | _G.neorg_dev = { 10 | state = isolated_environment.state, 11 | plugin_data = {}, 12 | } 13 | 14 | local string_metatable = debug.getmetatable("") 15 | local old_string_metatable = vim.deepcopy(string_metatable) 16 | 17 | string_metatable.__div = function(a, b) 18 | if type(a) == "string" then 19 | local keymaps = (type(b) == "string" and { b }) 20 | local modes = {} 21 | 22 | for mode in a:gmatch("%w") do 23 | table.insert(modes, mode) 24 | end 25 | 26 | return setmetatable(keymaps and { 27 | modes = modes, 28 | keymaps = keymaps, 29 | attributes = {}, 30 | } or vim.tbl_deep_extend("force", { 31 | modes = modes, 32 | attributes = {}, 33 | keymaps = {} 34 | }, b), { 35 | __add = string_metatable.__add, 36 | __div = string_metatable.__div, 37 | __mod = string_metatable.__mod, 38 | }) 39 | elseif type(a) == "table" and a.modes and a.keymaps then 40 | a.rhs = b 41 | return a 42 | else 43 | -- error 44 | return {} 45 | end 46 | end 47 | 48 | string_metatable.__add = function(a, b) 49 | a.attributes[b] = true 50 | return a 51 | end 52 | 53 | string_metatable.__mod = function(a, b) 54 | a.attributes.desc = b 55 | return a 56 | end 57 | 58 | string_metatable.__sub = function(a, b) 59 | local keymaps = (type(b) == "string" and { b } or b) 60 | local modes = {} 61 | 62 | for mode in a:gmatch("%w") do 63 | table.insert(modes, mode) 64 | end 65 | 66 | return { 67 | modes = modes, 68 | keymaps = keymaps, 69 | remove = true, 70 | } 71 | end 72 | 73 | string_metatable.__unm = function(a) 74 | return { 75 | keymaps = { a }, 76 | modes = "", 77 | remove = true, 78 | } 79 | end 80 | 81 | string_metatable.__pow = function(a, b) 82 | return { 83 | keymaps = (type(a) == "string" and { a } or a), 84 | swap = b, 85 | } 86 | end 87 | 88 | debug.setmetatable("", string_metatable) 89 | 90 | --> Run the user configuration 91 | -- NOTE: `setfenv` does not work as intended here and always breaks. To 92 | -- combat this, we're globally setting _G's metatable here temporarily 93 | -- instead. 94 | setmetatable(_G, { __index = isolated_environment }) 95 | 96 | dofile(config_path .. "/user/init.lua") 97 | 98 | -- Reset the global variable's metatable to not accidentally leak globals 99 | -- into other parts of code. 100 | setmetatable(_G, nil) 101 | 102 | debug.setmetatable("", old_string_metatable) 103 | 104 | -- Run post scripts 105 | isolated_environment.post() 106 | 107 | vim.cmd("packadd packer.nvim") 108 | 109 | local packer = require('packer') 110 | local packer_async = require('packer.async') 111 | 112 | packer.init({ 113 | autoremove = true, 114 | }) 115 | 116 | packer.startup(function(use) 117 | local plugin = isolated_environment.plugin 118 | 119 | plugin "packer.nvim" { 120 | "wbthomason/packer.nvim", 121 | opt = true, 122 | } 123 | 124 | for name, plugin_data in pairs(isolated_environment.plugins) do 125 | local real_plugin_name = plugin_data.packer_data[1]:match("/(.+)$") 126 | 127 | use(vim.tbl_deep_extend("keep", plugin_data.packer_data, { as = (real_plugin_name ~= name and name) })) 128 | end 129 | 130 | if is_fresh_install then 131 | vim.api.nvim_create_autocmd("User", { 132 | pattern = "PackerComplete", 133 | once = true, 134 | 135 | callback = function() 136 | -- TODO: Do we need this? 137 | -- for name in pairs(isolated_environment.plugins) do 138 | -- vim.cmd("silent packadd " .. name) 139 | -- end 140 | 141 | vim.notify("Plugins have been installed - a restart of Neovim is heavily recommended.") 142 | end, 143 | }) 144 | 145 | vim.notify("Installing plugins...") 146 | 147 | packer.sync() 148 | end 149 | end) 150 | end 151 | -------------------------------------------------------------------------------- /lua/config/environment.lua: -------------------------------------------------------------------------------- 1 | local environment = require("config.utilities") 2 | 3 | environment.state = {} 4 | environment.plugins = {} 5 | environment.hooks = { 6 | post = {}, 7 | config_write = {}, 8 | config_open = {}, 9 | } 10 | 11 | environment.require = function(module_path) 12 | package.loaded[module_path] = nil 13 | return require(module_path) 14 | end 15 | 16 | environment.editing = function(options) 17 | local function set_options(keys, value) 18 | return function() 19 | for _, key in ipairs(keys) do 20 | vim.opt[key] = value 21 | end 22 | end 23 | end 24 | 25 | for key, value in pairs(options) do 26 | environment.match(key) { 27 | indent = set_options({ "shiftwidth", "tabstop", "softtabstop" }, value), 28 | spaces = set_options({ "expandtab" }, value) 29 | } 30 | end 31 | end 32 | 33 | -- TODO: Export 34 | environment.keybinds = function(keybinds) 35 | if not keybinds then 36 | return 37 | end 38 | 39 | for _, keybind_data in ipairs(keybinds) do 40 | for _, keymap in ipairs(keybind_data.keymaps) do 41 | if keybind_data.swap then 42 | vim.keymap.set(keybind_data.modes, keymap, keybind_data.swap, keybind_data.attributes) 43 | vim.keymap.set(keybind_data.modes, keybind_data.swap, keymap, keybind_data.attributes) 44 | elseif keybind_data.remove then 45 | vim.keymap.del(keybind_data.modes, keymap) 46 | else 47 | vim.keymap.set(keybind_data.modes, keymap, keybind_data.rhs, keybind_data.attributes) 48 | end 49 | end 50 | end 51 | end 52 | 53 | environment.import = function(file_or_prefix) 54 | local ok, module = pcall(environment.require, file_or_prefix) 55 | 56 | local function override_global_metatable(contents) 57 | local global_metatable = getmetatable(_G) 58 | 59 | global_metatable.__index = vim.tbl_deep_extend("error", global_metatable.__index, contents) 60 | end 61 | 62 | if ok then 63 | module = (module == true and {} or module) 64 | override_global_metatable(module) 65 | end 66 | 67 | return function(modules) 68 | for _, module_name in ipairs(modules) do 69 | module = (module == true and {} or module) 70 | module = environment.require(file_or_prefix .. "." .. module_name) 71 | override_global_metatable(module) 72 | end 73 | end 74 | end 75 | 76 | environment.plugin = function(name) 77 | if environment.plugins[name] then 78 | return environment.plugins[name] 79 | end 80 | 81 | return setmetatable({ 82 | name = name, 83 | packer_data = {}, 84 | active = false, 85 | }, { 86 | __call = function(self, packer_data) 87 | self.packer_data = vim.tbl_deep_extend("force", self.packer_data, packer_data) 88 | self.active = true 89 | 90 | _G.neorg_dev.plugin_data[self.name] = vim.deepcopy(packer_data) 91 | 92 | if packer_data.config and type(packer_data.config) == "function" then 93 | self.packer_data.config = function(name, data) 94 | _G.neorg_dev.plugin_data[name].config(name, data) 95 | end 96 | end 97 | 98 | environment.plugins[self.name] = self 99 | return self 100 | end, 101 | }) 102 | end 103 | 104 | environment.post = function() 105 | for _, hook in ipairs(environment.hooks.post) do 106 | hook() 107 | end 108 | 109 | local augroup = vim.api.nvim_create_augroup("NeorgDev", {}) 110 | 111 | vim.api.nvim_create_autocmd("BufEnter", { 112 | group = augroup, 113 | pattern = vim.fn.stdpath("config") .. "/user/**.lua", 114 | callback = function(data) 115 | for _, hook in ipairs(environment.hooks.config_open) do 116 | hook() 117 | end 118 | end 119 | }) 120 | 121 | vim.api.nvim_create_autocmd("BufWritePost", { 122 | group = augroup, 123 | pattern = vim.fn.stdpath("config") .. "/user/**.lua", 124 | callback = function(data) 125 | for _, hook in ipairs(environment.hooks.config_write) do 126 | hook() 127 | end 128 | 129 | if not _G.packer_plugins then 130 | return 131 | end 132 | 133 | local old_plugin_count = vim.tbl_count(environment.plugins) 134 | 135 | local ok, err = pcall(require("config.setup")) 136 | 137 | if not ok then 138 | vim.api.nvim_err_writeln(err) 139 | return 140 | end 141 | 142 | local current_plugin_count = vim.tbl_count(environment.plugins) 143 | local packer_plugin_count = vim.tbl_count(_G.packer_plugins) 144 | local packer = require("packer") 145 | 146 | if current_plugin_count > old_plugin_count or current_plugin_count > packer_plugin_count then 147 | packer.sync() 148 | elseif current_plugin_count < old_plugin_count or current_plugin_count < packer_plugin_count then 149 | packer.clean() 150 | packer.compile() 151 | end 152 | end 153 | }) 154 | 155 | vim.api.nvim_create_autocmd("VimEnter", { 156 | group = augroup, 157 | callback = function() 158 | if not _G.packer_plugins then 159 | return 160 | end 161 | 162 | local current_plugin_count = vim.tbl_count(environment.plugins) 163 | local packer_plugin_count = vim.tbl_count(_G.packer_plugins) 164 | local packer = require("packer") 165 | 166 | if current_plugin_count > packer_plugin_count then 167 | packer.sync() 168 | elseif current_plugin_count < packer_plugin_count then 169 | packer.clean() 170 | packer.compile() 171 | end 172 | end, 173 | }) 174 | end 175 | 176 | environment.silent = "silent" 177 | environment.expr = "expr" 178 | environment.script = "script" 179 | environment.nowait = "nowait" 180 | environment.noremap = "noremap" 181 | 182 | return environment 183 | -------------------------------------------------------------------------------- /user/init.lua: -------------------------------------------------------------------------------- 1 | --> Core Imports 2 | import "core" 3 | import "core.integrations" 4 | 5 | --> Import 6 | import "plugins" { 7 | "neorg", 8 | "gitsigns", 9 | "neogit", 10 | "toggleterm", 11 | "presence", 12 | "telescope", 13 | "neogen", 14 | } 15 | 16 | editing { 17 | indent = 4, 18 | spaces = true, 19 | } 20 | 21 | --------------------------------------> BASIC OPTIONS <-------------------------------------- 22 | 23 | -- `set` is used to simply toggle an option on/off. 24 | -- It's syntax sugar for `opt.option = true` or `opt.option = false` 25 | -- 26 | -- To enable an option, simply type `set "optionname"` 27 | -- To disable an option, type `set "nooptionname"` 28 | 29 | -- Enable line numbers for buffers 30 | set "number" 31 | 32 | -- Make the line numbers relative 33 | set "relativenumber" 34 | 35 | -- Make horizontal splits show up at the bottom of the screen by default. 36 | set "splitbelow" 37 | 38 | -- Make vertical splits show up on the right of the screen by default. 39 | set "splitright" 40 | 41 | -- Disable wrapping (usually not good for coding, useful for writing though) 42 | set "nowrap" 43 | 44 | -- Enables richer colouring for terminals that support it (this assumes you have 45 | -- a fairly modern terminal emulator) 46 | set "termguicolors" 47 | 48 | -- Make Neovim case-insensitive (applies to many places, including search, completions etc.) 49 | set "ignorecase" 50 | 51 | --------------------------------------> OPTION CLUSTERS <-------------------------------------- 52 | 53 | -- Neovim Update Intervals 54 | opt { 55 | timeoutlen = 500, 56 | ttimeoutlen = 5, 57 | updatetime = 100, 58 | } 59 | 60 | -- Splitting behaviour 61 | opt { 62 | splitbelow = true, 63 | splitright = true, 64 | } 65 | 66 | ------------------------------------------------------------------------------------------------ 67 | 68 | --> Set to 69 | g.mapleader = " " 70 | g.maplocalleader = "," 71 | 72 | --> Make substitution (:s) commands preview changes in realtime 73 | opt.inccommand = "split" 74 | 75 | --> Enable virtualedit only when in visual block mode 76 | opt.virtualedit = "block" 77 | 78 | --> Don't autoclose folds 79 | opt.foldlevel = 999 80 | 81 | --> Synchronize the system clipboard and Neovim's clipboard 82 | opt.clipboard = "unnamedplus" 83 | 84 | --> Enable full concealing by default 85 | opt.conceallevel = 2 86 | 87 | --> Make scrolling much more convenient 88 | opt.scrolloff = 999 89 | 90 | --> Disable Neovim's default mouse behaviours 91 | opt.mouse = "" 92 | 93 | --> Store undo information persistently under the following directory 94 | undofile (vim.fn.stdpath("cache") .. "/nvim/undo") 95 | 96 | --> Colourscheme setup 97 | colorscheme ("rebelot/kanagawa.nvim", "kanagawa") 98 | -- colorscheme ("rose-pine/neovim", "rose-pine") 99 | -- colorscheme("Shadorain/shadotheme", "shado") 100 | -- colorscheme ("catppuccin/nvim", "catppuccin") 101 | 102 | --> Enable `neorg-dev` integrations with this init.lua file 103 | integrations "all" 104 | 105 | integrations_highlights { 106 | sections = "TSStrong", 107 | annotations = "TSUnderline", 108 | } 109 | 110 | --> Keybinds 111 | keybinds { 112 | ("n" / "" / "bd" +silent) % "closes the current buffer", 113 | ("n" / "" / "bn" +silent) % "cycles to the next buffer", 114 | ("n" / "" / "bp" +silent) % "cycles to the previous buffer", 115 | 116 | ("n" / "" / "h" +noremap) % "moves to the leftside split", 117 | ("n" / "" / "l" +noremap) % "moves to the rightside split", 118 | 119 | ("n" / "" / "noh" +silent) % "clears search highlights", 120 | 121 | ("nv" / ":" ^ ";" +noremap), 122 | } 123 | 124 | languages { 125 | "lua", -- Chad level 999 (plus one to account for one based indexing) 126 | "cpp", -- Where did my borrow checker go 127 | "javascript", -- For Treesitter development, don't be mad at me 128 | "zig", -- Objectively the best, don't @ me 129 | } 130 | 131 | neorg_setup { 132 | path = "~/dev/neorg/", 133 | -- treesitter = { 134 | -- norg = { 135 | -- url = "~/dev/tree-sitter-norg", 136 | -- } 137 | -- }, 138 | 139 | modules = { 140 | ["core.defaults"] = {}, 141 | -- ["core.semantic-analyzer"] = {}, 142 | -- ["core.norg.esupports.metagen"] = { 143 | -- config = { 144 | -- type = "empty", 145 | -- }, 146 | -- }, 147 | -- ["core.export.markdown"] = { 148 | -- config = { 149 | -- extensions = "all", 150 | -- }, 151 | -- }, 152 | -- ["core.extern"] = {}, 153 | ["core.norg.concealer"] = {}, 154 | -- ["core.gtd.base"] = { 155 | -- config = { 156 | -- workspace = "main", 157 | -- } 158 | -- } 159 | }, 160 | 161 | workspaces = { 162 | main = "~/neorg/", 163 | }, 164 | } 165 | 166 | gitsigns {} 167 | 168 | neogit { 169 | keybinds { 170 | ("n" / "g" / "Neogit") % "activate neogit", 171 | }, 172 | } 173 | 174 | toggleterm { 175 | keybinds { 176 | ("n" / "t" / function() return "" .. tostring(vim.v.count1) .. "ToggleTerm direction=float" end +expr +silent) % "toggle the terminal", 177 | ("t" / "" / "" +noremap) % "moves to normal mode in a terminal view", 178 | 179 | -- Treesitter development related (TODO: move into different section) 180 | ("nt" / "yg" / [[TermExec cmd="tree-sitter generate" direction=float]]) % "runs the equivalent of 'yarn gen' in a terminal", 181 | ("nt" / "yp" / [[TermExec cmd="tree-sitter parse test.norg" direction=float]]) % "runs the equivalent of 'yarn parse test.norg' in a terminal", 182 | ("nt" / "yt" / [[TermExec cmd="tree-sitter test" direction=float]]) % "runs the equivalent of 'yarn test' in a terminal", 183 | ("nt" / "yd" / [[TermExec cmd="tree-sitter parse -d test.norg" direction=float]]) % "runs the equivalent of 'yarn parse -d test.norg' in a terminal", 184 | } 185 | } 186 | 187 | presence { 188 | neovim_image_text = "Emacs Sucks Balls, Respectfully", 189 | enable_line_number = true, 190 | main_image = "file", 191 | } 192 | 193 | telescope { 194 | keybinds { 195 | ("n" / "ff" / "Telescope find_files") % "fuzzy searches through files using telescope", 196 | ("n" / "lg" / "Telescope live_grep") % "live greps through the current working directory with telescope", 197 | ("n" / "fh" / "Telescope help_tags") % "fuzzy searches through help tags using telescope", 198 | } 199 | } 200 | 201 | neogen { 202 | keybinds { 203 | ("n" / "d" / "Neogen") % "generates documentation for the current function", 204 | } 205 | } 206 | 207 | plugin "playground" { 208 | "nvim-treesitter/playground", 209 | cmd = "TSPlaygroundToggle", 210 | 211 | keybinds { 212 | ("n" / "p" / "TSPlaygroundToggle") % "toggles the TS playground", 213 | }, 214 | } 215 | 216 | -- TODO: Export this to something much less ugly 217 | plugin "nvim-lspconfig" { 218 | "neovim/nvim-lspconfig", 219 | config = function() 220 | -- Mappings. 221 | -- See `:help vim.diagnostic.*` for documentation on any of the below functions 222 | local opts = { noremap=true, silent=true } 223 | vim.keymap.set('n', 'e', vim.diagnostic.open_float, opts) 224 | vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, opts) 225 | vim.keymap.set('n', ']d', vim.diagnostic.goto_next, opts) 226 | vim.keymap.set('n', 'q', vim.diagnostic.setloclist, opts) 227 | 228 | -- Use an on_attach function to only map the following keys 229 | -- after the language server attaches to the current buffer 230 | local on_attach = function(client, bufnr) 231 | -- Enable completion triggered by 232 | vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc") 233 | 234 | -- Mappings. 235 | -- See `:help vim.lsp.*` for documentation on any of the below functions 236 | local bufopts = { noremap = true, silent = true, buffer = bufnr } 237 | 238 | vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, bufopts) 239 | vim.keymap.set('n', 'gd', vim.lsp.buf.definition, bufopts) 240 | vim.keymap.set('n', 'K', vim.lsp.buf.hover, bufopts) 241 | vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, bufopts) 242 | vim.keymap.set('n', 'gr', vim.lsp.buf.references, bufopts) 243 | vim.keymap.set('n', 'D', vim.lsp.buf.type_definition, bufopts) 244 | vim.keymap.set('n', 'rn', vim.lsp.buf.rename, bufopts) 245 | vim.keymap.set('n', 'ca', vim.lsp.buf.code_action, bufopts) 246 | vim.keymap.set('n', 'fo', vim.lsp.buf.format, bufopts) 247 | end 248 | 249 | local lspconfig = require("lspconfig") 250 | 251 | for _, lang in ipairs({"zls", "norg_lsp", "clangd"}) do 252 | lspconfig[lang].setup({ 253 | on_attach = on_attach, 254 | }) 255 | end 256 | end, 257 | } 258 | 259 | plugin "lsp_lines" { 260 | "https://git.sr.ht/~whynothugo/lsp_lines.nvim", 261 | config = function() 262 | vim.diagnostic.config({ 263 | virtual_text = false, 264 | }) 265 | require("lsp_lines").setup() 266 | end, 267 | 268 | keybinds { 269 | ("n" / "lt" / function() require("lsp_lines").toggle() end) % "toggles `lsp_lines`", 270 | } 271 | } 272 | --------------------------------------------------------------------------------