Skip to content

Commit d14e417

Browse files
committed
Fixed Surround and RemoveSurround
1 parent 2e27ed4 commit d14e417

File tree

2 files changed

+73
-455
lines changed

2 files changed

+73
-455
lines changed

lib/utils.vim

Lines changed: 32 additions & 222 deletions
Original file line numberDiff line numberDiff line change
@@ -11,60 +11,6 @@ export def Echowarn(msg: string)
1111
echohl WarningMsg | echom $'[markdown_extras] {msg}' | echohl None
1212
enddef
1313

14-
# TODO it may be limiting to have 'string' only
15-
export def KeysFromValue(dict: dict<string>, target_value: string): list<string>
16-
# Given a value, return all the keys associated to it
17-
return keys(filter(copy(dict), $'v:val == "{target_value}"'))
18-
enddef
19-
20-
export def DictToListOfDicts(d: dict<any>): list<dict<any>>
21-
# Convert a dict in a list of dict.
22-
#
23-
# For example, {a: 'foo', b: 'bar', c: 'baz'} becomes
24-
# [{a: 'foo'}, {b: 'bar'}, {c: 'baz'}]
25-
#
26-
var list_of_dicts = []
27-
for [k, v] in items(d)
28-
add(list_of_dicts, {[k]: v})
29-
endfor
30-
return list_of_dicts
31-
enddef
32-
33-
export def ZipLists(l1: list<any>, l2: list<any>): list<list<any>>
34-
# Zip-like function, like in Python
35-
var min_len = min([len(l1), len(l2)])
36-
return map(range(min_len), $'[{l1}[v:val], {l2}[v:val]]')
37-
enddef
38-
39-
export def GetTextObject(textobject: string): dict<any>
40-
# You pass a text object like 'iw' and it returns the text
41-
# associated to it along with the start and end positions.
42-
#
43-
# Note that when you yank some text, the registers '[' and ']' are set, so
44-
# after call this function, you can retrieve start and end position of the
45-
# text-object by looking at such marks.
46-
#
47-
# The function also work with motions.
48-
49-
# Backup the content of register t (arbitrary choice, YMMV) and marks
50-
var oldreg = getreg("t")
51-
var saved_A = getcharpos("'[")
52-
var saved_B = getcharpos("']")
53-
# silently yank the text covered by whatever text object
54-
# was given as argument into register t. Yank also set marks '[ and ']
55-
noautocmd execute 'silent normal "ty' .. textobject
56-
57-
var text = getreg("t")
58-
var start_pos = getcharpos("'[")
59-
var end_pos = getcharpos("']")
60-
61-
# restore register t and marks
62-
setreg("t", oldreg)
63-
setcharpos("'[", saved_A)
64-
setcharpos("']", saved_B)
65-
66-
return {text: text, start: start_pos, end: end_pos}
67-
enddef
6814

6915
export def FormatWithoutMoving(a: number = 0, b: number = 0)
7016
# To be used for formatting through autocmds
@@ -92,48 +38,42 @@ export def FormatWithoutMoving(a: number = 0, b: number = 0)
9238
enddef
9339

9440
export def RemoveSurrounding()
95-
var interval = IsInRange()
96-
if !empty(interval)
41+
const style_interval = IsInRange()
42+
if !empty(style_interval)
43+
const style = keys(style_interval)[0]
44+
const interval = values(style_interval)[0]
45+
9746
# Remove left delimiter
98-
var lA = interval[0][1]
99-
var cA = interval[0][2]
100-
var newline =
101-
strcharpart(getline(lA), 0, cA - 1 - len(keys(open_delimiter_dict)[0]))
102-
.. strcharpart(getline(lA), cA - 1)
47+
const lA = interval[0][0]
48+
const cA = interval[0][1]
49+
var newline = strcharpart(getline(lA), 0,
50+
\ cA - 1 - len(constants.TEXT_STYLES_DICT[style].open_delim))
51+
\ .. strcharpart(getline(lA), cA - 1)
10352
setline(lA, newline)
10453

