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
7 changes: 7 additions & 0 deletions crates/ruff_db/src/system/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,13 @@ impl Deref for SystemPathBuf {
}
}

impl AsRef<Path> for SystemPathBuf {
#[inline]
fn as_ref(&self) -> &Path {
self.0.as_std_path()
}
}

impl<P: AsRef<SystemPath>> FromIterator<P> for SystemPathBuf {
fn from_iter<I: IntoIterator<Item = P>>(iter: I) -> Self {
let mut buf = SystemPathBuf::new();
Expand Down
3 changes: 1 addition & 2 deletions crates/ty_python_semantic/tests/corpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,7 @@ fn run_corpus_tests(pattern: &str) -> anyhow::Result<()> {
let root = SystemPathBuf::from("/src");

let mut db = CorpusDb::new();
db.memory_file_system()
.create_directory_all(root.as_ref())?;
db.memory_file_system().create_directory_all(&root)?;

let workspace_root = get_cargo_workspace_root()?;
let workspace_root = workspace_root.to_string();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use crate::document::DocumentKey;
use crate::server::Result;
use crate::server::api::diagnostics::{publish_diagnostics, publish_settings_diagnostics};
use crate::server::api::diagnostics::{
publish_diagnostics_if_needed, publish_settings_diagnostics,
};
use crate::server::api::traits::{NotificationHandler, SyncNotificationHandler};
use crate::session::Session;
use crate::session::client::Client;
Expand Down Expand Up @@ -92,7 +94,7 @@ impl SyncNotificationHandler for DidChangeWatchedFiles {
);
} else {
for key in session.text_document_handles() {
publish_diagnostics(&key, session, client);
publish_diagnostics_if_needed(&key, session, client);
}
}

Expand Down
14 changes: 7 additions & 7 deletions crates/ty_server/tests/e2e/code_actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ unused-ignore-comment = \"warn\"
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -103,7 +103,7 @@ unused-ignore-comment = \"warn\"
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -145,7 +145,7 @@ unused-ignore-comment = \"warn\"
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -182,7 +182,7 @@ def my_func(): ...
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -221,7 +221,7 @@ def my_func(): ...
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -257,7 +257,7 @@ x: typing.Literal[1] = 1
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -294,7 +294,7 @@ html.parser
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// Wait for diagnostics to be computed.
let diagnostics = server.document_diagnostic_request(foo, None);
Expand Down
8 changes: 4 additions & 4 deletions crates/ty_server/tests/e2e/initialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let hover = server.hover_request(foo, Position::new(0, 5));

assert!(
Expand Down Expand Up @@ -326,7 +326,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let hover = server.hover_request(foo, Position::new(0, 5));

assert!(
Expand Down Expand Up @@ -367,14 +367,14 @@ def bar() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let hover_foo = server.hover_request(foo, Position::new(0, 5));
assert!(
hover_foo.is_none(),
"Expected no hover information for workspace A, got: {hover_foo:?}"
);

server.open_text_document(bar, &bar_content, 1);
server.open_text_document(bar, bar_content, 1);
let hover_bar = server.hover_request(bar, Position::new(0, 5));
assert!(
hover_bar.is_some(),
Expand Down
4 changes: 2 additions & 2 deletions crates/ty_server/tests/e2e/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ y = foo(1)
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let _ = server.await_notification::<PublishDiagnostics>();

let hints = server
Expand Down Expand Up @@ -132,7 +132,7 @@ fn variable_inlay_hints_disabled() -> Result<()> {
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let _ = server.await_notification::<PublishDiagnostics>();

let hints = server
Expand Down
12 changes: 7 additions & 5 deletions crates/ty_server/tests/e2e/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,23 +742,26 @@ impl TestServer {
}

pub(crate) fn file_uri(&self, path: impl AsRef<SystemPath>) -> Url {
Url::from_file_path(self.test_context.root().join(path.as_ref()).as_std_path())
.expect("Path must be a valid URL")
Url::from_file_path(self.file_path(path).as_std_path()).expect("Path must be a valid URL")
}

pub(crate) fn file_path(&self, path: impl AsRef<SystemPath>) -> SystemPathBuf {
self.test_context.root().join(path)
}

/// Send a `textDocument/didOpen` notification
pub(crate) fn open_text_document(
&mut self,
path: impl AsRef<SystemPath>,
content: &impl ToString,
content: impl AsRef<str>,
version: i32,
) {
let params = DidOpenTextDocumentParams {
text_document: TextDocumentItem {
uri: self.file_uri(path),
language_id: "python".to_string(),
version,
text: content.to_string(),
text: content.as_ref().to_string(),
},
};
self.send_notification::<DidOpenTextDocument>(params);
Expand Down Expand Up @@ -793,7 +796,6 @@ impl TestServer {
}

/// Send a `workspace/didChangeWatchedFiles` notification with the given file events
#[expect(dead_code)]
pub(crate) fn did_change_watched_files(&mut self, events: Vec<FileEvent>) {
let params = DidChangeWatchedFilesParams { changes: events };
self.send_notification::<DidChangeWatchedFiles>(params);
Expand Down
86 changes: 84 additions & 2 deletions crates/ty_server/tests/e2e/publish_diagnostics.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::time::Duration;

use anyhow::Result;
use lsp_types::notification::PublishDiagnostics;
use lsp_types::{FileChangeType, FileEvent, notification::PublishDiagnostics};
use ruff_db::system::SystemPath;

use crate::TestServerBuilder;
Expand All @@ -20,10 +22,90 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let diagnostics = server.await_notification::<PublishDiagnostics>();

insta::assert_debug_snapshot!(diagnostics);

Ok(())
}

#[test]
fn on_did_change_watched_files() -> Result<()> {
let workspace_root = SystemPath::new("src");
let foo = SystemPath::new("src/foo.py");
let foo_content = "\
def foo() -> str:
print(a)
";

let mut server = TestServerBuilder::new()?
.with_workspace(workspace_root, None)?
.with_file(foo, "")?
.enable_pull_diagnostics(false)
.build()
.wait_until_workspaces_are_initialized();

let foo = server.file_path(foo);

server.open_text_document(&foo, "", 1);

let _open_diagnostics = server.await_notification::<PublishDiagnostics>();

std::fs::write(&foo, foo_content)?;

server.did_change_watched_files(vec![FileEvent {
uri: server.file_uri(foo),
typ: FileChangeType::CHANGED,
}]);

let diagnostics = server.await_notification::<PublishDiagnostics>();

// Note how ty reports no diagnostics here. This is because
// the contents received by didOpen/didChange take precedence over the file
// content on disk. Or, more specifically, because the revision
// of the file is not bumped, because it still uses the version
// from the `didOpen` notification but we don't have any notification
// that we can use here.
insta::assert_json_snapshot!(diagnostics);

Ok(())
}

#[test]
fn on_did_change_watched_files_pull_diagnostics() -> Result<()> {
let workspace_root = SystemPath::new("src");
let foo = SystemPath::new("src/foo.py");
let foo_content = "\
def foo() -> str:
print(a)
";

let mut server = TestServerBuilder::new()?
.with_workspace(workspace_root, None)?
.with_file(foo, "")?
.enable_pull_diagnostics(true)
.build()
.wait_until_workspaces_are_initialized();

let foo = server.file_path(foo);

server.open_text_document(&foo, "", 1);

std::fs::write(&foo, foo_content)?;

server.did_change_watched_files(vec![FileEvent {
uri: server.file_uri(foo),
typ: FileChangeType::CHANGED,
}]);

let diagnostics =
server.try_await_notification::<PublishDiagnostics>(Some(Duration::from_millis(100)));

assert!(
diagnostics.is_err(),
"Server should not send a publish diagnostic notification if the client supports pull diagnostics"
);

Ok(())
}
26 changes: 13 additions & 13 deletions crates/ty_server/tests/e2e/pull_diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);
let diagnostics = server.document_diagnostic_request(foo, None);

assert_debug_snapshot!(diagnostics);
Expand All @@ -57,7 +57,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content, 1);
server.open_text_document(foo, foo_content, 1);

// First request with no previous result ID
let first_response = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -113,7 +113,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(foo, &foo_content_v1, 1);
server.open_text_document(foo, foo_content_v1, 1);

// First request with no previous result ID
let first_response = server.document_diagnostic_request(foo, None);
Expand Down Expand Up @@ -233,7 +233,7 @@ def foo() -> str:
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(file_a, &file_a_content, 1);
server.open_text_document(file_a, file_a_content, 1);

// First request with no previous result IDs
let mut first_response = server
Expand All @@ -250,10 +250,10 @@ def foo() -> str:

// Make changes to files B, C, D, and E (leave A unchanged)
// Need to open files before changing them
server.open_text_document(file_b, &file_b_content_v1, 1);
server.open_text_document(file_c, &file_c_content_v1, 1);
server.open_text_document(file_d, &file_d_content_v1, 1);
server.open_text_document(file_e, &file_e_content_v1, 1);
server.open_text_document(file_b, file_b_content_v1, 1);
server.open_text_document(file_c, file_c_content_v1, 1);
server.open_text_document(file_d, file_d_content_v1, 1);
server.open_text_document(file_e, file_e_content_v1, 1);

// File B: Add a new error
server.change_text_document(
Expand Down Expand Up @@ -536,9 +536,9 @@ fn workspace_diagnostic_streaming_with_caching() -> Result<()> {
.build()
.wait_until_workspaces_are_initialized();

server.open_text_document(SystemPath::new("src/error_0.py"), &error_content, 1);
server.open_text_document(SystemPath::new("src/error_1.py"), &error_content, 1);
server.open_text_document(SystemPath::new("src/error_2.py"), &error_content, 1);
server.open_text_document(SystemPath::new("src/error_0.py"), error_content, 1);
server.open_text_document(SystemPath::new("src/error_1.py"), error_content, 1);
server.open_text_document(SystemPath::new("src/error_2.py"), error_content, 1);

// First request to get result IDs (non-streaming for simplicity)
let first_response = server.workspace_diagnostic_request(None, None);
Expand Down Expand Up @@ -716,7 +716,7 @@ def hello() -> str:
create_workspace_server_with_file(workspace_root, file_path, file_content_no_error)?;

// Open the file first
server.open_text_document(file_path, &file_content_no_error, 1);
server.open_text_document(file_path, file_content_no_error, 1);

// Make a workspace diagnostic request to a project with one file but no diagnostics
// This should trigger long-polling since the project has no diagnostics
Expand Down Expand Up @@ -819,7 +819,7 @@ def hello() -> str:
create_workspace_server_with_file(workspace_root, file_path, file_content_no_error)?;

// Open the file first
server.open_text_document(file_path, &file_content_no_error, 1);
server.open_text_document(file_path, file_content_no_error, 1);

// PHASE 1: Initial suspend (no diagnostics)
let request_id_1 = send_workspace_diagnostic_request(&mut server);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: crates/ty_server/tests/e2e/publish_diagnostics.rs
expression: diagnostics
---
{
"uri": "file://<temp_dir>/src/foo.py",
"diagnostics": [],
"version": 1
}