dblume commited on 2025-04-03 22:55:25
Showing 18 changed files, with 2333 additions and 0 deletions.
Previously, I installed it manually https://wiki.dlma.com/neovim#cscope It worked in ~/.config/nvim/pack/cscope_maps/start/cscope_maps.nvim/... But with a new Ubuntu 24.04 install and nvim v0.11.0, this is the path (in ~/.local/...) that worked.
| ... | ... |
@@ -0,0 +1,21 @@ |
| 1 |
+MIT License |
|
| 2 |
+ |
|
| 3 |
+Copyright (c) 2021 Dhananjay |
|
| 4 |
+ |
|
| 5 |
+Permission is hereby granted, free of charge, to any person obtaining a copy |
|
| 6 |
+of this software and associated documentation files (the "Software"), to deal |
|
| 7 |
+in the Software without restriction, including without limitation the rights |
|
| 8 |
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
| 9 |
+copies of the Software, and to permit persons to whom the Software is |
|
| 10 |
+furnished to do so, subject to the following conditions: |
|
| 11 |
+ |
|
| 12 |
+The above copyright notice and this permission notice shall be included in all |
|
| 13 |
+copies or substantial portions of the Software. |
|
| 14 |
+ |
|
| 15 |
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
| 16 |
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
| 17 |
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
| 18 |
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
| 19 |
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
| 20 |
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
| 21 |
+SOFTWARE. |
| ... | ... |
@@ -0,0 +1,222 @@ |
| 1 |
+# cscope_maps.nvim |
|
| 2 |
+ |
|
| 3 |
+For old school code navigation :) |
|
| 4 |
+ |
|
| 5 |
+Heavily inspired by emacs' [xcscope.el](https://github.com/dkogan/xcscope.el). |
|
| 6 |
+ |
|
| 7 |
+**Adds `cscope` support for [Neovim](https://neovim.io/)** |
|
| 8 |
+ |
|
| 9 |
+[cscope_maps.nvim.v2.webm](https://github.com/dhananjaylatkar/cscope_maps.nvim/assets/27724944/7ab4d902-fe6d-4914-bff6-353136c72803) |
|
| 10 |
+ |
|
| 11 |
+## Requirements |
|
| 12 |
+ |
|
| 13 |
+- Neovim >= 0.10 |
|
| 14 |
+- [cscope](https://sourceforge.net/projects/cscope/files/) |
|
| 15 |
+ |
|
| 16 |
+## Features |
|
| 17 |
+ |
|
| 18 |
+### Cscope |
|
| 19 |
+ |
|
| 20 |
+- Tries to mimic vim's builtin cscope functionality. |
|
| 21 |
+- Provides user command, `:Cscope` which acts same as good old `:cscope`. |
|
| 22 |
+- Short commands are supported. e.g. `:Cs f g <sym>` |
|
| 23 |
+- `:Cstag <sym>` does `tags` search if no results are found in `cscope`. |
|
| 24 |
+- Empty `<sym>` can be used in `:Cs` and `:Cstag` to pick `<cword>` as sym. |
|
| 25 |
+- Supports `cscope` and `gtags-cscope`. Use `cscope.exec` option to specify executable. |
|
| 26 |
+- Keymaps can be disabled using `disable_maps` option. |
|
| 27 |
+- `:CsPrompt <op>` can be used to invoke cscope prompt. |
|
| 28 |
+- Display results in quickfix, **telescope**, **fzf-lua**, **mini.pick** or **snacks.nvim**. |
|
| 29 |
+- Has [which-key.nvim](https://github.com/folke/which-key.nvim) and [mini.clue](https://github.com/echasnovski/mini.clue) hints. |
|
| 30 |
+- See [this section](#vim-gutentags) for `vim-gutentags`. |
|
| 31 |
+ |
|
| 32 |
+### Cscope DB |
|
| 33 |
+ |
|
| 34 |
+- Statically provide table of db paths in config (`db_file`) OR add them at runtime using `:Cs db add ...` |
|
| 35 |
+- `:Cs db add <space sepatated files>` add db file(s) to cscope search. |
|
| 36 |
+- `:Cs db rm <space sepatated files>` remove db file(s) from cscope search. |
|
| 37 |
+- `:Cs db show` show all db connections. |
|
| 38 |
+- `:Cs db build` (re)builds db. |
|
| 39 |
+ - If `db_build_cmd.script == "default"` then only primary DB will be built using cscope binary. |
|
| 40 |
+ - e.g. `cscope -f ${db_file} ${db_build_cmd.args}` OR `gtags-cscope ${db_build_cmd.args}`
|
|
| 41 |
+ - Custom script can be provided using `db_build_cmd.script`. Example script is [here](https://github.com/dhananjaylatkar/cscope_maps.nvim/pull/67) |
|
| 42 |
+ - e.g. user script will be called as following - |
|
| 43 |
+ - `${db_build_cmd.script} ${db_build_cmd.args} -d <db1>::<pre_path1> -d <db2>::<pre_path2> ...`
|
|
| 44 |
+- `vim.g.cscope_maps_statusline_indicator` can be used in statusline to indicate ongoing db build. |
|
| 45 |
+- DB path grammar |
|
| 46 |
+ - `db_file::db_pre_path` db_pre_path (prefix path) will be appended to cscope results. |
|
| 47 |
+ - e.g. `:Cs db add ~/cscope.out::/home/code/proj2` => results from `~/cscope.out` will be prefixed with `/home/code/proj2/` |
|
| 48 |
+ - `@` can be used to indicate that parent of `db_file` is `db_pre_path`. |
|
| 49 |
+ - e.g. `:Cs db add ../proj2/cscope.out::@` => results from `../proj2/cscope.out` will be prefixed with `../proj2/` |
|
| 50 |
+ |
|
| 51 |
+### Stack View |
|
| 52 |
+ |
|
| 53 |
+- Visualize tree of caller functions and called functions. |
|
| 54 |
+- `:CsStackView open down <sym>` Opens "downward" stack showing all the functions who call the `<sym>`. |
|
| 55 |
+- `:CsStackView open up <sym>` Opens "upward" stack showing all the functions called by the `<sym>`. |
|
| 56 |
+- In `CsStackView` window, use following keymaps |
|
| 57 |
+ - `<tab>` toggle child under cursor |
|
| 58 |
+ - `<cr>` open location of symbol under cursor |
|
| 59 |
+ - `q` or `<esc>` close window |
|
| 60 |
+ - `<C-u>` or `<C-y>` scroll up preview |
|
| 61 |
+ - `<C-d>` or `<C-e>` scroll down preview |
|
| 62 |
+- `:CsStackView toggle` reopens last `CsStackView` window. |
|
| 63 |
+- In `CsStackView` window, all nodes that are part of current stack are highlighted. |
|
| 64 |
+ |
|
| 65 |
+## Installation |
|
| 66 |
+ |
|
| 67 |
+Install the plugin with your preferred package manager. |
|
| 68 |
+Following example uses [lazy.nvim](https://github.com/folke/lazy.nvim) |
|
| 69 |
+ |
|
| 70 |
+```lua |
|
| 71 |
+{
|
|
| 72 |
+ "dhananjaylatkar/cscope_maps.nvim", |
|
| 73 |
+ dependencies = {
|
|
| 74 |
+ "nvim-telescope/telescope.nvim", -- optional [for picker="telescope"] |
|
| 75 |
+ "ibhagwan/fzf-lua", -- optional [for picker="fzf-lua"] |
|
| 76 |
+ "echasnovski/mini.pick", -- optional [for picker="mini-pick"] |
|
| 77 |
+ "folke/snacks.nvim", -- optional [for picker="snacks"] |
|
| 78 |
+ }, |
|
| 79 |
+ opts = {
|
|
| 80 |
+ -- USE EMPTY FOR DEFAULT OPTIONS |
|
| 81 |
+ -- DEFAULTS ARE LISTED BELOW |
|
| 82 |
+ }, |
|
| 83 |
+} |
|
| 84 |
+``` |
|
| 85 |
+ |
|
| 86 |
+## Configuration |
|
| 87 |
+ |
|
| 88 |
+You must run `require("cscope_maps").setup()` to initialize the plugin even when using default options.
|
|
| 89 |
+ |
|
| 90 |
+NOTE: In `vimrc` use `lua require("cscope_maps").setup()`
|
|
| 91 |
+ |
|
| 92 |
+_cscope_maps_ comes with following defaults: |
|
| 93 |
+ |
|
| 94 |
+```lua |
|
| 95 |
+{
|
|
| 96 |
+ -- maps related defaults |
|
| 97 |
+ disable_maps = false, -- "true" disables default keymaps |
|
| 98 |
+ skip_input_prompt = false, -- "true" doesn't ask for input |
|
| 99 |
+ prefix = "<leader>c", -- prefix to trigger maps |
|
| 100 |
+ |
|
| 101 |
+ -- cscope related defaults |
|
| 102 |
+ cscope = {
|
|
| 103 |
+ -- location of cscope db file |
|
| 104 |
+ db_file = "./cscope.out", -- DB or table of DBs |
|
| 105 |
+ -- NOTE: |
|
| 106 |
+ -- when table of DBs is provided - |
|
| 107 |
+ -- first DB is "primary" and others are "secondary" |
|
| 108 |
+ -- primary DB is used for build and project_rooter |
|
| 109 |
+ -- cscope executable |
|
| 110 |
+ exec = "cscope", -- "cscope" or "gtags-cscope" |
|
| 111 |
+ -- choose your fav picker |
|
| 112 |
+ picker = "quickfix", -- "quickfix", "telescope", "fzf-lua", "mini-pick" or "snacks" |
|
| 113 |
+ -- size of quickfix window |
|
| 114 |
+ qf_window_size = 5, -- any positive integer |
|
| 115 |
+ -- position of quickfix window |
|
| 116 |
+ qf_window_pos = "bottom", -- "bottom", "right", "left" or "top" |
|
| 117 |
+ -- "true" does not open picker for single result, just JUMP |
|
| 118 |
+ skip_picker_for_single_result = false, -- "false" or "true" |
|
| 119 |
+ -- custom script can be used for db build |
|
| 120 |
+ db_build_cmd = { script = "default", args = { "-bqkv" } },
|
|
| 121 |
+ -- statusline indicator, default is cscope executable |
|
| 122 |
+ statusline_indicator = nil, |
|
| 123 |
+ -- try to locate db_file in parent dir(s) |
|
| 124 |
+ project_rooter = {
|
|
| 125 |
+ enable = false, -- "true" or "false" |
|
| 126 |
+ -- change cwd to where db_file is located |
|
| 127 |
+ change_cwd = false, -- "true" or "false" |
|
| 128 |
+ }, |
|
| 129 |
+ }, |
|
| 130 |
+ |
|
| 131 |
+ -- stack view defaults |
|
| 132 |
+ stack_view = {
|
|
| 133 |
+ tree_hl = true, -- toggle tree highlighting |
|
| 134 |
+ } |
|
| 135 |
+} |
|
| 136 |
+``` |
|
| 137 |
+ |
|
| 138 |
+## vim-gutentags |
|
| 139 |
+ |
|
| 140 |
+### Config for vim-gutentags |
|
| 141 |
+ |
|
| 142 |
+```lua |
|
| 143 |
+{
|
|
| 144 |
+ "ludovicchabant/vim-gutentags", |
|
| 145 |
+ init = function() |
|
| 146 |
+ vim.g.gutentags_modules = {"cscope_maps"} -- This is required. Other config is optional
|
|
| 147 |
+ vim.g.gutentags_cscope_build_inverted_index_maps = 1 |
|
| 148 |
+ vim.g.gutentags_cache_dir = vim.fn.expand("~/code/.gutentags")
|
|
| 149 |
+ vim.g.gutentags_file_list_command = "fd -e c -e h" |
|
| 150 |
+ -- vim.g.gutentags_trace = 1 |
|
| 151 |
+ end, |
|
| 152 |
+} |
|
| 153 |
+``` |
|
| 154 |
+ |
|
| 155 |
+### Alternative to vim-gutentags |
|
| 156 |
+ |
|
| 157 |
+Alternative to gutentags is to rebuild DB using `:Cscope db build` or `<prefix>b`. |
|
| 158 |
+ |
|
| 159 |
+You can create autocmd for running `:Cscope db build` after saving .c and .h files. |
|
| 160 |
+e.g |
|
| 161 |
+ |
|
| 162 |
+```lua |
|
| 163 |
+local group = vim.api.nvim_create_augroup("CscopeBuild", { clear = true })
|
|
| 164 |
+vim.api.nvim_create_autocmd("BufWritePost", {
|
|
| 165 |
+ pattern = { "*.c", "*.h" },
|
|
| 166 |
+ callback = function () |
|
| 167 |
+ vim.cmd("Cscope db build")
|
|
| 168 |
+ end, |
|
| 169 |
+ group = group, |
|
| 170 |
+}) |
|
| 171 |
+``` |
|
| 172 |
+ |
|
| 173 |
+## Keymaps |
|
| 174 |
+ |
|
| 175 |
+### Default Keymaps |
|
| 176 |
+ |
|
| 177 |
+`<prefix>` can be configured using `prefix` option. Default value for prefix |
|
| 178 |
+is `<leader>c`. |
|
| 179 |
+ |
|
| 180 |
+(Try setting it to `C-c` 😉) |
|
| 181 |
+ |
|
| 182 |
+| Keymaps | Description | |
|
| 183 |
+| ----------------- | --------------------------------------------------- | |
|
| 184 |
+| `<prefix>s` | find all references to the token under cursor | |
|
| 185 |
+| `<prefix>g` | find global definition(s) of the token under cursor | |
|
| 186 |
+| `<prefix>c` | find all calls to the function name under cursor | |
|
| 187 |
+| `<prefix>t` | find all instances of the text under cursor | |
|
| 188 |
+| `<prefix>e` | egrep search for the word under cursor | |
|
| 189 |
+| `<prefix>f` | open the filename under cursor | |
|
| 190 |
+| `<prefix>i` | find files that include the filename under cursor | |
|
| 191 |
+| `<prefix>d` | find functions that function under cursor calls | |
|
| 192 |
+| `<prefix>a` | find places where this symbol is assigned a value | |
|
| 193 |
+| `<prefix>b` | build cscope database | |
|
| 194 |
+| <kbd>Ctrl-]</kbd> | do `:Cstag <cword>` | |
|
| 195 |
+ |
|
| 196 |
+### Custom Keymaps |
|
| 197 |
+ |
|
| 198 |
+Disable default keymaps by setting `disable_maps = true`. |
|
| 199 |
+ |
|
| 200 |
+There are 2 ways to add keymaps for `Cscope`. |
|
| 201 |
+ |
|
| 202 |
+#### Using `:CsPrompt` command |
|
| 203 |
+ |
|
| 204 |
+`:CsPrompt <op>` is user command to invoke prompt. |
|
| 205 |
+This command provides prompt which asks for input |
|
| 206 |
+before running `:Cscope` command. |
|
| 207 |
+ |
|
| 208 |
+e.g. Following snippet maps <kbd>C-c C-g</kbd> to find global def of symbol |
|
| 209 |
+under cursor |
|
| 210 |
+ |
|
| 211 |
+```lua |
|
| 212 |
+vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>CsPrompt g<cr>")
|
|
| 213 |
+``` |
|
| 214 |
+ |
|
| 215 |
+#### Using `:Cscope` command |
|
| 216 |
+ |
|
| 217 |
+e.g. Following snippet maps <kbd>C-c C-g</kbd> to find global def of symbol |
|
| 218 |
+under cursor |
|
| 219 |
+ |
|
| 220 |
+```lua |
|
| 221 |
+vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>Cs f g<cr>")
|
|
| 222 |
+``` |
| ... | ... |
@@ -0,0 +1,266 @@ |
| 1 |
+*cscope_maps.txt* For Neovim >= v0.10.0 Last change: 2025 February 26 |
|
| 2 |
+ |
|
| 3 |
+============================================================================== |
|
| 4 |
+Table of Contents *cscope_maps-table-of-contents* |
|
| 5 |
+ |
|
| 6 |
+1. cscope_maps.nvim |cscope_maps-cscope_maps.nvim| |
|
| 7 |
+ - Requirements |cscope_maps-cscope_maps.nvim-requirements| |
|
| 8 |
+ - Features |cscope_maps-cscope_maps.nvim-features| |
|
| 9 |
+ - Installation |cscope_maps-cscope_maps.nvim-installation| |
|
| 10 |
+ - Configuration |cscope_maps-cscope_maps.nvim-configuration| |
|
| 11 |
+ - vim-gutentags |cscope_maps-cscope_maps.nvim-vim-gutentags| |
|
| 12 |
+ - Keymaps |cscope_maps-cscope_maps.nvim-keymaps| |
|
| 13 |
+ |
|
| 14 |
+============================================================================== |
|
| 15 |
+1. cscope_maps.nvim *cscope_maps-cscope_maps.nvim* |
|
| 16 |
+ |
|
| 17 |
+For old school code navigation :) |
|
| 18 |
+ |
|
| 19 |
+Heavily inspired by emacs’ xcscope.el <https://github.com/dkogan/xcscope.el>. |
|
| 20 |
+ |
|
| 21 |
+**Adds cscope support for Neovim** |
|
| 22 |
+ |
|
| 23 |
+cscope_maps.nvim.v2.webm |
|
| 24 |
+<https://github.com/dhananjaylatkar/cscope_maps.nvim/assets/27724944/7ab4d902-fe6d-4914-bff6-353136c72803> |
|
| 25 |
+ |
|
| 26 |
+ |
|
| 27 |
+REQUIREMENTS *cscope_maps-cscope_maps.nvim-requirements* |
|
| 28 |
+ |
|
| 29 |
+- Neovim >= 0.10 |
|
| 30 |
+- cscope <https://sourceforge.net/projects/cscope/files/> |
|
| 31 |
+ |
|
| 32 |
+ |
|
| 33 |
+FEATURES *cscope_maps-cscope_maps.nvim-features* |
|
| 34 |
+ |
|
| 35 |
+ |
|
| 36 |
+CSCOPE ~ |
|
| 37 |
+ |
|
| 38 |
+- Tries to mimic vim’s builtin cscope functionality. |
|
| 39 |
+- Provides user command, `:Cscope` which acts same as good old `:cscope`. |
|
| 40 |
+- Short commands are supported. e.g. `:Cs f g <sym>` |
|
| 41 |
+- `:Cstag <sym>` does `tags` search if no results are found in `cscope`. |
|
| 42 |
+- Empty `<sym>` can be used in `:Cs` and `:Cstag` to pick `<cword>` as sym. |
|
| 43 |
+- Supports `cscope` and `gtags-cscope`. Use `cscope.exec` option to specify executable. |
|
| 44 |
+- Keymaps can be disabled using `disable_maps` option. |
|
| 45 |
+- `:CsPrompt <op>` can be used to invoke cscope prompt. |
|
| 46 |
+- Display results in quickfix, **telescope**, **fzf-lua**, **mini.pick** or **snacks.nvim**. |
|
| 47 |
+- Has which-key.nvim <https://github.com/folke/which-key.nvim> and mini.clue <https://github.com/echasnovski/mini.clue> hints. |
|
| 48 |
+- See |cscope_maps-this-section| for `vim-gutentags`. |
|
| 49 |
+ |
|
| 50 |
+ |
|
| 51 |
+CSCOPE DB ~ |
|
| 52 |
+ |
|
| 53 |
+- Statically provide table of db paths in config (`db_file`) OR add them at runtime using `:Cs db add ...` |
|
| 54 |
+- `:Cs db add <space sepatated files>` add db file(s) to cscope search. |
|
| 55 |
+- `:Cs db rm <space sepatated files>` remove db file(s) from cscope search. |
|
| 56 |
+- `:Cs db show` show all db connections. |
|
| 57 |
+- `:Cs db build` (re)builds db. |
|
| 58 |
+ - If `db_build_cmd.script == "default"` then only primary DB will be built using cscope binary. |
|
| 59 |
+ - e.g. `cscope -f ${db_file} ${db_build_cmd.args}` OR `gtags-cscope ${db_build_cmd.args}`
|
|
| 60 |
+ - Custom script can be provided using `db_build_cmd.script`. Example script is here <https://github.com/dhananjaylatkar/cscope_maps.nvim/pull/67> |
|
| 61 |
+ - e.g. user script will be called as following - |
|
| 62 |
+ - `${db_build_cmd.script} ${db_build_cmd.args} -d <db1>::<pre_path1> -d <db2>::<pre_path2> ...`
|
|
| 63 |
+- `vim.g.cscope_maps_statusline_indicator` can be used in statusline to indicate ongoing db build. |
|
| 64 |
+- DB path grammar |
|
| 65 |
+ - `db_file::db_pre_path` db_pre_path (prefix path) will be appended to cscope results. |
|
| 66 |
+ - e.g. `:Cs db add ~/cscope.out::/home/code/proj2` => results from `~/cscope.out` will be prefixed with `/home/code/proj2/` |
|
| 67 |
+ - `@` can be used to indicate that parent of `db_file` is `db_pre_path`. |
|
| 68 |
+ - e.g. `:Cs db add ../proj2/cscope.out::@` => results from `../proj2/cscope.out` will be prefixed with `../proj2/` |
|
| 69 |
+ |
|
| 70 |
+ |
|
| 71 |
+STACK VIEW ~ |
|
| 72 |
+ |
|
| 73 |
+- Visualize tree of caller functions and called functions. |
|
| 74 |
+- `:CsStackView open down <sym>` Opens "downward" stack showing all the functions who call the `<sym>`. |
|
| 75 |
+- `:CsStackView open up <sym>` Opens "upward" stack showing all the functions called by the `<sym>`. |
|
| 76 |
+- In `CsStackView` window, use following keymaps |
|
| 77 |
+ - `<tab>` toggle child under cursor |
|
| 78 |
+ - `<cr>` open location of symbol under cursor |
|
| 79 |
+ - `q` or `<esc>` close window |
|
| 80 |
+ - `<C-u>` or `<C-y>` scroll up preview |
|
| 81 |
+ - `<C-d>` or `<C-e>` scroll down preview |
|
| 82 |
+- `:CsStackView toggle` reopens last `CsStackView` window. |
|
| 83 |
+- In `CsStackView` window, all nodes that are part of current stack are highlighted. |
|
| 84 |
+ |
|
| 85 |
+ |
|
| 86 |
+INSTALLATION *cscope_maps-cscope_maps.nvim-installation* |
|
| 87 |
+ |
|
| 88 |
+Install the plugin with your preferred package manager. Following example uses |
|
| 89 |
+lazy.nvim <https://github.com/folke/lazy.nvim> |
|
| 90 |
+ |
|
| 91 |
+>lua |
|
| 92 |
+ {
|
|
| 93 |
+ "dhananjaylatkar/cscope_maps.nvim", |
|
| 94 |
+ dependencies = {
|
|
| 95 |
+ "nvim-telescope/telescope.nvim", -- optional [for picker="telescope"] |
|
| 96 |
+ "ibhagwan/fzf-lua", -- optional [for picker="fzf-lua"] |
|
| 97 |
+ "echasnovski/mini.pick", -- optional [for picker="mini-pick"] |
|
| 98 |
+ "folke/snacks.nvim", -- optional [for picker="snacks"] |
|
| 99 |
+ }, |
|
| 100 |
+ opts = {
|
|
| 101 |
+ -- USE EMPTY FOR DEFAULT OPTIONS |
|
| 102 |
+ -- DEFAULTS ARE LISTED BELOW |
|
| 103 |
+ }, |
|
| 104 |
+ } |
|
| 105 |
+< |
|
| 106 |
+ |
|
| 107 |
+ |
|
| 108 |
+CONFIGURATION *cscope_maps-cscope_maps.nvim-configuration* |
|
| 109 |
+ |
|
| 110 |
+You must run `require("cscope_maps").setup()` to initialize the plugin even
|
|
| 111 |
+when using default options. |
|
| 112 |
+ |
|
| 113 |
+NOTE: In `vimrc` use `lua require("cscope_maps").setup()`
|
|
| 114 |
+ |
|
| 115 |
+_cscope_maps_ comes with following defaults: |
|
| 116 |
+ |
|
| 117 |
+>lua |
|
| 118 |
+ {
|
|
| 119 |
+ -- maps related defaults |
|
| 120 |
+ disable_maps = false, -- "true" disables default keymaps |
|
| 121 |
+ skip_input_prompt = false, -- "true" doesn't ask for input |
|
| 122 |
+ prefix = "<leader>c", -- prefix to trigger maps |
|
| 123 |
+ |
|
| 124 |
+ -- cscope related defaults |
|
| 125 |
+ cscope = {
|
|
| 126 |
+ -- location of cscope db file |
|
| 127 |
+ db_file = "./cscope.out", -- DB or table of DBs |
|
| 128 |
+ -- NOTE: |
|
| 129 |
+ -- when table of DBs is provided - |
|
| 130 |
+ -- first DB is "primary" and others are "secondary" |
|
| 131 |
+ -- primary DB is used for build and project_rooter |
|
| 132 |
+ -- cscope executable |
|
| 133 |
+ exec = "cscope", -- "cscope" or "gtags-cscope" |
|
| 134 |
+ -- choose your fav picker |
|
| 135 |
+ picker = "quickfix", -- "quickfix", "telescope", "fzf-lua", "mini-pick" or "snacks" |
|
| 136 |
+ -- size of quickfix window |
|
| 137 |
+ qf_window_size = 5, -- any positive integer |
|
| 138 |
+ -- position of quickfix window |
|
| 139 |
+ qf_window_pos = "bottom", -- "bottom", "right", "left" or "top" |
|
| 140 |
+ -- "true" does not open picker for single result, just JUMP |
|
| 141 |
+ skip_picker_for_single_result = false, -- "false" or "true" |
|
| 142 |
+ -- custom script can be used for db build |
|
| 143 |
+ db_build_cmd = { script = "default", args = { "-bqkv" } },
|
|
| 144 |
+ -- statusline indicator, default is cscope executable |
|
| 145 |
+ statusline_indicator = nil, |
|
| 146 |
+ -- try to locate db_file in parent dir(s) |
|
| 147 |
+ project_rooter = {
|
|
| 148 |
+ enable = false, -- "true" or "false" |
|
| 149 |
+ -- change cwd to where db_file is located |
|
| 150 |
+ change_cwd = false, -- "true" or "false" |
|
| 151 |
+ }, |
|
| 152 |
+ }, |
|
| 153 |
+ |
|
| 154 |
+ -- stack view defaults |
|
| 155 |
+ stack_view = {
|
|
| 156 |
+ tree_hl = true, -- toggle tree highlighting |
|
| 157 |
+ } |
|
| 158 |
+ } |
|
| 159 |
+< |
|
| 160 |
+ |
|
| 161 |
+ |
|
| 162 |
+VIM-GUTENTAGS *cscope_maps-cscope_maps.nvim-vim-gutentags* |
|
| 163 |
+ |
|
| 164 |
+ |
|
| 165 |
+CONFIG FOR VIM-GUTENTAGS ~ |
|
| 166 |
+ |
|
| 167 |
+>lua |
|
| 168 |
+ {
|
|
| 169 |
+ "ludovicchabant/vim-gutentags", |
|
| 170 |
+ init = function() |
|
| 171 |
+ vim.g.gutentags_modules = {"cscope_maps"} -- This is required. Other config is optional
|
|
| 172 |
+ vim.g.gutentags_cscope_build_inverted_index_maps = 1 |
|
| 173 |
+ vim.g.gutentags_cache_dir = vim.fn.expand("~/code/.gutentags")
|
|
| 174 |
+ vim.g.gutentags_file_list_command = "fd -e c -e h" |
|
| 175 |
+ -- vim.g.gutentags_trace = 1 |
|
| 176 |
+ end, |
|
| 177 |
+ } |
|
| 178 |
+< |
|
| 179 |
+ |
|
| 180 |
+ |
|
| 181 |
+ALTERNATIVE TO VIM-GUTENTAGS ~ |
|
| 182 |
+ |
|
| 183 |
+Alternative to gutentags is to rebuild DB using `:Cscope db build` or |
|
| 184 |
+`<prefix>b`. |
|
| 185 |
+ |
|
| 186 |
+You can create autocmd for running `:Cscope db build` after saving .c and .h |
|
| 187 |
+files. e.g |
|
| 188 |
+ |
|
| 189 |
+>lua |
|
| 190 |
+ local group = vim.api.nvim_create_augroup("CscopeBuild", { clear = true })
|
|
| 191 |
+ vim.api.nvim_create_autocmd("BufWritePost", {
|
|
| 192 |
+ pattern = { "*.c", "*.h" },
|
|
| 193 |
+ callback = function () |
|
| 194 |
+ vim.cmd("Cscope db build")
|
|
| 195 |
+ end, |
|
| 196 |
+ group = group, |
|
| 197 |
+ }) |
|
| 198 |
+< |
|
| 199 |
+ |
|
| 200 |
+ |
|
| 201 |
+KEYMAPS *cscope_maps-cscope_maps.nvim-keymaps* |
|
| 202 |
+ |
|
| 203 |
+ |
|
| 204 |
+DEFAULT KEYMAPS ~ |
|
| 205 |
+ |
|
| 206 |
+`<prefix>` can be configured using `prefix` option. Default value for prefix is |
|
| 207 |
+`<leader>c`. |
|
| 208 |
+ |
|
| 209 |
+(Try setting it to `C-c` ) |
|
| 210 |
+ |
|
| 211 |
+ ----------------------------------------------------------------------- |
|
| 212 |
+ Keymaps Description |
|
| 213 |
+ ----------------- ----------------------------------------------------- |
|
| 214 |
+ <prefix>s find all references to the token under cursor |
|
| 215 |
+ |
|
| 216 |
+ <prefix>g find global definition(s) of the token under cursor |
|
| 217 |
+ |
|
| 218 |
+ <prefix>c find all calls to the function name under cursor |
|
| 219 |
+ |
|
| 220 |
+ <prefix>t find all instances of the text under cursor |
|
| 221 |
+ |
|
| 222 |
+ <prefix>e egrep search for the word under cursor |
|
| 223 |
+ |
|
| 224 |
+ <prefix>f open the filename under cursor |
|
| 225 |
+ |
|
| 226 |
+ <prefix>i find files that include the filename under cursor |
|
| 227 |
+ |
|
| 228 |
+ <prefix>d find functions that function under cursor calls |
|
| 229 |
+ |
|
| 230 |
+ <prefix>a find places where this symbol is assigned a value |
|
| 231 |
+ |
|
| 232 |
+ <prefix>b build cscope database |
|
| 233 |
+ |
|
| 234 |
+ Ctrl-] do :Cstag <cword> |
|
| 235 |
+ ----------------------------------------------------------------------- |
|
| 236 |
+ |
|
| 237 |
+CUSTOM KEYMAPS ~ |
|
| 238 |
+ |
|
| 239 |
+Disable default keymaps by setting `disable_maps = true`. |
|
| 240 |
+ |
|
| 241 |
+There are 2 ways to add keymaps for `Cscope`. |
|
| 242 |
+ |
|
| 243 |
+ |
|
| 244 |
+USING :CSPROMPT COMMAND |
|
| 245 |
+ |
|
| 246 |
+`:CsPrompt <op>` is user command to invoke prompt. This command provides prompt |
|
| 247 |
+which asks for input before running `:Cscope` command. |
|
| 248 |
+ |
|
| 249 |
+e.g. Following snippet maps C-c C-g to find global def of symbol under cursor |
|
| 250 |
+ |
|
| 251 |
+>lua |
|
| 252 |
+ vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>CsPrompt g<cr>")
|
|
| 253 |
+< |
|
| 254 |
+ |
|
| 255 |
+ |
|
| 256 |
+USING :CSCOPE COMMAND |
|
| 257 |
+ |
|
| 258 |
+e.g. Following snippet maps C-c C-g to find global def of symbol under cursor |
|
| 259 |
+ |
|
| 260 |
+>lua |
|
| 261 |
+ vim.keymap.set({ "n", "v" }, "<C-c><C-g>", "<cmd>Cs f g<cr>")
|
|
| 262 |
+< |
|
| 263 |
+ |
|
| 264 |
+Generated by panvimdoc <https://github.com/kdheepak/panvimdoc> |
|
| 265 |
+ |
|
| 266 |
+vim:tw=78:ts=8:noet:ft=help:norl: |
| ... | ... |
@@ -0,0 +1,201 @@ |
| 1 |
+local utils = require("cscope_maps.utils")
|
|
| 2 |
+local log = require("cscope_maps.utils.log")
|
|
| 3 |
+ |
|
| 4 |
+local M = {}
|
|
| 5 |
+ |
|
| 6 |
+--- conns = { {file = db_file, pre_path = db_pre_path}, ... }
|
|
| 7 |
+M.conns = {}
|
|
| 8 |
+M.global_conn = nil |
|
| 9 |
+ |
|
| 10 |
+M.sep = "::" |
|
| 11 |
+ |
|
| 12 |
+M.reset = function() |
|
| 13 |
+ M.conns = {}
|
|
| 14 |
+ M.global_conn = nil |
|
| 15 |
+end |
|
| 16 |
+ |
|
| 17 |
+---Get all db connections |
|
| 18 |
+---If global connection is declared then use that |
|
| 19 |
+---@return table |
|
| 20 |
+M.all_conns = function() |
|
| 21 |
+ M.update_global_conn() |
|
| 22 |
+ return M.global_conn or M.conns |
|
| 23 |
+end |
|
| 24 |
+ |
|
| 25 |
+---Get primary db connection |
|
| 26 |
+---If global connection is declared then use that |
|
| 27 |
+---@return table |
|
| 28 |
+M.primary_conn = function() |
|
| 29 |
+ M.update_global_conn() |
|
| 30 |
+ if M.global_conn then |
|
| 31 |
+ return M.global_conn[1] |
|
| 32 |
+ end |
|
| 33 |
+ return M.conns[1] |
|
| 34 |
+end |
|
| 35 |
+ |
|
| 36 |
+---Update primary db connection |
|
| 37 |
+---@param file string |
|
| 38 |
+---@param pre_path string |
|
| 39 |
+M.update_primary_conn = function(file, pre_path) |
|
| 40 |
+ M.conns[1].file = vim.fs.normalize(file) |
|
| 41 |
+ M.conns[1].pre_path = vim.fs.normalize(pre_path) |
|
| 42 |
+end |
|
| 43 |
+ |
|
| 44 |
+---Update global db connection |
|
| 45 |
+M.update_global_conn = function() |
|
| 46 |
+ if vim.g.cscope_maps_db_file then |
|
| 47 |
+ local file, pre_path = M.sp_file_pre_path(vim.g.cscope_maps_db_file) |
|
| 48 |
+ M.global_conn = { { file = file, pre_path = pre_path } }
|
|
| 49 |
+ else |
|
| 50 |
+ M.global_conn = nil |
|
| 51 |
+ end |
|
| 52 |
+end |
|
| 53 |
+ |
|
| 54 |
+---Split input of ":Cs db add" into file and pre_path and normalize them |
|
| 55 |
+---@param path string |
|
| 56 |
+---@return string |
|
| 57 |
+---@return string|nil |
|
| 58 |
+M.sp_file_pre_path = function(path) |
|
| 59 |
+ local sp = vim.split(path, M.sep) |
|
| 60 |
+ local file = sp[1] |
|
| 61 |
+ local pre_path = sp[2] |
|
| 62 |
+ |
|
| 63 |
+ file = vim.fs.normalize(file) |
|
| 64 |
+ |
|
| 65 |
+ -- use cwd if pre_path is not provided |
|
| 66 |
+ if pre_path == nil or pre_path == "" then |
|
| 67 |
+ pre_path = vim.fn.getcwd() |
|
| 68 |
+ end |
|
| 69 |
+ |
|
| 70 |
+ -- use parent as pre_path if its "@" |
|
| 71 |
+ if pre_path == "@" then |
|
| 72 |
+ pre_path = utils.get_path_parent(file) |
|
| 73 |
+ end |
|
| 74 |
+ |
|
| 75 |
+ -- normalize it |
|
| 76 |
+ pre_path = vim.fs.normalize(pre_path) |
|
| 77 |
+ |
|
| 78 |
+ return file, pre_path |
|
| 79 |
+end |
|
| 80 |
+ |
|
| 81 |
+---Find index of db in all connections |
|
| 82 |
+---@param file string |
|
| 83 |
+---@param pre_path string|nil |
|
| 84 |
+---@return integer |
|
| 85 |
+M.find = function(file, pre_path) |
|
| 86 |
+ for i, cons in ipairs(M.conns) do |
|
| 87 |
+ if |
|
| 88 |
+ utils.is_path_same(cons.file, file) |
|
| 89 |
+ and (utils.is_path_same(cons.pre_path, pre_path) or cons.pre_path == nil) |
|
| 90 |
+ then |
|
| 91 |
+ return i |
|
| 92 |
+ end |
|
| 93 |
+ end |
|
| 94 |
+ |
|
| 95 |
+ return -1 |
|
| 96 |
+end |
|
| 97 |
+ |
|
| 98 |
+---Add db in db connections |
|
| 99 |
+---@param path string |
|
| 100 |
+M.add = function(path) |
|
| 101 |
+ local file, pre_path = M.sp_file_pre_path(path) |
|
| 102 |
+ if M.find(file, pre_path) == -1 then |
|
| 103 |
+ table.insert(M.conns, { file = file, pre_path = pre_path })
|
|
| 104 |
+ end |
|
| 105 |
+end |
|
| 106 |
+ |
|
| 107 |
+---Remove db from db connections |
|
| 108 |
+---Primary db connection will not be removed |
|
| 109 |
+---@param path string |
|
| 110 |
+M.remove = function(path) |
|
| 111 |
+ local file, pre_path = M.sp_file_pre_path(path) |
|
| 112 |
+ local loc = M.find(file, pre_path) |
|
| 113 |
+ -- do not remove first entry |
|
| 114 |
+ if loc > 1 then |
|
| 115 |
+ table.remove(M.conns, loc) |
|
| 116 |
+ end |
|
| 117 |
+end |
|
| 118 |
+ |
|
| 119 |
+---Update DB connections |
|
| 120 |
+---@param op string Operation (add/remove) |
|
| 121 |
+---@param files table list of files |
|
| 122 |
+M.update = function(op, files) |
|
| 123 |
+ if op == "a" then |
|
| 124 |
+ for _, f in ipairs(files) do |
|
| 125 |
+ M.add(f) |
|
| 126 |
+ end |
|
| 127 |
+ elseif op == "r" then |
|
| 128 |
+ for _, f in ipairs(files) do |
|
| 129 |
+ M.remove(f) |
|
| 130 |
+ end |
|
| 131 |
+ end |
|
| 132 |
+end |
|
| 133 |
+ |
|
| 134 |
+M.print_conns = function() |
|
| 135 |
+ if not M.conns then |
|
| 136 |
+ log.warn("No connections")
|
|
| 137 |
+ end |
|
| 138 |
+ |
|
| 139 |
+ for i, conn in ipairs(M.conns) do |
|
| 140 |
+ local file = utils.get_rel_path(vim.fn.getcwd(), conn.file) |
|
| 141 |
+ local pre_path = utils.get_rel_path(vim.fn.getcwd(), conn.pre_path) |
|
| 142 |
+ if not pre_path or pre_path == "" then |
|
| 143 |
+ log.warn(string.format("%d) db=%s", i, file))
|
|
| 144 |
+ else |
|
| 145 |
+ log.warn(string.format("%d) db=%s pre_path=%s", i, file, pre_path))
|
|
| 146 |
+ end |
|
| 147 |
+ end |
|
| 148 |
+end |
|
| 149 |
+ |
|
| 150 |
+---Create command to build DB |
|
| 151 |
+---1. If script is default then use opt.exec |
|
| 152 |
+---2. If custom script is provided then use that with "-d <db>::<pre_path>" args |
|
| 153 |
+M.get_build_cmd = function(opts) |
|
| 154 |
+ local cmd = {}
|
|
| 155 |
+ |
|
| 156 |
+ if opts.db_build_cmd.script == "default" then |
|
| 157 |
+ if opts.exec == "cscope" then |
|
| 158 |
+ cmd = { "cscope", "-f", M.primary_conn().file }
|
|
| 159 |
+ else -- "gtags-cscope" |
|
| 160 |
+ cmd = { "gtags-cscope" }
|
|
| 161 |
+ end |
|
| 162 |
+ |
|
| 163 |
+ vim.list_extend(cmd, opts.db_build_cmd.args) |
|
| 164 |
+ return cmd |
|
| 165 |
+ end |
|
| 166 |
+ |
|
| 167 |
+ -- custom script |
|
| 168 |
+ cmd = { opts.db_build_cmd.script }
|
|
| 169 |
+ vim.list_extend(cmd, opts.db_build_cmd.args) |
|
| 170 |
+ |
|
| 171 |
+ for _, conn in ipairs(M.conns) do |
|
| 172 |
+ vim.list_extend(cmd, { "-d", string.format("%s::%s", conn.file, conn.pre_path) })
|
|
| 173 |
+ end |
|
| 174 |
+ |
|
| 175 |
+ return cmd |
|
| 176 |
+end |
|
| 177 |
+ |
|
| 178 |
+local on_exit = function(obj) |
|
| 179 |
+ vim.g.cscope_maps_statusline_indicator = nil |
|
| 180 |
+ if obj.code == 0 then |
|
| 181 |
+ -- print("cscope: [build] out: " .. obj.stdout)
|
|
| 182 |
+ print("cscope: database built successfully")
|
|
| 183 |
+ else |
|
| 184 |
+ -- print("cscope: [build] out: " .. obj.stderr)
|
|
| 185 |
+ print("cscope: database build failed")
|
|
| 186 |
+ end |
|
| 187 |
+end |
|
| 188 |
+ |
|
| 189 |
+M.build = function(opts) |
|
| 190 |
+ if vim.g.cscope_maps_statusline_indicator then |
|
| 191 |
+ log.warn("db build is already in progress")
|
|
| 192 |
+ return |
|
| 193 |
+ end |
|
| 194 |
+ |
|
| 195 |
+ local cmd = M.get_build_cmd(opts) |
|
| 196 |
+ |
|
| 197 |
+ vim.g.cscope_maps_statusline_indicator = opts.statusline_indicator or opts.exec |
|
| 198 |
+ vim.system(cmd, { text = true }, on_exit)
|
|
| 199 |
+end |
|
| 200 |
+ |
|
| 201 |
+return M |
| ... | ... |
@@ -0,0 +1,541 @@ |
| 1 |
+local RC = require("cscope_maps.utils.ret_codes")
|
|
| 2 |
+local log = require("cscope_maps.utils.log")
|
|
| 3 |
+local helper = require("cscope_maps.utils.helper")
|
|
| 4 |
+local utils = require("cscope_maps.utils")
|
|
| 5 |
+local db = require("cscope.db")
|
|
| 6 |
+ |
|
| 7 |
+local M = {}
|
|
| 8 |
+ |
|
| 9 |
+---@class CsProjectRooterConfig |
|
| 10 |
+---@field enable? boolean |
|
| 11 |
+---@field change_cwd? boolean |
|
| 12 |
+ |
|
| 13 |
+---@class CsConfig |
|
| 14 |
+---@field db_file? string|[string] |
|
| 15 |
+---@field exec? string |
|
| 16 |
+---@field picker? string |
|
| 17 |
+---@field qf_window_size? integer |
|
| 18 |
+---@field qf_window_pos? string |
|
| 19 |
+---@field skip_picker_for_single_result? boolean |
|
| 20 |
+---@field db_build_cmd? table |
|
| 21 |
+---@field statusline_indicator? string|nil |
|
| 22 |
+---@field project_rooter? CsProjectRooterConfig |
|
| 23 |
+M.opts = {
|
|
| 24 |
+ db_file = "./cscope.out", |
|
| 25 |
+ exec = "cscope", |
|
| 26 |
+ picker = "quickfix", |
|
| 27 |
+ qf_window_size = 5, |
|
| 28 |
+ qf_window_pos = "bottom", |
|
| 29 |
+ skip_picker_for_single_result = false, |
|
| 30 |
+ db_build_cmd = { script = "default", args = { "-bqkv" } },
|
|
| 31 |
+ statusline_indicator = nil, |
|
| 32 |
+ project_rooter = {
|
|
| 33 |
+ enable = false, |
|
| 34 |
+ change_cwd = false, |
|
| 35 |
+ }, |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+M.user_opts = {}
|
|
| 39 |
+ |
|
| 40 |
+-- operation symbol to number map |
|
| 41 |
+M.op_s_n = {
|
|
| 42 |
+ s = "0", |
|
| 43 |
+ g = "1", |
|
| 44 |
+ d = "2", |
|
| 45 |
+ c = "3", |
|
| 46 |
+ t = "4", |
|
| 47 |
+ e = "6", |
|
| 48 |
+ f = "7", |
|
| 49 |
+ i = "8", |
|
| 50 |
+ a = "9", |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+-- operation number to symbol map |
|
| 54 |
+M.op_n_s = {}
|
|
| 55 |
+for k, v in pairs(M.op_s_n) do |
|
| 56 |
+ M.op_n_s[v] = k |
|
| 57 |
+end |
|
| 58 |
+ |
|
| 59 |
+local cscope_picker = nil |
|
| 60 |
+local gtags_db = "GTAGS" |
|
| 61 |
+ |
|
| 62 |
+M.help = function() |
|
| 63 |
+ print([[ |
|
| 64 |
+Cscope commands: |
|
| 65 |
+find : Query for a pattern (Usage: find a|c|d|e|f|g|i|s|t name) |
|
| 66 |
+ a: Find assignments to this symbol |
|
| 67 |
+ c: Find functions calling this function |
|
| 68 |
+ d: Find functions called by this function |
|
| 69 |
+ e: Find this egrep pattern |
|
| 70 |
+ f: Find this file |
|
| 71 |
+ g: Find this definition |
|
| 72 |
+ i: Find files #including this file |
|
| 73 |
+ s: Find this C symbol |
|
| 74 |
+ t: Find this text string |
|
| 75 |
+ |
|
| 76 |
+db : DB related queries (Usage: db build|add <files>|rm <files>|show) |
|
| 77 |
+ build : Build cscope database |
|
| 78 |
+ add : Add db file(s) |
|
| 79 |
+ rm : Remove db file(s) |
|
| 80 |
+ show : Show current db file(s) |
|
| 81 |
+ |
|
| 82 |
+reload: Reload plugin config |
|
| 83 |
+help : Show this message (Usage: help) |
|
| 84 |
+]]) |
|
| 85 |
+end |
|
| 86 |
+ |
|
| 87 |
+M.push_tagstack = function() |
|
| 88 |
+ local from = { vim.fn.bufnr("%"), vim.fn.line("."), vim.fn.col("."), 0 }
|
|
| 89 |
+ local items = { { tagname = vim.fn.expand("<cword>"), from = from } }
|
|
| 90 |
+ local ts = vim.fn.gettagstack() |
|
| 91 |
+ local ts_last_item = ts.items[ts.curidx - 1] |
|
| 92 |
+ |
|
| 93 |
+ if |
|
| 94 |
+ ts_last_item |
|
| 95 |
+ and ts_last_item.tagname == items[1].tagname |
|
| 96 |
+ and ts_last_item.from[1] == items[1].from[1] |
|
| 97 |
+ and ts_last_item.from[2] == items[1].from[2] |
|
| 98 |
+ then |
|
| 99 |
+ -- Don't push duplicates on tagstack |
|
| 100 |
+ return |
|
| 101 |
+ end |
|
| 102 |
+ |
|
| 103 |
+ vim.fn.settagstack(vim.fn.win_getid(), { items = items }, "t")
|
|
| 104 |
+end |
|
| 105 |
+ |
|
| 106 |
+M.parse_line = function(line, db_pre_path) |
|
| 107 |
+ local t = {}
|
|
| 108 |
+ |
|
| 109 |
+ -- Populate t with filename, context and linenumber |
|
| 110 |
+ local sp = vim.split(line, "%s+") |
|
| 111 |
+ |
|
| 112 |
+ t.filename = sp[1] |
|
| 113 |
+ -- prepend db_pre_path when both of following are true - |
|
| 114 |
+ -- 1. relative path is used for filename |
|
| 115 |
+ -- 2. db_pre_path is not present in filename |
|
| 116 |
+ if db_pre_path and not utils.is_path_abs(t.filename) and not vim.startswith(t.filename, db_pre_path) then |
|
| 117 |
+ t.filename = vim.fs.joinpath(db_pre_path, t.filename) |
|
| 118 |
+ end |
|
| 119 |
+ t.filename = utils.get_rel_path(vim.fn.getcwd(), t.filename) |
|
| 120 |
+ |
|
| 121 |
+ t.ctx = sp[2] |
|
| 122 |
+ t.lnum = sp[3] |
|
| 123 |
+ local sz = #sp[1] + #sp[2] + #sp[3] + 3 |
|
| 124 |
+ |
|
| 125 |
+ -- Populate t["text"] with search result |
|
| 126 |
+ t.text = string.sub(line, sz, -1) |
|
| 127 |
+ |
|
| 128 |
+ -- Enclose context with << >> |
|
| 129 |
+ if string.sub(t.ctx, 1, 1) == "<" then |
|
| 130 |
+ t.ctx = "<" .. t.ctx .. ">" |
|
| 131 |
+ else |
|
| 132 |
+ t.ctx = "<<" .. t.ctx .. ">>" |
|
| 133 |
+ end |
|
| 134 |
+ |
|
| 135 |
+ -- Add context to text |
|
| 136 |
+ t.text = t.ctx .. t.text |
|
| 137 |
+ |
|
| 138 |
+ return t |
|
| 139 |
+end |
|
| 140 |
+ |
|
| 141 |
+M.parse_output = function(cs_out, db_pre_path) |
|
| 142 |
+ -- Parse cscope output to be populated in QuickFix List |
|
| 143 |
+ -- setqflist() takes list of dicts to be shown in QF List. See :h setqflist() |
|
| 144 |
+ |
|
| 145 |
+ local res = {}
|
|
| 146 |
+ |
|
| 147 |
+ for line in string.gmatch(cs_out, "([^\n]+)") do |
|
| 148 |
+ local parsed_line = M.parse_line(line, db_pre_path) |
|
| 149 |
+ table.insert(res, parsed_line) |
|
| 150 |
+ end |
|
| 151 |
+ |
|
| 152 |
+ return res |
|
| 153 |
+end |
|
| 154 |
+ |
|
| 155 |
+M.open_picker = function(op_s, symbol, parsed_output) |
|
| 156 |
+ local title = "cscope find " .. op_s .. " " .. symbol |
|
| 157 |
+ |
|
| 158 |
+ -- Push current symbol on tagstack |
|
| 159 |
+ M.push_tagstack() |
|
| 160 |
+ |
|
| 161 |
+ -- update jumplist |
|
| 162 |
+ vim.cmd([[normal! m']]) |
|
| 163 |
+ |
|
| 164 |
+ if M.opts.skip_picker_for_single_result and #parsed_output == 1 then |
|
| 165 |
+ utils.open_file(parsed_output[1]["filename"], tonumber(parsed_output[1]["lnum"], 10)) |
|
| 166 |
+ return RC.SUCCESS |
|
| 167 |
+ end |
|
| 168 |
+ |
|
| 169 |
+ local picker_opts = {}
|
|
| 170 |
+ picker_opts.cscope = {}
|
|
| 171 |
+ picker_opts.cscope.parsed_output = parsed_output |
|
| 172 |
+ picker_opts.cscope.prompt_title = title |
|
| 173 |
+ picker_opts.cscope.qf_window_size = M.opts.qf_window_size |
|
| 174 |
+ picker_opts.cscope.qf_window_pos = M.opts.qf_window_pos |
|
| 175 |
+ |
|
| 176 |
+ cscope_picker.run(picker_opts) |
|
| 177 |
+ return RC.SUCCESS |
|
| 178 |
+end |
|
| 179 |
+ |
|
| 180 |
+M.cmd_exec = function(cmd) |
|
| 181 |
+ local file = assert(io.popen(cmd, "r")) |
|
| 182 |
+ file:flush() |
|
| 183 |
+ local output = file:read("*all")
|
|
| 184 |
+ file:close() |
|
| 185 |
+ return output |
|
| 186 |
+end |
|
| 187 |
+ |
|
| 188 |
+M.get_result = function(op_n, op_s, symbol, hide_log) |
|
| 189 |
+ -- Executes cscope search and return parsed output |
|
| 190 |
+ |
|
| 191 |
+ local db_conns = db.all_conns() |
|
| 192 |
+ local cmd = string.format("%s -dL -%s %s", M.opts.exec, op_n, symbol)
|
|
| 193 |
+ local out = "" |
|
| 194 |
+ local res = {}
|
|
| 195 |
+ |
|
| 196 |
+ local exec_and_update_res = function(_db_con, _cmd_args) |
|
| 197 |
+ if vim.loop.fs_stat(_db_con.file) == nil then |
|
| 198 |
+ return |
|
| 199 |
+ end |
|
| 200 |
+ |
|
| 201 |
+ local _cmd = string.format("%s -f %s -P %s %s", cmd, _db_con.file, _db_con.pre_path, _cmd_args)
|
|
| 202 |
+ out = M.cmd_exec(_cmd) |
|
| 203 |
+ res = vim.list_extend(res, M.parse_output(out, _db_con.pre_path)) |
|
| 204 |
+ end |
|
| 205 |
+ |
|
| 206 |
+ if M.opts.exec == "cscope" then |
|
| 207 |
+ for _, db_con in ipairs(db_conns) do |
|
| 208 |
+ exec_and_update_res(db_con, "") |
|
| 209 |
+ end |
|
| 210 |
+ elseif M.opts.exec == "gtags-cscope" then |
|
| 211 |
+ if op_s == "d" then |
|
| 212 |
+ log.warn("'d' operation is not available for " .. M.opts.exec, hide_log)
|
|
| 213 |
+ return RC.INVALID_OP, nil |
|
| 214 |
+ end |
|
| 215 |
+ exec_and_update_res(db.primary_conn(), "-a") |
|
| 216 |
+ else |
|
| 217 |
+ log.warn("'" .. M.opts.exec .. "' executable is not supported", hide_log)
|
|
| 218 |
+ return RC.INVALID_EXEC, nil |
|
| 219 |
+ end |
|
| 220 |
+ |
|
| 221 |
+ if vim.tbl_isempty(res) then |
|
| 222 |
+ log.warn("no results for 'cscope find " .. op_s .. " " .. symbol .. "'", hide_log)
|
|
| 223 |
+ return RC.NO_RESULTS, nil |
|
| 224 |
+ end |
|
| 225 |
+ |
|
| 226 |
+ return RC.SUCCESS, res |
|
| 227 |
+end |
|
| 228 |
+ |
|
| 229 |
+M.find = function(op, symbol) |
|
| 230 |
+ if symbol == nil then |
|
| 231 |
+ return RC.INVALID_SYMBOL |
|
| 232 |
+ end |
|
| 233 |
+ |
|
| 234 |
+ local ok, res |
|
| 235 |
+ op = tostring(op) |
|
| 236 |
+ |
|
| 237 |
+ if #op ~= 1 then |
|
| 238 |
+ log.warn("operation '" .. op .. "' is invalid")
|
|
| 239 |
+ return RC.INVALID_OP |
|
| 240 |
+ end |
|
| 241 |
+ |
|
| 242 |
+ if string.find("012346789", op) then
|
|
| 243 |
+ ok, res = M.get_result(op, M.op_n_s[op], symbol) |
|
| 244 |
+ elseif string.find("sgdctefia", op) then
|
|
| 245 |
+ ok, res = M.get_result(M.op_s_n[op], op, symbol) |
|
| 246 |
+ else |
|
| 247 |
+ log.warn("operation '" .. op .. "' is invalid")
|
|
| 248 |
+ return RC.INVALID_OP |
|
| 249 |
+ end |
|
| 250 |
+ |
|
| 251 |
+ if ok == RC.SUCCESS then |
|
| 252 |
+ return M.open_picker(op, symbol, res) |
|
| 253 |
+ end |
|
| 254 |
+ |
|
| 255 |
+ return RC.NO_RESULTS |
|
| 256 |
+end |
|
| 257 |
+ |
|
| 258 |
+M.cstag = function(symbol) |
|
| 259 |
+ local op = "g" |
|
| 260 |
+ -- if symbol is not provided use cword |
|
| 261 |
+ symbol = symbol or M.default_sym(op) |
|
| 262 |
+ |
|
| 263 |
+ local ok, res = M.get_result(M.op_s_n[op], op, symbol, true) |
|
| 264 |
+ if ok == RC.SUCCESS then |
|
| 265 |
+ return M.open_picker(op, symbol, res) |
|
| 266 |
+ end |
|
| 267 |
+ -- log.info("trying tags...")
|
|
| 268 |
+ if not pcall(vim.cmd.tjump, symbol) then |
|
| 269 |
+ log.warn("Vim(tag):E426: tag not found: " .. symbol)
|
|
| 270 |
+ return RC.NO_RESULTS |
|
| 271 |
+ end |
|
| 272 |
+ return RC.NO_RESULTS |
|
| 273 |
+end |
|
| 274 |
+ |
|
| 275 |
+M.default_sym = function(op) |
|
| 276 |
+ local sym = "" |
|
| 277 |
+ if vim.fn.mode() == "v" then |
|
| 278 |
+ local saved_reg = vim.fn.getreg("v")
|
|
| 279 |
+ vim.cmd([[noautocmd sil norm! "vy]]) |
|
| 280 |
+ sym = vim.fn.getreg("v")
|
|
| 281 |
+ vim.fn.setreg("v", saved_reg)
|
|
| 282 |
+ else |
|
| 283 |
+ local arg = "<cword>" |
|
| 284 |
+ if vim.tbl_contains({ "f", "i", "7", "8" }, op) then
|
|
| 285 |
+ arg = "<cfile>" |
|
| 286 |
+ end |
|
| 287 |
+ sym = vim.fn.expand(arg) |
|
| 288 |
+ end |
|
| 289 |
+ return sym |
|
| 290 |
+end |
|
| 291 |
+ |
|
| 292 |
+M.run = function(args) |
|
| 293 |
+ -- Parse top level input and call appropriate functions |
|
| 294 |
+ local args_num = #args |
|
| 295 |
+ if args_num < 1 then |
|
| 296 |
+ -- invalid command |
|
| 297 |
+ log.warn("invalid cmd. see :Cscope help")
|
|
| 298 |
+ return |
|
| 299 |
+ end |
|
| 300 |
+ |
|
| 301 |
+ local cmd = args[1] |
|
| 302 |
+ |
|
| 303 |
+ if cmd:sub(1, 1) == "f" then |
|
| 304 |
+ local op = args[2] |
|
| 305 |
+ -- if symbol is not provided use cword or cfile |
|
| 306 |
+ local symbol = args[3] or M.default_sym(op) |
|
| 307 |
+ |
|
| 308 |
+ -- collect all args |
|
| 309 |
+ for i = 4, args_num do |
|
| 310 |
+ symbol = symbol .. " " .. args[i] |
|
| 311 |
+ end |
|
| 312 |
+ |
|
| 313 |
+ -- escape commonly used special chars |
|
| 314 |
+ symbol = symbol:gsub("([%s\"%'%(%)><])", {
|
|
| 315 |
+ [" "] = "\\ ", |
|
| 316 |
+ ['"'] = '\\"', |
|
| 317 |
+ ["'"] = "\\'", |
|
| 318 |
+ ["("] = "\\(",
|
|
| 319 |
+ [")"] = "\\)", |
|
| 320 |
+ [">"] = "\\>", |
|
| 321 |
+ ["<"] = "\\<", |
|
| 322 |
+ }) |
|
| 323 |
+ |
|
| 324 |
+ M.find(op, symbol) |
|
| 325 |
+ elseif cmd:sub(1, 1) == "b" then |
|
| 326 |
+ log.warn("':Cs build' is deprecated. Use ':Cs db build'")
|
|
| 327 |
+ elseif cmd:sub(1, 1) == "d" then |
|
| 328 |
+ if args_num < 2 then |
|
| 329 |
+ log.warn("db command expects atleast 2 arguments")
|
|
| 330 |
+ return |
|
| 331 |
+ end |
|
| 332 |
+ |
|
| 333 |
+ local op = args[2]:sub(1, 1) |
|
| 334 |
+ if op == "b" then |
|
| 335 |
+ db.build(M.opts) |
|
| 336 |
+ elseif op == "a" or op == "r" then |
|
| 337 |
+ -- collect all args |
|
| 338 |
+ local files = {}
|
|
| 339 |
+ for i = 3, args_num do |
|
| 340 |
+ table.insert(files, args[i]) |
|
| 341 |
+ end |
|
| 342 |
+ |
|
| 343 |
+ db.update(op, files) |
|
| 344 |
+ elseif op == "s" then |
|
| 345 |
+ db.print_conns() |
|
| 346 |
+ else |
|
| 347 |
+ log.warn("invalid operation")
|
|
| 348 |
+ end |
|
| 349 |
+ elseif cmd:sub(1, 1) == "h" then |
|
| 350 |
+ M.help() |
|
| 351 |
+ elseif cmd:sub(1, 1) == "r" then |
|
| 352 |
+ M.reload() |
|
| 353 |
+ else |
|
| 354 |
+ log.warn("command '" .. cmd .. "' is invalid")
|
|
| 355 |
+ end |
|
| 356 |
+end |
|
| 357 |
+ |
|
| 358 |
+M.cmd_cmp = function(_, line) |
|
| 359 |
+ local cmds = { "find", "db", "reload", "help" }
|
|
| 360 |
+ local l = vim.split(line, "%s+") |
|
| 361 |
+ local n = #l - 2 |
|
| 362 |
+ |
|
| 363 |
+ if n == 0 then |
|
| 364 |
+ return vim.tbl_filter(function(val) |
|
| 365 |
+ return vim.startswith(val, l[2]) |
|
| 366 |
+ end, cmds) |
|
| 367 |
+ end |
|
| 368 |
+ |
|
| 369 |
+ local short_cmd = l[2]:sub(1, 1) |
|
| 370 |
+ if n == 1 then |
|
| 371 |
+ if short_cmd == "f" then |
|
| 372 |
+ return vim.tbl_keys(M.op_s_n) |
|
| 373 |
+ end |
|
| 374 |
+ |
|
| 375 |
+ if short_cmd == "d" then |
|
| 376 |
+ cmds = { "build", "add", "rm", "show" }
|
|
| 377 |
+ return vim.tbl_filter(function(val) |
|
| 378 |
+ return vim.startswith(val, l[3]) |
|
| 379 |
+ end, cmds) |
|
| 380 |
+ end |
|
| 381 |
+ end |
|
| 382 |
+ |
|
| 383 |
+ local short_cmd2 = l[3]:sub(1, 1) |
|
| 384 |
+ local cur_arg = l[#l] |
|
| 385 |
+ |
|
| 386 |
+ if n == 2 and short_cmd == "f" then |
|
| 387 |
+ -- complete default_sym for "find" cmd |
|
| 388 |
+ local default_sym = M.default_sym(short_cmd2) |
|
| 389 |
+ if cur_arg == "" or vim.startswith(default_sym, cur_arg) then |
|
| 390 |
+ return { default_sym }
|
|
| 391 |
+ end |
|
| 392 |
+ end |
|
| 393 |
+ |
|
| 394 |
+ if n >= 2 and short_cmd == "d" and short_cmd2 == "a" then |
|
| 395 |
+ local sp = vim.split(cur_arg, db.sep) |
|
| 396 |
+ local parent, fs_entries |
|
| 397 |
+ |
|
| 398 |
+ if sp[2] ~= nil then |
|
| 399 |
+ -- complete pre path. |
|
| 400 |
+ -- this will show "@" and dirs only |
|
| 401 |
+ parent = utils.get_path_parent(sp[2]) |
|
| 402 |
+ fs_entries = utils.get_dirs_in_dir(parent) |
|
| 403 |
+ table.insert(fs_entries, 1, "@") |
|
| 404 |
+ |
|
| 405 |
+ fs_entries = vim.tbl_map(function(x) |
|
| 406 |
+ return sp[1] .. db.sep .. x |
|
| 407 |
+ end, fs_entries) |
|
| 408 |
+ else |
|
| 409 |
+ -- complete db path |
|
| 410 |
+ -- this will show all files |
|
| 411 |
+ parent = utils.get_path_parent(cur_arg) |
|
| 412 |
+ fs_entries = utils.get_files_in_dir(parent) |
|
| 413 |
+ end |
|
| 414 |
+ |
|
| 415 |
+ return vim.tbl_filter(function(val) |
|
| 416 |
+ return vim.startswith(val, cur_arg) |
|
| 417 |
+ end, fs_entries) |
|
| 418 |
+ end |
|
| 419 |
+ |
|
| 420 |
+ if n >= 2 and short_cmd == "d" and short_cmd2 == "r" then |
|
| 421 |
+ -- complete db_conns except primary_conn |
|
| 422 |
+ local db_conns = db.all_conns() |
|
| 423 |
+ local entries = {}
|
|
| 424 |
+ if not db_conns then |
|
| 425 |
+ return entries |
|
| 426 |
+ end |
|
| 427 |
+ |
|
| 428 |
+ for i, conn in ipairs(db_conns) do |
|
| 429 |
+ if i > 1 then |
|
| 430 |
+ table.insert(entries, string.format("%s%s%s", conn.file, db.sep, conn.pre_path))
|
|
| 431 |
+ end |
|
| 432 |
+ end |
|
| 433 |
+ |
|
| 434 |
+ return vim.tbl_filter(function(val) |
|
| 435 |
+ return vim.startswith(val, cur_arg) |
|
| 436 |
+ end, entries) |
|
| 437 |
+ end |
|
| 438 |
+end |
|
| 439 |
+ |
|
| 440 |
+M.user_command = function() |
|
| 441 |
+ -- Create the :Cscope user command |
|
| 442 |
+ vim.api.nvim_create_user_command("Cscope", function(opts)
|
|
| 443 |
+ M.run(opts.fargs) |
|
| 444 |
+ end, {
|
|
| 445 |
+ nargs = "*", |
|
| 446 |
+ complete = M.cmd_cmp, |
|
| 447 |
+ }) |
|
| 448 |
+ |
|
| 449 |
+ -- Create the :Cs user command |
|
| 450 |
+ vim.api.nvim_create_user_command("Cs", function(opts)
|
|
| 451 |
+ M.run(opts.fargs) |
|
| 452 |
+ end, {
|
|
| 453 |
+ nargs = "*", |
|
| 454 |
+ complete = M.cmd_cmp, |
|
| 455 |
+ }) |
|
| 456 |
+ |
|
| 457 |
+ -- Create the :Cstag user command |
|
| 458 |
+ vim.api.nvim_create_user_command("Cstag", function(opts)
|
|
| 459 |
+ M.cstag(unpack(opts.fargs)) |
|
| 460 |
+ end, {
|
|
| 461 |
+ nargs = "*", |
|
| 462 |
+ }) |
|
| 463 |
+end |
|
| 464 |
+ |
|
| 465 |
+M.reload = function() |
|
| 466 |
+ db.reset() |
|
| 467 |
+ M.setup(M.user_opts) |
|
| 468 |
+end |
|
| 469 |
+ |
|
| 470 |
+M.root = function(source, marker) |
|
| 471 |
+ if vim.fn.filereadable(vim.fs.joinpath(source, marker)) == 1 then |
|
| 472 |
+ return source |
|
| 473 |
+ end |
|
| 474 |
+ |
|
| 475 |
+ for dir in vim.fs.parents(source) do |
|
| 476 |
+ if vim.fn.filereadable(vim.fs.joinpath(dir, marker)) == 1 then |
|
| 477 |
+ return dir |
|
| 478 |
+ end |
|
| 479 |
+ end |
|
| 480 |
+ return nil |
|
| 481 |
+end |
|
| 482 |
+ |
|
| 483 |
+---Initialization api |
|
| 484 |
+---@param opts CsConfig |
|
| 485 |
+M.setup = function(opts) |
|
| 486 |
+ -- save original opts for reload operation |
|
| 487 |
+ M.user_opts = vim.deepcopy(opts) |
|
| 488 |
+ M.opts = vim.tbl_deep_extend("force", M.opts, opts)
|
|
| 489 |
+ -- This variable can be used by other plugins to change db_file |
|
| 490 |
+ -- e.g. vim-gutentags can use it for when |
|
| 491 |
+ -- vim.g.gutentags_cache_dir is enabled. |
|
| 492 |
+ vim.g.cscope_maps_db_file = nil |
|
| 493 |
+ vim.g.cscope_maps_statusline_indicator = nil |
|
| 494 |
+ |
|
| 495 |
+ if M.opts.exec == "gtags-cscope" then |
|
| 496 |
+ M.opts.db_file = gtags_db |
|
| 497 |
+ end |
|
| 498 |
+ |
|
| 499 |
+ if type(M.opts.db_file) == "string" then |
|
| 500 |
+ db.add(M.opts.db_file) |
|
| 501 |
+ else -- table |
|
| 502 |
+ for _, f in ipairs(M.opts.db_file) do |
|
| 503 |
+ db.add(f) |
|
| 504 |
+ end |
|
| 505 |
+ end |
|
| 506 |
+ |
|
| 507 |
+ if M.opts.db_build_cmd.script ~= "default" and vim.fn.executable(M.opts.db_build_cmd.script) ~= 1 then |
|
| 508 |
+ log.warn(string.format("db_build script(%s) not found. Using default", M.opts.db_build_cmd.script))
|
|
| 509 |
+ M.opts.db_build_cmd = { script = "default", args = { "-bqkv" } }
|
|
| 510 |
+ end |
|
| 511 |
+ |
|
| 512 |
+ if M.opts.db_build_cmd_args then |
|
| 513 |
+ M.opts.db_build_cmd.args = M.opts.db_build_cmd_args |
|
| 514 |
+ log.warn( |
|
| 515 |
+ string.format( |
|
| 516 |
+ [[db_build_cmd_args is deprecated. Use 'db_build_cmd = { args = %s }']],
|
|
| 517 |
+ vim.inspect(M.opts.db_build_cmd_args) |
|
| 518 |
+ ) |
|
| 519 |
+ ) |
|
| 520 |
+ end |
|
| 521 |
+ |
|
| 522 |
+ -- if project rooter is enabled, |
|
| 523 |
+ -- 1. get root of project and update primary conn |
|
| 524 |
+ -- 2. if change_cwd is enabled, change into it (?) |
|
| 525 |
+ if M.opts.project_rooter.enable then |
|
| 526 |
+ local primary_conn = db.primary_conn() |
|
| 527 |
+ local root = M.root(vim.fn.getcwd(), primary_conn.file) |
|
| 528 |
+ if root then |
|
| 529 |
+ db.update_primary_conn(vim.fs.joinpath(root, primary_conn.file), root) |
|
| 530 |
+ |
|
| 531 |
+ if M.opts.project_rooter.change_cwd then |
|
| 532 |
+ vim.cmd("cd " .. root)
|
|
| 533 |
+ end |
|
| 534 |
+ end |
|
| 535 |
+ end |
|
| 536 |
+ |
|
| 537 |
+ cscope_picker = require("cscope.pickers." .. M.opts.picker)
|
|
| 538 |
+ M.user_command() |
|
| 539 |
+end |
|
| 540 |
+ |
|
| 541 |
+return M |
| ... | ... |
@@ -0,0 +1,29 @@ |
| 1 |
+local config = require("fzf-lua.config")
|
|
| 2 |
+local ansi_codes = require("fzf-lua.utils").ansi_codes
|
|
| 3 |
+local make_entry = require("fzf-lua.make_entry")
|
|
| 4 |
+ |
|
| 5 |
+local M = {}
|
|
| 6 |
+ |
|
| 7 |
+local prepare = function(parsed_output) |
|
| 8 |
+ local res = {}
|
|
| 9 |
+ |
|
| 10 |
+ for _, entry in ipairs(parsed_output) do |
|
| 11 |
+ local _entry = ("%s:%s:%s"):format(
|
|
| 12 |
+ make_entry.file(entry["filename"], { file_icons = true, color_icons = true }),
|
|
| 13 |
+ ansi_codes.green(entry["lnum"]), |
|
| 14 |
+ ansi_codes.magenta(entry["text"]) |
|
| 15 |
+ ) |
|
| 16 |
+ |
|
| 17 |
+ table.insert(res, _entry) |
|
| 18 |
+ end |
|
| 19 |
+ return res |
|
| 20 |
+end |
|
| 21 |
+ |
|
| 22 |
+M.run = function(opts) |
|
| 23 |
+ local entries = prepare(opts.cscope.parsed_output) |
|
| 24 |
+ local _config = { prompt = opts.cscope.prompt_title .. "> " }
|
|
| 25 |
+ _config = config.normalize_opts(_config, config.globals.files) |
|
| 26 |
+ require("fzf-lua").fzf_exec(entries, _config)
|
|
| 27 |
+end |
|
| 28 |
+ |
|
| 29 |
+return M |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+M.run = function(opts) |
|
| 4 |
+ opts = opts or {}
|
|
| 5 |
+ local entries = {}
|
|
| 6 |
+ |
|
| 7 |
+ for _, item in ipairs(opts.cscope.parsed_output) do |
|
| 8 |
+ local entry = {
|
|
| 9 |
+ path = item.filename, |
|
| 10 |
+ lnum = tonumber(item.lnum), |
|
| 11 |
+ text = string.format("%s:%s:%s", item.filename, item.lnum, item.text),
|
|
| 12 |
+ } |
|
| 13 |
+ table.insert(entries, entry) |
|
| 14 |
+ end |
|
| 15 |
+ |
|
| 16 |
+ MiniPick.start({
|
|
| 17 |
+ source = {
|
|
| 18 |
+ items = entries, |
|
| 19 |
+ name = opts.cscope.prompt_title, |
|
| 20 |
+ show = function(buf_id, items, query) |
|
| 21 |
+ MiniPick.default_show(buf_id, items, query, { show_icons = true })
|
|
| 22 |
+ end, |
|
| 23 |
+ }, |
|
| 24 |
+ }) |
|
| 25 |
+end |
|
| 26 |
+ |
|
| 27 |
+return M |
| ... | ... |
@@ -0,0 +1,22 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+M.run = function(opts) |
|
| 4 |
+ local pos |
|
| 5 |
+ |
|
| 6 |
+ vim.fn.setqflist(opts.cscope.parsed_output, "r") |
|
| 7 |
+ vim.fn.setqflist({}, "a", { title = opts.cscope.prompt_title })
|
|
| 8 |
+ |
|
| 9 |
+ if opts.cscope.qf_window_pos == "top" then |
|
| 10 |
+ pos = "topleft" |
|
| 11 |
+ elseif opts.cscope.qf_window_pos == "bottom" then |
|
| 12 |
+ pos = "botright" |
|
| 13 |
+ elseif opts.cscope.qf_window_pos == "right" then |
|
| 14 |
+ pos = "botright vertical" |
|
| 15 |
+ elseif opts.cscope.qf_window_pos == "left" then |
|
| 16 |
+ pos = "topleft vertical" |
|
| 17 |
+ end |
|
| 18 |
+ |
|
| 19 |
+ vim.cmd(pos .. " copen " .. opts.cscope.qf_window_size) |
|
| 20 |
+end |
|
| 21 |
+ |
|
| 22 |
+return M |
| ... | ... |
@@ -0,0 +1,25 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+local prepare = function(items) |
|
| 4 |
+ local res = {}
|
|
| 5 |
+ for i, item in ipairs(items) do |
|
| 6 |
+ table.insert(res, {
|
|
| 7 |
+ file = item["filename"], |
|
| 8 |
+ score = i, |
|
| 9 |
+ text = item["text"], |
|
| 10 |
+ line = item["text"], |
|
| 11 |
+ pos = { tonumber(item["lnum"]), 0 },
|
|
| 12 |
+ }) |
|
| 13 |
+ end |
|
| 14 |
+ |
|
| 15 |
+ return res |
|
| 16 |
+end |
|
| 17 |
+ |
|
| 18 |
+M.run = function(opts) |
|
| 19 |
+ Snacks.picker({
|
|
| 20 |
+ items = prepare(opts.cscope.parsed_output), |
|
| 21 |
+ title = opts.cscope.prompt_title, |
|
| 22 |
+ }) |
|
| 23 |
+end |
|
| 24 |
+ |
|
| 25 |
+return M |
| ... | ... |
@@ -0,0 +1,61 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+local pickers = require("telescope.pickers")
|
|
| 4 |
+local finders = require("telescope.finders")
|
|
| 5 |
+local config = require("telescope.config")
|
|
| 6 |
+local utils = require("telescope.utils")
|
|
| 7 |
+local cs_utils = require("cscope_maps.utils")
|
|
| 8 |
+ |
|
| 9 |
+local entry_maker = function(entry) |
|
| 10 |
+ return {
|
|
| 11 |
+ value = entry, |
|
| 12 |
+ display = function() |
|
| 13 |
+ local display_filename = cs_utils.get_rel_path(vim.fn.getcwd(), entry["filename"]) |
|
| 14 |
+ local coordinates = string.format(":%s:", entry["lnum"])
|
|
| 15 |
+ local display_string = "%s%s%s" |
|
| 16 |
+ local display, hl_group, icon = utils.transform_devicons( |
|
| 17 |
+ entry["filename"], |
|
| 18 |
+ string.format(display_string, display_filename, coordinates, entry["text"]), |
|
| 19 |
+ false |
|
| 20 |
+ ) |
|
| 21 |
+ |
|
| 22 |
+ if hl_group then |
|
| 23 |
+ return display, { { { 1, #icon }, hl_group } }
|
|
| 24 |
+ else |
|
| 25 |
+ return display |
|
| 26 |
+ end |
|
| 27 |
+ end, |
|
| 28 |
+ ordinal = entry["filename"] .. entry["text"], |
|
| 29 |
+ path = cs_utils.get_abs_path(entry["filename"]), |
|
| 30 |
+ lnum = tonumber(entry["lnum"]), |
|
| 31 |
+ } |
|
| 32 |
+end |
|
| 33 |
+ |
|
| 34 |
+local finder = nil |
|
| 35 |
+local prompt_title = nil |
|
| 36 |
+ |
|
| 37 |
+local prepare = function(cscope_parsed_output, telescope_title) |
|
| 38 |
+ finder = finders.new_table({
|
|
| 39 |
+ results = cscope_parsed_output, |
|
| 40 |
+ entry_maker = entry_maker, |
|
| 41 |
+ }) |
|
| 42 |
+ |
|
| 43 |
+ prompt_title = telescope_title |
|
| 44 |
+end |
|
| 45 |
+ |
|
| 46 |
+M.run = function(opts) |
|
| 47 |
+ opts = opts or {}
|
|
| 48 |
+ opts.entry_maker = entry_maker |
|
| 49 |
+ prepare(opts.cscope.parsed_output, opts.cscope.prompt_title) |
|
| 50 |
+ |
|
| 51 |
+ pickers |
|
| 52 |
+ .new(opts, {
|
|
| 53 |
+ prompt_title = prompt_title, |
|
| 54 |
+ finder = finder, |
|
| 55 |
+ previewer = config.values.grep_previewer(opts), |
|
| 56 |
+ sorter = config.values.generic_sorter(opts), |
|
| 57 |
+ }) |
|
| 58 |
+ :find() |
|
| 59 |
+end |
|
| 60 |
+ |
|
| 61 |
+return M |
| ... | ... |
@@ -0,0 +1,96 @@ |
| 1 |
+local tree = require("cscope.stack_view.tree")
|
|
| 2 |
+local fn = vim.fn |
|
| 3 |
+local api = vim.api |
|
| 4 |
+ |
|
| 5 |
+local M = {}
|
|
| 6 |
+M.ft = "CsStackView" |
|
| 7 |
+ |
|
| 8 |
+M.get_pos = function(lnum) |
|
| 9 |
+ local line = fn.getline(lnum) |
|
| 10 |
+ local indent_len = #line |
|
| 11 |
+ line = vim.trim(line) |
|
| 12 |
+ indent_len = indent_len - #line |
|
| 13 |
+ |
|
| 14 |
+ local line_split = vim.split(line, "%s+") |
|
| 15 |
+ local symbol = line_split[2] |
|
| 16 |
+ local fname = "" |
|
| 17 |
+ local flnum = "" |
|
| 18 |
+ |
|
| 19 |
+ if #line_split == 3 then |
|
| 20 |
+ local file_loc = vim.split(line_split[3], "::") |
|
| 21 |
+ fname = file_loc[1]:sub(2) |
|
| 22 |
+ flnum = file_loc[2]:sub(1, -2) |
|
| 23 |
+ end |
|
| 24 |
+ |
|
| 25 |
+ local indicator_s = indent_len |
|
| 26 |
+ local indicator_e = indicator_s + 2 |
|
| 27 |
+ |
|
| 28 |
+ local symbol_s = indicator_e + 1 |
|
| 29 |
+ local symbol_e = symbol_s + #symbol |
|
| 30 |
+ |
|
| 31 |
+ local bo_s = symbol_e + 1 |
|
| 32 |
+ local bo_e = bo_s + 1 |
|
| 33 |
+ |
|
| 34 |
+ local fname_s = symbol_e + 2 |
|
| 35 |
+ local fname_e = fname_s + #fname |
|
| 36 |
+ |
|
| 37 |
+ local delim_s = fname_e |
|
| 38 |
+ local delim_e = fname_e + 2 |
|
| 39 |
+ |
|
| 40 |
+ local lnum_s = fname_e + 2 |
|
| 41 |
+ local lnum_e = lnum_s + #flnum |
|
| 42 |
+ |
|
| 43 |
+ local bc_s = lnum_e |
|
| 44 |
+ local bc_e = bc_s + 1 |
|
| 45 |
+ |
|
| 46 |
+ return indicator_s, |
|
| 47 |
+ indicator_e, |
|
| 48 |
+ symbol_s, |
|
| 49 |
+ symbol_e, |
|
| 50 |
+ bo_s, |
|
| 51 |
+ bo_e, |
|
| 52 |
+ fname_s, |
|
| 53 |
+ fname_e, |
|
| 54 |
+ delim_s, |
|
| 55 |
+ delim_e, |
|
| 56 |
+ lnum_s, |
|
| 57 |
+ lnum_e, |
|
| 58 |
+ bc_s, |
|
| 59 |
+ bc_e |
|
| 60 |
+end |
|
| 61 |
+ |
|
| 62 |
+M.refresh = function(buf, root) |
|
| 63 |
+ if vim.bo.filetype ~= M.ft then |
|
| 64 |
+ return |
|
| 65 |
+ end |
|
| 66 |
+ local ancestors = tree.get_ancestors(root, fn.line("."))
|
|
| 67 |
+ |
|
| 68 |
+ local ns = api.nvim_create_namespace("CsStackViewHighlight")
|
|
| 69 |
+ |
|
| 70 |
+ local buf_lnum_start = fn.line("w0") - 1
|
|
| 71 |
+ local buf_lnum_end = fn.line("w$")
|
|
| 72 |
+ local buf_lnum = buf_lnum_start |
|
| 73 |
+ |
|
| 74 |
+ api.nvim_buf_clear_namespace(buf, ns, 0, -1) |
|
| 75 |
+ |
|
| 76 |
+ while buf_lnum < buf_lnum_end do |
|
| 77 |
+ if buf_lnum == 0 then |
|
| 78 |
+ api.nvim_buf_add_highlight(buf, ns, "Function", buf_lnum, 0, -1) |
|
| 79 |
+ elseif vim.tbl_contains(ancestors, buf_lnum + 1) then |
|
| 80 |
+ local indicator_s, indicator_e, symbol_s, symbol_e, bo_s, bo_e, fname_s, fname_e, delim_s, delim_e, lnum_s, lnum_e, bc_s, bc_e = |
|
| 81 |
+ M.get_pos(buf_lnum + 1) |
|
| 82 |
+ api.nvim_buf_add_highlight(buf, ns, "Operator", buf_lnum, indicator_s, indicator_e) |
|
| 83 |
+ api.nvim_buf_add_highlight(buf, ns, "Function", buf_lnum, symbol_s, symbol_e) |
|
| 84 |
+ api.nvim_buf_add_highlight(buf, ns, "Delimiter", buf_lnum, bo_s, bo_e) |
|
| 85 |
+ api.nvim_buf_add_highlight(buf, ns, "String", buf_lnum, fname_s, fname_e) |
|
| 86 |
+ api.nvim_buf_add_highlight(buf, ns, "Delimiter", buf_lnum, delim_s, delim_e) |
|
| 87 |
+ api.nvim_buf_add_highlight(buf, ns, "Number", buf_lnum, lnum_s, lnum_e) |
|
| 88 |
+ api.nvim_buf_add_highlight(buf, ns, "Delimiter", buf_lnum, bc_s, bc_e) |
|
| 89 |
+ else |
|
| 90 |
+ api.nvim_buf_add_highlight(buf, ns, "Comment", buf_lnum, 0, -1) |
|
| 91 |
+ end |
|
| 92 |
+ buf_lnum = buf_lnum + 1 |
|
| 93 |
+ end |
|
| 94 |
+end |
|
| 95 |
+ |
|
| 96 |
+return M |
| ... | ... |
@@ -0,0 +1,424 @@ |
| 1 |
+local cs = require("cscope")
|
|
| 2 |
+local tree = require("cscope.stack_view.tree")
|
|
| 3 |
+local hl = require("cscope.stack_view.hl")
|
|
| 4 |
+local utils = require("cscope_maps.utils")
|
|
| 5 |
+local M = {}
|
|
| 6 |
+ |
|
| 7 |
+M.opts = {
|
|
| 8 |
+ tree_hl = true, -- toggle tree highlighting |
|
| 9 |
+} |
|
| 10 |
+ |
|
| 11 |
+-- m() |
|
| 12 |
+-- -> a() |
|
| 13 |
+-- -> b() |
|
| 14 |
+-- -> c() |
|
| 15 |
+-- |
|
| 16 |
+-- a() |
|
| 17 |
+-- <- m() |
|
| 18 |
+-- <- n() |
|
| 19 |
+-- |
|
| 20 |
+-- {a : {m : { { n : {} }, { o : {} } } }}
|
|
| 21 |
+-- node = {data: {} children: {}}
|
|
| 22 |
+ |
|
| 23 |
+-- callers --> DOWN the stack |
|
| 24 |
+-- called --> UP the stack |
|
| 25 |
+ |
|
| 26 |
+M.cache = { sv = { buf = nil, win = nil }, pv = { buf = nil, win = nil, files = {}, last_file = "" } }
|
|
| 27 |
+M.dir_map = {
|
|
| 28 |
+ down = {
|
|
| 29 |
+ indicator = "<- ", |
|
| 30 |
+ cs_func = function(symbol) |
|
| 31 |
+ local _, res = cs.get_result(cs.op_s_n.c, "c", symbol) |
|
| 32 |
+ return res |
|
| 33 |
+ end, |
|
| 34 |
+ }, |
|
| 35 |
+ up = {
|
|
| 36 |
+ indicator = "-> ", |
|
| 37 |
+ cs_func = function(symbol) |
|
| 38 |
+ local _, res = cs.get_result(cs.op_s_n.d, "d", symbol) |
|
| 39 |
+ return res |
|
| 40 |
+ end, |
|
| 41 |
+ }, |
|
| 42 |
+} |
|
| 43 |
+ |
|
| 44 |
+M.ft = "CsStackView" |
|
| 45 |
+local api = vim.api |
|
| 46 |
+local fn = vim.fn |
|
| 47 |
+local root = nil |
|
| 48 |
+local buf_lines = nil |
|
| 49 |
+local cur_dir = nil |
|
| 50 |
+local buf_last_pos = nil |
|
| 51 |
+ |
|
| 52 |
+M.buf_lock = function(buf) |
|
| 53 |
+ api.nvim_set_option_value("readonly", true, { buf = buf })
|
|
| 54 |
+ api.nvim_set_option_value("modifiable", false, { buf = buf })
|
|
| 55 |
+end |
|
| 56 |
+ |
|
| 57 |
+M.buf_unlock = function(buf) |
|
| 58 |
+ api.nvim_set_option_value("readonly", false, { buf = buf })
|
|
| 59 |
+ api.nvim_set_option_value("modifiable", true, { buf = buf })
|
|
| 60 |
+end |
|
| 61 |
+ |
|
| 62 |
+M.pv_scroll = function(dir) |
|
| 63 |
+ local input = dir > 0 and [[]] or [[]] |
|
| 64 |
+ |
|
| 65 |
+ return function() |
|
| 66 |
+ vim.api.nvim_win_call(M.cache.pv.win, function() |
|
| 67 |
+ vim.cmd([[normal! ]] .. input) |
|
| 68 |
+ end) |
|
| 69 |
+ end |
|
| 70 |
+end |
|
| 71 |
+ |
|
| 72 |
+M.set_keymaps = function() |
|
| 73 |
+ local opts = { buffer = M.cache.sv.buf, silent = true }
|
|
| 74 |
+ |
|
| 75 |
+ -- close window |
|
| 76 |
+ vim.keymap.set("n", "q", M.toggle_win, opts)
|
|
| 77 |
+ vim.keymap.set("n", "<esc>", M.toggle_win, opts)
|
|
| 78 |
+ |
|
| 79 |
+ -- toggle children |
|
| 80 |
+ vim.keymap.set("n", "<tab>", M.toggle_children, opts)
|
|
| 81 |
+ |
|
| 82 |
+ -- open location under cursor |
|
| 83 |
+ vim.keymap.set("n", "<cr>", M.enter_action, opts)
|
|
| 84 |
+ |
|
| 85 |
+ -- scroll up |
|
| 86 |
+ vim.keymap.set("n", "<C-u>", M.pv_scroll(-1), opts)
|
|
| 87 |
+ vim.keymap.set("n", "<C-y>", M.pv_scroll(-1), opts)
|
|
| 88 |
+ |
|
| 89 |
+ -- scroll down |
|
| 90 |
+ vim.keymap.set("n", "<C-d>", M.pv_scroll(1), opts)
|
|
| 91 |
+ vim.keymap.set("n", "<C-e>", M.pv_scroll(1), opts)
|
|
| 92 |
+end |
|
| 93 |
+ |
|
| 94 |
+M.buf_open = function() |
|
| 95 |
+ local vim_height = vim.o.lines |
|
| 96 |
+ local vim_width = vim.o.columns |
|
| 97 |
+ |
|
| 98 |
+ local width = math.floor(vim_width * 0.8 / 2 + 3 / 2) |
|
| 99 |
+ local height = math.floor(vim_height * 0.7) |
|
| 100 |
+ local col = vim_width * 0.1 - 1 |
|
| 101 |
+ local row = vim_height * 0.15 |
|
| 102 |
+ |
|
| 103 |
+ M.cache.pv.buf = M.cache.pv.buf or api.nvim_create_buf(false, true) |
|
| 104 |
+ M.cache.pv.win = M.cache.pv.win |
|
| 105 |
+ or api.nvim_open_win(M.cache.pv.buf, true, {
|
|
| 106 |
+ relative = "editor", |
|
| 107 |
+ title = "preview", |
|
| 108 |
+ title_pos = "center", |
|
| 109 |
+ width = width, |
|
| 110 |
+ height = height, |
|
| 111 |
+ col = col + 1 + width, |
|
| 112 |
+ row = row, |
|
| 113 |
+ style = "minimal", |
|
| 114 |
+ focusable = false, |
|
| 115 |
+ border = "single", |
|
| 116 |
+ }) |
|
| 117 |
+ api.nvim_set_option_value("filetype", "c", { buf = M.cache.pv.buf })
|
|
| 118 |
+ api.nvim_set_option_value("cursorline", true, { win = M.cache.pv.win })
|
|
| 119 |
+ |
|
| 120 |
+ M.cache.sv.buf = M.cache.sv.buf or api.nvim_create_buf(false, true) |
|
| 121 |
+ M.cache.sv.win = M.cache.sv.win |
|
| 122 |
+ or api.nvim_open_win(M.cache.sv.buf, true, {
|
|
| 123 |
+ relative = "editor", |
|
| 124 |
+ title = M.ft, |
|
| 125 |
+ title_pos = "center", |
|
| 126 |
+ width = width, |
|
| 127 |
+ height = height, |
|
| 128 |
+ col = col - 1, |
|
| 129 |
+ row = row, |
|
| 130 |
+ style = "minimal", |
|
| 131 |
+ focusable = false, |
|
| 132 |
+ border = "single", |
|
| 133 |
+ }) |
|
| 134 |
+ api.nvim_set_option_value("filetype", M.ft, { buf = M.cache.sv.buf })
|
|
| 135 |
+ api.nvim_set_option_value("cursorline", true, { win = M.cache.sv.win })
|
|
| 136 |
+ |
|
| 137 |
+ M.set_keymaps() |
|
| 138 |
+end |
|
| 139 |
+ |
|
| 140 |
+M.buf_close = function() |
|
| 141 |
+ if M.cache.sv.buf ~= nil and api.nvim_buf_is_valid(M.cache.sv.buf) then |
|
| 142 |
+ api.nvim_buf_delete(M.cache.sv.buf, { force = true })
|
|
| 143 |
+ end |
|
| 144 |
+ |
|
| 145 |
+ if M.cache.sv.win ~= nil and api.nvim_win_is_valid(M.cache.sv.win) then |
|
| 146 |
+ api.nvim_win_close(M.cache.sv.win, true) |
|
| 147 |
+ end |
|
| 148 |
+ |
|
| 149 |
+ if M.cache.pv.buf ~= nil and api.nvim_buf_is_valid(M.cache.pv.buf) then |
|
| 150 |
+ api.nvim_buf_delete(M.cache.pv.buf, { force = true })
|
|
| 151 |
+ end |
|
| 152 |
+ |
|
| 153 |
+ if M.cache.pv.win ~= nil and api.nvim_win_is_valid(M.cache.pv.win) then |
|
| 154 |
+ api.nvim_win_close(M.cache.pv.win, true) |
|
| 155 |
+ end |
|
| 156 |
+ |
|
| 157 |
+ M.cache.sv.buf = nil |
|
| 158 |
+ M.cache.sv.win = nil |
|
| 159 |
+ |
|
| 160 |
+ M.cache.pv.buf = nil |
|
| 161 |
+ M.cache.pv.win = nil |
|
| 162 |
+ |
|
| 163 |
+ M.cache.pv.last_file = "" |
|
| 164 |
+end |
|
| 165 |
+ |
|
| 166 |
+M.buf_update = function() |
|
| 167 |
+ if root == nil then |
|
| 168 |
+ return |
|
| 169 |
+ end |
|
| 170 |
+ |
|
| 171 |
+ -- print(vim.inspect(root)) |
|
| 172 |
+ buf_lines = {}
|
|
| 173 |
+ M.buf_create_lines(root) |
|
| 174 |
+ -- print(vim.inspect(buf_lines)) |
|
| 175 |
+ M.buf_open() |
|
| 176 |
+ |
|
| 177 |
+ M.buf_unlock(M.cache.sv.buf) |
|
| 178 |
+ api.nvim_buf_set_lines(M.cache.sv.buf, 0, -1, false, buf_lines) |
|
| 179 |
+ if buf_last_pos ~= nil then |
|
| 180 |
+ api.nvim_win_set_cursor(M.cache.sv.win, { buf_last_pos, 0 })
|
|
| 181 |
+ buf_last_pos = nil |
|
| 182 |
+ end |
|
| 183 |
+ M.buf_lock(M.cache.sv.buf) |
|
| 184 |
+ |
|
| 185 |
+ local augroup = api.nvim_create_augroup("CscopeMaps", {})
|
|
| 186 |
+ api.nvim_create_autocmd({ "BufLeave" }, {
|
|
| 187 |
+ group = augroup, |
|
| 188 |
+ buffer = M.cache.sv.buf, |
|
| 189 |
+ callback = M.toggle_win, |
|
| 190 |
+ }) |
|
| 191 |
+ |
|
| 192 |
+ api.nvim_create_autocmd("CursorMoved", {
|
|
| 193 |
+ group = augroup, |
|
| 194 |
+ buffer = M.cache.sv.buf, |
|
| 195 |
+ callback = function() |
|
| 196 |
+ if M.opts.tree_hl then |
|
| 197 |
+ hl.refresh(M.cache.sv.buf, root) |
|
| 198 |
+ end |
|
| 199 |
+ M.preview_update() |
|
| 200 |
+ end, |
|
| 201 |
+ }) |
|
| 202 |
+end |
|
| 203 |
+ |
|
| 204 |
+--- Read data from given file |
|
| 205 |
+--- @param file string |
|
| 206 |
+--- @return table |
|
| 207 |
+M.read_lines_from_file = function(file) |
|
| 208 |
+ local lines = {}
|
|
| 209 |
+ for line in io.lines(file) do |
|
| 210 |
+ lines[#lines + 1] = line |
|
| 211 |
+ end |
|
| 212 |
+ return lines |
|
| 213 |
+end |
|
| 214 |
+ |
|
| 215 |
+--- Update preview window to show location under cursor |
|
| 216 |
+M.preview_update = function() |
|
| 217 |
+ vim.schedule(function() |
|
| 218 |
+ local _, filename, lnum = M.line_to_data(fn.getline("."))
|
|
| 219 |
+ if filename == "" then |
|
| 220 |
+ M.cache.pv.last_file = "" |
|
| 221 |
+ api.nvim_buf_set_lines(M.cache.pv.buf, 0, -1, false, {})
|
|
| 222 |
+ return |
|
| 223 |
+ end |
|
| 224 |
+ if filename ~= M.cache.pv.last_file then |
|
| 225 |
+ local lines = M.cache.pv.files[filename] or M.read_lines_from_file(filename) |
|
| 226 |
+ -- cache files for reuse |
|
| 227 |
+ M.cache.pv.files[filename] = lines |
|
| 228 |
+ M.cache.pv.last_file = filename |
|
| 229 |
+ |
|
| 230 |
+ api.nvim_buf_set_lines(M.cache.pv.buf, 0, -1, false, lines) |
|
| 231 |
+ end |
|
| 232 |
+ api.nvim_win_set_cursor(M.cache.pv.win, { lnum, 0 })
|
|
| 233 |
+ end) |
|
| 234 |
+end |
|
| 235 |
+ |
|
| 236 |
+M.line_to_data = function(line) |
|
| 237 |
+ line = vim.trim(line) |
|
| 238 |
+ local line_split = vim.split(line, "%s+") |
|
| 239 |
+ local symbol = line_split[2] |
|
| 240 |
+ local filename = "" |
|
| 241 |
+ local lnum = 0 |
|
| 242 |
+ |
|
| 243 |
+ if #line_split == 3 then |
|
| 244 |
+ local file_loc = vim.split(line_split[3], "::") |
|
| 245 |
+ filename = file_loc[1]:sub(2) |
|
| 246 |
+ lnum = tonumber(file_loc[2]:sub(1, -2), 10) |
|
| 247 |
+ end |
|
| 248 |
+ |
|
| 249 |
+ return symbol, filename, lnum |
|
| 250 |
+end |
|
| 251 |
+ |
|
| 252 |
+M.buf_create_lines = function(node) |
|
| 253 |
+ local item = "" |
|
| 254 |
+ if node.is_root then |
|
| 255 |
+ item = node.data.symbol |
|
| 256 |
+ else |
|
| 257 |
+ item = string.format( |
|
| 258 |
+ "%s%s%s [%s::%s]", |
|
| 259 |
+ string.rep(" ", node.depth * #M.dir_map[cur_dir].indicator),
|
|
| 260 |
+ M.dir_map[cur_dir].indicator, |
|
| 261 |
+ node.data.symbol, |
|
| 262 |
+ node.data.filename, |
|
| 263 |
+ node.data.lnum |
|
| 264 |
+ ) |
|
| 265 |
+ end |
|
| 266 |
+ |
|
| 267 |
+ table.insert(buf_lines, item) |
|
| 268 |
+ node.id = #buf_lines |
|
| 269 |
+ |
|
| 270 |
+ if not node.children then |
|
| 271 |
+ return |
|
| 272 |
+ end |
|
| 273 |
+ |
|
| 274 |
+ for _, c in ipairs(node.children) do |
|
| 275 |
+ M.buf_create_lines(c) |
|
| 276 |
+ end |
|
| 277 |
+end |
|
| 278 |
+ |
|
| 279 |
+M.toggle_children = function() |
|
| 280 |
+ if vim.bo.filetype ~= M.ft then |
|
| 281 |
+ return |
|
| 282 |
+ end |
|
| 283 |
+ |
|
| 284 |
+ if cur_dir == nil then |
|
| 285 |
+ return |
|
| 286 |
+ end |
|
| 287 |
+ |
|
| 288 |
+ if root == nil then |
|
| 289 |
+ return |
|
| 290 |
+ end |
|
| 291 |
+ |
|
| 292 |
+ local cur_line = fn.line(".")
|
|
| 293 |
+ |
|
| 294 |
+ if cur_line == 1 then |
|
| 295 |
+ return |
|
| 296 |
+ end |
|
| 297 |
+ |
|
| 298 |
+ local psymbol, pfilename, plnum = M.line_to_data(fn.getline("."))
|
|
| 299 |
+ local parent_id = cur_line |
|
| 300 |
+ local cs_res = M.dir_map[cur_dir].cs_func(psymbol) |
|
| 301 |
+ |
|
| 302 |
+ if not cs_res then |
|
| 303 |
+ return |
|
| 304 |
+ end |
|
| 305 |
+ |
|
| 306 |
+ -- update children list |
|
| 307 |
+ local children = {}
|
|
| 308 |
+ for _, r in ipairs(cs_res) do |
|
| 309 |
+ local node = tree.create_node(r.ctx:sub(3, -3), r.filename, r.lnum) |
|
| 310 |
+ table.insert(children, node) |
|
| 311 |
+ end |
|
| 312 |
+ |
|
| 313 |
+ root = tree.update_node(root, parent_id, children) |
|
| 314 |
+ M.buf_update() |
|
| 315 |
+end |
|
| 316 |
+ |
|
| 317 |
+M.open = function(dir, symbol) |
|
| 318 |
+ if vim.bo.filetype == M.ft then |
|
| 319 |
+ return |
|
| 320 |
+ end |
|
| 321 |
+ |
|
| 322 |
+ M.buf_close() |
|
| 323 |
+ root = nil |
|
| 324 |
+ buf_last_pos = nil |
|
| 325 |
+ |
|
| 326 |
+ if not vim.tbl_contains(vim.tbl_keys(M.dir_map), dir) then |
|
| 327 |
+ return |
|
| 328 |
+ end |
|
| 329 |
+ |
|
| 330 |
+ local cs_res = M.dir_map[dir].cs_func(symbol) |
|
| 331 |
+ |
|
| 332 |
+ if not cs_res then |
|
| 333 |
+ return |
|
| 334 |
+ end |
|
| 335 |
+ |
|
| 336 |
+ cur_dir = dir |
|
| 337 |
+ |
|
| 338 |
+ -- update children list |
|
| 339 |
+ local children = {}
|
|
| 340 |
+ for _, r in ipairs(cs_res) do |
|
| 341 |
+ local node = tree.create_node(r.ctx:sub(3, -3), r.filename, r.lnum) |
|
| 342 |
+ table.insert(children, node) |
|
| 343 |
+ end |
|
| 344 |
+ |
|
| 345 |
+ root = tree.create_node(symbol, "", 0) |
|
| 346 |
+ root.children = children |
|
| 347 |
+ root.is_root = true |
|
| 348 |
+ |
|
| 349 |
+ M.buf_update() |
|
| 350 |
+end |
|
| 351 |
+ |
|
| 352 |
+M.toggle_win = function() |
|
| 353 |
+ if vim.bo.filetype == M.ft then |
|
| 354 |
+ buf_last_pos = fn.line(".")
|
|
| 355 |
+ M.buf_close() |
|
| 356 |
+ return |
|
| 357 |
+ end |
|
| 358 |
+ M.buf_update() |
|
| 359 |
+end |
|
| 360 |
+ |
|
| 361 |
+M.enter_action = function() |
|
| 362 |
+ if vim.bo.filetype ~= M.ft then |
|
| 363 |
+ return |
|
| 364 |
+ end |
|
| 365 |
+ |
|
| 366 |
+ if fn.line(".") == 1 then
|
|
| 367 |
+ return |
|
| 368 |
+ end |
|
| 369 |
+ |
|
| 370 |
+ local _, pfilename, plnum = M.line_to_data(fn.getline("."))
|
|
| 371 |
+ M.toggle_win() |
|
| 372 |
+ utils.open_file(pfilename, plnum) |
|
| 373 |
+end |
|
| 374 |
+ |
|
| 375 |
+-- :CsStackView toggle |
|
| 376 |
+-- :CsStackView open down|up symbol |
|
| 377 |
+ |
|
| 378 |
+M.run_cmd = function(args) |
|
| 379 |
+ local cmd = args[1] |
|
| 380 |
+ |
|
| 381 |
+ if vim.startswith(cmd, "o") then |
|
| 382 |
+ local stk_dir = args[2] |
|
| 383 |
+ local symbol = args[3] or cs.default_sym("s")
|
|
| 384 |
+ if vim.startswith(stk_dir, "d") then |
|
| 385 |
+ stk_dir = "down" |
|
| 386 |
+ elseif vim.startswith(stk_dir, "u") then |
|
| 387 |
+ stk_dir = "up" |
|
| 388 |
+ end |
|
| 389 |
+ M.open(stk_dir, symbol) |
|
| 390 |
+ elseif vim.startswith(cmd, "t") then |
|
| 391 |
+ M.toggle_win() |
|
| 392 |
+ end |
|
| 393 |
+end |
|
| 394 |
+ |
|
| 395 |
+M.set_user_cmd = function() |
|
| 396 |
+ -- Create the :CsStackView user command |
|
| 397 |
+ vim.api.nvim_create_user_command("CsStackView", function(opts)
|
|
| 398 |
+ M.run_cmd(opts.fargs) |
|
| 399 |
+ end, {
|
|
| 400 |
+ nargs = "*", |
|
| 401 |
+ complete = function(_, line) |
|
| 402 |
+ local cmds = { "open", "toggle" }
|
|
| 403 |
+ local l = vim.split(line, "%s+") |
|
| 404 |
+ local n = #l - 2 |
|
| 405 |
+ |
|
| 406 |
+ if n == 0 then |
|
| 407 |
+ return vim.tbl_filter(function(val) |
|
| 408 |
+ return vim.startswith(val, l[2]) |
|
| 409 |
+ end, cmds) |
|
| 410 |
+ end |
|
| 411 |
+ |
|
| 412 |
+ if n == 1 and vim.startswith(l[2], "o") then |
|
| 413 |
+ return { "down", "up" }
|
|
| 414 |
+ end |
|
| 415 |
+ end, |
|
| 416 |
+ }) |
|
| 417 |
+end |
|
| 418 |
+ |
|
| 419 |
+M.setup = function(opts) |
|
| 420 |
+ M.opts = vim.tbl_deep_extend("force", M.opts, opts)
|
|
| 421 |
+ M.set_user_cmd() |
|
| 422 |
+end |
|
| 423 |
+ |
|
| 424 |
+return M |
| ... | ... |
@@ -0,0 +1,102 @@ |
| 1 |
+local RC = require("cscope_maps.utils.ret_codes")
|
|
| 2 |
+local M = {}
|
|
| 3 |
+ |
|
| 4 |
+--- node = {data: d, children: {n1, n2, n3, ...}}
|
|
| 5 |
+ |
|
| 6 |
+M.create_node = function(symbol, filename, lnum) |
|
| 7 |
+ local node = {}
|
|
| 8 |
+ |
|
| 9 |
+ node.children = nil |
|
| 10 |
+ node.depth = 0 |
|
| 11 |
+ node.data = {}
|
|
| 12 |
+ node.is_root = false |
|
| 13 |
+ node.id = 0 |
|
| 14 |
+ |
|
| 15 |
+ node.data.symbol = symbol |
|
| 16 |
+ node.data.filename = filename |
|
| 17 |
+ node.data.lnum = tonumber(lnum, 10) |
|
| 18 |
+ |
|
| 19 |
+ return node |
|
| 20 |
+end |
|
| 21 |
+ |
|
| 22 |
+M.compare_node = function(node, id) |
|
| 23 |
+ return (node and node.id == id) |
|
| 24 |
+end |
|
| 25 |
+ |
|
| 26 |
+M.get_node = function(root, id) |
|
| 27 |
+ if M.compare_node(root, id) then |
|
| 28 |
+ return root |
|
| 29 |
+ end |
|
| 30 |
+ |
|
| 31 |
+ local children = root.children |
|
| 32 |
+ |
|
| 33 |
+ if not children then |
|
| 34 |
+ return nil |
|
| 35 |
+ end |
|
| 36 |
+ |
|
| 37 |
+ for _, c in ipairs(children) do |
|
| 38 |
+ local node = M.get_node(c, id) |
|
| 39 |
+ if node ~= nil then |
|
| 40 |
+ return node |
|
| 41 |
+ end |
|
| 42 |
+ end |
|
| 43 |
+end |
|
| 44 |
+ |
|
| 45 |
+M.update_children_depth = function(children, depth) |
|
| 46 |
+ for _, c in ipairs(children) do |
|
| 47 |
+ c.depth = depth |
|
| 48 |
+ end |
|
| 49 |
+end |
|
| 50 |
+ |
|
| 51 |
+M.update_children = function(root, parent_id, children) |
|
| 52 |
+ local node = M.get_node(root, parent_id) |
|
| 53 |
+ |
|
| 54 |
+ if not node then |
|
| 55 |
+ return RC.NODE_NOT_FOUND |
|
| 56 |
+ end |
|
| 57 |
+ |
|
| 58 |
+ if node.children == nil then |
|
| 59 |
+ node.children = children |
|
| 60 |
+ M.update_children_depth(node.children, node.depth + 1) |
|
| 61 |
+ else |
|
| 62 |
+ node.children = nil |
|
| 63 |
+ end |
|
| 64 |
+ |
|
| 65 |
+ return RC.SUCCESS |
|
| 66 |
+end |
|
| 67 |
+ |
|
| 68 |
+M.update_node = function(root, parent_id, children) |
|
| 69 |
+ local ret = M.update_children(root, parent_id, children) |
|
| 70 |
+ |
|
| 71 |
+ if ret == RC.SUCCESS then |
|
| 72 |
+ return root |
|
| 73 |
+ end |
|
| 74 |
+ |
|
| 75 |
+ return nil |
|
| 76 |
+end |
|
| 77 |
+ |
|
| 78 |
+M.get_ancestors = function(root, node_id) |
|
| 79 |
+ if root.id == node_id then |
|
| 80 |
+ return { root.id }
|
|
| 81 |
+ end |
|
| 82 |
+ |
|
| 83 |
+ local st = { { root } }
|
|
| 84 |
+ while #st ~= 0 do |
|
| 85 |
+ local cur_path = table.remove(st, 1) |
|
| 86 |
+ local cur_node = cur_path[#cur_path] |
|
| 87 |
+ if cur_node.children then |
|
| 88 |
+ for _, c in ipairs(cur_node.children) do |
|
| 89 |
+ table.insert(cur_path, c) |
|
| 90 |
+ if c.id == node_id then |
|
| 91 |
+ return vim.tbl_map(function(x) |
|
| 92 |
+ return x.id |
|
| 93 |
+ end, cur_path) |
|
| 94 |
+ end |
|
| 95 |
+ table.insert(st, vim.deepcopy(cur_path)) |
|
| 96 |
+ table.remove(cur_path) |
|
| 97 |
+ end |
|
| 98 |
+ end |
|
| 99 |
+ end |
|
| 100 |
+end |
|
| 101 |
+ |
|
| 102 |
+return M |
| ... | ... |
@@ -0,0 +1,71 @@ |
| 1 |
+local helper = require("cscope_maps.utils.helper")
|
|
| 2 |
+local cs = require("cscope")
|
|
| 3 |
+local M = {}
|
|
| 4 |
+ |
|
| 5 |
+---@class CsMapsConfig |
|
| 6 |
+---@field disable_maps? boolean |
|
| 7 |
+---@field skip_input_prompt? boolean |
|
| 8 |
+---@field prefix? string |
|
| 9 |
+---@field cscope? CsConfig |
|
| 10 |
+M.opts = {
|
|
| 11 |
+ disable_maps = false, -- "true" disables default keymaps |
|
| 12 |
+ skip_input_prompt = false, -- "true" doesn't ask for input |
|
| 13 |
+ prefix = "<leader>c", -- prefix to trigger maps |
|
| 14 |
+ cscope = {}, -- defaults are in cscope.lua
|
|
| 15 |
+ stack_view = {}, -- defaults are in stack_view
|
|
| 16 |
+} |
|
| 17 |
+ |
|
| 18 |
+-- function to print xcscpoe.el like prompts |
|
| 19 |
+M.cscope_prompt = function(operation) |
|
| 20 |
+ if |
|
| 21 |
+ not vim.tbl_contains(vim.tbl_keys(cs.op_s_n), operation) |
|
| 22 |
+ and not vim.tbl_contains(vim.tbl_values(cs.op_s_n), operation) |
|
| 23 |
+ then |
|
| 24 |
+ return |
|
| 25 |
+ end |
|
| 26 |
+ if vim.tbl_contains(vim.tbl_values(cs.op_s_n), operation) then |
|
| 27 |
+ operation = cs.op_n_s[operation] |
|
| 28 |
+ end |
|
| 29 |
+ local default_symbol = cs.default_sym(operation) |
|
| 30 |
+ if M.opts.skip_input_prompt then |
|
| 31 |
+ vim.cmd.Cscope({ args = { "find", operation, default_symbol } })
|
|
| 32 |
+ else |
|
| 33 |
+ local prompt = string.format("%s (default: '%s'): ", helper.sym_map[operation], default_symbol)
|
|
| 34 |
+ vim.ui.input({ prompt = prompt }, function(new_symbol)
|
|
| 35 |
+ if new_symbol == nil then |
|
| 36 |
+ return |
|
| 37 |
+ end |
|
| 38 |
+ if new_symbol ~= "" then |
|
| 39 |
+ vim.cmd.Cscope({ args = { "find", operation, new_symbol } })
|
|
| 40 |
+ else |
|
| 41 |
+ vim.cmd.Cscope({ args = { "find", operation, default_symbol } })
|
|
| 42 |
+ end |
|
| 43 |
+ end) |
|
| 44 |
+ end |
|
| 45 |
+end |
|
| 46 |
+ |
|
| 47 |
+---Initialization api |
|
| 48 |
+---@param opts CsMapsConfig |
|
| 49 |
+M.setup = function(opts) |
|
| 50 |
+ opts = opts or {}
|
|
| 51 |
+ M.opts = vim.tbl_deep_extend("force", M.opts, opts)
|
|
| 52 |
+ |
|
| 53 |
+ vim.api.nvim_create_user_command("CsPrompt", function(opts)
|
|
| 54 |
+ M.cscope_prompt(opts.fargs[1]) |
|
| 55 |
+ end, {
|
|
| 56 |
+ nargs = "*", |
|
| 57 |
+ complete = function() |
|
| 58 |
+ return vim.tbl_keys(cs.op_s_n) |
|
| 59 |
+ end, |
|
| 60 |
+ }) |
|
| 61 |
+ |
|
| 62 |
+ if not M.opts.disable_maps then |
|
| 63 |
+ -- Mappings |
|
| 64 |
+ helper.default_keymaps(M.opts.prefix) |
|
| 65 |
+ end |
|
| 66 |
+ |
|
| 67 |
+ cs.setup(M.opts.cscope) |
|
| 68 |
+ require("cscope.stack_view").setup(M.opts.stack_view)
|
|
| 69 |
+end |
|
| 70 |
+ |
|
| 71 |
+return M |
| ... | ... |
@@ -0,0 +1,45 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+-- define key table for input strings |
|
| 4 |
+M.sym_map = {
|
|
| 5 |
+ s = "Find this symbol", |
|
| 6 |
+ g = "Find this global definition", |
|
| 7 |
+ c = "Find functions calling this function", |
|
| 8 |
+ t = "Find this text string", |
|
| 9 |
+ e = "Find this egrep pattern", |
|
| 10 |
+ f = "Find this file", |
|
| 11 |
+ i = "Find files #including this file", |
|
| 12 |
+ d = "Find functions called by this function", |
|
| 13 |
+ a = "Find places where this symbol is assigned a value", |
|
| 14 |
+ b = "Build database", |
|
| 15 |
+} |
|
| 16 |
+ |
|
| 17 |
+M.default_keymaps = function(prefix) |
|
| 18 |
+ local map = vim.keymap.set |
|
| 19 |
+ local sym_map = M.sym_map |
|
| 20 |
+ if MiniClue then |
|
| 21 |
+ table.insert(MiniClue.config.clues, { mode = "n", keys = prefix, desc = "+cscope" })
|
|
| 22 |
+ else |
|
| 23 |
+ local ok, wk = pcall(require, "which-key") |
|
| 24 |
+ if ok then |
|
| 25 |
+ if wk.add then |
|
| 26 |
+ wk.add({ { prefix, group = "+cscope" } })
|
|
| 27 |
+ else |
|
| 28 |
+ wk.register({ [prefix] = { name = "+cscope" } })
|
|
| 29 |
+ end |
|
| 30 |
+ end |
|
| 31 |
+ end |
|
| 32 |
+ map({ "n", "v" }, prefix .. "s", "<cmd>CsPrompt s<cr>", { desc = sym_map.s })
|
|
| 33 |
+ map({ "n", "v" }, prefix .. "g", "<cmd>CsPrompt g<cr>", { desc = sym_map.g })
|
|
| 34 |
+ map({ "n", "v" }, prefix .. "c", "<cmd>CsPrompt c<cr>", { desc = sym_map.c })
|
|
| 35 |
+ map({ "n", "v" }, prefix .. "t", "<cmd>CsPrompt t<cr>", { desc = sym_map.t })
|
|
| 36 |
+ map({ "n", "v" }, prefix .. "e", "<cmd>CsPrompt e<cr>", { desc = sym_map.e })
|
|
| 37 |
+ map({ "n", "v" }, prefix .. "f", "<cmd>CsPrompt f<cr>", { desc = sym_map.f })
|
|
| 38 |
+ map({ "n", "v" }, prefix .. "i", "<cmd>CsPrompt i<cr>", { desc = sym_map.i })
|
|
| 39 |
+ map({ "n", "v" }, prefix .. "d", "<cmd>CsPrompt d<cr>", { desc = sym_map.d })
|
|
| 40 |
+ map({ "n", "v" }, prefix .. "a", "<cmd>CsPrompt a<cr>", { desc = sym_map.a })
|
|
| 41 |
+ map({ "n", "v" }, prefix .. "b", "<cmd>Cs db build<cr>", { desc = sym_map.b })
|
|
| 42 |
+ map({ "n", "v" }, "<C-]>", "<cmd>Cstag<cr>", { noremap = true, silent = true, desc = "ctag" })
|
|
| 43 |
+end |
|
| 44 |
+ |
|
| 45 |
+return M |
| ... | ... |
@@ -0,0 +1,125 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+--- Check if given path is absolute path |
|
| 4 |
+---@param path string |
|
| 5 |
+---@return boolean |
|
| 6 |
+M.is_path_abs = function(path) |
|
| 7 |
+ return vim.startswith(path, "/") |
|
| 8 |
+end |
|
| 9 |
+ |
|
| 10 |
+--- Get relative path |
|
| 11 |
+--- if "rel_to" or "path" are not absolute paths then return "path" as it is |
|
| 12 |
+--- else return relative path of "path" wrt to "rel_to" |
|
| 13 |
+---@param rel_to string |
|
| 14 |
+---@param path string |
|
| 15 |
+---@return string |
|
| 16 |
+M.get_rel_path = function(rel_to, path) |
|
| 17 |
+ if not M.is_path_abs(rel_to) or not M.is_path_abs(path) then |
|
| 18 |
+ return path |
|
| 19 |
+ end |
|
| 20 |
+ |
|
| 21 |
+ local rel_path = "" |
|
| 22 |
+ local sp_rel_to = vim.split(vim.fs.normalize(rel_to), "/") |
|
| 23 |
+ local sp_path = vim.split(vim.fs.normalize(path), "/") |
|
| 24 |
+ local len_rel_to = #sp_rel_to + 1 |
|
| 25 |
+ local len_path = #sp_path + 1 |
|
| 26 |
+ local i = 1 |
|
| 27 |
+ |
|
| 28 |
+ -- skip till parents are same |
|
| 29 |
+ while i < len_rel_to and i < len_path do |
|
| 30 |
+ if sp_rel_to[i] == sp_path[i] then |
|
| 31 |
+ i = i + 1 |
|
| 32 |
+ else |
|
| 33 |
+ break |
|
| 34 |
+ end |
|
| 35 |
+ end |
|
| 36 |
+ |
|
| 37 |
+ -- append "../" for remaining parents |
|
| 38 |
+ rel_path = rel_path .. string.rep("../", len_rel_to - i)
|
|
| 39 |
+ |
|
| 40 |
+ -- append remaining path |
|
| 41 |
+ rel_path = rel_path .. table.concat(sp_path, "/", i) |
|
| 42 |
+ |
|
| 43 |
+ return rel_path |
|
| 44 |
+end |
|
| 45 |
+ |
|
| 46 |
+--- Convert given path to absolute path |
|
| 47 |
+---@param path string |
|
| 48 |
+---@return string |
|
| 49 |
+M.get_abs_path = function(path) |
|
| 50 |
+ if M.is_path_abs(path) then |
|
| 51 |
+ return path |
|
| 52 |
+ end |
|
| 53 |
+ |
|
| 54 |
+ local abs_path = vim.fs.joinpath(vim.fn.getcwd(), path) |
|
| 55 |
+ |
|
| 56 |
+ return vim.fs.normalize(abs_path) |
|
| 57 |
+end |
|
| 58 |
+ |
|
| 59 |
+--- Get parent of given path |
|
| 60 |
+---@param path string |
|
| 61 |
+---@return string |
|
| 62 |
+M.get_path_parent = function(path) |
|
| 63 |
+ for parent in vim.fs.parents(path) do |
|
| 64 |
+ return parent |
|
| 65 |
+ end |
|
| 66 |
+ return "" |
|
| 67 |
+end |
|
| 68 |
+ |
|
| 69 |
+---Get all dirs and files in given path |
|
| 70 |
+---@param dir string |
|
| 71 |
+---@return table |
|
| 72 |
+M.get_files_in_dir = function(dir) |
|
| 73 |
+ local fs_entries = vim.fn.readdir(dir) |
|
| 74 |
+ |
|
| 75 |
+ -- add "/" suffix for dirs and return |
|
| 76 |
+ return vim.tbl_map(function(x) |
|
| 77 |
+ local entry = x |
|
| 78 |
+ if dir ~= "." then |
|
| 79 |
+ entry = vim.fs.joinpath(dir, x) |
|
| 80 |
+ end |
|
| 81 |
+ if vim.fn.isdirectory(x) == 1 then |
|
| 82 |
+ entry = entry .. "/" |
|
| 83 |
+ end |
|
| 84 |
+ return entry |
|
| 85 |
+ end, fs_entries) |
|
| 86 |
+end |
|
| 87 |
+ |
|
| 88 |
+---Get all dirs in given path |
|
| 89 |
+---@param dir string |
|
| 90 |
+---@return table |
|
| 91 |
+M.get_dirs_in_dir = function(dir) |
|
| 92 |
+ local fs_entries = vim.fn.readdir(dir) |
|
| 93 |
+ |
|
| 94 |
+ -- add "/" suffix for dirs |
|
| 95 |
+ fs_entries = vim.tbl_map(function(x) |
|
| 96 |
+ local entry = x |
|
| 97 |
+ if dir ~= "." then |
|
| 98 |
+ entry = vim.fs.joinpath(dir, x) |
|
| 99 |
+ end |
|
| 100 |
+ return entry .. "/" |
|
| 101 |
+ end, fs_entries) |
|
| 102 |
+ |
|
| 103 |
+ -- return only dirs |
|
| 104 |
+ return vim.tbl_filter(function(x) |
|
| 105 |
+ return vim.fn.isdirectory(x) == 1 |
|
| 106 |
+ end, fs_entries) |
|
| 107 |
+end |
|
| 108 |
+ |
|
| 109 |
+M.is_path_same = function(path1, path2) |
|
| 110 |
+ return path1 and path2 and M.get_abs_path(path1) == M.get_abs_path(path2) |
|
| 111 |
+end |
|
| 112 |
+ |
|
| 113 |
+---Opens file at given line number |
|
| 114 |
+---@param fname string |
|
| 115 |
+---@param lnum number |
|
| 116 |
+M.open_file = function(fname, lnum) |
|
| 117 |
+ if M.is_path_same(vim.api.nvim_buf_get_name(0), fname) then |
|
| 118 |
+ -- change position when in same buffer |
|
| 119 |
+ vim.api.nvim_win_set_cursor(0, { lnum, 0 })
|
|
| 120 |
+ else |
|
| 121 |
+ vim.cmd(string.format("edit +%d %s", lnum, fname))
|
|
| 122 |
+ end |
|
| 123 |
+end |
|
| 124 |
+ |
|
| 125 |
+return M |
| ... | ... |
@@ -0,0 +1,46 @@ |
| 1 |
+local M = {}
|
|
| 2 |
+ |
|
| 3 |
+M.lvl = vim.log.levels |
|
| 4 |
+-- DEBUG |
|
| 5 |
+-- ERROR |
|
| 6 |
+-- INFO |
|
| 7 |
+-- TRACE |
|
| 8 |
+-- WARN |
|
| 9 |
+-- OFF |
|
| 10 |
+ |
|
| 11 |
+M.debug = function(msg, hide) |
|
| 12 |
+ if hide then |
|
| 13 |
+ return |
|
| 14 |
+ end |
|
| 15 |
+ vim.notify("cscope: " .. msg, M.lvl.DEBUG)
|
|
| 16 |
+end |
|
| 17 |
+ |
|
| 18 |
+M.error = function(msg, hide) |
|
| 19 |
+ if hide then |
|
| 20 |
+ return |
|
| 21 |
+ end |
|
| 22 |
+ vim.notify("cscope: " .. msg, M.lvl.ERROR)
|
|
| 23 |
+end |
|
| 24 |
+ |
|
| 25 |
+M.info = function(msg, hide) |
|
| 26 |
+ if hide then |
|
| 27 |
+ return |
|
| 28 |
+ end |
|
| 29 |
+ vim.notify("cscope: " .. msg, M.lvl.INFO)
|
|
| 30 |
+end |
|
| 31 |
+ |
|
| 32 |
+M.trace = function(msg, hide) |
|
| 33 |
+ if hide then |
|
| 34 |
+ return |
|
| 35 |
+ end |
|
| 36 |
+ vim.notify("cscope: " .. msg, M.lvl.trace)
|
|
| 37 |
+end |
|
| 38 |
+ |
|
| 39 |
+M.warn = function(msg, hide) |
|
| 40 |
+ if hide then |
|
| 41 |
+ return |
|
| 42 |
+ end |
|
| 43 |
+ vim.notify("cscope: " .. msg, M.lvl.WARN)
|
|
| 44 |
+end |
|
| 45 |
+ |
|
| 46 |
+return M |