diff --git a/dotfiles/neovim/lazy-lock.json b/dotfiles/neovim/lazy-lock.json
index 28cb7a0..9d8e751 100644
--- a/dotfiles/neovim/lazy-lock.json
+++ b/dotfiles/neovim/lazy-lock.json
@@ -10,6 +10,7 @@
   "cmp-nvim-lsp": { "branch": "main", "commit": "0e6b2ed705ddcff9738ec4ea838141654f12eeef" },
   "cmp-path": { "branch": "main", "commit": "91ff86cd9c29299a64f968ebb45846c485725f23" },
   "cmp_luasnip": { "branch": "master", "commit": "18095520391186d634a0045dacaa346291096566" },
+  "crates.nvim": { "branch": "main", "commit": "1d4bb1e7a0fe8bae3f97061be5fbf6f9081a27e2" },
   "dashboard-nvim": { "branch": "master", "commit": "6e0a35343fc37a2a2ca5ab1734d60fcc06c4cf01" },
   "dhall-vim": { "branch": "master", "commit": "68500ef46ff3706f46c99db3be7a0c8abcf6a3ae" },
   "dressing.nvim": { "branch": "master", "commit": "5f44f829481640be0f96759c965ae22a3bcaf7ce" },
@@ -53,6 +54,7 @@
   "purescript-vim": { "branch": "main", "commit": "82348352e6568fcc0385bd7c99a8ead3a479feea" },
   "rasi.vim": { "branch": "main", "commit": "eac9969cf935cd4380987dc99bfa10d69d3f34a6" },
   "rose-pine": { "branch": "main", "commit": "69f015b9522b468b320fbd56eb4ae72af4d5074f" },
+  "rust-tools.nvim": { "branch": "master", "commit": "71d2cf67b5ed120a0e31b2c8adb210dd2834242f" },
   "scrap.nvim": { "branch": "main", "commit": "16db44ae9403ec9c4b140394f294475d1af80a18" },
   "smart-splits.nvim": { "branch": "master", "commit": "52b521618511b3a874255c8a717ace7155fd5f21" },
   "telescope-file-browser.nvim": { "branch": "master", "commit": "94fe37a1ea217dd2f90d91222bc1531521146ac3" },
diff --git a/dotfiles/neovim/lua/my/keymaps.lua b/dotfiles/neovim/lua/my/keymaps.lua
index b36b84b..6fd8e0a 100644
--- a/dotfiles/neovim/lua/my/keymaps.lua
+++ b/dotfiles/neovim/lua/my/keymaps.lua
@@ -31,11 +31,17 @@ end
 ---@param to string|function
 ---@param desc string
 ---@param silent boolean|nil
-function M.nmap(from, to, desc, silent)
+---@param isLocal boolean|nil
+function M.nmap(from, to, desc, silent, isLocal)
   if silent == nil then
     silent = true
   end
-  vim.keymap.set("n", from, to, { desc = desc })
+
+  if isLocal == nil then
+    isLocal = false
+  end
+
+  vim.keymap.set("n", from, to, { desc = desc, silent = silent, buffer = isLocal })
 end
 
 -- }}}
diff --git a/dotfiles/neovim/lua/my/plugins/crates.lua b/dotfiles/neovim/lua/my/plugins/crates.lua
new file mode 100644
index 0000000..f095fca
--- /dev/null
+++ b/dotfiles/neovim/lua/my/plugins/crates.lua
@@ -0,0 +1,51 @@
+local K = require("my.keymaps")
+local M = {
+  "saecki/crates.nvim",
+  event = "BufReadPost Cargo.toml",
+  dependencies = { "nvim-lua/plenary.nvim" },
+  config = function()
+    local crates = require("crates")
+
+    crates.setup({
+      null_ls = {
+        enabled = true,
+        name = "crates.nvim",
+      },
+    })
+
+    vim.api.nvim_create_autocmd("InsertEnter", {
+      group = vim.api.nvim_create_augroup("CmpSourceCargo", {}),
+      pattern = "Cargo.toml",
+      callback = function()
+        require("cmp").setup.buffer({ sources = { { name = "crates" } } })
+      end,
+    })
+
+    local function nmap(from, to, desc)
+      K.nmap(from, to, desc, true, true)
+    end
+
+    nmap("<leader>lct", crates.toggle, "[c]rates [t]oggle")
+    nmap("<leader>lcr", crates.reload, "[c]rates [r]efresh")
+
+    nmap("<leader>lcH", crates.open_homepage, "[c]rates [H]omepage")
+    nmap("<leader>lcR", crates.open_repository, "[c]rates [R]repository")
+    nmap("<leader>lcD", crates.open_documentation, "[c]rates [D]ocumentation")
+    nmap("<leader>lcC", crates.open_crates_io, "[c]rates [C]rates.io")
+
+    nmap("<leader>lcv", crates.show_versions_popup, "[c]rates [v]ersions")
+    nmap("<leader>lcf", crates.show_features_popup, "[c]rates [f]eatures")
+    nmap("<leader>lcd", crates.show_dependencies_popup, "[c]rates [d]eps")
+    nmap("K", crates.show_popup, "crates popup")
+
+    local wk = require("which-key")
+
+    wk.register({
+      ["<leader>lc"] = {
+        name = "[l]ocal [c]rates",
+      },
+    })
+  end,
+}
+
+return M
diff --git a/dotfiles/neovim/lua/my/plugins/init.lua b/dotfiles/neovim/lua/my/plugins/init.lua
index 089d8ac..5c6db22 100644
--- a/dotfiles/neovim/lua/my/plugins/init.lua
+++ b/dotfiles/neovim/lua/my/plugins/init.lua
@@ -173,7 +173,7 @@ return {
 
   {
     "ruifm/gitlinker.nvim", -- generate permalinks for code
-    -- dependencies = { "plenary.nvim" },
+    dependencies = { "nvim-lua/plenary.nvim" },
     opts = {
       mappings = "<leader>yg",
     },
diff --git a/dotfiles/neovim/lua/my/plugins/lspconfig.lua b/dotfiles/neovim/lua/my/plugins/lspconfig.lua
index 41272da..4c8a177 100644
--- a/dotfiles/neovim/lua/my/plugins/lspconfig.lua
+++ b/dotfiles/neovim/lua/my/plugins/lspconfig.lua
@@ -10,7 +10,7 @@ local lspconfig = {
       "folke/neodev.nvim",
       config = true,
     },
-    "hrsh7th/cmp-nvim-lsp",
+    "simrat39/rust-tools.nvim",
   },
   cond = env.vscode.not_active(),
 }
