diff --git a/.config/nvim/init.lua b/.config/nvim/init.lua
index 79ff5085..249221c1 100644
--- a/.config/nvim/init.lua
+++ b/.config/nvim/init.lua
@@ -4,5 +4,6 @@ require("lazy-init")
 require("config.autocmds")
 require("config.filetypes")
 require("funcs").set_title()
-require("funcs").set_maps(require("config.keymaps").maps())
 require("funcs").set_cwd()
+require("funcs").set_maps(require("config.keymaps").standard_maps())
+require("funcs").diagnostic_setup(require("config.diagnostic"))
diff --git a/.config/nvim/lua/config/diagnostic.lua b/.config/nvim/lua/config/diagnostic.lua
new file mode 100644
index 00000000..ad3f7163
--- /dev/null
+++ b/.config/nvim/lua/config/diagnostic.lua
@@ -0,0 +1,40 @@
+local M = {}
+local icons = require("config.icons")
+
+M.signs = {
+    { name = "DapStopped",             text = icons.DapStopped,             texthl = "DiagnosticWarn" },
+    { name = "DapBreakpoint",          text = icons.DapBreakpoint,          texthl = "DiagnosticInfo" },
+    { name = "DapBreakpointRejected",  text = icons.DapBreakpointRejected,  texthl = "DiagnosticError" },
+    { name = "DapBreakpointCondition", text = icons.DapBreakpointCondition, texthl = "DiagnosticInfo" },
+    { name = "DapLogPoint",            text = icons.DapLogPoint,            texthl = "DiagnosticInfo" },
+    { name = "DiagnosticSignError",    text = icons.Error,                  texthl = "DiagnosticSignError" },
+    { name = "DiagnosticSignWarn",     text = icons.Warn,                   texthl = "DiagnosticSignWarn" },
+    { name = "DiagnosticSignHint",     text = icons.Hint,                   texthl = "DiagnosticSignHint" },
+    { name = "DiagnosticSignInfo",     text = icons.Info,                   texthl = "DiagnosticSignInfo" },
+}
+
+M.diagnostic = {
+    virtual_text = true,
+    signs = {
+        text = {
+            [vim.diagnostic.severity.ERROR] = icons.Error,
+            [vim.diagnostic.severity.HINT] = icons.Hint,
+            [vim.diagnostic.severity.WARN] = icons.Warn,
+            [vim.diagnostic.severity.INFO] = icons.Info,
+        },
+        active = M.signs,
+    },
+    update_in_insert = true,
+    underline = true,
+    severity_sort = true,
+    float = {
+        focused = false,
+        style = "minimal",
+        border = "rounded",
+        source = "always",
+        header = "",
+        prefix = "",
+    },
+}
+
+return M
diff --git a/.config/nvim/lua/config/keymaps.lua b/.config/nvim/lua/config/keymaps.lua
index d203961b..71ad07ea 100644
--- a/.config/nvim/lua/config/keymaps.lua
+++ b/.config/nvim/lua/config/keymaps.lua
@@ -2,8 +2,6 @@ local M = {}
 local f = require("funcs")
 local icons = require("config.icons")
 
-local maps = f.empty_map_table()
-
 M.sections = {
     p = { desc = icons.Package .. " Packages" },
     b = { desc = icons.Buffer .. " Buffers" },
@@ -18,7 +16,8 @@ M.sections = {
     e = { desc = icons.FileTree .. " File Manager" },
 }
 
-M.maps = function()
+M.standard_maps = function()
+    local maps = f.empty_map_table()
     -- Standard --
     maps.n["j"] = { "v:count == 0 ? 'gj' : 'j'", expr = true, desc = "Move cursor down" }
     maps.n["k"] = { "v:count == 0 ? 'gk' : 'k'", expr = true, desc = "Move cursor up" }
@@ -565,8 +564,11 @@ M.maps = function()
             end,
             desc = "Debugger: Stop",
         }
