Skip to content

Commit

Permalink
Feature - Ability to extend the default patterns
Browse files Browse the repository at this point in the history
Cliff notes (will expand in more detail in PR):

1. Added ability to pass in a patterns table and a overrideDefaults boolean.
2. Backwards compatible.
3. Updated README.
  • Loading branch information
roy.crippen4 committed Jan 2, 2024
1 parent ab39a1b commit 3120aaa
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 38 deletions.
91 changes: 63 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<!-- LTeX: enabled=false -->

# nvim-spider 🕷️🕸️

<!-- LTeX: enabled=true -->
<a href="https://dotfyle.com/plugins/chrisgrieser/nvim-spider">
<img alt="badge" src="https://dotfyle.com/plugins/chrisgrieser/nvim-spider/shield"/></a>
Expand All @@ -14,26 +16,28 @@ mode. Supports counts and dot-repeat.

<!-- toc -->

- [Features](#features)
* [Subword Motion](#subword-motion)
* [Skipping Insignificant Punctuation](#skipping-insignificant-punctuation)
- [Installation](#installation)
- [Configuration](#configuration)
* [Advanced: Custom Movement Patterns](#advanced-custom-movement-patterns)
- [Special Cases](#special-cases)
* [UTF-8 support](#utf-8-support)
* [Subword Text Object](#subword-text-object)
* [Operator-pending Mode: The case of `cw`](#operator-pending-mode-the-case-of-cw)
* [Motions in Insert Mode](#motions-in-insert-mode)
- [Credits](#credits)
- [Features](#features)
- [Subword Motion](#subword-motion)
- [Skipping Insignificant Punctuation](#skipping-insignificant-punctuation)
- [Installation](#installation)
- [Configuration](#configuration)
- [Advanced: Custom Movement Patterns](#advanced-custom-movement-patterns)
- [Special Cases](#special-cases)
- [UTF-8 support](#utf-8-support)
- [Subword Text Object](#subword-text-object)
- [Operator-pending Mode: The case of `cw`](#operator-pending-mode-the-case-of-cw)
- [Motions in Insert Mode](#motions-in-insert-mode)
- [Credits](#credits)

<!-- tocstop -->

## Features

The `w`, `e`, `b` (and `ge`) motions work the same as the default ones by vim,
except for two differences:

### Subword Motion

The movements happen by subwords, meaning it stops at the sub-parts of a
camelCase, SCREAMING_SNAKE_CASE, or kebab-case variable.

Expand All @@ -48,6 +52,7 @@ local myVariableName = FOO_BAR_BAZ
```

### Skipping Insignificant Punctuation

A sequence of one or more punctuation characters is considered significant if it
is surrounded by whitespace and does not include any non-punctuation characters.

Expand Down Expand Up @@ -91,6 +96,18 @@ call.
"<cmd>lua require('spider').motion('e')<CR>",
mode = { "n", "o", "x" },
},
{ -- example using an explicit function
"w",
function()
require('spider').motion('w', {
customPatterns = {
patterns = { ('%x'):rep(6) .. '+' },
overrideDefault = true,
},
})
end,
mode = { 'n', 'o', 'x' },
},
},
},

Expand Down Expand Up @@ -124,21 +141,24 @@ vim.keymap.set(
{ desc = "Spider-b" }
)
```

<!-- vale Google.Will = NO -->

> [!NOTE]
> For dot-repeat to work, you have to call the motions as Ex-commands. When
> using `function() require("spider").motion("w") end` as third argument of
> the keymap, dot-repeatability will not work.
## Configuration

The `.setup()` call is optional.

```lua
-- default values
require("spider").setup {
skipInsignificantPunctuation = true,
subwordMovement = true,
customPatterns = {}, -- overrides other settings if not-empty. See README.
customPatterns = {}, -- check Custom Movement Patterns for details
}
```

Expand All @@ -152,6 +172,7 @@ Any options passed here will be used, and override the options set in the
`setup()` call.

### Advanced: Custom Movement Patterns

You can use the `customPatterns` table to define custom movement patterns. These
must be [lua patterns](https://www.lua.org/manual/5.4/manual.html#6.4.1), and
they must be symmetrical (work the same backwards and forwards) to work
Expand Down Expand Up @@ -179,8 +200,14 @@ require("spider").motion("w", {

-- The motion stops only at hashes like `ef82a2`, avoiding repetition by using
-- `string.rep()`.
-- Extend default patterns by passing a `patterns` table and
-- setting `overrideDefault` to false.
require("spider").motion("w", {
customPatterns = { ("%x"):rep(6) .. "+" },
customPatterns = {
patterns = {
("%x"):rep(6) .. "+" },
},
overrideDefault = false,
})

-- The motion stops at the next declaration of a variable in -- javascript.
Expand All @@ -192,14 +219,17 @@ require("spider").motion("e", {
```

> [!NOTE]
> Setting the option overrides `nvim-spider`'s default behavior, meaning subword
> The customPatterns option overrides `nvim-spider`'s default behavior, meaning subword
> movement and skipping of punctuation are disabled. You can add
> `customPatterns` as an option to the `.motion` call to create extra motions,
> `customPatterns` as an option to the `.motion` call to create new motions,
> while still having access `nvim-spider`'s default behavior.
> Pass a patterns table and set overrideDefault to false to extend
> `nvim-spider`'s default behavior with a new pattern.
## Special Cases

### UTF-8 support

For adding UTF-8 support for matching non-ASCII text, add `luautf8` as rocks.
You can do so directly in `packer.nvim` or via dependency on `nvim_rocks` in
`lazy.nvim`.
Expand All @@ -221,15 +251,18 @@ You can do so directly in `packer.nvim` or via dependency on `nvim_rocks` in
```

### Subword Text Object

This plugin supports `w`, `e`, and `b` in operator-pending mode, but does not
include a subword variant of `iw`. For a version of `iw` that considers
camelCase, check out the `subword` text object of
[nvim-various-textobjs](https://github.com/chrisgrieser/nvim-various-textobjs).

<!-- vale Google.FirstPerson = NO -->

### Operator-pending Mode: The case of `cw`

In operator pending mode, vim's `web` motions are actually a bit inconsistent.
For instance, `cw` will change to the *end* of a word instead of the start of
For instance, `cw` will change to the _end_ of a word instead of the start of
the next word, like `dw` does. This is probably done for convenience in vi's
early days before there were text objects. In my view, this is quite problematic
since it makes people habitualize inconsistent motion behavior.
Expand Down Expand Up @@ -258,26 +291,28 @@ vim.keymap.set("i", "<C-b>", "<Esc><cmd>lua require('spider').motion('b')<CR>i")
```

## Credits
__Thanks__

**Thanks**
To `@vypxl` and `@ii14` [for figuring out dot-repeatability](https://github.com/chrisgrieser/nvim-spider/pull/4).

__About Me__
**About Me**
In my day job, I am a sociologist studying the social mechanisms underlying the
digital economy. For my PhD project, I investigate the governance of the app
economy and how software ecosystems manage the tension between innovation and
compatibility. If you are interested in this subject, feel free to get in touch.

__Blog__
**Blog**
I also occasionally blog about vim: [Nano Tips for Vim](https://nanotipsforvim.prose.sh)

__Profiles__
- [reddit](https://www.reddit.com/user/pseudometapseudo)
- [Discord](https://discordapp.com/users/462774483044794368/)
- [Academic Website](https://chris-grieser.de/)
- [Twitter](https://twitter.com/pseudo_meta)
- [Mastodon](https://pkm.social/@pseudometa)
- [ResearchGate](https://www.researchgate.net/profile/Christopher-Grieser)
- [LinkedIn](https://www.linkedin.com/in/christopher-grieser-ba693b17a/)
**Profiles**

- [reddit](https://www.reddit.com/user/pseudometapseudo)
- [Discord](https://discordapp.com/users/462774483044794368/)
- [Academic Website](https://chris-grieser.de/)
- [Twitter](https://twitter.com/pseudo_meta)
- [Mastodon](https://pkm.social/@pseudometa)
- [ResearchGate](https://www.researchgate.net/profile/Christopher-Grieser)
- [LinkedIn](https://www.linkedin.com/in/christopher-grieser-ba693b17a/)

<a href='https://ko-fi.com/Y8Y86SQ91' target='_blank'><img
height='36'
Expand Down
19 changes: 12 additions & 7 deletions lua/spider/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,29 @@ else
end

--------------------------------------------------------------------------------
---@class customPatterns
---@field patterns string[]? string array of lua patterns to match against.
---@field overrideDefault boolean? set to false to extend the default patterns with customPatterns. Defaults to true.

-- CONFIG
---@class (exact) optsObj
---@field skipInsignificantPunctuation boolean
---@field subwordMovement boolean
---@field customPatterns string[]
---@field skipInsignificantPunctuation boolean?
---@field subwordMovement boolean? determines movement through camelCase and snake_case. Defaults to true.
---@field customPatterns customPatterns|string[]? user defined patterns to match for motion movement

---@type optsObj
local defaultOpts = {
skipInsignificantPunctuation = true,
subwordMovement = true,
customPatterns = {},
customPatterns = {
patterns = {},
overrideDefault = true,
},
}
local globalOpts = defaultOpts

---@param userOpts? optsObj
function M.setup(userOpts)
globalOpts = vim.tbl_deep_extend("force", defaultOpts, userOpts or {})
end
function M.setup(userOpts) globalOpts = vim.tbl_deep_extend("force", defaultOpts, userOpts or {}) end

--------------------------------------------------------------------------------

Expand Down
35 changes: 32 additions & 3 deletions lua/spider/pattern-variants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ local M = {}

---@type patternList
local subwordPatterns = {
number = "%d+",
number = "%d+",
camelCaseWordForward = "%u?%l+",
camelCaseWordBackward = "%l+%u?",
ALL_UPPER_CASE_WORD = "%u%u+",
Expand Down Expand Up @@ -37,15 +37,41 @@ local allPunctuationPatterns = {
punctuation = "%p+",
}

---@param table table table to verify.
---@return boolean
local function isStringArray(table)
if type(table) ~= "table" then return false end

for _, value in ipairs(table) do
if type(value) ~= "string" then return false end
end

return true
end

--------------------------------------------------------------------------------

---@param opts optsObj configuration table as in setup()
---@param backwards boolean whether to adjust patterns for backward motions
---@return patternList
---@nodiscard
function M.get(opts, backwards)
-- opts.customPatterns.overrideDefault will default to true if it is not passed in.
-- This preserves the original behavior of spider.
-- Users can set overrideDefault to true, but spider will behave the same as the original behavior.
-- Behavior will change if a user sets the overrideDefault key to true within a customPatterns.patterns table.
-- any custom patterns take precedence
if opts.customPatterns and #opts.customPatterns > 0 then return opts.customPatterns end

-- need to check if opts.customPatterns is a string array to avoid breaking changes.
if opts.customPatterns and isStringArray(opts.customPatterns) then
if #opts.customPatterns > 0 then return opts.customPatterns end
end

-- this checks if a user set a custom pattern in the patterns table
-- then it checks if overrideDefault was set
if opts.customPatterns.patterns and #opts.customPatterns.patterns > 0 then
if opts.customPatterns.overrideDefault then return opts.customPatterns.patterns end
end

local punctuationPatterns = opts.skipInsignificantPunctuation and skipPunctuationPatterns
or allPunctuationPatterns
Expand All @@ -54,7 +80,10 @@ function M.get(opts, backwards)
if backwards then wordPatterns.camelCaseWordForward = nil end
if not backwards then wordPatterns.camelCaseWordBackward = nil end

local patternsToUse = vim.tbl_extend("force", wordPatterns, punctuationPatterns)
-- user patterns default to {}
-- user patterns table only changes patterntsToUse if patterns has a pattern and overrideDefaults was set to false.
local patternsToUse =
vim.tbl_extend("force", wordPatterns, punctuationPatterns, opts.customPatterns.patterns)
return patternsToUse
end

Expand Down

0 comments on commit 3120aaa

Please sign in to comment.