[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: dhananjaylatkar\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: # Replace with a single Buy Me a Coffee username\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: dhananjaylatkar\n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**nvim version (please complete the following information):**\nnvim -v\n\n**cscope_maps config (please complete the following information):**\n```lua\nopts = {}\n```\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: dhananjaylatkar\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/workflows/panvimdoc.yml",
    "content": "name: panvimdoc\n\non:\n  push:\n    paths:\n      - \"README.md\"\n\njobs:\n  docs:\n    runs-on: ubuntu-latest\n    name: pandoc to vimdoc\n    steps:\n      - uses: actions/checkout@v2\n      - name: panvimdoc\n        uses: kdheepak/panvimdoc@main\n        with:\n          vimdoc: cscope_maps\n          pandoc: \"README.md\"\n          version: \"Neovim >= v0.10.0\"\n          treesitter: true\n          demojify: true\n      - name: Push changes\n        uses: stefanzweifel/git-auto-commit-action@v4\n        with:\n          commit_message: \"docs: auto generate\"\n          branch: ${{ github.head_ref }}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Dhananjay\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# cscope_maps.nvim\n\nFor old school code navigation :)\n\nHeavily inspired by emacs' [xcscope.el](https://github.com/dkogan/xcscope.el).\n\n**Adds `cscope` support for [Neovim](https://neovim.io/)**\n\n[cscope_maps.nvim.v2.webm](https://github.com/dhananjaylatkar/cscope_maps.nvim/assets/27724944/7ab4d902-fe6d-4914-bff6-353136c72803)\n\n## Requirements\n\n- Neovim >= 0.10\n- [cscope](https://sourceforge.net/projects/cscope/files/)\n\n## Features\n\n### Cscope\n\n- Tries to mimic vim's builtin cscope functionality.\n- Provides user command, `:Cscope` which acts same as good old `:cscope`.\n- Short commands are supported. e.g. `:Cs f g <sym>`\n- `:Cstag <sym>` does `tags` search if no results are found in `cscope`.\n- Empty `<sym>` can be used in `:Cs` and `:Cstag` to pick `<cword>` as sym.\n- Supports `cscope` and `gtags-cscope`. Use `cscope.exec` option to specify executable.\n- Keymaps can be disabled using `disable_maps` option.\n- `:CsPrompt <op>` can be used to invoke cscope prompt.\n- Display results in quickfix list, location list, **telescope**, **fzf-lua**, **mini.pick** or **snacks.nvim**.\n- Has [which-key.nvim](https://github.com/folke/which-key.nvim) and [mini.clue](https://github.com/echasnovski/mini.clue) hints.\n- See [this section](#vim-gutentags) for `vim-gutentags`.\n\n### Cscope DB\n\n- Add DB files\n  - Statically provide table of db paths in config (`db_file`)\n  - `db_file` opt can be `function`. This function __must__ return table of db paths. This can be used to conditionally (e.g. based on `cwd`) add db paths.\n  - Add DB file at runtime using `:Cs db add ...`\n    - `:Cs db add <space sepatated files>` add db file(s) to cscope search.\n    - `:Cs db rm <space sepatated files>` remove db file(s) from cscope search.\n- `:Cs db show` show all db connections.\n- `:Cs db build` (re)builds db.\n  - If `db_build_cmd.script == \"default\"` then only primary DB will be built using cscope binary.\n    - e.g. `cscope -f ${db_file} ${db_build_cmd.args}` OR `gtags-cscope ${db_build_cmd.args}`\n  - Custom script can be provided using `db_build_cmd.script`. Example script is [here](https://github.com/dhananjaylatkar/cscope_maps.nvim/pull/67)\n    - e.g. user script will be called as following -\n    - `${db_build_cmd.script} ${db_build_cmd.args} -d <db1>::<pre_path1> -d <db2>::<pre_path2> ...`\n- `vim.g.cscope_maps_statusline_indicator` can be used in statusline to indicate ongoing db build.\n- DB path grammar\n  - `db_file::db_pre_path` db_pre_path (prefix path) will be appended to cscope results.\n  - e.g. `:Cs db add ~/cscope.out::/home/code/proj2` => results from `~/cscope.out` will be prefixed with `/home/code/proj2/`\n  - `@` can be used to indicate that parent of `db_file` is `db_pre_path`.\n  - e.g. `:Cs db add ../proj2/cscope.out::@` => results from `../proj2/cscope.out` will be prefixed with `../proj2/`\n\n### Stack View\n\n- Visualize tree of caller functions and called functions.\n- `:CsStackView open down <sym>` Opens \"downward\" stack showing all the functions who call the `<sym>`.\n- `:CsStackView open up <sym>` Opens \"upward\" stack showing all the functions called by the `<sym>`.\n- In `CsStackView` window, use following keymaps\n  - `<tab>` toggle child under cursor\n  - `<cr>` open location of symbol under cursor\n  - `<C-v>` open location of symbol under cursor in vertical split\n  - `<C-s>` open location of symbol under cursor in horizontal split\n  - `q` or `<esc>` close window\n  - `<C-u>` or `<C-y>` scroll up preview\n  - `<C-d>` or `<C-e>` scroll down preview\n- `:CsStackView toggle` reopens last `CsStackView` window.\n- In `CsStackView` window, all nodes that are part of current stack are highlighted.\n\n## Installation\n\nInstall the plugin with your preferred package manager.\nFollowing example uses [lazy.nvim](https://github.com/folke/lazy.nvim)\n\n```lua\n{\n  \"dhananjaylatkar/cscope_maps.nvim\",\n  dependencies = {\n    \"nvim-telescope/telescope.nvim\", -- optional [for picker=\"telescope\"]\n    \"ibhagwan/fzf-lua\", -- optional [for picker=\"fzf-lua\"]\n    \"echasnovski/mini.pick\", -- optional [for picker=\"mini-pick\"]\n    \"folke/snacks.nvim\", -- optional [for picker=\"snacks\"]\n  },\n  opts = {\n    -- USE EMPTY FOR DEFAULT OPTIONS\n    -- DEFAULTS ARE LISTED BELOW\n  },\n}\n```\n\n## Configuration\n\nYou must run `require(\"cscope_maps\").setup()` to initialize the plugin even when using default options.\n\nNOTE: In `vimrc` use `lua require(\"cscope_maps\").setup()`\n\n_cscope_maps_ comes with following defaults:\n\n```lua\n{\n  -- maps related defaults\n  disable_maps = false, -- \"true\" disables default keymaps\n  skip_input_prompt = false, -- \"true\" doesn't ask for input\n  prefix = \"<leader>c\", -- prefix to trigger maps\n\n  -- cscope related defaults\n  cscope = {\n    -- location of cscope db file\n    db_file = \"./cscope.out\", -- DB or table of DBs\n                              -- NOTE:\n                              --   when table of DBs is provided -\n                              --   first DB is \"primary\" and others are \"secondary\"\n                              --   primary DB is used for build and project_rooter\n    -- cscope executable\n    exec = \"cscope\", -- \"cscope\" or \"gtags-cscope\"\n    -- choose your fav picker\n    picker = \"quickfix\", -- \"quickfix\", \"location\", \"telescope\", \"fzf-lua\", \"mini-pick\" or \"snacks\"\n    -- qf_window_size = 5, -- deprecated, replaced by picket_opts below, but still supported for backward compatibility\n    -- qf_window_pos = \"bottom\", -- deprecated, replaced by picket_opts below, but still supported for backward compatibility\n    picker_opts = {\n      window_size = 5, -- any positive integer\n      window_pos = \"bottom\", -- \"bottom\", \"right\", \"left\" or \"top\"\n      -- options for Snacks picker (---@class snacks.picker.Config)\n      -- pass-through options for Snacks picker\n      snacks = {}, -- snacks config\n    },\n    -- \"true\" does not open picker for single result, just JUMP\n    skip_picker_for_single_result = false, -- \"false\" or \"true\"\n    -- custom script can be used for db build\n    db_build_cmd = { script = \"default\", args = { \"-bqkv\" } },\n    -- statusline indicator, default is cscope executable\n    statusline_indicator = nil,\n    -- try to locate db_file in parent dir(s)\n    project_rooter = {\n      enable = false, -- \"true\" or \"false\"\n      -- change cwd to where db_file is located\n      change_cwd = false, -- \"true\" or \"false\"\n    },\n    -- cstag related defaults\n    tag = {\n      -- bind \":Cstag\" to \"<C-]>\"\n      keymap = true, -- \"true\" or \"false\"\n      -- order of operation to run for \":Cstag\"\n      order = { \"cs\", \"tag_picker\", \"tag\" }, -- any combination of these 3 (ops can be excluded)\n      -- cmd to use for \"tag\" op in above table\n      tag_cmd = \"tjump\",\n    },\n  },\n\n  -- stack view defaults\n  stack_view = {\n    tree_hl = true, -- toggle tree highlighting\n    size = \"medium\", -- \"medium\" or \"large\" (large is 95% of screen)\n  }\n}\n```\n\n## vim-gutentags\n\n### Config for vim-gutentags\n\n```lua\n{\n  \"ludovicchabant/vim-gutentags\",\n  init = function()\n    vim.g.gutentags_modules = {\"cscope_maps\"} -- This is required. Other config is optional\n    vim.g.gutentags_cscope_build_inverted_index_maps = 1\n    vim.g.gutentags_cache_dir = vim.fn.expand(\"~/code/.gutentags\")\n    \n    -- ⚠️ WARNING: This limits tags to .c and .h files only, add more extensions (e.g., `-e py`) or remove to avoid skipping other files.\n    vim.g.gutentags_file_list_command = \"fd -e c -e h\"\n    \n    -- vim.g.gutentags_trace = 1\n  end,\n}\n```\n\n### Alternative to vim-gutentags\n\nAlternative to gutentags is to rebuild DB using `:Cscope db build` or `<prefix>b`.\n\nYou can create autocmd for running `:Cscope db build` after saving .c and .h files.\ne.g\n\n```lua\nlocal group = vim.api.nvim_create_augroup(\"CscopeBuild\", { clear = true })\nvim.api.nvim_create_autocmd(\"BufWritePost\", {\n  pattern = { \"*.c\", \"*.h\" },\n  callback = function ()\n    vim.cmd(\"Cscope db build\")\n  end,\n  group = group,\n})\n```\n\n## Keymaps\n\n### Default Keymaps\n\n`<prefix>` can be configured using `prefix` option. Default value for prefix\nis `<leader>c`.\n\n(Try setting it to `C-c` 😉)\n\n| Keymaps           | Description                                         |\n| ----------------- | --------------------------------------------------- |\n| `<prefix>s`       | find all references to the token under cursor       |\n| `<prefix>g`       | find global definition(s) of the token under cursor |\n| `<prefix>c`       | find all calls to the function name under cursor    |\n| `<prefix>t`       | find all instances of the text under cursor         |\n| `<prefix>e`       | egrep search for the word under cursor              |\n| `<prefix>f`       | open the filename under cursor                      |\n| `<prefix>i`       | find files that include the filename under cursor   |\n| `<prefix>d`       | find functions that function under cursor calls     |\n| `<prefix>a`       | find places where this symbol is assigned a value   |\n| `<prefix>b`       | build cscope database                               |\n| <kbd>Ctrl-]</kbd> | do `:Cstag <cword>`                                 |\n\n### Custom Keymaps\n\nDisable default keymaps by setting `disable_maps = true`.\n\nThere are 2 ways to add keymaps for `Cscope`.\n\n#### Using `:CsPrompt` command\n\n`:CsPrompt <op>` is user command to invoke prompt.\nThis command provides prompt which asks for input\nbefore running `:Cscope` command.\n\ne.g. Following snippet maps <kbd>C-c C-g</kbd> to find global def of symbol\nunder cursor\n\n```lua\nvim.keymap.set({ \"n\", \"v\" }, \"<C-c><C-g>\", \"<cmd>CsPrompt g<cr>\")\n```\n\n#### Using `:Cscope` command\n\ne.g. Following snippet maps <kbd>C-c C-g</kbd> to find global def of symbol\nunder cursor\n\n```lua\nvim.keymap.set({ \"n\", \"v\" }, \"<C-c><C-g>\", \"<cmd>Cs f g<cr>\")\n```\n\n## Advanced Examples\n\n### Using Snacks picker with custom options\n\nFollowing snippet shows how to use `snacks.nvim` picker with custom layout.\n\n`cscope.picker_opts.snacks` are passed as is to `Snacks.picker()`.\nMore details about `Snacks.picker()` can be found [here](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md)\n\nThe layout used in this example is as follows -\n - Horizontal layout (input and list on the left, preview on the right)\n - Total width is 90% of screen width\n - Total height is 85% of screen height\n - The left side (input and list) takes up 60% of the width\n - The right side (preview) takes up 40% of the width\n - Rounded borders for input and preview windows\n - Preview window has word wrap enabled\n\n```lua\n\n-- only showing relevant part of the config\nOpts = {\n      cscope = {\n        picker = 'snacks', -- snacks.picker (alternative: telescope)\n        picker_opts = {\n          ---@class snacks.picker.Config\n          snacks = {\n            -- layout = 'vertical', -- Use \"vertical\" or \"horizontal\" if you want to use presets\n            ---@class snacks.picker.layout.Config\n            layout = {\n              layout = {\n                height = 0.85, -- Take up 85% of the total height\n                width = 0.9, -- Take up 90% of the total width (adjust as needed)\n                box = 'horizontal', -- Horizontal layout (input and list on the left, preview on the right)\n                { -- Left side (input and list)\n                  box = 'vertical',\n                  width = 0.6, -- List and input take up 60% of the width\n                  border = 'rounded',\n                  { win = 'input', height = 1, border = 'bottom' },\n                  { win = 'list', border = 'none' },\n                },\n                { win = 'preview', border = 'rounded', width = 0.4 }, -- Preview window takes up 40% of the width\n              },\n            },\n            ---@class snacks.picker.win.Config\n            win = {\n              preview = {\n                wo = { wrap = true },\n              },\n            },\n          }, -- snacks\n        }, -- picker_opts\n      }, -- cscope\n    }, -- Opts\n\n```\n\n"
  },
  {
    "path": "doc/cscope_maps.txt",
    "content": "*cscope_maps.txt*\n                             For Neovim >= v0.10.0    Last change: 2026 May 07\n\n==============================================================================\nTable of Contents                              *cscope_maps-table-of-contents*\n\n1. cscope_maps.nvim                             |cscope_maps-cscope_maps.nvim|\n  - Requirements                   |cscope_maps-cscope_maps.nvim-requirements|\n  - Features                           |cscope_maps-cscope_maps.nvim-features|\n  - Installation                   |cscope_maps-cscope_maps.nvim-installation|\n  - Configuration                 |cscope_maps-cscope_maps.nvim-configuration|\n  - vim-gutentags                 |cscope_maps-cscope_maps.nvim-vim-gutentags|\n  - Keymaps                             |cscope_maps-cscope_maps.nvim-keymaps|\n  - Advanced Examples         |cscope_maps-cscope_maps.nvim-advanced-examples|\n\n==============================================================================\n1. cscope_maps.nvim                             *cscope_maps-cscope_maps.nvim*\n\nFor old school code navigation :)\n\nHeavily inspired by emacs’ xcscope.el <https://github.com/dkogan/xcscope.el>.\n\n**Adds cscope support for Neovim**\n\ncscope_maps.nvim.v2.webm\n<https://github.com/dhananjaylatkar/cscope_maps.nvim/assets/27724944/7ab4d902-fe6d-4914-bff6-353136c72803>\n\n\nREQUIREMENTS                       *cscope_maps-cscope_maps.nvim-requirements*\n\n- Neovim >= 0.10\n- cscope <https://sourceforge.net/projects/cscope/files/>\n\n\nFEATURES                               *cscope_maps-cscope_maps.nvim-features*\n\n\nCSCOPE ~\n\n- Tries to mimic vim’s builtin cscope functionality.\n- Provides user command, `:Cscope` which acts same as good old `:cscope`.\n- Short commands are supported. e.g. `:Cs f g <sym>`\n- `:Cstag <sym>` does `tags` search if no results are found in `cscope`.\n- Empty `<sym>` can be used in `:Cs` and `:Cstag` to pick `<cword>` as sym.\n- Supports `cscope` and `gtags-cscope`. Use `cscope.exec` option to specify executable.\n- Keymaps can be disabled using `disable_maps` option.\n- `:CsPrompt <op>` can be used to invoke cscope prompt.\n- Display results in quickfix list, location list, **telescope**, **fzf-lua**, **mini.pick** or **snacks.nvim**.\n- Has which-key.nvim <https://github.com/folke/which-key.nvim> and mini.clue <https://github.com/echasnovski/mini.clue> hints.\n- See |cscope_maps-this-section| for `vim-gutentags`.\n\n\nCSCOPE DB ~\n\n- Add DB files\n    - Statically provide table of db paths in config (`db_file`)\n    - `db_file` opt can be `function`. This function **must** return table of db paths. This can be used to conditionally (e.g. based on `cwd`) add db paths.\n    - Add DB file at runtime using `:Cs db add ...`\n        - `:Cs db add <space sepatated files>` add db file(s) to cscope search.\n        - `:Cs db rm <space sepatated files>` remove db file(s) from cscope search.\n- `:Cs db show` show all db connections.\n- `:Cs db build` (re)builds db.\n    - If `db_build_cmd.script == \"default\"` then only primary DB will be built using cscope binary.\n        - e.g. `cscope -f ${db_file} ${db_build_cmd.args}` OR `gtags-cscope ${db_build_cmd.args}`\n    - Custom script can be provided using `db_build_cmd.script`. Example script is here <https://github.com/dhananjaylatkar/cscope_maps.nvim/pull/67>\n        - e.g. user script will be called as following -\n        - `${db_build_cmd.script} ${db_build_cmd.args} -d <db1>::<pre_path1> -d <db2>::<pre_path2> ...`\n- `vim.g.cscope_maps_statusline_indicator` can be used in statusline to indicate ongoing db build.\n- DB path grammar\n    - `db_file::db_pre_path` db_pre_path (prefix path) will be appended to cscope results.\n    - e.g. `:Cs db add ~/cscope.out::/home/code/proj2` => results from `~/cscope.out` will be prefixed with `/home/code/proj2/`\n    - `@` can be used to indicate that parent of `db_file` is `db_pre_path`.\n    - e.g. `:Cs db add ../proj2/cscope.out::@` => results from `../proj2/cscope.out` will be prefixed with `../proj2/`\n\n\nSTACK VIEW ~\n\n- Visualize tree of caller functions and called functions.\n- `:CsStackView open down <sym>` Opens \"downward\" stack showing all the functions who call the `<sym>`.\n- `:CsStackView open up <sym>` Opens \"upward\" stack showing all the functions called by the `<sym>`.\n- In `CsStackView` window, use following keymaps\n    - `<tab>` toggle child under cursor\n    - `<cr>` open location of symbol under cursor\n    - `<C-v>` open location of symbol under cursor in vertical split\n    - `<C-s>` open location of symbol under cursor in horizontal split\n    - `q` or `<esc>` close window\n    - `<C-u>` or `<C-y>` scroll up preview\n    - `<C-d>` or `<C-e>` scroll down preview\n- `:CsStackView toggle` reopens last `CsStackView` window.\n- In `CsStackView` window, all nodes that are part of current stack are highlighted.\n\n\nINSTALLATION                       *cscope_maps-cscope_maps.nvim-installation*\n\nInstall the plugin with your preferred package manager. Following example uses\nlazy.nvim <https://github.com/folke/lazy.nvim>\n\n>lua\n    {\n      \"dhananjaylatkar/cscope_maps.nvim\",\n      dependencies = {\n        \"nvim-telescope/telescope.nvim\", -- optional [for picker=\"telescope\"]\n        \"ibhagwan/fzf-lua\", -- optional [for picker=\"fzf-lua\"]\n        \"echasnovski/mini.pick\", -- optional [for picker=\"mini-pick\"]\n        \"folke/snacks.nvim\", -- optional [for picker=\"snacks\"]\n      },\n      opts = {\n        -- USE EMPTY FOR DEFAULT OPTIONS\n        -- DEFAULTS ARE LISTED BELOW\n      },\n    }\n<\n\n\nCONFIGURATION                     *cscope_maps-cscope_maps.nvim-configuration*\n\nYou must run `require(\"cscope_maps\").setup()` to initialize the plugin even\nwhen using default options.\n\nNOTE: In `vimrc` use `lua require(\"cscope_maps\").setup()`\n\n_cscope_maps_ comes with following defaults:\n\n>lua\n    {\n      -- maps related defaults\n      disable_maps = false, -- \"true\" disables default keymaps\n      skip_input_prompt = false, -- \"true\" doesn't ask for input\n      prefix = \"<leader>c\", -- prefix to trigger maps\n    \n      -- cscope related defaults\n      cscope = {\n        -- location of cscope db file\n        db_file = \"./cscope.out\", -- DB or table of DBs\n                                  -- NOTE:\n                                  --   when table of DBs is provided -\n                                  --   first DB is \"primary\" and others are \"secondary\"\n                                  --   primary DB is used for build and project_rooter\n        -- cscope executable\n        exec = \"cscope\", -- \"cscope\" or \"gtags-cscope\"\n        -- choose your fav picker\n        picker = \"quickfix\", -- \"quickfix\", \"location\", \"telescope\", \"fzf-lua\", \"mini-pick\" or \"snacks\"\n        -- qf_window_size = 5, -- deprecated, replaced by picket_opts below, but still supported for backward compatibility\n        -- qf_window_pos = \"bottom\", -- deprecated, replaced by picket_opts below, but still supported for backward compatibility\n        picker_opts = {\n          window_size = 5, -- any positive integer\n          window_pos = \"bottom\", -- \"bottom\", \"right\", \"left\" or \"top\"\n          -- options for Snacks picker (---@class snacks.picker.Config)\n          -- pass-through options for Snacks picker\n          snacks = {}, -- snacks config\n        },\n        -- \"true\" does not open picker for single result, just JUMP\n        skip_picker_for_single_result = false, -- \"false\" or \"true\"\n        -- custom script can be used for db build\n        db_build_cmd = { script = \"default\", args = { \"-bqkv\" } },\n        -- statusline indicator, default is cscope executable\n        statusline_indicator = nil,\n        -- try to locate db_file in parent dir(s)\n        project_rooter = {\n          enable = false, -- \"true\" or \"false\"\n          -- change cwd to where db_file is located\n          change_cwd = false, -- \"true\" or \"false\"\n        },\n        -- cstag related defaults\n        tag = {\n          -- bind \":Cstag\" to \"<C-]>\"\n          keymap = true, -- \"true\" or \"false\"\n          -- order of operation to run for \":Cstag\"\n          order = { \"cs\", \"tag_picker\", \"tag\" }, -- any combination of these 3 (ops can be excluded)\n          -- cmd to use for \"tag\" op in above table\n          tag_cmd = \"tjump\",\n        },\n      },\n    \n      -- stack view defaults\n      stack_view = {\n        tree_hl = true, -- toggle tree highlighting\n        size = \"medium\", -- \"medium\" or \"large\" (large is 95% of screen)\n      }\n    }\n<\n\n\nVIM-GUTENTAGS                     *cscope_maps-cscope_maps.nvim-vim-gutentags*\n\n\nCONFIG FOR VIM-GUTENTAGS ~\n\n>lua\n    {\n      \"ludovicchabant/vim-gutentags\",\n      init = function()\n        vim.g.gutentags_modules = {\"cscope_maps\"} -- This is required. Other config is optional\n        vim.g.gutentags_cscope_build_inverted_index_maps = 1\n        vim.g.gutentags_cache_dir = vim.fn.expand(\"~/code/.gutentags\")\n        \n        -- ⚠️ WARNING: This limits tags to .c and .h files only, add more extensions (e.g., `-e py`) or remove to avoid skipping other files.\n        vim.g.gutentags_file_list_command = \"fd -e c -e h\"\n        \n        -- vim.g.gutentags_trace = 1\n      end,\n    }\n<\n\n\nALTERNATIVE TO VIM-GUTENTAGS ~\n\nAlternative to gutentags is to rebuild DB using `:Cscope db build` or\n`<prefix>b`.\n\nYou can create autocmd for running `:Cscope db build` after saving .c and .h\nfiles. e.g\n\n>lua\n    local group = vim.api.nvim_create_augroup(\"CscopeBuild\", { clear = true })\n    vim.api.nvim_create_autocmd(\"BufWritePost\", {\n      pattern = { \"*.c\", \"*.h\" },\n      callback = function ()\n        vim.cmd(\"Cscope db build\")\n      end,\n      group = group,\n    })\n<\n\n\nKEYMAPS                                 *cscope_maps-cscope_maps.nvim-keymaps*\n\n\nDEFAULT KEYMAPS ~\n\n`<prefix>` can be configured using `prefix` option. Default value for prefix is\n`<leader>c`.\n\n(Try setting it to `C-c` )\n\n  -----------------------------------------------------------------------\n  Keymaps           Description\n  ----------------- -----------------------------------------------------\n  <prefix>s         find all references to the token under cursor\n\n  <prefix>g         find global definition(s) of the token under cursor\n\n  <prefix>c         find all calls to the function name under cursor\n\n  <prefix>t         find all instances of the text under cursor\n\n  <prefix>e         egrep search for the word under cursor\n\n  <prefix>f         open the filename under cursor\n\n  <prefix>i         find files that include the filename under cursor\n\n  <prefix>d         find functions that function under cursor calls\n\n  <prefix>a         find places where this symbol is assigned a value\n\n  <prefix>b         build cscope database\n\n  Ctrl-]            do :Cstag <cword>\n  -----------------------------------------------------------------------\n\nCUSTOM KEYMAPS ~\n\nDisable default keymaps by setting `disable_maps = true`.\n\nThere are 2 ways to add keymaps for `Cscope`.\n\n\nUSING :CSPROMPT COMMAND\n\n`:CsPrompt <op>` is user command to invoke prompt. This command provides prompt\nwhich asks for input before running `:Cscope` command.\n\ne.g. Following snippet maps C-c C-g to find global def of symbol under cursor\n\n>lua\n    vim.keymap.set({ \"n\", \"v\" }, \"<C-c><C-g>\", \"<cmd>CsPrompt g<cr>\")\n<\n\n\nUSING :CSCOPE COMMAND\n\ne.g. Following snippet maps C-c C-g to find global def of symbol under cursor\n\n>lua\n    vim.keymap.set({ \"n\", \"v\" }, \"<C-c><C-g>\", \"<cmd>Cs f g<cr>\")\n<\n\n\nADVANCED EXAMPLES             *cscope_maps-cscope_maps.nvim-advanced-examples*\n\n\nUSING SNACKS PICKER WITH CUSTOM OPTIONS ~\n\nFollowing snippet shows how to use `snacks.nvim` picker with custom layout.\n\n`cscope.picker_opts.snacks` are passed as is to `Snacks.picker()`. More details\nabout `Snacks.picker()` can be found here\n<https://github.com/folke/snacks.nvim/blob/main/docs/picker.md>\n\nThe layout used in this example is as follows - - Horizontal layout (input and\nlist on the left, preview on the right) - Total width is 90% of screen width -\nTotal height is 85% of screen height - The left side (input and list) takes up\n60% of the width - The right side (preview) takes up 40% of the width - Rounded\nborders for input and preview windows - Preview window has word wrap enabled\n\n>lua\n    \n    -- only showing relevant part of the config\n    Opts = {\n          cscope = {\n            picker = 'snacks', -- snacks.picker (alternative: telescope)\n            picker_opts = {\n              ---@class snacks.picker.Config\n              snacks = {\n                -- layout = 'vertical', -- Use \"vertical\" or \"horizontal\" if you want to use presets\n                ---@class snacks.picker.layout.Config\n                layout = {\n                  layout = {\n                    height = 0.85, -- Take up 85% of the total height\n                    width = 0.9, -- Take up 90% of the total width (adjust as needed)\n                    box = 'horizontal', -- Horizontal layout (input and list on the left, preview on the right)\n                    { -- Left side (input and list)\n                      box = 'vertical',\n                      width = 0.6, -- List and input take up 60% of the width\n                      border = 'rounded',\n                      { win = 'input', height = 1, border = 'bottom' },\n                      { win = 'list', border = 'none' },\n                    },\n                    { win = 'preview', border = 'rounded', width = 0.4 }, -- Preview window takes up 40% of the width\n                  },\n                },\n                ---@class snacks.picker.win.Config\n                win = {\n                  preview = {\n                    wo = { wrap = true },\n                  },\n                },\n              }, -- snacks\n            }, -- picker_opts\n          }, -- cscope\n        }, -- Opts\n<\n\nGenerated by panvimdoc <https://github.com/kdheepak/panvimdoc>\n\nvim:tw=78:ts=8:noet:ft=help:norl:\n"
  },
  {
    "path": "lua/cscope/db.lua",
    "content": "local utils = require(\"cscope_maps.utils\")\nlocal log = require(\"cscope_maps.utils.log\")\n\nlocal M = {}\n\n--- conns = { {file = db_file, pre_path = db_pre_path}, ... }\nM.conns = {}\nM.global_conn = nil\n\nM.sep = \"::\"\n\nM.reset = function()\n\tM.conns = {}\n\tM.global_conn = nil\nend\n\n---Get all db connections\n---If global connection is declared then use that\n---@return table\nM.all_conns = function()\n\tM.update_global_conn()\n\treturn M.global_conn or M.conns\nend\n\n---Get primary db connection\n---If global connection is declared then use that\n---@return table\nM.primary_conn = function()\n\tM.update_global_conn()\n\tif M.global_conn then\n\t\treturn M.global_conn[1]\n\tend\n\treturn M.conns[1]\nend\n\n---Update primary db connection\n---@param file string\n---@param pre_path string\nM.update_primary_conn = function(file, pre_path)\n\tM.conns[1].file = vim.fs.normalize(file)\n\tM.conns[1].pre_path = vim.fs.normalize(pre_path)\nend\n\n---Update global db connection\nM.update_global_conn = function()\n\tif vim.g.cscope_maps_db_file then\n\t\tlocal file, pre_path = M.sp_file_pre_path(vim.g.cscope_maps_db_file)\n\t\tM.global_conn = { { file = file, pre_path = pre_path } }\n\telse\n\t\tM.global_conn = nil\n\tend\nend\n\n---Split input of \":Cs db add\" into file and pre_path and normalize them\n---@param path string\n---@return string\n---@return string|nil\nM.sp_file_pre_path = function(path)\n\tlocal sp = vim.split(path, M.sep)\n\tlocal file = sp[1]\n\tlocal pre_path = sp[2]\n\n\tfile = vim.fs.normalize(file)\n\n\t-- use cwd if pre_path is not provided\n\tif pre_path == nil or pre_path == \"\" then\n\t\tpre_path = vim.fn.getcwd()\n\tend\n\n\t-- use parent as pre_path if its \"@\"\n\tif pre_path == \"@\" then\n\t\tpre_path = utils.get_path_parent(file)\n\tend\n\n\t-- normalize it\n\tpre_path = vim.fs.normalize(pre_path)\n\n\treturn file, pre_path\nend\n\n---Find index of db in all connections\n---@param file string\n---@param pre_path string|nil\n---@return integer\nM.find = function(file, pre_path)\n\tfor i, cons in ipairs(M.conns) do\n\t\tif\n\t\t\tutils.is_path_same(cons.file, file)\n\t\t\tand (utils.is_path_same(cons.pre_path, pre_path) or cons.pre_path == nil)\n\t\tthen\n\t\t\treturn i\n\t\tend\n\tend\n\n\treturn -1\nend\n\n---Add db in db connections\n---@param path string\nM.add = function(path)\n\tlocal file, pre_path = M.sp_file_pre_path(path)\n\tif M.find(file, pre_path) == -1 then\n\t\ttable.insert(M.conns, { file = file, pre_path = pre_path })\n\tend\nend\n\n---Remove db from db connections\n---Primary db connection will not be removed\n---@param path string\nM.remove = function(path)\n\tlocal file, pre_path = M.sp_file_pre_path(path)\n\tlocal loc = M.find(file, pre_path)\n\t-- do not remove first entry\n\tif loc > 1 then\n\t\ttable.remove(M.conns, loc)\n\tend\nend\n\n---Update DB connections\n---@param op string Operation (add/remove)\n---@param files table list of files\nM.update = function(op, files)\n\tif op == \"a\" then\n\t\tfor _, f in ipairs(files) do\n\t\t\tM.add(f)\n\t\tend\n\telseif op == \"r\" then\n\t\tfor _, f in ipairs(files) do\n\t\t\tM.remove(f)\n\t\tend\n\tend\nend\n\nM.print_conns = function()\n\tif not M.conns then\n\t\tlog.warn(\"No connections\")\n\tend\n\n\tfor i, conn in ipairs(M.conns) do\n\t\tlocal file = utils.get_rel_path(vim.fn.getcwd(), conn.file)\n\t\tlocal pre_path = utils.get_rel_path(vim.fn.getcwd(), conn.pre_path)\n\t\tif not pre_path or pre_path == \"\" then\n\t\t\tlog.warn(string.format(\"%d) db=%s\", i, file))\n\t\telse\n\t\t\tlog.warn(string.format(\"%d) db=%s pre_path=%s\", i, file, pre_path))\n\t\tend\n\tend\nend\n\n---Create command to build DB\n---1. If script is default then use opt.exec\n---2. If custom script is provided then use that with \"-d <db>::<pre_path>\" args\nM.get_build_cmd = function(opts)\n\tlocal cmd = {}\n\n\tif opts.db_build_cmd.script == \"default\" then\n\t\tif opts.exec == \"cscope\" then\n\t\t\tcmd = { \"cscope\", \"-f\", M.primary_conn().file }\n\t\telse -- \"gtags-cscope\"\n\t\t\tcmd = { \"gtags-cscope\" }\n\t\tend\n\n\t\tvim.list_extend(cmd, opts.db_build_cmd.args)\n\t\treturn cmd\n\tend\n\n\t-- custom script\n\tcmd = { opts.db_build_cmd.script }\n\tvim.list_extend(cmd, opts.db_build_cmd.args)\n\n\tfor _, conn in ipairs(M.conns) do\n\t\tvim.list_extend(cmd, { \"-d\", string.format(\"%s::%s\", conn.file, conn.pre_path) })\n\tend\n\n\treturn cmd\nend\n\nlocal on_exit = function(obj)\n\tvim.g.cscope_maps_statusline_indicator = nil\n\tif obj.code == 0 then\n\t\t-- print(\"cscope: [build] out: \" .. obj.stdout)\n\t\tprint(\"cscope: database built successfully\")\n\telse\n\t\t-- print(\"cscope: [build] out: \" .. obj.stderr)\n\t\tprint(\"cscope: database build failed\")\n\tend\nend\n\nM.build = function(opts)\n\tif vim.g.cscope_maps_statusline_indicator then\n\t\tlog.warn(\"db build is already in progress\")\n\t\treturn\n\tend\n\n\tlocal cmd = M.get_build_cmd(opts)\n\n\tvim.g.cscope_maps_statusline_indicator = opts.statusline_indicator or opts.exec\n\tvim.system(cmd, { text = true }, on_exit)\nend\n\n---Parse db_file and call db.add()\n---@param opts table\nM.init = function(opts)\n\tif type(opts.db_file) == \"string\" then\n\t\tM.add(opts.db_file)\n\t\treturn\n\tend\n\n\tif type(opts.db_file) == \"function\" then\n\t\topts.db_file = opts.db_file()\n\tend\n\n\tif type(opts.db_file) == \"table\" then\n\t\tfor _, f in ipairs(opts.db_file) do\n\t\t\tM.add(f)\n\t\tend\n\tend\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/init.lua",
    "content": "local RC = require(\"cscope_maps.utils.ret_codes\")\nlocal log = require(\"cscope_maps.utils.log\")\nlocal helper = require(\"cscope_maps.utils.helper\")\nlocal utils = require(\"cscope_maps.utils\")\nlocal db = require(\"cscope.db\")\n\nlocal M = {}\n\n---@class CsProjectRooterConfig\n---@field enable? boolean\n---@field change_cwd? boolean\n\n---@class CsPicketOpts\n---@field window_size? integer\n---@field window_pos? string\n\n---@class CsConfig\n---@field db_file? string|[string]\n---@field exec? string\n---@field picker? string\n---@field skip_picker_for_single_result? boolean\n---@field db_build_cmd? table\n---@field statusline_indicator? string|nil\n---@field project_rooter? CsProjectRooterConfig\nM.opts = {\n\tdb_file = \"./cscope.out\",\n\texec = \"cscope\",\n\tpicker = \"quickfix\",\n\tpicker_opts = {\n\t\twindow_size = 5,\n\t\twindow_pos = \"bottom\",\n\t},\n\tskip_picker_for_single_result = false,\n\tdb_build_cmd = { script = \"default\", args = { \"-bqkv\" } },\n\tstatusline_indicator = nil,\n\tproject_rooter = {\n\t\tenable = false,\n\t\tchange_cwd = false,\n\t},\n\ttag = {\n\t\tkeymap = true,\n\t\torder = { \"cs\", \"tag_picker\", \"tag\" },\n\t\ttag_cmd = \"tjump\",\n\t},\n}\n\nM.user_opts = {}\n\n-- operation symbol to number map\nM.op_s_n = {\n\ts = \"0\",\n\tg = \"1\",\n\td = \"2\",\n\tc = \"3\",\n\tt = \"4\",\n\te = \"6\",\n\tf = \"7\",\n\ti = \"8\",\n\ta = \"9\",\n}\n\n-- operation number to symbol map\nM.op_n_s = {}\nfor k, v in pairs(M.op_s_n) do\n\tM.op_n_s[v] = k\nend\n\nlocal cscope_picker = nil\nlocal gtags_db = \"GTAGS\"\n\nM.help = function()\n\tprint([[\nCscope commands:\nfind : Query for a pattern            (Usage: find a|c|d|e|f|g|i|s|t name)\n       a: Find assignments to this symbol\n       c: Find functions calling this function\n       d: Find functions called by this function\n       e: Find this egrep pattern\n       f: Find this file\n       g: Find this definition\n       i: Find files #including this file\n       s: Find this C symbol\n       t: Find this text string\n\ndb   : DB related queries             (Usage: db build|add <files>|rm <files>|show)\n       build : Build cscope database\n       add   : Add db file(s)\n       rm    : Remove db file(s)\n       show  : Show current db file(s)\n\nreload: Reload plugin config\nhelp : Show this message              (Usage: help)\n]])\nend\n\nM.push_tagstack = function()\n\tlocal from = { vim.fn.bufnr(\"%\"), vim.fn.line(\".\"), vim.fn.col(\".\"), 0 }\n\tlocal items = { { tagname = vim.fn.expand(\"<cword>\"), from = from } }\n\tlocal ts = vim.fn.gettagstack()\n\tlocal ts_last_item = ts.items[ts.curidx - 1]\n\n\tif\n\t\tts_last_item\n\t\tand ts_last_item.tagname == items[1].tagname\n\t\tand ts_last_item.from[1] == items[1].from[1]\n\t\tand ts_last_item.from[2] == items[1].from[2]\n\tthen\n\t\t-- Don't push duplicates on tagstack\n\t\treturn\n\tend\n\n\tvim.fn.settagstack(vim.fn.win_getid(), { items = items }, \"t\")\nend\n\nM.parse_line = function(line, db_pre_path)\n\tlocal t = {}\n\n\t-- Populate t with filename, context and linenumber\n\tlocal sp = vim.split(line, \"%s+\")\n\n\tt.filename = sp[1]\n\t-- prepend db_pre_path when both of following are true -\n\t-- 1. relative path is used for filename\n\t-- 2. db_pre_path is not present in filename\n\tif db_pre_path and not utils.is_path_abs(t.filename) and not vim.startswith(t.filename, db_pre_path) then\n\t\tt.filename = vim.fs.joinpath(db_pre_path, t.filename)\n\tend\n\tt.filename = utils.get_rel_path(vim.fn.getcwd(), t.filename)\n\n\tt.ctx = sp[2]\n\tt.lnum = sp[3]\n\tlocal sz = #sp[1] + #sp[2] + #sp[3] + 3\n\n\t-- Populate t[\"text\"] with search result\n\tt.text = string.sub(line, sz, -1)\n\n\t-- Enclose context with << >>\n\tif string.sub(t.ctx, 1, 1) == \"<\" then\n\t\tt.ctx = \"<\" .. t.ctx .. \">\"\n\telse\n\t\tt.ctx = \"<<\" .. t.ctx .. \">>\"\n\tend\n\n\t-- Add context to text\n\tt.text = t.ctx .. t.text\n\n\treturn t\nend\n\nM.parse_output = function(cs_out, db_pre_path)\n\t-- Parse cscope output to be populated in QuickFix List\n\t-- setqflist() takes list of dicts to be shown in QF List. See :h setqflist()\n\n\tlocal res = {}\n\n\tfor line in string.gmatch(cs_out, \"([^\\n]+)\") do\n\t\tlocal parsed_line = M.parse_line(line, db_pre_path)\n\t\ttable.insert(res, parsed_line)\n\tend\n\n\treturn res\nend\n\nM.open_picker = function(op_s, symbol, parsed_output)\n\tlocal title = \"cscope find \" .. op_s .. \" \" .. symbol\n\n\t-- Push current symbol on tagstack\n\tM.push_tagstack()\n\n\t-- update jumplist\n\tvim.cmd([[normal! m']])\n\n\tif M.opts.skip_picker_for_single_result and #parsed_output == 1 then\n\t\tutils.open_file(parsed_output[1][\"filename\"], tonumber(parsed_output[1][\"lnum\"], 10))\n\t\treturn RC.SUCCESS\n\tend\n\n\tlocal picker_opts = {}\n\tpicker_opts.cscope = {}\n\tpicker_opts.cscope.parsed_output = parsed_output\n\tpicker_opts.cscope.prompt_title = title\n\tpicker_opts.cscope.picker_opts = M.opts.picker_opts\n\t-- backward compatibility for qf_window_pos and qf_window_size\n\tpicker_opts.cscope.qf_window_size = M.opts.qf_window_size\n\tpicker_opts.cscope.qf_window_pos = M.opts.qf_window_pos\n\n\tcscope_picker.run(picker_opts)\n\treturn RC.SUCCESS\nend\n\nM.get_result = function(op_n, op_s, symbol, hide_log)\n\t-- Executes cscope search and return parsed output\n\n\tlocal db_conns = db.all_conns()\n\tlocal res = {}\n\n\tlocal exec_and_update_res = function(_db_con, _cmd_args)\n\t\tif vim.loop.fs_stat(_db_con.file) == nil then\n\t\t\treturn\n\t\tend\n\t\tlocal cmd = {\n\t\t\tM.opts.exec,\n\t\t\t\"-dL\",\n\t\t\t\"-\" .. op_n,\n\t\t\tsymbol,\n\t\t\t\"-f\",\n\t\t\tutils.get_rel_path(vim.fn.getcwd(), _db_con.file),\n\t\t\t\"-P\",\n\t\t\tutils.get_rel_path(vim.fn.getcwd(), _db_con.pre_path),\n\t\t}\n\t\tcmd = vim.list_extend(cmd, _cmd_args)\n\t\tlocal proc = vim.system(cmd, { text = true }):wait()\n\t\tif proc.code ~= 0 then\n\t\t\treturn\n\t\tend\n\n\t\tres = vim.list_extend(res, M.parse_output(proc.stdout, _db_con.pre_path))\n\tend\n\n\tif M.opts.exec == \"cscope\" then\n\t\tfor _, db_con in ipairs(db_conns) do\n\t\t\texec_and_update_res(db_con, {})\n\t\tend\n\telseif M.opts.exec == \"gtags-cscope\" then\n\t\tif op_s == \"d\" then\n\t\t\tlog.warn(\"'d' operation is not available for \" .. M.opts.exec, hide_log)\n\t\t\treturn RC.INVALID_OP, nil\n\t\tend\n\t\texec_and_update_res(db.primary_conn(), { \"-a\" })\n\telse\n\t\tlog.warn(\"'\" .. M.opts.exec .. \"' executable is not supported\", hide_log)\n\t\treturn RC.INVALID_EXEC, nil\n\tend\n\n\tif vim.tbl_isempty(res) then\n\t\tlog.warn(\"no results for 'cscope find \" .. op_s .. \" \" .. symbol .. \"'\", hide_log)\n\t\treturn RC.NO_RESULTS, nil\n\tend\n\n\treturn RC.SUCCESS, res\nend\n\nM.find = function(op, symbol)\n\tif symbol == nil then\n\t\treturn RC.INVALID_SYMBOL\n\tend\n\n\tlocal ok, res\n\top = tostring(op)\n\n\tif #op ~= 1 then\n\t\tlog.warn(\"operation '\" .. op .. \"' is invalid\")\n\t\treturn RC.INVALID_OP\n\tend\n\n\tif string.find(\"012346789\", op) then\n\t\tok, res = M.get_result(op, M.op_n_s[op], symbol)\n\telseif string.find(\"sgdctefia\", op) then\n\t\tok, res = M.get_result(M.op_s_n[op], op, symbol)\n\telse\n\t\tlog.warn(\"operation '\" .. op .. \"' is invalid\")\n\t\treturn RC.INVALID_OP\n\tend\n\n\tif ok == RC.SUCCESS then\n\t\treturn M.open_picker(op, symbol, res)\n\tend\n\n\treturn RC.NO_RESULTS\nend\n\n-- get lnum and text of tag\n-- returns list of all occurrences of tag\nM.tag_get_info = function(tag)\n\tlocal res = {}\n\tlocal bin = \"\"\n\tif vim.fn.executable(\"rg\") == 1 then\n\t\tbin = \"rg\"\n\telseif vim.fn.executable(\"grep\") == 1 then\n\t\tbin = \"grep\"\n\tend\n\n\tif bin == \"\" then\n\t\treturn {}\n\tend\n\n\t-- remove leading and trailing \"/\" because \"cmd\" is Ex cmd for vim\n\tlocal filter = string.gsub(tag.cmd, \"^/\", \"\")\n\tfilter = string.gsub(filter, \"/$\", \"\")\n\n\t-- escape shell chars\n\tfilter = filter:gsub(\"[\\\\~?*|{\\\\[()%-%.%+]\", function(x)\n\t\treturn \"\\\\\" .. x\n\tend)\n\n\tlocal proc = vim.system({ bin, \"-n\", filter, tag.filename }, { text = true }):wait()\n\n\tif proc.code ~= 0 then\n\t\treturn {}\n\tend\n\n\tlocal lines = vim.split(proc.stdout, \"\\n\")\n\tfor _, line in ipairs(lines) do\n\t\tlocal sp = vim.split(line, \":\")\n\t\tif #sp == 2 then\n\t\t\ttable.insert(res, { lnum = sp[1], text = sp[2] })\n\t\tend\n\tend\n\n\treturn res\nend\n\n-- parse taglist for give sym\n-- returns same format as cscope parse_output\nM.get_tags = function(sym)\n\t-- don't use custom picker for help tags\n\tif vim.bo.filetype == \"help\" then\n\t\treturn {}\n\tend\n\n\tlocal tags = vim.fn.taglist(string.format(\"^%s$\", sym))\n\tlocal res = {}\n\n\tfor _, tag in ipairs(tags) do\n\t\tlocal info = M.tag_get_info(tag)\n\t\tfor _, info_item in ipairs(info) do\n\t\t\tlocal item = {}\n\t\t\titem.filename = tag.filename\n\t\t\titem.ctx = string.format(\"<<%s>>\", tag.name)\n\t\t\titem.lnum = info_item.lnum\n\t\t\titem.text = string.format(\"%s %s\", item.ctx, info_item.text)\n\t\t\ttable.insert(res, item)\n\t\tend\n\tend\n\treturn res\nend\n\nM.cstag = function(symbol)\n\tlocal op = \"g\"\n\t-- if symbol is not provided use cword\n\tsymbol = symbol or M.default_sym(op)\n\n\tfor _, tag in ipairs(M.opts.tag.order) do\n\t\tif tag == \"cs\" then\n\t\t\tlocal ok, res = M.get_result(M.op_s_n[op], op, symbol, true)\n\t\t\tif ok == RC.SUCCESS then\n\t\t\t\tM.open_picker(op, symbol, res)\n\t\t\t\treturn\n\t\t\tend\n\t\telseif tag == \"tag_picker\" then\n\t\t\tlocal res = M.get_tags(symbol)\n\t\t\tif #res ~= 0 then\n\t\t\t\tM.open_picker(\"tags\", symbol, res)\n\t\t\t\treturn\n\t\t\tend\n\t\telseif tag == \"tag\" then\n\t\t\tlocal ok, msg = pcall(vim.cmd, { cmd = M.opts.tag.tag_cmd, args = { symbol } })\n\t\t\tif ok then\n\t\t\t\treturn\n\t\t\tend\n\t\t\tlog.warn(msg)\n\t\tend\n\tend\nend\n\nM.default_sym = function(op)\n\tlocal sym = \"\"\n\tif vim.fn.mode() == \"v\" then\n\t\tlocal saved_reg = vim.fn.getreg(\"v\")\n\t\tvim.cmd([[noautocmd sil norm! \"vy]])\n\t\tsym = vim.fn.getreg(\"v\")\n\t\tvim.fn.setreg(\"v\", saved_reg)\n\telse\n\t\tlocal arg = \"<cword>\"\n\t\tif vim.tbl_contains({ \"f\", \"i\", \"7\", \"8\" }, op) then\n\t\t\targ = \"<cfile>\"\n\t\tend\n\t\tsym = vim.fn.expand(arg)\n\tend\n\treturn sym\nend\n\nM.run = function(args)\n\t-- Parse top level input and call appropriate functions\n\tlocal args_num = #args\n\tif args_num < 1 then\n\t\t-- invalid command\n\t\tlog.warn(\"invalid cmd. see :Cscope help\")\n\t\treturn\n\tend\n\n\tlocal cmd = args[1]\n\n\tif cmd:sub(1, 1) == \"f\" then\n\t\tlocal op = args[2]\n\t\t-- if symbol is not provided use cword or cfile\n\t\tlocal symbol = args[3] or M.default_sym(op)\n\n\t\t-- collect all args\n\t\tfor i = 4, args_num do\n\t\t\tsymbol = symbol .. \" \" .. args[i]\n\t\tend\n\n\t\tM.find(op, symbol)\n\telseif cmd:sub(1, 1) == \"b\" then\n\t\tlog.warn(\"':Cs build' is deprecated. Use ':Cs db build'\")\n\telseif cmd:sub(1, 1) == \"d\" then\n\t\tif args_num < 2 then\n\t\t\tlog.warn(\"db command expects atleast 2 arguments\")\n\t\t\treturn\n\t\tend\n\n\t\tlocal op = args[2]:sub(1, 1)\n\t\tif op == \"b\" then\n\t\t\tdb.build(M.opts)\n\t\telseif op == \"a\" or op == \"r\" then\n\t\t\t-- collect all args\n\t\t\tlocal files = {}\n\t\t\tfor i = 3, args_num do\n\t\t\t\ttable.insert(files, args[i])\n\t\t\tend\n\n\t\t\tdb.update(op, files)\n\t\telseif op == \"s\" then\n\t\t\tdb.print_conns()\n\t\telse\n\t\t\tlog.warn(\"invalid operation\")\n\t\tend\n\telseif cmd:sub(1, 1) == \"h\" then\n\t\tM.help()\n\telseif cmd:sub(1, 1) == \"r\" then\n\t\tM.reload()\n\telse\n\t\tlog.warn(\"command '\" .. cmd .. \"' is invalid\")\n\tend\nend\n\nM.cmd_cmp = function(_, line)\n\tlocal cmds = { \"find\", \"db\", \"reload\", \"help\" }\n\tlocal l = vim.split(line, \"%s+\")\n\tlocal n = #l - 2\n\n\tif n == 0 then\n\t\treturn vim.tbl_filter(function(val)\n\t\t\treturn vim.startswith(val, l[2])\n\t\tend, cmds)\n\tend\n\n\tlocal short_cmd = l[2]:sub(1, 1)\n\tif n == 1 then\n\t\tif short_cmd == \"f\" then\n\t\t\treturn vim.tbl_keys(M.op_s_n)\n\t\tend\n\n\t\tif short_cmd == \"d\" then\n\t\t\tcmds = { \"build\", \"add\", \"rm\", \"show\" }\n\t\t\treturn vim.tbl_filter(function(val)\n\t\t\t\treturn vim.startswith(val, l[3])\n\t\t\tend, cmds)\n\t\tend\n\tend\n\n\tlocal short_cmd2 = l[3]:sub(1, 1)\n\tlocal cur_arg = l[#l]\n\n\tif n == 2 and short_cmd == \"f\" then\n\t\t-- complete default_sym for \"find\" cmd\n\t\tlocal default_sym = M.default_sym(short_cmd2)\n\t\tif cur_arg == \"\" or vim.startswith(default_sym, cur_arg) then\n\t\t\treturn { default_sym }\n\t\tend\n\tend\n\n\tif n >= 2 and short_cmd == \"d\" and short_cmd2 == \"a\" then\n\t\tlocal sp = vim.split(cur_arg, db.sep)\n\t\tlocal parent, fs_entries\n\n\t\tif sp[2] ~= nil then\n\t\t\t-- complete pre path.\n\t\t\t-- this will show \"@\" and dirs only\n\t\t\tparent = utils.get_path_parent(sp[2])\n\t\t\tfs_entries = utils.get_dirs_in_dir(parent)\n\t\t\ttable.insert(fs_entries, 1, \"@\")\n\n\t\t\tfs_entries = vim.tbl_map(function(x)\n\t\t\t\treturn sp[1] .. db.sep .. x\n\t\t\tend, fs_entries)\n\t\telse\n\t\t\t-- complete db path\n\t\t\t-- this will show all files\n\t\t\tparent = utils.get_path_parent(cur_arg)\n\t\t\tfs_entries = utils.get_files_in_dir(parent)\n\t\tend\n\n\t\treturn vim.tbl_filter(function(val)\n\t\t\treturn vim.startswith(val, cur_arg)\n\t\tend, fs_entries)\n\tend\n\n\tif n >= 2 and short_cmd == \"d\" and short_cmd2 == \"r\" then\n\t\t-- complete db_conns except primary_conn\n\t\tlocal db_conns = db.all_conns()\n\t\tlocal entries = {}\n\t\tif not db_conns then\n\t\t\treturn entries\n\t\tend\n\n\t\tfor i, conn in ipairs(db_conns) do\n\t\t\tif i > 1 then\n\t\t\t\ttable.insert(entries, string.format(\"%s%s%s\", conn.file, db.sep, conn.pre_path))\n\t\t\tend\n\t\tend\n\n\t\treturn vim.tbl_filter(function(val)\n\t\t\treturn vim.startswith(val, cur_arg)\n\t\tend, entries)\n\tend\nend\n\nM.user_command = function()\n\t-- Create the :Cscope user command\n\tvim.api.nvim_create_user_command(\"Cscope\", function(opts)\n\t\tM.run(opts.fargs)\n\tend, {\n\t\tnargs = \"*\",\n\t\tcomplete = M.cmd_cmp,\n\t})\n\n\t-- Create the :Cs user command\n\tvim.api.nvim_create_user_command(\"Cs\", function(opts)\n\t\tM.run(opts.fargs)\n\tend, {\n\t\tnargs = \"*\",\n\t\tcomplete = M.cmd_cmp,\n\t})\n\n\t-- Create the :Cstag user command\n\tvim.api.nvim_create_user_command(\"Cstag\", function(opts)\n\t\tM.cstag(unpack(opts.fargs))\n\tend, {\n\t\tnargs = \"*\",\n\t})\n\t-- Bind :Cstag to \"<C-]>\"\n\tif M.opts.tag.keymap == true then\n\t\tvim.keymap.set({ \"n\", \"v\" }, \"<C-]>\", \"<cmd>Cstag<cr>\", { desc = \"cstag\" })\n\tend\nend\n\nM.reload = function()\n\tdb.reset()\n\tM.setup(M.user_opts)\nend\n\nM.root = function(source, marker)\n\tif vim.fn.filereadable(vim.fs.joinpath(source, marker)) == 1 then\n\t\treturn source\n\tend\n\n\tfor dir in vim.fs.parents(source) do\n\t\tif vim.fn.filereadable(vim.fs.joinpath(dir, marker)) == 1 then\n\t\t\treturn dir\n\t\tend\n\tend\n\treturn nil\nend\n\n---Initialization api\n---@param opts CsConfig\nM.setup = function(opts)\n\t-- save original opts for reload operation\n\tM.user_opts = vim.deepcopy(opts)\n\tM.opts = vim.tbl_deep_extend(\"force\", M.opts, opts)\n\t-- This variable can be used by other plugins to change db_file\n\t-- e.g. vim-gutentags can use it for when\n\t--\tvim.g.gutentags_cache_dir is enabled.\n\tvim.g.cscope_maps_db_file = nil\n\tvim.g.cscope_maps_statusline_indicator = nil\n\n\tif M.opts.exec == \"gtags-cscope\" then\n\t\tM.opts.db_file = gtags_db\n\tend\n\n\tdb.init(M.opts)\n\n\tif M.opts.db_build_cmd.script ~= \"default\" and vim.fn.executable(M.opts.db_build_cmd.script) ~= 1 then\n\t\tlog.warn(string.format(\"db_build script(%s) not found. Using default\", M.opts.db_build_cmd.script))\n\t\tM.opts.db_build_cmd = { script = \"default\", args = { \"-bqkv\" } }\n\tend\n\n\tif M.opts.db_build_cmd_args then\n\t\tM.opts.db_build_cmd.args = M.opts.db_build_cmd_args\n\t\tlog.warn(\n\t\t\tstring.format(\n\t\t\t\t[[db_build_cmd_args is deprecated. Use 'db_build_cmd = { args = %s }']],\n\t\t\t\tvim.inspect(M.opts.db_build_cmd_args)\n\t\t\t)\n\t\t)\n\tend\n\n\t-- if project rooter is enabled,\n\t-- 1. get root of project and update primary conn\n\t-- 2. if change_cwd is enabled, change into it (?)\n\tif M.opts.project_rooter.enable then\n\t\tlocal primary_conn = db.primary_conn()\n\t\tlocal root = M.root(vim.fn.getcwd(), primary_conn.file)\n\t\tif root then\n\t\t\tdb.update_primary_conn(vim.fs.joinpath(root, primary_conn.file), root)\n\n\t\t\tif M.opts.project_rooter.change_cwd then\n\t\t\t\tvim.cmd(\"cd \" .. root)\n\t\t\tend\n\t\tend\n\tend\n\n\tcscope_picker = require(\"cscope.pickers.\" .. M.opts.picker)\n\tM.user_command()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/fzf-lua.lua",
    "content": "local config = require(\"fzf-lua.config\")\nlocal make_entry = require(\"fzf-lua.make_entry\")\n\nlocal M = {}\n\nlocal prepare = function(parsed_output)\n\tlocal res = {}\n\n\tfor _, entry in ipairs(parsed_output) do\n\t\tlocal _entry = (\"%s:%s:%s\"):format(\n\t\t\tmake_entry.file(entry[\"filename\"], { file_icons = true, color_icons = true }),\n\t\t\tentry[\"lnum\"],\n\t\t\tentry[\"text\"]\n\t\t)\n\n\t\ttable.insert(res, _entry)\n\tend\n\treturn res\nend\n\nM.run = function(opts)\n\tlocal entries = prepare(opts.cscope.parsed_output)\n\tlocal _config = { prompt = opts.cscope.prompt_title .. \"> \" }\n\t_config = config.normalize_opts(_config, \"grep\")\n\trequire(\"fzf-lua\").fzf_exec(entries, _config)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/location.lua",
    "content": "local M = {}\n\nM.run = function(opts)\n\tlocal pos_cmd = \"\"\n\n\tvim.fn.setloclist(0, opts.cscope.parsed_output)\n\tvim.fn.setloclist(0, {}, \"a\", { title = opts.cscope.prompt_title })\n\n\tif opts.cscope.picker_opts.window_pos == \"top\" then\n\t\tpos_cmd = \"aboveleft\"\n\telseif opts.cscope.picker_opts.window_pos == \"bottom\" then\n\t\tpos_cmd = \"belowright\"\n\telseif opts.cscope.picker_opts.window_pos == \"right\" then\n\t\tpos_cmd = \"belowright vertical\"\n\telseif opts.cscope.picker_opts.window_pos == \"left\" then\n\t\tpos_cmd = \"aboveleft vertical\"\n\tend\n\n\tvim.cmd(pos_cmd .. \" lopen \" .. opts.cscope.picker_opts.window_size)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/mini-pick.lua",
    "content": "local M = {}\n\nM.run = function(opts)\n\topts = opts or {}\n\tlocal entries = {}\n\n\tfor _, item in ipairs(opts.cscope.parsed_output) do\n\t\tlocal entry = {\n\t\t\tpath = item.filename,\n\t\t\tlnum = tonumber(item.lnum),\n\t\t\ttext = string.format(\"%s:%s:%s\", item.filename, item.lnum, item.text),\n\t\t}\n\t\ttable.insert(entries, entry)\n\tend\n\n\tMiniPick.start({\n\t\tsource = {\n\t\t\titems = entries,\n\t\t\tname = opts.cscope.prompt_title,\n\t\t\tshow = function(buf_id, items, query)\n\t\t\t\tMiniPick.default_show(buf_id, items, query, { show_icons = true })\n\t\t\tend,\n\t\t},\n\t})\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/quickfix.lua",
    "content": "local M = {}\n\nM.run = function(opts)\n\tlocal pos_cmd = \"\"\n\tlocal window_size = opts.cscope.qf_window_size or opts.cscope.picker_opts.window_size\n\tlocal window_pos = opts.cscope.qf_window_pos or opts.cscope.picker_opts.window_pos\n\n\tvim.fn.setqflist(opts.cscope.parsed_output)\n\tvim.fn.setqflist({}, \"a\", { title = opts.cscope.prompt_title })\n\n\tif window_pos == \"top\" then\n\t\tpos_cmd = \"topleft\"\n\telseif window_pos == \"bottom\" then\n\t\tpos_cmd = \"botright\"\n\telseif window_pos == \"right\" then\n\t\tpos_cmd = \"botright vertical\"\n\telseif window_pos == \"left\" then\n\t\tpos_cmd = \"topleft vertical\"\n\tend\n\n\tvim.cmd(pos_cmd .. \" copen \" .. window_size)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/snacks.lua",
    "content": "local M = {}\n\nlocal prepare = function(items)\n\tlocal res = {}\n\tfor i, item in ipairs(items) do\n\t\ttable.insert(res, {\n\t\t\tfile = item[\"filename\"],\n\t\t\tscore = i,\n\t\t\ttext = item[\"text\"],\n\t\t\tline = item[\"text\"],\n\t\t\tpos = { tonumber(item[\"lnum\"]), 0 },\n\t\t})\n\tend\n\n\treturn res\nend\n\nM.run = function(opts)\n\tlocal snacks_opts = opts.cscope.picker_opts.snacks or {}\n\tsnacks_opts.items = prepare(opts.cscope.parsed_output)\n\tsnacks_opts.title = opts.cscope.prompt_title\n\tSnacks.picker(snacks_opts)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/pickers/telescope.lua",
    "content": "local M = {}\n\nlocal pickers = require(\"telescope.pickers\")\nlocal finders = require(\"telescope.finders\")\nlocal config = require(\"telescope.config\")\nlocal utils = require(\"telescope.utils\")\nlocal cs_utils = require(\"cscope_maps.utils\")\n\nlocal entry_maker = function(entry)\n\treturn {\n\t\tvalue = entry,\n\t\tdisplay = function()\n\t\t\tlocal display_filename = cs_utils.get_rel_path(vim.fn.getcwd(), entry[\"filename\"])\n\t\t\tlocal coordinates = string.format(\":%s:\", entry[\"lnum\"])\n\t\t\tlocal display_string = \"%s%s%s\"\n\t\t\tlocal display, hl_group, icon = utils.transform_devicons(\n\t\t\t\tentry[\"filename\"],\n\t\t\t\tstring.format(display_string, display_filename, coordinates, entry[\"text\"]),\n\t\t\t\tfalse\n\t\t\t)\n\n\t\t\tif hl_group then\n\t\t\t\treturn display, { { { 1, #icon }, hl_group } }\n\t\t\telse\n\t\t\t\treturn display\n\t\t\tend\n\t\tend,\n\t\tordinal = entry[\"filename\"] .. entry[\"text\"],\n\t\tpath = cs_utils.get_abs_path(entry[\"filename\"]),\n\t\tlnum = tonumber(entry[\"lnum\"]),\n\t}\nend\n\nlocal finder = nil\nlocal prompt_title = nil\n\nlocal prepare = function(cscope_parsed_output, telescope_title)\n\tfinder = finders.new_table({\n\t\tresults = cscope_parsed_output,\n\t\tentry_maker = entry_maker,\n\t})\n\n\tprompt_title = telescope_title\nend\n\nM.run = function(opts)\n\topts = opts or {}\n\topts.entry_maker = entry_maker\n\tprepare(opts.cscope.parsed_output, opts.cscope.prompt_title)\n\n\tpickers\n\t\t.new(opts, {\n\t\t\tprompt_title = prompt_title,\n\t\t\tfinder = finder,\n\t\t\tpreviewer = config.values.grep_previewer(opts),\n\t\t\tsorter = config.values.generic_sorter(opts),\n\t\t})\n\t\t:find()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/stack_view/hl.lua",
    "content": "local tree = require(\"cscope.stack_view.tree\")\nlocal fn = vim.fn\nlocal api = vim.api\n\nlocal M = {}\nM.ft = \"CsStackView\"\n\nM.get_pos = function(lnum)\n\tlocal line = fn.getline(lnum)\n\tlocal indent_len = #line\n\tline = vim.trim(line)\n\tindent_len = indent_len - #line\n\n\tlocal line_split = vim.split(line, \"%s+\")\n\tlocal symbol = line_split[2]\n\tlocal fname = \"\"\n\tlocal flnum = \"\"\n\n\tif #line_split == 3 then\n\t\tlocal file_loc = vim.split(line_split[3], \"::\")\n\t\tfname = file_loc[1]:sub(2)\n\t\tflnum = file_loc[2]:sub(1, -2)\n\tend\n\n\tlocal indicator_s = indent_len\n\tlocal indicator_e = indicator_s + 2\n\n\tlocal symbol_s = indicator_e + 1\n\tlocal symbol_e = symbol_s + #symbol\n\n\tlocal bo_s = symbol_e + 1\n\tlocal bo_e = bo_s + 1\n\n\tlocal fname_s = symbol_e + 2\n\tlocal fname_e = fname_s + #fname\n\n\tlocal delim_s = fname_e\n\tlocal delim_e = fname_e + 2\n\n\tlocal lnum_s = fname_e + 2\n\tlocal lnum_e = lnum_s + #flnum\n\n\tlocal bc_s = lnum_e\n\tlocal bc_e = bc_s + 1\n\n\treturn indicator_s,\n\t\tindicator_e,\n\t\tsymbol_s,\n\t\tsymbol_e,\n\t\tbo_s,\n\t\tbo_e,\n\t\tfname_s,\n\t\tfname_e,\n\t\tdelim_s,\n\t\tdelim_e,\n\t\tlnum_s,\n\t\tlnum_e,\n\t\tbc_s,\n\t\tbc_e\nend\n\nM.refresh = function(buf, root)\n\tif vim.bo.filetype ~= M.ft then\n\t\treturn\n\tend\n\n\tlocal ns = api.nvim_create_namespace(\"CsStackViewHighlight\")\n\n\tlocal buf_lnum_start = fn.line(\"w0\") - 1\n\tlocal buf_lnum_end = fn.line(\"w$\") - 1\n\tlocal cursor_lnum = fn.line(\".\") - 1\n\n\tlocal min_indent = vim.fn.indent(cursor_lnum)\n\n\tapi.nvim_buf_clear_namespace(buf, ns, 0, -1)\n\n\t-- go from cursor up and highlight every place where the indentation gets smaller\n\tfor buf_lnum = cursor_lnum, buf_lnum_start, -1 do\n\t\tlocal index_indent = vim.fn.indent(buf_lnum + 1)\n\t\tif buf_lnum == 0 then\n\t\t\t-- always highlight the first line\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Function\", 0, 0, -1)\n\t\telseif (buf_lnum == cursor_lnum) or (index_indent < min_indent) then\n\t\t\t-- highlight the cursor line and all the lines above it whose indentation is smaller than its previous line\n\t\t\tmin_indent = index_indent\n\t\t\tlocal 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 =\n\t\t\t\tM.get_pos(buf_lnum + 1)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Operator\", buf_lnum, indicator_s, indicator_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Function\", buf_lnum, symbol_s, symbol_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Delimiter\", buf_lnum, bo_s, bo_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"String\", buf_lnum, fname_s, fname_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Delimiter\", buf_lnum, delim_s, delim_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Number\", buf_lnum, lnum_s, lnum_e)\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Delimiter\", buf_lnum, bc_s, bc_e)\n\t\telse\n\t\t\t-- all the rest are comments\n\t\t\tapi.nvim_buf_add_highlight(buf, ns, \"Comment\", buf_lnum, 0, -1)\n\t\tend\n\tend\n\n\t-- all the lines below the cursor are comments for sure\n\tfor buf_lnum = cursor_lnum + 1, buf_lnum_end do\n\t\tapi.nvim_buf_add_highlight(buf, ns, \"Comment\", buf_lnum, 0, -1)\n\tend\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/stack_view/init.lua",
    "content": "local cs = require(\"cscope\")\nlocal tree = require(\"cscope.stack_view.tree\")\nlocal hl = require(\"cscope.stack_view.hl\")\nlocal log = require(\"cscope_maps.utils.log\")\nlocal utils = require(\"cscope_maps.utils\")\nlocal M = {}\n\nM.opts = {\n\ttree_hl = true, -- toggle tree highlighting\n\tsize = \"medium\", -- \"medium\" or \"large\"\n}\n\n-- Size presets for stack view window\n-- width: fraction of screen width (0.85 = 85% of screen width)\n-- height: fraction of screen height (0.7 = 70% of screen height)\nlocal size_presets = {\n\tmedium = { width = 0.85, height = 0.7 },\n\tlarge = { width = 0.95, height = 0.95 },\n}\n\n-- m()\n-- -> a()\n-- -> b()\n-- -> c()\n--\n-- a()\n-- <- m()\n--    <- n()\n--\n-- {a : {m : { { n : {} }, { o : {} } } }}\n-- node = {data: {} children: {}}\n\n-- callers --> DOWN the stack\n-- called  --> UP the stack\n\nM.cache = {\n\tsv = { buf = nil, win = nil },\n\tpv = { buf = nil, win = nil, files = {}, last_file = \"\" },\n\tlast_win = nil,\n\twin_opened = false,\n}\n\nM.dir_map = {\n\tdown = {\n\t\tindicator = \"<- \",\n\t\tcs_func = function(symbol)\n\t\t\tlocal _, res = cs.get_result(cs.op_s_n.c, \"c\", symbol)\n\t\t\treturn res\n\t\tend,\n\t},\n\tup = {\n\t\tindicator = \"-> \",\n\t\tcs_func = function(symbol)\n\t\t\tlocal _, res = cs.get_result(cs.op_s_n.d, \"d\", symbol)\n\t\t\treturn res\n\t\tend,\n\t},\n}\n\nM.ft = \"CsStackView\"\nlocal api = vim.api\nlocal fn = vim.fn\nlocal root = nil\nlocal buf_lines = nil\nlocal cur_dir = nil\nlocal buf_last_pos = nil\n\nM.buf_lock = function(buf)\n\tapi.nvim_set_option_value(\"readonly\", true, { buf = buf })\n\tapi.nvim_set_option_value(\"modifiable\", false, { buf = buf })\nend\n\nM.buf_unlock = function(buf)\n\tapi.nvim_set_option_value(\"readonly\", false, { buf = buf })\n\tapi.nvim_set_option_value(\"modifiable\", true, { buf = buf })\nend\n\nM.pv_scroll = function(dir)\n\tlocal input = dir > 0 and [[\u0005]] or [[\u0019]]\n\n\treturn function()\n\t\tvim.api.nvim_win_call(M.cache.pv.win, function()\n\t\t\tvim.cmd([[normal! ]] .. input)\n\t\tend)\n\tend\nend\n\n---Saves last window from where stack_view is opened.\n---last_win is used by buf_close() to return to correct window\nM.save_last_window = function()\n\tif M.cache.last_win ~= nil then\n\t\treturn\n\tend\n\tM.cache.last_win = api.nvim_get_current_win()\nend\n\nM.set_keymaps = function()\n\tlocal opts = { buffer = M.cache.sv.buf, silent = true }\n\n\t-- close window\n\tvim.keymap.set(\"n\", \"q\", M.toggle_win, opts)\n\tvim.keymap.set(\"n\", \"<esc>\", M.toggle_win, opts)\n\n\t-- toggle children\n\tvim.keymap.set(\"n\", \"<tab>\", M.toggle_children, opts)\n\n\t-- open location under cursor\n\tvim.keymap.set(\"n\", \"<cr>\", function()\n\t\tM.open_action(\"none\")\n\tend, opts)\n\tvim.keymap.set(\"n\", \"<C-v>\", function()\n\t\tM.open_action(\"vert\")\n\tend, opts)\n\tvim.keymap.set(\"n\", \"<C-s>\", function()\n\t\tM.open_action(\"horiz\")\n\tend, opts)\n\n\t-- scroll up\n\tvim.keymap.set(\"n\", \"<C-u>\", M.pv_scroll(-1), opts)\n\tvim.keymap.set(\"n\", \"<C-y>\", M.pv_scroll(-1), opts)\n\n\t-- scroll down\n\tvim.keymap.set(\"n\", \"<C-d>\", M.pv_scroll(1), opts)\n\tvim.keymap.set(\"n\", \"<C-e>\", M.pv_scroll(1), opts)\nend\n\nM.set_autocmds = function()\n\tlocal augroup = api.nvim_create_augroup(\"CscopeMaps\", {})\n\tapi.nvim_create_autocmd({ \"BufLeave\" }, {\n\t\tgroup = augroup,\n\t\tbuffer = M.cache.sv.buf,\n\t\tcallback = M.toggle_win,\n\t})\n\n\tapi.nvim_create_autocmd(\"CursorMoved\", {\n\t\tgroup = augroup,\n\t\tbuffer = M.cache.sv.buf,\n\t\tcallback = function()\n\t\t\tif M.opts.tree_hl then\n\t\t\t\thl.refresh(M.cache.sv.buf, root)\n\t\t\tend\n\t\t\tM.preview_update()\n\t\tend,\n\t})\n\n\tapi.nvim_create_autocmd(\"VimResized\", {\n\t\tgroup = augroup,\n\t\tbuffer = M.cache.sv.buf,\n\t\tcallback = function()\n\t\t\tbuf_last_pos = fn.line(\".\")\n\t\t\tM.buf_close()\n\t\t\tM.buf_open_and_update()\n\t\tend,\n\t})\nend\n\nM.buf_open = function()\n\tlocal vim_height = vim.o.lines\n\tlocal vim_width = vim.o.columns\n\n\t-- Each pane takes half the preset width, full preset height.\n\t-- col/row center the pair of panes on screen.\n\tlocal preset = size_presets[M.opts.size]\n\tlocal width = math.floor(vim_width * preset.width * 0.5)\n\tlocal height = math.floor(vim_height * preset.height)\n\tlocal col = vim_width * (1 - preset.width) * 0.5\n\tlocal row = vim_height * (1 - preset.height) * 0.5\n\n\tM.save_last_window()\n\n\tM.cache.pv.buf = M.cache.pv.buf or api.nvim_create_buf(false, true)\n\tM.cache.pv.win = M.cache.pv.win\n\t\tor api.nvim_open_win(M.cache.pv.buf, true, {\n\t\t\trelative = \"editor\",\n\t\t\ttitle = \"preview\",\n\t\t\ttitle_pos = \"center\",\n\t\t\twidth = width,\n\t\t\theight = height,\n\t\t\tcol = col + width + 1,\n\t\t\trow = row,\n\t\t\tstyle = \"minimal\",\n\t\t\tfocusable = false,\n\t\t\tborder = \"single\",\n\t\t})\n\tapi.nvim_set_option_value(\"filetype\", \"c\", { buf = M.cache.pv.buf })\n\tapi.nvim_set_option_value(\"cursorline\", true, { win = M.cache.pv.win })\n\n\tM.cache.sv.buf = M.cache.sv.buf or api.nvim_create_buf(false, true)\n\tM.cache.sv.win = M.cache.sv.win\n\t\tor api.nvim_open_win(M.cache.sv.buf, true, {\n\t\t\trelative = \"editor\",\n\t\t\ttitle = M.ft,\n\t\t\ttitle_pos = \"center\",\n\t\t\twidth = width,\n\t\t\theight = height,\n\t\t\tcol = col - 1,\n\t\t\trow = row,\n\t\t\tstyle = \"minimal\",\n\t\t\tfocusable = false,\n\t\t\tborder = \"single\",\n\t\t})\n\tapi.nvim_set_option_value(\"filetype\", M.ft, { buf = M.cache.sv.buf })\n\tapi.nvim_set_option_value(\"cursorline\", true, { win = M.cache.sv.win })\n\n\tM.set_keymaps()\n\tM.set_autocmds()\n\n\tM.cache.win_opened = true\nend\n\nM.buf_close = function()\n\tif M.cache.sv.buf ~= nil and api.nvim_buf_is_valid(M.cache.sv.buf) then\n\t\tapi.nvim_buf_delete(M.cache.sv.buf, { force = true })\n\tend\n\n\tif M.cache.sv.win ~= nil and api.nvim_win_is_valid(M.cache.sv.win) then\n\t\tapi.nvim_win_close(M.cache.sv.win, true)\n\tend\n\n\tif M.cache.pv.buf ~= nil and api.nvim_buf_is_valid(M.cache.pv.buf) then\n\t\tapi.nvim_buf_delete(M.cache.pv.buf, { force = true })\n\tend\n\n\tif M.cache.pv.win ~= nil and api.nvim_win_is_valid(M.cache.pv.win) then\n\t\tapi.nvim_win_close(M.cache.pv.win, true)\n\tend\n\n\tif M.cache.last_win ~= nil and api.nvim_win_is_valid(M.cache.last_win) then\n\t\tapi.nvim_set_current_win(M.cache.last_win)\n\tend\n\n\tM.cache.sv.buf = nil\n\tM.cache.sv.win = nil\n\n\tM.cache.pv.buf = nil\n\tM.cache.pv.win = nil\n\n\tM.cache.pv.last_file = \"\"\n\n\tM.cache.last_win = nil\n\n\tM.cache.win_opened = false\nend\n\n---open stack_view window (if not already) and update using buf_lines\nM.buf_open_and_update = function()\n\tif root == nil then\n\t\treturn\n\tend\n\n\tif not M.cache.win_opened then\n\t\tM.buf_open()\n\tend\n\n\t-- print(vim.inspect(root))\n\tbuf_lines = {}\n\tM.buf_create_lines(root)\n\t-- print(vim.inspect(buf_lines))\n\n\tM.buf_unlock(M.cache.sv.buf)\n\tapi.nvim_buf_set_lines(M.cache.sv.buf, 0, -1, false, buf_lines)\n\tif buf_last_pos ~= nil then\n\t\tapi.nvim_win_set_cursor(M.cache.sv.win, { buf_last_pos, 0 })\n\t\tbuf_last_pos = nil\n\tend\n\tM.buf_lock(M.cache.sv.buf)\nend\n\n--- Read data from given file\n--- @param file string\n--- @return table\nM.read_lines_from_file = function(file)\n\tlocal lines = {}\n\tfor line in io.lines(file) do\n\t\tlines[#lines + 1] = line\n\tend\n\treturn lines\nend\n\n--- Update preview window to show location under cursor\nM.preview_update = function()\n\tvim.schedule(function()\n\t\tlocal _, filename, lnum = M.line_to_data(fn.getline(\".\"))\n\t\tif filename == \"\" then\n\t\t\tM.cache.pv.last_file = \"\"\n\t\t\tapi.nvim_buf_set_lines(M.cache.pv.buf, 0, -1, false, {})\n\t\t\treturn\n\t\tend\n\t\tif filename ~= M.cache.pv.last_file then\n\t\t\tlocal lines = M.cache.pv.files[filename] or M.read_lines_from_file(filename)\n\t\t\t-- cache files for reuse\n\t\t\tM.cache.pv.files[filename] = lines\n\t\t\tM.cache.pv.last_file = filename\n\n\t\t\tapi.nvim_buf_set_lines(M.cache.pv.buf, 0, -1, false, lines)\n\t\tend\n\t\tapi.nvim_win_set_cursor(M.cache.pv.win, { lnum, 0 })\n\tend)\nend\n\nM.line_to_data = function(line)\n\tline = vim.trim(line)\n\tlocal line_split = vim.split(line, \"%s+\")\n\tlocal symbol = line_split[2]\n\tlocal filename = \"\"\n\tlocal lnum = 0\n\n\tif #line_split == 3 then\n\t\tlocal file_loc = vim.split(line_split[3], \"::\")\n\t\tfilename = file_loc[1]:sub(2)\n\t\tlnum = tonumber(file_loc[2]:sub(1, -2), 10)\n\tend\n\n\treturn symbol, filename, lnum\nend\n\nM.buf_create_lines = function(node)\n\tlocal item = \"\"\n\tif node.is_root then\n\t\titem = node.data.symbol\n\telse\n\t\titem = string.format(\n\t\t\t\"%s%s%s [%s::%s]\",\n\t\t\tstring.rep(\" \", node.depth * #M.dir_map[cur_dir].indicator),\n\t\t\tM.dir_map[cur_dir].indicator,\n\t\t\tnode.data.symbol,\n\t\t\tnode.data.filename,\n\t\t\tnode.data.lnum\n\t\t)\n\tend\n\n\ttable.insert(buf_lines, item)\n\tnode.id = #buf_lines\n\n\tif not node.children then\n\t\treturn\n\tend\n\n\tfor _, c in ipairs(node.children) do\n\t\tM.buf_create_lines(c)\n\tend\nend\n\nM.toggle_children = function()\n\tif vim.bo.filetype ~= M.ft then\n\t\treturn\n\tend\n\n\tif cur_dir == nil then\n\t\treturn\n\tend\n\n\tif root == nil then\n\t\treturn\n\tend\n\n\tlocal cur_line = fn.line(\".\")\n\n\tif cur_line == 1 then\n\t\treturn\n\tend\n\n\tlocal psymbol, pfilename, plnum = M.line_to_data(fn.getline(\".\"))\n\tlocal parent_id = cur_line\n\tlocal cs_res = M.dir_map[cur_dir].cs_func(psymbol)\n\n\tif not cs_res then\n\t\treturn\n\tend\n\n\t-- update children list\n\tlocal children = {}\n\tfor _, r in ipairs(cs_res) do\n\t\tlocal node = tree.create_node(r.ctx:sub(3, -3), r.filename, r.lnum)\n\t\ttable.insert(children, node)\n\tend\n\n\troot = tree.update_node(root, parent_id, children)\n\tM.buf_open_and_update()\nend\n\nM.open = function(dir, symbol)\n\tif vim.bo.filetype == M.ft then\n\t\treturn\n\tend\n\n\tM.buf_close()\n\troot = nil\n\tbuf_last_pos = nil\n\n\tif not vim.tbl_contains(vim.tbl_keys(M.dir_map), dir) then\n\t\treturn\n\tend\n\n\tlocal cs_res = M.dir_map[dir].cs_func(symbol)\n\n\tif not cs_res then\n\t\treturn\n\tend\n\n\tcur_dir = dir\n\n\t-- update children list\n\tlocal children = {}\n\tfor _, r in ipairs(cs_res) do\n\t\tlocal node = tree.create_node(r.ctx:sub(3, -3), r.filename, r.lnum)\n\t\ttable.insert(children, node)\n\tend\n\n\troot = tree.create_node(symbol, \"\", 0)\n\troot.children = children\n\troot.is_root = true\n\n\tM.buf_open_and_update()\nend\n\nM.toggle_win = function()\n\tif vim.bo.filetype == M.ft then\n\t\tbuf_last_pos = fn.line(\".\")\n\t\tM.buf_close()\n\t\treturn\n\tend\n\tM.buf_open_and_update()\nend\n\nM.open_action = function(split)\n\tif vim.bo.filetype ~= M.ft then\n\t\treturn\n\tend\n\n\tif fn.line(\".\") == 1 then\n\t\treturn\n\tend\n\n\tlocal _, pfilename, plnum = M.line_to_data(fn.getline(\".\"))\n\tM.toggle_win()\n\tutils.open_file(pfilename, plnum, split)\nend\n\n-- :CsStackView toggle\n-- :CsStackView open down|up symbol\n\nM.run_cmd = function(args)\n\tlocal cmd = args[1]\n\n\tif vim.startswith(cmd, \"o\") then\n\t\tlocal stk_dir = args[2]\n\t\tlocal symbol = args[3] or cs.default_sym(\"s\")\n\t\tif vim.startswith(stk_dir, \"d\") then\n\t\t\tstk_dir = \"down\"\n\t\telseif vim.startswith(stk_dir, \"u\") then\n\t\t\tstk_dir = \"up\"\n\t\tend\n\t\tM.open(stk_dir, symbol)\n\telseif vim.startswith(cmd, \"t\") then\n\t\tM.toggle_win()\n\tend\nend\n\nM.set_user_cmd = function()\n\t-- Create the :CsStackView user command\n\tvim.api.nvim_create_user_command(\"CsStackView\", function(opts)\n\t\tM.run_cmd(opts.fargs)\n\tend, {\n\t\tnargs = \"*\",\n\t\tcomplete = function(_, line)\n\t\t\tlocal cmds = { \"open\", \"toggle\" }\n\t\t\tlocal l = vim.split(line, \"%s+\")\n\t\t\tlocal n = #l - 2\n\n\t\t\tif n == 0 then\n\t\t\t\treturn vim.tbl_filter(function(val)\n\t\t\t\t\treturn vim.startswith(val, l[2])\n\t\t\t\tend, cmds)\n\t\t\tend\n\n\t\t\tif n == 1 and vim.startswith(l[2], \"o\") then\n\t\t\t\treturn { \"down\", \"up\" }\n\t\t\tend\n\t\tend,\n\t})\nend\n\nM.setup = function(opts)\n\tM.opts = vim.tbl_deep_extend(\"force\", M.opts, opts)\n\t-- Validate size preset (fall back to \"medium\" if invalid)\n\tif not size_presets[M.opts.size] then\n\t\tlog.warn(string.format('Invalid stack_view size \"%s\", using \"medium\"', M.opts.size))\n\t\tM.opts.size = \"medium\"\n\tend\n\tM.set_user_cmd()\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope/stack_view/tree.lua",
    "content": "local RC = require(\"cscope_maps.utils.ret_codes\")\nlocal M = {}\n\n--- node = {data: d, children: {n1, n2, n3, ...}}\n\nM.create_node = function(symbol, filename, lnum)\n\tlocal node = {}\n\n\tnode.children = nil\n\tnode.depth = 0\n\tnode.data = {}\n\tnode.is_root = false\n\tnode.id = 0\n\n\tnode.data.symbol = symbol\n\tnode.data.filename = filename\n\tnode.data.lnum = tonumber(lnum, 10)\n\n\treturn node\nend\n\nM.compare_node = function(node, id)\n\treturn (node and node.id == id)\nend\n\nM.get_node = function(root, id)\n\tif M.compare_node(root, id) then\n\t\treturn root\n\tend\n\n\tlocal children = root.children\n\n\tif not children then\n\t\treturn nil\n\tend\n\n\tfor _, c in ipairs(children) do\n\t\tlocal node = M.get_node(c, id)\n\t\tif node ~= nil then\n\t\t\treturn node\n\t\tend\n\tend\nend\n\nM.update_children_depth = function(children, depth)\n\tfor _, c in ipairs(children) do\n\t\tc.depth = depth\n\tend\nend\n\nM.update_children = function(root, parent_id, children)\n\tlocal node = M.get_node(root, parent_id)\n\n\tif not node then\n\t\treturn RC.NODE_NOT_FOUND\n\tend\n\n\tif node.children == nil then\n\t\tnode.children = children\n\t\tM.update_children_depth(node.children, node.depth + 1)\n\telse\n\t\tnode.children = nil\n\tend\n\n\treturn RC.SUCCESS\nend\n\nM.update_node = function(root, parent_id, children)\n\tlocal ret = M.update_children(root, parent_id, children)\n\n\tif ret == RC.SUCCESS then\n\t\treturn root\n\tend\n\n\treturn nil\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope_maps/init.lua",
    "content": "local helper = require(\"cscope_maps.utils.helper\")\nlocal cs = require(\"cscope\")\nlocal M = {}\n\n---@class CsMapsConfig\n---@field disable_maps? boolean\n---@field skip_input_prompt? boolean\n---@field prefix? string\n---@field cscope? CsConfig\nM.opts = {\n\tdisable_maps = false, -- \"true\" disables default keymaps\n\tskip_input_prompt = false, -- \"true\" doesn't ask for input\n\tprefix = \"<leader>c\", -- prefix to trigger maps\n\tcscope = {}, -- defaults are in cscope.lua\n\tstack_view = {}, -- defaults are in stack_view\n}\n\n-- function to print xcscpoe.el like prompts\nM.cscope_prompt = function(operation)\n\tif\n\t\tnot vim.tbl_contains(vim.tbl_keys(cs.op_s_n), operation)\n\t\tand not vim.tbl_contains(vim.tbl_values(cs.op_s_n), operation)\n\tthen\n\t\treturn\n\tend\n\tif vim.tbl_contains(vim.tbl_values(cs.op_s_n), operation) then\n\t\toperation = cs.op_n_s[operation]\n\tend\n\tlocal default_symbol = cs.default_sym(operation)\n\tif M.opts.skip_input_prompt then\n\t\tvim.cmd.Cscope({ args = { \"find\", operation, default_symbol } })\n\telse\n\t\tlocal prompt = string.format(\"%s (default: '%s'): \", helper.sym_map[operation], default_symbol)\n\t\tvim.ui.input({ prompt = prompt }, function(new_symbol)\n\t\t\tif new_symbol == nil then\n\t\t\t\treturn\n\t\t\tend\n\t\t\tif new_symbol ~= \"\" then\n\t\t\t\tvim.cmd.Cscope({ args = { \"find\", operation, new_symbol } })\n\t\t\telse\n\t\t\t\tvim.cmd.Cscope({ args = { \"find\", operation, default_symbol } })\n\t\t\tend\n\t\tend)\n\tend\nend\n\n---Initialization api\n---@param opts CsMapsConfig\nM.setup = function(opts)\n\topts = opts or {}\n\tM.opts = vim.tbl_deep_extend(\"force\", M.opts, opts)\n\n\tvim.api.nvim_create_user_command(\"CsPrompt\", function(opts)\n\t\tM.cscope_prompt(opts.fargs[1])\n\tend, {\n\t\tnargs = \"*\",\n\t\tcomplete = function()\n\t\t\treturn vim.tbl_keys(cs.op_s_n)\n\t\tend,\n\t})\n\n\tif not M.opts.disable_maps then\n\t\t-- Mappings\n\t\thelper.default_keymaps(M.opts.prefix)\n\tend\n\n\tcs.setup(M.opts.cscope)\n\trequire(\"cscope.stack_view\").setup(M.opts.stack_view)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope_maps/utils/helper.lua",
    "content": "local M = {}\n\n-- define key table for input strings\nM.sym_map = {\n\ts = \"Find this symbol\",\n\tg = \"Find this global definition\",\n\tc = \"Find functions calling this function\",\n\tt = \"Find this text string\",\n\te = \"Find this egrep pattern\",\n\tf = \"Find this file\",\n\ti = \"Find files #including this file\",\n\td = \"Find functions called by this function\",\n\ta = \"Find places where this symbol is assigned a value\",\n\tb = \"Build database\",\n}\n\nM.default_keymaps = function(prefix)\n\tlocal map = vim.keymap.set\n\tlocal sym_map = M.sym_map\n\tif MiniClue then\n\t\ttable.insert(MiniClue.config.clues, { mode = \"n\", keys = prefix, desc = \"+cscope\" })\n\telse\n\t\tlocal ok, wk = pcall(require, \"which-key\")\n\t\tif ok then\n\t\t\tif wk.add then\n\t\t\t\twk.add({ { prefix, group = \"+cscope\" } })\n\t\t\telse\n\t\t\t\twk.register({ [prefix] = { name = \"+cscope\" } })\n\t\t\tend\n\t\tend\n\tend\n\tmap({ \"n\", \"v\" }, prefix .. \"s\", \"<cmd>CsPrompt s<cr>\", { desc = sym_map.s })\n\tmap({ \"n\", \"v\" }, prefix .. \"g\", \"<cmd>CsPrompt g<cr>\", { desc = sym_map.g })\n\tmap({ \"n\", \"v\" }, prefix .. \"c\", \"<cmd>CsPrompt c<cr>\", { desc = sym_map.c })\n\tmap({ \"n\", \"v\" }, prefix .. \"t\", \"<cmd>CsPrompt t<cr>\", { desc = sym_map.t })\n\tmap({ \"n\", \"v\" }, prefix .. \"e\", \"<cmd>CsPrompt e<cr>\", { desc = sym_map.e })\n\tmap({ \"n\", \"v\" }, prefix .. \"f\", \"<cmd>CsPrompt f<cr>\", { desc = sym_map.f })\n\tmap({ \"n\", \"v\" }, prefix .. \"i\", \"<cmd>CsPrompt i<cr>\", { desc = sym_map.i })\n\tmap({ \"n\", \"v\" }, prefix .. \"d\", \"<cmd>CsPrompt d<cr>\", { desc = sym_map.d })\n\tmap({ \"n\", \"v\" }, prefix .. \"a\", \"<cmd>CsPrompt a<cr>\", { desc = sym_map.a })\n\tmap({ \"n\", \"v\" }, prefix .. \"b\", \"<cmd>Cs db build<cr>\", { desc = sym_map.b })\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope_maps/utils/init.lua",
    "content": "local M = {}\n\n-- global state of utils\nM.g = {\n\trel_paths = {},\n}\n\n--- Check if given path is absolute path\n---@param path string\n---@return boolean\nM.is_path_abs = function(path)\n\tif vim.fn.has(\"nvim-0.11\") == 1 then\n\t\treturn vim.fn.isabsolutepath(path) == 1\n\tend\n\n\tif vim.fn.has(\"win32\") == 1 then\n\t\t--  match \"\\\\abc\", \"C:\\abc\" or \"C:/abc\"\n\t\treturn vim.startswith(path, \"\\\\\\\\\")\n\t\t\tor (path:sub(1, 1):match(\"%a\") and path:sub(2, 2) == \":\" and (path:sub(3, 3) == \"\\\\\" or path:sub(3, 3) == \"/\"))\n\t\t\tor false\n\tend\n\n\treturn vim.startswith(path, \"/\") or vim.startswith(path, \"~/\")\nend\n\n--- Get relative path\n--- if \"rel_to\" or \"path\" are not absolute paths then return \"path\" as it is\n--- else return relative path of \"path\" wrt to \"rel_to\"\n---@param rel_to string\n---@param path string\n---@return string\nM.get_rel_path = function(rel_to, path)\n\tif not M.is_path_abs(rel_to) or not M.is_path_abs(path) then\n\t\treturn path\n\tend\n\n\t-- get memoized path\n\tlocal g_key = string.format(\"%s#%s\", rel_to, path)\n\tif M.g.rel_paths[g_key] then\n\t\treturn M.g.rel_paths[g_key]\n\tend\n\n\tlocal rel_path = \"\"\n\tlocal sp_rel_to = vim.split(vim.fs.normalize(rel_to), \"/\")\n\tlocal sp_path = vim.split(vim.fs.normalize(path), \"/\")\n\tlocal len_rel_to = #sp_rel_to + 1\n\tlocal len_path = #sp_path + 1\n\tlocal i = 1\n\n\t-- skip till parents are same\n\twhile i < len_rel_to and i < len_path do\n\t\tif sp_rel_to[i] == sp_path[i] then\n\t\t\ti = i + 1\n\t\telse\n\t\t\tbreak\n\t\tend\n\tend\n\n\t-- append \"../\" for remaining parents\n\trel_path = rel_path .. string.rep(\"../\", len_rel_to - i)\n\n\t-- append remaining path\n\trel_path = rel_path .. table.concat(sp_path, \"/\", i)\n\n\tif rel_path == \"\" then\n\t\trel_path = \".\"\n\tend\n\n\t-- memoize path\n\tM.g.rel_paths[g_key] = rel_path\n\n\treturn rel_path\nend\n\n--- Convert given path to absolute path\n---@param path string\n---@return string\nM.get_abs_path = function(path)\n\tif M.is_path_abs(path) then\n\t\treturn path\n\tend\n\n\tlocal abs_path = vim.fs.joinpath(vim.fn.getcwd(), path)\n\n\treturn vim.fs.normalize(abs_path)\nend\n\n--- Get parent of given path\n---@param path string\n---@return string\nM.get_path_parent = function(path)\n\tfor parent in vim.fs.parents(path) do\n\t\treturn parent\n\tend\n\treturn \"\"\nend\n\n---Get all dirs and files in given path\n---@param dir string\n---@return table\nM.get_files_in_dir = function(dir)\n\tlocal fs_entries = vim.fn.readdir(dir)\n\n\t-- add \"/\" suffix for dirs and return\n\treturn vim.tbl_map(function(x)\n\t\tlocal entry = x\n\t\tif dir ~= \".\" then\n\t\t\tentry = vim.fs.joinpath(dir, x)\n\t\tend\n\t\tif vim.fn.isdirectory(x) == 1 then\n\t\t\tentry = entry .. \"/\"\n\t\tend\n\t\treturn entry\n\tend, fs_entries)\nend\n\n---Get all dirs in given path\n---@param dir string\n---@return table\nM.get_dirs_in_dir = function(dir)\n\tlocal fs_entries = vim.fn.readdir(dir)\n\n\t-- add \"/\" suffix for dirs\n\tfs_entries = vim.tbl_map(function(x)\n\t\tlocal entry = x\n\t\tif dir ~= \".\" then\n\t\t\tentry = vim.fs.joinpath(dir, x)\n\t\tend\n\t\treturn entry .. \"/\"\n\tend, fs_entries)\n\n\t-- return only dirs\n\treturn vim.tbl_filter(function(x)\n\t\treturn vim.fn.isdirectory(x) == 1\n\tend, fs_entries)\nend\n\nM.is_path_same = function(path1, path2)\n\treturn path1 and path2 and M.get_abs_path(path1) == M.get_abs_path(path2)\nend\n\n---Opens file at given line number and split orientation\n---@param fname string\n---@param lnum number\n---@param split string\nM.open_file = function(fname, lnum, split)\n\tif split == \"vert\" then\n\t\tvim.cmd(\"vsplit\")\n\telseif split == \"horiz\" then\n\t\tvim.cmd(\"split\")\n\tend\n\n\tif M.is_path_same(vim.api.nvim_buf_get_name(0), fname) then\n\t\t-- change position when in same buffer\n\t\tvim.api.nvim_win_set_cursor(0, { lnum, 0 })\n\telse\n\t\tvim.cmd(string.format(\"edit +%d %s\", lnum, fname))\n\tend\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope_maps/utils/log.lua",
    "content": "local M = {}\n\nM.lvl = vim.log.levels\n-- DEBUG\n-- ERROR\n-- INFO\n-- TRACE\n-- WARN\n-- OFF\n\nM.debug = function(msg, hide)\n\tif hide then\n\t\treturn\n\tend\n\tvim.notify(\"cscope: \" .. msg, M.lvl.DEBUG)\nend\n\nM.error = function(msg, hide)\n\tif hide then\n\t\treturn\n\tend\n\tvim.notify(\"cscope: \" .. msg, M.lvl.ERROR)\nend\n\nM.info = function(msg, hide)\n\tif hide then\n\t\treturn\n\tend\n\tvim.notify(\"cscope: \" .. msg, M.lvl.INFO)\nend\n\nM.trace = function(msg, hide)\n\tif hide then\n\t\treturn\n\tend\n\tvim.notify(\"cscope: \" .. msg, M.lvl.trace)\nend\n\nM.warn = function(msg, hide)\n\tif hide then\n\t\treturn\n\tend\n\tvim.notify(\"cscope: \" .. msg, M.lvl.WARN)\nend\n\nreturn M\n"
  },
  {
    "path": "lua/cscope_maps/utils/ret_codes.lua",
    "content": "return {\n\tSUCCESS = 0,\n\tINVALID_OP = 1,\n\tINVALID_EXEC = 2,\n\tDB_NOT_FOUND = 3,\n\tNO_RESULTS = 4,\n\tINVALID_SYMBOL = 5,\n\tNODE_NOT_FOUND = 6,\n}\n"
  }
]