Skip to content

Commit c81236c

Browse files
authored
Merge pull request #2 from kubkon/cross-platform-path-handling
Handle paths in a platform-agnostic way by relying on std::path::Path
2 parents 7158409 + 1445ba7 commit c81236c

File tree

1 file changed

+66
-41
lines changed

1 file changed

+66
-41
lines changed

src/lsp.rs

Lines changed: 66 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tower_lsp::lsp_types::{
1313
DidSaveTextDocumentParams, ExecuteCommandParams, InitializeParams, InitializeResult,
1414
InitializedParams, MessageType, Position, SaveOptions, ServerCapabilities,
1515
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
16-
TextDocumentSyncSaveOptions,
16+
TextDocumentSyncSaveOptions, Url,
1717
};
1818

1919
use tower_lsp::{Client, LanguageServer, LspService, Server};
@@ -34,7 +34,7 @@ struct LspFileService {
3434
eager: bool,
3535
port: Arc<Mutex<u16>>,
3636
root: Arc<PathBuf>,
37-
files: Arc<Mutex<HashMap<String, String>>>,
37+
files: Arc<Mutex<HashMap<PathBuf, String>>>,
3838
sig: Signal,
3939
}
4040

@@ -49,18 +49,14 @@ enum LspFile {
4949

5050
impl LspFile {
5151
async fn new(
52-
files: Arc<Mutex<HashMap<String, String>>>,
52+
files: Arc<Mutex<HashMap<PathBuf, String>>>,
5353
path: &Path,
5454
eager: bool,
5555
) -> Result<Self, Error> {
5656
if !eager {
5757
return Ok(LspFile::File(TokioFile::open(path).await?));
5858
}
59-
let content = files
60-
.lock()
61-
.await
62-
.get(&format!("file://{}", path.to_str().unwrap_or_default()))
63-
.cloned();
59+
let content = files.lock().await.get(path).cloned();
6460
Ok(match content {
6561
Some(v) => LspFile::Content(v.to_string()),
6662
None => LspFile::File(TokioFile::open(path).await?),
@@ -95,45 +91,54 @@ impl Dir for LspDir {
9591
}
9692

9793
impl FileSystemInterface for LspFileService {
98-
async fn get_dir(&self, path: &Path) -> Result<impl Dir, rusty_live_server::Error> {
94+
async fn get_dir(&self, path: &Path) -> Result<impl Dir, Error> {
9995
LspDir::new(path).await
10096
}
10197

102-
async fn get_file(&self, path: &Path) -> Result<impl File, rusty_live_server::Error> {
98+
async fn get_file(&self, path: &Path) -> Result<impl File, Error> {
10399
LspFile::new(self.files.clone(), path, self.eager).await
104100
}
105101
}
106102

107103
#[tower_lsp::async_trait]
108104
impl LanguageServer for Backend {
109105
async fn did_open(&self, params: DidOpenTextDocumentParams) {
110-
let uri = params.text_document.uri.to_string();
106+
let uri = params.text_document.uri;
107+
let Some(file_path) = self.uri_to_file_path(&uri).await else {
108+
return;
109+
};
111110
let content = params.text_document.text;
112111

113-
if let Some((_, service)) = self.get_workspace_for_file(&uri).await {
112+
if let Some((_, service)) = self.get_workspace_for_file(&file_path).await {
114113
let mut files = service.files.lock().await;
115114
if *self.eager.read().await {
116-
files.insert(uri.clone(), content.clone());
115+
files.insert(file_path.clone(), content.clone());
117116
}
118-
self.update_file(&uri, &service, false).await;
117+
self.update_file(&file_path, &service, false).await;
119118
}
120119
}
121120

122121
async fn did_save(&self, params: DidSaveTextDocumentParams) {
123-
let uri = params.text_document.uri.to_string();
124-
if let Some((_, service)) = self.get_workspace_for_file(&uri).await {
122+
let uri = params.text_document.uri;
123+
let Some(file_path) = self.uri_to_file_path(&uri).await else {
124+
return;
125+
};
126+
if let Some((_, service)) = self.get_workspace_for_file(&file_path).await {
125127
let message = format!("File saved: {}", uri);
126128
self.client.log_message(MessageType::INFO, message).await;
127-
self.update_file(&uri, &service, true).await;
129+
self.update_file(&file_path, &service, true).await;
128130
}
129131
}
130132

131133
async fn did_change(&self, params: DidChangeTextDocumentParams) {
132-
let uri = params.text_document.uri.to_string();
134+
let uri = params.text_document.uri;
135+
let Some(file_path) = self.uri_to_file_path(&uri).await else {
136+
return;
137+
};
133138

134-
if let Some((_, service)) = self.get_workspace_for_file(&uri).await {
139+
if let Some((_, service)) = self.get_workspace_for_file(&file_path).await {
135140
let mut files = service.files.lock().await;
136-
if let Some(file) = files.get_mut(&uri) {
141+
if let Some(file) = files.get_mut(&file_path) {
137142
if *self.eager.read().await {
138143
for change in params.content_changes {
139144
if let Some(range) = change.range {
@@ -147,7 +152,7 @@ impl LanguageServer for Backend {
147152
}
148153
}
149154
}
150-
self.update_file(&uri, &service, false).await;
155+
self.update_file(&file_path, &service, false).await;
151156
}
152157
}
153158

@@ -276,15 +281,15 @@ impl LanguageServer for Backend {
276281
) -> tower_lsp::jsonrpc::Result<Option<CodeActionResponse>> {
277282
let mut actions = vec![];
278283

279-
let uri = params.text_document.uri.to_string();
284+
let uri = params.text_document.uri;
285+
let Some(file_path) = self.uri_to_file_path(&uri).await else {
286+
return Err(tower_lsp::jsonrpc::Error::invalid_params(
287+
"URL argument invalid",
288+
));
289+
};
280290

281-
if let Some((_, service)) = self.get_workspace_for_file(&uri).await {
291+
if let Some((_, service)) = self.get_workspace_for_file(&file_path).await {
282292
let port = *service.port.lock().await;
283-
let file = uri.strip_prefix("file://").unwrap_or(&uri);
284-
let file = file
285-
.strip_prefix(service.root.to_str().unwrap_or_default())
286-
.unwrap_or(file);
287-
let file = file.strip_prefix("/").unwrap_or(file);
288293
let action = CodeActionOrCommand::CodeAction(CodeAction {
289294
title: format!("Open in Browser({})", port),
290295
kind: Some(CodeActionKind::EMPTY),
@@ -293,7 +298,7 @@ impl LanguageServer for Backend {
293298
command: "openProjectWeb".to_string(),
294299
arguments: Some(vec![
295300
Value::from(service.root.to_str().unwrap_or_default().to_string()),
296-
Value::from(file),
301+
Value::from(file_path.to_str().unwrap_or_default().to_string()),
297302
]),
298303
}),
299304
edit: None,
@@ -343,11 +348,14 @@ impl LanguageServer for Backend {
343348
}
344349

345350
async fn did_close(&self, params: DidCloseTextDocumentParams) {
346-
let uri = params.text_document.uri.to_string();
351+
let uri = params.text_document.uri;
352+
let Some(file_path) = self.uri_to_file_path(&uri).await else {
353+
return;
354+
};
347355

348-
if let Some((_, service)) = self.get_workspace_for_file(&uri).await {
356+
if let Some((_, service)) = self.get_workspace_for_file(&file_path).await {
349357
let mut files = service.files.lock().await;
350-
files.remove(&uri);
358+
files.remove(&file_path);
351359
}
352360
}
353361

@@ -358,25 +366,39 @@ impl LanguageServer for Backend {
358366
}
359367

360368
impl Backend {
361-
async fn get_workspace_for_file(&self, uri: &str) -> Option<(PathBuf, LspFileService)> {
369+
async fn uri_to_file_path(&self, uri: &Url) -> Option<PathBuf> {
370+
let Ok(file_path) = uri.to_file_path() else {
371+
self.client
372+
.log_message(
373+
MessageType::ERROR,
374+
format!("Could not convert URI to file path: {}", uri.to_string()),
375+
)
376+
.await;
377+
return None;
378+
};
379+
Some(file_path)
380+
}
381+
382+
async fn get_workspace_for_file(&self, file_path: &Path) -> Option<(PathBuf, LspFileService)> {
362383
let folders = self.workspace_folders.lock().await;
363384
for (path, (_, service)) in folders.iter() {
364-
let file_path = Path::new(uri.strip_prefix("file://").unwrap_or(uri));
365385
if file_path.starts_with(&service.root.as_ref()) {
366386
return Some((path.clone(), service.clone()));
367387
}
368388
}
369389
None
370390
}
371391

372-
async fn update_file(&self, uri: &str, service: &LspFileService, saved: bool) {
392+
async fn update_file(&self, file_path: &Path, service: &LspFileService, saved: bool) {
373393
self.client
374-
.log_message(MessageType::INFO, format!("File updated: {}", uri))
394+
.log_message(
395+
MessageType::INFO,
396+
format!("File updated: {}", file_path.display()),
397+
)
375398
.await;
376-
let abs = uri.strip_prefix("file://").unwrap_or(uri);
377-
let rel = abs
378-
.strip_prefix(service.root.to_str().unwrap_or_default())
379-
.unwrap_or(abs);
399+
let rel = file_path
400+
.strip_prefix(service.root.as_ref())
401+
.unwrap_or(&file_path);
380402
self.call_custom_function(&service.root, Path::new(rel), saved)
381403
.await;
382404
}
@@ -426,7 +448,10 @@ pub fn get_byte_index_from_position(s: &str, position: Position) -> usize {
426448
if char_index >= char_count {
427449
s.char_indices().last().map(|(i, _)| i).unwrap_or(0)
428450
} else {
429-
s.char_indices().nth(char_index).map(|(i, _)| i).unwrap_or(s.len())
451+
s.char_indices()
452+
.nth(char_index)
453+
.map(|(i, _)| i)
454+
.unwrap_or(s.len())
430455
}
431456
}
432457

0 commit comments

Comments
 (0)