Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/oxc_language_server/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,8 @@ impl LanguageServer for Backend {
// saving the file means we can read again from the file system
self.file_system.write().await.remove(uri);

if let Some(diagnostics) = worker.run_diagnostic_on_save(uri, None).await {
if let Some(diagnostics) = worker.run_diagnostic_on_save(uri, params.text.as_deref()).await
{
self.client.publish_diagnostics(uri.clone(), diagnostics, None).await;
}
}
Expand Down
122 changes: 120 additions & 2 deletions crates/oxc_language_server/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,33 @@ impl Tool for FakeTool {

vec![]
}

fn run_diagnostic(&self, uri: &Uri, content: Option<&str>) -> Option<Vec<Diagnostic>> {
if uri.as_str().ends_with("diagnostics.config") {
return Some(vec![Diagnostic {
message: format!(
"Fake diagnostic for content: {}",
content.unwrap_or("<no content>")
),
..Default::default()
}]);
}
None
}

fn run_diagnostic_on_change(
&self,
uri: &Uri,
content: Option<&str>,
) -> Option<Vec<Diagnostic>> {
// For this fake tool, we use the same logic as run_diagnostic
self.run_diagnostic(uri, content)
}

fn run_diagnostic_on_save(&self, uri: &Uri, content: Option<&str>) -> Option<Vec<Diagnostic>> {
// For this fake tool, we use the same logic as run_diagnostic
self.run_diagnostic(uri, content)
}
}

// A test server that can send requests and receive responses.
Expand Down Expand Up @@ -454,8 +481,8 @@ mod test_suite {
use tower_lsp_server::{
jsonrpc::{Id, Response},
lsp_types::{
ApplyWorkspaceEditResponse, InitializeResult, ServerInfo, WorkspaceEdit,
WorkspaceFolder,
ApplyWorkspaceEditResponse, InitializeResult, PublishDiagnosticsParams, ServerInfo,
WorkspaceEdit, WorkspaceFolder,
},
};

Expand Down Expand Up @@ -989,4 +1016,95 @@ mod test_suite {

server.shutdown(4).await;
}

#[tokio::test]
async fn test_diagnostic_on_open() {
let mut server = TestServer::new_initialized(
|client| Backend::new(client, server_info(), vec![Box::new(FakeToolBuilder)]),
initialize_request(false, false, false),
)
.await;

let file = format!("{WORKSPACE}/diagnostics.config");
let content = "some text";
server.send_request(did_open(&file, content)).await;

let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");
let params: PublishDiagnosticsParams =
serde_json::from_value(diagnostic_response.params().unwrap().clone()).unwrap();
assert_eq!(params.uri, file.parse().unwrap());
assert_eq!(params.diagnostics.len(), 1);
assert_eq!(
params.diagnostics[0].message,
format!("Fake diagnostic for content: {content}")
);

server.shutdown(4).await;
}

#[tokio::test]
async fn test_diagnostic_on_change() {
let mut server = TestServer::new_initialized(
|client| Backend::new(client, server_info(), vec![Box::new(FakeToolBuilder)]),
initialize_request(false, false, false),
)
.await;

let file = format!("{WORKSPACE}/diagnostics.config");
let content = "new text";
server.send_request(did_open(&file, "old text")).await;
let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");

server.send_request(did_change(&file, content)).await;

let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");
let params: PublishDiagnosticsParams =
serde_json::from_value(diagnostic_response.params().unwrap().clone()).unwrap();
assert_eq!(params.uri, file.parse().unwrap());
assert_eq!(params.diagnostics.len(), 1);
assert_eq!(
params.diagnostics[0].message,
format!("Fake diagnostic for content: {content}")
);

server.shutdown(4).await;
}

#[tokio::test]
async fn test_diagnostic_on_save() {
let mut server = TestServer::new_initialized(
|client| Backend::new(client, server_info(), vec![Box::new(FakeToolBuilder)]),
initialize_request(false, false, false),
)
.await;

let file = format!("{WORKSPACE}/diagnostics.config");
let content = "new text";
server.send_request(did_open(&file, "old text")).await;
let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");

server.send_request(did_change(&file, content)).await;

let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");

server.send_request(did_save(&file, content)).await;

let diagnostic_response = server.recv_notification().await;
assert_eq!(diagnostic_response.method(), "textDocument/publishDiagnostics");
let params: PublishDiagnosticsParams =
serde_json::from_value(diagnostic_response.params().unwrap().clone()).unwrap();
assert_eq!(params.uri, file.parse().unwrap());
assert_eq!(params.diagnostics.len(), 1);
assert_eq!(
params.diagnostics[0].message,
format!("Fake diagnostic for content: {content}")
);

server.shutdown(4).await;
}
}
119 changes: 119 additions & 0 deletions crates/oxc_language_server/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,4 +563,123 @@ mod tests {
panic!("Expected CodeAction");
}
}

