██████╗██╗ █████╗ ███╗ ██╗██╗ ██╗███████╗██████╗
██╔════╝██║ ██╔══██╗████╗ ██║██║ ██╔╝██╔════╝██╔══██╗
██║ ██║ ███████║██╔██╗ ██║█████╔╝ █████╗ ██████╔╝
██║ ██║ ██╔══██║██║╚██╗██║██╔═██╗ ██╔══╝ ██╔══██╗
╚██████╗███████╗██║ ██║██║ ╚████║██║ ██╗███████╗██║ ██║
╚═════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
for neovim
Semantic code navigation and focused editing for Rust projects.
Stop thinking about files. Start thinking about code.
- Semantic Search - Find functions, structs, traits, and impl blocks by their qualified path (e.g.,
crate::handlers::api::MyStruct::new) - Focused Editing - Edit a single code unit with the rest of the file hidden away
- Hierarchical Navigation - Impl blocks are treated as containers; navigate to
Type::impl::method - Project-Wide Indexing - Search across your entire Rust project, not just open buffers
- Telescope Integration - Fuzzy finding with preview support
- Winbar Breadcrumbs - Always know where you are:
crate > module > Type > impl > method
┌─────────────────────────────────────────────────────────────────────────┐
│ Clanker > MyStruct │
├─────────────────────────────────────────────────────────────────────────┤
│ > crate::handlers::api::MyStruct [struct] pub struct ... │
│ crate::handlers::api::impl MyStruct::new [method] pub fn new() │
│ crate::handlers::api::impl MyStruct::process[method] pub fn proce...│
│ crate::handlers::api::impl Debug for MyStruct::fmt │
│ crate::models::MyStructBuilder [struct] pub struct ... │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ <CR> edit <C-v> vsplit <C-x> split <Tab> preview │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ [clanker] crate > handlers > api > impl MyStruct > new │
├─────────────────────────────────────────────────────────────────────────┤
│ 1 │ pub fn new() -> Self { │
│ 2 │ Self { │
│ 3 │ data: Vec::new(), │
│ 4 │ valid: false, │
│ 5 │ timestamp: None, │
│ 6 │ } │
│ 7 │ } │
│~ │
│~ │
├─────────────────────────────────────────────────────────────────────────┤
│ NORMAL clanker://crate::handlers::api::impl MyStruct::new 1:1 │
└─────────────────────────────────────────────────────────────────────────┘
:w = save changes back to original file
:q = close and return to original buffer
<Space>cn = next sibling method
<Space>cp = previous sibling method
<Space>cu = go up to impl block
┌─────────────────────────────────────────────────────────────────────────┐
│ [clanker] crate > handlers > api > impl MyStruct │
├─────────────────────────────────────────────────────────────────────────┤
│ 1 │ impl MyStruct { │
│ 2 │ pub fn new() -> Self { │
│ 3 │ Self { │
│ 4 │ data: Vec::new(), │
│ 5 │ valid: false, │
│ 6 │ } │
│ 7 │ } │
│ 8 │ │
│ 9 │ pub fn process(&mut self) -> Result<()> { │
│ 10 │ self.validate()?; │
│ 11 │ Ok(()) │
│ 12 │ } │
│ 13 │ │
│ 14 │ fn validate(&self) -> Result<()> { │
│ 15 │ if self.data.is_empty() { │
│ 16 │ return Err(Error::Empty); │
│ 17 │ } │
│ 18 │ Ok(()) │
│ 19 │ } │
│ 20 │ } │
└─────────────────────────────────────────────────────────────────────────┘
- Neovim >= 0.9.0
- telescope.nvim
- plenary.nvim
- Tree-sitter Rust parser (for parsing)
- Optional: rust-analyzer (for enhanced features)
{
"your-username/clanker.nvim",
dependencies = {
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim",
"nvim-treesitter/nvim-treesitter",
},
ft = "rust",
config = function()
require("clanker").setup({
-- options (see Configuration below)
})
require("telescope").load_extension("clanker")
end,
}use {
"your-username/clanker.nvim",
requires = {
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim",
"nvim-treesitter/nvim-treesitter",
},
config = function()
require("clanker").setup()
require("telescope").load_extension("clanker")
end,
}The repository includes a flake.nix for Nix users that provides a complete development environment.
# Clone the repo
git clone https://github.com/your-username/clanker.nvim
cd clanker.nvim
# Enter the development shell
nix develop
# Open a Rust project
cd /path/to/your/rust/project
nvim src/main.rs# In your flake inputs
inputs.clanker-nvim.url = "github:your-username/clanker.nvim";
# In your neovim configuration
programs.neovim = {
plugins = [
{
plugin = inputs.clanker-nvim.packages.${system}.default;
type = "lua";
config = ''
require("clanker").setup()
require("telescope").load_extension("clanker")
'';
}
];
};{
vim.lazy.plugins.clanker-nvim = {
package = inputs.clanker-nvim.packages.${system}.default;
setupModule = "clanker";
setupOpts = {
winbar_context = true;
previewer = true;
};
after = ''
require("telescope").load_extension("clanker")
'';
};
}require("clanker").setup({
-- Provider selection: "lsp", "treesitter", or nil (auto-detect)
provider = nil,
-- Show original file line numbers in focused edit
show_original_line_numbers = true,
-- Lines of context around the code unit (0 = just the unit)
context_lines = 0,
-- Auto-close focused edit when leaving the buffer
auto_close_on_leave = false,
-- Enable telescope previewer
previewer = true,
-- Show breadcrumb path in winbar
winbar_context = true,
-- Set default keymaps
set_default_keymaps = true,
-- Enable statusline integration
statusline_integration = true,
})| Command | Description |
|---|---|
:Clanker [query] |
Search all code units |
:ClankerFunctions [query] |
Search functions and methods |
:ClankerTypes [query] |
Search structs, enums, type aliases |
:ClankerTraits [query] |
Search traits |
:ClankerImpls [query] |
Search impl blocks |
:ClankerMembers [Type] |
Show members of a type |
:ClankerClose |
Close focused edit view |
:ClankerNext |
Go to next sibling |
:ClankerPrev |
Go to previous sibling |
:ClankerUp |
Go to parent code unit |
:ClankerDown |
Go to first child |
| Keymap | Action |
|---|---|
<Space>cc |
Search all code units |
<Space>cf |
Search functions/methods |
<Space>ct |
Search types |
<Space>ci |
Search impl blocks |
<Space>cm |
Search members |
| Keymap | Action |
|---|---|
:q / :bd |
Close and return to original buffer |
:w |
Save changes to original file |
<Space>cn |
Next sibling |
<Space>cp |
Previous sibling |
<Space>cu |
Go to parent |
<Space>cd |
Go to first child |
Add clanker status to your statusline:
-- lualine.nvim example
require("lualine").setup({
sections = {
lualine_c = {
{ require("clanker").statusline },
},
},
})When in focused edit mode, displays: [clanker] crate::module::Type::method 1/3
- Indexing: On first search, clanker scans all
.rsfiles in your project (excludingtarget/) - Parsing: Uses tree-sitter to extract code units (functions, structs, enums, traits, impl blocks, etc.)
- Path Building: Derives qualified paths from file locations and AST structure
- Caching: Results are cached for 30 seconds for fast subsequent searches
- Focused Editing: Creates a special buffer containing only the selected code unit
- Splicing: On save, changes are written back to the exact location in the original file
┌─────────────────────────────────────────────────────────────────┐
│ clanker.nvim │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │
│ │ Pickers │ │ Focus │ │ Backend │ │ Providers │ │
│ │ │ │ │ │ │ │ │ │
│ │Telescope│──▶│ Focused │──▶│ Router │──▶│ ┌─────────────┐ │ │
│ │ Search │ │ Editing │ │ │ │ │ Tree-sitter │ │ │
│ └─────────┘ └─────────┘ └─────────┘ │ └─────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ LSP │ │ │
│ │ └─────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ (sidecar) │ │ │
│ │ └─────────────┘ │ │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
The backend is modular - currently supports tree-sitter and LSP providers, with architecture ready for a custom Rust sidecar for enhanced analysis.
Q: Why "clanker"? A: It's a Rust plugin. Rust clanks. The name stuck.
Q: Does it work with workspaces?
A: Yes! It finds the nearest Cargo.toml and indexes all .rs files from there.
Q: Can I use it with other languages? A: Currently Rust-only. The architecture could support other languages, but that's not planned.
Q: Why not just use LSP goto-definition? A: Different use case. Clanker is for finding code when you know the semantic path but not the file, and for focused editing to reduce visual noise.
MIT
Contributions welcome! Please open an issue first to discuss major changes.
┌─────────────────────────────────────────────────────────────────┐
│ │
│ "I used to grep through files. Now I just think in paths." │
│ - A Happy Rustacean │
│ │
└─────────────────────────────────────────────────────────────────┘