-
Notifications
You must be signed in to change notification settings - Fork 177
/
mini-splitjoin.txt
537 lines (412 loc) · 20.8 KB
/
mini-splitjoin.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
*mini.splitjoin* Split and join arguments
*MiniSplitjoin*
MIT License Copyright (c) 2023 Evgeni Chasnovski
==============================================================================
Features:
- Mappings and Lua functions that modify arguments (regions inside brackets
between allowed separators) under cursor.
Supported actions:
- Toggle - split if arguments are on single line, join otherwise.
Main supported function of the module. See |MiniSplitjoin.toggle()|.
- Split - make every argument separator be on end of separate line.
See |MiniSplitjoin.split()|.
- Join - make all arguments be on single line.
See |MiniSplitjoin.join()|.
- Mappings are dot-repeatable in Normal mode and work in Visual mode.
- Customizable argument detection (see |MiniSplitjoin.config.detect|):
- Which brackets can contain arguments.
- Which strings can separate arguments.
- Which regions are excluded when looking for separators (like inside
nested brackets or quotes).
- Customizable pre and post hooks for both split and join. See `split` and
`join` in |MiniSplitjoin.config|. There are several built-in ones
in |MiniSplitjoin.gen_hook|.
- Works inside comments by using modified notion of indent.
See |MiniSplitjoin.get_indent_part()|.
- Provides low-level Lua functions for split and join at positions.
See |MiniSplitjoin.split_at()| and |MiniSplitjoin.join_at()|.
Notes:
- Search for arguments is done using Lua patterns (regex-like approach).
Certain amount of false positives is to be expected.
- This module is mostly designed around |MiniSplitjoin.toggle()|. If target
split positions are on different lines, join first and then split.
- Actions can be done on Visual mode selection, which mostly present as
a safety route in case of incorrect detection of initial region.
It uses |MiniSplitjoin.get_visual_region()| which treats selection as full
brackets (include brackets in selection).
# Setup ~
This module needs a setup with `require('mini.splitjoin').setup({})` (replace
`{}` with your `config` table). It will create global Lua table `MiniSplitjoin`
which you can use for scripting or manually (with `:lua MiniSplitjoin.*`).
See |MiniSplitjoin.config| for available config settings.
You can override runtime config settings (like action hooks) locally to
buffer inside `vim.b.minisplitjoin_config` which should have same structure
as `MiniSplitjoin.config`. See |mini.nvim-buffer-local-config| for more details.
# Comparisons ~
- 'FooSoft/vim-argwrap':
- Mostly has the same design as this module.
- Doesn't work inside comments, while this module does.
- Has more built-in ways to control split and join, while this module
intentionally provides only handful.
- 'AndrewRadev/splitjoin.vim':
- More oriented towards language-depended transformations, while this
module intntionally deals with more generic text-related functionality.
- 'Wansmer/treesj':
- Operates based on tree-sitter nodes. This is more accurate in
some edge cases, but **requires** tree-sitter parser.
- Doesn't work inside comments or strings.
# Disabling ~
To disable, set `g:minisplitjoin_disable` (globally) or `b:minisplitjoin_disable`
(for a buffer) to `v:true`. Considering high number of different scenarios
and customization intentions, writing exact rules for disabling module's
functionality is left to user. See |mini.nvim-disabling-recipes| for common
recipes.
------------------------------------------------------------------------------
*MiniSplitjoin-glossary*
- POSITION - table with fields <line> and <col> containing line and column
numbers respectively. Both are 1-indexed. Example: `{ line = 2, col = 1 }`.
- REGION - table representing region in a buffer. Fields: <from> and <to> for
inclusive start and end positions. Example: >lua
{ from = { line = 1, col = 1 }, to = { line = 2, col = 1 } }
<
------------------------------------------------------------------------------
*MiniSplitjoin.setup()*
`MiniSplitjoin.setup`({config})
Module setup
Parameters ~
{config} `(table|nil)` Module config table. See |MiniSplitjoin.config|.
Usage ~
>lua
require('mini.splitjoin').setup() -- use default config
-- OR
require('mini.splitjoin').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniSplitjoin.config*
`MiniSplitjoin.config`
Module config
Default values:
>lua
MiniSplitjoin.config = {
-- Module mappings. Use `''` (empty string) to disable one.
-- Created for both Normal and Visual modes.
mappings = {
toggle = 'gS',
split = '',
join = '',
},
-- Detection options: where split/join should be done
detect = {
-- Array of Lua patterns to detect region with arguments.
-- Default: { '%b()', '%b[]', '%b{}' }
brackets = nil,
-- String Lua pattern defining argument separator
separator = ',',
-- Array of Lua patterns for sub-regions to exclude separators from.
-- Enables correct detection in presence of nested brackets and quotes.
-- Default: { '%b()', '%b[]', '%b{}', '%b""', "%b''" }
exclude_regions = nil,
},
-- Split options
split = {
hooks_pre = {},
hooks_post = {},
},
-- Join options
join = {
hooks_pre = {},
hooks_post = {},
},
}
<
*MiniSplitjoin.config.detect*
# Detection ~
The table at `config.detect` controls how arguments are detected using Lua
patterns. General idea is to convert whole buffer into a single line,
perform string search, and convert results back into 2d positions.
Example configuration: >lua
require('mini.splitjoin').setup({
detect = {
-- Detect only inside balanced parenthesis
brackets = { '%b()' },
-- Allow both `,` and `;` to separate arguments
separator = '[,;]',
-- Make any separator define an argument
exclude_regions = {},
},
})
<
## Outer brackets ~
`detect.brackets` is an array of Lua patterns used to find enclosing region.
It is done by traversing whole buffer to find the smallest region matching
any supplied pattern.
Default: `nil`, inferred as `{ '%b()', '%b[]', '%b{}' }`.
So an argument can be inside a balanced `()`, `[]`, or `{}`.
Example: `brackets = { '%b()' }` will search for arguments only inside
balanced `()`.
## Separator ~
`detect.separator` is a single Lua pattern defining which strings should be
treated as argument separators.
Empty string in `detect.separator` will result in only surrounding brackets
used as separators.
Only end of pattern match will be used as split/join positions.
Default: `','`. So an argument can be separated only with comma.
Example: `separator = { '[,;]' }` will treat both `,` and `;` as separators.
## Excluded regions ~
`detect.exclude_regions` is an array of Lua patterns for sub-regions from which
to exclude separators. Enables correct detection in case of nested brackets
and quotes.
Default: `nil`; inferred as `{ '%b()', '%b[]', '%b{}', '%b""', "%b''" }`.
So a separator **can not** be inside a balanced `()`, `[]`, `{}` (representing
nested argument regions) or `""`, `''` (representing strings).
Example: `exclude_regions = {}` will not exclude any regions. So in case of
`f(a, { b, c })` it will detect both commas as argument separators.
# Hooks ~
`split.hooks_pre`, `split.hooks_post`, `join.hooks_pre`, and `join.hooks_post`
are arrays of hook functions. If empty (default) no hook is applied.
Hooks should take and return array of positions. See |MiniSplitjoin-glossary|.
They can be used to tweak actions:
- Pre-hooks are called before action. Each is applied on the output of
previous one. Input of first hook are detected split/join positions.
Output of last one is actually used to perform split/join.
- Post-hooks are called after action. Each is applied on the output of
previous one. Input of first hook are split/join positions from actual
action plus its region's right end as last position (for easier hook code).
Output of last one is used as action return value.
For more specific details see |MiniSplitjoin.split()| and |MiniSplitjoin.join()|.
See |MiniSplitjoin.gen_hook| for generating common hooks with examples.
------------------------------------------------------------------------------
*MiniSplitjoin.toggle()*
`MiniSplitjoin.toggle`({opts})
Toggle arguments
Overview:
- Detect region at input position: either by using supplied `opts.region` or
by finding smallest bracketed region surrounding position.
See |MiniSplitjoin.config.detect| for more details.
- If region spans single line, use |MiniSplitjoin.split()| with found region.
Otherwise use |MiniSplitjoin.join()|.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of chosen `split()` or `join()` action.
------------------------------------------------------------------------------
*MiniSplitjoin.split()*
`MiniSplitjoin.split`({opts})
Split arguments
Overview:
- Detect region: either by using supplied `opts.region` or by finding smallest
bracketed region surrounding input position (cursor position by default).
See |MiniSplitjoin.config.detect| for more details.
- Find separator positions using `separator` and `exclude_regions` from `opts`.
Both brackets are treated as separators.
See |MiniSplitjoin.config.detect| for more details.
Note: stop if no separator positions are found.
- Modify separator positions to represent split positions. Last split position
(which is inferred from right bracket) is moved one column to left so that
right bracket would move on new line.
- Apply all hooks from `opts.split.hooks_pre`. Each is applied on the output of
previous one. Input of first hook is split positions from previous step.
Output of last one is used as split positions in next step.
- Split and update split positions with |MiniSplitjoin.split_at()|.
- Apply all hooks from `opts.split.hooks_post`. Each is applied on the output of
previous one. Input of first hook is split positions from previous step plus
region's right end (for easier hook code).
Output of last one is used as function return value.
Note:
- By design, it doesn't detect if argument **should** be split, so application
on arguments spanning multiple lines can lead to undesirable result.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of last `opts.split.hooks_post` or `nil` if no split positions
found. Default: return value of |MiniSplitjoin.split_at()| application.
------------------------------------------------------------------------------
*MiniSplitjoin.join()*
`MiniSplitjoin.join`({opts})
Join arguments
Overview:
- Detect region: either by using supplied `opts.region` or by finding smallest
bracketed region surrounding input position (cursor position by default).
See |MiniSplitjoin.config.detect| for more details.
- Compute join positions to be line ends of all but last region lines.
Note: stop if no join positions are found.
- Apply all hooks from `opts.join.hooks_pre`. Each is applied on the output
of previous one. Input of first hook is join positions from previous step.
Output of last one is used as join positions in next step.
- Join and update join positions with |MiniSplitjoin.join_at()|.
- Apply all hooks from `opts.join.hooks_post`. Each is applied on the output
of previous one. Input of first hook is join positions from previous step
plus region's right end for easier hook code.
Output of last one is used as function return value.
Parameters ~
{opts} `(table|nil)` Options. Has structure from |MiniSplitjoin.config|
inheriting its default values.
Following extra optional fields are allowed:
- <position> `(table)` - position at which to find smallest bracket region.
See |MiniSplitjoin-glossary| for the structure.
Default: cursor position.
- <region> `(table)` - region at which to perform action. Assumes inclusive
both start at left bracket and end at right bracket.
See |MiniSplitjoin-glossary| for the structure.
Default: `nil` to automatically detect region.
Return ~
`(any)` Output of last `opts.split.hooks_post` or `nil` of no join positions
found. Default: return value of |MiniSplitjoin.join_at()| application.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook*
`MiniSplitjoin.gen_hook`
Generate common hooks
This is a table with function elements. Call to actually get hook.
All generated post-hooks return updated versions of their input reflecting
changes done inside hook.
Example for `lua` filetype (place it in 'lua.lua' filetype plugin, |ftplugin|): >lua
local gen_hook = MiniSplitjoin.gen_hook
local curly = { brackets = { '%b{}' } }
-- Add trailing comma when splitting inside curly brackets
local add_comma_curly = gen_hook.add_trailing_separator(curly)
-- Delete trailing comma when joining inside curly brackets
local del_comma_curly = gen_hook.del_trailing_separator(curly)
-- Pad curly brackets with single space after join
local pad_curly = gen_hook.pad_brackets(curly)
-- Create buffer-local config
vim.b.minisplitjoin_config = {
split = { hooks_post = { add_comma_curly } },
join = { hooks_post = { del_comma_curly, pad_curly } },
}
<
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.pad_brackets()*
`MiniSplitjoin.gen_hook.pad_brackets`({opts})
Generate hook to pad brackets
This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <pad> `(string)` - pad to add after first and before last join positions.
Default: `' '` (single space).
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds inner pad to first and last join positions and
returns updated input join positions.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.add_trailing_separator()*
`MiniSplitjoin.gen_hook.add_trailing_separator`({opts})
Generate hook to add trailing separator
This is a split post-hook. Use in `split.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <sep> `(string)` - separator to add before last split position.
Default: `','`.
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds separator before last split position and
returns updated input split positions.
------------------------------------------------------------------------------
*MiniSplitjoin.gen_hook.del_trailing_separator()*
`MiniSplitjoin.gen_hook.del_trailing_separator`({opts})
Generate hook to delete trailing separator
This is a join post-hook. Use in `join.hooks_post` of |MiniSplitjoin.config|.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <sep> `(string)` - separator to remove before last join position.
Default: `','`.
- <brackets> `(table)` - array of bracket patterns indicating on which
brackets action should be made. Has same structure as `brackets`
in |MiniSplitjoin.config.detect|.
Default: `MiniSplitjoin.config.detect.brackets`.
Return ~
`(function)` A hook which adds separator before last split position and
returns updated input split positions.
------------------------------------------------------------------------------
*MiniSplitjoin.split_at()*
`MiniSplitjoin.split_at`({positions})
Split at positions
Overview:
- For each position move all characters after it to next line and make it have
same indent as current one (see |MiniSplitjoin.get_indent_part()|).
Also remove trailing whitespace at position line.
- Increase indent of inner lines by a single pad: tab in case of |noexpandtab|
or |shiftwidth()| number of spaces otherwise.
Notes:
- Cursor is adjusted to follow text updates.
- Use output of this function to keep track of input positions.
Parameters ~
{positions} `(table)` Array of positions at which to perform split.
See |MiniSplitjoin-glossary| for their structure. Note: they don't have
to be ordered, but first and last ones will be used to infer lines for
which indent will be increased.
Return ~
`(table)` Array of new positions to where input `positions` were moved.
------------------------------------------------------------------------------
*MiniSplitjoin.join_at()*
`MiniSplitjoin.join_at`({positions})
Join at positions
Overview:
- For each position join its line with the next line. Joining is done by
replacing trailing whitespace of the line and indent of its next line
(see |MiniSplitjoin.get_indent_part()|) with a pad string (single space except
empty string for first and last positions). To adjust this, use hooks
(for example, see |MiniSplitjoin.gen_hook.pad_brackets()|).
Notes:
- Cursor is adjusted to follow text updates.
- Use output of this function to keep track of input positions.
Parameters ~
{positions} `(table)` Array of positions at which to perform join.
See |MiniSplitjoin-glossary| for their structure. Note: they don't have
to be ordered, but first and last ones will have different pad string.
Return ~
`(table)` Array of new positions to where input `positions` were moved.
------------------------------------------------------------------------------
*MiniSplitjoin.get_visual_region()*
`MiniSplitjoin.get_visual_region`()
Get previous visual region
Get previous visual selection using |`<| and |`>| marks in the format of
region (see |MiniSplitjoin-glossary|). Used in Visual mode mappings.
Note:
- Both marks are included in region, so for better
- In linewise Visual mode
Return ~
`(table)` A region. See |MiniSplitjoin-glossary| for exact structure.
------------------------------------------------------------------------------
*MiniSplitjoin.get_indent_part()*
`MiniSplitjoin.get_indent_part`({line}, {respect_comments})
Get string's indent part
Parameters ~
{line} `(string)` String for which to compute indent.
{respect_comments} `(boolean|nil)` Whether to respect comments as indent part.
Default: `true`.
Return ~
`(string)` Part of input representing line's indent. Can be empty string.
Use `string.len()` to compute indent in bytes.
------------------------------------------------------------------------------
*MiniSplitjoin.operator()*
`MiniSplitjoin.operator`({task})
Operator for Normal mode mappings
Main function to be used in expression mappings. No need to use it
directly, everything is setup in |MiniSplitjoin.setup()|.
Parameters ~
{task} `(string)` Name of task.
vim:tw=78:ts=8:noet:ft=help:norl: