1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
4
5use crate::modules::ModuleId;
6
7#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
15#[ts(export, type = "[number, number, number]")]
16pub struct SourceRange([usize; 3]);
17
18impl From<[usize; 3]> for SourceRange {
19 fn from(value: [usize; 3]) -> Self {
20 Self(value)
21 }
22}
23
24impl From<&SourceRange> for miette::SourceSpan {
25 fn from(source_range: &SourceRange) -> Self {
26 let length = source_range.end() - source_range.start();
27 let start = miette::SourceOffset::from(source_range.start());
28 Self::new(start, length)
29 }
30}
31
32impl From<SourceRange> for miette::SourceSpan {
33 fn from(source_range: SourceRange) -> Self {
34 Self::from(&source_range)
35 }
36}
37
38impl SourceRange {
39 pub fn new(start: usize, end: usize, module_id: ModuleId) -> Self {
41 Self([start, end, module_id.as_usize()])
42 }
43
44 pub fn synthetic() -> Self {
46 Self::default()
47 }
48
49 pub fn is_synthetic(&self) -> bool {
52 self.start() == 0 && self.end() == 0
53 }
54
55 pub fn start(&self) -> usize {
57 self.0[0]
58 }
59
60 pub fn start_as_range(&self) -> Self {
63 Self([self.0[0], self.0[0], self.0[2]])
64 }
65
66 pub fn end(&self) -> usize {
68 self.0[1]
69 }
70
71 pub fn module_id(&self) -> ModuleId {
73 ModuleId::from_usize(self.0[2])
74 }
75
76 pub fn contains(&self, pos: usize) -> bool {
78 pos >= self.start() && pos <= self.end()
79 }
80
81 pub fn start_to_lsp_position(&self, code: &str) -> LspPosition {
82 let mut line = code.get(..self.start()).unwrap_or_default().lines().count();
85 if line > 0 {
86 line = line.saturating_sub(1);
87 }
88 let column = code[..self.start()].lines().last().map(|l| l.len()).unwrap_or_default();
89
90 LspPosition {
91 line: line as u32,
92 character: column as u32,
93 }
94 }
95
96 pub fn end_to_lsp_position(&self, code: &str) -> LspPosition {
97 let lines = code.get(..self.end()).unwrap_or_default().lines();
98 if lines.clone().count() == 0 {
99 return LspPosition { line: 0, character: 0 };
100 }
101
102 let line = lines.clone().count() - 1;
105 let column = lines.last().map(|l| l.len()).unwrap_or_default();
106
107 LspPosition {
108 line: line as u32,
109 character: column as u32,
110 }
111 }
112
113 pub fn to_lsp_range(&self, code: &str) -> LspRange {
114 let start = self.start_to_lsp_position(code);
115 let end = self.end_to_lsp_position(code);
116 LspRange { start, end }
117 }
118}