-
-
Notifications
You must be signed in to change notification settings - Fork 63
For Developers
Hi there! Welcome to a (hopefully) brief overview on the nvim-surround
codebase. My hope is that this will help new contributors navigate the codebase,
as well as shed some light on certain design decisions I've made. For more
details, see the comments, and feel free to open a discussion tagging @kylechui!
Note: For clarity/consistency, all singular elements are 1-indexed, and all ranges are endpoint-inclusive.
-
delimiter pair: A text pair that represents what surrounds a
selection
on the left and right. - add: Any action that introduces a new delimiter pair to the buffer, either through insert/normal/visual mode commands.
- modify: Any action that deletes/changes an existing delimiter pair in the buffer.
See init.lua
for more technical definitions (e.g. selection
).
As of this writing, the codebase currently takes the following form:
nvim-surround
│ lua/nvim-surround
│ ├── init.lua
│ └── ...
├── tests
│ └── minimal_init.lua
│ └── basics_spec.lua
│ └── ...
└── doc
└── nvim-surround.txt
This folder contains the actual code that runs the plugin. Here's a brief description of what each file does:
-
init.lua
: Defines type annotations for the project, as well as all high-level functions for actually using the plugin, e.g.normal_surround
andnormal_callback
. -
config.lua
: Defines a set of functions related to user configuration. -
buffer.lua
: Defines a bunch of helper functions related to the current buffer. The functions are grouped into a few categories (cursor, marks, buffer contents, highlights). -
utils.lua
: Defines a set of helpful utility functions that don't really fit in elsewhere. -
cache.lua
: Defines some local variables as cache, for dot-repeatability.
The remaining four files can be thought of as various "engines" for finding selection(s):
-
patterns.lua
: Deals with Lua patterns. -
motions.lua
: Deals with Vim motions, either built-in or defined via:h operator-pending
. -
treesitter.lua
: Deals with singular Tree-sitter nodes. -
queries.lua
: Deals with queries; can be thought of as analogous to capture clauses for Lua patterns.
Contains a set of
plenary.nvim
test cases. Also holds a minimal_init.lua
, for standardized testing.
Contains the documentation for :h nvim-surround
in nvim-surround.txt
.
To provide various syntax sugars for configuring the plugin, while also
preserving internal simplicity, there is a translation layer between the
user-provided configuration and the internal configuration of the plugin. This
is primarily handled by config.translate_opts
.
The internal model requires that all surrounds contain all possible keys (add
,
find
, delete
, change.target
, change.replacement
).
For any given surround, the internal model is a restriction on the configuration options provided to the end user:
-
add
: Necessarily a function that returns a pair of tables of strings. The elements of the pair represent the left/right delimiter pair. Tables of strings represent multi-line strings. -
find
: Necessarily a function that returns aselection
. -
delete
: Necessarily a function that returns a pair ofselections
. -
change.target
: Same asdelete
. -
change.replacement
: Same asadd
.
Here we describe the general code execution flow for an arbitrary add command.
- There are no arguments passed to
normal_surround
, so the dot-repeat cache is cleared andnormal_callback
is called with a user-provided Vim motion. See:h opfunc
for details.- The
selection
to be surrounded is determined by the[
and]
marks. -
normal_callback
queries the user for the delimiter pair to surround theselection
. This pair is found by calling the surround'sadd
key. - The dot-repeat cache is set.
- The
-
normal_surround
is re-called, but this time with theselection
and delimiter pair.- The
selection
is surrounded with the delimiter pair.
- The
The other functions in init.lua
all behave similarly, often using this
"back-and-forth" strategy for differentiating what should and should not be
dot-repeated (visual-mode additions are not dot-repeatable).
Here we describe the general code execution flow for an arbitrary delete command.
- The
delete_surround
function is called by the user. - Since there are no arguments passed,
delete_callback
is called.- The user is queried for a character to be deleted.
-
delete_surround
is then called with this character.- The selections to be deleted are computed by taking the selection found via
the surround's
find
key, then "filtering" it down to a pair ofselections
via the surround'sdelete
key. - The pair of selections are then deleted from the buffer, right delimiter first (since deleting the left one will shift the positions around in the buffer).
- The selections to be deleted are computed by taking the selection found via
the surround's
In general, the process for finding pairs of selections
is done by taking a
"parent" selection
and "filtering" it down. This is done to provide more
extensibility/modularity, as the parent selection may be found via a variety of
different methods (i.e. Vim motion, Lua pattern, etc.).
Changing a delimiter pair to a different pair is more or less a combination of
delete
, then add
operations.