@@ -28,19 +28,6 @@ local M = {
 }
 
 function M.on_attach(client, bufnr)
-  -- {{{ Auto format
-  local function format()
-    vim.lsp.buf.format({ async = false, bufnr = bufnr })
-  end
-
-  if false and client.supports_method("textDocument/formatting") then
-    vim.api.nvim_create_autocmd("BufWritePre", {
-      group = vim.api.nvim_create_augroup("LspFormatting", { clear = false }),
-      buffer = bufnr,
-      callback = format,
-    })
-  end
-  -- }}}
   -- {{{ Keymap helpers
   local opts = function(desc)
     return { noremap = true, silent = true, desc = desc, buffer = bufnr }
@@ -50,6 +37,20 @@ function M.on_attach(client, bufnr)
     vim.keymap.set("n", from, to, opts(desc))
   end
   -- }}}
+  -- {{{ Auto format
+  local function format()
+    vim.lsp.buf.format({ async = false, bufnr = bufnr })
+  end
+
+  if client.supports_method("textDocument/formatting") then
+    nmap("<leader>F", format, "[F]ormat")
+    -- vim.api.nvim_create_autocmd("BufWritePre", {
+    --   group = vim.api.nvim_create_augroup("LspFormatting", { clear = false }),
+    --   buffer = bufnr,
+    --   callback = format,
+    -- })
+  end
+  -- }}}
   -- {{{ Go to declaration / references / implementation
   nmap("gd", vim.lsp.buf.definition, "[G]o to [d]efinition")
   nmap("gi", vim.lsp.buf.implementation, "[G]o to [i]mplementation")
@@ -57,12 +58,13 @@ function M.on_attach(client, bufnr)
   -- }}}
   -- {{{ Hover
   -- Note: diagnostics are already covered in keymaps.lua
-  nmap("K", vim.lsp.buf.hover, "Hover")
+  if client.supports_method("textDocument/hover") then
+    nmap("K", vim.lsp.buf.hover, "Hover")
+  end
   nmap("L", vim.lsp.buf.signature_help, "Signature help")
   -- }}}
   -- {{{ Code actions
   nmap("<leader>c", vim.lsp.buf.code_action, "[C]ode actions")
-  nmap("<leader>F", format, "[F]ormat")
   nmap("<leader>li", "<cmd>LspInfo<cr>", "[L]sp [i]nfo")
 
   vim.keymap.set("n", "<leader>rn", function()
@@ -165,7 +167,6 @@ local servers = {
   nil_ls = {},
   cssls = {},
   jsonls = {},
-  rust_analyzer = {},
   dhall_lsp_server = {},
   -- pylsp = {},
   -- pyright = {},
diff --git a/dotfiles/neovim/lua/my/plugins/neogit.lua b/dotfiles/neovim/lua/my/plugins/neogit.lua
index 978a3dc..2f2c9d2 100644
--- a/dotfiles/neovim/lua/my/plugins/neogit.lua
+++ b/dotfiles/neovim/lua/my/plugins/neogit.lua
@@ -3,7 +3,7 @@ local env = require("my.helpers.env")
 local M = {
   "TimUntersberger/neogit",
 
-  -- dependencies = { "plenary.nvim" },
+  dependencies = { "nvim-lua/plenary.nvim" },
 
   cmd = "Neogit",
   cond = env.firenvim.not_active() and env.vscode.not_active(),
diff --git a/dotfiles/neovim/lua/my/plugins/rust-tools.lua b/dotfiles/neovim/lua/my/plugins/rust-tools.lua
new file mode 100644
index 0000000..f905bd9
--- /dev/null
+++ b/dotfiles/neovim/lua/my/plugins/rust-tools.lua
@@ -0,0 +1,17 @@
+local K = require("my.keymaps")
+local lspconfig = require("my.plugins.lspconfig")
+
+local M = {
+  "simrat39/rust-tools.nvim",
+  config = function()
+    require("rust-tools").setup({
+      server = {
+        on_attach = lspconfig.on_attach,
+      },
+    })
+
+    K.nmap("<leader>lc", "<cmd>RustOpenCargo<cr>", "Open [c]argo.toml", true, true)
+  end,
+}
+
+return M
diff --git a/home/adrielus/features/neovim/default.nix b/home/adrielus/features/neovim/default.nix
index 9581bea..aaffcaf 100644
--- a/home/adrielus/features/neovim/default.nix
+++ b/home/adrielus/features/neovim/default.nix
@@ -45,6 +45,7 @@ let
     tree-sitter # Syntax highlighting
     libstdcxx5 # Required by treesitter aparently
     python310Packages.jupytext # Convert between jupyter notebooks and python files
+    graphviz # For rust crate graph
 
     # Preview
     zathura # Pdf reader