{"id":20035956,"url":"https://github.com/everduin94/nvim-quick-switcher","last_synced_at":"2025-05-05T05:31:42.308Z","repository":{"id":41832356,"uuid":"441039604","full_name":"Everduin94/nvim-quick-switcher","owner":"Everduin94","description":"Quickly navigate to other files/extensions based on the current file name or jump to nodes in a file via treesitter. Written in Lua.","archived":false,"fork":false,"pushed_at":"2024-03-18T00:00:19.000Z","size":8360,"stargazers_count":49,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-26T06:42:28.201Z","etag":null,"topics":["lua","neovim","neovim-plugin","nvim","plugin","treesitter","vim"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Everduin94.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-12-23T02:29:00.000Z","updated_at":"2025-02-02T16:26:13.000Z","dependencies_parsed_at":"2023-12-22T15:55:02.158Z","dependency_job_id":null,"html_url":"https://github.com/Everduin94/nvim-quick-switcher","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Everduin94%2Fnvim-quick-switcher","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Everduin94%2Fnvim-quick-switcher/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Everduin94%2Fnvim-quick-switcher/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Everduin94%2Fnvim-quick-switcher/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Everduin94","download_url":"https://codeload.github.com/Everduin94/nvim-quick-switcher/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252446296,"owners_count":21749201,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["lua","neovim","neovim-plugin","nvim","plugin","treesitter","vim"],"created_at":"2024-11-13T10:10:06.344Z","updated_at":"2025-05-05T05:31:42.030Z","avatar_url":"https://github.com/Everduin94.png","language":"Lua","readme":"# Nvim Quick Switcher\nQuickly navigate to related/alternate files/extensions based on the current file name. Written in Lua.\n\nhttps://user-images.githubusercontent.com/14320878/205079483-82f2cd39-915e-485a-a5c6-20b188e1b26b.mp4\n\n## Features\n- 🦕 Switch to files with common prefix (example: \"tasks\"):\n  - `tasks.component.ts` --\u003e `tasks.component.html`\n  - `tasks.component.html` --\u003e `tasks.component.scss`\n  - `tasks.component.scss` --\u003e `tasks.component.ts`\n- 🦎 Toggle between file extensions\n  - `tasks.cpp` \u003c--\u003e `tasks.h`\n  - `tasks.html` \u003c--\u003e `tasks.css`\n- 🐙 Switch to files with different suffix\n  - `tasks.component.ts` --\u003e `tasks.module.ts`\n  - `tasks.component.ts` --\u003e `tasks.component.spec.ts`\n  - `tasks.query.ts` --\u003e `tasks.store.ts`\n- 🐒 Find files by Wilcard (Glob) or Regex \n  - `tasks.ts` --\u003e `tasks.spec.ts` | `tasks.test.ts` \n  - `tasks.ts` --\u003e `tasks.css` | `tasks.scss` | `tasks.sass`\n  - `/tasks.components.ts` --\u003e `state/tasks.query.ts` \n  - `state/tasks.query.ts` --\u003e `../tasks.component.ts`\n  - `controller.lua` --\u003e `controller_spec.lua`\n  - `controller-util.lua` --\u003e `controller-service.lua`\n- 🌳 Navigate inside files with Treesitter Queries\n\n## Installation\nVim-plug \n```vim\nPlug 'Everduin94/nvim-quick-switcher'\n```\n\nPacker\n```lua\nuse {\n  \"Everduin94/nvim-quick-switcher\",\n}\n```\n\n## Usage\n\nSwitcher has 4 functions. `switch`, `toggle`, `find`, `inline_ts_switch`. No setup function call required. Just call a function with the desired arguments.\n\nFor more examples, see `Recipes`\n\n### ➡️ Switch\n*Gets \"prefix\" of file name, switches to `prefix`.`suffix`*\n\n`switch(search_string, options)`\n\n#### Example\n\n`require('nvim-quick-switcher').switch('component.ts')`\n- `ticket.component.html` --\u003e `ticket.component.ts`\n\n#### Options\n```lua\n{\n  split = 'vertical'|'horizontal'|nil -- nil is default\n  size = 100 -- # of columns | rows. default is 50% split\n  ignore_prefix = false -- useful for navigating to files like \"index.ts\" or \"+page.svelte\"\n}\n```\n\n### 🔄 Toggle\n*Toggles `prefix`.`suffix`, based on current suffix / file extension*\n\n`toggle(search_string_one, search_string_two, options)`\n\nFor `options` see `Common Options`\n\n#### Example\n`require('nvim-quick-switcher').toggle('cpp', 'h')`\n- `node.cpp` --\u003e `node.h`\n- `node.h` --\u003e `node.cpp`\n\n### 🔍 Find \n*Uses gnu/linux `find` \u0026 `grep` to find file and switch to `prefix`+`pattern`*\n\n`find(search_string, options)`\n\n#### Example\n`require('nvim-quick-switcher').find('*query*')`\n- Allows wild cards, i.e. first argument is search parameter for gnu/linux find\n- file: `ticket.component.ts` --\u003e pattern: `ticket*query*`\n\n`require('nvim-quick-switcher').find('.+css|.+scss|.+sass', { regex = true })`\n- Uses find, then filters via grep regex, i.e. first argument is regex\n- file: `ticket.component.ts` --\u003e find: `ticket*` --\u003e grep: `.+css|.+scss|.+sass`\n- When using backslash, may have to escape via `\\\\`\n\nIf multiple matches are found will prompt UI Select.\n- You can use options like `prefix` or `regex` to make your searches more specific. See `Recipes` for examples.\n\nIf no results are found, will search backwards one directory, see `reverse`\n\n#### Options \n```lua\n{\n  split = 'vertical'|'horizontal'|nil\n  size = 100 \n  regex = false, -- If true, first argument uses regex instead of find string/wildcard.\n  maxdepth = 2, -- directory depth \n  reverse = true, -- false will disable reverse search when no results are found\n  path = nil, -- overwrite path (experimental).\n  prefix = 'default', \n    -- full: stop at last period\n    -- short: stop at first _ or -\n    -- long: stop at last _ \n    -- default: stop at first period.\n    -- lua function (you can pass a lua function to create a custom prefix)\n  regex_type = 'E' -- default regex extended. See grep for types.\n  ignore_prefix = false -- useful for navigating to files like \"index.ts\" or \"+page.svelte\"\n}\n```\n\n### 🌳 Inline Switch (Treesitter)\nRequires `nvim-treesitter/nvim-treesitter`\n\n*Uses treesitter queries to navigate inside of a file*\n\n`inline_ts_switch(file_type, query_string, options)`\n\n#### Example\n`require('nvim-quick-switcher').inline_ts_switch('svelte', '(script_element (end_tag) @capture)')`\n- Places cursor at start of `\u003c/script\u003e` node\n\n#### Options \n```lua\n{\n  goto_end = false, -- go to start of node if false, go to end of node if true\n  avoid_set_jump = false, -- do not add to jumplist if true\n}\n```\n\n### 🚀 Find by Function (Advanced)\nAccepts a lua function that provides path/file information as an argument, and returns a file path as a string. Useful for switching across directories / folders.\ni.e. the user is provided file path data to then build an entire file path, that gets passed to vim select, that gets passed to navigate.\n\n`require('nvim-quick-switcher').find_by_fn(fn, options)`\n\n#### Example\n\nThis example switches from a test folder to a src folder (and vice versa) like in to a Java J-Unit project.\n\n```lua\nlocal ex_find_test_fn = function (p)\n  local path = p.path;\n  local file_name = p.prefix;\n  local result = path:gsub('src', 'test') .. '/' .. file_name .. '*';\n  return result;\nend\n\nlocal find_src_fn = function (p)\n  local path = p.path;\n  local file_name = p.prefix;\n  local result = path:gsub('test', 'src') .. '/' .. file_name .. '*';\n  return result;\nend\n\nrequire('nvim-quick-switcher').find_by_fn(ex_find_test_fn)\nrequire('nvim-quick-switcher').find_by_fn(find_src_fn)\n```\n\n#### Args\n```lua\nprefix -- The prefix of the file (task.component.ts) --\u003e task\nfull_prefix -- (task.component.ts) --\u003e task.component\nfull_suffix -- (task.component.ts) --\u003e component.ts\nshort_prefix -- (task-util.lua) --\u003e task\nfile_type -- (task-util.lua) --\u003e lua\nfile_name -- (src/tasks/task-util.lua) --\u003e task-util.lua\npath -- (src/tasks/task-util.lua) --\u003e src/tasks/task-util.lua\n```\n\n### 🌌 Common Options\nOptions common for file location functions (`switch/toggle/find/find_by_fn`).\n```lua\n{\n  only_existing = false,\n  only_existing_notify = false,\n}\n```\n`only_existing` Causes the switcher to check if the target file exists\nbefore switching to it.\n`only_existing_notify` will print if the file does not exist.\n\n\n## Recipes (My Keymaps)\n*My configuration for nvim-quick-switcher. Written in Lua*\n\n```lua\nlocal opts = { noremap = true, silent = true }\n\nlocal function find(file_regex, opts)\n  return function() require('nvim-quick-switcher').find(file_regex, opts) end\nend\n\nlocal function inline_ts_switch(file_type, scheme)\n  return function() require('nvim-quick-switcher').inline_ts_switch(file_type, scheme) end\nend\n\nlocal function find_by_fn(fn, opts)\n  return function() require('nvim-quick-switcher').find_by_fn(fn, opts) end\nend\n\n-- Styles\nvim.keymap.set(\"n\", \"\u003cleader\u003eoi\", find('.+css|.+scss|.+sass', { regex = true, prefix='full' }), opts)\n\n-- Types\nvim.keymap.set(\"n\", \"\u003cleader\u003eorm\", find('.+model.ts|.+models.ts|.+types.ts', { regex = true }), opts)\n\n-- Util\nvim.keymap.set(\"n\", \"\u003cleader\u003eol\", find('*util.*', { prefix = 'short' }), opts)\n\n-- Tests\nvim.keymap.set(\"n\", \"\u003cleader\u003eot\", find('.+test|.+spec', { regex = true, prefix='full' }), opts)\n\n-- Project Specific Keymaps\n-- * Maps keys based on project using an auto command. Ideal for reusing keymaps based on context.\n-- * Example: In Angular, `oo` switches to .component.html. In Svelte, `oo` switches to *page.svelte\nvim.api.nvim_create_autocmd({'UIEnter'}, {\n    callback = function(event)\n      local is_angular = next(vim.fs.find({ \"angular.json\", \"nx.json\" }, { upward = true }))\n      local is_svelte = next(vim.fs.find({ \"svelte.config.js\", \"svelte.config.ts\" }, { upward = true }))\n\n      -- Angular\n      if is_angular then\n        print('Angular')\n        vim.keymap.set(\"n\", \"\u003cleader\u003eoo\", find('.component.html'), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eou\", find('.component.ts'), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eop\", find('.module.ts'), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eoy\", find('.service.ts'), opts)\n      end\n\n      -- SvelteKit\n      if is_svelte then\n        print('Svelte')\n        vim.keymap.set(\"n\", \"\u003cleader\u003eoo\", find('*page.svelte', { maxdepth = 1, ignore_prefix = true }), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eou\", find('.*page.server(.+js|.+ts)|.*page(.+js|.+ts)', { maxdepth = 1, regex = true, ignore_prefix = true }), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eop\", find('*layout.svelte', { maxdepth = 1, ignore_prefix = true }), opts)\n\n         -- Inline TS\n        vim.keymap.set(\"n\", \"\u003cleader\u003eoj\", inline_ts_switch('svelte', '(script_element (end_tag) @capture)'), opts)\n        vim.keymap.set(\"n\", \"\u003cleader\u003eok\", inline_ts_switch('svelte', '(style_element (start_tag) @capture)'), opts)\n      end\n    end\n})\n\n-- Redux-like\nvim.keymap.set(\"n\", \"\u003cleader\u003eore\", find('*effects.ts'), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eora\", find('*actions.ts'), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eorw\", find('*store.ts'), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eorf\", find('*facade.ts'), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eors\", find('.+query.ts|.+selectors.ts|.+selector.ts', { regex = true }), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eorr\", find('.+reducer.ts|.+repository.ts', { regex = true }), opts)\n\n-- Java J-Unit (Advanced Example)\nlocal find_test_fn = function (p)\n  local path = p.path;\n  local file_name = p.prefix;\n  local result = path:gsub('src', 'test') .. '/' .. file_name .. '*';\n  return result;\nend\n\nlocal find_src_fn = function (p)\n  local path = p.path;\n  local file_name = p.prefix;\n  local result = path:gsub('test', 'src') .. '/' .. file_name .. '*';\n  return result;\nend\n\nvim.keymap.set(\"n\", \"\u003cleader\u003eojj\", find_by_fn(find_test_fn), opts)\nvim.keymap.set(\"n\", \"\u003cleader\u003eojk\", find_by_fn(find_src_fn), opts)\n```\n\n## Personal Motivation\nMany moons ago, as a sweet summer child, I used VS Code with an extension called \"Angular Switcher\".\nAngular switcher enables switching to various file extensions related to the current component.\n\nI wanted to take that idea and make it work for many frameworks or file extensions\n\nI currently use nvim-quick-switcher on a daily basis for Svelte / Svelte-Kit, Angular Components, Tests, Stylesheets, Lua util files, and Redux-like files.\n\n## Alternatives\n- [projectionist](https://github.com/tpope/vim-projectionist)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feverduin94%2Fnvim-quick-switcher","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feverduin94%2Fnvim-quick-switcher","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feverduin94%2Fnvim-quick-switcher/lists"}