cooklang_language_server/lsp/
from_proto.rs1use anyhow::{format_err, Result};
7use text_size::{TextRange, TextSize};
8use tower_lsp::lsp_types;
9
10use crate::utils::line_index::LineIndex;
11
12#[derive(Debug, Clone, Copy, Default)]
14pub enum PositionEncoding {
15 Utf8,
17 #[default]
19 Utf16,
20}
21
22pub fn offset(
24 line_index: &LineIndex,
25 position: lsp_types::Position,
26 encoding: PositionEncoding,
27) -> Result<TextSize> {
28 let line = position.line;
29 let col = position.character;
30
31 let line_start = line_index.line_start(line).ok_or_else(|| {
33 format_err!(
34 "Invalid line {} (document has {} lines)",
35 line,
36 line_index.line_count()
37 )
38 })?;
39
40 let col_offset = match encoding {
42 PositionEncoding::Utf8 => TextSize::from(col),
43 PositionEncoding::Utf16 => line_index
44 .utf16_to_utf8_col(line, col)
45 .map(TextSize::from)
46 .ok_or_else(|| format_err!("Invalid UTF-16 column {} on line {}", col, line))?,
47 };
48
49 Ok(line_start + col_offset)
50}
51
52pub fn text_range(
54 line_index: &LineIndex,
55 range: lsp_types::Range,
56 encoding: PositionEncoding,
57) -> Result<TextRange> {
58 let start = offset(line_index, range.start, encoding)?;
59 let end = offset(line_index, range.end, encoding)?;
60
61 if end < start {
62 return Err(format_err!("Invalid range: end before start"));
63 }
64
65 Ok(TextRange::new(start, end))
66}
67
68pub fn line_col(
70 line_index: &LineIndex,
71 position: lsp_types::Position,
72 encoding: PositionEncoding,
73) -> Result<(u32, u32)> {
74 let line = position.line;
75 let col = match encoding {
76 PositionEncoding::Utf8 => position.character,
77 PositionEncoding::Utf16 => line_index
78 .utf16_to_utf8_col(line, position.character)
79 .ok_or_else(|| {
80 format_err!(
81 "Invalid UTF-16 column {} on line {}",
82 position.character,
83 line
84 )
85 })?,
86 };
87
88 Ok((line, col))
89}