https://github.com/mhiro2/code-shape.nvim
Structure-aware navigation for large codebases in Neovim - fast symbol search, call graph exploration, and hotspot-based refactoring prioritization
https://github.com/mhiro2/code-shape.nvim
code-navigation neovim neovim-plugin nvim nvim-pluigin
Last synced: 7 days ago
JSON representation
Structure-aware navigation for large codebases in Neovim - fast symbol search, call graph exploration, and hotspot-based refactoring prioritization
- Host: GitHub
- URL: https://github.com/mhiro2/code-shape.nvim
- Owner: mhiro2
- License: mit
- Created: 2026-03-01T12:55:08.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-03-23T05:46:57.000Z (19 days ago)
- Last Synced: 2026-03-24T02:02:38.439Z (18 days ago)
- Topics: code-navigation, neovim, neovim-plugin, nvim, nvim-pluigin
- Language: Lua
- Homepage:
- Size: 208 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# code-shape.nvim
> **Structure-aware navigation** for large repositories.
>
> - π **Defs**: fast symbol definition search from a local Rust index
> - πΈοΈ **Calls**: follow call/reference edges from the selected symbol
> - π₯ **Hotspots + Debt**: prioritize risky areas via git churn Γ complexity
`code-shape.nvim` keeps a fast incremental index in a small Rust core and gives
you one workflow for exploration and refactoring prioritization.
## π‘ Why code-shape.nvim?
Built for codebases where the key question is **"what should we touch first?"**
**Best fit:**
- Large monorepos where LSP symbol search gets slow or unstable
- Legacy refactoring where hotspot Γ complexity guides starting points
- Environments that need fast startup/search via persisted snapshots
**Not fit** β for small projects (β€10k LOC) or plain text search, `live_grep` / `lsp_workspace_symbols` are sufficient.
## β¨ Features
- β‘ Millisecond-level symbol search from an incremental Rust index
- π§ Unified picker with three modes: **Defs**, **Calls**, **Hotspots**
- πΈοΈ Call graph exploration from the selected symbol (`gc`, `l`, `h`, `r`)
- π₯ Hotspots ranked by git history (with optional churn/time-decay weighting)
- π Code metrics: cyclomatic complexity, LOC, nesting depth (via Tree-sitter)
- π― Tech debt scoring: hotspot Γ complexity for targeted refactoring
- ποΈ Automatic indexing of opened buffers + workspace symbol indexing
- πΎ Snapshot persistence on startup/exit (`snapshot.*`)
- π³ Multi-root support for monorepos and git worktrees (per-root stats/hotspots)
- π§© External picker integrations for Telescope / fzf-lua / snacks.nvim
## π§ Workflow: Defs β Calls β Hotspots
1. **Defs** β find the symbol (`gc` to explore callers/callees)
2. **Calls** β follow the call graph (`l` forward, `h` back)
3. **Hotspots** β prioritize by git churn Γ complexity
Details and example scenario: `:help code-shape-workflow`
## π¦ Requirements
- Neovim `>= 0.10`
- A working LSP setup (for best results)
- Optional: `git` (for Hotspots)
## π Installation
### lazy.nvim
```lua
{
"mhiro2/code-shape.nvim",
config = function()
require("code-shape").setup({})
end,
}
```
### packer.nvim
```lua
use({
"mhiro2/code-shape.nvim",
config = function()
require("code-shape").setup({})
end,
})
```
## Quickstart
1. Open your project in Neovim
2. Run:
```vim
:CodeShapeIndex
```
3. Open the picker:
```vim
:CodeShape
```
Type to search for symbol definitions.
## π» Commands
| Command | Description |
| -------------------- | --------------------------------- |
| `:CodeShape` | Open the picker UI |
| `:CodeShapeIndex` | Build/refresh index (incremental) |
| `:CodeShapeReindex` | Full rebuild |
| `:CodeShapeIndexCancel` | Cancel running workspace index |
| `:CodeShapeStatus` | Show index stats |
| `:CodeShapeClear` | Clear indexed symbols, hotspot scores, and tracked roots |
| `:CodeShapeHotspots` | Open Hotspots view (drill into symbol metrics) |
| `:CodeShapeCallsFromCursor` | Open Calls mode for symbol under cursor |
| `:CodeShapeDiffImpact [--base=] [--head=] [--staged]` | Open AI-era diff impact view (risk order) |
| `:CodeShapeTelescope [mode]` | Open with Telescope (defs/hotspots/impact) |
| `:CodeShapeFzf [mode]` | Open with fzf-lua (defs/hotspots/impact) |
| `:CodeShapeSnacks [mode]` | Open with snacks.nvim (defs/hotspots/impact) |
## β¨οΈ Default Keymaps (in picker)
> [!IMPORTANT]
> `config.keymaps` in this section applies only to the built-in picker UI (`picker = "builtin"` or `nil`).
> If you use an external backend (`telescope` / `fzf_lua` / `snacks`), configure keymaps in that picker plugin or map `:CodeShape*` commands directly.
All bindings below can be customized via `config.keymaps`.
### Normal Mode
| Key | Action |
| -------- | ------------------------- |
| `` | Jump to symbol |
| `` | Split jump |
| `` | Vsplit jump |
| `j` / `k`| Next / Previous item |
| `q` / `` | Close |
| `` | Next mode (DefsβCallsβHotspots) |
| ``| Previous mode |
| `t` | Cycle kind filter (All/Func/Class/Var/Type) |
| `gd` | Go to definition (LSP) |
| `gr` | Show references (LSP) |
| `gc` | Build/follow call graph |
| `l` | Follow selected graph node |
| `h` | Back to previous graph node |
| `r` | Refresh current graph node |
### Insert Mode (in input window)
| Key | Action |
| -------- | ------------------------- |
| `` | Jump to symbol |
| `` / `` | Next / Previous item |
| `` | Next mode |
| ``| Previous mode |
| `` | Cycle kind filter |
| `` | Build/follow call graph |
### Hotspots Drill-down (`:CodeShapeHotspots`)
| Key | Action |
| -------- | ------------------------- |
| `` | Drill into file β show symbol metrics |
| `` | Back to file list |
| `h` | Back to file list |
| `q` | Close |
| `` | Close |
In the symbol metrics view, `` jumps to the symbol location.
### Mode Tabs
The picker supports three modes:
- **Defs**: Search for symbol definitions (functions, classes, methods, etc.)
- **Calls**: Explore call/reference edges in a lightweight graph panel (`gc`/`l` follow, `h` back, `r` refresh)
- **Hotspots**: Browse files by change frequency
Use `` / `` to cycle between modes.
#### Calls Mode Features
**Navigation History**: The breadcrumb shows your path through the call graph:
```
Path (3/5): authenticate > check_token > validate_session
```
The `(3/5)` indicates you're at position 3 of 5 in the history.
**Section Summary**: A quick overview of graph edges:
```
Callers: 5 | Callees: 3 | Refs: 12
```
**Context Preservation**: When switching between modes, your query and selected symbol are preserved:
- Defs β Calls: Selected symbol becomes the call graph center
- Calls β Defs: Query is restored
- Defs β Hotspots: Query and selection position are maintained
**External Picker Users**: Call graph navigation requires interactive `l`/`h`/`r` keys, which are not supported in external pickers. Use `:CodeShapeCallsFromCursor` to open the builtin UI directly in Calls mode for the symbol under cursor.
### Kind Filters
Press `t` (normal mode) or `` (insert mode) to cycle through kind filters:
- **All**: Show all symbols
- **Func**: Methods, Constructors, Functions
- **Class**: Classes, Interfaces, Structs
- **Var**: Properties, Fields, Variables, Constants
- **Type**: Enums, EnumMembers, TypeParameters
## π§Ί Picker Integration (telescope / fzf-lua / snacks.nvim)
Set `picker` in your config to use an external backend:
```lua
require("code-shape").setup({
picker = "telescope", -- "builtin" | "telescope" | "fzf_lua" | "snacks"
})
```
Per-backend setup, keymaps, and Lua API: `:help code-shape-picker`
## βοΈ Configuration
Configure via `require("code-shape").setup({ ... })`.
Default Settings
```lua
{
-- UI preferences
ui = {
width = 0.8, -- width ratio (0-1) or absolute columns (integer >= 1)
height = 0.8, -- height ratio (0-1)
border = "rounded", -- border style: "none", "single", "double", "rounded", "solid", "shadow"
preview = true, -- show preview window
},
-- Search settings
search = {
limit = 50, -- max results
debounce_ms = 100, -- debounce time for input
},
-- Hotspots (git churn)
hotspots = {
enabled = true,
since = "3 months ago", -- time range for git log
max_files = 1000, -- max files to analyze
-- Optional hotspot scoring options
half_life_days = 30, -- days for commit weight to decay to 0.5
use_churn = true, -- use numstat for line change analysis
},
-- Code metrics (Tree-sitter based)
metrics = {
enabled = true, -- compute metrics during indexing
complexity_cap = 50, -- cap for tech debt normalization
},
-- Picker keymaps
keymaps = {
select = "",
open_vsplit = "",
open_split = "",
prev = "k",
prev_alt = "",
next = "j",
next_alt = "",
prev_insert = "",
next_insert = "",
mode_next = "",
mode_prev = "",
cycle_kind_filter = "t",
goto_definition = "gd",
show_references = "gr",
show_calls = "gc",
graph_follow = "l",
graph_back = "h",
graph_refresh = "r",
close = "q",
close_alt = "",
},
-- Snapshot persistence
snapshot = {
enabled = true,
load_on_start = true,
save_on_exit = true,
remote_cache = {
enabled = false, -- optional (for shared/remote filesystem cache)
dir = "/mnt/code-shape-cache", -- base directory for remote cache
load_on_start = true, -- pull remote snapshot before local load
save_on_exit = true, -- push local snapshot after save
},
},
-- Picker backend: "builtin" (default), "telescope", "fzf_lua", "snacks"
-- picker = nil,
-- Debug mode (shows $/progress and $/log notifications)
debug = false,
}
```
## ποΈ How it works
**Lua** handles UI/LSP, **Rust core** serves fast search via stdio JSON-RPC.
Indexes LSP `documentSymbol` / `workspace/symbol` into an incremental n-gram inverted index.
| Repo scale | code-shape p50 (ms) | lsp_workspace_symbols p50 (ms) | live_grep p50 (ms) |
|------------|---------------------|--------------------------------|---------------------|
| 100k LOC | 0.606 | 0.400 | 5 |
| 500k LOC | 0.745 | 2.081 | 5 |
| 1M LOC | 1.371 | 4.223 | 4 |
Benchmark details: [`benchmark/README.md`](./benchmark/README.md) | `:help code-shape-benchmark`
Advanced features (snapshots, multi-root, code metrics, AI-era diffs): `:help code-shape-advanced`
## π License
MIT License. See [LICENSE](./LICENSE).
## π Alternatives
- [stevearc/aerial.nvim](https://github.com/stevearc/aerial.nvim)
- [simrat39/symbols-outline.nvim](https://github.com/simrat39/symbols-outline.nvim)
- [nvimdev/lspsaga.nvim](https://github.com/nvimdev/lspsaga.nvim)
- [folke/trouble.nvim](https://github.com/folke/trouble.nvim)