Text overwriting (#1495)
* added text overwriting * rewrote `DocView:draw_caret` to not use the order of draws * forgot to delete some old code in `DocView:draw_overlay` also added a temporary solution to overwriting and added the missing arguments in `DocView:draw_ime_decoration` and fixed `DocView:draw_caret` * accidentally broke the `draw_caret` call in `draw_overlay` in the process * multiline * fixed calling `Doc:get_char` as a function that, in turn, crashed the editor because "can't index a number" * move and rename some stuff * remove unneeded extra check I just had to change the `~=` to `<` in the second condition * overwrite disregards pasting text * disregard overwrite on selections; doc only removes selection * Fixed error where `doc` was used, instead of `self`. --------- Co-authored-by: ThaCuber <70547062+ThaCuber@users.noreply.github.com> Co-authored-by: Adam Harrison <adamdharrison@gmail.com>
This commit is contained in:
parent
234dd40e49
commit
f43cfc4a94
|
@ -545,6 +545,11 @@ local commands = {
|
|||
dv.doc.crlf = not dv.doc.crlf
|
||||
end,
|
||||
|
||||
["doc:toggle-overwrite"] = function(dv)
|
||||
dv.doc.overwrite = not dv.doc.overwrite
|
||||
core.blink_reset() -- to show the cursor has changed edit modes
|
||||
end,
|
||||
|
||||
["doc:save-as"] = function(dv)
|
||||
local last_doc = core.last_active_view and core.last_active_view.doc
|
||||
local text
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
local Object = require "core.object"
|
||||
local Highlighter = require "core.doc.highlighter"
|
||||
local translate = require "core.doc.translate"
|
||||
local core = require "core"
|
||||
local syntax = require "core.syntax"
|
||||
local config = require "core.config"
|
||||
|
@ -29,7 +30,6 @@ function Doc:new(filename, abs_filename, new_file)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:reset()
|
||||
self.lines = { "\n" }
|
||||
self.selections = { 1, 1, 1, 1 }
|
||||
|
@ -38,10 +38,10 @@ function Doc:reset()
|
|||
self.redo_stack = { idx = 1 }
|
||||
self.clean_change_id = 1
|
||||
self.highlighter = Highlighter(self)
|
||||
self.overwrite = false
|
||||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:reset_syntax()
|
||||
local header = self:get_text(1, 1, self:position_offset(1, 1, 128))
|
||||
local path = self.abs_filename
|
||||
|
@ -56,16 +56,14 @@ function Doc:reset_syntax()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_filename(filename, abs_filename)
|
||||
self.filename = filename
|
||||
self.abs_filename = abs_filename
|
||||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:load(filename)
|
||||
local fp = assert( io.open(filename, "rb") )
|
||||
local fp = assert(io.open(filename, "rb"))
|
||||
self:reset()
|
||||
self.lines = {}
|
||||
local i = 1
|
||||
|
@ -85,7 +83,6 @@ function Doc:load(filename)
|
|||
self:reset_syntax()
|
||||
end
|
||||
|
||||
|
||||
function Doc:reload()
|
||||
if self.filename then
|
||||
local sel = { self:get_selection() }
|
||||
|
@ -95,7 +92,6 @@ function Doc:reload()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:save(filename, abs_filename)
|
||||
if not filename then
|
||||
assert(self.filename, "no filename set to default to")
|
||||
|
@ -104,7 +100,7 @@ function Doc:save(filename, abs_filename)
|
|||
else
|
||||
assert(self.filename or abs_filename, "calling save on unnamed doc without absolute path")
|
||||
end
|
||||
local fp = assert( io.open(filename, "wb") )
|
||||
local fp = assert(io.open(filename, "wb"))
|
||||
for _, line in ipairs(self.lines) do
|
||||
if self.crlf then line = line:gsub("\n", "\r\n") end
|
||||
fp:write(line)
|
||||
|
@ -115,12 +111,10 @@ function Doc:save(filename, abs_filename)
|
|||
self:clean()
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_name()
|
||||
return self.filename or "unsaved"
|
||||
end
|
||||
|
||||
|
||||
function Doc:is_dirty()
|
||||
if self.new_file then
|
||||
if self.filename then return true end
|
||||
|
@ -130,20 +124,17 @@ function Doc:is_dirty()
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:clean()
|
||||
self.clean_change_id = self:get_change_id()
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_indent_info()
|
||||
if not self.indent_info then return config.tab_type, config.indent_size, false end
|
||||
return self.indent_info.type or config.tab_type,
|
||||
self.indent_info.size or config.indent_size,
|
||||
self.indent_info.confirmed
|
||||
self.indent_info.size or config.indent_size,
|
||||
self.indent_info.confirmed
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_change_id()
|
||||
return self.undo_stack.idx
|
||||
end
|
||||
|
@ -167,13 +158,14 @@ function Doc:get_selection(sort)
|
|||
return line1, col1, line2, col2, swap
|
||||
end
|
||||
|
||||
|
||||
---Get the selection specified by `idx`
|
||||
---@param idx integer @the index of the selection to retrieve
|
||||
---@param sort? boolean @whether to sort the selection returned
|
||||
---@return integer,integer,integer,integer,boolean? @line1, col1, line2, col2, was the selection sorted
|
||||
function Doc:get_selection_idx(idx, sort)
|
||||
local line1, col1, line2, col2 = self.selections[idx*4-3], self.selections[idx*4-2], self.selections[idx*4-1], self.selections[idx*4]
|
||||
local line1, col1, line2, col2 = self.selections[idx * 4 - 3], self.selections[idx * 4 - 2],
|
||||
self.selections[idx * 4 - 1],
|
||||
self.selections[idx * 4]
|
||||
if line1 and sort then
|
||||
return sort_positions(line1, col1, line2, col2)
|
||||
else
|
||||
|
@ -217,7 +209,7 @@ function Doc:set_selections(idx, line1, col1, line2, col2, swap, rm)
|
|||
if swap then line1, col1, line2, col2 = line2, col2, line1, col1 end
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2 or line1, col2 or col1)
|
||||
common.splice(self.selections, (idx - 1)*4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
|
||||
common.splice(self.selections, (idx - 1) * 4 + 1, rm == nil and 4 or rm, { line1, col1, line2, col2 })
|
||||
end
|
||||
|
||||
function Doc:add_selection(line1, col1, line2, col2, swap)
|
||||
|
@ -233,7 +225,6 @@ function Doc:add_selection(line1, col1, line2, col2, swap)
|
|||
self.last_selection = target
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove_selection(idx)
|
||||
if self.last_selection >= idx then
|
||||
self.last_selection = self.last_selection - 1
|
||||
|
@ -241,7 +232,6 @@ function Doc:remove_selection(idx)
|
|||
common.splice(self.selections, (idx - 1) * 4 + 1, 4)
|
||||
end
|
||||
|
||||
|
||||
function Doc:set_selection(line1, col1, line2, col2, swap)
|
||||
self.selections = {}
|
||||
self:set_selections(1, line1, col1, line2, col2, swap)
|
||||
|
@ -252,24 +242,24 @@ function Doc:merge_cursors(idx)
|
|||
for i = (idx or (#self.selections - 3)), (idx or 5), -4 do
|
||||
for j = 1, i - 4, 4 do
|
||||
if self.selections[i] == self.selections[j] and
|
||||
self.selections[i+1] == self.selections[j+1] then
|
||||
common.splice(self.selections, i, 4)
|
||||
if self.last_selection >= (i+3)/4 then
|
||||
self.last_selection = self.last_selection - 1
|
||||
end
|
||||
break
|
||||
self.selections[i + 1] == self.selections[j + 1] then
|
||||
common.splice(self.selections, i, 4)
|
||||
if self.last_selection >= (i + 3) / 4 then
|
||||
self.last_selection = self.last_selection - 1
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function selection_iterator(invariant, idx)
|
||||
local target = invariant[3] and (idx*4 - 7) or (idx*4 + 1)
|
||||
local target = invariant[3] and (idx * 4 - 7) or (idx * 4 + 1)
|
||||
if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end
|
||||
if invariant[2] then
|
||||
return idx+(invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target+4))
|
||||
return idx + (invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target + 4))
|
||||
else
|
||||
return idx+(invariant[3] and -1 or 1), table.unpack(invariant[1], target, target+4)
|
||||
return idx + (invariant[3] and -1 or 1), table.unpack(invariant[1], target, target + 4)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -277,8 +267,9 @@ end
|
|||
-- If a number, runs for exactly that iteration.
|
||||
function Doc:get_selections(sort_intra, idx_reverse)
|
||||
return selection_iterator, { self.selections, sort_intra, idx_reverse },
|
||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1)+1)
|
||||
idx_reverse == true and ((#self.selections / 4) + 1) or ((idx_reverse or -1) + 1)
|
||||
end
|
||||
|
||||
-- End of cursor seciton.
|
||||
|
||||
function Doc:sanitize_position(line, col)
|
||||
|
@ -291,7 +282,6 @@ function Doc:sanitize_position(line, col)
|
|||
return line, common.clamp(col, 1, #self.lines[line])
|
||||
end
|
||||
|
||||
|
||||
local function position_offset_func(self, line, col, fn, ...)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return fn(self, line, col, ...)
|
||||
|
@ -330,7 +320,6 @@ function Doc:position_offset(line, col, ...)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_text(line1, col1, line2, col2)
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
line2, col2 = self:sanitize_position(line2, col2)
|
||||
|
@ -346,13 +335,11 @@ function Doc:get_text(line1, col1, line2, col2)
|
|||
return table.concat(lines)
|
||||
end
|
||||
|
||||
|
||||
function Doc:get_char(line, col)
|
||||
line, col = self:sanitize_position(line, col)
|
||||
return self.lines[line]:sub(col, col)
|
||||
end
|
||||
|
||||
|
||||
local function push_undo(undo_stack, time, type, ...)
|
||||
undo_stack[undo_stack.idx] = { type = type, time = time, ... }
|
||||
undo_stack[undo_stack.idx - config.max_undos] = nil
|
||||
|
@ -413,7 +400,8 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
if cline1 < line then break end
|
||||
local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0
|
||||
local column_addition = line == cline1 and ccol1 > col and len or 0
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition)
|
||||
self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition,
|
||||
ccol2 + column_addition)
|
||||
end
|
||||
|
||||
-- push undo
|
||||
|
@ -426,7 +414,6 @@ function Doc:raw_insert(line, col, text, undo_stack, time)
|
|||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
||||
function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
||||
-- push undo
|
||||
local text = self:get_text(line1, col1, line2, col2)
|
||||
|
@ -485,7 +472,6 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time)
|
|||
self:sanitize_selection()
|
||||
end
|
||||
|
||||
|
||||
function Doc:insert(line, col, text)
|
||||
self.redo_stack = { idx = 1 }
|
||||
-- Reset the clean id when we're pushing something new before it
|
||||
|
@ -497,7 +483,6 @@ function Doc:insert(line, col, text)
|
|||
self:on_text_change("insert")
|
||||
end
|
||||
|
||||
|
||||
function Doc:remove(line1, col1, line2, col2)
|
||||
self.redo_stack = { idx = 1 }
|
||||
line1, col1 = self:sanitize_position(line1, col1)
|
||||
|
@ -507,28 +492,34 @@ function Doc:remove(line1, col1, line2, col2)
|
|||
self:on_text_change("remove")
|
||||
end
|
||||
|
||||
|
||||
function Doc:undo()
|
||||
pop_undo(self, self.undo_stack, self.redo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:redo()
|
||||
pop_undo(self, self.redo_stack, self.undo_stack, false)
|
||||
end
|
||||
|
||||
|
||||
function Doc:text_input(text, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||
local had_selection = false
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
self:delete_to_cursor(sidx)
|
||||
had_selection = true
|
||||
end
|
||||
|
||||
if self.overwrite
|
||||
and not had_selection
|
||||
and col1 < #self.lines[line1]
|
||||
and text:ulen() == 1 then
|
||||
self:remove(line1, col1, translate.next_char(self, line1, col1))
|
||||
end
|
||||
|
||||
self:insert(line1, col1, text)
|
||||
self:move_to_cursor(sidx, #text)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:ime_text_editing(text, start, length, idx)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -539,7 +530,6 @@ function Doc:ime_text_editing(text, start, length, idx)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
local old_text = self:get_text(line1, col1, line2, col2)
|
||||
local new_text, res = fn(old_text)
|
||||
|
@ -555,7 +545,7 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn)
|
|||
end
|
||||
|
||||
function Doc:replace(fn)
|
||||
local has_selection, results = false, { }
|
||||
local has_selection, results = false, {}
|
||||
for idx, line1, col1, line2, col2 in self:get_selections(true) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
results[idx] = self:replace_cursor(idx, line1, col1, line2, col2, fn)
|
||||
|
@ -569,7 +559,6 @@ function Doc:replace(fn)
|
|||
return results
|
||||
end
|
||||
|
||||
|
||||
function Doc:delete_to_cursor(idx, ...)
|
||||
for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do
|
||||
if line1 ~= line2 or col1 ~= col2 then
|
||||
|
@ -583,6 +572,7 @@ function Doc:delete_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
|
||||
function Doc:delete_to(...) return self:delete_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:move_to_cursor(idx, ...)
|
||||
|
@ -591,8 +581,8 @@ function Doc:move_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:move_to(...) return self:move_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:select_to_cursor(idx, ...)
|
||||
for sidx, line, col, line2, col2 in self:get_selections(false, idx) do
|
||||
|
@ -601,8 +591,8 @@ function Doc:select_to_cursor(idx, ...)
|
|||
end
|
||||
self:merge_cursors(idx)
|
||||
end
|
||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:select_to(...) return self:select_to_cursor(nil, ...) end
|
||||
|
||||
function Doc:get_indent_string()
|
||||
local indent_type, indent_size = self:get_indent_info()
|
||||
|
@ -625,7 +615,7 @@ function Doc:get_line_indent(line, rnd_up)
|
|||
local indent = e and line:sub(1, e):gsub("\t", soft_tab) or ""
|
||||
local number = #indent / #soft_tab
|
||||
return e, indent:sub(1,
|
||||
(rnd_up and math.ceil(number) or math.floor(number))*#soft_tab)
|
||||
(rnd_up and math.ceil(number) or math.floor(number)) * #soft_tab)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -674,5 +664,4 @@ function Doc:on_close()
|
|||
core.log_quiet("Closed doc \"%s\"", self:get_name())
|
||||
end
|
||||
|
||||
|
||||
return Doc
|
||||
|
|
|
@ -460,9 +460,14 @@ function DocView:draw_line_text(line, x, y)
|
|||
return self:get_line_height()
|
||||
end
|
||||
|
||||
function DocView:draw_caret(x, y)
|
||||
local lh = self:get_line_height()
|
||||
function DocView:draw_caret(x, y, line, col)
|
||||
local lh = self:get_line_height()
|
||||
if self.doc.overwrite then
|
||||
local w = self:get_font():get_width(self.doc:get_char(line, col))
|
||||
renderer.draw_rect(x, y + lh, w, style.caret_width * 2, style.caret)
|
||||
else
|
||||
renderer.draw_rect(x, y, style.caret_width, lh, style.caret)
|
||||
end
|
||||
end
|
||||
|
||||
function DocView:draw_line_body(line, x, y)
|
||||
|
@ -542,7 +547,7 @@ function DocView:draw_ime_decoration(line1, col1, line2, col2)
|
|||
line_size = style.caret_width
|
||||
renderer.draw_rect(x + math.min(x1, x2), y + lh - line_size, math.abs(x1 - x2), line_size, style.caret)
|
||||
end
|
||||
self:draw_caret(x + x1, y)
|
||||
self:draw_caret(x + x1, y, line1, col)
|
||||
end
|
||||
|
||||
|
||||
|
@ -559,7 +564,8 @@ function DocView:draw_overlay()
|
|||
else
|
||||
if config.disable_blink
|
||||
or (core.blink_timer - core.blink_start) % T < T / 2 then
|
||||
self:draw_caret(self:get_line_screen_position(line1, col1))
|
||||
local x, y = self:get_line_screen_position(line1, col1)
|
||||
self:draw_caret(x, y, line1, col1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -341,6 +341,7 @@ keymap.add_direct {
|
|||
["ctrl+x"] = "doc:cut",
|
||||
["ctrl+c"] = "doc:copy",
|
||||
["ctrl+v"] = "doc:paste",
|
||||
["insert"] = "doc:toggle-overwrite",
|
||||
["ctrl+insert"] = "doc:copy",
|
||||
["shift+insert"] = "doc:paste",
|
||||
["escape"] = { "command:escape", "doc:select-none", "dialog:select-no" },
|
||||
|
|
|
@ -319,6 +319,19 @@ function StatusView:register_docview_items()
|
|||
end,
|
||||
command = "doc:toggle-line-ending"
|
||||
})
|
||||
|
||||
self:add_item {
|
||||
predicate = predicate_docview,
|
||||
name = "doc:overwrite-mode",
|
||||
alignment = StatusView.Item.RIGHT,
|
||||
get_item = function()
|
||||
return {
|
||||
style.text, core.active_view.doc.overwrite and "OVR" or "INS"
|
||||
}
|
||||
end,
|
||||
command = "doc:toggle-overwrite",
|
||||
separator = StatusView.separator2
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue