cleanup + undo overhaul

This commit is contained in:
kepler155c@gmail.com
2020-04-10 22:51:56 -06:00
parent 3321866ba3
commit 569dd5572d

View File

@@ -1,5 +1,6 @@
local fuzzy = require('opus.fuzzy') local fuzzy = require('opus.fuzzy')
local UI = require('opus.ui') local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors local colors = _G.colors
local fs = _G.fs local fs = _G.fs
@@ -9,6 +10,14 @@ local shell = _ENV.shell
local term = _G.term local term = _G.term
local textutils = _G.textutils local textutils = _G.textutils
local _format = string.format
local _rep = string.rep
local _sub = string.sub
local _concat = table.concat
local _insert = table.insert
local _remove = table.remove
local _unpack = table.unpack
local x, y = 1, 1 local x, y = 1, 1
local w, h = term.getSize() local w, h = term.getSize()
local scrollX = 0 local scrollX = 0
@@ -21,7 +30,7 @@ local lastSave
local dirty = { y = 1, ey = h } local dirty = { y = 1, ey = h }
local mark = { } local mark = { }
local searchPattern local searchPattern
local undo = { chain = { }, pointer = 0 } local undo = { chain = { } }
h = h - 1 h = h - 1
@@ -47,9 +56,9 @@ local keyMapping = {
down = 'down', down = 'down',
left = 'left', left = 'left',
right = 'right', right = 'right',
pageUp = 'pageUp', pageUp = 'page_up',
[ 'control-b' ] = 'pageUp', [ 'control-b' ] = 'page_up',
pageDown = 'pageDown', pageDown = 'page_down',
home = 'home', home = 'home',
[ 'end' ] = 'toend', [ 'end' ] = 'toend',
[ 'control-home' ] = 'top', [ 'control-home' ] = 'top',
@@ -77,6 +86,7 @@ local keyMapping = {
[ 'shift-home' ] = 'mark_home', [ 'shift-home' ] = 'mark_home',
[ 'mouse_down' ] = 'mark_anchor', [ 'mouse_down' ] = 'mark_anchor',
[ 'mouse_doubleclick' ] = 'mark_current_word', [ 'mouse_doubleclick' ] = 'mark_current_word',
[ 'mouse_tripleclick' ] = 'mark_line',
-- editing -- editing
delete = 'delete', delete = 'delete',
@@ -91,6 +101,7 @@ local keyMapping = {
-- copy/paste -- copy/paste
[ 'control-x' ] = 'cut', [ 'control-x' ] = 'cut',
[ 'control-c' ] = 'copy', [ 'control-c' ] = 'copy',
[ 'control-y' ] = 'paste_internal',
-- file -- file
[ 'control-s' ] = 'save', [ 'control-s' ] = 'save',
@@ -126,7 +137,7 @@ local page = UI.Page {
{ text = 'Edit', dropdown = { { text = 'Edit', dropdown = {
{ text = 'Cut ^x', event = 'menu_action', action = 'cut' }, { text = 'Cut ^x', event = 'menu_action', action = 'cut' },
{ text = 'Copy ^c', event = 'menu_action', action = 'copy' }, { text = 'Copy ^c', event = 'menu_action', action = 'copy' },
{ text = 'Paste ^V', event = 'menu_action', action = 'paste_internal' }, { text = 'Paste ^y,^V', event = 'menu_action', action = 'paste_internal' },
{ spacer = true }, { spacer = true },
{ text = 'Find... ^f', event = 'menu_action', action = 'find_prompt' }, { text = 'Find... ^f', event = 'menu_action', action = 'find_prompt' },
{ text = 'Find Next ^n', event = 'menu_action', action = 'find_next' }, { text = 'Find Next ^n', event = 'menu_action', action = 'find_next' },
@@ -220,7 +231,7 @@ local page = UI.Page {
}, },
}, },
show = function(self) show = function(self)
self.filename.value = fileInfo.abspath self.filename:setValue(fileInfo.abspath)
self.filename:setPosition(#self.filename.value) self.filename:setPosition(#self.filename.value)
UI.MiniSlideOut.show(self) UI.MiniSlideOut.show(self)
end, end,
@@ -295,11 +306,11 @@ local page = UI.Page {
}, },
}, },
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
x = 2, y = 4, ex = -2, ey = -4, x = 2, y = 3, ex = -2, ey = -4,
sortColumn = 'name', disableHeader = true,
columns = { columns = {
{ heading = 'Name', key = 'name' }, { key = 'name' },
{ heading = 'Dir', key = 'dir' }, { key = 'dir', textColor = colors.lightGray },
}, },
accelerators = { accelerators = {
grid_select = 'accept', grid_select = 'accept',
@@ -308,7 +319,7 @@ local page = UI.Page {
cancel = UI.Button { cancel = UI.Button {
x = -9, y = -2, x = -9, y = -2,
text = 'Cancel', text = 'Cancel',
event = 'quick_cancel', event = 'slide_hide',
}, },
apply_filter = function(self, filter) apply_filter = function(self, filter)
local t = { } local t = { }
@@ -320,17 +331,16 @@ local page = UI.Page {
for _,v in pairs(self.listing) do for _,v in pairs(self.listing) do
v.score = fuzzy(v.lname, filter) v.score = fuzzy(v.lname, filter)
if v.score then if v.score then
table.insert(t, v) _insert(t, v)
end end
end end
else else
self.grid.sortColumn = 'name' self.grid.sortColumn = 'lname'
self.grid.inverseSort = false self.grid.inverseSort = false
t = self.listing t = self.listing
end end
self.grid:setValues(t) self.grid:setValues(t)
self.grid:update()
self.grid:setIndex(1) self.grid:setIndex(1)
end, end,
show = function(self) show = function(self)
@@ -340,9 +350,9 @@ local page = UI.Page {
for _,f in ipairs(files) do for _,f in ipairs(files) do
local fullName = fs.combine(dir, f) local fullName = fs.combine(dir, f)
if fs.native.isDir(fullName) then if fs.native.isDir(fullName) then
recurse(fullName) if f ~= '.git' then recurse(fullName) end
else else
table.insert(listing, { _insert(listing, {
name = f, name = f,
dir = dir, dir = dir,
lname = f:lower(), lname = f:lower(),
@@ -371,9 +381,6 @@ local page = UI.Page {
self:hide() self:hide()
end end
elseif event.type == 'quick_cancel' then
self:hide()
elseif event.type == 'text_change' then elseif event.type == 'text_change' then
self:apply_filter(event.text) self:apply_filter(event.text)
self.grid:draw() self.grid:draw()
@@ -395,7 +402,7 @@ local page = UI.Page {
accelerators = { accelerators = {
[ ' ' ] = 'down', [ ' ' ] = 'down',
backspace = 'slide_hide', backspace = 'slide_hide',
} },
}, },
show = function(self, values) show = function(self, values)
local m = 12 local m = 12
@@ -443,7 +450,7 @@ local page = UI.Page {
UI.Window.resize(self) UI.Window.resize(self)
w, h = self.width, self.height w, h = self.width, self.height
actions.setCursor(x, y) actions.set_cursor(x, y)
actions.dirty_all() actions.dirty_all()
actions.redraw() actions.redraw()
end, end,
@@ -461,7 +468,6 @@ local page = UI.Page {
elseif ie.code == "mouse_click" or elseif ie.code == "mouse_click" or
ie.code == 'mouse_drag' or ie.code == 'mouse_drag' or
--ie.code == 'mouse_up' or
ie.code == 'shift-mouse_click' or ie.code == 'shift-mouse_click' or
ie.code == 'mouse_down' or ie.code == 'mouse_down' or
ie.code == 'mouse_doubleclick' then ie.code == 'mouse_doubleclick' then
@@ -528,121 +534,50 @@ local function getFileInfo(path)
return fi return fi
end end
local function setStatus(pattern, ...) local keywords = Util.transpose {
page.notification:info(string.format(pattern, ...)) 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if',
end 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while'
local function setError(pattern, ...)
page.notification:error(string.format(pattern, ...))
end
local function save( _sPath )
-- Create intervening folder
local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() )
if not fs.exists( sDir ) then
fs.makeDir( sDir )
end
-- Save
local file = nil
local function innerSave()
file = fs.open( _sPath, "w" )
if file then
for _,sLine in ipairs( tLines ) do
file.write(sLine .. "\n")
end
else
error( "Failed to open ".._sPath )
end
end
local ok, err = pcall( innerSave )
if file then
file.close()
end
return ok, err
end
local function split(str, pattern)
pattern = pattern or "(.-)\n"
local t = {}
local function helper(line) table.insert(t, line) return "" end
helper((str:gsub(pattern, helper)))
return t
end
local tKeywords = {
["and"] = true,
["break"] = true,
["do"] = true,
["else"] = true,
["elseif"] = true,
["end"] = true,
["false"] = true,
["for"] = true,
["function"] = true,
["if"] = true,
["in"] = true,
["local"] = true,
["nil"] = true,
["not"] = true,
["or"] = true,
["repeat"] = true,
["return"] = true,
["then"] = true,
["true"] = true,
["until"]= true,
["while"] = true,
} }
local function writeHighlighted(sLine, ny, dy) local function writeHighlighted(sLine, ny, dy)
local buffer = { local buffer = { fg = { }, text = { } }
fg = '',
text = '',
}
local function tryWrite(line, regex, fgcolor) local function tryWrite(line, regex, fgcolor)
local match = line:match(regex) local match = line:match(regex)
if match then if match then
local fg = type(fgcolor) == "string" and fgcolor or fgcolor(match) local fg = type(fgcolor) == "string" and fgcolor or fgcolor(match)
buffer.text = buffer.text .. match _insert(buffer.text, match)
buffer.fg = buffer.fg .. string.rep(fg, #match) _insert(buffer.fg, _rep(fg, #match))
return line:sub(#match + 1) return _sub(line, #match + 1)
end end
return nil return nil
end end
while #sLine > 0 do while #sLine > 0 do
sLine = sLine =
-- tryWrite(sLine, "^[%\26]", '7' ) or
tryWrite(sLine, "^%-%-%[%[.-%]%]", color.commentColor ) or tryWrite(sLine, "^%-%-%[%[.-%]%]", color.commentColor ) or
tryWrite(sLine, "^%-%-.*", color.commentColor ) or tryWrite(sLine, "^%-%-.*", color.commentColor ) or
tryWrite(sLine, "^\".-[^\\]\"", color.stringColor ) or tryWrite(sLine, "^\".-[^\\]\"", color.stringColor ) or
tryWrite(sLine, "^\'.-[^\\]\'", color.stringColor ) or tryWrite(sLine, "^\'.-[^\\]\'", color.stringColor ) or
tryWrite(sLine, "^%[%[.-%]%]", color.stringColor ) or tryWrite(sLine, "^%[%[.-%]%]", color.stringColor ) or
tryWrite(sLine, "^[%w_]+", function(match) tryWrite(sLine, "^[%w_]+", function(match)
if tKeywords[match] then return keywords[match] and color.keywordColor or color.textColor
return color.keywordColor
end
return color.textColor
end) or end) or
tryWrite(sLine, "^[^%w_]", color.textColor) tryWrite(sLine, "^[^%w_]", color.textColor)
end end
buffer.fg = buffer.fg .. '7' buffer.fg = _concat(buffer.fg) .. '7'
buffer.text = buffer.text .. '\183' buffer.text = _concat(buffer.text) .. '\183'
if mark.active and ny >= mark.y and ny <= mark.ey then if mark.active and ny >= mark.y and ny <= mark.ey then
local sx = ny == mark.y and mark.x or 1 local sx = ny == mark.y and mark.x or 1
local ex = #buffer.text local ex = ny == mark.ey and mark.ex or #buffer.text
if ny == mark.ey then buffer.bg = _rep('f', sx - 1) ..
ex = mark.ex _rep('7', ex - sx) ..
end _rep('f', #buffer.text - ex + 1)
buffer.bg = string.rep('f', sx - 1) ..
string.rep('7', ex - sx) ..
string.rep('f', #buffer.text - ex + 1)
else else
buffer.bg = string.rep('f', #buffer.text) buffer.bg = _rep('f', #buffer.text)
end end
page.editor:blit(1 - scrollX, dy, buffer.text, buffer.bg, buffer.fg) page.editor:blit(1 - scrollX, dy, buffer.text, buffer.bg, buffer.fg)
@@ -651,7 +586,6 @@ end
local function redraw() local function redraw()
if dirty.y > 0 then if dirty.y > 0 then
for dy = 1, h do for dy = 1, h do
local sLine = tLines[dy + scrollY] local sLine = tLines[dy + scrollY]
if sLine ~= nil then if sLine ~= nil then
if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then
@@ -665,7 +599,7 @@ local function redraw()
end end
local modifiedIndicator = undo.chain[#undo.chain] == lastSave and ' ' or '*' local modifiedIndicator = undo.chain[#undo.chain] == lastSave and ' ' or '*'
page.menuBar.status.value = string.format(' %d:%d%s', y, x, modifiedIndicator) page.menuBar.status.value = _format('%d:%d%s', y, x, modifiedIndicator)
page.menuBar.status:draw() page.menuBar.status:draw()
if page.editor.focused then if page.editor.focused then
@@ -688,36 +622,34 @@ local function nextWord(line, cx)
end end
actions = { actions = {
info = function(pattern, ...)
page.notification:info(_format(pattern, ...))
end,
error = function(pattern, ...)
page.notification:error(_format(pattern, ...))
end,
undo = function() undo = function()
local last = table.remove(undo.chain) local last = _remove(undo.chain)
if last then if last then
undo.active = true undo.active = true
actions[last.action](table.unpack(last.args)) for i = #last, 1, -1 do
local u = last[i]
actions[u.action](_unpack(u.args))
end
undo.active = false undo.active = false
else else
setStatus('Already at oldest change') actions.info('already at oldest change')
end end
end, end,
addUndo = function(entry) undo_add = function(entry)
local last = undo.chain[#undo.chain] local last = undo.chain[#undo.chain]
if last and last.action == entry.action then if last and undo.continue then
if last.action == 'deleteText' then table.insert(last, entry)
if last.args[3] == entry.args[1] and
last.args[4] == entry.args[2] then
last.args = {
last.args[1], last.args[2], entry.args[3], entry.args[4],
last.args[5] .. entry.args[5]
}
else else
table.insert(undo.chain, entry) _insert(undo.chain, { entry })
end
else
-- insertText (need to finish)
table.insert(undo.chain, entry)
end
else
table.insert(undo.chain, entry)
end end
end, end,
@@ -726,7 +658,7 @@ actions = {
local results = sLine and textutils.complete(sLine, _ENV) or { } local results = sLine and textutils.complete(sLine, _ENV) or { }
if #results == 0 then if #results == 0 then
setError('No completions available') actions.error('no completions available')
elseif #results == 1 then elseif #results == 1 then
actions.insertText(x, y, results[1]) actions.insertText(x, y, results[1])
@@ -746,7 +678,7 @@ actions = {
refresh = function() refresh = function()
actions.dirty_all() actions.dirty_all()
mark.continue = mark.active mark.continue = mark.active
setStatus('refreshed') actions.info('refreshed')
end, end,
goto_line = function() goto_line = function()
@@ -763,7 +695,7 @@ actions = {
local nx = tLines[ny]:lower():find(pattern, sx, true) local nx = tLines[ny]:lower():find(pattern, sx, true)
if nx then if nx then
if ny < y or ny == y and nx <= x then if ny < y or ny == y and nx <= x then
setStatus('search hit BOTTOM, continuing at TOP') actions.info('search hit BOTTOM, continuing at TOP')
end end
actions.go_to(nx, ny) actions.go_to(nx, ny)
actions.mark_to(nx + #pattern, ny) actions.mark_to(nx + #pattern, ny)
@@ -772,7 +704,7 @@ actions = {
end end
sx = 1 sx = 1
end end
setError('Pattern not found') actions.error('pattern not found')
end, end,
find_next = function() find_next = function()
@@ -812,7 +744,7 @@ actions = {
open = function(filename) open = function(filename)
if not actions.load(filename) then if not actions.load(filename) then
setError('Unable to load file') actions.error('unable to load file')
end end
end, end,
@@ -828,35 +760,47 @@ actions = {
lastSave = nil lastSave = nil
dirty = { y = 1, ey = h } dirty = { y = 1, ey = h }
mark = { } mark = { }
undo = { chain = { }, pointer = 0 } undo = { chain = { } }
tLines = { }
if fs.exists(fileInfo.abspath) then
local file = io.open(fileInfo.abspath, "r")
local sLine = file:read()
while sLine do
table.insert(tLines, sLine)
sLine = file:read()
end
file:close()
end
tLines = Util.readLines(fileInfo.abspath) or { }
if #tLines == 0 then if #tLines == 0 then
table.insert(tLines, '') _insert(tLines, '')
end
--[[
local function detabify(l)
return l:gsub('\26\26', '\9'):gsub('\26', '\9')
end ]]
-- since we can't handle tabs, convert them to spaces :(
local t1, t2 = ' ', ' '
local function tabify(l)
repeat
local i = l:find('\9')
if i then
local tabs = (i - 1) % 2 == 0 and t2 or t1
l = l:sub(1, i - 1) .. tabs .. l:sub(i + 1)
end
until not i
return l
end
for k, v in pairs(tLines) do
tLines[k] = tabify(v)
end end
local name = fileInfo.path local name = fileInfo.path
if fileInfo.isNew then if fileInfo.isNew then
if not fileInfo.dirExists then if not fileInfo.dirExists then
setStatus('"%s" [New DIRECTORY]', name) actions.info('"%s" [New DIRECTORY]', name)
else else
setStatus('"%s" [New File]', name) actions.info('"%s" [New File]', name)
end end
elseif fileInfo.isReadOnly then elseif fileInfo.isReadOnly then
setStatus('"%s" [readonly] %dL, %dC', actions.info('"%s" [readonly] %dL, %dC',
name, #tLines, fs.getSize(fileInfo.abspath)) name, #tLines, fs.getSize(fileInfo.abspath))
else else
setStatus('"%s" %dL, %dC', actions.info('"%s" %dL, %dC',
name, #tLines, fs.getSize(fileInfo.abspath)) name, #tLines, fs.getSize(fileInfo.abspath))
end end
@@ -866,17 +810,22 @@ actions = {
save = function(filename) save = function(filename)
filename = filename or fileInfo.abspath filename = filename or fileInfo.abspath
if fs.isReadOnly(filename) then if fs.isReadOnly(filename) then
setError("Access denied") actions.error("access denied")
else else
local ok = save(filename) local s, m = pcall(function()
if ok then if not Util.writeLines(filename, tLines) then
error("Failed to open " .. filename)
end
end)
if s then
lastSave = undo.chain[#undo.chain] lastSave = undo.chain[#undo.chain]
fileInfo = getFileInfo(filename) fileInfo = getFileInfo(filename)
setStatus('"%s" %dL, %dC written', actions.info('"%s" %dL, %dC written',
fileInfo.path, #tLines, fs.getSize(fileInfo.abspath)) fileInfo.path, #tLines, fs.getSize(fileInfo.abspath))
return true return true
else else
setError("Error saving to %s", filename) actions.error(m)
end end
end end
end, end,
@@ -894,26 +843,34 @@ actions = {
end, end,
run = function() run = function()
--input:reset() if undo.chain[#undo.chain] == lastSave then
local sTempPath = "/.temp" local nTask = shell.openTab(fileInfo.abspath)
local ok = save(sTempPath)
if ok then
local nTask = shell.openTab(sTempPath)
if nTask then if nTask then
shell.switchTab(nTask) shell.switchTab(nTask)
else else
setError("Error starting Task") actions.error("error starting Task")
end end
os.sleep(0)
fs.delete(sTempPath)
else else
setError("Error saving to %s", sTempPath) local fn, msg = load(_concat(tLines, '\n'), fileInfo.abspath)
if fn then
multishell.openTab({
fn = fn,
focused = true,
title = fs.getName(fileInfo.abspath),
})
else
local ln = msg:match(':(%d+):')
if ln and tonumber(ln) then
actions.go_to(1, tonumber(ln))
end
actions.error(msg)
end
end end
end, end,
status = function() status = function()
local modified = undo.chain[#undo.chain] == lastSave and '' or '[Modified] ' local modified = undo.chain[#undo.chain] == lastSave and '' or '[Modified] '
setStatus('"%s" %s%d lines --%d%%--', actions.info('"%s" %s%d lines --%d%%--',
fileInfo.abspath, modified, #tLines, fileInfo.abspath, modified, #tLines,
math.floor((y - 1) / (#tLines - 1) * 100)) math.floor((y - 1) / (#tLines - 1) * 100))
end, end,
@@ -1019,6 +976,14 @@ actions = {
actions.mark_finish() actions.mark_finish()
end, end,
mark_line = function()
actions.home()
actions.mark_begin()
actions.toend()
actions.right()
actions.mark_finish()
end,
mark_word = function() mark_word = function()
actions.mark_begin() actions.mark_begin()
actions.word() actions.word()
@@ -1074,7 +1039,7 @@ actions = {
actions.dirty_all() actions.dirty_all()
end, end,
setCursor = function() set_cursor = function()
lastPos.x = x lastPos.x = x
lastPos.y = y lastPos.y = y
@@ -1128,11 +1093,11 @@ actions = {
actions.insertText(x, y, ' ') actions.insertText(x, y, ' ')
end, end,
pageUp = function() page_up = function()
actions.go_to(x, y - h) actions.go_to(x, y - h)
end, end,
pageDown = function() page_down = function()
actions.go_to(x, y + h) actions.go_to(x, y + h)
end, end,
@@ -1208,21 +1173,21 @@ actions = {
actions.dirty_line(y) actions.dirty_line(y)
x = x + #text x = x + #text
else else
local lines = split(text) local lines = Util.split(text)
local remainder = sLine:sub(x) local remainder = sLine:sub(x)
tLines[y] = sLine:sub(1, x - 1) .. lines[1] tLines[y] = sLine:sub(1, x - 1) .. lines[1]
actions.dirty_range(y, #tLines + #lines) actions.dirty_range(y, #tLines + #lines)
x = x + #lines[1] x = x + #lines[1]
for k = 2, #lines do for k = 2, #lines do
y = y + 1 y = y + 1
table.insert(tLines, y, lines[k]) _insert(tLines, y, lines[k])
x = #lines[k] + 1 x = #lines[k] + 1
end end
tLines[y] = tLines[y]:sub(1, x) .. remainder tLines[y] = tLines[y]:sub(1, x) .. remainder
end end
if not undo.active then if not undo.active then
actions.addUndo( actions.undo_add(
{ action = 'deleteText', args = { sx, sy, x, y, text } }) { action = 'deleteText', args = { sx, sy, x, y, text } })
end end
end, end,
@@ -1233,14 +1198,14 @@ actions = {
if not undo.active then if not undo.active then
local text = actions.copyText(sx, sy, ex, ey) local text = actions.copyText(sx, sy, ex, ey)
actions.addUndo( actions.undo_add(
{ action = 'insertText', args = { sx, sy, text } }) { action = 'insertText', args = { sx, sy, text } })
end end
local front = tLines[sy]:sub(1, sx - 1) local front = tLines[sy]:sub(1, sx - 1)
local back = tLines[ey]:sub(ex, #tLines[ey]) local back = tLines[ey]:sub(ex, #tLines[ey])
for _ = 2, ey - sy + 1 do for _ = 2, ey - sy + 1 do
table.remove(tLines, y + 1) _remove(tLines, y + 1)
end end
tLines[y] = front .. back tLines[y] = front .. back
if sy ~= ey then if sy ~= ey then
@@ -1267,10 +1232,10 @@ actions = {
end end
local str = line:sub(cx, ex) local str = line:sub(cx, ex)
count = count + #str count = count + #str
table.insert(lines, str) _insert(lines, str)
end end
end end
return table.concat(lines, '\n'), count return _concat(lines, '\n'), count
end, end,
delete = function() delete = function()
@@ -1304,7 +1269,7 @@ actions = {
if mark.active then if mark.active then
actions.delete() actions.delete()
end end
actions.insertText(x, y, '\n' .. string.rep(' ', spaces)) actions.insertText(x, y, '\n' .. _rep(' ', spaces))
end, end,
char = function(ch) char = function(ch)
@@ -1317,7 +1282,7 @@ actions = {
copy_marked = function() copy_marked = function()
local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey) local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey)
os.queueEvent('clipboard_copy', text) os.queueEvent('clipboard_copy', text)
setStatus('shift-^v to paste') actions.info('shift-^v to paste')
end, end,
cut = function() cut = function()
@@ -1340,9 +1305,9 @@ actions = {
end end
if text then if text then
actions.insertText(x, y, text) actions.insertText(x, y, text)
setStatus('%d chars added', #text) actions.info('%d chars added', #text)
else else
setStatus('Clipboard empty') actions.info('clipboard empty')
end end
end, end,
@@ -1384,10 +1349,16 @@ actions = {
local wasMarking = mark.continue local wasMarking = mark.continue
mark.continue = false mark.continue = false
-- for undo purposes, treat tab and enter as char actions
local a = (action == 'tab' or action == 'enter') and 'char' or action
undo.continue = a == undo.lastAction
actions[action](...) actions[action](...)
undo.lastAction = a
if x ~= lastPos.x or y ~= lastPos.y then if x ~= lastPos.x or y ~= lastPos.y then
actions.setCursor() actions.set_cursor()
end end
if not mark.continue and wasMarking then if not mark.continue and wasMarking then
actions.unmark() actions.unmark()