diff options
| author | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:02:34 +0200 |
|---|---|---|
| committer | Jackson Moore <jacksonmoore@tuta.io> | 2026-09-04 12:02:34 +0200 |
| commit | dbbbe30b637fccfacb15cd7317d4af87b658cfb5 (patch) | |
| tree | 8978cf092b8432a8f1e39d75ee4029f813a50fad | |
| parent | 154f639078470763fa788d31fac9b020d26b7825 (diff) | |
Normalize ordinary acquisition input
| -rw-r--r-- | lua/clever_f/acquisition_service.lua | 78 | ||||
| -rw-r--r-- | tests/run.lua | 28 |
2 files changed, 105 insertions, 1 deletions
diff --git a/lua/clever_f/acquisition_service.lua b/lua/clever_f/acquisition_service.lua index 707f568..b1e74bb 100644 --- a/lua/clever_f/acquisition_service.lua +++ b/lua/clever_f/acquisition_service.lua @@ -118,6 +118,7 @@ function TemporaryResourceScope.new(request, feedback) direct_marker = nil, cursor_presentation_lease = nil, input_packet = nil, + acquired_target = nil, } return scope end @@ -151,6 +152,10 @@ function TemporaryResourceScope:set_input_packet(packet) return set_scope_resource(self, "input_packet", packet) end +function TemporaryResourceScope:set_acquired_target(target) + return set_scope_resource(self, "acquired_target", target) +end + function TemporaryResourceScope:release() local record = scope_records[self] if record == nil then @@ -301,6 +306,72 @@ function AcquisitionService:started_scope_count() return service_records[self].started_scope_count end +local function utf8_first_code(character) + local first = string.byte(character, 1) + if first < 0x80 then + return first + end + local length + local code + if first >= 0xc2 and first <= 0xdf then + length = 2 + code = first - 0xc0 + elseif first >= 0xe0 and first <= 0xef then + length = 3 + code = first - 0xe0 + elseif first >= 0xf0 and first <= 0xf4 then + length = 4 + code = first - 0xf0 + else + fail("ordinary input must start with a valid editor character", 3) + end + for index = 2, length do + local byte = string.byte(character, index) + if byte == nil or byte < 0x80 or byte > 0xbf then + fail("ordinary input must contain a complete editor character", 3) + end + code = code * 0x40 + byte - 0x80 + end + return code +end + +local function first_editor_character(text) + local characters = text_topology.split_editor_characters(text) + if #characters == 0 then + fail("ordinary input must contain an editor character", 3) + end + return characters[1] +end + +function M.normalize_ordinary_input(packet) + packet = domain.InputPacket.from_table(packet) + local text + if packet.kind == domain.InputPacketKind.TEXT then + text = packet.text + elseif packet.kind == domain.InputPacketKind.RAW_BYTES then + local bytes = packet:bytes() + local characters = {} + for index = 1, #bytes do + characters[index] = string.char(bytes[index]) + end + text = table.concat(characters) + else + fail("ordinary input packet must contain text or raw bytes", 2) + end + local character = first_editor_character(text) + local runtime = rawget(_G, "vim") + local first_code + if type(runtime) == "table" + and type(runtime.fn) == "table" + and type(runtime.fn.char2nr) == "function" + then + first_code = runtime.fn.char2nr(character) + else + first_code = utf8_first_code(character) + end + return domain.TargetValue.character(character, first_code) +end + function M.is_terminal_artifact(packet) packet = domain.InputPacket.from_table(packet) if packet.kind ~= domain.InputPacketKind.RAW_BYTES then @@ -377,7 +448,12 @@ function AcquisitionService:acquire(descriptor, context, position, count, macro_ record.host:show_prompt(M.PROMPT) end record.transitions:BeginAcquisition(request.context, request.descriptor) - scope:set_input_packet(read_input_packet(record.host)) + local packet = scope:set_input_packet(read_input_packet(record.host)) + if packet.kind == domain.InputPacketKind.TEXT + or packet.kind == domain.InputPacketKind.RAW_BYTES + then + scope:set_acquired_target(M.normalize_ordinary_input(packet)) + end return request end diff --git a/tests/run.lua b/tests/run.lua index 3f4c957..c279844 100644 --- a/tests/run.lua +++ b/tests/run.lua @@ -5153,6 +5153,34 @@ test("Acquisition discards the terminal artifact packet", function() same(2, reads) end) +test("Acquisition normalizes ordinary input to editor characters", function() + fresh_sequence_state() + local multibyte = "\227\129\130" + local host = MemoryHost.new({ + configuration = { mark_cursor = false }, + input_packets = { + { kind = "text", text = multibyte }, + }, + }) + local service = acquisition_service.new(host) + service:acquire("f", "n", domain.Position.new(1, 1), nil, nil) + + local target = service:last_temporary_scope().acquired_target + same(domain.TargetKind.CHARACTER, target.kind) + same(multibyte, target.value) + same(0x3042, target.first_code) + + local ascii = acquisition_service.normalize_ordinary_input( + domain.InputPacket.raw_bytes({ 65 }) + ) + same("A", ascii.value) + same(65, ascii.first_code) + local control = acquisition_service.normalize_ordinary_input( + domain.InputPacket.text(string.char(1)) + ) + same(1, control.first_code) +end) + for _, item in ipairs(tests) do local ok, failure = xpcall(item.body, debug.traceback) if not ok then |
