linecol

function linecol( str, idx ) --> line, col

Description

Converts an index(idx) inside a multi-line string(str) into a line and column number.

Parameters

str

A multi-line string.

idx

Index inside the string.

Return Values

line

Line number of the index that starts with 1.

col

Column number of the index that starts with 1.

Code

--ZFUNC-linecol-v1
local function linecol( str, idx ) --> line, col
   --ZFUNC-asindex-v1
   local function asindex( i, length, fit )
      --ZFUNC-clamp-v1
      local function clamp( num, min, max )
         if num < min then return min
         elseif num > max then return max
         else return num
         end
      end

      if i < 0 then i = length + i + 1 end

      if fit then
         return clamp( i, 1, length )
      end

      return i
   end

   local n = #str
   if n < idx then idx = n
   elseif n < 1 then idx = 1
   end

   idx = asindex( idx, #str, true )

   local line = 1
   local col = 1

   for i = 1, idx-1 do
      if string.sub( str, i, i ) == "\n" then
         line = line + 1
         col = 1
      else
         col = col + 1
      end
   end

   return line, col
end

return linecol

Examples

local t = require( "tapered" )
local linecol = require( "linecol" )

-- should work with a single line
line, col = linecol( "abcdefgh", 4 )
t.is( 1, line )
t.is( 4, col )

line, col = linecol( "abcdefgh", 1 )
t.is( 1, line )
t.is( 1, col )

-- first and last value
line, col = linecol( "", 1 )
t.is( 1, line )
t.is( 1, col )

line, col = linecol( "abc", 3 )
t.is( 1, line )
t.is( 3, col )

-- should work with multi line strings
line, col = linecol( "\nabcd\n\nefgh\n\n", 9 )
t.is( 4, line )
t.is( 2, col )

line, col = linecol( "abc\ndef\nghi", -1 )
t.is( 3, line )
t.is( 3, col )

-- should trim out of word values
line, col = linecol( "abc\ndef\nghi", 0 )
t.is( 1, line )
t.is( 1, col )

line, col = linecol( "abc\ndef\nghi", 20 )
t.is( 3, line )
t.is( 3, col )

t.done()