use tower_lsp::lsp_types::{Position, Range};
pub fn byte_to_position(content: &str, byte_offset: usize) -> Position {
let mut line = 0u32;
let mut character = 0u32;
let mut current_byte = 0usize;
for c in content.chars() {
if current_byte >= byte_offset {
break;
}
if c == '\n' {
line += 1;
character = 0;
} else {
character += 1;
}
current_byte += c.len_utf8();
}
Position { line, character }
}
pub fn byte_range_to_lsp_range(content: &str, start_byte: usize, end_byte: usize) -> Range {
Range {
start: byte_to_position(content, start_byte),
end: byte_to_position(content, end_byte),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_byte_to_position_start() {
let content = "hello";
let pos = byte_to_position(content, 0);
assert_eq!(pos.line, 0);
assert_eq!(pos.character, 0);
}
#[test]
fn test_byte_to_position_same_line() {
let content = "hello world";
let pos = byte_to_position(content, 6); assert_eq!(pos.line, 0);
assert_eq!(pos.character, 6);
}
#[test]
fn test_byte_to_position_second_line() {
let content = "hello\nworld";
let pos = byte_to_position(content, 6); assert_eq!(pos.line, 1);
assert_eq!(pos.character, 0);
}
#[test]
fn test_byte_to_position_middle_of_second_line() {
let content = "hello\nworld";
let pos = byte_to_position(content, 8); assert_eq!(pos.line, 1);
assert_eq!(pos.character, 2);
}
#[test]
fn test_byte_to_position_multiple_lines() {
let content = "line1\nline2\nline3";
let pos = byte_to_position(content, 12); assert_eq!(pos.line, 2);
assert_eq!(pos.character, 0);
}
#[test]
fn test_byte_to_position_end_of_content() {
let content = "hello";
let pos = byte_to_position(content, 5); assert_eq!(pos.line, 0);
assert_eq!(pos.character, 5);
}
#[test]
fn test_byte_to_position_empty_content() {
let content = "";
let pos = byte_to_position(content, 0);
assert_eq!(pos.line, 0);
assert_eq!(pos.character, 0);
}
#[test]
fn test_byte_to_position_utf8_multibyte() {
let content = "hello\u{00e9}world"; let pos = byte_to_position(content, 7);
assert_eq!(pos.line, 0);
assert_eq!(pos.character, 6); }
#[test]
fn test_byte_to_position_crlf() {
let content = "hello\r\nworld";
let pos = byte_to_position(content, 7); assert_eq!(pos.line, 1);
assert_eq!(pos.character, 0);
}
#[test]
fn test_byte_range_to_lsp_range_same_line() {
let content = "hello world";
let range = byte_range_to_lsp_range(content, 0, 5); assert_eq!(range.start.line, 0);
assert_eq!(range.start.character, 0);
assert_eq!(range.end.line, 0);
assert_eq!(range.end.character, 5);
}
#[test]
fn test_byte_range_to_lsp_range_cross_line() {
let content = "hello\nworld";
let range = byte_range_to_lsp_range(content, 3, 8); assert_eq!(range.start.line, 0);
assert_eq!(range.start.character, 3);
assert_eq!(range.end.line, 1);
assert_eq!(range.end.character, 2);
}
#[test]
fn test_byte_range_to_lsp_range_insertion_point() {
let content = "hello";
let range = byte_range_to_lsp_range(content, 5, 5);
assert_eq!(range.start, range.end);
assert_eq!(range.start.character, 5);
}
#[test]
fn test_byte_range_yaml_frontmatter() {
let content = "---\nname: test-skill\nversion: 1.0.0\n---\n";
let range = byte_range_to_lsp_range(content, 10, 20);
assert_eq!(range.start.line, 1);
assert_eq!(range.start.character, 6); assert_eq!(range.end.line, 1);
assert_eq!(range.end.character, 16); }
}