@@ -21,7 +21,6 @@ export def DictToListOfDicts(d: dict<any>): list<dict<any>>
2121 return list_of_dicts
2222enddef
2323
24-
2524export def ZipLists (l1: list <any> , l2: list <any> ): list <list<any> >
2625 # Zip function , like in Python
2726 var min_len = min ([len (l1), len (l2)])
@@ -92,31 +91,11 @@ def RemoveSurrounding(a: any, b: any)
9291 echom " TBD"
9392enddef
9493
95- export def Surround (open_delimiter: string ,
94+ def SurroundSmart (open_delimiter: string ,
9695 close_delimiter: string ,
9796 open_delimiters_dict: dict <string> ,
9897 close_delimiters_dict: dict <string> ,
9998 text_object: string = ' ' )
100- # Usage:
101- # Select text and hit <leader> + e .g . parenthesis
102- #
103- # ' open_delimiter' and ' close_delimiter' are the strings to add to the text.
104- # They also serves as keys for the dics ' open_delimiters_dict' and
105- # ' close_delimiters_dict' .
106- #
107- # We need dicts because we need the strings to add to the text for
108- # surrounding purposes, but also a mechanism to search the surrounding
109- # delimiters in the text.
110- # We need regex because the delimiters strings may not be disjoint (think
111- # for example, in the markdown case , you have ' *' delimiter which is
112- # contained in the ' **' delimiter ) and therefore we cannot find the
113- # delimiting string as -is .
114- # Finally, open_delimiters_dict[ii] is zipped with
115- # close_delimiters_dict[ii], therefore be sure that there is correspondence
116- # between opening and closing delimiters.
117- #
118- # Remember that Visual Selections and Text Objects are cousins.
119- # Also, remember that a yank set the marks ' [ and ' ].
12099
121100 var open_string = open_delimiter
122101 var open_regex = open_delimiters_dict[open_string]
@@ -125,159 +104,185 @@ export def Surround(open_delimiter: string,
125104 var close_string = close_delimiter
126105 var close_regex = close_delimiters_dict[close_string]
127106 var close_delimiter_dict = {close_string: close_regex}
107+ # Set marks
108+ var A = getcharpos (" '<" )
109+ var B = getcharpos (" '>" )
110+ if ! empty (text_object)
111+ # GetTextObject is called for setting ' [ and ' ] marks through a yank .
112+ GetTextObject (text_object)
113+ A = getcharpos (" '[" )
114+ B = getcharpos (" ']" )
115+ endif
128116
129- if ! empty (IsInRange (open_delimiter_dict, close_delimiter_dict))
130- RemoveSurrounding (open_regex, close_regex)
131- else
132- # Set marks
133- var A = getcharpos (" '<" )
134- var B = getcharpos (" '>" )
135- if ! empty (text_object)
136- # GetTextObject is called for setting ' [ and ' ] marks through a yank .
137- GetTextObject (text_object)
138- A = getcharpos (" '[" )
139- B = getcharpos (" ']" )
140- endif
117+ # marks - > (x ,y ) coordinates
118+ # line and column
119+ var lA = A[1 ]
120+ var cA = A[2 ]
141121
142- # marks - > (x ,y ) coordinates
143- # line and column
144- var lA = A[1 ]
145- var cA = A[2 ]
122+ # line and column
123+ var lB = B[1 ]
124+ var cB = B[2 ]
146125
147- # line and column
148- var lB = B[ 1 ]
149- var cB = B[ 2 ]
126+ if A == B
127+ return
128+ endif
150129
151- if A == B
152- return
153- endif
130+ # -------- SMART DELIMITERS BEGIN ---------------------------
131+ # We check conditions like the following and we adjust the style
132+ # delimiters
133+ # We assume that the existing style ranges are (C,D) and (E,F) and we want
134+ # to place (A,B) as in the picture
135+ #
136+ # - E------- A------------
137+ # ------------ F---------
138+ # ------------ C------ B--
139+ # -------- D-------------
140+ #
141+ # We want to get :
142+ #
143+ # - E------ FA------------
144+ # ----------------------
145+ # ------------------ BC--
146+ # -------- D-------------
147+ #
148+ # so that all the styles are visible
149+
150+ # We need a list - of- dicts [{a: ' foo' }, {b: ' bar' }, {c : ' baz' }]
151+ var open_delimiters_dict_list = DictToListOfDicts (open_delimiters_dict)
152+ var close_delimiters_dict_list = DictToListOfDicts (close_delimiters_dict)
153+
154+ # Check if A falls in an existing interval
155+ var found_delimiters_interval = []
156+ cursor (lA, cA)
157+ var old_right_delimiter = ' '
158+ for delim in ZipLists (open_delimiters_dict_list,
159+ close_delimiters_dict_list)
160+
161+ found_delimiters_interval = IsInRange (delim[0 ], delim[1 ])
154162
155- # -------- SMART DELIMITERS BEGIN ---------------------------
156- # We check conditions like the following and we adjust the style
157- # delimiters
158- # We assume that the existing style ranges are (C,D) and (E,F) and we want
159- # to place (A,B) as in the picture
160- #
161- # - E------- A------------
162- # ------------ F---------
163- # ------------ C------ B--
164- # -------- D-------------
165- #
166- # We want to get :
167- #
168- # - E------ FA------------
169- # ----------------------
170- # ------------------ BC--
171- # -------- D-------------
172- #
173- # so that all the styles are visible
174-
175- var open_delim_leftovers = copy (open_delimiters_dict)
176- var close_delim_leftovers = copy (close_delimiters_dict)
177- unlet open_delim_leftovers[open_string]
178- unlet close_delim_leftovers[close_string]
179-
180- # We need a list - of- dicts [{a: ' foo' }, {b: ' bar' }, {c : ' baz' }]
181- var open_delim_leftovers_list = DictToListOfDicts (open_delim_leftovers)
182- var close_delim_leftovers_list = DictToListOfDicts (close_delim_leftovers)
183-
184- # Check if A falls in an existing interval
185- var found_delimiters_interval = []
186- cursor (lA, cA)
187- var old_right_delimiter = ' '
188- for delim in ZipLists (open_delim_leftovers_list,
189- close_delim_leftovers_list)
190-
191- found_delimiters_interval = IsInRange (delim[0 ], delim[1 ])
192-
193- if ! empty (found_delimiters_interval)
194- old_right_delimiter = keys (delim[0 ])[0 ]
195- # Existing blocks shall be disjoint,
196- # so we can break as soon as we find a delimiter
197- break
198- endif
199- endfor
200-
201- # Try to preserve overlapping ranges by moving the delimiters.
202- # For example. If we have the pairs (C, D) and (E,F) as it follows:
203- # ------ C------- D------ E------ F
204- # and we want to add (A, B) as it follows
205- # ------ C--- A--- D----- E-- B--- F
206- # then the results becomes a mess. The idea is to move D before A and E
207- # after E, thus obtaining:
208- # ------ C-- DA----------- BE---- F
209- #
210- # TODO :
211- # If you don't want to try to automatically adjust existing ranges , then
212- # remove ' old_right_delimiter' and ' old_left_limiter' from what follows,
213- # AND don't remove anything between A and B
214- #
215- # TODO : the following is specifically designed for markdown, so if you use
216- # for other languages, you have to modify it!
217- var toA = ' '
218163 if ! empty (found_delimiters_interval)
219- toA = strcharpart ( getline (lA), 0 , cA - 1 ) - > substitute ( ' \s*$ ' , ' ' , ' ' )
220- .. $ ' {old_right_delimiter} {open_string} '
221- else
222- toA = strcharpart ( getline (lA), 0 , cA - 1 ) .. open_string
164+ old_right_delimiter = keys (delim[ 0 ])[ 0 ]
165+ # Existing blocks shall be disjoint,
166+ # so we can break as soon as we find a delimiter
167+ break
223168 endif
169+ endfor
170+
171+ # Try to preserve overlapping ranges by moving the delimiters.
172+ # For example. If we have the pairs (C, D) and (E,F) as it follows:
173+ # ------ C------- D------ E------ F
174+ # and we want to add (A, B) as it follows
175+ # ------ C--- A--- D----- E-- B--- F
176+ # then the results becomes a mess. The idea is to move D before A and E
177+ # after E, thus obtaining:
178+ # ------ C-- DA----------- BE---- F
179+ #
180+ # TODO :
181+ # If you don't want to try to automatically adjust existing ranges , then
182+ # remove ' old_right_delimiter' and ' old_left_limiter' from what follows,
183+ # AND don't remove anything between A and B
184+ #
185+ # TODO : the following is specifically designed for markdown, so if you use
186+ # for other languages, you have to modify it!
187+ var toA = ' '
188+ if ! empty (found_delimiters_interval)
189+ toA = strcharpart (getline (lA), 0 , cA - 1 )- >substitute (' \s*$' , ' ' , ' ' )
190+ .. $ ' {old_right_delimiter} {open_string}'
191+ else
192+ toA = strcharpart (getline (lA), 0 , cA - 1 ) .. open_string
193+ endif
194+
195+ # Check if B falls in an existing interval
196+ cursor (lB, cB)
197+ var old_left_delimiter = ' '
198+ found_delimiters_interval = []
199+ for delim in ZipLists (close_delimiters_dict_list,
200+ close_delimiters_dict_list)
201+
202+ found_delimiters_interval = IsInRange (delim[0 ], delim[0 ])
224203
225- # Check if B falls in an existing interval
226- cursor (lB, cB)
227- var old_left_delimiter = ' '
228- found_delimiters_interval = []
229- for delim in ZipLists (close_delim_leftovers_list,
230- close_delim_leftovers_list)
231-
232- found_delimiters_interval = IsInRange (delim[0 ], delim[0 ])
233-
234- if ! empty (found_delimiters_interval)
235- echom " delim: " .. string (delim)
236- old_left_delimiter = keys (delim[0 ])[0 ]
237- echom " foo: " .. old_left_delimiter
238- # Existing blocks shall be disjoint,
239- # so we can break as soon as we find a delimiter
240- break
241- endif
242- endfor
243-
244- var fromB = ' '
245204 if ! empty (found_delimiters_interval)
246- fromB = $ ' {close_string} {old_left_delimiter} '
247- .. strcharpart ( getline (lB), cB) - > substitute ( ' ^\s* ' , ' ' , ' ' )
248- else
249- # TODO : Non - smart delimiters
250- fromB = close_string .. strcharpart ( getline (lB), cB)
205+ echom " delim: " .. string (delim)
206+ old_left_delimiter = keys (delim[ 0 ])[ 0 ]
207+ # Existing blocks shall be disjoint,
208+ # so we can break as soon as we find a delimiter
209+ break
251210 endif
211+ endfor
252212
253- # ------- SMART DELIMITERS PART END -----------
254- # We have compute the partial strings until A and the partial string that
255- # leaves B. Existing delimiters are set .
256- # Next , we have to adjust the text between A and B, by removing all the
257- # possible delimiters left between them.
213+ var fromB = ' '
214+ if ! empty (found_delimiters_interval)
215+ fromB = $ ' {close_string} {old_left_delimiter}'
216+ .. strcharpart (getline (lB), cB)- >substitute (' ^\s*' , ' ' , ' ' )
217+ else
218+ # TODO : Non- smart delimiters
219+ fromB = close_string .. strcharpart (getline (lB), cB)
220+ endif
258221
259- var A_to_B = ' '
260- if lA == lB
261- # Overwrite everything that is in the middle
262- var junk_delimiters =
263- RegexList2RegexOR ( values (open_delimiters_dict), true)
222+ # ------- SMART DELIMITERS PART END -----------
223+ # We have compute the partial strings until A and the partial string that
224+ # leaves B. Existing delimiters are set .
225+ # Next , we have to adjust the text between A and B, by removing all the
226+ # possible delimiters left between them.
264227
265- # TODO : if smart delimiter , don't use substitute
266- A_to_B = strcharpart (getline (lA), cA - 1 , cB - cA + 1 )
267- - > substitute (junk_delimiters, ' ' , ' g' )
228+ var A_to_B = ' '
229+ if lA == lB
230+ # Overwrite everything that is in the middle
231+ var all_delimiters_regex =
232+ RegexList2RegexOR (values (open_delimiters_dict), true)
268233
269- setline (lA, toA .. A_to_B .. fromB)
234+ A_to_B = strcharpart (getline (lA), cA - 1 , cB - cA + 1 )
235+ - > substitute (all_delimiters_regex, ' ' , ' g' )
270236
271- elseif lB - lA == 1
272- echom " TBD"
273- else
274- echom " TBD"
275- endif
237+ setline (lA, toA .. A_to_B .. fromB)
276238
277- # echom ' toA: ' .. toA
278- # echom ' middle: ' .. AAA
279- # echom ' fromB: ' .. fromB
239+ elseif lB - lA == 1
240+ echom " TBD"
241+ else
242+ echom " TBD"
280243 endif
244+
245+ # echom ' toA: ' .. toA
246+ # echom ' middle: ' .. AAA
247+ # echom ' fromB: ' .. fromB
248+ enddef
249+
250+ export def SurroundToggle (open_delimiter: string ,
251+ close_delimiter: string ,
252+ open_delimiters_dict: dict <string> ,
253+ close_delimiters_dict: dict <string> ,
254+ text_object: string = ' ' )
255+ # Usage:
256+ # Select text and hit <leader> + e .g . parenthesis
257+ #
258+ # ' open_delimiter' and ' close_delimiter' are the strings to add to the text.
259+ # They also serves as keys for the dics ' open_delimiters_dict' and
260+ # ' close_delimiters_dict' .
261+ #
262+ # We need dicts because we need the strings to add to the text for
263+ # surrounding purposes, but also a mechanism to search the surrounding
264+ # delimiters in the text.
265+ # We need regex because the delimiters strings may not be disjoint (think
266+ # for example, in the markdown case , you have ' *' delimiter which is
267+ # contained in the ' **' delimiter ) and therefore we cannot find the
268+ # delimiting string as -is .
269+ # Finally, open_delimiters_dict[ii] is zipped with
270+ # close_delimiters_dict[ii], therefore be sure that there is correspondence
271+ # between opening and closing delimiters.
272+ #
273+ # Remember that Visual Selections and Text Objects are cousins.
274+ # Also, remember that a yank set the marks ' [ and ' ].
275+
276+
277+ if ! empty (IsInRange (open_delimiter_dict, close_delimiter_dict))
278+ RemoveSurrounding (open_regex, close_regex)
279+ else
280+ Surround (open_delimiter,
281+ close_delimiter,
282+ open_delimiters_dict,
283+ close_delimiters_dict,
284+ text_object
285+ )
281286enddef
282287
283288export def GetTextBetweenMarks (A: string , B: string ): list <string>
@@ -495,3 +500,12 @@ export def DeleteTextBetweenMarks(A: string, B: string): string
495500 # This to get rid off E1186
496501 return ' '
497502enddef
503+
504+
505+ # TODO
506+ export var Surround = SurroundSmart
507+ if exists (' g:markdown_extras_config' )
508+ && has_key (g: markdown_extras_config , ' smart_delimiters' )
509+ && g: markdown_extras_config [' smart_delimiters' ]
510+ Surround = SurroundSimple
511+ endif
0 commit comments