11-- Some string processing utility functions
22local M = {}
33
4- --- In-place dedents strings in lines.
4+ --- In-place dedents strings in lines.
55--- @param lines string[].
66local function dedent (lines )
77 if # lines > 0 then
88 local ind_size = math.huge
99 for i , _ in ipairs (lines ) do
1010 local i1 , i2 = lines [i ]:find (" ^%s*[^%s]" )
11- if i1 and i2 < ind_size then
11+ if i1 and i2 and i2 < ind_size then
1212 ind_size = i2
1313 end
1414 end
@@ -18,7 +18,7 @@ local function dedent(lines)
1818 end
1919end
2020
21- --- Convert string `from` to unit indent
21+ --- In-place convert string `from` to unit indent in lines.
2222--- @param lines string[]
2323--- @param from string
2424--- @param unit_indent string
@@ -51,15 +51,17 @@ local function convert_indent(lines, from, unit_indent)
5151 end
5252end
5353
54- --- Applies opts to lines.
55- --- lines is modified in-place.
56- --- @param lines string[].
57- --- @param options table , required , can have values :
58- --- - trim_empty: removes empty first and last lines.
59- --- - dedent: removes indent common to all lines.
60- --- - indent_string: an unit indent at beginning of each line after applying `dedent`, default empty string (disabled)
61- function M .process_multiline (lines , options )
62- if options .trim_empty then
54+ --- @class LuaSnip.Opts.Str.MultilineProcess
55+ --- @field trim_empty ? boolean Whether to remove whitespace-only first /last lines
56+ --- @field dedent ? boolean Whether to remove all common indent in ` str` .
57+ --- @field indent_string ? string When set , will convert ` indent_string` at
58+ --- beginning of each line to unit indent ('\t') after applying `dedent`.
59+
60+ --- In-place process lines with given opts.
61+ --- @param lines string[]
62+ --- @param opts LuaSnip.Opts.Str.MultilineProcess
63+ function M .process_multiline (lines , opts )
64+ if opts .trim_empty then
6365 if lines [1 ]:match (" ^%s*$" ) then
6466 table.remove (lines , 1 )
6567 end
@@ -68,21 +70,28 @@ function M.process_multiline(lines, options)
6870 end
6971 end
7072
71- if options .dedent then
73+ if opts .dedent then
7274 dedent (lines )
7375 end
7476
75- if options .indent_string and # options .indent_string > 0 then
76- convert_indent (lines , options .indent_string , " \t " )
77+ if opts .indent_string and # opts .indent_string > 0 then
78+ convert_indent (lines , opts .indent_string , " \t " )
7779 end
7880end
7981
82+ --- Remove common indentation from the given string.
83+ --- @param s string
84+ --- @return string
8085function M .dedent (s )
8186 local lst = vim .split (s , " \n " )
8287 dedent (lst )
8388 return table.concat (lst , " \n " )
8489end
8590
91+ --- Convert string `indent_string` to unit indent (\t) in given string.
92+ --- @param s string
93+ --- @param indent_string string
94+ --- @return string
8695function M .convert_indent (s , indent_string )
8796 local lst = vim .split (s , " \n " )
8897 convert_indent (lst , indent_string , " \t " )
@@ -101,11 +110,12 @@ local function is_escaped(s, indx)
101110 return count % 2 == 1
102111end
103112
104- --- return position of next (relative to `start`) unescaped occurence of
113+ --- Return position of next (relative to `start`) unescaped occurence of
105114--- `target` in `s`.
106115--- @param s string
107116--- @param target string
108- --- @param start number
117+ --- @param start integer
118+ --- @return integer ?
109119local function find_next_unescaped (s , target , start )
110120 while true do
111121 local from = s :find (target , start , true )
125135--- @param s string
126136--- @param left string
127137--- @param right string
128- --- @return function : iterator , returns pairs from,to.
138+ --- @return fun (): ( integer ?, integer ?) An iterator returning pairs from,to.
129139function M .unescaped_pairs (s , left , right )
130140 local search_from = 1
131141
@@ -144,6 +154,7 @@ function M.unescaped_pairs(s, left, right)
144154 end
145155end
146156
157+ -- FIXME(@L3MON4D3): not used anywhere?
147158function M .aupatescape (s )
148159 if vim .fn .has (" win32" ) == 1 or vim .fn .has (" win64" ) == 1 then
149160 -- windows: replace \ with / for au-pattern.
@@ -153,20 +164,26 @@ function M.aupatescape(s)
153164 return vim .fn .fnameescape (escaped )
154165end
155166
167+ --- Sanitize the given string (e.g. \r)
168+ --- @param str string
169+ --- @return string
156170function M .sanitize (str )
157- return str :gsub (" %\r " , " " )
171+ local ret = str :gsub (" %\r " , " " )
172+ return ret -- note: local var required for correct typing
158173end
159174
160- -- requires that from and to are within the region of str.
161- -- str is treated as a 0,0-indexed, and the character at `to` is excluded from
162- -- the result.
163- -- `from` may not be before `to`.
164- function M .multiline_substr (str , from , to )
175+ --- Extract a rectangular block of lines in a multiline string area.
176+ --- @param lines string[]
177+ --- @param from LuaSnip.Pos00 From this position , MUST be within ` lines` .
178+ --- @param to LuaSnip.Pos00 To this position (excluded ), MUST be within ` lines`
179+ --- and before `from`.
180+ --- @return string[]
181+ function M .multiline_substr (lines , from , to )
165182 local res = {}
166183
167184 -- include all rows
168185 for i = from [1 ], to [1 ] do
169- table.insert (res , str [i + 1 ])
186+ table.insert (res , lines [i + 1 ])
170187 end
171188
172189 -- trim text before from and after to.
@@ -179,35 +196,42 @@ function M.multiline_substr(str, from, to)
179196 return res
180197end
181198
182- function M .multiline_upper (str )
183- for i , s in ipairs (str ) do
184- str [i ] = s :upper ()
199+ --- In-place uppercase all text in `lines`
200+ --- @param lines string[]
201+ function M .multiline_upper (lines )
202+ for i , s in ipairs (lines ) do
203+ lines [i ] = s :upper ()
185204 end
186205end
187- function M .multiline_lower (str )
188- for i , s in ipairs (str ) do
189- str [i ] = s :lower ()
206+
207+ --- In-place lowercase all text in `lines`
208+ --- @param lines string[]
209+ function M .multiline_lower (lines )
210+ for i , s in ipairs (lines ) do
211+ lines [i ] = s :lower ()
190212 end
191213end
192214
193215-- modifies strmod
216+ -- FIXME(@L3MON4D3): not used anywhere?
194217function M .multiline_append (strmod , strappend )
195218 strmod [# strmod ] = strmod [# strmod ] .. strappend [1 ]
196219 for i = 2 , # strappend do
197220 table.insert (strmod , strappend [i ])
198221 end
199222end
200223
201- -- turn a row+col-offset for a multiline-string (string[]) (where the column is
202- -- given in bytes and 0-based) into an offset (in bytes, 1-based) for
203- -- the \n-concatenated version of that string.
224+ --- Turns a row+col-offset for a multiline-string (string[]) (where the column is
225+ --- given in bytes and 0-based) into an offset (in bytes, 1-based) for
226+ --- the \n-concatenated version of that string.
204227---
205- --- @param str string[] , a multiline string
206- --- @param pos LuaSnip.ApiPosition , an api-position relative to the start of str.
207- function M .multiline_to_byte_offset (str , pos )
208- if pos [1 ] < 0 or pos [1 ] + 1 > # str or pos [2 ] < 0 then
228+ --- @param lines string[] a multiline string
229+ --- @param pos LuaSnip.ApiPosition an api-position relative to the start of str.
230+ --- @return integer ?
231+ function M .multiline_to_byte_offset (lines , pos )
232+ if pos [1 ] < 0 or pos [1 ] + 1 > # lines or pos [2 ] < 0 then
209233 -- pos is trivially (row negative or beyond str, or col negative)
210- -- outside of str , can't represent position in str .
234+ -- outside of lines , can't represent position in lines .
211235 -- col-wise outside will be determined later, but we want this
212236 -- precondition for following code.
213237 return nil
@@ -216,12 +240,12 @@ function M.multiline_to_byte_offset(str, pos)
216240 local byte_pos = 0
217241 for i = 1 , pos [1 ] do
218242 -- increase index by full lines, don't forget +1 for \n.
219- byte_pos = byte_pos + # str [i ] + 1
243+ byte_pos = byte_pos + # lines [i ] + 1
220244 end
221245
222246 -- allow positions one beyond the last character for all lines (even the
223247 -- last line).
224- if pos [2 ] >= # str [pos [1 ] + 1 ] + 1 then
248+ if pos [2 ] >= # lines [pos [1 ] + 1 ] + 1 then
225249 -- in this case, pos is outside of the multiline-region.
226250 return nil
227251 end
@@ -233,16 +257,18 @@ function M.multiline_to_byte_offset(str, pos)
233257 return byte_pos + 1
234258end
235259
236- -- inverse of multiline_to_byte_offset, 1-based byte to 0,0-based row,column.
237- --- @param str string[] , the multiline string
238- --- @param byte_pos number , a 1-based index into the \n-concatenated ` str` .
239- function M .byte_to_multiline_offset (str , byte_pos )
260+ --- Convert a 1-based byte index in a multiline string to 0,0-based row,column.
261+ --- (It is functionally the inverse of multiline_to_byte_offset)
262+ --- @param lines string[] the multiline string
263+ --- @param byte_pos number 1-based index into the \n-concatenated ` lines` .
264+ --- @return LuaSnip.Pos00 ?
265+ function M .byte_to_multiline_offset (lines , byte_pos )
240266 if byte_pos < 0 then
241267 return nil
242268 end
243269
244270 local byte_pos_so_far = 0
245- for i , line in ipairs (str ) do
271+ for i , line in ipairs (lines ) do
246272 -- line-length + \n.
247273 local line_i_end = byte_pos_so_far + # line + 1
248274 if byte_pos <= line_i_end then
@@ -256,27 +282,36 @@ end
256282-- string-operations implemented according to
257283-- https://github.com/microsoft/vscode/blob/71c221c532996c9976405f62bb888283c0cf6545/src/vs/editor/contrib/snippet/browser/snippetParser.ts#L372-L415
258284-- such that they can be used for snippet-transformations in vscode-snippets.
285+ --- @param str string
286+ --- @return string
259287local function capitalize (str )
260288 -- uppercase first character.
261- return str :gsub (" ^." , string.upper )
289+ local ret = str :gsub (" ^." , string.upper )
290+ return ret -- note: local var required for correct typing
262291end
292+ --- @param str string
293+ --- @return string
263294local function pascalcase (str )
264295 local pascalcased = " "
265296 for match in str :gmatch (" [a-zA-Z0-9]+" ) do
266297 pascalcased = pascalcased .. capitalize (match )
267298 end
268299 return pascalcased
269300end
301+ --- @param str string
302+ --- @return string
303+ local function camelcase (str )
304+ -- same as pascalcase, but first character lowercased.
305+ local ret = pascalcase (str ):gsub (" ^." , string.lower )
306+ return ret -- note: local var required for correct typing
307+ end
270308
271309M .vscode_string_modifiers = {
272310 upcase = string.upper ,
273311 downcase = string.lower ,
274312 capitalize = capitalize ,
275313 pascalcase = pascalcase ,
276- camelcase = function (str )
277- -- same as pascalcase, but first character lowercased.
278- return pascalcase (str ):gsub (" ^." , string.lower )
279- end ,
314+ camelcase = camelcase ,
280315}
281316
282317return M
0 commit comments