-
Notifications
You must be signed in to change notification settings - Fork 1
Custom Motions
This guide walks you through creating your own motion using SmartMotion's modular pipeline system.
Important
SmartMotion motions are composable, made up of collectors, extractors, filters, visualizers, and actions - plus an optional pipeline wrapper.
Tip
The first argument to register_motion(name, config) becomes the motion's name. If trigger_key is not explicitly provided, it defaults to this name.
This means you can do things like:
require("smart-motion").register_motion("find_2char", {
trigger_key = "f",
...
})Here's what a minimal custom motion might look like:
require("smart-motion").register_motion("m", {
pipeline = {
collector = "lines",
extractor = "words",
filter = "filter_visible_lines",
visualizer = "hint_start",
},
pipeline_wrapper = "default",
action = "jump",
state = {
direction = DIRECTION.AFTER_CURSOR,
hint_position = HINT_POSITION.START,
},
map = true,
modes = { "n" },
metadata = {
label = "My Custom Motion",
description = "Example motion using word targets",
},
})This would:
- Extract words from all visible lines
- Visualize with hints
- Let the user select one
- Then jump to it
Fetches source data. Most start with:
collector = "lines"But you could register one for git changes, Telescope results, etc.
Extracts targets from the collected data.
extractor = "words"Extractors yield target objects, including position and metadata.
Narrows down the targets based on visibility, direction, or custom rules.
filter = "filter_visible_lines"Renders your targets however you want - hints, popup, floating menu, etc.
visualizer = "hint_start"Controls when and how the pipeline runs.
pipeline_wrapper = "text_search"What happens when a target is selected.
action = "jump"Or use merge({ "jump", "delete" }) for compound motions.
Your motion config can include the following:
-
state: for static configuration likedirection,hint_position -
opts: for dynamic input likenum_of_char
These are passed to all modules in the pipeline.
Note
In a future version:
-
optswill be deprecated - all dynamic input will be stored directly inmotion_state -
directionwill be handled by filters instead of state -
hint_positionwill likely become a property of the visualizer at registration time, so you'll register multiple visualizers forbefore,after, orboth- reducing the need to specifyhint_positioninstate
This improves consistency and separation of concerns.
require("smart-motion").register_motion("f", {
pipeline = {
collector = "lines",
extractor = "text_search",
filter = "filter_visible_lines",
visualizer = "hint_start",
},
pipeline_wrapper = "text_search",
action = "jump",
state = {
direction = DIRECTION.AFTER_CURSOR,
hint_position = HINT_POSITION.START,
},
opts = {
num_of_char = 2,
},
map = true,
modes = { "n" },
})Here are the fields you can use when calling register_motion(name, config):
SmartMotionMotionEntry {
trigger_key?: string -- Optional override; defaults to `name`
infer?: boolean -- Is this an action trigger?
pipeline: {
collector: string
extractor: string
visualizer: string
filter?: string
}
pipeline_wrapper?: string
action: string
state?: SmartMotionMotionState -- Motion direction, hint position, etc.
opts?: table -- Dynamic input like character count (deprecated soon)
map?: boolean -- Whether to auto-create keymap
modes?: string[] -- Vim modes ("n", "v", etc.)
name?: string -- Internal ID; usually same as first argument
metadata?: {
label?: string
description?: string
}
}Note
Most fields are optional, and many are auto-filled during registration:
-
trigger_keydefaults to the motion name -
name,label, anddescriptionare generated if not given -
optswill be removed in favor of writing directly tomotion_state