perl_position_tracking/
wire.rs1use crate::{offset_to_utf16_line_col, utf16_line_col_to_offset};
3use serde::{Deserialize, Serialize};
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
5pub struct WirePosition {
6 pub line: u32,
7 pub character: u32,
8}
9impl WirePosition {
10 pub fn new(line: u32, character: u32) -> Self {
11 Self { line, character }
12 }
13 pub fn from_byte_offset(source: &str, byte_offset: usize) -> Self {
14 let (line, character) = offset_to_utf16_line_col(source, byte_offset);
15 Self { line, character }
16 }
17 pub fn to_byte_offset(&self, source: &str) -> usize {
18 utf16_line_col_to_offset(source, self.line, self.character)
19 }
20}
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
22pub struct WireRange {
23 pub start: WirePosition,
24 pub end: WirePosition,
25}
26impl WireRange {
27 pub fn new(start: WirePosition, end: WirePosition) -> Self {
28 Self { start, end }
29 }
30 pub fn from_byte_offsets(source: &str, start_byte: usize, end_byte: usize) -> Self {
31 Self {
32 start: WirePosition::from_byte_offset(source, start_byte),
33 end: WirePosition::from_byte_offset(source, end_byte),
34 }
35 }
36 pub fn empty(pos: WirePosition) -> Self {
37 Self { start: pos, end: pos }
38 }
39 pub fn whole_document(source: &str) -> Self {
40 Self {
41 start: WirePosition::new(0, 0),
42 end: WirePosition::from_byte_offset(source, source.len()),
43 }
44 }
45}
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct WireLocation {
48 pub uri: String,
49 pub range: WireRange,
50}
51impl WireLocation {
52 pub fn new(uri: String, range: WireRange) -> Self {
53 Self { uri, range }
54 }
55}
56#[cfg(feature = "lsp-compat")]
57impl From<WirePosition> for lsp_types::Position {
58 fn from(p: WirePosition) -> Self {
59 Self { line: p.line, character: p.character }
60 }
61}
62#[cfg(feature = "lsp-compat")]
63impl From<lsp_types::Position> for WirePosition {
64 fn from(p: lsp_types::Position) -> Self {
65 Self { line: p.line, character: p.character }
66 }
67}
68#[cfg(feature = "lsp-compat")]
69impl From<WireRange> for lsp_types::Range {
70 fn from(r: WireRange) -> Self {
71 Self { start: r.start.into(), end: r.end.into() }
72 }
73}
74#[cfg(feature = "lsp-compat")]
75impl From<lsp_types::Range> for WireRange {
76 fn from(r: lsp_types::Range) -> Self {
77 Self { start: r.start.into(), end: r.end.into() }
78 }
79}
80#[cfg(feature = "lsp-compat")]
81fn fallback_lsp_uri() -> lsp_types::Uri {
82 for candidate in ["file:///unknown", "file:///", "about:blank", "urn:perl-lsp:unknown"] {
83 if let Ok(uri) = candidate.parse::<lsp_types::Uri>() {
84 return uri;
85 }
86 }
87
88 let mut suffix = 0usize;
90 loop {
91 let candidate = format!("http://localhost/{suffix}");
92 if let Ok(uri) = candidate.parse::<lsp_types::Uri>() {
93 return uri;
94 }
95 suffix = suffix.saturating_add(1);
96 }
97}
98
99#[cfg(feature = "lsp-compat")]
100impl From<WireLocation> for lsp_types::Location {
101 fn from(l: WireLocation) -> Self {
102 let uri = match l.uri.parse::<lsp_types::Uri>() {
103 Ok(u) => u,
104 Err(_) => fallback_lsp_uri(),
105 };
106 Self { uri, range: l.range.into() }
107 }
108}