#[tokio::test]
async fn test_run_diagnostic() {
let worker = WorkspaceWorker::new(Uri::from_str("file:///root/").unwrap());
let tools: Vec<Box<dyn ToolBuilder>> = vec![Box::new(FakeToolBuilder)];
worker.start_worker(serde_json::Value::Null, &tools).await;

let diagnostics_no_content = worker
.run_diagnostic(&Uri::from_str("file:///root/diagnostics.config").unwrap(), None)
.await;

assert!(diagnostics_no_content.is_some());
assert_eq!(diagnostics_no_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_no_content.unwrap()[0].message,
"Fake diagnostic for content: <no content>"
);

let diagnostics_with_content = worker
.run_diagnostic(
&Uri::from_str("file:///root/diagnostics.config").unwrap(),
Some("helloworld"),
)
.await;

assert!(diagnostics_with_content.is_some());
assert_eq!(diagnostics_with_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_with_content.unwrap()[0].message,
"Fake diagnostic for content: helloworld"
);

let no_diagnostics =
worker.run_diagnostic(&Uri::from_str("file:///root/unknown.file").unwrap(), None).await;

assert!(no_diagnostics.is_none());
}

#[tokio::test]
async fn test_run_diagnostic_on_change() {
let worker = WorkspaceWorker::new(Uri::from_str("file:///root/").unwrap());
let tools: Vec<Box<dyn ToolBuilder>> = vec![Box::new(FakeToolBuilder)];
worker.start_worker(serde_json::Value::Null, &tools).await;

let diagnostics_no_content = worker
.run_diagnostic_on_change(
&Uri::from_str("file:///root/diagnostics.config").unwrap(),
None,
)
.await;

assert!(diagnostics_no_content.is_some());
assert_eq!(diagnostics_no_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_no_content.unwrap()[0].message,
"Fake diagnostic for content: <no content>"
);

let diagnostics_with_content = worker
.run_diagnostic_on_change(
&Uri::from_str("file:///root/diagnostics.config").unwrap(),
Some("helloworld"),
)
.await;

assert!(diagnostics_with_content.is_some());
assert_eq!(diagnostics_with_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_with_content.unwrap()[0].message,
"Fake diagnostic for content: helloworld"
);

let no_diagnostics = worker
.run_diagnostic_on_change(&Uri::from_str("file:///root/unknown.file").unwrap(), None)
.await;

assert!(no_diagnostics.is_none());
}

#[tokio::test]
async fn test_run_diagnostic_on_save() {
let worker = WorkspaceWorker::new(Uri::from_str("file:///root/").unwrap());
let tools: Vec<Box<dyn ToolBuilder>> = vec![Box::new(FakeToolBuilder)];
worker.start_worker(serde_json::Value::Null, &tools).await;

let diagnostics_no_content = worker
.run_diagnostic_on_save(
&Uri::from_str("file:///root/diagnostics.config").unwrap(),
None,
)
.await;

assert!(diagnostics_no_content.is_some());
assert_eq!(diagnostics_no_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_no_content.unwrap()[0].message,
"Fake diagnostic for content: <no content>"
);

let diagnostics_with_content = worker
.run_diagnostic_on_save(
&Uri::from_str("file:///root/diagnostics.config").unwrap(),
Some("helloworld"),
)
.await;

assert!(diagnostics_with_content.is_some());
assert_eq!(diagnostics_with_content.as_ref().unwrap().len(), 1);
assert_eq!(
diagnostics_with_content.unwrap()[0].message,
"Fake diagnostic for content: helloworld"
);

let no_diagnostics = worker
.run_diagnostic_on_save(&Uri::from_str("file:///root/unknown.file").unwrap(), None)
.await;

assert!(no_diagnostics.is_none());
}
}
Loading