diff options
Diffstat (limited to 'lua/clever_f/testing/memory_host.lua')
| -rw-r--r-- | lua/clever_f/testing/memory_host.lua | 142 |
1 files changed, 140 insertions, 2 deletions
diff --git a/lua/clever_f/testing/memory_host.lua b/lua/clever_f/testing/memory_host.lua index 57af1f1..8b473ad 100644 --- a/lua/clever_f/testing/memory_host.lua +++ b/lua/clever_f/testing/memory_host.lua @@ -1,5 +1,6 @@ local capabilities = require("clever_f.capabilities") local domain = require("clever_f.domain") +local text_topology = require("clever_f.text_topology") local M = {} local MemoryHost = {} @@ -383,11 +384,148 @@ function MemoryHost:_emit_movement_event(previous) end end -function MemoryHost:apply_cursor(position) +local function motion_descriptor(motion) + if domain.Descriptor.is(motion) then + return motion + end + if type(motion) == "table" and motion.descriptor ~= nil then + return domain.Descriptor.from_string(motion.descriptor) + end + return nil +end + +local function character_boundary(view, position) + if view:line_is_empty(position.line) then + return 1 + end + return view:character_index_for_position(position) +end + +local function character_lines(snapshot) + local result = {} + for line_number, line in ipairs(snapshot:lines()) do + result[line_number] = text_topology.split_editor_characters(line) + end + return result +end + +local function joined_range(characters, first, last) + local result = {} + for index = first, last do + result[#result + 1] = characters[index] + end + return table.concat(result) +end + +local function delete_character_range( + snapshot, + start_line, + start_index, + finish_line, + finish_index +) + local source = character_lines(snapshot) + local lines = snapshot:lines() + local result = {} + + for line_number = 1, start_line - 1 do + result[#result + 1] = lines[line_number] + end + + local prefix = joined_range(source[start_line], 1, start_index - 1) + if start_line == finish_line then + result[#result + 1] = prefix + .. joined_range( + source[start_line], + finish_index, + #source[start_line] + ) + else + result[#result + 1] = prefix + .. joined_range( + source[finish_line], + finish_index, + #source[finish_line] + ) + end + + for line_number = finish_line + 1, #lines do + result[#result + 1] = lines[line_number] + end + return domain.TextSnapshot.new(result) +end + +local function normalized_cursor(snapshot, encoding, position) + local line_number = math.min(position.line, snapshot.line_count) + local view = text_topology.new(snapshot, encoding) + return view:normalize_endpoint(line_number, position.byte_column) +end + +function MemoryHost:_apply_pending_delete(origin, destination, descriptor) + if self._pending_operator ~= "delete" and self._pending_operator ~= "d" then + return false + end + + local view = text_topology.new(self._text, self._encoding) + local origin_index = character_boundary(view, origin) + local destination_index = character_boundary(view, destination) + local start_line + local start_index + local finish_line + local finish_index + local final_cursor + + if descriptor.direction == domain.Direction.FORWARD then + start_line = origin.line + start_index = origin_index + finish_line = destination.line + finish_index = destination_index + (self._operator_inclusive and 1 or 0) + final_cursor = origin + elseif descriptor.family == domain.Family.FIND then + start_line = destination.line + start_index = destination_index + 1 + finish_line = origin.line + finish_index = origin_index + 1 + final_cursor = destination + else + start_line = destination.line + start_index = destination_index + finish_line = origin.line + finish_index = origin_index + final_cursor = view:predecessor(destination) or destination + end + + self._text = delete_character_range( + self._text, + start_line, + start_index, + finish_line, + finish_index + ) + self._cursor = normalized_cursor(self._text, self._encoding, final_cursor) + self:_record("apply_operator", { + operator = self._pending_operator, + descriptor = descriptor.value, + origin = origin, + endpoint = destination, + position = self._cursor, + }) + return true +end + +function MemoryHost:apply_cursor(position, motion) position = domain.Position.coerce(position) local previous = self._cursor + local descriptor = motion_descriptor(motion) self._cursor = position - self:_record("apply_cursor", { position = position }) + self:_record("apply_cursor", { + position = position, + descriptor = descriptor and descriptor.value or nil, + }) + if descriptor ~= nil then + local origin = type(motion) == "table" and motion.origin or previous + self:_apply_pending_delete(domain.Position.coerce(origin), position, descriptor) + end self:_emit_movement_event(previous) end |