10554
# Remove right delimiter
106-
var lB = interval[1][1]
107-
var cB = interval[1][2]
55+
const lB = interval[1][0]
56+
var cB = interval[1][1]
10857
# The value of cB may no longer be valid since we shortened the line
10958
if lA == lB
110-
cB = cB - len(keys(open_delimiter_dict)[0])
59+
cB = cB - len(constants.TEXT_STYLES_DICT[style].open_delim)
11160
endif
11261

113-
newline =
114-
strcharpart(getline(lB), 0, cB)
115-
.. strcharpart(getline(lB), cB + len(keys(close_delimiter_dict)[0]))
62+
newline = strcharpart(getline(lB), 0, cB)
63+
\ .. strcharpart(getline(lB),
64+
\ cB + len(constants.TEXT_STYLES_DICT[style].open_delim))
11665
setline(lB, newline)
11766
endif
11867
enddef
11968

120-
export def SurroundSimple(open_delimiter: string,
121-
close_delimiter: string,
122-
open_delimiters_dict: dict<string>,
123-
close_delimiters_dict: dict<string>,
124-
type: string = '')
69+
export def SurroundSimple(style: string, type: string = '')
12570

12671
if getcharpos("'[") == getcharpos("']")
12772
return
12873
endif
12974

130-
var open_string = open_delimiter
131-
var open_regex = open_delimiters_dict[open_string]
132-
var open_delimiter_dict = {open_string: open_regex}
133-
134-
var close_string = close_delimiter
135-
var close_regex = close_delimiters_dict[close_string]
136-
var close_delimiter_dict = {close_string: close_regex}
75+
var open_delim = constants.TEXT_STYLES_DICT[style].open_delim
76+
var close_delim = constants.TEXT_STYLES_DICT[style].close_delim
13777

