{"id":41933112,"url":"https://github.com/lettertwo/occurrence.nvim","last_synced_at":"2026-01-25T18:03:26.965Z","repository":{"id":332468131,"uuid":"1000446614","full_name":"lettertwo/occurrence.nvim","owner":"lettertwo","description":"A Neovim plugin to mark occurrences of words/patterns/selections in a buffer and perform operations on them.  Inspired by vim-mode-plus's occurrence feature.","archived":false,"fork":false,"pushed_at":"2026-01-14T00:34:41.000Z","size":491,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-14T04:21:10.239Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lettertwo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-11T19:50:22.000Z","updated_at":"2026-01-14T00:33:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lettertwo/occurrence.nvim","commit_stats":null,"previous_names":["lettertwo/occurrence.nvim"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/lettertwo/occurrence.nvim","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettertwo%2Foccurrence.nvim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettertwo%2Foccurrence.nvim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettertwo%2Foccurrence.nvim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettertwo%2Foccurrence.nvim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lettertwo","download_url":"https://codeload.github.com/lettertwo/occurrence.nvim/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lettertwo%2Foccurrence.nvim/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28756432,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T16:32:25.380Z","status":"ssl_error","status_checked_at":"2026-01-25T16:32:09.189Z","response_time":113,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":[],"created_at":"2026-01-25T18:03:24.089Z","updated_at":"2026-01-25T18:03:26.950Z","avatar_url":"https://github.com/lettertwo.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# occurrence.nvim\n\nA Neovim plugin to mark occurrences of words/patterns/selections in a buffer and perform operations on them.\n\n\u003c!-- panvimdoc-ignore-start --\u003e\n\nInspired by [vim-mode-plus]'s occurrence feature.\n\n## Key Features\n\n### 🔍 Smart Occurrence Detection\n\n- Word under cursor with boundary matching\n- Visual selections (character, line, or block)\n- Last search pattern from `/` or `?`\n\n### ⚡ Native and Custom Operator Integration\n\n- Use standard Vim operators: `c`, `d`, `y`, `p`, `\u003c`, `\u003e`, `=`, `gu`, `gU`, `g~`\n- Define custom operators to work with occurrences\n- Two interaction modes: mark-then-operate or operator-pending modifier\n- Works with motions and text objects (`ip`, `$`, `G`, etc.)\n- Dot-repeat support for all operations\n\n### 🎯 Visual Feedback\n\n- Real-time highlighting of all matches and marked occurrences\n- Current occurrence highlighting during navigation\n- Status API for showing current/total counts\n\n### 🛠️ Highly Configurable\n\n- Enable/disable default keymaps or define custom ones\n- Choose which operators to enable or disable, or add custom ones\n- Customizable highlight groups\n- Lua API for advanced usage and integration\n\n\u003c!-- panvimdoc-ignore-end --\u003e\n\n# Installation\n\n### Requirements\n\n- Neovim \u003e= 0.10.0\n\n### Using [lazy.nvim](https://github.com/folke/lazy.nvim)\n\n```lua\n{\n  \"lettertwo/occurrence.nvim\",\n  lazy = false,\n  ---@module \"occurrence\"\n  ---@type occurrence.Options\n  -- opts = {} -- setup is optional; the defaults will work out of the box.\n}\n```\n\n### Note on lazy loading:\n\n`occurrence.nvim` is designed to progressively load by default, so it is not necessary to configure the plugin manager for lazy loading.\nHowever, if you wish to explicitly control loading, you can disable the auto setup behavior:\n\n```lua\n{\n  \"lettertwo/occurrence.nvim\",\n  init = function()\n    -- Prevents automatic setup on load\n    vim.g.occurrence_auto_setup = false\n  end,\n  -- Explicitly load on specific keys.\n  keys = {\n    { \"go\", \"\u003cPlug\u003e(OccurrenceMark)\", mode = { \"n\", \"v\" }, desc = \"Mark occurrences\" },\n    { \"o\", \"\u003cPlug\u003e(OccurrenceModifyOperator)\", mode = \"o\", desc = \"Modify operator for occurrences\" },\n  },\n}\n```\n\n### Using :h vim.pack\n\n```lua\nvim.pack.add(\"lettertwo/occurrence.nvim\")\n-- require(\"occurrence\").setup({}) -- setup is optional; the defaults will work out of the box.\n```\n\n# Quick Start\n\nWith the default configuration, you can try these workflows to get a feel for `occurrence.nvim`.\n\n1. Install the plugin using your preferred package manager\n2. No configuration required - default keymaps work out of the box\n\n## Marking occurrences\n\nYou can enter 'occurrence mode' to mark occurrences and then operate on them:\n\n1. Place cursor on a word and press `go` to mark all occurrences\n2. Use `n`/`N` to navigate between marked occurrences\n3. Press `c` followed by a motion (e.g., `ip`) to change marked occurrences in that range\n4. Type your replacement text\n5. Press `\u003cEsc\u003e` to exit occurrence mode\n\nMarking occurrences can be done in several ways:\n\n- **Word under cursor**: Place cursor on a word and press `go` in normal mode\n- **Visual selection**: Select text in visual mode and press `go`\n- **Last search pattern**: After searching with `/pattern` or `?pattern`, press `go` in normal mode\n\nOnce occurrences are marked, you can navigate, add and remove them:\n\n- **Navigate**: Use `n`/`N` to jump between marked occurrences, or `gn`/`gN` for all occurrences\n- **Add matches**: `ga` to add a new occurrence\n- **Remove marks**: `gx` to unmark current occurrence\n- **Toggle individual marks**: `go` to toggle mark on current occurrence\n\n## Operating on occurrences\n\nWith occurrences marked, you can perform operations on them in several ways:\n\n1. **Choose operation**: Use vim operators like `c` (change), `d` (delete), `y` (yank) on marked occurrences\n2. **Choose range**: Use vim motions like `$`, `ip`, etc. to apply operator to occurrences in that range\n\nOr, you can use visual mode:\n\n1. **Start visual mode**: Press `v` to enter visual mode, or `V` for visual line mode\n2. **Select range**: Use vim motions to select a range\n3. **Choose operation**: Use vim operators like `c` (change), `d` (delete), `y` (yank) on marked occurrences in the visual range.\n\nWhen you're done, press `\u003cEsc\u003e` to exit occurrence mode and clear all marks.\n\n## Operator-pending mode\n\n**Alternative workflow:** Use operator-pending mode with `c`, `d`, or `y` followed by `o` and a motion (e.g., `doip` deletes word occurrences in the paragraph).\n\nYou can modify most vim operators to work on occurrences of the word under cursor:\n\n1. **Choose operator**: Start an operation like `c`, `d`, `y`\n2. **Modify operator**: Press `o` to enter occurrence operator-modifier mode\n3. **Choose range**: Use vim motions like `$`, `ip`, etc. to apply to occurrences in that range\n\n# Configuration\n\nThe plugin works with zero configuration but can be customized through `require(\"occurrence\").setup({...})`. Configuration options include:\n\n```lua\nrequire(\"occurrence\").setup({\n  -- Whether to include default keymaps.\n  --\n  -- If `false`, global keymaps, such as the default `go` to activate\n  -- occurrence mode, or the default `o` to modify a pending operator,\n  -- are not set, so activation keymaps must be set manually,\n  -- e.g., `vim.keymap.set(\"n\", \"\u003cleader\u003eo\", \"\u003cPlug\u003e(OccurrenceMark)\")``\n  -- or `vim.keymap.set(\"o\", \"\u003cC-o\u003e\", \"\u003cPlug\u003e(OccurrenceModifyOperator)\")`.\n  --\n  -- Additionally, when `false`, only keymaps explicitly defined in `keymaps`\n  -- will be automatically set when activating occurrence mode. Keymaps for\n  -- occurrence mode can also be set manually in an `OccurrenceActivate`\n  -- autocmd using `occurrence.keymap:set(...)`.\n  --\n  -- Default `operators` will still be set unless `default_operators` is also `false`.\n  --\n  -- Defaults to `true`.\n  default_keymaps = true,\n\n  -- Whether to include default operator support.\n  -- (c, d, y, p, gp, \u003c, \u003e, =, gu, gU, g~)\n  --\n  -- If `false`, only operators explicitly defined in `operators`\n  -- will be supported.\n  --\n  -- Defaults to `true`.\n  default_operators = true,\n\n  -- A table defining keymaps that will be active in occurrence mode.\n  -- Each key is a string representing the keymap, and each value is either:\n  --   - a string representing the name of a built-in API action,\n  --   - a table defining a custom keymap configuration,\n  --   - or `false` to disable the keymap.\n  keymaps = {\n    [\"n\"] = \"next\",                     -- Next marked occurrence\n    [\"N\"] = \"previous\",                 -- Previous marked occurrence\n    [\"gn\"] = \"match_next\",              -- Next occurrence (all matches)\n    [\"gN\"] = \"match_previous\",          -- Previous occurrence (all matches)\n    [\"go\"] = \"toggle\",                  -- Toggle or mark an occurrence\n    [\"ga\"] = \"mark\",                    -- Mark current occurrence\n    [\"gx\"] = \"unmark\",                  -- Unmark current occurrence\n    [\"\u003cEsc\u003e\"] = \"deactivate\",           -- Exit occurrence mode\n    [\"\u003cC-c\u003e\"] = \"deactivate\",           -- Exit occurrence mode\n    [\"\u003cC-[\u003e\"] = \"deactivate\",           -- Exit occurrence mode\n  },\n\n  -- A table defining operators that can be modified to operate on occurrences.\n  -- These operators will also be active as keymaps in occurrence mode.\n  -- Each key is a string representing either the operator key or\n  -- a custom operator name, and each value is either:\n  --   - a string representing the name of a builtin or custom operator,\n  --   - a table defining a custom operator configuration,\n  --   - or `false` to disable the operator.\n  operators = {\n    [\"c\"] = \"change\",             -- Change marked occurrences\n    [\"d\"] = \"delete\",             -- Delete marked occurrences\n    [\"y\"] = \"yank\",               -- Yank marked occurrences\n    [\"p\"] = \"put\",                -- Put register at marked occurrences\n    [\"gp\"] = \"distribute\",        -- Distribute lines from register across occurrences\n    [\"\u003c\"] = \"indent_left\",        -- Indent left\n    [\"\u003e\"] = \"indent_right\",       -- Indent right\n    [\"=\"] = \"indent_format\",      -- Indent/format\n    [\"gu\"] = \"lowercase\",         -- Convert to lowercase\n    [\"gU\"] = \"uppercase\",         -- Convert to uppercase\n    [\"g~\"] = \"swap_case\",         -- Swap case\n  },\n})\n```\n\n### Default Keymaps\n\nThese keymaps are set automatically when `default_keymaps = true`.\n\nNormal/Visual mode:\n\n- `go` - Find and mark occurrences (word/selection/search pattern)\n\nOperator-pending mode:\n\n- `o` - Occurrence operator modifier (e.g., `coip`, `do$`)\n\nOccurrence mode (after marking occurrences from normal/visual mode):\n\n- `n` / `N` - Next/previous marked occurrence\n- `gn` / `gN` - Next/previous occurrence (all matches)\n- `go` - Toggle mark on current occurrence or word\n- `ga` - Mark or current occurrence or add word\n- `gx` - Unmark current occurrence\n- `\u003cEsc\u003e`, `\u003cC-c\u003e`, `\u003cC-[\u003e` - Exit occurrence mode\n- All configured operators (`c`, `d`, `y`, `p`, `gp`, `\u003c`, `\u003e`, `=`, `gu`, `gU`, `g~`)\n\n## Keymaps\n\nYou can disable default keymaps and set up custom ones:\n\n```lua\nrequire(\"occurrence\").setup({\n  -- NOTE: If you disable default keymaps\n  -- you'll want a way to exit occurrence mode!\n  default_keymaps = false,  -- Disable defaults\n  keymaps = {\n    -- Custom navigation\n    [\"\u003cTab\u003e\"] = \"next\",\n    [\"\u003cS-Tab\u003e\"] = \"previous\",\n    [\"q\"] = \"deactivate\",  -- Exit occurrence mode\n  },\n})\n\n-- Set up custom keymaps using \u003cPlug\u003e mappings\nvim.keymap.set(\"n\", \"\u003cleader\u003eo\", \"\u003cPlug\u003e(OccurrenceMark)\")\n\n-- Or using the `:Occurrence` command:\nvim.keymap.set(\"v\", \"\u003cC-o\u003e\", \"\u003ccmd\u003eOccurrence toggle\u003cCR\u003e\")\n\n-- Or using Lua API:\nvim.keymap.set(\"o\", \"\u003cC-o\u003e\", function()\n  require('occurrence').modify_operator()\nend)\n\n-- Set up custom keymaps on occurrence activation.\n-- These keymaps will be buffer-local and active only in occurrence mode.\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OccurrenceActivate\",\n  callback = function(e)\n    local occurrence = require(\"occurrence\").get(e.buf)\n    if occurrence and not occurrence:is_disposed() then\n      -- Batch operations\n      occurrence.keymap:set(\"n\", \"\u003cleader\u003ea\", function()\n        assert(require(\"occurrence\").get()):mark_all()\n      end)\n      occurrence.keymap:set(\"n\", \"\u003cleader\u003ex\", function()\n        assert(require(\"occurrence\").get()):unmark_all()\n      end)\n    end\n  end,\n})\n```\n\n## Operators\n\nSimilarly to keymaps, you can disable default operators and set up custom ones.\n\n```lua\nrequire(\"occurrence\").setup({\n  default_operators = false,  -- Disable all defaults\n  operators = {\n    [\"c\"] = \"change\", -- Keep default change operator\n    [\"d\"] = \"delete\", -- Keep default delete operator\n    [\"g~\"] = false,  -- Disable swap case (if `default_operators` were `true`)\n    -- Define a custom operator:\n    [\"upper_first\"] = {\n      desc = \"Uppercase first letter\",\n      ---@type occurrence.OperatorFn\n      operator = function(current)\n        local text = current.text\n        text[1] = text[1]:gsub(\"^%l\", string.upper)\n        return text\n      end\n    },\n    -- and bind it to a key:\n    [\"gU\"] = \"upper_first\",\n    -- or define it to a key directly:\n    [\"gu\"] = {\n      desc = \"Lowercase first letter\",\n      ---@type occurrence.OperatorFn\n      operator = function(current)\n        local text = current.text\n        text[1] = text[1]:gsub(\"^%u\", string.lower)\n        return text\n      end\n    },\n  },\n})\n```\n\nFor more on defining custom operators, see [Custom Operators](#custom-operators).\n\n## Highlights\n\noccurrence.nvim uses three highlight groups for visual feedback:\n\n- **`OccurrenceMatch`**: All occurrence matches (default: links to `Search`)\n- **`OccurrenceMark`**: Marked occurrences (default: links to `IncSearch`)\n- **`OccurrenceCurrent`**: Current occurrence (default: links to `CurSearch`)\n\nYou can customize these highlight groups in your configuration:\n\n```lua\n-- Example: Bold and underlined for emphasis\nvim.api.nvim_set_hl(0, \"OccurrenceMatch\", {})\nvim.api.nvim_set_hl(0, \"OccurrenceMark\", { bold = true, underline = true })\nvim.api.nvim_set_hl(0, \"OccurrenceCurrent\", { bold = true, underline = true, reverse = true })\n```\n\n## Statusline Integration\n\nDisplay occurrence count in your statusline similar to Neovim's search count using the `status()` API:\n\n```lua\n-- Example: lualine component\nlocal occurrence_status = {\n  function()\n    local count = require('occurrence').status()\n    if not count then\n      return \"\"\n    end\n    return string.format(\"[%d/%d]\", count.current, count.total)\n  end,\n  cond = function()\n    -- Only show if occurrence.nvim is loaded\n    return package.loaded[\"occurrence\"] ~= nil\n  end,\n}\n\nrequire('lualine').setup({\n  sections = {\n    lualine_c = { 'filename', occurrence_status },\n  }\n})\n```\n\nThe `status()` function returns `nil` if there is no active occurrence. Otherwise, it returns:\n\n- `current`: Current match index\n- `total`: Total number of matches\n- `exact_match`: 1 if cursor is on a match, 0 otherwise\n- `marked_only`: Whether counting only marked occurrences\n\n# Usage Examples\n\nSome examples of possible workflows using `occurrence.nvim`.\n\n### Example: Selective Editing\n\nChange only some occurrences of a word:\n\n```vim\n\" Buffer: The quick brown fox jumps over the lazy dog.\n\"         The fox is quick and the dog is lazy.\n\"         Another fox and dog appear here.\n\ngo          \" Mark all occurrences of 'fox' (cursor on first 'fox')\nn           \" Navigate to next occurrence (line 2)\ngx          \" Unmark this one (skip it)\nn           \" Navigate to next (line 3)\ncip         \" 'c'hange marked occurrences 'i'n 'p'aragraph\nwolf        \" Type replacement\n\u003cEsc\u003e       \" Exit and clear marks\n\n\" Result: 'fox' on lines 1 and 3 changed to 'wolf', line 2 unchanged\n```\n\n### Example: Working with Search Patterns\n\nMark occurrences from last search pattern:\n\n```vim\n\" Buffer: The quick brown fox jumps over the lazy dog.\n\"         The fox is quick and the dog is lazy.\n\"         Another fox and dog appear here.\n\n/\\\u003c...\\\u003e    \" Search for 3-letter words (the, fox, the, dog, and)\ngo          \" Mark all occurrences matching the search pattern\ngggUG       \" Uppercase all marked occurrences: 'gg' to start, 'gU' uppercase, 'G' to end\n\n\" Result: THE quick brown FOX jumps over THE lazy DOG.\n\"         THE FOX is quick AND THE DOG is lazy.\n\"         Another FOX AND DOG appear here.\n```\n\n### Example: Working with Multiple Patterns\n\nMark different words and edit them together:\n\n```vim\n\" Buffer: foo is here and bar is there\n\"         foo and bar together\n\"         only foo here\n\"         only bar there\n\ngo          \" Mark all 'foo' occurrences (cursor on first 'foo')\nj2w         \" Move cursor to 'bar' on line 2\nga          \" Mark all 'bar' occurrences as well\ncip         \" 'c'hange all marked occurrences 'i'n 'p'aragraph\ntest\u003cCR\u003e    \" Type replacement\n\u003cEsc\u003e       \" Exit\n\n\" Result: test is here and test is there\n\"         test and test together\n\"         only test here\n\"         only test there\n```\n\n### Example: Yanking and Putting Occurrences\n\nYank marked occurrences and paste at different locations:\n\n```vim\n\" Buffer: SOURCE SOURCE SOURCE\n\"         dest dest dest\n\ngo          \" Mark 'SOURCE' occurrences (cursor on first SOURCE)\ny$          \" Yank all marked occurrences to end of line\n\u003cEsc\u003e       \" Exit occurrence mode\nj^          \" Move to line 2, first column\ngo          \" Mark all 'dest' occurrences\np$          \" Put yanked content at all marked locations\n\n\" Result: Multi-line content replaces each dest\n\"         Each dest becomes: SOURCE\n\"                            SOURCE\n\"                            SOURCE\n```\n\n### Example: Distributing Values\n\nThe `distribute` operator (`gp`) cycles through lines from a register when pasting, giving each occurrence a different value. This is useful for refactoring or batch renaming with distinct values.\n\n```vim\n\" Buffer: alpha foo beta bar gamma bat\n\"         foo dest bar dest bat dest\n\n\" 1. Mark and yank source values using search pattern\n/\\(alpha\\|beta\\|gamma\\)\u003cCR\u003e  \" Search for the three values\ngo                           \" Mark all matching occurrences\nVy                           \" Yank marked values (creates \"alpha\\nbeta\\ngamma\")\n\u003cEsc\u003e                        \" Exit occurrence mode\n\n\" 2. Distribute values to destinations\n/\\(foo\\|bar\\|bat\\)\u003cCR\u003e       \" Search for the three dest values\ngo                           \" Mark all matching occurrences\njVgp                         \" Move to line2 and Distribute - cycles through yanked lines\n\n\" Result: alpha foo beta bar gamma bat\n\"         alpha dest beta dest gamma dest\n\" (Each dest gets a different value: first-\u003ealpha, second-\u003ebeta, third-\u003egamma)\n```\n\n**Difference between `p` and `gp`:**\n\n- `p` (put): Replicates the same text at each occurrence\n- `gp` (distribute): Cycles through lines in the register, giving each occurrence a different line\n\n# API Reference\n\n## Lua API\n\noccurrence.nvim provides a Lua API for programmatic control:\n\nsetup\n: `require('occurrence').setup(opts)`\n\nConfigure the plugin. See [Configuration](#configuration) for available options.\n**Note:** calling `setup()` is **not required** unless you intend to customize settings!\n\nstatus\n: `require('occurrence').status(opts)`\n\nGet occurrence count information for statusline (or other) integrations.\n\n**Parameters:**\n\n- `opts` (table, optional):\n  - `marked` (boolean): Count only marked occurrences (default: `false`)\n  - `buffer` (integer): Buffer number (default: current buffer)\n\n**Returns:**\n\n- `nil` if no active occurrence\n- or a table with fields:\n  - `current` (integer): Current match index (1-based)\n  - `total` (integer): Total number of matches\n  - `exact_match` (integer): 1 if cursor is exactly on a match, 0 otherwise\n  - `marked_only` (boolean): Whether counting only marked occurrences\n\n## Actions\n\nAll actions are available in three ways:\n\n- **Lua API**:\n\n  ```lua\n  require('occurrence').mark()\n  ```\n\n- **Vim commands**:\n\n  ```vim\n  :Occurrence mark\n  ```\n\n- **\u003cPlug\u003e mappings**:\n\n  ```vim\n  \u003cPlug\u003e(OccurrenceMark)\n  ```\n\n**modify_operator**\n\n: `require('occurrence').modify_operator()`  \n`:Occurrence modify_operator`  \n`\u003cPlug\u003e(OccurrenceModifyOperator)`\n\nModify a pending operator to act on occurrences of the word under the cursor. Only useful in operator-pending mode (e.g., `c`, `d`, etc.)\n\nOnce a pending operator is modified, the operator will act on occurrences within the range specified by the subsequent motion.\n\nNote that this action does not activate occurrence mode, and it does not have any effect when occurrence mode is active, as operators already act on occurrences in that mode.\n\n**mark**\n\n: `require('occurrence').mark()`  \n`:Occurrence mark`  \n`\u003cPlug\u003e(OccurrenceMark)`\n\nMark one or more occurrences and activate occurrence mode.\n\nIf occurrence already has matches, mark matches based on:\n\n- In visual mode, if matches exist in the range of the visual selection, mark those matches.\n- Otherwise, if a match exists at the cursor, mark that match.\n\nIf no occurrence match exists to satisfy the above, add a new pattern based on:\n\n- In visual mode, mark occurrences of the visual selection.\n- If `:h hlsearch` is active, mark occurrences of the search pattern.\n- Otherwise, mark occurrences of the word under the cursor.\n\n**unmark**\n\n: `require('occurrence').unmark()`  \n`:Occurrence unmark`  \n`\u003cPlug\u003e(OccurrenceUnmark)`\n\nUnmark one or more occurrences.\n\nIf occurrence has matches, unmark matches based on:\n\n- In visual mode, unmark matches in the range of the visual selection.\n- Otherwise, if a match exists at the cursor, unmark that match.\n\nIf no match exists to satisfy the above, does nothing.\n\n**toggle**\n\n: `require('occurrence').toggle()`  \n`:Occurrence toggle`  \n`\u003cPlug\u003e(OccurrenceToggle)`\n\nMark or unmark one (or more) occurrence(s) and activate occurrence mode.\n\nIf occurrence already has matches, toggle matches based on:\n\n- In visual mode, if matches exist in the range of the visual selection, toggle marks on those matches.\n- Otherwise, if a match exists at the cursor, toggle that mark.\n\nIf no occurrence match exists to satisfy the above, add a new pattern based on:\n\n- In visual mode, mark the closest occurrence of the visual selection.\n- If `:h hlsearch` is active, mark the closest occurrence of the search pattern.\n- Otherwise, mark the closest occurrence of the word under the cursor.\n\n**next**\n\n: `require('occurrence').next()`  \n`:Occurrence next`  \n`\u003cPlug\u003e(OccurrenceNext)`\n\nMove to the next marked occurrence and activate occurrence mode.\n\nIf occurrence has no matches, acts like `mark` and then moves to the next marked occurrence.\n\n**previous**\n\n: `require('occurrence').previous()`  \n`:Occurrence previous`  \n`\u003cPlug\u003e(OccurrencePrevious)`\n\nMove to the previous marked occurrence and activate occurrence mode.\n\nIf occurrence has no matches, acts like `mark` and then moves to the previous marked occurrence.\n\n**match_next**\n\n: `require('occurrence').match_next()`  \n`:Occurrence match_next`  \n`\u003cPlug\u003e(OccurrenceMatchNext)`\n\nMove to the next occurrence match, whether marked or unmarked, and activate occurrence mode.\n\nIf occurrence has no matches, acts like `mark` and then moves to the next occurrence match.\n\n**match_previous**\n\n: `require('occurrence').match_previous()`  \n`:Occurrence match_previous`  \n`\u003cPlug\u003e(OccurrenceMatchPrevious)`\n\nMove to the previous occurrence match, whether marked or unmarked, and activate occurrence mode.\n\nIf occurrence has no matches, acts like `mark` and then moves to the previous occurrence match.\n\n**deactivate**\n\n: `require('occurrence').deactivate()`  \n`:Occurrence deactivate`  \n`\u003cPlug\u003e(OccurrenceDeactivate)`\n\nClear all marks and patterns, and deactivate occurrence mode.\n\n# Builtin Operators\n\nThe following operators are supported via `modify_operator` or with marked occurrences (configured via `operators` table):\n\n| Operator        | Key  | Description                                                       |\n| --------------- | ---- | ----------------------------------------------------------------- |\n| `change`        | `c`  | Change marked occurrences (prompts for replacement)               |\n| `delete`        | `d`  | Delete marked occurrences                                         |\n| `yank`          | `y`  | Yank marked occurrences to register                               |\n| `put`           | `p`  | Put register content at marked occurrences (replicates same text) |\n| `distribute`    | `gp` | Distribute lines from register cyclically across occurrences      |\n| `indent_left`   | `\u003c`  | Indent left                                                       |\n| `indent_right`  | `\u003e`  | Indent right                                                      |\n| `indent_format` | `=`  | Format through `:h equalprg`                                      |\n| `uppercase`     | `gU` | Convert to uppercase                                              |\n| `lowercase`     | `gu` | Convert to lowercase                                              |\n| `swap_case`     | `g~` | Swap case                                                         |\n\nThese operators are also available as API methods, e.g.,:\n\n```lua\nrequire(\"occurrence\").change()\nrequire(\"occurrence\").delete()\n```\n\nAnd as subcommands, e.g.,:\n\n```vim\n:Occurrence change\n:Occurrence delete\n```\n\n# Custom Operators\n\nYou can define custom operators to work with occurrences by configuring the `operators` table in `require(\"occurrence\").setup({...})`.\n\n**Key points about operators:**\n\n- Async operations are sized to 10 concurrent operations by default (configurable via `batch_size`)\n- The `before` hook runs once before processing marks\n- The `operator` function runs for each mark\n- The `after` hook runs once after all marks have been processed\n- The `before` and `operator` can be async by returning a function that accepts `done`\n- The `after` hook receives updated mark positions for post-processing\n\nSee the [Configuration](#configuration) section for simple examples of defining custom operators.\n\nSee the [Custom Operators and Integrations](https://github.com/lettertwo/occurrence.nvim/wiki/Custom-Operators-and-Integrations) wiki for complete documentation and advanced examples.\n\n# Events\n\noccurrence.nvim triggers custom User events that you can listen to with autocommands. These events allow you to react to occurrence lifecycle changes and integrate with other plugins or workflows.\n\n## OccurrenceCreate\n\nTriggered when an occurrence instance is first created for a buffer.\n\n**When it fires:**\n\n- First time an occurrence action is used in a buffer\n- When `Occurrence.get(bufnr)` creates a new instance\n\n**Does NOT fire:**\n\n- When occurrence mode is activated (use `OccurrenceActivate` instead)\n- When patterns or marks are added to an existing occurrence\n\n**Event data:**\n\n- `buf` (integer): Buffer number where occurrence was created\n\n**Example:**\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OccurrenceCreate\",\n  callback = function(event)\n    -- get the occurrence instance\n    local occurrence = require(\"occurrence\").get(event.buf)\n    print(\"Occurrence created in buffer \" .. event.buf)\n    print(\"Initial pattern: \" .. vim.inspect(occurrence.patterns))\n  end,\n})\n```\n\n## OccurrenceActivate\n\nTriggered when occurrence mode is activated in a buffer.\n\n**When it fires:**\n\n- When occurrence mode keymaps are activated\n- After an occurrence-mode action completes successfully (e.g., `mark`, `toggle`)\n\n**Does NOT fire:**\n\n- When occurrence instance is created without activating mode\n- When already in occurrence mode\n- When using operator-modifier mode (`doip`)\n\n**Event data:**\n\n- `buf` (integer): Buffer number where occurrence mode was activated\n\n**Example:**\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OccurrenceActivate\",\n  callback = function(event)\n    local occurrence = require(\"occurrence\").get(event.buf)\n    if occurrence and not occurrence:is_disposed() then\n      -- Set up buffer-local keymaps that are only active in occurrence mode\n      occurrence.keymap:set(\"n\", \"\u003cleader\u003ea\", function()\n        assert(require(\"occurrence\").get()):mark_all()\n      end, { desc = \"Mark all occurrences\" })\n\n      occurrence.keymap:set(\"n\", \"\u003cleader\u003ex\", function()\n        assert(require(\"occurrence\").get()):unmark_all()\n      end, { desc = \"Unmark all occurrences\" })\n    end\n  end,\n})\n```\n\n## OccurrenceUpdate\n\nTriggered when an occurrence instance is updated with new patterns or marks.\n\n**When it fires:**\n\n- When new patterns are added\n- When marks are added or removed\n\n**Does NOT fire:**\n\n- When occurrence instance is created without patterns or marks\n- When occurrence mode is activated (use `OccurrenceActivate` instead)\n- When occurrence instance is disposed (use `OccurrenceDispose` instead)\n\n**Event data:**\n\n- `buf` (integer): Buffer number where occurrence was updated\n\n**Example:**\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OccurrenceUpdate\",\n  callback = function(event)\n    print(vim.inspect(require(\"occurrence\").status({ buffer = event.buf })))\n  end,\n})\n```\n\n## OccurrenceDispose\n\nTriggered when an occurrence instance is disposed and its resources are cleaned up.\n\n**When it fires:**\n\n- When exiting occurrence mode (e.g., pressing `\u003cEsc\u003e` or `q`)\n- When `Occurrence.del(bufnr)` is called\n- When the buffer is deleted\n- When all marks are cleared and occurrence is no longer needed\n\n**Does NOT fire:**\n\n- When marks are cleared but occurrence mode remains active\n\n**Event data:**\n\n- `buf` (integer): Buffer number where occurrence was disposed\n\n**Example:**\n\n```lua\nvim.api.nvim_create_autocmd(\"User\", {\n  pattern = \"OccurrenceDispose\",\n  callback = function(event)\n    print(\"Occurrence disposed in buffer \" .. event.buf)\n    -- Clean up any custom state associated with this occurrence\n  end,\n})\n```\n\n# Command Usage\n\nThe `:Occurrence` command provides access to all builtin actions and operators. It features basic completion for subcommands, count and range modifiers for fine-grained control, and arguments for specific actions.\n\nActions can be invoked via the `:Occurrence` command:\n\n```vim\n:Occurrence mark          \" Mark occurrences of word under cursor\n:Occurrence toggle        \" Toggle mark at cursor position\n:Occurrence next          \" Navigate to next marked occurrence\n:Occurrence deactivate    \" Clear all marks\n```\n\nAnd some can take arguments:\n\n```vim\n:Occurrence mark \\w\\s\\s    \" Mark occurrences of a pattern\n:Occurrence toggle foo     \" Toggle the next occurrence of 'foo'\n:Occurrence next 2         \" Move to the 2nd next marked occurrence\n```\n\nOperators will trigger operator-pending mode and then operate on marked occurrences:\n\n```vim\n:Occurrence delete        \" Delete all marked occurrences\n:Occurrence change        \" Change all marked occurrences (prompts for input)\n:Occurrence yank          \" Yank all marked occurrences to register\n:Occurrence uppercase     \" Convert all marked to uppercase\n```\n\nAnd any that use a register can specify which register to use:\n\n```vim\n:Occurrence delete b      \" Delete all marked occurrences to register 'b'\n:Occurrence put b         \" Put register 'b' content at all marked occurrences\n```\n\n### Count Modifier\n\nPrefix the command with a count to limit operations to the first N marked occurrences:\n\n```vim\n:3Occurrence delete       \" Delete only the first 3 marked occurrences\n:5Occurrence yank         \" Yank only the first 5 marked occurrences\n:2Occurrence uppercase    \" Uppercase only the first 2 marked occurrences\n```\n\nOr to limit the number of marked occurrences:\n\n```vim\n:4Occurrence mark       \" Mark only the first 4 occurrences\n:3Occurrence next       \" Navigate to the 3rd marked occurrence\n```\n\n### Range Modifier\n\nUse a range to operate only on marked occurrences within specific lines:\n\n```vim\n:2,5Occurrence delete     \" Delete marks only in lines 2-5\n:'\u003c,'\u003eOccurrence change   \" Change marks only in visual selection\n```\n\n# Custom Integrations\n\noccurrence.nvim can be integrated with other plugins to create powerful workflows through flexible action, operator, and event systems that provide easy access to the occurrence API.\n\nSee the [wiki](https://github.com/lettertwo/occurrence.nvim/wiki) for detailed examples, including:\n\n- **[Multicursor](https://github.com/lettertwo/occurrence.nvim/wiki/Integration:-Multicursor-nvim)** - Spawn multiple cursors at marked occurrences using [multicursor.nvim](https://github.com/jake-stewart/multicursor.nvim)\n- **[Surround](https://github.com/lettertwo/occurrence.nvim/wiki/Integration:-Mini-surround)** - Surround marked occurrences with quotes, brackets, or tags using [mini.surround](https://github.com/echasnovski/mini.surround)\n- **[Snacks.picker](https://github.com/lettertwo/occurrence.nvim/wiki/Integration:-Snacks-picker)** - Select and manipulate marked occurrences using [snacks.nvim picker](https://github.com/folke/snacks.nvim/blob/main/docs/picker.md)\n\n## Building Your Own\n\nIntegrations leverage:\n\n- **Custom operators** - Call into other plugins from occurrence operators\n- **Before hooks** - Setup phase for user prompts or state initialization\n- **Async operators** - Handle async plugin APIs\n- **Events** - React to occurrence lifecycle changes\n\nSee [Custom Operators](#custom-operators) and [Events](#events) for the building blocks, or check the wiki for complete examples.\n\nHave an integration to share? Please contribute to the [wiki](https://github.com/lettertwo/occurrence.nvim/wiki)!\n\n\u003c!-- panvimdoc-ignore-start --\u003e\n\n# Development\n\nSee [CONTRIBUTING](./CONTRIBUTING.md) for contribution guidelines.\n\n# License\n\n[MIT](./LICENSE)\n\n[vim-mode-plus]: https://github.com/t9md/atom-vim-mode-plus?tab=readme-ov-file#some-features\n\n\u003c!-- panvimdoc-ignore-end --\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flettertwo%2Foccurrence.nvim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flettertwo%2Foccurrence.nvim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flettertwo%2Foccurrence.nvim/lists"}