Rust源代码位置打印库print-positions的使用,精准定位代码行号与字符位置信息

Rust源代码位置打印库print-positions的使用,精准定位代码行号与字符位置信息

简介

print_positions 是一个 Rust 库,它提供了迭代器来返回组成"打印位置"的字符切片,而不是源字符串中的单个字符。

打印位置是扩展字形簇的泛化。像字形一样,它在屏幕上渲染时占据一个"字符"位置。然而,它还可能包含改变颜色或渲染强度的ANSI 转义码。

使用场景

在布局固定宽度屏幕应用程序时,了解内容将占用多少可见列非常有用。但内容中的字节或字符数量通常会更大,这是由于 UTF8 编码、Unicode 组合字符和零宽度连接器以及 ANSI 兼容设备和应用程序的控制码和转义序列(用于指定文本颜色和强调)所导致的。

print_position 迭代器考虑了这些因素并简化了计算:内容在屏幕上占用的列数就是迭代器返回的打印位置切片数量。

示例

use print_positions::print_positions;

// 内容是带有分音符的 e,以绿色显示并在末尾有颜色重置。
// 在屏幕上看起来像1个字符。参见示例"padding"打印一个。
let content = ["\u{1b}[30;42m", "\u{0065}", "\u{0308}", "\u{1b}[0m"].join("");

let print_positions:Vec<_> = print_positions(&content).collect();
assert_eq!(content.len(), 15);          // content是15个字符长
assert_eq!(print_positions.len(), 1);   // 但只有1个打印位置

完整示例

下面是一个更完整的示例,展示了如何使用 print-positions 库来精确计算字符串在终端显示时的实际宽度:

use print_positions::{print_positions, print_position_indices};

fn main() {
    // 包含ANSI颜色代码和Unicode组合字符的字符串
    let colored_text = "\x1b[31mH\x1b[0m\x1b[32me\x1b{0m\x1b[33ml\x1b[0m\x1b[34ml\x1b[0m\x1b[35mo\x1b[0m\x1b[36m!\x1b[0m";
    let complex_unicode = "cafe\u{301}"; // 带有重音符号的"café"
    
    // 计算打印位置
    let colored_positions: Vec<_> = print_positions(colored_text).collect();
    let unicode_positions: Vec<_> = print_positions(complex_unicode).collect();
    
    println!("原始字符串: {}", colored_text);
    println!("字符长度: {}", colored_text.len());
    println!("打印位置数量: {}", colored_positions.len());
    
    println!("\n复杂Unicode字符串: {}", complex_unicode);
    println!("字符长度: {}", complex_unicode.len());
    println!("打印位置数量: {}", unicode_positions.len());
    
    // 使用print_position_indices获取索引信息
    let indices: Vec<_> = print_position_indices(colored_text).collect();
    println!("\n打印位置索引:");
    for (start, end) in indices {
        println!("位置: {}..{} 内容: {:?}", start, end, &colored_text[start..end]);
    }
}

已知问题

  • 不考虑光标移动
    所有 ANSI 控制字符和序列都被假定为不占用屏幕空间。
    在退格、制表符、换行、CUP、CUU、CUD 等情况下可能有误。欢迎提交 PR 或改进建议!

安装

在项目目录中运行以下 Cargo 命令:

cargo add print-positions

或在 Cargo.toml 中添加以下行:

print-positions = "0.6.1"

许可证

MIT 或 Apache-2.0


1 回复

print-positions库:Rust源代码位置精准定位工具

print-positions是一个Rust库,用于在运行时精确打印代码位置信息(文件名、行号、列号),帮助开发者快速定位代码执行位置。

功能特点

  • 精确到代码行号和字符位置
  • 支持自定义输出格式
  • 轻量级,无额外依赖
  • 适用于调试和日志记录

安装方法

在Cargo.toml中添加依赖:

[dependencies]
print-positions = "0.2"

基本使用方法

use print_positions::print_positions;

fn main() {
    print_positions!(); // 打印当前位置
    
    if true {
        print_positions!("条件分支内"); // 带自定义消息
    }
}

输出示例:

src/main.rs:4:5 - 
src/main.rs:6:9 - 条件分支内

高级用法

1. 获取位置信息而不打印

use print_positions::pos;

