PranitInitial commit439f83c3 months agoHistory
693 lines · 23.3 KB · Zig
RawDownload
const std = @import("std");const input_action = @import("../../core/input/input_action.zig"); const InputEscapeAction = input_action.Action;const MousePointerKind = input_action.MousePointerKind; pub const MouseInput = struct {    button: u16 = 0,    column: u16 = 0,    row: u16 = 0,    sgr_bytes: u8 = 0,    discard_remaining: u8 = 0,     pub fn reset(self: *MouseInput) void {        self.* = .{};    }}; fn appendCsiDigitSaturating(param: *u16, byte: u8) void {    const digit: u16 = byte - '0';    const multiplied = std.math.mul(u16, param.*, 10) catch std.math.maxInt(u16);    param.* = std.math.add(u16, multiplied, digit) catch std.math.maxInt(u16);} const escape_meta_mask: u8 = 0x80;const sgr_mouse_stage: u8 = 7;const sgr_mouse_column_stage: u8 = 8;const sgr_mouse_row_stage: u8 = 9;const legacy_x10_button_stage: u8 = 10;const legacy_x10_column_stage: u8 = 11;const legacy_x10_row_stage: u8 = 12;const discarded_sgr_mouse_stage: u8 = 13;const discarded_x10_mouse_stage: u8 = 14;const csi_private_stage: u8 = 15;const sgr_mouse_max_bytes: u8 = 18;const csi_private_max_bytes: u16 = 32;const kitty_up_key: u16 = 57352;const kitty_down_key: u16 = 57353;const shift_modifier: u16 = 0x01;const alt_modifier: u16 = 0x02;const ctrl_modifier: u16 = 0x04;const super_modifier: u16 = 0x08; fn composerMove(kind: input_action.MoveKind, modifiers: u16) InputEscapeAction {    return .{ .composer_shortcut = .{ .move = .{        .kind = kind,        .extend_selection = (modifiers & shift_modifier) != 0,    } } };} fn modifiedArrowAction(byte: u8, modifiers: u16, meta_prefixed: bool) ?InputEscapeAction {    if ((modifiers & super_modifier) != 0) return switch (byte) {        'A' => composerMove(.draft_start, modifiers),        'B' => composerMove(.draft_end, modifiers),        'C' => composerMove(.line_end, modifiers),        'D' => composerMove(.line_start, modifiers),        'H' => composerMove(.draft_start, modifiers),        'F' => composerMove(.draft_end, modifiers),        else => null,    };    if (meta_prefixed or (modifiers & alt_modifier) != 0) return switch (byte) {        'A' => composerMove(.paragraph_up, modifiers),        'B' => composerMove(.paragraph_down, modifiers),        'C' => composerMove(.word_right, modifiers),        'D' => composerMove(.word_left, modifiers),        else => null,    };    if ((modifiers & ctrl_modifier) != 0) return switch (byte) {        'C' => composerMove(.word_right, modifiers),        'D' => composerMove(.word_left, modifiers),        'A' => composerMove(.visual_up, modifiers),        'B' => composerMove(.visual_down, modifiers),        'H' => composerMove(.draft_start, modifiers),        'F' => composerMove(.draft_end, modifiers),        else => null,    };    if ((modifiers & shift_modifier) != 0) return switch (byte) {        'A' => composerMove(.visual_up, modifiers),        'B' => composerMove(.visual_down, modifiers),        'C' => composerMove(.character_right, modifiers),        'D' => composerMove(.character_left, modifiers),        'H' => composerMove(.line_start, modifiers),        'F' => composerMove(.line_end, modifiers),        else => null,    };    return null;} fn ctrlOKeyAction(meta_prefixed: bool, modifiers: u16) InputEscapeAction {    if (!meta_prefixed and modifiers == 0x04) return .toggle_full_transcript;    return .ignore;} // Resolve a Kitty CSI u report (`ESC[<keycode>;<mod>u`). Shared by the// single-parameter and modifier stages, and never returns null so the leading// ESC's pending-cancel is always cleared.fn kittyUnicodeKeyAction(keycode: u16, modifiers: u16, meta_prefixed: bool) InputEscapeAction {    if (keycode == 27 and modifiers == 0) return .escape;    if (keycode == kitty_up_key or keycode == kitty_down_key) {        if (meta_prefixed or modifiers != 0) {            return modifiedArrowAction(                if (keycode == kitty_up_key) 'A' else 'B',                modifiers,                meta_prefixed,            ) orelse .ignore;        }        return if (keycode == kitty_up_key) .cursor_up else .cursor_down;    }    if (keycode == 13 and (modifiers & (shift_modifier | alt_modifier)) != 0) {        return .insert_newline;    }    if (keycode == ' ' and modifiers == shift_modifier and !meta_prefixed) return .{ .remapped_byte = ' ' };    if ((keycode == 'a' or keycode == 'A') and (modifiers & super_modifier) != 0) {        return .{ .composer_shortcut = .select_all };    }    if ((keycode == 'c' or keycode == 'C') and (modifiers & super_modifier) != 0) {        return .{ .composer_shortcut = .copy_selection };    }    if ((keycode == 'x' or keycode == 'X') and (modifiers & super_modifier) != 0) {        return .{ .composer_shortcut = .cut_selection };    }    if ((keycode == 'z' or keycode == 'Z') and (modifiers & super_modifier) != 0) {        return .{ .composer_shortcut = if ((modifiers & shift_modifier) != 0) .redo else .undo };    }    if ((modifiers & (shift_modifier | ctrl_modifier)) ==        (shift_modifier | ctrl_modifier))    {        if (keycode == 'a' or keycode == 'A') return composerMove(.line_start, modifiers);        if (keycode == 'b' or keycode == 'B') return composerMove(.character_left, modifiers);        if (keycode == 'e' or keycode == 'E') return composerMove(.line_end, modifiers);        if (keycode == 'f' or keycode == 'F') return composerMove(.character_right, modifiers);    }    if (keycode == 'b' and (modifiers & alt_modifier) != 0) {        if ((modifiers & shift_modifier) != 0) return composerMove(.word_left, modifiers);        return .word_left;    }    if (keycode == 'f' and (modifiers & alt_modifier) != 0) {        if ((modifiers & shift_modifier) != 0) return composerMove(.word_right, modifiers);        return .word_right;    }    if ((keycode == 'd' or keycode == 'D') and (meta_prefixed or (modifiers & 0x02) != 0)) return .delete_word_right;    if (keycode == 'o' or keycode == 'O') return ctrlOKeyAction(meta_prefixed, modifiers);    if ((keycode == 'r' or keycode == 'R') and (modifiers & 0x08) != 0) return .open_all_sessions;    if (keycode == 9 and (modifiers & 0x01) != 0) return .toggle_permission_mode;    if ((modifiers & 0x04) != 0) {        if (keycode == '_') return .{ .remapped_byte = 31 };        if (keycode >= 'a' and keycode <= 'z') return .{ .remapped_byte = @intCast(keycode - 96) };        if (keycode >= 'A' and keycode <= 'Z') return .{ .remapped_byte = @intCast(keycode - 64) };    }    if (keycode == 127 and (modifiers & 0x08) != 0) return .delete_to_line_start;    if (keycode == 127 and (modifiers & 0x02) != 0) return .delete_word_left;    if (keycode == 13) return .{ .remapped_byte = '\r' };    if (keycode == 9) return .{ .remapped_byte = '\t' };    if (keycode == 127) return .{ .remapped_byte = 127 };    return .ignore;} pub fn controlByteFeatureAction(byte: u8) ?InputEscapeAction {    return switch (byte) {        15 => .toggle_full_transcript,        else => null,    };} pub const MouseReportDiscardResult = enum {    pending,    report_end,    bound,    restart,}; fn baseEscapeStage(stage: u8) u8 {    return stage & ~escape_meta_mask;} fn hasMetaPrefix(stage: u8) bool {    return (stage & escape_meta_mask) != 0;} fn setBaseEscapeStage(stage: *u8, base: u8) void {    stage.* = (stage.* & escape_meta_mask) | base;} pub fn isLegacyX10PayloadStage(stage: u8) bool {    return switch (baseEscapeStage(stage)) {        legacy_x10_button_stage,        legacy_x10_column_stage,        legacy_x10_row_stage,        => true,        else => false,    };} pub fn isMouseReportPayloadStage(stage: u8) bool {    return switch (baseEscapeStage(stage)) {        sgr_mouse_stage,        sgr_mouse_column_stage,        sgr_mouse_row_stage,        legacy_x10_button_stage,        legacy_x10_column_stage,        legacy_x10_row_stage,        => true,        else => false,    };} pub fn isMouseReportDiscardStage(stage: u8) bool {    return switch (baseEscapeStage(stage)) {        discarded_sgr_mouse_stage,        discarded_x10_mouse_stage,        => true,        else => false,    };} pub fn isPrivateCsiPayloadStage(stage: u8) bool {    return baseEscapeStage(stage) == csi_private_stage;} fn resetMouseEscapeDecode(stage: *u8, param: *u16, param2: *u16, mouse: *MouseInput) void {    stage.* = 0;    param.* = 0;    param2.* = 0;    mouse.reset();} pub fn beginMouseReportDiscard(stage: *u8, param: *u16, param2: *u16, mouse: *MouseInput) bool {    const remaining = switch (baseEscapeStage(stage.*)) {        sgr_mouse_stage,        sgr_mouse_column_stage,        sgr_mouse_row_stage,        => sgr_mouse_max_bytes -| mouse.sgr_bytes,        legacy_x10_button_stage => 3,        legacy_x10_column_stage => 2,        legacy_x10_row_stage => 1,        else => return false,    };    if (remaining == 0) {        resetMouseEscapeDecode(stage, param, param2, mouse);        return true;    }     mouse.discard_remaining = remaining;    param.* = 0;    param2.* = 0;    if (isLegacyX10PayloadStage(stage.*)) {        setBaseEscapeStage(stage, discarded_x10_mouse_stage);    } else {        setBaseEscapeStage(stage, discarded_sgr_mouse_stage);    }    return true;} pub fn consumeMouseReportDiscardByte(    stage: *u8,    param: *u16,    param2: *u16,    mouse: *MouseInput,    byte: u8,) MouseReportDiscardResult {    std.debug.assert(isMouseReportDiscardStage(stage.*));    if (byte == 0x1b) {        resetMouseEscapeDecode(stage, param, param2, mouse);        stage.* = 1;        return .restart;    }     if (baseEscapeStage(stage.*) == discarded_sgr_mouse_stage and (byte == 'M' or byte == 'm')) {        resetMouseEscapeDecode(stage, param, param2, mouse);        return .report_end;    }     std.debug.assert(mouse.discard_remaining > 0);    mouse.discard_remaining -= 1;    if (mouse.discard_remaining != 0) return .pending;     resetMouseEscapeDecode(stage, param, param2, mouse);    return .bound;} fn noteSgrMouseByte(mouse: *MouseInput) bool {    if (mouse.sgr_bytes == sgr_mouse_max_bytes) return false;    mouse.sgr_bytes += 1;    return true;} fn sgrMouseAction(button: u16, column: u16, row: u16, terminator: u8) InputEscapeAction {    if (column == 0 or row == 0) return .ignore;    if ((button & 64) != 0) {        if (terminator != 'M') return .ignore;        return switch (button & 0b11) {            0 => .{ .mouse_wheel = .up },            1 => .{ .mouse_wheel = .down },            else => .ignore,        };    }    if ((button & 0b11) != 0) return .ignore;     const kind: MousePointerKind = if (terminator == 'm')        .release    else if (terminator == 'M' and (button & 32) != 0)        .drag    else if (terminator == 'M')        .press    else        return .ignore;    return .{ .mouse_pointer = .{        .kind = kind,        .column = column,        .row = row,        .shift = (button & 4) != 0,        .alt = (button & 8) != 0,        .ctrl = (button & 16) != 0,    } };} pub fn consumeInputEscapeByte(stage: *u8, param: *u16, param2: *u16, byte: u8) ?InputEscapeAction {    var mouse = MouseInput{};    return consumeInputEscapeByteWithMouse(stage, param, param2, &mouse, byte);} pub fn consumeInputEscapeByteWithMouse(    stage: *u8,    param: *u16,    param2: *u16,    mouse: *MouseInput,    byte: u8,) ?InputEscapeAction {    const base_stage = baseEscapeStage(stage.*);    if (base_stage == 0) return null;    if (byte == 0x1b and base_stage != 1 and !isLegacyX10PayloadStage(stage.*)) {        stage.* = 1;        param.* = 0;        param2.* = 0;        mouse.reset();        return null;    }     switch (base_stage) {        1 => {            if (byte == '[') {                setBaseEscapeStage(stage, 2);                param.* = 0;                param2.* = 0;                mouse.reset();                return null;            }            if (byte == 'O') {                setBaseEscapeStage(stage, 4);                param.* = 0;                param2.* = 0;                return null;            }            if (byte == '\r' or byte == '\n') {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .insert_newline;            }            if (byte == 'o') {                stage.* = 0;                param.* = 0;                param2.* = 0;                // ESC o is Meta-O, not a Ctrl-O terminal report.                return .ignore;            }            if (byte == 'b') {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .word_left;            }            if (byte == 'f') {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .word_right;            }            if (byte == 'd') {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .{ .composer_shortcut = .delete_word_right };            }            if (byte == 0x7f or byte == 0x08) {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .delete_word_left;            }            if (byte == 0x1b) {                stage.* = escape_meta_mask | 1;                return null;            }            stage.* = 0;            param.* = 0;            param2.* = 0;            return null;        },        2 => {            const meta_prefixed = hasMetaPrefix(stage.*);            const action: ?InputEscapeAction = switch (byte) {                'A', 'B', 'C', 'D' => if (meta_prefixed)                    modifiedArrowAction(byte, 0, true)                else switch (byte) {                    'A' => .cursor_up,                    'B' => .cursor_down,                    'C' => .cursor_right,                    'D' => .cursor_left,                    else => unreachable,                },                'H' => .home,                'F' => .end,                'Z' => .toggle_permission_mode,                '<' => {                    mouse.reset();                    setBaseEscapeStage(stage, sgr_mouse_stage);                    return null;                },                'M' => {                    mouse.reset();                    setBaseEscapeStage(stage, legacy_x10_button_stage);                    return null;                },                '?' => {                    param.* = 0;                    param2.* = 0;                    mouse.reset();                    setBaseEscapeStage(stage, csi_private_stage);                    return null;                },                '0'...'9' => {                    param.* = byte - '0';                    param2.* = 1;                    setBaseEscapeStage(stage, 3);                    return null;                },                else => null,            };            stage.* = 0;            param.* = 0;            param2.* = 0;            return action;        },        csi_private_stage => {            if (byte >= 0x40 and byte <= 0x7e) {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .ignore;            }             param.* = std.math.add(u16, param.*, 1) catch std.math.maxInt(u16);            if (param.* >= csi_private_max_bytes) {                stage.* = 0;                param.* = 0;                param2.* = 0;                return .ignore;            }            return null;        },        3 => {            if (byte >= '0' and byte <= '9') {                appendCsiDigitSaturating(param, byte);                if (param2.* < std.math.maxInt(u16)) param2.* += 1;                return null;            }             if (byte == ';') {                param2.* = param.*;                param.* = 0;                setBaseEscapeStage(stage, 5);                return null;            }             const meta_prefixed = hasMetaPrefix(stage.*);            const value = param.*;            const digit_count = param2.*;            stage.* = 0;            param.* = 0;            param2.* = 0;            if (byte == 'u') {                // Single-parameter CSI u: no `;`, so modifiers == 0.                return kittyUnicodeKeyAction(value, 0, meta_prefixed);            }            if (byte != '~') return null;             if (digit_count == 3 and value == 200) return .paste_start;            if (digit_count == 3 and value == 201) return .paste_end;             return switch (value) {                1, 7 => .home,                3 => .delete_next,                4, 8 => .end,                5 => .page_up,                6 => .page_down,                else => .ignore,            };        },        4 => {

Lines 1–500 of 693.