local NEWLINE=string.byte('\n',1)
local wv = require 'nylon.debug'{ name = 'pls-buffer' }
local Store = require 'pls-textstore'
local Buffer = setmetatable({}, { __index = Store })
function Buffer:new()
return Store.new(Buffer)
end
function Buffer.search_forward_to( buffer, startPoint, pat )
local point = startPoint
if #pat == 1 or (#pat==2 and pat:sub(1,1) == '%') then
while not buffer:char_at_point(point):find(pat) do
if point < buffer:end_point() then
point = point + 1
else
return
end
end
return point
else
for i = startPoint, buffer:end_point() do
local sub = buffer:sub( i, i+#pat-1 )
wv.log('debug',"search-forward pt=%d pat='%s' sub='%s'", i, pat, sub)
if sub:find(pat) then
return i
end
end
end
end
function Buffer.search_forward_past( buffer, startPoint, pat )
local point = startPoint
if #pat == 1 or (#pat==2 and pat:sub(1,1) == '%') then
while buffer:char_at_point(point):find(pat) and
point <= buffer:end_point() do
point = point + 1
end
return point <= buffer:end_point() and point
else
for i = startPoint, buffer:end_point() do
local sub = buffer:sub( i, i+(#pat*2) )
if not sub:find(pat) then
return point
end
end
end
end
function Buffer.search_backward_to( buffer, startPoint, pat )
local point = startPoint
if #pat == 1 then
local c = string.byte(pat,1)
while buffer:char_at_point_dec(point) ~= c do
if point <= 1 then
return
end
point = point - 1
end
return point
elseif #pat==2 and pat:sub(1,1) == '%' then
while not buffer:char_at_point(point):find(pat) do
if point <= 1 then
return
end
point = point - 1
end
return point
else
error 'not implemented, multi-char search forward'
end
end
function Buffer.search_backward_past( buffer, startPoint, pat )
local point = startPoint
if #pat == 1 or (#pat==2 and pat:sub(1,1) == '%') then
while buffer:char_at_point(point):find(pat) do
if point <= 1 then
return
end
point = point - 1
end
return point
else
error 'not implemented, multi-char search forward'
end
end
function Buffer:beginning_of_line( startPoint )
local point = (self:char_at_point_dec(startPoint) == NEWLINE and startPoint > 1) and (startPoint-1) or startPoint
local found = self:search_backward_to(point,'\n')
if found then
return (found + 1)
else
return 1
end
end
function Buffer.end_of_line( buffer, startPoint )
local nexteol = buffer:search_forward_to(startPoint, '\n')
return nexteol and nexteol or buffer:end_point() + 1
end
function Buffer.backward_lines( buffer, startPoint, nlines )
local point
if startPoint > buffer:end_point() +1 then
point = buffer:end_point()
nlines = nlines - 1
else
point = startPoint
end
local linestart = point
while buffer:char_at_point_dec(point) == NEWLINE do
if point ~= linestart then
if nlines > 0 then
nlines = nlines - 1
else
return point
end
end
point = point - 1
end
local lastfound
while nlines >= 0 do
local found = Buffer.search_backward_to( buffer, point, '\n')
if not found or found == point then
break
end
lastfound = found
if found <= 1 then
break
end
point = found
while (buffer:char_at_point_dec(point) == NEWLINE) and nlines >= 0 and point > 1 do
point = point - 1
nlines = nlines - 1
end
end
if lastfound then
return lastfound + 1
end
end
function Buffer.forward_word(buffer,point)
if not buffer:char_at_point(point):find('%w') then
local p = buffer:search_forward_to(point,'%w')
point = p or point
end
return buffer:search_forward_past( point, '%w' )
end
function Buffer:backward_word(point)
if point <= 2 then
return 1
end
if self:char_at_point(point):find('%w') then
if self:char_at_point(point-1):find('%w') then
local justBeforeWord = self:search_backward_past(point-1, '%w')
return justBeforeWord and justBeforeWord + 1 or 1
else
end
end
local lastLetterOfPreviousWord = self:search_backward_to( point-1, '%w' )
if not lastLetterOfPreviousWord or lastLetterOfPreviousWord < 2 then
return 1 end
local bpw = self:search_backward_past( lastLetterOfPreviousWord-1, '%w' )
return bpw and (bpw+1) or 1
end
function Buffer:word_at_point(point)
local endOfWord = self:forward_word(point) or self:end_point()
local beginningOfWord = self:backward_word(endOfWord)
if endOfWord > beginningOfWord then
return self:sub(beginningOfWord,endOfWord-1), beginningOfWord, endOfWord-1
end
end
function Buffer.asOneString( buffer )
local all = {}
for l in buffer:walkFragments(1) do
table.insert(all,l)
end
return table.concat(all)
end
function Buffer.forward_lines( buffer, startPoint, nlines )
local point = startPoint
for l, col, nl in buffer:walkFragmentsEOL(startPoint) do
if nl then
point = point + (nl-col) + 1
if nlines <= 1 then
return point
else
nlines = nlines - 1
end
else
if not l then
return (point ~= startPoint) and point
else
point = point + (#l - col) + 1
end
end
end
return point
end
function Buffer:insertFileAtPoint( point, fname )
local f = io.open(fname,'r')
if f then
for l in f:lines() do
local fix = l:gsub('\t',' ') .. '\n'
self:insert( point, fix )
point = point + #fix
end
f:close()
end
end
local function buffer_openFile( fname )
local f = io.open(fname,'r')
if f then
local buffer = Buffer:new()
for l in f:lines() do
buffer:append(l:gsub('\t',' ') .. '\n')
end
f:close()
end
return buffer
end
local function buffer_withText(t)
local buffer = Buffer:new()
if t then
buffer:append(t)
end
return buffer
end
return {
openFile = buffer_openFile,
withText = buffer_withText
}