-        maps.n["<S-F9>"] = { conditional_breakpoint, desc = "Debugger: Conditional Breakpoint" }
-        maps.n["<C-F5>"] = {
+
+        -- S-F9
+        maps.n["<F21>"] = { conditional_breakpoint, desc = "Debugger: Conditional Breakpoint" }
+        -- C-F5
+        maps.n["<F29>"] = {
             function()
                 require("dap").restart_frame()
             end,
@@ -596,7 +598,8 @@ M.maps = function()
             end,
             desc = "Debugger: Step Into",
         }
-        maps.n["<S-F11>"] = {
+        -- S-F11
+        maps.n["<F23>"] = {
             function()
                 require("dap").step_out()
             end,
@@ -744,6 +747,278 @@ M.maps = function()
     return maps
 end
 
+M.lsp_maps = function(client, bufnr)
+    local maps = f.empty_map_table()
+
+    maps.n["<Leader>ld"] = {
+        function()
+            vim.diagnostic.open_float()
+        end,
+        desc = "Hover diagnostics",
+    }
+    maps.n["[d"] = {
+        function()
+            vim.diagnostic.goto_prev()
+        end,
+        desc = "Previous diagnostic",
+    }
+    maps.n["]d"] = {
+        function()
+            vim.diagnostic.goto_next()
+        end,
+        desc = "Next diagnostic",
+    }
+    maps.n["gl"] = {
+        function()
+            vim.diagnostic.open_float()
+        end,
+        desc = "Hover diagnostics",
+    }
+
+    if f.is_available("telescope.nvim") then
+        maps.n["<Leader>lD"] = {
+            function()
+                require("telescope.builtin").diagnostics()
+            end,
+            desc = "Search diagnostics",
+        }
+    end
+
+    if f.is_available("mason-lspconfig.nvim") then
+        maps.n["<Leader>li"] = { "<cmd>LspInfo<cr>", desc = "LSP information" }
+    end
+
+    if f.is_available("none-ls.nvim") then
+        maps.n["<Leader>lI"] = { "<cmd>NullLsInfo<cr>", desc = "Null-ls information" }
+    end
+
+    if client.supports_method("textDocument/codeAction") then
+        maps.n["<Leader>la"] = {
+            function()
+                vim.lsp.buf.code_action()
+            end,
+            desc = "Code action",
+        }
+        maps.v["<Leader>la"] = maps.n["<Leader>la"]
+    end
+
+    if client.supports_method("textDocument/codeLens") then
+        vim.lsp.codelens.refresh()
+        maps.n["<Leader>ll"] = {
+            function()
+                vim.lsp.codelens.refresh()
+            end,
+            desc = "Refresh CodeLens",
+        }
+        maps.n["<Leader>lL"] = {
+            function()
+                vim.lsp.codelens.run()
+            end,
+            desc = "Run CodeLens",
+        }
+        maps.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
+        maps.n["<Leader>uL"] = {
+            function()
+                vim.lsp.codelens.clear()
+            end,
+            desc = "Toggle CodeLens",
+        }
+    end
+
+    if client.supports_method("textDocument/definition") then
+        maps.n["gd"] = {
+            function()
+                vim.lsp.buf.definition()
+            end,
+            desc = "Go to definition",
+        }
+    end
+
+    if client.supports_method("textDocument/typeDefinition") then
+        maps.n["gy"] = {
+            function()
+                vim.lsp.buf.type_definition()
+            end,
+            desc = "Go to type definition",
+        }
+    end
+
+    if client.supports_method("textDocument/declaration") then
+        maps.n["gD"] = {
+            function()
+                vim.lsp.buf.declaration()
+            end,
+            desc = "Go to declaration",
+        }
+    end
+
+    if client.supports_method("textDocument/implementation") then
+        maps.n["gI"] = {
+            function()
+                vim.lsp.buf.implementation()
+            end,
+            desc = "List implementations",
+        }
+    end
+
+    if client.supports_method("textDocument/references") then
+        maps.n["gr"] = {
+            function()
+                vim.lsp.buf.references()
+            end,
+            desc = "List references",
+        }
+    end
+
+    if client.supports_method("workspace/symbol") then
+        maps.n["<Leader>lG"] = {
+            function()
+                vim.lsp.buf.workspace_symbol("")
+            end,
+            desc = "List symbols",
+        }
+    end
+
+    if client.supports_method("textDocument/rename") then
+        maps.n["<Leader>lr"] = {
+            function()
+                vim.lsp.buf.rename()
+            end,
+            desc = "Rename symbol",
+        }
+    end
+
+    -- TODO: Check this on 0.10.0 release
+    if client.supports_method("textDocument/semanticTokens/full") and vim.lsp.semantic_tokens then
+        maps.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
+        maps.n["<Leader>uY"] = {
+            function()
+                vim.b[bufnr].semantic_tokens_enabled = not vim.b[bufnr].semantic_tokens_enabled
+                for _, active_client in ipairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do
+                    if active_client.server_capabilities.semanticTokensProvider then
+                        vim.lsp.semantic_tokens[vim.b[bufnr].semantic_tokens_enabled and "start" or "stop"](
+                            bufnr,
+                            active_client.id
+                        )
+                    end
+                end
+            end,
+            desc = "Toggle LSP semantic highlight (buffer)",
+        }
+    end
+
+    if client.supports_method("textDocument/formatting") then
+        maps.n["<Leader>lf"] = {
+            function()
+                vim.lsp.buf.format({
+                    filter = function(c)
+                        local filetype = vim.bo.filetype
+                        local n = require("null-ls")
+                        local s = require("null-ls.sources")
+                        local method = n.methods.FORMATTING
+                        local available_formatters = s.get_available(filetype, method)
+                        if #available_formatters > 0 then
+                            return c.name == "null-ls"
+                        end
+                        return true
+                    end,
+                })
+            end,
+            desc = "Format buffer",
+        }
+        maps.v["<Leader>lf"] = maps.n["<Leader>lf"]
+    end
+
+    if client.supports_method("textDocument/hover") then
+        -- TODO: Check this on 0.10.0 release
+        if vim.fn.has("nvim-0.10") == 0 then
+            maps.n["K"] = {
+                function()
+                    vim.lsp.buf.hover()
+                end,
+                desc = "Hover symbol",
+            }
+        end
+    end
+
+    if client.supports_method("textDocument/inlayHint") then
+        if vim.lsp.inlay_hint then
+            maps.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
+            -- TODO: Check this on 0.10.0 release
+            maps.n["<Leader>uH"] = {
+                function()
+                    vim.b[bufnr].inlay_hints_enabled = not vim.b[bufnr].inlay_hints_enabled
+                    if vim.lsp.inlay_hint then
+                        vim.lsp.inlay_hint.enable(bufnr, vim.b[bufnr].inlay_hints_enabled)
+                    end
+                end,
+                desc = "Toggle inlay hints",
+            }
+        end
+    end
+
+    if client.supports_method("textDocument/signatureHelp") then
+        maps.n["<Leader>lh"] = {
+            function()
+                vim.lsp.buf.signature_help()
+            end,
+            desc = "Signature help",
+        }
+    end
+
+    if f.is_available("telescope.nvim") then
+        if maps.n.gd then
+            maps.n.gd[1] = function()
+                require("telescope.builtin").lsp_definitions()
+            end
+        end
+        if maps.n.gI then
+            maps.n.gI[1] = function()
+                require("telescope.builtin").lsp_implementations()
+            end
+        end
+        if maps.n.gr then
+            maps.n.gr[1] = function()
+                require("telescope.builtin").lsp_references()
+            end
+        end
+        if maps.n["<Leader>lR"] then
+            maps.n["<Leader>lR"][1] = function()
+                require("telescope.builtin").lsp_references()
+            end
+        end
+        if maps.n.gy then
+            maps.n.gy[1] = function()
+                require("telescope.builtin").lsp_type_definitions()
+            end
+        end
+        if maps.n["<Leader>lG"] then
+            maps.n["<Leader>lG"][1] = function()
+                vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query)
+                    if query then
+                        -- word under cursor if given query is empty
+                        if query == "" then
+                            query = vim.fn.expand("<cword>")
+                        end
+                        require("telescope.builtin").lsp_workspace_symbols({
+                            query = query,
+                            prompt_title = ("Find word (%s)"):format(query),
+                        })
+                    end
+                end)
+            end
+        end
+    end
+
+    if not vim.tbl_isempty(maps.v) then
+        if maps.v["<Leader>la"] or maps.v["<Leader>lf"] then
+            maps.v["<Leader>l"] = { desc = icons.Code .. " LSP" }
+        end
+    end
+
+    return maps
+end
+
 M.surround = {
     insert = false,
     insert_line = false,
@@ -769,4 +1044,174 @@ M.aerial = {
     ["]]"] = false,
 }
 
+M.treesitter = {
+    select = {
+        ["ak"] = { query = "@block.outer", desc = "around block" },
+        ["ik"] = { query = "@block.inner", desc = "inside block" },
+        ["ac"] = { query = "@class.outer", desc = "around class" },
+        ["ic"] = { query = "@class.inner", desc = "inside class" },
+        ["a?"] = { query = "@conditional.outer", desc = "around conditional" },
+        ["i?"] = { query = "@conditional.inner", desc = "inside conditional" },
+        ["af"] = { query = "@function.outer", desc = "around function " },
+        ["if"] = { query = "@function.inner", desc = "inside function " },
+        ["al"] = { query = "@loop.outer", desc = "around loop" },
+        ["il"] = { query = "@loop.inner", desc = "inside loop" },
+        ["aa"] = { query = "@parameter.outer", desc = "around argument" },
+        ["ia"] = { query = "@parameter.inner", desc = "inside argument" },
+    },
+    move = {
+        goto_next_start = {
+            ["]k"] = { query = "@block.outer", desc = "Next block start" },
+            ["]f"] = { query = "@function.outer", desc = "Next function start" },
+            ["]a"] = { query = "@parameter.inner", desc = "Next argument start" },
+        },
+        goto_next_end = {
+            ["]K"] = { query = "@block.outer", desc = "Next block end" },
+            ["]F"] = { query = "@function.outer", desc = "Next function end" },
+            ["]A"] = { query = "@parameter.inner", desc = "Next argument end" },
+        },
+        goto_previous_start = {
+            ["[k"] = { query = "@block.outer", desc = "Previous block start" },
+            ["[f"] = { query = "@function.outer", desc = "Previous function start" },
+            ["[a"] = { query = "@parameter.inner", desc = "Previous argument start" },
+        },
+        goto_previous_end = {
+            ["[K"] = { query = "@block.outer", desc = "Previous block end" },
+            ["[F"] = { query = "@function.outer", desc = "Previous function end" },
+            ["[A"] = { query = "@parameter.inner", desc = "Previous argument end" },
+        },
+    },
+    swap = {
+        -- TODO: Move to config.keymaps
+        swap_next = {
+            [">K"] = { query = "@block.outer", desc = "Swap next block" },
+            [">F"] = { query = "@function.outer", desc = "Swap next function" },
+            [">A"] = { query = "@parameter.inner", desc = "Swap next argument" },
+        },
+        -- TODO: Move to config.keymaps
+        swap_previous = {
+            ["<K"] = { query = "@block.outer", desc = "Swap previous block" },
+            ["<F"] = { query = "@function.outer", desc = "Swap previous function" },
+            ["<A"] = { query = "@parameter.inner", desc = "Swap previous argument" },
+        },
+    },
+}
+M.telescope = function(actions)
+    return {
+        i = {
+            ["<C-n>"] = actions.cycle_history_next,
+            ["<C-p>"] = actions.cycle_history_prev,
+            ["<C-c>"] = actions.close,
+            ["<Down>"] = actions.move_selection_next,
+            ["<Up>"] = actions.move_selection_previous,
+            ["<CR>"] = actions.select_default,
+            ["<C-x>"] = actions.select_horizontal,
+            ["<C-v>"] = actions.select_vertical,
+            ["<C-t>"] = actions.select_tab,
+            ["<C-u>"] = actions.preview_scrolling_up,
+            ["<C-d>"] = actions.preview_scrolling_down,
+            ["<C-f>"] = actions.preview_scrolling_left,
+            ["<C-k>"] = actions.preview_scrolling_right,
+            ["<PageUp>"] = actions.results_scrolling_up,
+            ["<PageDown>"] = actions.results_scrolling_down,
+            ["<M-f>"] = actions.results_scrolling_left,
+            ["<M-k>"] = actions.results_scrolling_right,
+            ["<Tab>"] = actions.toggle_selection + actions.move_selection_worse,
+            ["<S-Tab>"] = actions.toggle_selection + actions.move_selection_better,
+            ["<C-q>"] = actions.send_to_qflist + actions.open_qflist,
+            ["<M-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
+            ["<C-l>"] = actions.complete_tag,
+            ["<C-/>"] = actions.which_key,
+            ["<C-_>"] = actions.which_key, -- keys from pressing <C-/>
+            ["<C-w>"] = { "<c-s-w>", type = "command" },
+            ["<C-r><C-w>"] = actions.insert_original_cword,
+            ["<C-j>"] = actions.nop,
+        },
+        n = {
+            ["<esc>"] = actions.close,
+            ["q"] = actions.close,
+            ["<CR>"] = actions.select_default,
+            ["<C-x>"] = actions.select_horizontal,
+            ["<C-v>"] = actions.select_vertical,
+            ["<C-t>"] = actions.select_tab,
+            ["<Tab>"] = actions.toggle_selection + actions.move_selection_worse,
+            ["<S-Tab>"] = actions.toggle_selection + actions.move_selection_better,
+            ["<C-q>"] = actions.send_to_qflist + actions.open_qflist,
+            ["<M-q>"] = actions.send_selected_to_qflist + actions.open_qflist,
+            ["j"] = actions.move_selection_next,
+            ["k"] = actions.move_selection_previous,
+            ["H"] = actions.move_to_top,
+            ["M"] = actions.move_to_middle,
+            ["L"] = actions.move_to_bottom,
+            ["<Down>"] = actions.move_selection_next,
+            ["<Up>"] = actions.move_selection_previous,
+            ["gg"] = actions.move_to_top,
+            ["G"] = actions.move_to_bottom,
+            ["<C-u>"] = actions.preview_scrolling_up,
+            ["<C-d>"] = actions.preview_scrolling_down,
+            ["<C-f>"] = actions.preview_scrolling_left,
+            ["<C-k>"] = actions.preview_scrolling_right,
+            ["<PageUp>"] = actions.results_scrolling_up,
+            ["<PageDown>"] = actions.results_scrolling_down,
+            ["<M-f>"] = actions.results_scrolling_left,
+            ["<M-k>"] = actions.results_scrolling_right,
+            ["?"] = actions.which_key,
+        },
+    }
+end
+
+M.cmp = {
+    ["<Up>"] = require("cmp").mapping.select_prev_item({ behavior = require("cmp").SelectBehavior.Select }),
+    ["<Down>"] = require("cmp").mapping.select_next_item({ behavior = require("cmp").SelectBehavior.Select }),
+    ["<C-p>"] = require("cmp").mapping.select_prev_item({ behavior = require("cmp").SelectBehavior.Insert }),
+    ["<C-n>"] = require("cmp").mapping.select_next_item({ behavior = require("cmp").SelectBehavior.Insert }),
+    ["<C-k>"] = require("cmp").mapping.select_prev_item({ behavior = require("cmp").SelectBehavior.Insert }),
+    ["<C-j>"] = require("cmp").mapping.select_next_item({ behavior = require("cmp").SelectBehavior.Insert }),
+    ["<C-u>"] = require("cmp").mapping(require("cmp").mapping.scroll_docs(-4), { "i", "c" }),
+    ["<C-d>"] = require("cmp").mapping(require("cmp").mapping.scroll_docs(4), { "i", "c" }),
+    ["<C-Space>"] = require("cmp").mapping(require("cmp").mapping.complete(), { "i", "c" }),
+    ["<C-y>"] = require("cmp").config.disable,
+    ["<C-e>"] = require("cmp").mapping({ i = require("cmp").mapping.abort(), c = require("cmp").mapping.close() }),
+    ["<CR>"] = require("cmp").mapping.confirm({ select = false }),
+    ["<Tab>"] = require("cmp").mapping(function(fallback)
+        local function has_words_before()
+            local line, col = (unpack or table.unpack)(vim.api.nvim_win_get_cursor(0))
+            return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
+        end
+        if require("cmp").visible() then
+            require("cmp").select_next_item()
+        elseif require("luasnip").expand_or_locally_jumpable() then
+            require("luasnip").expand_or_jump()
+        elseif has_words_before() then
+            require("cmp").complete()
+        else
+            fallback()
+        end
+    end, { "i", "s" }),
+    ["<S-Tab>"] = require("cmp").mapping(
+        function(fallback)
+            if require("cmp").visible() then
+                require("cmp").select_prev_item()
+            elseif require("luasnip").jumpable(-1) then
+                require("luasnip").jump(-1)
+            else
+                fallback()
+            end
+        end,
+        { "i", "s" }
+    ),
+}
+
+M.ufo = {
+    scrollB = "<C-b>",
+    scrollF = "<C-f>",
+    scrollU = "<C-u>",
+    scrollD = "<C-d>",
+}
+
+M.tabnine = {
+    accept_keymap = "<Tab>",
+    dismiss_keymap = "<C-]>",
+}
+
 return M
diff --git a/.config/nvim/lua/funcs.lua b/.config/nvim/lua/funcs.lua
index 48b564af..efeca7fd 100644
--- a/.config/nvim/lua/funcs.lua
+++ b/.config/nvim/lua/funcs.lua
@@ -1,5 +1,52 @@
 local M = {}
-local icons = require("config.icons")
+
+function M.lsp_on_attach(client, bufnr)
+    -- TODO: Check this on 0.10.0 release
+    if client.supports_method("textDocument/semanticTokens/full") and vim.lsp.semantic_tokens then
+        vim.b[bufnr].semantic_tokens_enabled = true
+    end
+
+    if client.supports_method("textDocument/documentHighlight") then
+        M.add_buffer_autocmd("lsp_document_highlight", bufnr, {
+            {
+                events = { "CursorHold", "CursorHoldI" },
+                desc = "highlight references when cursor holds",
+                callback = function()
+                    if not M.has_capability("textDocument/documentHighlight", { bufnr = bufnr }) then
+                        M.del_buffer_autocmd("lsp_document_highlight", bufnr)
+                        return
+                    end
+                    vim.lsp.buf.document_highlight()
+                end,
+            },
+            {
+                events = { "CursorMoved", "CursorMovedI", "BufLeave" },
+                desc = "clear references when cursor moves",
+                callback = function()
+                    vim.lsp.buf.clear_references()
+                end,
+            },
+        })
+    end
+
+    if client.supports_method("textDocument/inlayHint") then
+        if vim.b[bufnr].inlay_hints_enabled == nil then
+            vim.b[bufnr].inlay_hints_enabled = true
+        end
+        -- TODO: Check this on 0.10.0 release
+        if vim.lsp.inlay_hint and vim.b[bufnr].inlay_hints_enabled then
+            vim.lsp.inlay_hint.enable(bufnr, true)
+        end
+    end
+
+    -- TODO: Check this on 0.10.0 release
+    if vim.lsp.inlay_hint and vim.b[bufnr].inlay_hints_enabled then
+        vim.lsp.inlay_hint.enable(bufnr, true)
+    end
+
+    local maps = require("config.keymaps").lsp_maps(client, bufnr)
+    M.set_maps(maps, { buffer = bufnr })
+end
 
 function M.set_title()
     local title = " %t"
@@ -213,7 +260,7 @@ function M.set_maps(map_table, base)
     end
 end
 
-local function del_buffer_autocmd(augroup, bufnr)
+function M.del_buffer_autocmd(augroup, bufnr)
     local cmds_found, cmds = pcall(vim.api.nvim_get_autocmds, { group = augroup, buffer = bufnr })
     if cmds_found then
         vim.tbl_map(function(cmd)
@@ -222,7 +269,7 @@ local function del_buffer_autocmd(augroup, bufnr)
     end
 end
 
-local function add_buffer_autocmd(augroup, bufnr, autocmds)
+function M.add_buffer_autocmd(augroup, bufnr, autocmds)
     if not vim.tbl_islist(autocmds) then
         autocmds = { autocmds }
     end
@@ -239,7 +286,7 @@ local function add_buffer_autocmd(augroup, bufnr, autocmds)
     end
 end
 
-local function has_capability(capability, filter)
+function M.has_capability(capability, filter)
     for _, client in ipairs(vim.lsp.get_active_clients(filter)) do
         if client.supports_method(capability) then
             return true
@@ -248,311 +295,11 @@ local function has_capability(capability, filter)
     return false
 end
 
--- NOTE: LSP Keymaps here
--- TODO: Move to config.keymaps
-function M.lsp_on_attach(client, bufnr)
-    local lsp_mappings = M.empty_map_table()
-
-    lsp_mappings.n["<Leader>ld"] = {
-        function()
-            vim.diagnostic.open_float()
-        end,
-        desc = "Hover diagnostics",
-    }
-    lsp_mappings.n["[d"] = {
-        function()
-            vim.diagnostic.goto_prev()
-        end,
-        desc = "Previous diagnostic",
-    }
-    lsp_mappings.n["]d"] = {
-        function()
-            vim.diagnostic.goto_next()
-        end,
-        desc = "Next diagnostic",
-    }
-    lsp_mappings.n["gl"] = {
-        function()
-            vim.diagnostic.open_float()
-        end,
-        desc = "Hover diagnostics",
-    }
-
-    if M.is_available("telescope.nvim") then
-        lsp_mappings.n["<Leader>lD"] = {
-            function()
-                require("telescope.builtin").diagnostics()
-            end,
-            desc = "Search diagnostics",
-        }
+function M.diagnostic_setup(conf)
+    for _, sign in ipairs(conf.signs) do
+        vim.fn.sign_define(sign.name, sign)
     end
-
-    if M.is_available("mason-lspconfig.nvim") then
-        lsp_mappings.n["<Leader>li"] = { "<cmd>LspInfo<cr>", desc = "LSP information" }
-    end
-
-    if M.is_available("none-ls.nvim") then
-        lsp_mappings.n["<Leader>lI"] = { "<cmd>NullLsInfo<cr>", desc = "Null-ls information" }
-    end
-
-    if client.supports_method("textDocument/codeAction") then
-        lsp_mappings.n["<Leader>la"] = {
-            function()
-                vim.lsp.buf.code_action()
-            end,
-            desc = "Code action",
-        }
-        lsp_mappings.v["<Leader>la"] = lsp_mappings.n["<Leader>la"]
-    end
-
-    if client.supports_method("textDocument/codeLens") then
-        vim.lsp.codelens.refresh()
-        lsp_mappings.n["<Leader>ll"] = {
-            function()
-                vim.lsp.codelens.refresh()
-            end,
-            desc = "Refresh CodeLens",
-        }
-        lsp_mappings.n["<Leader>lL"] = {
-            function()
-                vim.lsp.codelens.run()
-            end,
-            desc = "Run CodeLens",
-        }
-        lsp_mappings.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
-        lsp_mappings.n["<Leader>uL"] = {
-            function()
-                vim.lsp.codelens.clear()
-            end,
-            desc = "Toggle CodeLens",
-        }
-    end
-
-    if client.supports_method("textDocument/definition") then
-        lsp_mappings.n["gd"] = {
-            function()
-                vim.lsp.buf.definition()
-            end,
-            desc = "Go to definition",
-        }
-    end
-
-    if client.supports_method("textDocument/typeDefinition") then
-        lsp_mappings.n["gy"] = {
-            function()
-                vim.lsp.buf.type_definition()
-            end,
-            desc = "Go to type definition",
-        }
-    end
-
-    if client.supports_method("textDocument/declaration") then
-        lsp_mappings.n["gD"] = {
-            function()
-                vim.lsp.buf.declaration()
-            end,
-            desc = "Go to declaration",
-        }
-    end
-
-    if client.supports_method("textDocument/implementation") then
-        lsp_mappings.n["gI"] = {
-            function()
-                vim.lsp.buf.implementation()
-            end,
-            desc = "List implementations",
-        }
-    end
-
-    if client.supports_method("textDocument/references") then
-        lsp_mappings.n["gr"] = {
-            function()
-                vim.lsp.buf.references()
-            end,
-            desc = "List references",
-        }
-    end
-
-    if client.supports_method("workspace/symbol") then
-        lsp_mappings.n["<Leader>lG"] = {
-            function()
-                vim.lsp.buf.workspace_symbol()
-            end,
-            desc = "List symbols",
-        }
-    end
-
-    if client.supports_method("textDocument/rename") then
-        lsp_mappings.n["<Leader>lr"] = {
-            function()
-                vim.lsp.buf.rename()
-            end,
-            desc = "Rename symbol",
-        }
-    end
-
-    -- TODO: Check this on 0.10.0 release
-    if client.supports_method("textDocument/semanticTokens/full") and vim.lsp.semantic_tokens then
-        vim.b[bufnr].semantic_tokens_enabled = true
-        lsp_mappings.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
-        lsp_mappings.n["<Leader>uY"] = {
-            function()
-                vim.b[bufnr].semantic_tokens_enabled = not vim.b[bufnr].semantic_tokens_enabled
-                for _, active_client in ipairs(vim.lsp.get_active_clients({ bufnr = bufnr })) do
-                    if active_client.server_capabilities.semanticTokensProvider then
-                        vim.lsp.semantic_tokens[vim.b[bufnr].semantic_tokens_enabled and "start" or "stop"](
-                            bufnr,
-                            active_client.id
-                        )
-                    end
-                end
-            end,
-            desc = "Toggle LSP semantic highlight (buffer)",
-        }
-    end
-
-    if client.supports_method("textDocument/formatting") then
-        lsp_mappings.n["<Leader>lf"] = {
-            function()
-                vim.lsp.buf.format({
-                    filter = function(c)
-                        local filetype = vim.bo.filetype
-                        local n = require("null-ls")
-                        local s = require("null-ls.sources")
-                        local method = n.methods.FORMATTING
-                        local available_formatters = s.get_available(filetype, method)
-                        if #available_formatters > 0 then
-                            return c.name == "null-ls"
-                        end
-                        return true
-                    end,
-                })
-            end,
-            desc = "Format buffer",
-        }
-        lsp_mappings.v["<Leader>lf"] = lsp_mappings.n["<Leader>lf"]
-    end
-
-    if client.supports_method("textDocument/documentHighlight") then
-        add_buffer_autocmd("lsp_document_highlight", bufnr, {
-            {
-                events = { "CursorHold", "CursorHoldI" },
-                desc = "highlight references when cursor holds",
-                callback = function()
-                    if not has_capability("textDocument/documentHighlight", { bufnr = bufnr }) then
-                        del_buffer_autocmd("lsp_document_highlight", bufnr)
-                        return
-                    end
-                    vim.lsp.buf.document_highlight()
-                end,
-            },
-            {
-                events = { "CursorMoved", "CursorMovedI", "BufLeave" },
-                desc = "clear references when cursor moves",
-                callback = function()
-                    vim.lsp.buf.clear_references()
-                end,
-            },
-        })
-    end
-
-    if client.supports_method("textDocument/hover") then
-        -- TODO: Check this on 0.10.0 release
-        if vim.fn.has("nvim-0.10") == 0 then
-            lsp_mappings.n["K"] = {
-                function()
-                    vim.lsp.buf.hover()
-                end,
-                desc = "Hover symbol",
-            }
-        end
-    end
-
-    if client.supports_method("textDocument/inlayHint") then
-        if vim.b.inlay_hints_enabled == nil then
-            vim.b.inlay_hints_enabled = true
-        end
-        -- TODO: Check this on 0.10.0 release
-        if vim.lsp.inlay_hint then
-            if vim.b.inlay_hints_enabled then
-                vim.lsp.inlay_hint.enable(bufnr, true)
-            end
-            lsp_mappings.n["<Leader>u"] = { desc = icons.Gear .. " Utility" }
-            lsp_mappings.n["<Leader>uH"] = {
-                function()
-                    vim.b[bufnr].inlay_hints_enabled = not vim.b[bufnr].inlay_hints_enabled
-                    if vim.lsp.inlay_hint then
-                        vim.lsp.inlay_hint.enable(bufnr, vim.b[bufnr].inlay_hints_enabled)
-                    end
-                end,
-                desc = "Toggle inlay hints",
-            }
-        end
-    end
-
-    if client.supports_method("textDocument/signatureHelp") then
-        lsp_mappings.n["<Leader>lh"] = {
-            function()
-                vim.lsp.buf.signature_help()
-            end,
-            desc = "Signature help",
-        }
-    end
-
-    if M.is_available("telescope.nvim") then
-        if lsp_mappings.n.gd then
-            lsp_mappings.n.gd[1] = function()
-                require("telescope.builtin").lsp_definitions()
-            end
-        end
-        if lsp_mappings.n.gI then
-            lsp_mappings.n.gI[1] = function()
-                require("telescope.builtin").lsp_implementations()
-            end
-        end
-        if lsp_mappings.n.gr then
-            lsp_mappings.n.gr[1] = function()
-                require("telescope.builtin").lsp_references()
-            end
-        end
-        if lsp_mappings.n["<Leader>lR"] then
-            lsp_mappings.n["<Leader>lR"][1] = function()
-                require("telescope.builtin").lsp_references()
-            end
-        end
-        if lsp_mappings.n.gy then
-            lsp_mappings.n.gy[1] = function()
-                require("telescope.builtin").lsp_type_definitions()
-            end
-        end
-        if lsp_mappings.n["<Leader>lG"] then
-            lsp_mappings.n["<Leader>lG"][1] = function()
-                vim.ui.input({ prompt = "Symbol Query: (leave empty for word under cursor)" }, function(query)
-                    if query then
-                        -- word under cursor if given query is empty
-                        if query == "" then
-                            query = vim.fn.expand("<cword>")
-                        end
-                        require("telescope.builtin").lsp_workspace_symbols({
-                            query = query,
-                            prompt_title = ("Find word (%s)"):format(query),
-                        })
-                    end
-                end)
-            end
-        end
-    end
-
-    if not vim.tbl_isempty(lsp_mappings.v) then
-        if lsp_mappings.v["<Leader>l"] then
-            lsp_mappings.v["<Leader>l"] = { desc = icons.Code .. " LSP" }
-        end
-        if lsp_mappings.v["<Leader>u"] then
-            lsp_mappings.v["<Leader>u"] = { desc = icons.Code .. " LSP" }
-        end
-    end
-
-    M.set_maps(lsp_mappings, { buffer = bufnr })
+    vim.diagnostic.config(conf.diagnostic)
 end
 
 function M.has_value(table, value)
diff --git a/.config/nvim/lua/plugins/cmp/cmp.lua b/.config/nvim/lua/plugins/cmp/cmp.lua
index 523e1a1a..0b6edb52 100644
--- a/.config/nvim/lua/plugins/cmp/cmp.lua
+++ b/.config/nvim/lua/plugins/cmp/cmp.lua
@@ -22,11 +22,6 @@ M.opts = function()
         winhighlight = "Normal:NormalFloat,FloatBorder:FloatBorder,CursorLine:PmenuSel,Search:None",
     }
 
-    local function has_words_before()
-        local line, col = (unpack or table.unpack)(vim.api.nvim_win_get_cursor(0))
-        return col ~= 0 and vim.api.nvim_buf_get_lines(0, line - 1, line, true)[1]:sub(col, col):match("%s") == nil
-    end
-
     return {
         enabled = function()
             local dap_prompt = require("funcs").is_available("cmp-dap")
@@ -64,41 +59,7 @@ M.opts = function()
             completion = cmp.config.window.bordered(border_opts),
             documentation = cmp.config.window.bordered(border_opts),
         },
-        -- TODO: Move to config.keymaps
-        mapping = {
-            ["<Up>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Select }),
-            ["<Down>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Select }),
-            ["<C-p>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
-            ["<C-n>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
-            ["<C-k>"] = cmp.mapping.select_prev_item({ behavior = cmp.SelectBehavior.Insert }),
-            ["<C-j>"] = cmp.mapping.select_next_item({ behavior = cmp.SelectBehavior.Insert }),
-            ["<C-u>"] = cmp.mapping(cmp.mapping.scroll_docs(-4), { "i", "c" }),
-            ["<C-d>"] = cmp.mapping(cmp.mapping.scroll_docs(4), { "i", "c" }),
-            ["<C-Space>"] = cmp.mapping(cmp.mapping.complete(), { "i", "c" }),
-            ["<C-y>"] = cmp.config.disable,
-            ["<C-e>"] = cmp.mapping({ i = cmp.mapping.abort(), c = cmp.mapping.close() }),
-            ["<CR>"] = cmp.mapping.confirm({ select = false }),
-            ["<Tab>"] = cmp.mapping(function(fallback)
-                if cmp.visible() then
-                    cmp.select_next_item()
-                elseif luasnip.expand_or_locally_jumpable() then
-                    luasnip.expand_or_jump()
-                elseif has_words_before() then
-                    cmp.complete()
-                else
-                    fallback()
-                end
-            end, { "i", "s" }),
-            ["<S-Tab>"] = cmp.mapping(function(fallback)
-                if cmp.visible() then
-                    cmp.select_prev_item()
-                elseif luasnip.jumpable(-1) then
-                    luasnip.jump(-1)
-                else
-                    fallback()
-                end
-            end, { "i", "s" }),
-        },
+        mapping = require("config.keymaps").cmp,
         sources = cmp.config.sources({
             { name = "nvim_lsp", priority = 1000 },
             { name = "luasnip",  priority = 750 },
diff --git a/.config/nvim/lua/plugins/dap/dap.lua b/.config/nvim/lua/plugins/dap/dap.lua
new file mode 100644
index 00000000..c567c28f
--- /dev/null
+++ b/.config/nvim/lua/plugins/dap/dap.lua
@@ -0,0 +1,12 @@
+local icons = require("config.icons")
+local signs = {
+    { name = "DapStopped",             text = icons.DapStopped,             texthl = "DiagnosticWarn" },
+    { name = "DapBreakpoint",          text = icons.DapBreakpoint,          texthl = "DiagnosticInfo" },
+    { name = "DapBreakpointRejected",  text = icons.DapBreakpointRejected,  texthl = "DiagnosticError" },
+    { name = "DapBreakpointCondition", text = icons.DapBreakpointCondition, texthl = "DiagnosticInfo" },
+    { name = "DapLogPoint",            text = icons.DapLogPoint,            texthl = "DiagnosticInfo" },
+}
+
+for _, sign in ipairs(signs) do
+    vim.fn.sign_define(sign.name, sign)
+end
diff --git a/.config/nvim/lua/plugins/lsp/mason-lspconfig.lua b/.config/nvim/lua/plugins/lsp/mason-lspconfig.lua
index 856a3ab8..b82a0625 100644
--- a/.config/nvim/lua/plugins/lsp/mason-lspconfig.lua
+++ b/.config/nvim/lua/plugins/lsp/mason-lspconfig.lua
@@ -28,46 +28,6 @@ M.event = { "BufReadPost", "BufNewFile", "BufNew" }
 M.cmd = { "LspInstall", "LspUninstall" }
 
 M.config = function()
-    local icons = require("config.icons")
-    local signs = {
-        { name = "DiagnosticSignError",    text = icons.Error,                  texthl = "DiagnosticSignError" },
-        { name = "DiagnosticSignWarn",     text = icons.Warn,                   texthl = "DiagnosticSignWarn" },
-        { name = "DiagnosticSignHint",     text = icons.Hint,                   texthl = "DiagnosticSignHint" },
-        { name = "DiagnosticSignInfo",     text = icons.Info,                   texthl = "DiagnosticSignInfo" },
-        { name = "DapStopped",             text = icons.DapStopped,             texthl = "DiagnosticWarn" },
-        { name = "DapBreakpoint",          text = icons.DapBreakpoint,          texthl = "DiagnosticInfo" },
-        { name = "DapBreakpointRejected",  text = icons.DapBreakpointRejected,  texthl = "DiagnosticError" },
-        { name = "DapBreakpointCondition", text = icons.DapBreakpointCondition, texthl = "DiagnosticInfo" },
-        { name = "DapLogPoint",            text = icons.DapLogPoint,            texthl = "DiagnosticInfo" },
-    }
-    for _, sign in ipairs(signs) do
-        vim.fn.sign_define(sign.name, sign)
-    end
-
-    vim.diagnostic.config({
-        virtual_text = true,
-        signs = {
-            text = {
-                [vim.diagnostic.severity.ERROR] = icons.Error,
-                [vim.diagnostic.severity.HINT] = icons.Hint,
-                [vim.diagnostic.severity.WARN] = icons.Warn,
-                [vim.diagnostic.severity.INFO] = icons.Info,
-            },
-            active = signs,
-        },
-        update_in_insert = true,
-        underline = true,
-        severity_sort = true,
-        float = {
-            focused = false,
-            style = "minimal",
-            border = "rounded",
-            source = "always",
-            header = "",
-            prefix = "",
-        },
-    })
-
     vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(vim.lsp.handlers.hover, { border = "rounded", silent = true })
     vim.lsp.handlers["textDocument/signatureHelp"] =
         vim.lsp.with(vim.lsp.handlers.signature_help, { border = "rounded", silent = true })
diff --git a/.config/nvim/lua/plugins/misc/tabnine.lua b/.config/nvim/lua/plugins/misc/tabnine.lua
index 439de931..1f4be3c8 100644
--- a/.config/nvim/lua/plugins/misc/tabnine.lua
+++ b/.config/nvim/lua/plugins/misc/tabnine.lua
@@ -8,10 +8,11 @@ M.main = "tabnine"
 
 M.opts = function()
     local colors = require("tokyonight.colors").setup({ transform = true })
+    local maps = require("config.keymaps").tabnine
     return {
         disable_auto_comment = true,
-        accept_keymap = "<Tab>",
-        dismiss_keymap = "<C-]>",
+        accept_keymap = maps.accept_keymap,
+        dismiss_keymap = maps.dismiss_keymap,
         debounce_ms = 800,
         suggestion_color = { gui = colors.fg_gutter, cterm = 244 },
         exclude_filetypes = { "TelescopePrompt", "NvimTree" },
diff --git a/.config/nvim/lua/plugins/misc/telescope.lua b/.config/nvim/lua/plugins/misc/telescope.lua
index 78350c9b..f6daa886 100644
--- a/.config/nvim/lua/plugins/misc/telescope.lua
+++ b/.config/nvim/lua/plugins/misc/telescope.lua
@@ -1,12 +1,11 @@
 local M = { "nvim-telescope/telescope.nvim" }
 local icons = require("config.icons")
 
-M.dependencies = { "telescope-fzf-native.nvim" }
+M.dependencies = { "telescope-fzf-native.nvim", "aerial.nvim" }
 
 M.cmd = "Telescope"
 
-local opts = function()
-    local actions = require("telescope.actions")
+M.opts = function()
     return {
         defaults = {
             git_worktrees = vim.g.git_worktrees,
@@ -20,29 +19,18 @@ local opts = function()
                 height = 0.80,
                 preview_cutoff = 120,
             },
-            -- TODO: Move to config.keymaps
-            mappings = {
-                i = {
-                    ["<C-n>"] = actions.cycle_history_next,
-                    ["<C-p>"] = actions.cycle_history_prev,
-                    ["<C-j>"] = actions.move_selection_next,
-                    ["<C-k>"] = actions.move_selection_previous,
-                },
-                n = { q = actions.close },
-            },
         },
     }
 end
 
-M.config = function()
-    local telescope = require("telescope")
-    telescope.setup(opts())
-    if pcall(require, "aerial") then
-        telescope.load_extension("aerial")
-    end
-    if require("funcs").is_available("telescope-fzf-native.nvim") then
-        telescope.load_extension("fzf")
-    end
+M.config = function(plug, opts)
+    local name = require("lazy.core.loader").get_main(plug)
+    local m = require(name)
+    local actions = require(name .. ".actions")
+    opts.mappings = require("config.keymaps").telescope(actions)
+    m.setup(opts)
+    m.load_extension("aerial")
+    m.load_extension("fzf")
 end
 
 return M
diff --git a/.config/nvim/lua/plugins/misc/treesitter.lua b/.config/nvim/lua/plugins/misc/treesitter.lua
index 4e370042..fcb40a34 100644
--- a/.config/nvim/lua/plugins/misc/treesitter.lua
+++ b/.config/nvim/lua/plugins/misc/treesitter.lua
@@ -25,6 +25,7 @@ M.init = function(plugin)
     require("nvim-treesitter.query_predicates")
 end
 
+local maps = require("config.keymaps").treesitter
 M.opts = {
     ensure_installed = { "lua", "yaml", "json", "bash" },
     autotag = { enable = true },
@@ -41,64 +42,20 @@ M.opts = {
         select = {
             enable = true,
             lookahead = true,
-            -- TODO: Move to config.keymaps
-            keymaps = {
-                ["ak"] = { query = "@block.outer", desc = "around block" },
-                ["ik"] = { query = "@block.inner", desc = "inside block" },
-                ["ac"] = { query = "@class.outer", desc = "around class" },
-                ["ic"] = { query = "@class.inner", desc = "inside class" },
-                ["a?"] = { query = "@conditional.outer", desc = "around conditional" },
-                ["i?"] = { query = "@conditional.inner", desc = "inside conditional" },
-                ["af"] = { query = "@function.outer", desc = "around function " },
-                ["if"] = { query = "@function.inner", desc = "inside function " },
-                ["al"] = { query = "@loop.outer", desc = "around loop" },
-                ["il"] = { query = "@loop.inner", desc = "inside loop" },
-                ["aa"] = { query = "@parameter.outer", desc = "around argument" },
-                ["ia"] = { query = "@parameter.inner", desc = "inside argument" },
-            },
+            keymaps = maps.select,
         },
         move = {
             enable = true,
             set_jumps = true,
-            -- TODO: Move to config.keymaps
-            goto_next_start = {
-                ["]k"] = { query = "@block.outer", desc = "Next block start" },
-                ["]f"] = { query = "@function.outer", desc = "Next function start" },
-                ["]a"] = { query = "@parameter.inner", desc = "Next argument start" },
-            },
-            -- TODO: Move to config.keymaps
-            goto_next_end = {
-                ["]K"] = { query = "@block.outer", desc = "Next block end" },
-                ["]F"] = { query = "@function.outer", desc = "Next function end" },
-                ["]A"] = { query = "@parameter.inner", desc = "Next argument end" },
-            },
-            -- TODO: Move to config.keymaps
-            goto_previous_start = {
-                ["[k"] = { query = "@block.outer", desc = "Previous block start" },
-                ["[f"] = { query = "@function.outer", desc = "Previous function start" },
-                ["[a"] = { query = "@parameter.inner", desc = "Previous argument start" },
-            },
-            -- TODO: Move to config.keymaps
-            goto_previous_end = {
-                ["[K"] = { query = "@block.outer", desc = "Previous block end" },
-                ["[F"] = { query = "@function.outer", desc = "Previous function end" },
-                ["[A"] = { query = "@parameter.inner", desc = "Previous argument end" },
-            },
+            goto_next_start = maps.move.goto_next_start,
+            goto_next_end = maps.move.goto_next_end,
+            goto_previous_start = maps.move.goto_previous_start,
+            goto_previous_end = maps.move.goto_previous_end,
         },
         swap = {
             enable = true,
-            -- TODO: Move to config.keymaps
-            swap_next = {
-                [">K"] = { query = "@block.outer", desc = "Swap next block" },
-                [">F"] = { query = "@function.outer", desc = "Swap next function" },
-                [">A"] = { query = "@parameter.inner", desc = "Swap next argument" },
-            },
-            -- TODO: Move to config.keymaps
-            swap_previous = {
-                ["<K"] = { query = "@block.outer", desc = "Swap previous block" },
-                ["<F"] = { query = "@function.outer", desc = "Swap previous function" },
-                ["<A"] = { query = "@parameter.inner", desc = "Swap previous argument" },
-            },
+            swap_next = maps.swap.swap_next,
+            swap_previous = maps.swap.swap_previous,
         },
     },
 }
diff --git a/.config/nvim/lua/plugins/misc/ufo.lua b/.config/nvim/lua/plugins/misc/ufo.lua
index 26dceccb..3121f75d 100644
--- a/.config/nvim/lua/plugins/misc/ufo.lua
+++ b/.config/nvim/lua/plugins/misc/ufo.lua
@@ -1,17 +1,12 @@
-local M = { "nvim-treesitter/nvim-treesitter" }
+local M = { "kevinhwang91/nvim-ufo" }
 
 M.event = { "InsertEnter" }
 
-M.dependencies = { "kevinhwang91/promise-async" }
+M.dependencies = { "kevinhwang91/promise-async", "nvim-treesitter" }
 
 M.opts = {
     preview = {
-        mappings = {
-            scrollB = "<C-b>",
-            scrollF = "<C-f>",
-            scrollU = "<C-u>",
-            scrollD = "<C-d>",
-        },
+        mappings = require("config.keymaps").ufo,
     },
     provider_selector = function(_, filetype, buftype)
         local function handleFallbackException(bufnr, err, providerName)