fn main() {
    let location = pos!();
    println!("当前位置: {:?}", location);
}

2. 自定义格式输出

use print_positions::print_positions_format;

fn main() {
    print_positions_format!("[文件: {file}, 行: {line}, 列: {column}] - 自定义格式");
}

3. 在函数中使用

use print_positions::print_positions;

fn divide(a: i32, b: i32) -> i32 {
    print_positions!("执行除法运算");
    if b == 0 {
        print_positions!("除数为零检查");
        panic!("除数不能为零");
    }
    a / b
}

fn main() {
    let _ = divide(10, 2);
    // let _ = divide(10, 0); // 会触发panic并显示位置
}

4. 与日志系统集成

use log::info;
use print_positions::pos;

fn main() {
    env_logger::init();
    
    let location = pos!();
    info!("在位置 {:?} 执行重要操作", location);
}

完整示例代码

// 引入print-positions库的功能
use print_positions::{print_positions, print_positions_format, pos};
use log::info;

fn process_data(value: i32) -> Result<i32, &'static str> {
    print_positions!("进入process_data函数");
    
    if value < 0 {
        print_positions_format!("错误检查: 值 {value} 不能为负");
        return Err("输入值不能为负数");
    }
    
    let result = value * 2;
    print_positions!("计算结果: {}", result);
    Ok(result)
}

fn setup_logger() {
    env_logger::Builder::from_default_env()
        .format(|buf, record| {
            let location = pos!();
            writeln!(
                buf,
                "[{}] {} - {} (位置: {:?})",
                record.level(),
                record.target(),
                record.args(),
                location
            )
        })
        .init();
}

fn main() {
    setup_logger();
    
    // 基本用法
    print_positions!("程序开始执行");
    
    // 获取位置信息
    let current_pos = pos!();
    println!("当前代码位置: {:?}", current_pos);
    
    // 在条件分支中使用
    for i in -1..3 {
        if i < 0 {
            print_positions!("处理负数情况");
        } else {
            print_positions_format!("处理非负数: {i}");
        }
        
        match process_data(i) {
            Ok(res) => info!("处理结果: {}", res),
            Err(e) => info!("处理错误: {}", e),
        }
    }
    
    print_positions!("程序结束");
}

输出示例:

src/main.rs:30:5 - 程序开始执行
当前代码位置: Location { file: "src/main.rs", line: 33, column: 21 }
src/main.rs:38:9 - 处理负数情况
src/main.rs:6:5 - 进入process_data函数
src/main.rs:9:9 - 错误检查: 值 -1 不能为负
[INFO] print_positions_demo - 处理错误: 输入值不能为负数 (位置: Location { file: "src/main.rs", line: 45, column: 17 })
src/main.rs:40:13 - 处理非负数: 0
src/main.rs:6:5 - 进入process_data函数
src/main.rs:14:5 - 计算结果: 0
[INFO] print_positions_demo - 处理结果: 0 (位置: Location { file: "src/main.rs", line: 45, column: 17 })
src/main.rs:40:13 - 处理非负数: 1
src/main.rs:6:5 - 进入process_data函数
src/main.rs:14:5 - 计算结果: 2
[INFO] print_positions_demo - 处理结果: 2 (位置: Location { file: "src/main.rs", line: 45, column: 17 })
src/main.rs:40:13 - 处理非负数: 2
src/main.rs:6:5 - 进入process_data函数
src/main.rs:14:5 - 计算结果: 4
[INFO] print_positions_demo - 处理结果: 4 (位置: Location { file: "src/main.rs", line: 45, column: 17 })
src/main.rs:50:5 - 程序结束

实际应用场景

  1. 调试复杂逻辑流:快速定位代码执行路径
  2. 错误定位:精确标记错误发生位置
  3. 性能分析:标记关键代码段的执行位置
  4. 日志增强:为日志添加精确的代码位置

注意事项

  • 位置信息是编译时确定的,不会随代码移动而自动更新
  • 在release模式下仍会保留位置信息,但可能会被优化影响精确度
  • 对于宏展开后的代码,位置信息可能指向宏定义而非调用处

这个库特别适合在需要精确跟踪代码执行位置但又不想或不能使用调试器的情况下使用。

回到顶部