13878
# line and column of point A
13979
var lA = line("'[")
@@ -143,8 +83,8 @@ export def SurroundSimple(open_delimiter: string,
14383
var lB = line("']")
14484
var cB = col("']")
14585

146-
var toA = strcharpart(getline(lA), 0, cA - 1) .. open_string
147-
var fromB = close_string .. strcharpart(getline(lB), cB)
86+
var toA = strcharpart(getline(lA), 0, cA - 1) .. open_delim
87+
var fromB = close_delim .. strcharpart(getline(lB), cB)
14888

14989
# If on the same line
15090
if lA == lB
@@ -189,9 +129,11 @@ export def SurroundSmart(style: string, type: string = '')
189129
# We don't want to remove links between A and B
190130
var regex_for_removal = keys(constants.TEXT_STYLES_DICT)
191131
->filter("v:val !~ '\\v(markdownLinkText)'")
132+
# TODO here we should use open_regex and close_regex but then the substitute()
133+
# will replace also the \S :-(
192134
for k in regex_for_removal
193-
add(delimiters_to_remove, constants.TEXT_STYLES_DICT[k].open_regex)
194-
add(delimiters_to_remove, constants.TEXT_STYLES_DICT[k].close_regex)
135+
add(delimiters_to_remove, constants.TEXT_STYLES_DICT[k].open_delim)
136+
add(delimiters_to_remove, constants.TEXT_STYLES_DICT[k].close_delim)
195137
endfor
196138

197139
# line and column of point A
@@ -226,7 +168,6 @@ export def SurroundSmart(style: string, type: string = '')
226168
cursor(lA, cA)
227169
var old_right_delimiter = ''
228170
var found_interval = IsInRange()
229-
echom found_interval
230171
if !empty(found_interval)
231172
var found_style = keys(found_interval)[0]
232173
old_right_delimiter = constants.TEXT_STYLES_DICT[found_style].open_delim
@@ -294,11 +235,7 @@ export def SurroundSmart(style: string, type: string = '')
294235
var A_to_B = ''
295236
A_to_B = strcharpart(getline(lA), cA - 1, cB - cA + 1)
296237
if style != 'markdownCode'
297-
for regex in delimiters_to_remove
298-
A_to_B = A_to_B->substitute(regex, '', 'g')
299-
# echom regex
300-
echom "A_to_B: " .. A_to_B
301-
endfor
238+
A_to_B = A_to_B->substitute($'\V{join(delimiters_to_remove, '\|')}', '', 'g')
302239
endif
303240
# echom "A_to_B: " .. A_to_B
304241

@@ -314,19 +251,17 @@ export def SurroundSmart(style: string, type: string = '')
314251
# Set line A
315252
var afterA = strcharpart(getline(lA), cA - 1)
316253
if style != 'markdownCode'
317-
for regex in delimiters_to_remove
318-
afterA = afterA->substitute(regex, '', 'g')
319-
endfor
254+
afterA = afterA
255+
->substitute($'\V{join(delimiters_to_remove, '\|')}', '', 'g')
320256
endif
321257
var lineA = toA .. afterA
322258
setline(lA, lineA)
323259

324260
# Set line B
325261
var beforeB = strcharpart(getline(lB), 0, cB)
326262
if style != 'markdownCode'
327-
for regex in delimiters_to_remove
328-
beforeB = beforeB->substitute(regex, '', 'g')
329-
endfor
263+
beforeB = beforeB
264+
->substitute($'\V{join(delimiters_to_remove, '\|')}', '', 'g')
330265
endif
331266
var lineB = beforeB .. fromB
332267
setline(lB, lineB)
@@ -336,9 +271,8 @@ export def SurroundSmart(style: string, type: string = '')
336271
while lA + ii < lB
337272
var middleline = getline(lA + ii)
338273
if style != 'markdownCode'
339-
for regex in delimiters_to_remove
340-
middleline = middleline-> substitute(regex, '', 'g')
341-
endfor
274+
middleline = middleline
275+
->substitute($'\V{join(delimiters_to_remove, '\|')}', '', 'g')
342276
endif
343277
setline(lA + ii, middleline)
344278
ii += 1
@@ -385,116 +319,6 @@ export def SurroundToggle(open_delimiter: string,
385319
)
386320
enddef
387321

388-
export def GetTextBetweenMarks(A: string, B: string): list<string>
389-
# Usage: GetTextBetweenMarks("'A", "'B").
390-
#
391-
# Arguments must be marks called with the back ticks to get the exact
392-
# position ('a jump to the marker but places the cursor
393-
# at the beginning of the line.)
394-
#
395-
var [_, l1, c1, _] = getcharpos(A)
396-
var [_, l2, c2, _] = getcharpos(B)
397-
398-
if l1 == l2
399-
# Extract text within a single line
400-
return [getline(l1)[c1 - 1 : c2 - 1]]
401-
else
402-
# Extract text across multiple lines
403-
var lines = getline(l1, l2)
404-
lines[0] = lines[0][c1 - 1 : ] # Trim the first line from c1
405-
lines[-1] = lines[-1][ : c2 - 1] # Trim the last line up to c2
406-
return lines
407-
endif
408-
enddef
409-
410-
export def GetDelimitersRanges(
411-
open_delimiter_dict: dict<string>,
412-
close_delimiter_dict: dict<string>,
413-
): list<list<list<number>>>
414-
# It returns open-intervals, i.e. the delimiters are excluded.
415-
#
416-
# Passed delimiters are singleton dicts with key = the delimiter string,
417-
# value = the regex to exactly capture such a delimiter string
418-
#
419-
# It is assumed that the ranges have no intersections. This happens if
420-
# open_delimiter = close_delimiter, as in many languages.
421-
#
422-
# By contradiction, say that open_delimiter = * and close_delimiter = /. You may
423-
# have something like:
424-
# ----*---*===/---/-----
425-
# The part in === is an intersection between two ranges.
426-
# In these cases, this function will not work.
427-
# However, languages where open_delimiter = close_delimiter such intersections
428-
# cannot happen and this function apply.
429-
#
430-
var saved_cursor = getcursorcharpos()
431-
cursor(1, 1)
432-
433-
var ranges = []
434-
435-
var open_regex = values(open_delimiter_dict)[0]
436-
var open_string = keys(open_delimiter_dict)[0]
437-
var close_regex = values(close_delimiter_dict)[0]
438-
var close_string = keys(close_delimiter_dict)[0]
439-
440-
# 2D format due to that searchpos() returns a 2D vector
441-
var open_regex_pos_short = [-1, -1]
442-
var close_regex_pos_short = [-1, -1]
443-
var open_regex_pos_short_final = [-1, -1]
444-
var close_regex_pos_short_final = [-1, -1]
445-
446-
# 4D format due to that marks have 4-coordinates
447-
var open_regex_pos = [0] + open_regex_pos_short + [0]
448-
var open_regex_match = ''
449-
var close_regex_pos = [0] + close_regex_pos_short + [0]
450-
var close_regex_length = 0
451-
var close_regex_match = ''
452-
453-
while open_regex_pos_short != [0, 0]
454-
455-
# A. ------------ open_regex -----------------
456-
open_regex_pos_short = searchpos(open_regex, 'W')
457-
458-
# If the open delimiter is the tail of the line,
459-
# then the open-interval starts from the next line, column 1
460-
if open_regex_pos_short[1] + len(open_string) == col('$')
461-
open_regex_pos_short_final[0] = open_regex_pos_short[0] + 1
462-
open_regex_pos_short_final[1] = 1
463-
else
464-
# Pick the open-interval
465-
open_regex_pos_short_final[0] = open_regex_pos_short[0]
466-
open_regex_pos_short_final[1] = open_regex_pos_short[1]
467-
+ len(open_string)
468-
endif
469-
open_regex_pos = [0] + open_regex_pos_short_final + [0]
470-
471-
# B. ------ Close regex -------
472-
close_regex_pos_short = searchpos(close_regex, 'W')
473-
# TODO: if close_regex_pos_short = [0, 0] => anomaly! One tag has been
474-
# opened and never closed!
475-
476-
# If the closed delimiter is the lead of the line, then the open-interval
477-
# starts from the previous line, last column
478-
if close_regex_pos_short[1] - 1 == 0
479-
close_regex_pos_short_final[0] = close_regex_pos_short[0] - 1
480-
close_regex_pos_short_final[1] = len(getline(close_regex_pos_short_final[0]))
481-
else
482-
close_regex_pos_short_final[0] = close_regex_pos_short[0]
483-
close_regex_pos_short_final[1] = close_regex_pos_short[1] - 1
484-
endif
485-
close_regex_pos = [0] + close_regex_pos_short_final + [0]
486-
487-
add(ranges, [open_regex_pos, close_regex_pos])
488-
endwhile
489-
setcursorcharpos(saved_cursor[1 : 2])
490-
491-
# Remove the last element junky [[0,0,len(open_delimiter),0], [0,0,-1,0]]
492-
# TODO it does not seems to remove anything...
493-
remove(ranges, -1)
494-
495-
return ranges
496-
enddef
497-
498322
export def IsLess(l1: list<number>, l2: list<number>): bool
499323
# Lexicographic comparison on common prefix, i.e.for two vectors in N^n and
500324
# N^m you compare their projections onto the smaller subspace.
@@ -626,20 +450,6 @@ export def IsInRange(): dict<list<list<number>>>
626450
return return_val
627451
enddef
628452

629-
# Not used in markdown
630-
export def DeleteTextBetweenMarks(A: string, B: string): string
631-
# To jump to the exact position (and not at the beginning of a line) you
632-
# have to call the marker with the backtick ` rather than with ', e.g. `a
633-
# instead of 'a
634-
# TODO
635-
# This implementation most likely modify the jumplist.
636-
# Find a solution based on functions instead
637-
var exact_A = substitute(A, "'", "`", "")
638-
var exact_B = substitute(B, "'", "`", "")
639-
execute $'norm! {exact_A}v{exact_B}"_d'
640-
# This to get rid off E1186
641-
return ''
642-
enddef
643453

644454
export def SetBlock(open_block: dict<string>,
645455
close_block: dict<string>,

0 commit comments

Comments
 (0)