For old school code navigation :)
Heavily inspired by emacs' xcscope.el.
Adds cscope support for Neovim
cscope_maps.nvim.v2.webm
- Neovim >= 0.10
- cscope
- Tries to mimic vim's builtin cscope functionality.
- Provides user command,
:Cscopewhich acts same as good old:cscope. - Short commands are supported. e.g.
:Cs f g <sym> :Cstag <sym>doestagssearch if no results are found incscope.- Empty
<sym>can be used in:Csand:Cstagto pick<cword>as sym. - Supports
cscopeandgtags-cscope. Usecscope.execoption to specify executable. - Keymaps can be disabled using
disable_mapsoption. :CsPrompt <op>can be used to invoke cscope prompt.- Display results in quickfix list, location list, telescope, fzf-lua, mini.pick or snacks.nvim.
- Has which-key.nvim and mini.clue hints.
- See this section for
vim-gutentags.
- Statically provide table of db paths in config (
db_file) OR add them at runtime using:Cs db add ... :Cs db add <space sepatated files>add db file(s) to cscope search.:Cs db rm <space sepatated files>remove db file(s) from cscope search.:Cs db showshow all db connections.:Cs db build(re)builds db.- If
db_build_cmd.script == "default"then only primary DB will be built using cscope binary.- e.g.
cscope -f ${db_file} ${db_build_cmd.args}ORgtags-cscope ${db_build_cmd.args}
- e.g.
- Custom script can be provided using
db_build_cmd.script. Example script is here- e.g. user script will be called as following -
${db_build_cmd.script} ${db_build_cmd.args} -d <db1>::<pre_path1> -d <db2>::<pre_path2> ...
- If
vim.g.cscope_maps_statusline_indicatorcan be used in statusline to indicate ongoing db build.- DB path grammar
db_file::db_pre_pathdb_pre_path (prefix path) will be appended to cscope results.- e.g.
:Cs db add ~/cscope.out::/home/code/proj2=> results from~/cscope.outwill be prefixed with/home/code/proj2/ @can be used to indicate that parent ofdb_fileisdb_pre_path.- e.g.
:Cs db add ../proj2/cscope.out::@=> results from../proj2/cscope.outwill be prefixed with../proj2/
- Visualize tree of caller functions and called functions.
:CsStackView open down <sym>Opens "downward" stack showing all the functions who call the<sym>.:CsStackView open up <sym>Opens "upward" stack showing all the functions called by the<sym>.- In
CsStackViewwindow, use following keymaps<tab>toggle child under cursor<cr>open location of symbol under cursor<C-v>open location of symbol under cursor in vertical split<C-s>open location of symbol under cursor in horizontal splitqor<esc>close window<C-u>or<C-y>scroll up preview<C-d>or<C-e>scroll down preview
:CsStackView togglereopens lastCsStackViewwindow.- In
CsStackViewwindow, all nodes that are part of current stack are highlighted.
Install the plugin with your preferred package manager. Following example uses lazy.nvim
{
"dhananjaylatkar/cscope_maps.nvim",
dependencies = {
"nvim-telescope/telescope.nvim", -- optional [for picker="telescope"]
"ibhagwan/fzf-lua", -- optional [for picker="fzf-lua"]
"echasnovski/mini.pick", -- optional [for picker="mini-pick"]
"folke/snacks.nvim", -- optional [for picker="snacks"]
},
opts = {
-- USE EMPTY FOR DEFAULT OPTIONS
-- DEFAULTS ARE LISTED BELOW
},
}You must run require("cscope_maps").setup() to initialize the plugin even when using default options.
NOTE: In vimrc use lua require("cscope_maps").setup()
cscope_maps comes with following defaults:
{
-- maps related defaults
disable_maps = false, -- "true" disables default keymaps
skip_input_prompt = false, -- "true" doesn't ask for input
prefix = "<leader>c", -- prefix to trigger maps
-- cscope related defaults
cscope = {
-- location of cscope db file
db_file = "./cscope.out", -- DB or table of DBs
-- NOTE:
-- when table of DBs is provided -
-- first DB is "primary" and others are "secondary"
-- primary DB is used for build and project_rooter
-- cscope executable
exec = "cscope", -- "cscope" or "gtags-cscope"
-- choose your fav picker
picker = "quickfix", -- "quickfix", "location", "telescope", "fzf-lua", "mini-pick" or "snacks"
-- qf_window_size = 5, -- deprecated, replaced by picket_opts below, but still supported for backward compatibility
-- qf_window_pos = "bottom", -- deprecated, replaced by picket_opts below, but still supported for backward compatibility
picker_opts = {
window_size = 5, -- any positive integer
window_pos = "bottom", -- "bottom", "right", "left" or "top"
-- options for Snacks picker (---@class snacks.picker.Config)
-- pass-through options for Snacks picker
snacks = {}, -- snacks config
},
-- "true" does not open picker for single result, just JUMP
skip_picker_for_single_result = false, -- "false" or "true"
-- custom script can be used for db build
db_build_cmd = { script = "default", args = { "-bqkv" } },
-- statusline indicator, default is cscope executable
statusline_indicator = nil,
-- try to locate db_file in parent dir(s)
project_rooter = {
enable = false, -- "true" or "false"
-- change cwd to where db_file is located
change_cwd = false, -- "true" or "false"
},
-- cstag related defaults
tag = {
-- bind ":Cstag" to "<C-]>"
keymap = true, -- "true" or "false"
-- order of operation to run for ":Cstag"
order = { "cs", "tag_picker", "tag" }, -- any combination of these 3 (ops can be excluded)
-- cmd to use for "tag" op in above table
tag_cmd = "tjump",
},
},
-- stack view defaults
stack_view = {
tree_hl = true, -- toggle tree highlighting
}
}{
"ludovicchabant/vim-gutentags",
init = function()
vim.g.gutentags_modules = {"cscope_maps"} -- This is required. Other config is optional
vim.g.gutentags_cscope_build_inverted_index_maps = 1
vim.g.gutentags_cache_dir = vim.fn.expand("~/code/.gutentags")
-- ⚠️ WARNING: This limits tags to .c and .h files only, add more extensions (e.g., `-e py`) or remove to avoid skipping other files.
vim.g.gutentags_file_list_command = "fd -e c -e h"
-- vim.g.gutentags_trace = 1
end,
}Alternative to gutentags is to rebuild DB using :Cscope db build or <prefix>b.
You can create autocmd for running :Cscope db build after saving .c and .h files.
e.g
local group = vim.api.nvim_create_augroup("CscopeBuild", { clear = true })
vim.api.nvim_create_autocmd("BufWritePost", {
pattern = { "*.c", "*.h" },
callback = function ()
vim.cmd("Cscope db build")
end,
group = group,
})<prefix> can be configured using prefix option. Default value for prefix
is <leader>c.
(Try setting it to C-c 😉)
| Keymaps | Description |
|---|---|
<prefix>s |
find all references to the token under cursor |
<prefix>g |
find global definition(s) of the token under cursor |
<prefix>c |
find all calls to the function name under cursor |
<prefix>t |
find all instances of the text under cursor |
<prefix>e |
egrep search for the word under cursor |
<prefix>f |
open the filename under cursor |
<prefix>i |
find files that include the filename under cursor |
<prefix>d |
find functions that function under cursor calls |
<prefix>a |
find places where this symbol is assigned a value |
<prefix>b |
build cscope database |
| Ctrl-] | do :Cstag <cword> |
Disable default keymaps by setting disable_maps = true.
There are 2 ways to add keymaps for Cscope.
:CsPrompt <op> is user command to invoke prompt.
This command provides prompt which asks for input
before running :Cscope command.
e.g. Following snippet maps C-c C-g to find global def of symbol under cursor
vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>CsPrompt g<cr>")e.g. Following snippet maps C-c C-g to find global def of symbol under cursor
vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>Cs f g<cr>")Following snippet shows how to use snacks.nvim picker with custom layout.
cscope.picker_opts.snacks are passed as is to Snacks.picker().
More details about Snacks.picker() can be found here
The layout used in this example is as follows -
- Horizontal layout (input and list on the left, preview on the right)
- Total width is 90% of screen width
- Total height is 85% of screen height
- The left side (input and list) takes up 60% of the width
- The right side (preview) takes up 40% of the width
- Rounded borders for input and preview windows
- Preview window has word wrap enabled
-- only showing relevant part of the config
Opts = {
cscope = {
picker = 'snacks', -- snacks.picker (alternative: telescope)
picker_opts = {
---@class snacks.picker.Config
snacks = {
-- layout = 'vertical', -- Use "vertical" or "horizontal" if you want to use presets
---@class snacks.picker.layout.Config
layout = {
layout = {
height = 0.85, -- Take up 85% of the total height
width = 0.9, -- Take up 90% of the total width (adjust as needed)
box = 'horizontal', -- Horizontal layout (input and list on the left, preview on the right)
{ -- Left side (input and list)
box = 'vertical',
width = 0.6, -- List and input take up 60% of the width
border = 'rounded',
{ win = 'input', height = 1, border = 'bottom' },
{ win = 'list', border = 'none' },
},
{ win = 'preview', border = 'rounded', width = 0.4 }, -- Preview window takes up 40% of the width
},
},
---@class snacks.picker.win.Config
win = {
preview = {
wo = { wrap = true },
},
},
}, -- snacks
}, -- picker_opts
}, -- cscope
}, -- Opts