tinymist_query/
lsp_typst_boundary.rs1use tinymist_std::path::PathClean;
4use tinymist_world::vfs::PathResolution;
5
6use crate::prelude::*;
7
8pub use tinymist_analysis::location::LspPosition;
10pub use tinymist_analysis::location::LspRange;
12
13pub use tinymist_analysis::location::*;
14
15const UNTITLED_ROOT: &str = "/untitled";
16static EMPTY_URL: LazyLock<Url> = LazyLock::new(|| Url::parse("file://").unwrap());
17
18pub fn untitled_url(path: &Path) -> anyhow::Result<Url> {
20 Ok(Url::parse(&format!("untitled:{}", path.display()))?)
21}
22
23pub fn is_untitled_path(p: &Path) -> bool {
25 p.starts_with(UNTITLED_ROOT)
26}
27
28pub fn path_to_url(path: &Path) -> anyhow::Result<Url> {
30 if let Ok(untitled) = path.strip_prefix(UNTITLED_ROOT) {
31 if untitled == Path::new("nEoViM-BuG") {
33 return Ok(EMPTY_URL.clone());
34 }
35
36 return untitled_url(untitled);
37 }
38
39 url_from_file_path(path)
40}
41
42pub fn path_res_to_url(path: PathResolution) -> anyhow::Result<Url> {
44 match path {
45 PathResolution::Rootless(path) => untitled_url(path.as_rooted_path()),
46 PathResolution::Resolved(path) => path_to_url(&path),
47 }
48}
49
50pub fn url_to_path(uri: &Url) -> PathBuf {
52 if uri.scheme() == "file" {
53 if !uri.has_host() && uri.path() == "/" {
55 return PathBuf::from("/untitled/nEoViM-BuG");
56 }
57
58 return url_to_file_path(uri);
59 }
60
61 if uri.scheme() == "untitled" {
62 let mut bytes = UNTITLED_ROOT.as_bytes().to_vec();
63
64 let path = uri.path();
66 let segs = path.strip_prefix('/').unwrap_or(path).split('/');
67 for segment in segs {
68 bytes.push(b'/');
69 bytes.extend(percent_encoding::percent_decode(segment.as_bytes()));
70 }
71
72 return Path::new(String::from_utf8_lossy(&bytes).as_ref()).clean();
73 }
74
75 url_to_file_path(uri)
76}
77
78#[cfg(not(target_arch = "wasm32"))]
79fn url_from_file_path(path: &Path) -> anyhow::Result<Url> {
80 Url::from_file_path(path).or_else(|never| {
81 let _: () = never;
82
83 anyhow::bail!("could not convert path to URI: path: {path:?}",)
84 })
85}
86
87#[cfg(target_arch = "wasm32")]
88fn url_from_file_path(path: &Path) -> anyhow::Result<Url> {
89 let path_str = path.to_string_lossy();
91 let url_str = if path_str.starts_with('/') {
92 format!("file://{}", path_str)
93 } else {
94 format!("file:///{}", path_str)
95 };
96 Url::parse(&url_str).map_err(|e| anyhow::anyhow!("could not convert path to URI: {}", e))
97}
98
99#[cfg(not(target_arch = "wasm32"))]
100fn url_to_file_path(uri: &Url) -> PathBuf {
101 uri.to_file_path()
102 .unwrap_or_else(|_| panic!("could not convert URI to path: URI: {uri:?}",))
103}
104
105#[cfg(target_arch = "wasm32")]
106fn url_to_file_path(uri: &Url) -> PathBuf {
107 PathBuf::from(uri.path())
109}
110#[cfg(test)]
111mod test {
112 use super::*;
113
114 #[test]
115 fn test_untitled() {
116 let path = Path::new("/untitled/test");
117 let uri = path_to_url(path).unwrap();
118 assert_eq!(uri.scheme(), "untitled");
119 assert_eq!(uri.path(), "test");
120
121 let path = url_to_path(&uri);
122 assert_eq!(path, Path::new("/untitled/test").clean());
123 assert!(is_untitled_path(&path));
124 }
125
126 #[test]
127 fn unnamed_buffer() {
128 let uri = EMPTY_URL.clone();
130 let path = url_to_path(&uri);
131 assert_eq!(path, Path::new("/untitled/nEoViM-BuG"));
132
133 let uri2 = path_to_url(&path).unwrap();
134 assert_eq!(EMPTY_URL.clone(), uri2);
135 }
136}