Extractors - FluxxField/smart-motion.nvim GitHub Wiki
Extractors
Extractors are the second stage in the SmartMotion pipeline. They receive data yielded from a collector and generate targets β positional objects describing where actions can apply.
[!TIP] If collectors define where to look, extractors decide what to act on.
β What an Extractor Does
Extractors operate on each yielded item from the collector (often a line object) and generate one or more SmartMotionTarget
entries. These targets include position info, visual text, and metadata that can be used for labeling, filtering, and execution.
Extractors are also written as coroutines, allowing for:
- Early-exit target detection (e.g. flow state or quick jump)
- Scalable, memory-efficient target generation
[!IMPORTANT] Extractors should not apply logic for direction, cursor filtering, or context sensitivity. That logic belongs to filters. Extractors should only describe all possible targets.
π§ Coroutine Design
Like collectors, extractors are implemented as coroutines.
- The collector is resumed to yield an item
- The extractor processes the item and can
coroutine.yield()
one or more targets - If the pipeline needs only one match, it stops early
This system minimizes overhead and makes SmartMotion fast, even on large files.
[!IMPORTANT] One coroutine loop pulls from the collector, and one loop extracts all targets β and thatβs it. No extra passes are needed.
π¦ Built-in Extractors
Name | Description |
---|---|
lines |
Extracts whole-line targets |
words |
Extracts words using regex-based word boundaries |
text_search |
Extracts fixed-length search targets (1-2 chars) |
[!NOTE] All built-in extractors are registered using
register_many()
in theextractors
registry. You can override or extend them.
π§± Example Usage
Used inside a pipeline:
pipeline = {
collector = "lines",
extractor = "words",
visualizer = "hint_start",
}
This example extracts visible words from all lines in the buffer.
β¨ Example: Custom Word Extractor
Hereβs a simplified version of a custom words
extractor that finds word matches using regex and yields them as targets. This version omits cursor and direction logic entirely β such filtering should be handled in the filters
module.
---@type SmartMotionExtractorModuleEntry
local M = {}
function M.run(collector, opts)
return coroutine.create(function(ctx, cfg, motion_state)
while true do
local ok, data_or_error = coroutine.resume(collector, ctx, cfg, motion_state)
if not ok then
log.error("Collector Coroutine Error: " .. tostring(data_or_error))
break
end
if data_or_error == nil then
break
end
local row = line_data.line_number
local line = line_data.text
for match_text, start_pos in line:gmatch("()%w+") do
local end_pos = start_pos + #match_text - 1
coroutine.yield({
row = row,
col = start_pos,
text = match_text,
start_pos = { row = row, col = start_pos },
end_pos = { row = row, col = end_pos },
type = "words",
})
end
end
end)
end
return M
π Registering Your Extractor
local extractors = require("smart-motion.core.registries"):get().extractors
extractors.register("my_extractor", MyModule)
You can also provide metadata and keys
to link extractors to motion keys if using infer = true
.
π Advanced Example: Word Extractor
The built-in words
extractor:
- Uses
vim.fn.matchstrpos
to find regex-based words - Applies cursor logic and direction-specific filtering
- Reorders results for BEFORE_CURSOR
[!NOTE] This behavior will be moved into filters in the future to better separate concerns.
π Next Steps
Now that you're generating targets, it's time to:
- Visualize them with
visualizers.md
- Define what happens when selected using
actions.md