Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ebb1779
feat(editor): allow monster blocks to be edited/saved if selection st…
hleb-rubanau Mar 23, 2026
75ac660
fix(editor/buffer): as part of submit preprocesing, enforce newlines …
hleb-rubanau Mar 23, 2026
0534da2
feat(editor): scroll into first oversized block if in refactoring mode
hleb-rubanau Mar 23, 2026
11ff361
fix(editor/replace): restrict reopening oversized blocks to lua only
hleb-rubanau Mar 23, 2026
a8ff17c
fix(editor/buffer): function breaking when optional arg not provided
hleb-rubanau Mar 28, 2026
b723321
feat(editor): refactoring mode reorganized -- only partial saves, ove…
hleb-rubanau Mar 28, 2026
7ac8ec7
fix(editor): visibility check should always be oversize-tolerant
hleb-rubanau Mar 28, 2026
11a93a0
chore(tests): simple test for code editing
hleb-rubanau Mar 29, 2026
c37dc6f
chore(tests/editor/blocks): simple test refactored to use helpers
hleb-rubanau Mar 29, 2026
c94a7fb
chore(tests/editor): rescope
hleb-rubanau Mar 29, 2026
f37482b
chore(tests/editor): rescope
hleb-rubanau Mar 29, 2026
68b1ca5
chore(tests/editor): rescope and add basic test for block addition
hleb-rubanau Mar 29, 2026
efb9201
chore(editor/tests): more test cases
hleb-rubanau Mar 29, 2026
177e172
chore(tests/editor): coverage for work with blocks (4 cases pending)
hleb-rubanau Mar 29, 2026
d692242
fix(editor): reject non-refactorable oversize edit
hleb-rubanau Mar 29, 2026
dcacf71
chore(tests): context isolation
hleb-rubanau Mar 29, 2026
bf9fda4
chore(tests): assert cosmetics
hleb-rubanau Mar 29, 2026
331026d
chore(tests): unpend 2 cases (1 ok. 1 fails) + more assertion cosmetics
hleb-rubanau Mar 29, 2026
f3421b0
fix(editor): tests and multiline handing for insertion (3 tests still…
hleb-rubanau Mar 29, 2026
0d131f0
chore(tests): typo in replacement test case
hleb-rubanau Mar 29, 2026
afd80fc
fix(editor): refresh view before moving selection
hleb-rubanau Mar 29, 2026
75f9103
fix(editor/blocks): account for emptyline collapses on injection
hleb-rubanau Mar 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 89 additions & 24 deletions src/controller/editorController.lua
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ function EditorController:_handle_submit(go)
local ok, res = inter:evaluate()
local _, chunks = buf.chunker(pretty, true)
if ok then
local newlines_injection_needed = (#chunks > 1)
if #chunks < #raw_chunks then
local rc = raw_chunks
if rc[1].tag == 'empty' then
Expand All @@ -351,6 +352,17 @@ function EditorController:_handle_submit(go)
table.insert(chunks, Empty(li + 1))
end
end
if newlines_injection_needed then
for i = #chunks, 2, -1 do
local ch=chunks
local this_nonempty = ch[i].tag ~= 'empty'
Comment thread
hleb-rubanau marked this conversation as resolved.
local prev_nonempty = ch[i-1].tag ~= 'empty'
if this_nonempty and prev_nonempty then
local prev_pos = ch[i-1].pos.fin
table.insert(ch, i, Empty(prev_pos+1))
end
end
end
go(chunks)
else
local eval_err = res
Expand Down Expand Up @@ -589,42 +601,95 @@ function EditorController:_normal_mode_keys(k)
--- handlers
local function submit()
local bufv = self.view:get_current_buffer()
local is_lua = bufv.content_type == 'lua'
local size_limit = bufv:get_size()
local is_oversized_chunk = function(v)
return (v and v.pos and v.pos:len() > size_limit)
end
local first_oversized_chunk = function(chunks)
if is_lua then
return table.find_by(chunks, is_oversized_chunk)
Comment thread
hleb-rubanau marked this conversation as resolved.
end
end
local to_lines = function(x) return x.lines end
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could also add this as a helper in Chunk, returning an empty table or nil for Empty

local analyze_input = function(newtext)
local oversized = first_oversized_chunk(newtext)
if not oversized then
return newtext, false
end
if oversized == 1 then
return false
end
local approved = table.slice(newtext, 1, oversized-1)
local rejected = table.slice(newtext, oversized)
local rejected_lines = table.map(rejected, to_lines)
local rejected_raw = table.flatten(rejected_lines)
return approved, rejected_raw
end

local function replace(newtext)
if bufv:is_selection_visible() then
if buf:loaded_is_sel(true) then
local _, n = buf:replace_content(newtext)
buf:clear_loaded()
self:save(buf)
input:clear()
self.view:refresh()
self:_move_sel('down', n)
load_selection()
self:update_status()
else
buf:select_loaded()
bufv:follow_selection()
end
else
if not bufv:is_selection_visible(true) then
return bufv:follow_selection()
end

if not buf:loaded_is_sel(true) then
buf:select_loaded()
bufv:follow_selection()
return
end

local approved, rejected = analyze_input(newtext)
if not approved then return end
if rejected then
local _, n = buf:insert_content(approved)
self:save(buf)
self.view:refresh()
self:_move_sel('down', n)
buf:set_loaded() -- still editing that piece
input:set_text(rejected)
input:jump_home()
else
local _, n = buf:replace_content(approved)
self:save(buf)
self.view:refresh()
self:_move_sel('down', n)
buf:clear_loaded()
input:clear()
load_selection()
end

self:update_status()
end

if Key.ctrl()
and not Key.shift()
and not Key.alt()
and Key.is_enter(k) then
local function add(newtext)
if bufv:is_selection_visible() then
local sel = buf:get_selection()
local _, n = buf:insert_content(newtext, sel)
self:save(buf)
self.view:refresh()
self:_move_sel('down', n)
buf:clear_loaded()
self:update_status()
if not bufv:is_selection_visible() then
return bufv:follow_selection()
end

local approved, rejected = newtext, false
if is_lua then
approved, rejected = analyze_input(newtext)
end
if not approved then return end

local sel = buf:get_selection()
local _, n = buf:insert_content(approved, sel)
self:save(buf)
self.view:refresh()
self:_move_sel('down', n)
if rejected then
buf:set_loaded() -- still editing that piece
input:set_text(rejected)
input:jump_home()
else
bufv:follow_selection()
buf:clear_loaded()
-- input not cleared/reset, is it ok?
end
self:update_status()
end

self:_handle_submit(add)
Expand Down
9 changes: 7 additions & 2 deletions src/model/editor/bufferModel.lua
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ end
function BufferModel:insert_content(t, lbn)
local num = lbn or self:get_selection()
local len = self:get_content_length() + 1
if lbn < 1 or lbn > len then
if num < 1 or num > len then
return false
end
if self.content_type == 'lua' then
Expand All @@ -468,6 +468,8 @@ function BufferModel:insert_content(t, lbn)
end
end)()

local oldlen = self:get_content_length()

for i = #chunks, 1, -1 do
local c = chunks[i]
local nr = c.pos:translate(cs - 1)
Expand All @@ -485,7 +487,10 @@ function BufferModel:insert_content(t, lbn)
end

self:_text_change(true)
return true, n
-- rechunk in #text_change may erase some new empty blocks
-- so factual n of blocks added should be recalculted
Comment thread
hleb-rubanau marked this conversation as resolved.
local newlen = self:get_content_length()
return true, (newlen - oldlen)
else
local ti = num
for i = #t, 1, -1 do
Expand Down
14 changes: 13 additions & 1 deletion src/view/editor/bufferView.lua
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ function BufferView:get_state()
}
end

--- @return integer
function BufferView:get_size()
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not quite correct conceptually, I probably should have named the field better.
The 'size' of a buffer(view) is number of lines it currently holds, which can be less than the maximum, so this should probably be called get_max_size. It might also be redundant, because the value comes from the config, which should be available wherever this value is read (haven't check all call sites yet), however, there might be use for smaller sub-buffers, splits, etc in the future, we can keep the function.

return self.LINES
end

--- @param moved integer?
function BufferView:refresh(moved)
if not self.content then
Expand Down Expand Up @@ -211,7 +216,7 @@ end
--- @return boolean
--- @return VerticalDir?
--- @return number? diff
function BufferView:is_selection_visible()
function BufferView:is_selection_visible(tolerate_oversize)
local sel = self.buffer:get_selection()

local s_w
Expand All @@ -227,6 +232,13 @@ function BufferView:is_selection_visible()
local r = self.content:get_range()
if r:inc(sel_s) and r:inc(sel_e) then return true end

-- handle monster blocks
if tolerate_oversize then
if (sel_s == r.start) and (sel_e > r.fin) then
return true
end
end

local dir = (function()
if r.start > sel_s then return 'up' end
if r.fin < sel_e then return 'down' end
Expand Down
Loading
Loading