You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义类nano编辑器垂直光标移动功能故障求助

解决Zig类nano编辑器上下光标移动问题

核心思路

上下光标移动的核心是线性位置与行列坐标的双向转换:先通过坐标转换获取当前光标所在行列,再计算目标行的对应列(目标列不能超过目标行长度),最后转换回线性位置pos_idx。同时要处理边界情况(首行/末行不移动)、空行(目标列设为0)和短行(光标落到目标行尾)。

辅助函数实现

首先实现几个关键辅助函数,用于行列与线性位置的转换、行信息获取:

1. 线性位置转行列坐标(pos_to_xy)

对应你提到的render函数中用到的坐标转换逻辑:

fn pos_to_xy(buffer: *GapBuffer, pos_idx: usize) struct { x: usize, y: usize } {
    var y: usize = 0;
    var x: usize = 0;
    var current_pos: usize = 0;

    for (buffer.items, 0..) |c, i| {
        if (i >= pos_idx) break;
        if (c == '\n') {
            y += 1;
            x = 0;
        } else {
            x += 1;
        }
        current_pos = i + 1;
    }

    // 处理光标在行尾(超过当前遍历到的字符位置)的情况
    if (current_pos < pos_idx) {
        x += pos_idx - current_pos;
    }

    return .{ .x = x, .y = y };
}

2. 行列坐标转线性位置(xy_to_pos)

这是上下移动的核心转换函数:

fn xy_to_pos(buffer: *GapBuffer, x: usize, y: usize) usize {
    var current_y: usize = 0;
    var current_pos: usize = 0;
    var line_x: usize = 0;

    for (buffer.items, 0..) |c, i| {
        if (current_y == y) {
            if (line_x >= x) break;
            if (c != '\n') {
                line_x += 1;
            }
            current_pos = i + 1;
            continue;
        }

        if (c == '\n') {
            current_y += 1;
            if (current_y > y) break;
        }
        current_pos = i + 1;
    }

    // 目标行长度小于指定x时,自动落到行尾
    if (current_y == y && line_x < x) {
        return current_pos;
    }

    return current_pos;
}

3. 行信息获取函数

用于获取总行数和指定行的字符长度:

fn get_line_count(buffer: *GapBuffer) usize {
    var count: usize = 1; // 至少存在一行(即使为空)
    for (buffer.items) |c| {
        if (c == '\n') count += 1;
    }
    return count;
}

fn get_line_length(buffer: *GapBuffer, line_num: usize) usize {
    var current_line: usize = 0;
    var length: usize = 0;

    for (buffer.items) |c| {
        if (current_line == line_num) {
            if (c == '\n') break;
            length += 1;
        } else if (c == '\n') {
            current_line += 1;
            if (current_line > line_num) break;
        }
    }

    return length;
}

上下移动函数实现

1. 修复后的handle_up函数

解决光标跳转错误、跳过空行的问题:

fn handle_up(buffer: *GapBuffer, pos_idx: *usize) void {
    const current_xy = pos_to_xy(buffer, pos_idx.*);
    if (current_xy.y == 0) return; // 已在首行,不移动

    const target_y = current_xy.y - 1;
    const target_line_len = get_line_length(buffer, target_y);
    const target_x = @min(current_xy.x, target_line_len); // 避免超出目标行长度

    pos_idx.* = xy_to_pos(buffer, target_x, target_y);
}

2. 实现handle_down函数

逻辑与handle_up对称,处理末行边界:

fn handle_down(buffer: *GapBuffer, pos_idx: *usize) void {
    const current_xy = pos_to_xy(buffer, pos_idx.*);
    const total_lines = get_line_count(buffer);
    if (current_xy.y >= total_lines - 1) return; // 已在末行,不移动

    const target_y = current_xy.y + 1;
    const target_line_len = get_line_length(buffer, target_y);
    const target_x = @min(current_xy.x, target_line_len);

    pos_idx.* = xy_to_pos(buffer, target_x, target_y);
}

关键注意点

  • 空行处理:空行的get_line_length返回0,因此光标会自动落到空行的起始位置(x=0),不会跳过空行。
  • 短行处理:如果目标行长度小于当前光标所在列,光标会自动落到目标行的行尾,符合nano的行为。
  • Gap Buffer兼容性:上述函数直接基于Gap Buffer的items数组遍历,无需修改Gap Buffer的核心逻辑,适配你现有的存储结构。

内容的提问来源于stack exchange,提问作者Viliam Holly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 10:07:33