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
2 changes: 1 addition & 1 deletion crates/ty_server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::server::{ConnectionInitializer, Server};
use anyhow::Context;
pub use document::{NotebookDocument, PositionEncoding, TextDocument};
pub use session::{ClientSettings, DocumentQuery, DocumentSnapshot, Session};
pub use session::{DocumentQuery, DocumentSnapshot, Session};
use std::num::NonZeroUsize;

mod document;
Expand Down
50 changes: 32 additions & 18 deletions crates/ty_server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use self::schedule::spawn_main_loop;
use crate::PositionEncoding;
use crate::session::{AllSettings, ClientSettings, Session};
use crate::session::{AllOptions, ClientOptions, Session};
use lsp_server::Connection;
use lsp_types::{
ClientCapabilities, DiagnosticOptions, DiagnosticServerCapabilities, HoverProviderCapability,
Expand Down Expand Up @@ -42,10 +42,10 @@ impl Server {
) -> crate::Result<Self> {
let (id, init_params) = connection.initialize_start()?;

let AllSettings {
global_settings,
mut workspace_settings,
} = AllSettings::from_value(
let AllOptions {
global: global_options,
workspace: mut workspace_options,
} = AllOptions::from_value(
init_params
.initialization_options
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
Expand All @@ -68,34 +68,48 @@ impl Server {
let client = Client::new(main_loop_sender.clone(), connection.sender.clone());

crate::logging::init_logging(
global_settings.tracing.log_level.unwrap_or_default(),
global_settings.tracing.log_file.as_deref(),
global_options.tracing.log_level.unwrap_or_default(),
global_options.tracing.log_file.as_deref(),
);

let mut workspace_for_url = |url: Url| {
let Some(workspace_settings) = workspace_settings.as_mut() else {
return (url, ClientSettings::default());
let Some(workspace_settings) = workspace_options.as_mut() else {
return (url, ClientOptions::default());
};
let settings = workspace_settings.remove(&url).unwrap_or_else(|| {
tracing::warn!("No workspace settings found for {}", url);
ClientSettings::default()
tracing::warn!(
"No workspace options found for {}, using default options",
url
);
ClientOptions::default()
});
(url, settings)
};

let workspaces = init_params
.workspace_folders
.filter(|folders| !folders.is_empty())
.map(|folders| folders.into_iter().map(|folder| {
workspace_for_url(folder.uri)
}).collect())
.map(|folders| {
folders
.into_iter()
.map(|folder| workspace_for_url(folder.uri))
.collect()
})
.or_else(|| {
tracing::warn!("No workspace(s) were provided during initialization. Using the current working directory as a default workspace...");
let uri = Url::from_file_path(std::env::current_dir().ok()?).ok()?;
let current_dir = std::env::current_dir().ok()?;
tracing::warn!(
"No workspace(s) were provided during initialization. \
Using the current working directory as a default workspace: {}",
current_dir.display()
);
let uri = Url::from_file_path(current_dir).ok()?;
Some(vec![workspace_for_url(uri)])
})
.ok_or_else(|| {
anyhow::anyhow!("Failed to get the current working directory while creating a default workspace.")
anyhow::anyhow!(
"Failed to get the current working directory while creating a \
default workspace."
)
})?;

let workspaces = if workspaces.len() > 1 {
Expand All @@ -121,7 +135,7 @@ impl Server {
session: Session::new(
&client_capabilities,
position_encoding,
global_settings,
global_options,
&workspaces,
)?,
client_capabilities,
Expand Down
4 changes: 4 additions & 0 deletions crates/ty_server/src/server/api/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ impl BackgroundDocumentRequestHandler for CompletionRequestHandler {
_client: &Client,
params: CompletionParams,
) -> crate::server::Result<Option<CompletionResponse>> {
if snapshot.client_settings().is_language_services_disabled() {
return Ok(None);
}

Comment on lines +33 to +36
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried doing this centrally in background_request_task but that didn't work:

diff --git a/crates/ty_server/src/server/api.rs b/crates/ty_server/src/server/api.rs
index a9dd9da1d0..9899184fd4 100644
--- a/crates/ty_server/src/server/api.rs
+++ b/crates/ty_server/src/server/api.rs
@@ -143,7 +143,18 @@ fn background_request_task<'a, R: traits::BackgroundDocumentRequestHandler>(
 
         Box::new(move |notifier, responder| {
             let _span = tracing::trace_span!("request", %id, method = R::METHOD).entered();
-            let result = R::run_with_snapshot(&db, snapshot, notifier, params);
+            let result = if snapshot.client_settings().is_language_services_disabled()
+                && matches!(
+                    R::METHOD,
+                    request::GotoTypeDefinitionRequestHandler::METHOD
+                        | request::CompletionRequestHandler::METHOD
+                        | request::InlayHintRequestHandler::METHOD
+                        | request::HoverRequestHandler::METHOD
+                ) {
+                // Return an empty response here ...
+            } else {
+                R::run_with_snapshot(&db, snapshot, notifier, params)
+            };
             respond::<R>(id, result, &responder);
         })
     }))

But, then we'd need to somehow be able to get an "empty" response from those handlers which could be None or something else.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer having this in the specific handlers. It's less surprising, and someone copying an existing handler (which I always do as a starting point) is then also more likely to think about checking this option.

I guess, it's somewhat wasteful to still have the client send us requests but I suspect that unregistering the providers is more complicated and not supported by all clients?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess, it's somewhat wasteful to still have the client send us requests but I suspect that unregistering the providers is more complicated and not supported by all clients?

I think unregistering only works for capabilities that support dynamic registration and I'm not sure if all language capabilities supports that. And, this would also require that we go through the dynamic registration path (via registering each capabilities dynamically) to un-register the capability later as it requires the id that the server used to perform the registration.

let Some(file) = snapshot.file(db) else {
tracing::debug!("Failed to resolve file for {:?}", params);
return Ok(None);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl BackgroundDocumentRequestHandler for GotoTypeDefinitionRequestHandler {
_client: &Client,
params: GotoTypeDefinitionParams,
) -> crate::server::Result<Option<GotoDefinitionResponse>> {
if snapshot.client_settings().is_language_services_disabled() {
return Ok(None);
}

let Some(file) = snapshot.file(db) else {
tracing::debug!("Failed to resolve file for {:?}", params);
return Ok(None);
Expand Down
4 changes: 4 additions & 0 deletions crates/ty_server/src/server/api/requests/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ impl BackgroundDocumentRequestHandler for HoverRequestHandler {
_client: &Client,
params: HoverParams,
) -> crate::server::Result<Option<lsp_types::Hover>> {
if snapshot.client_settings().is_language_services_disabled() {
return Ok(None);
}

let Some(file) = snapshot.file(db) else {
tracing::debug!("Failed to resolve file for {:?}", params);
return Ok(None);
Expand Down
4 changes: 4 additions & 0 deletions crates/ty_server/src/server/api/requests/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ impl BackgroundDocumentRequestHandler for InlayHintRequestHandler {
_client: &Client,
params: InlayHintParams,
) -> crate::server::Result<Option<Vec<lsp_types::InlayHint>>> {
if snapshot.client_settings().is_language_services_disabled() {
return Ok(None);
}

let Some(file) = snapshot.file(db) else {
tracing::debug!("Failed to resolve file for {:?}", params);
return Ok(None);
Expand Down
21 changes: 15 additions & 6 deletions crates/ty_server/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,16 @@ use std::sync::Arc;

use anyhow::anyhow;
use lsp_types::{ClientCapabilities, TextDocumentContentChangeEvent, Url};
use options::GlobalOptions;
use ruff_db::Db;
use ruff_db::files::{File, system_path_to_file};
use ruff_db::system::SystemPath;
use ty_project::{ProjectDatabase, ProjectMetadata};

pub(crate) use self::capabilities::ResolvedClientCapabilities;
pub use self::index::DocumentQuery;
pub(crate) use self::settings::AllSettings;
pub use self::settings::ClientSettings;
pub(crate) use self::options::{AllOptions, ClientOptions};
use self::settings::ClientSettings;
use crate::document::{DocumentKey, DocumentVersion, NotebookDocument};
use crate::session::request_queue::RequestQueue;
use crate::system::{AnySystemPath, LSPSystem};
Expand All @@ -24,6 +25,7 @@ use crate::{PositionEncoding, TextDocument};
mod capabilities;
pub(crate) mod client;
pub(crate) mod index;
mod options;
mod request_queue;
mod settings;

Expand Down Expand Up @@ -58,12 +60,13 @@ impl Session {
pub(crate) fn new(
client_capabilities: &ClientCapabilities,
position_encoding: PositionEncoding,
global_settings: ClientSettings,
workspace_folders: &[(Url, ClientSettings)],
global_options: GlobalOptions,
workspace_folders: &[(Url, ClientOptions)],
) -> crate::Result<Self> {
let mut workspaces = BTreeMap::new();
let index = Arc::new(index::Index::new(global_settings));
let index = Arc::new(index::Index::new(global_options.into_settings()));

// TODO: Consider workspace settings
for (url, _) in workspace_folders {
let path = url
.to_file_path()
Expand Down Expand Up @@ -168,6 +171,7 @@ impl Session {
let key = self.key_from_url(url).ok()?;
Some(DocumentSnapshot {
resolved_client_capabilities: self.resolved_client_capabilities.clone(),
client_settings: self.index().global_settings(),
document_ref: self.index().make_document_ref(&key)?,
position_encoding: self.position_encoding,
})
Expand Down Expand Up @@ -303,6 +307,7 @@ impl Drop for MutIndexGuard<'_> {
#[derive(Debug)]
pub struct DocumentSnapshot {
resolved_client_capabilities: Arc<ResolvedClientCapabilities>,
client_settings: Arc<ClientSettings>,
document_ref: index::DocumentQuery,
position_encoding: PositionEncoding,
}
Expand All @@ -312,14 +317,18 @@ impl DocumentSnapshot {
&self.resolved_client_capabilities
}

pub fn query(&self) -> &index::DocumentQuery {
pub(crate) fn query(&self) -> &index::DocumentQuery {
&self.document_ref
}

pub(crate) fn encoding(&self) -> PositionEncoding {
self.position_encoding
}

pub(crate) fn client_settings(&self) -> &ClientSettings {
&self.client_settings
}

pub(crate) fn file(&self, db: &dyn Db) -> Option<File> {
match AnySystemPath::try_from_url(self.document_ref.file_url()).ok()? {
AnySystemPath::System(path) => system_path_to_file(db, path).ok(),
Expand Down
14 changes: 8 additions & 6 deletions crates/ty_server/src/session/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,15 @@ use std::sync::Arc;
use lsp_types::Url;
use rustc_hash::FxHashMap;

use crate::session::settings::ClientSettings;
use crate::{
PositionEncoding, TextDocument,
document::{DocumentKey, DocumentVersion, NotebookDocument},
system::AnySystemPath,
};

use super::ClientSettings;

/// Stores and tracks all open documents in a session, along with their associated settings.
#[derive(Default, Debug)]
#[derive(Debug)]
pub(crate) struct Index {
/// Maps all document file paths to the associated document controller
documents: FxHashMap<AnySystemPath, DocumentController>,
Expand All @@ -21,16 +20,15 @@ pub(crate) struct Index {
notebook_cells: FxHashMap<Url, AnySystemPath>,

/// Global settings provided by the client.
#[expect(dead_code)]
global_settings: ClientSettings,
global_settings: Arc<ClientSettings>,
}

impl Index {
pub(super) fn new(global_settings: ClientSettings) -> Self {
Self {
documents: FxHashMap::default(),
notebook_cells: FxHashMap::default(),
global_settings,
global_settings: Arc::new(global_settings),
}
}

Expand Down Expand Up @@ -177,6 +175,10 @@ impl Index {
Ok(())
}

pub(crate) fn global_settings(&self) -> Arc<ClientSettings> {
self.global_settings.clone()
}

fn document_controller_for_key(
&mut self,
key: &DocumentKey,
Expand Down
Loading
Loading