use harper_core::Span;
use tower_lsp::lsp_types::{Position, Range};
pub fn span_to_range(source: &[char], span: Span) -> Range {
let start = index_to_position(source, span.start);
let end = index_to_position(source, span.end);
Range { start, end }
}
fn index_to_position(source: &[char], index: usize) -> Position {
let before = &source[0..index];
let newline_indices: Vec<_> = before
.iter()
.enumerate()
.filter_map(|(idx, c)| if *c == '\n' { Some(idx + 1) } else { None })
.collect();
let lines = newline_indices.len();
let cols = index - newline_indices.last().copied().unwrap_or(0);
Position {
line: lines as u32,
character: cols as u32,
}
}
fn position_to_index(source: &[char], position: Position) -> usize {
let newline_indices =
source
.iter()
.enumerate()
.filter_map(|(idx, c)| if *c == '\n' { Some(idx + 1) } else { None });
let line_start_idx = newline_indices
.take(position.line as usize)
.last()
.unwrap_or(0);
line_start_idx + position.character as usize
}
pub fn range_to_span(source: &[char], range: Range) -> Span {
let start = position_to_index(source, range.start);
let end = position_to_index(source, range.end);
Span::new(start, end)
}
#[cfg(test)]
mod tests {
use tower_lsp::lsp_types::Position;
use super::{index_to_position, position_to_index};
#[test]
fn first_line_correct() {
let source: Vec<_> = "Hello there.".chars().collect();
let start = Position {
line: 0,
character: 4,
};
let i = position_to_index(&source, start);
assert_eq!(i, 4);
let p = index_to_position(&source, i);
assert_eq!(p, start)
}
#[test]
fn reversible_position_conv() {
let source: Vec<_> = "There was a man,\n his voice had timbre,\n unlike a boy."
.chars()
.collect();
let a = Position {
line: 1,
character: 2,
};
let b = position_to_index(&source, a);
assert_eq!(b, 19);
let c = index_to_position(&source, b);
let d = position_to_index(&source, a);
assert_eq!(a, c);
assert_eq!(b, d);
}
}