use anyhow::{format_err, Result};
use text_size::{TextRange, TextSize};
use tower_lsp::lsp_types;
use crate::utils::line_index::LineIndex;
#[derive(Debug, Clone, Copy, Default)]
pub enum PositionEncoding {
Utf8,
#[default]
Utf16,
}
pub fn offset(
line_index: &LineIndex,
position: lsp_types::Position,
encoding: PositionEncoding,
) -> Result<TextSize> {
let line = position.line;
let col = position.character;
let line_start = line_index.line_start(line).ok_or_else(|| {
format_err!(
"Invalid line {} (document has {} lines)",
line,
line_index.line_count()
)
})?;
let col_offset = match encoding {
PositionEncoding::Utf8 => TextSize::from(col),
PositionEncoding::Utf16 => line_index
.utf16_to_utf8_col(line, col)
.map(TextSize::from)
.ok_or_else(|| format_err!("Invalid UTF-16 column {} on line {}", col, line))?,
};
Ok(line_start + col_offset)
}
pub fn text_range(
line_index: &LineIndex,
range: lsp_types::Range,
encoding: PositionEncoding,
) -> Result<TextRange> {
let start = offset(line_index, range.start, encoding)?;
let end = offset(line_index, range.end, encoding)?;
if end < start {
return Err(format_err!("Invalid range: end before start"));
}
Ok(TextRange::new(start, end))
}
pub fn line_col(
line_index: &LineIndex,
position: lsp_types::Position,
encoding: PositionEncoding,
) -> Result<(u32, u32)> {
let line = position.line;
let col = match encoding {
PositionEncoding::Utf8 => position.character,
PositionEncoding::Utf16 => line_index
.utf16_to_utf8_col(line, position.character)
.ok_or_else(|| {
format_err!(
"Invalid UTF-16 column {} on line {}",
position.character,
line
)
})?,
};
Ok((line, col))
}