1 -- $Id: utf8.lua 179 2009-04-03 18:10:03Z pasta $
3 -- Provides UTF-8 aware string functions implemented in pure lua:
8 -- * utf8unicode(s, i, j)
9 -- * utf8gensub(s, sub_len)
10 -- * utf8find(str, regex, init, plain)
11 -- * utf8match(str, regex, init)
12 -- * utf8gmatch(str, regex, all)
13 -- * utf8gsub(str, regex, repl, limit)
15 -- If utf8data.lua (containing the lower<->upper case mappings) is loaded, these
16 -- additional functions are available:
20 -- All functions behave as their non UTF-8 aware counterparts with the exception
21 -- that UTF-8 characters are used instead of bytes for all units.
24 Copyright (c) 2006-2007, Kyle Smith
30 Redistribution and use in source and binary forms, with or without
31 modification, are permitted provided that the following conditions are met:
33 * Redistributions of source code must retain the above copyright notice,
34 this list of conditions and the following disclaimer.
35 * Redistributions in binary form must reproduce the above copyright
36 notice, this list of conditions and the following disclaimer in the
37 documentation and/or other materials provided with the distribution.
38 * Neither the name of the author nor the names of its contributors may be
39 used to endorse or promote products derived from this software without
40 specific prior written permission.
42 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
45 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
46 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
51 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56 -- UTF8-octets = *( UTF8-char )
57 -- UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
59 -- UTF8-2 = %xC2-DF UTF8-tail
60 -- UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
61 -- %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
62 -- UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
63 -- %xF4 %x80-8F 2( UTF8-tail )
64 -- UTF8-tail = %x80-BF
67 local byte = string.byte
68 local char = string.char
69 local dump = string.dump
70 local find = string.find
71 local format = string.format
72 local len = string.len
73 local lower = string.lower
74 local rep = string.rep
75 local sub = string.sub
76 local upper = string.upper
78 -- returns the number of bytes used by the UTF-8 character at byte i in s
79 -- also doubles as a UTF-8 character validator
80 local function utf8charbytes (s, i)
85 if type(s) ~= "string" then
86 error("bad argument #1 to 'utf8charbytes' (string expected, got ".. type(s).. ")")
88 if type(i) ~= "number" then
89 error("bad argument #2 to 'utf8charbytes' (number expected, got ".. type(i).. ")")
94 -- determine bytes needed for character, based on RFC 3629
96 if c > 0 and c <= 127 then
100 elseif c >= 194 and c <= 223 then
102 local c2 = byte(s, i + 1)
105 error("UTF-8 string terminated early")
109 if c2 < 128 or c2 > 191 then
110 error("Invalid UTF-8 character")
115 elseif c >= 224 and c <= 239 then
117 local c2 = byte(s, i + 1)
118 local c3 = byte(s, i + 2)
120 if not c2 or not c3 then
121 error("UTF-8 string terminated early")
125 if c == 224 and (c2 < 160 or c2 > 191) then
126 error("Invalid UTF-8 character")
127 elseif c == 237 and (c2 < 128 or c2 > 159) then
128 error("Invalid UTF-8 character")
129 elseif c2 < 128 or c2 > 191 then
130 error("Invalid UTF-8 character")
134 if c3 < 128 or c3 > 191 then
135 error("Invalid UTF-8 character")
140 elseif c >= 240 and c <= 244 then
142 local c2 = byte(s, i + 1)
143 local c3 = byte(s, i + 2)
144 local c4 = byte(s, i + 3)
146 if not c2 or not c3 or not c4 then
147 error("UTF-8 string terminated early")
151 if c == 240 and (c2 < 144 or c2 > 191) then
152 error("Invalid UTF-8 character")
153 elseif c == 244 and (c2 < 128 or c2 > 143) then
154 error("Invalid UTF-8 character")
155 elseif c2 < 128 or c2 > 191 then
156 error("Invalid UTF-8 character")
160 if c3 < 128 or c3 > 191 then
161 error("Invalid UTF-8 character")
165 if c4 < 128 or c4 > 191 then
166 error("Invalid UTF-8 character")
172 error("Invalid UTF-8 character")
176 -- returns the number of characters in a UTF-8 string
177 local function utf8len (s)
179 if type(s) ~= "string" then
180 for k,v in pairs(s) do print('"',tostring(k),'"',tostring(v),'"') end
181 error("bad argument #1 to 'utf8len' (string expected, got ".. type(s).. ")")
188 while pos <= bytes do
190 pos = pos + utf8charbytes(s, pos)
196 -- functions identically to string.sub except that i and j are UTF-8 characters
198 local function utf8sub (s, i, j)
206 -- only set l if i or j is negative
207 local l = (i >= 0 and j >= 0) or utf8len(s)
208 local startChar = (i >= 0) and i or l + i + 1
209 local endChar = (j >= 0) and j or l + j + 1
211 -- can't have start before end!
212 if startChar > endChar then
216 -- byte offsets to pass to string.sub
217 local startByte,endByte = 1,bytes
219 while pos <= bytes do
222 if length == startChar then
226 pos = pos + utf8charbytes(s, pos)
228 if length == endChar then
234 if startChar > length then startByte = bytes+1 end
235 if endChar < 1 then endByte = 0 end
237 return sub(s, startByte, endByte)
241 -- replace UTF-8 characters based on a mapping table
242 local function utf8replace (s, mapping)
244 if type(s) ~= "string" then
245 error("bad argument #1 to 'utf8replace' (string expected, got ".. type(s).. ")")
247 if type(mapping) ~= "table" then
248 error("bad argument #2 to 'utf8replace' (table expected, got ".. type(mapping).. ")")
256 while pos <= bytes do
257 charbytes = utf8charbytes(s, pos)
258 local c = sub(s, pos, pos + charbytes - 1)
260 newstr = newstr .. (mapping[c] or c)
262 pos = pos + charbytes
269 -- identical to string.upper except it knows about unicode simple case conversions
270 local function utf8upper (s)
271 return utf8replace(s, utf8_lc_uc)
274 -- identical to string.lower except it knows about unicode simple case conversions
275 local function utf8lower (s)
276 return utf8replace(s, utf8_uc_lc)
280 -- identical to string.reverse except that it supports UTF-8
281 local function utf8reverse (s)
283 if type(s) ~= "string" then
284 error("bad argument #1 to 'utf8reverse' (string expected, got ".. type(s).. ")")
293 local c = byte(s, pos)
294 while c >= 128 and c <= 191 do
299 charbytes = utf8charbytes(s, pos)
301 newstr = newstr .. sub(s, pos, pos + charbytes - 1)
309 -- http://en.wikipedia.org/wiki/Utf8
310 -- http://developer.coronalabs.com/code/utf-8-conversion-utility
311 local function utf8char(unicode)
312 if unicode <= 0x7F then return char(unicode) end
314 if (unicode <= 0x7FF) then
315 local Byte0 = 0xC0 + math.floor(unicode / 0x40);
316 local Byte1 = 0x80 + (unicode % 0x40);
317 return char(Byte0, Byte1);
320 if (unicode <= 0xFFFF) then
321 local Byte0 = 0xE0 + math.floor(unicode / 0x1000);
322 local Byte1 = 0x80 + (math.floor(unicode / 0x40) % 0x40);
323 local Byte2 = 0x80 + (unicode % 0x40);
324 return char(Byte0, Byte1, Byte2);
327 if (unicode <= 0x10FFFF) then
329 local Byte3= 0x80 + (code % 0x40);
330 code = math.floor(code / 0x40)
331 local Byte2= 0x80 + (code % 0x40);
332 code = math.floor(code / 0x40)
333 local Byte1= 0x80 + (code % 0x40);
334 code = math.floor(code / 0x40)
335 local Byte0= 0xF0 + code;
337 return char(Byte0, Byte1, Byte2, Byte3);
340 error 'Unicode cannot be greater than U+10FFFF!'
344 local shift_12 = 2^12
345 local shift_18 = 2^18
348 utf8unicode = function(str, i, j, byte_pos)
352 if i > j then return end
357 bytes = utf8charbytes(str,byte_pos)
358 ch = sub(str,byte_pos,byte_pos-1+bytes)
360 ch,byte_pos = utf8sub(str,i,i), 0
366 if bytes == 1 then unicode = byte(ch) end
368 local byte0,byte1 = byte(ch,1,2)
369 local code0,code1 = byte0-0xC0,byte1-0x80
370 unicode = code0*shift_6 + code1
373 local byte0,byte1,byte2 = byte(ch,1,3)
374 local code0,code1,code2 = byte0-0xE0,byte1-0x80,byte2-0x80
375 unicode = code0*shift_12 + code1*shift_6 + code2
378 local byte0,byte1,byte2,byte3 = byte(ch,1,4)
379 local code0,code1,code2,code3 = byte0-0xF0,byte1-0x80,byte2-0x80,byte3-0x80
380 unicode = code0*shift_18 + code1*shift_12 + code2*shift_6 + code3
383 return unicode,utf8unicode(str, i+1, j, byte_pos+bytes)
386 -- Returns an iterator which returns the next substring and its byte interval
387 local function utf8gensub(str, sub_len)
388 sub_len = sub_len or 1
391 return function(skip)
392 if skip then byte_pos = byte_pos + skip end
394 local start = byte_pos
396 if byte_pos > length then return end
397 char_count = char_count + 1
398 local bytes = utf8charbytes(str,byte_pos)
399 byte_pos = byte_pos+bytes
401 until char_count == sub_len
403 local last = byte_pos-1
404 local slice = sub(str,start,last)
405 return slice, start, last
409 local function binsearch(sortedTable, item, comp)
410 local head, tail = 1, #sortedTable
411 local mid = math.floor((head + tail)/2)
413 while (tail - head) > 1 do
414 if sortedTable[tonumber(mid)] > item then
419 mid = math.floor((head + tail)/2)
422 if sortedTable[tonumber(head)] == item then
423 return true, tonumber(head)
424 elseif sortedTable[tonumber(tail)] == item then
425 return true, tonumber(tail)
430 local function classMatchGenerator(class, plain)
435 local firstletter = true
436 local unmatch = false
438 local it = utf8gensub(class)
441 for c, _, be in it do
443 if not ignore and not plain then
447 table.insert(codes, utf8unicode(c))
450 if not firstletter then
459 table.insert(codes, utf8unicode(c))
461 table.remove(codes) -- removing '-'
462 table.insert(ranges, {table.remove(codes), utf8unicode(c)})
466 elseif ignore and not plain then
467 if c == 'a' then -- %a: represents all letters. (ONLY ASCII)
468 table.insert(ranges, {65, 90}) -- A - Z
469 table.insert(ranges, {97, 122}) -- a - z
470 elseif c == 'c' then -- %c: represents all control characters.
471 table.insert(ranges, {0, 31})
472 table.insert(codes, 127)
473 elseif c == 'd' then -- %d: represents all digits.
474 table.insert(ranges, {48, 57}) -- 0 - 9
475 elseif c == 'g' then -- %g: represents all printable characters except space.
476 table.insert(ranges, {1, 8})
477 table.insert(ranges, {14, 31})
478 table.insert(ranges, {33, 132})
479 table.insert(ranges, {134, 159})
480 table.insert(ranges, {161, 5759})
481 table.insert(ranges, {5761, 8191})
482 table.insert(ranges, {8203, 8231})
483 table.insert(ranges, {8234, 8238})
484 table.insert(ranges, {8240, 8286})
485 table.insert(ranges, {8288, 12287})
486 elseif c == 'l' then -- %l: represents all lowercase letters. (ONLY ASCII)
487 table.insert(ranges, {97, 122}) -- a - z
488 elseif c == 'p' then -- %p: represents all punctuation characters. (ONLY ASCII)
489 table.insert(ranges, {33, 47})
490 table.insert(ranges, {58, 64})
491 table.insert(ranges, {91, 96})
492 table.insert(ranges, {123, 126})
493 elseif c == 's' then -- %s: represents all space characters.
494 table.insert(ranges, {9, 13})
495 table.insert(codes, 32)
496 table.insert(codes, 133)
497 table.insert(codes, 160)
498 table.insert(codes, 5760)
499 table.insert(ranges, {8192, 8202})
500 table.insert(codes, 8232)
501 table.insert(codes, 8233)
502 table.insert(codes, 8239)
503 table.insert(codes, 8287)
504 table.insert(codes, 12288)
505 elseif c == 'u' then -- %u: represents all uppercase letters. (ONLY ASCII)
506 table.insert(ranges, {65, 90}) -- A - Z
507 elseif c == 'w' then -- %w: represents all alphanumeric characters. (ONLY ASCII)
508 table.insert(ranges, {48, 57}) -- 0 - 9
509 table.insert(ranges, {65, 90}) -- A - Z
510 table.insert(ranges, {97, 122}) -- a - z
511 elseif c == 'x' then -- %x: represents all hexadecimal digits.
512 table.insert(ranges, {48, 57}) -- 0 - 9
513 table.insert(ranges, {65, 70}) -- A - F
514 table.insert(ranges, {97, 102}) -- a - f
517 table.insert(codes, utf8unicode(c))
519 table.remove(codes) -- removing '-'
520 table.insert(ranges, {table.remove(codes), utf8unicode(c)})
527 table.insert(codes, utf8unicode(c))
529 table.remove(codes) -- removing '-'
530 table.insert(ranges, {table.remove(codes), utf8unicode(c)})
541 local function inRanges(charCode)
542 for _,r in ipairs(ranges) do
543 if r[1] <= charCode and charCode <= r[2] then
550 return function(charCode)
551 return binsearch(codes, charCode) or inRanges(charCode)
554 return function(charCode)
555 return charCode ~= -1 and not (binsearch(codes, charCode) or inRanges(charCode))
561 -- utf8sub with extra argument, and extra result value
562 local function utf8subWithBytes (s, i, j, sb)
570 -- only set l if i or j is negative
571 local l = (i >= 0 and j >= 0) or utf8len(s)
572 local startChar = (i >= 0) and i or l + i + 1
573 local endChar = (j >= 0) and j or l + j + 1
575 -- can't have start before end!
576 if startChar > endChar then
580 -- byte offsets to pass to string.sub
581 local startByte,endByte = 1,bytes
583 while pos <= bytes do
586 if length == startChar then
590 pos = pos + utf8charbytes(s, pos)
592 if length == endChar then
598 if startChar > length then startByte = bytes+1 end
599 if endChar < 1 then endByte = 0 end
601 return sub(s, startByte, endByte), endByte + 1
605 local cache = setmetatable({},{
608 local cachePlain = setmetatable({},{
611 local function matcherGenerator(regex, plain)
617 cache[regex] = matcher
619 cachePlain[regex] = matcher
621 local function simple(func)
631 local function star(func)
634 matcher:fullResetOnNextFunc()
641 local function minus(func)
644 matcher:fullResetOnNextStr()
649 local function question(func)
652 matcher:fullResetOnNextFunc()
659 local function capture(id)
661 local l = matcher.captures[id][2] - matcher.captures[id][1]
662 local captured = utf8sub(matcher.string, matcher.captures[id][1], matcher.captures[id][2])
663 local check = utf8sub(matcher.string, matcher.str, matcher.str + l)
664 if captured == check then
674 local function captureStart(id)
676 matcher.captures[id][1] = matcher.str
680 local function captureStop(id)
682 matcher.captures[id][2] = matcher.str - 1
687 local function balancer(str)
689 local bc, ec = utf8sub(str, 1, 1), utf8sub(str, 2, 2)
690 local skip = len(bc) + len(ec)
691 bc, ec = utf8unicode(bc), utf8unicode(ec)
693 if cC == ec and sum > 0 then
703 if sum == 0 or cC == -1 then
713 matcher.functions[1] = function(_)
714 matcher:fullResetOnNextStr()
715 matcher.seqStart = matcher.str
717 if (matcher.str > matcher.startStr and matcher.fromStart) or matcher.str >= matcher.stringLen then
719 matcher.seqStart = nil
726 local it = (function()
727 local gen = utf8gensub(regex)
733 for c, bs, be in it do
736 table.insert(matcher.functions, simple(classMatchGenerator(c, plain)))
739 if find('123456789', c, 1, true) then
741 table.insert(matcher.functions, simple(lastFunc))
744 table.insert(matcher.functions, capture(tonumber(c)))
747 table.insert(matcher.functions, simple(lastFunc))
751 b, skip = balancer(sub(regex, be + 1, be + 9))
752 table.insert(matcher.functions, b)
754 lastFunc = classMatchGenerator('%' .. c)
760 table.insert(matcher.functions, star(lastFunc))
763 error('invalid regex after ' .. sub(regex, 1, bs))
767 table.insert(matcher.functions, simple(lastFunc))
768 table.insert(matcher.functions, star(lastFunc))
771 error('invalid regex after ' .. sub(regex, 1, bs))
775 table.insert(matcher.functions, minus(lastFunc))
778 error('invalid regex after ' .. sub(regex, 1, bs))
782 table.insert(matcher.functions, question(lastFunc))
785 error('invalid regex after ' .. sub(regex, 1, bs))
789 matcher.fromStart = true
791 error('invalid regex after ' .. sub(regex, 1, bs))
794 if be == len(regex) then
797 error('invalid regex after ' .. sub(regex, 1, bs))
801 table.insert(matcher.functions, simple(lastFunc))
803 lastFunc, skip = classMatchGenerator(sub(regex, be + 1))
806 table.insert(matcher.functions, simple(lastFunc))
809 table.insert(matcher.captures, {})
810 table.insert(cs, #matcher.captures)
811 table.insert(matcher.functions, captureStart(cs[#cs]))
812 if sub(regex, be + 1, be + 1) == ')' then matcher.captures[#matcher.captures].empty = true end
815 table.insert(matcher.functions, simple(lastFunc))
818 local cap = table.remove(cs)
820 error('invalid capture: "(" missing')
822 table.insert(matcher.functions, captureStop(cap))
825 table.insert(matcher.functions, simple(lastFunc))
827 lastFunc = function(cC) return cC ~= -1 end
832 table.insert(matcher.functions, simple(lastFunc))
834 lastFunc = classMatchGenerator(c)
840 error('invalid capture: ")" missing')
843 table.insert(matcher.functions, simple(lastFunc))
846 table.insert(matcher.functions, function()
847 if matcher.toEnd and matcher.str ~= matcher.stringLen then
854 matcher.nextFunc = function(self)
855 self.func = self.func + 1
857 matcher.nextStr = function(self)
858 self.str = self.str + 1
860 matcher.strReset = function(self)
861 local oldReset = self.reset
863 self.reset = function(s)
868 matcher.fullResetOnNextFunc = function(self)
869 local oldReset = self.reset
870 local func = self.func +1
872 self.reset = function(s)
878 matcher.fullResetOnNextStr = function(self)
879 local oldReset = self.reset
880 local str = self.str + 1
881 local func = self.func
882 self.reset = function(s)
889 matcher.process = function(self, str, start)
893 self.startStr = (start >= 0) and start or utf8len(str) + start + 1
894 self.seqStart = self.startStr
895 self.str = self.startStr
896 self.stringLen = utf8len(str) + 1
900 self.reset = function(s)
904 -- local lastPos = self.str
907 while not self.stop do
908 if self.str < self.stringLen then
909 --[[ if lastPos < self.str then
910 print('last byte', lastByte)
911 ch, lastByte = utf8subWithBytes(str, 1, self.str - lastPos - 1, lastByte)
912 ch, lastByte = utf8subWithBytes(str, 1, 1, lastByte)
913 lastByte = lastByte - 1
915 ch, lastByte = utf8subWithBytes(str, self.str, self.str)
917 lastPos = self.str ]]
918 ch = utf8sub(str, self.str,self.str)
919 --print('char', ch, utf8unicode(ch))
920 self.functions[self.func](utf8unicode(ch))
922 self.functions[self.func](-1)
926 if self.seqStart then
928 for _,pair in pairs(self.captures) do
930 table.insert(captures, pair[1])
932 table.insert(captures, utf8sub(str, pair[1], pair[2]))
935 return self.seqStart, self.str - 1, unpack(captures)
943 local function utf8find(str, regex, init, plain)
944 local matcher = cache[regex] or matcherGenerator(regex, plain)
945 return matcher:process(str, init)
949 local function utf8match(str, regex, init)
951 local found = {utf8find(str, regex, init)}
954 return unpack(found, 3)
956 return utf8sub(str, found[1], found[2])
961 local function utf8gmatch(str, regex, all)
962 regex = (utf8sub(regex,1,1) ~= '^') and regex or '%' .. regex
965 local found = {utf8find(str, regex, lastChar)}
967 lastChar = found[2] + 1
968 if found[all and 1 or 3] then
969 return unpack(found, all and 1 or 3)
971 return utf8sub(str, found[1], found[2])
976 local function replace(repl, args)
978 if type(repl) == 'string' then
981 for c in utf8gensub(repl) do
991 ret = ret .. args[num]
998 elseif type(repl) == 'table' then
999 ret = repl[args[1] or args[0]] or ''
1000 elseif type(repl) == 'function' then
1002 ret = repl(unpack(args, 1)) or ''
1004 ret = repl(args[0]) or ''
1010 local function utf8gsub(str, regex, repl, limit)
1014 local it = utf8gmatch(str, regex, true)
1015 local found = {it()}
1017 while #found > 0 and limit ~= n do
1018 local args = {[0] = utf8sub(str, found[1], found[2]), unpack(found, 3)}
1019 ret = ret .. utf8sub(str, prevEnd, found[1] - 1)
1020 .. replace(repl, args)
1021 prevEnd = found[2] + 1
1025 return ret .. utf8sub(str, prevEnd), n
1031 utf8.reverse = utf8reverse
1032 utf8.char = utf8char
1033 utf8.unicode = utf8unicode
1034 utf8.gensub = utf8gensub
1035 utf8.byte = utf8unicode
1036 utf8.find = utf8find
1037 utf8.match = utf8match
1038 utf8.gmatch = utf8gmatch
1039 utf8.gsub = utf8gsub
1041 utf8.format = format
1046 function charAt(str, i)
1047 if i <= utf8.len(str) then
1048 return utf8.sub(str, i, i)
1054 if GetOption("autoclose") == nil then
1055 AddOption("autoclose", true)
1058 local autoclosePairs = {"\"\"", "''", "``", "()", "{}", "[]"}
1059 local autoNewlinePairs = {"()", "{}", "[]"}
1061 function onRune(r, v)
1062 if not GetOption("autoclose") then
1066 for i = 1, #autoclosePairs do
1067 if r == charAt(autoclosePairs[i], 2) then
1068 local curLine = v.Buf:Line(v.Cursor.Y)
1070 if charAt(curLine, v.Cursor.X+1) == charAt(autoclosePairs[i], 2) then
1072 v:CursorRight(false)
1076 if v.Cursor.X > 1 and (IsWordChar(charAt(curLine, v.Cursor.X-1)) or charAt(curLine, v.Cursor.X-1) == charAt(autoclosePairs[i], 1)) then
1080 if r == charAt(autoclosePairs[i], 1) then
1081 local curLine = v.Buf:Line(v.Cursor.Y)
1083 if v.Cursor.X == utf8.len(curLine) or not IsWordChar(charAt(curLine, v.Cursor.X+1)) then
1084 -- the '-' here is to derefence the pointer to v.Cursor.Loc which is automatically made
1085 -- when converting go structs to lua
1086 -- It needs to be dereferenced because the function expects a non pointer struct
1087 v.Buf:Insert(-v.Cursor.Loc, charAt(autoclosePairs[i], 2))
1095 function preInsertNewline(v)
1096 if not GetOption("autoclose") then
1100 local curLine = v.Buf:Line(v.Cursor.Y)
1101 local curRune = charAt(curLine, v.Cursor.X)
1102 local nextRune = charAt(curLine, v.Cursor.X+1)
1103 local ws = GetLeadingWhitespace(curLine)
1105 for i = 1, #autoNewlinePairs do
1106 if curRune == charAt(autoNewlinePairs[i], 1) then
1107 if nextRune == charAt(autoNewlinePairs[i], 2) then
1108 v:InsertNewline(false)
1110 v.Buf:Insert(-v.Cursor.Loc, "\n" .. ws)
1120 function preBackspace(v)
1121 if not GetOption("autoclose") then
1125 for i = 1, #autoclosePairs do
1126 local curLine = v.Buf:Line(v.Cursor.Y)
1127 if charAt(curLine, v.Cursor.X+1) == charAt(autoclosePairs[i], 2) and charAt(curLine, v.Cursor.X) == charAt(autoclosePairs[i], 1) then