123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- local M = {}
- --[[ This comment is for LuaDoc.
- ---
- -- A module for hotexit - keeps unsaved buffers on quit
- module('_M.hotexit')]]
- -- for better random()
- math.randomseed(os.time())
- -- Base 64 encoding decoding from: https://stackoverflow.com/a/35303321
- local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' -- You will need this for encoding/decoding
- -- encoding
- function enc_base64(data)
- return ((data:gsub('.', function(x)
- local r,b='',x:byte()
- for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
- return r;
- end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
- if (#x < 6) then return '' end
- local c=0
- for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
- return b:sub(c+1,c+1)
- end)..({ '', '==', '=' })[#data%3+1])
- end
- -- decoding
- function dec_base64(data)
- data = string.gsub(data, '[^'..b..'=]', '')
- return (data:gsub('.', function(x)
- if (x == '=') then return '' end
- local r,f='',(b:find(x)-1)
- for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
- return r;
- end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
- if (#x ~= 8) then return '' end
- local c=0
- for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
- return string.char(c)
- end))
- end
- _UNSAVED_CHANGED = false
- -- when made changes on a saved buffer
- events.connect(events.SAVE_POINT_LEFT, function(str)
- -- we remember that this has not been saved on unsaved memory
- buffer.unsavedbuffer = true
- -- pending changes to be written to disk (changes are in RAM)
- buffer.unsavedpendingchanges = true
- -- we give unsaved buffer an id
- if buffer.unsavedid == nil then
- buffer.unsavedid = math.random(100001,999999)
- end
- -- we keep the time when it was modified
- -- we will use it later to determine if buffer changed after we last
- -- saved it to unsaved memory
- buffer.unsavedmod = os.time(os.date("!*t"))
- _UNSAVED_CHANGED = true
- end)
- events.connect(events.CHAR_ADDED, function(str)
- -- so that ui.print does not see it as a savable change
- if buffer.tab_label ~= '[Message Buffer]' then
- buffer.unsavedbuffer = true
- buffer.unsavedpendingchanges = true
- _UNSAVED_CHANGED = true
- end
- end)
- events.connect(events.SAVE_POINT_REACHED, function(str)
- buffer.unsavedbuffer = false
- buffer.unsavedpendingchanges = false
- end)
- function save_unsaveddata(force_save)
- if not force_save then force_save = false end
- -- when force_save is true, we won't respect the timeout.
- -- it will run the function right away if it's set to true
- -- regardless of what is set on _UNSAVED_CHANGED.
- if force_save == false and _UNSAVED_CHANGED == false then return '' end
- local buffer_filename = ''
- local buffer_filenames = {}
- local unsaved_buffer_count = 0
- os.execute("mkdir " .. _USERHOME..'/_hotexitdata')
- os.remove(_USERHOME..'/_hotexitdata/hotexitdata.txt')
- if (#_BUFFERS > 0) then
- file = io.open(_USERHOME..'/_hotexitdata/hotexitdata.txt', "a")
- for j = 1, #_BUFFERS do
- if (_BUFFERS[j].unsavedbuffer == true) then
- unsaved_buffer_count = unsaved_buffer_count + 1
- if not _BUFFERS[j].filename then
- buffer_filename = ''
- else
- buffer_filename = _BUFFERS[j].filename
- end
- --[[
- Data order:
- 1. the text "unsavedbuffer"
- 2. unsaved buffer serial number
- 3. buffer filename (blank if not saved anywhere)
- 4. unsaved buffer modified date
- 5. cursor position
- 6. lexer type for the unsaved buffer
- * Separated by double pipe characters (||)
- --]]
- file:write('unsavedbuffer||'.._BUFFERS[j].unsavedid..'||'..buffer_filename..'||'.._BUFFERS[j].unsavedmod..'||'.._BUFFERS[j].current_pos..'||'..buffer.get_lexer(_BUFFERS[j]), "\n")
- if _BUFFERS[j].unsavedpendingchanges == true then
- unsavedfile = io.open(_USERHOME..'/_hotexitdata/'.._BUFFERS[j].unsavedid..'.txt', "w") -- w to overwrite existing file
- unsavedfile:write(enc_base64(buffer.get_text(_BUFFERS[j])))
- io.close(unsavedfile)
- _BUFFERS[j].unsavedpendingchanges = false
- end
-
- -- keep the filename for cleanup later
- buffer_filenames[#buffer_filenames+1] = _BUFFERS[j].unsavedid .. '.txt'
- end
- end
- io.close(file)
- _UNSAVED_CHANGED = false
- -- cleanup
- clean_unsaveddata(buffer_filenames)
- end
- return false -- quit. returning true will not quit
- end
- -- gets the filenames in an array
- function get_files(dir)
- local dircmd = "ls -1 " .. dir -- default to Unix
- if string.sub(package.config,1,1) == '\\' then
- -- Windows
- dircmd = "dir /b " .. dir
- end
- --~ local files = os.execute(dircmd)
- local handle = io.popen(dircmd)
- local result = handle:read("*a")
- handle:close()
- -- get the files as array
- local files = explode("\n", result)
- --~ print(files)
- return files
- end
- -- Source: http://lua-users.org/wiki/MakingLuaLikePhp
- -- Credit: http://richard.warburton.it/
- function explode(div,str)
- if (div=='') then return false end
- local pos,arr = 0,{}
- for st,sp in function() return string.find(str,div,pos,true) end do
- table.insert(arr,string.sub(str,pos,st-1))
- pos = sp + 1
- end
- table.insert(arr,string.sub(str,pos))
- return arr
- end
- -- check if a string is there in an array
- local function array_has_value(tab, val)
- for index, value in ipairs(tab) do
- if value == val then
- return true
- end
- end
- return false
- end
- -- clean unsaved data files that we don't need
- -- (files that are no longer referenced in the hostexitdata.txt)
- function clean_unsaveddata(logged_buffers)
- local buffer_files = get_files(_USERHOME..'/_hotexitdata/')
- for fileCount = 1, #buffer_files do
- if buffer_files[fileCount] ~= 'hotexitdata.txt' and array_has_value(logged_buffers, buffer_files[fileCount]) == false then
- os.remove(_USERHOME .. '/_hotexitdata/' .. buffer_files[fileCount])
- end
- end
- end
- events.connect(events.QUIT, function(str)
- textadept.session.save( _USERHOME..'/session' )
- save_unsaveddata(true)
- return false
- end, 1) -- adding 1 prevents the default quit event function to run
- -- save unsaved data after certain intervals
- timeout(10, function()
- save_unsaveddata()
- return true -- true means repeat
- end)
- function file_exists(file)
- local f = io.open(file, "rb")
- if f then f:close() end
- return f ~= nil
- end
- function split(pString, pPattern)
- local Table = {n = 0} -- NOTE: use {n = 0} in Lua-5.0
- local fpat = "(.-)" .. pPattern
- local last_end = 1
- local s, e, cap = pString:find(fpat, 1)
- while s do
- if s ~= 1 or cap ~= "" then
- table.insert(Table,cap)
- end
- last_end = e+1
- s, e, cap = pString:find(fpat, last_end)
- end
- if last_end <= #pString then
- cap = pString:sub(last_end)
- table.insert(Table, cap)
- end
- return Table
- end
- -- return buffer index after searching for filename
- function getbufferbyfilename(filename)
- for j = 1, #_BUFFERS do
- if (_BUFFERS[j].filename == filename) then
- return j
- end
- end
- return nil
- end
- -- read all the content on file
- function readallcontent(file)
- local f = assert(io.open(file, "rb"))
- local content = f:read("*all")
- f:close()
- return content
- end
- -- read back the previously saved unsaved data
- function restore_unsaveddata()
- local unsaveddata_filename = _USERHOME..'/_hotexitdata/hotexitdata.txt'
- local line_data
- if not file_exists(unsaveddata_filename) then return {} end
- lines = {}
- for line in io.lines(unsaveddata_filename) do
- line_data = split(line, "||") -- index is 1 based
-
- local unsaveddata_buffer_filename = _USERHOME..'/_hotexitdata/'..line_data[2]..'.txt'
-
- if file_exists(unsaveddata_buffer_filename) then
- local buffer_index = getbufferbyfilename(line_data[3])
- if buffer_index ~= nil then -- buffer already exists
- _mybuffer = _BUFFERS[buffer_index]
- else
- _mybuffer = buffer.new()
- end
- -- fix: set the text so that it can undo to original text
- -- when buffer has unsaved change
- buffer.set_text(_mybuffer, dec_base64(readallcontent(unsaveddata_buffer_filename)))
- if (line_data[3] ~= '') then
- _mybuffer.filename = line_data[3]
- end
- buffer.goto_pos(_mybuffer, tonumber(line_data[5])) -- doesn't work properly for all buffers
- buffer.scroll_caret(_mybuffer)
- buffer.set_lexer(buffer, line_data[6])
- -- we indicate that we have this state saved
- _mybuffer.unsavedbuffer = true
- _mybuffer.unsavedid = line_data[2]
- _mybuffer.unsavedmod = line_data[4]
- _mybuffer.unsavedpendingchanges = false
- end
- end
- end
- events.connect(events.INITIALIZED, function(str)
- restore_unsaveddata()
- end)
- return M
|