{"id":13412781,"url":"https://github.com/chrisgrieser/nvim-spider","last_synced_at":"2025-05-15T08:10:40.004Z","repository":{"id":147646070,"uuid":"616465119","full_name":"chrisgrieser/nvim-spider","owner":"chrisgrieser","description":"Use the w, e, b motions like a spider. Move by subwords and skip insignificant punctuation.","archived":false,"fork":false,"pushed_at":"2025-03-25T13:02:50.000Z","size":208,"stargazers_count":720,"open_issues_count":4,"forks_count":14,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-07T04:05:29.563Z","etag":null,"topics":["camelcase","editing-support","nvim-plugin"],"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/chrisgrieser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"custom":"https://www.paypal.me/ChrisGrieser","ko_fi":"pseudometa"}},"created_at":"2023-03-20T12:51:48.000Z","updated_at":"2025-04-06T17:06:27.000Z","dependencies_parsed_at":"2024-03-15T14:30:53.524Z","dependency_job_id":"3cc0d7f7-d556-44e5-ac88-36b462173472","html_url":"https://github.com/chrisgrieser/nvim-spider","commit_stats":{"total_commits":230,"total_committers":9,"mean_commits":"25.555555555555557","dds":0.06956521739130439,"last_synced_commit":"b1c542a78522d59432a827f6ec2b28f9422c7e7f"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":"chrisgrieser/nvim-pseudometa-plugin-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-spider","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-spider/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-spider/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/chrisgrieser%2Fnvim-spider/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/chrisgrieser","download_url":"https://codeload.github.com/chrisgrieser/nvim-spider/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248890244,"owners_count":21178386,"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":["camelcase","editing-support","nvim-plugin"],"created_at":"2024-07-30T20:01:29.126Z","updated_at":"2025-05-15T08:10:39.993Z","avatar_url":"https://github.com/chrisgrieser.png","language":"Lua","funding_links":["https://www.paypal.me/ChrisGrieser","https://ko-fi.com/pseudometa","https://ko-fi.com/Y8Y86SQ91'"],"categories":["Motion","Lua"],"sub_categories":["GitHub","GitLab"],"readme":"\u003c!-- LTeX: enabled=false --\u003e\n# nvim-spider 🕷️🕸️\n\u003c!-- LTeX: enabled=true --\u003e\n\u003ca href=\"https://dotfyle.com/plugins/chrisgrieser/nvim-spider\"\u003e\u003cimg alt=\"badge\" src=\"https://dotfyle.com/plugins/chrisgrieser/nvim-spider/shield\"/\u003e\u003c/a\u003e\n\nUse the `w`, `e`, `b` motions like a spider. Move by subwords and skip\ninsignificant punctuation.\n\n\u003c!-- toc --\u003e\n\n- [Features](#features)\n\t* [Subword motion](#subword-motion)\n\t* [Skipping insignificant punctuation](#skipping-insignificant-punctuation)\n- [Installation](#installation)\n- [Configuration](#configuration)\n\t* [Basic configuration](#basic-configuration)\n\t* [Advanced: custom movement patterns](#advanced-custom-movement-patterns)\n- [Special cases](#special-cases)\n\t* [UTF-8 support](#utf-8-support)\n\t* [Subword text object](#subword-text-object)\n\t* [Operator-pending mode: the case of `cw`](#operator-pending-mode-the-case-of-cw)\n\t* [Consistent operator-pending mode](#consistent-operator-pending-mode)\n\t* [Motions in insert mode](#motions-in-insert-mode)\n- [Credits](#credits)\n\n\u003c!-- tocstop --\u003e\n\n## Features\nThe `w`, `e`, `b` (and `ge`) motions work the same as the default ones by vim,\nexcept for two differences:\n\n### Subword motion\nThe motions are based on subwords, meaning they stop at the segments of a\n`camelCase`, `SNAKE_CASE`, or `kebab-case` variable.\n\n```lua\n-- positions vim's `w` will move to\nlocal myVariableName = FOO_BAR_BAZ\n--    ^              ^ ^\n\n-- positions spider's `w` will move to\nlocal myVariableName = FOO_BAR_BAZ\n--    ^ ^       ^    ^ ^   ^   ^\n```\n\n### Skipping insignificant punctuation\nA sequence of one or more punctuation characters is considered significant if it\nis surrounded by whitespace and does not include any non-punctuation characters.\n\n```lua\nfoo == bar .. \"baz\"\n--  ^      ^    significant punctuation\n\nfoo:find(\"a\")\n-- ^    ^  ^  insignificant punctuation\n```\n\nThis speeds up the movement across the line by reducing the number of mostly\nunnecessary stops.\n\n```lua\n-- positions vim's `w` will move to\nif foo:find(\"%d\") and foo == bar then print(\"[foo] has\" .. bar) end\n-- ^  ^^   ^  ^^  ^   ^   ^  ^   ^    ^    ^  ^  ^ ^  ^ ^  ^  ^ ^  -\u003e 21\n\n-- positions spider's `w` will move to\nif foo:find(\"%d\") and foo == bar then print(\"[foo] has\" .. bar) end\n-- ^   ^      ^   ^   ^   ^  ^   ^    ^       ^    ^    ^  ^    ^  -\u003e 14\n```\n\nIf you prefer to use this plugin only for subword motions, you can disable this\nfeature by setting `skipInsignificantPunctuation = false` in the `.setup()`\ncall.\n\n\u003e [!NOTE]\n\u003e This plugin ignores vim's `iskeyword` option.\n\n## Installation\n\n```lua\n-- packer\nuse { \"chrisgrieser/nvim-spider\" }\n\n-- lazy.nvim\n{ \"chrisgrieser/nvim-spider\", lazy = true },\n\n-- vim-plug\nPlug(\"chrisgrieser/nvim-spider\")\n```\n\nNo keybindings are created by default. Below are the mappings to replace the\ndefault `w`, `e`, and `b` motions with this plugin's version of them.\n\n```lua\nvim.keymap.set({ \"n\", \"o\", \"x\" }, \"w\", \"\u003ccmd\u003elua require('spider').motion('w')\u003cCR\u003e\")\nvim.keymap.set({ \"n\", \"o\", \"x\" }, \"e\", \"\u003ccmd\u003elua require('spider').motion('e')\u003cCR\u003e\")\nvim.keymap.set({ \"n\", \"o\", \"x\" }, \"b\", \"\u003ccmd\u003elua require('spider').motion('b')\u003cCR\u003e\")\n\n-- OR: lazy-load on keystroke (lazy.nvim)\n{\n\t\"chrisgrieser/nvim-spider\",\n\tkeys = {\n\t\t{ \"w\", \"\u003ccmd\u003elua require('spider').motion('w')\u003cCR\u003e\", mode = { \"n\", \"o\", \"x\" } },\n\t\t{ \"e\", \"\u003ccmd\u003elua require('spider').motion('e')\u003cCR\u003e\", mode = { \"n\", \"o\", \"x\" } },\n\t\t{ \"b\", \"\u003ccmd\u003elua require('spider').motion('b')\u003cCR\u003e\", mode = { \"n\", \"o\", \"x\" } },\n\t},\n},\n```\n\n\u003e [!NOTE]\n\u003e For dot-repeat to work, you have to call the motions as Ex-commands.\n\u003e Dot-repeat will not work when using `function() require(\"spider\").motion(\"w\")\n\u003e end` as third argument.\n\n## Configuration\n\n### Basic configuration\nThe `.setup()` call is optional.\n\n```lua\n-- default values\nrequire(\"spider\").setup {\n\tskipInsignificantPunctuation = true,\n\tconsistentOperatorPending = false, -- see \"Consistent Operator-pending Mode\" in the README\n\tsubwordMovement = true,\n\tcustomPatterns = {}, -- check \"Custom Movement Patterns\" in the README for details\n}\n```\n\nYou can also pass this configuration table to the `motion` function:\n\n```lua\nrequire(\"spider\").motion(\"w\", { skipInsignificantPunctuation = false })\n```\n\nAny options passed here will be used, and override the options set in the\n`setup()` call.\n\n### Advanced: custom movement patterns\nYou can use the `customPatterns` table to define custom movement patterns. These\nmust be [lua patterns](https://www.lua.org/manual/5.4/manual.html#6.4.1), and\nthey must be symmetrical (work the same backwards and forwards) to work\ncorrectly with `b` and `ge`. If multiple patterns are given, the motion searches\nfor all of them and stops at the closest one. When there is no match, the search\ncontinues in the next line.\n\nIf you have interesting ideas for custom patterns, please share them in the\n[GitHub discussions](./discussions), or make a PR to add them as built-in\noptions.\n\nA few examples:\n\n```lua\n-- The motion stops only at numbers.\nrequire(\"spider\").motion(\"w\", {\n\tcustomPatterns = { \"%d+\" },\n})\n\n-- The motion stops only at words with 3 or more chars or at any punctuation.\n-- (Lua patterns have no quantifier like `{3,}`, thus the repetition.)\nrequire(\"spider\").motion(\"w\", {\n\tcustomPatterns = { \"%w%w%w+\", \"%p+\" },\n})\n\n-- The motion stops only at hashes like `ef82a2`\n-- (here avoiding repetition by using `string.rep()`)\n-- Extend default patterns by passing a `patterns` table and\n-- setting `overrideDefault` to false.\nrequire(\"spider\").motion(\"w\", {\n\tcustomPatterns = {\n\t\tpatterns = { (\"%x\"):rep(6) .. \"+\" } },\n\t\toverrideDefault = false,\n\t},\n})\n\n-- The motion stops at the next declaration of a javascript variable.\n-- (The `e` motion combined with the `.` matching any character in\n-- lua patterns ensures that you stop at beginning of the variable name.)\nrequire(\"spider\").motion(\"e\", {\n\tcustomPatterns = { \"const .\", \"let .\", \"var .\" },\n})\n```\n\n\u003e [!NOTE]\n\u003e The `customPatterns` option overrides `nvim-spider`'s default behavior,\n\u003e meaning subword movement and skipping of punctuation are disabled. You can add\n\u003e `customPatterns` as an option to the `.motion` call to create new motions,\n\u003e while still having access `nvim-spider`'s default behavior. Pass a patterns\n\u003e table and set `overrideDefault = false` to extend `nvim-spider`'s default\n\u003e behavior with a new pattern.\n\n## Special cases\n\n### UTF-8 support\nFor adding UTF-8 support for matching non-ASCII text, add `luautf8` as rocks.\nYou can do so directly in `packer.nvim` or via dependency on `nvim_rocks` in\n`lazy.nvim`.\n\n```lua\n-- packer\n{ \"chrisgrieser/nvim-spider\", rocks = \"luautf8\" }\n\n-- lazy.nvim\n{\n    \"chrisgrieser/nvim-spider\",\n    lazy = true,\n    dependencies = {\n    \t\"theHamsta/nvim_rocks\",\n    \tbuild = \"pip3 install --user hererocks \u0026\u0026 python3 -mhererocks . -j2.1.0-beta3 -r3.0.0 \u0026\u0026 cp nvim_rocks.lua lua\",\n    \tconfig = function() require(\"nvim_rocks\").ensure_installed(\"luautf8\") end,\n    },\n},\n```\n\n### Subword text object\nThis plugin supports `w`, `e`, and `b` in operator-pending mode, but does not\ninclude a subword variant of `iw`. For a version of `iw` that considers\ncamelCase, check out the `subword` text object of\n[nvim-various-textobjs](https://github.com/chrisgrieser/nvim-various-textobjs).\n\n\u003c!-- vale Google.FirstPerson = NO --\u003e\n### Operator-pending mode: the case of `cw`\nIn operator pending mode, vim's `web` motions are actually a bit inconsistent.\nFor instance, `cw` will change to the *end* of a word instead of the start of\nthe next word, like `dw` does. This is probably done for convenience in `vi`'s\nearly days before there were text objects. In my view, this is quite problematic\nsince it makes people habitualize inconsistent motion behavior.\n\nIn this plugin, such small inconsistencies are therefore deliberately not\nimplemented. Apart from the inconsistency, such a behavior can create unexpected\nresults when used in subwords or near punctuation. If you nevertheless prefer\nthat behavior, you can achieve that behavior by mapping `cw` to `ce`:\n\n```lua\nvim.keymap.set(\"o\", \"w\", \"\u003ccmd\u003elua require('spider').motion('w')\u003cCR\u003e\")\nvim.keymap.set(\"n\", \"cw\", \"ce\", { remap = true })\n\n-- OR in one mapping\nvim.keymap.set(\"n\", \"cw\", \"c\u003ccmd\u003elua require('spider').motion('e')\u003cCR\u003e\")\n```\n\n### Consistent operator-pending mode\nVim has more inconsistencies related to how the motion range is\ninterpreted (see `:h exclusive`). For example, if the end of the motion is at\nthe beginning of a line, the endpoint is moved to the last character of the\nprevious line.\n\n```lua\nfoo bar\n--  ^\nbaz\n```\n\nTyping `dw` deletes only `bar`. `baz` stays on the next line.\n\nSimilarly, if the start of the motion is before or at the first non-blank\ncharacter in a line, and the end is at the beginning of a line, the motion\nis changed to `linewise`.\n\n```lua\n    foo\n--  ^\nbar\n```\n\nTyping `yw` yanks `foo\\r`, that is, the indentation before the cursor is included,\nand the register type is set to `linewise`.\n\nSetting `consistentOperatorPending = true` removes these special cases. In the\nfirst example, `bar\\r` would be deleted charwise. In the second example, `foo\\r`\nwould be yanked charwise.\n\n**Caveats**\n1. Last visual selection marks (`` `[ `` and `` `] ``) are updated\n   and point to the endpoints of the motion. This was not always the case before.\n2. Forced blockwise motion may be canceled if it cannot be correctly\n   represented with the current `selection` option.\n\n### Motions in insert mode\nSimple and pragmatic: Wrap the normal mode motions in `\u003cEsc\u003el` and `i`. (Drop\nthe `l` on backwards motions.)\n\n```lua\nvim.keymap.set(\"i\", \"\u003cC-f\u003e\", \"\u003cEsc\u003el\u003ccmd\u003elua require('spider').motion('w')\u003cCR\u003ei\")\nvim.keymap.set(\"i\", \"\u003cC-b\u003e\", \"\u003cEsc\u003e\u003ccmd\u003elua require('spider').motion('b')\u003cCR\u003ei\")\n```\n\n## Credits\n**Thanks**  \n- `@vypxl` and `@ii14` [for figuring out dot-repeatability of\n  textobjects](https://github.com/chrisgrieser/nvim-spider/pull/4).\n- `@vanaigr` for a large contribution regarding operator-pending mode.\n\n**About the developer**  \nIn my day job, I am a sociologist studying the social mechanisms underlying the\ndigital economy. For my PhD project, I investigate the governance of the app\neconomy and how software ecosystems manage the tension between innovation and\ncompatibility. If you are interested in this subject, feel free to get in touch.\n\n- [Website](https://chris-grieser.de/)\n- [Mastodon](https://pkm.social/@pseudometa)\n- [ResearchGate](https://www.researchgate.net/profile/Christopher-Grieser)\n- [LinkedIn](https://www.linkedin.com/in/christopher-grieser-ba693b17a/)\n\n\u003ca href='https://ko-fi.com/Y8Y86SQ91' target='_blank'\u003e\u003cimg height='36'\nstyle='border:0px;height:36px;' src='https://cdn.ko-fi.com/cdn/kofi1.png?v=3'\nborder='0' alt='Buy Me a Coffee at ko-fi.com' /\u003e\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisgrieser%2Fnvim-spider","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fchrisgrieser%2Fnvim-spider","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fchrisgrieser%2Fnvim-spider/lists"}