Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid indexing the workspace for single-file mode #13770

Merged
merged 3 commits into from
Oct 18, 2024
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/ruff_server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ rustc-hash = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
shellexpand = { workspace = true }
thiserror = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }

Expand Down
2 changes: 1 addition & 1 deletion crates/ruff_server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pub use edit::{DocumentKey, NotebookDocument, PositionEncoding, TextDocument};
use lsp_types::CodeActionKind;
pub use server::Server;
pub use server::{Server, Workspace, Workspaces};
pub use session::{ClientSettings, DocumentQuery, DocumentSnapshot, Session};

#[macro_use]
Expand Down
170 changes: 140 additions & 30 deletions crates/ruff_server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@

use lsp_server as lsp;
use lsp_types as types;
use lsp_types::InitializeParams;
use lsp_types::WorkspaceFolder;
use std::num::NonZeroUsize;
use std::ops::Deref;
use std::panic::PanicInfo;
use std::str::FromStr;
use thiserror::Error;
use types::ClientCapabilities;
use types::CodeActionKind;
use types::CodeActionOptions;
Expand All @@ -18,6 +22,7 @@ use types::OneOf;
use types::TextDocumentSyncCapability;
use types::TextDocumentSyncKind;
use types::TextDocumentSyncOptions;
use types::Url;
use types::WorkDoneProgressOptions;
use types::WorkspaceFoldersServerCapabilities;

Expand All @@ -29,6 +34,7 @@ use self::schedule::Task;
use crate::session::AllSettings;
use crate::session::ClientSettings;
use crate::session::Session;
use crate::session::WorkspaceSettingsMap;
use crate::PositionEncoding;

mod api;
Expand Down Expand Up @@ -71,17 +77,23 @@ impl Server {

crate::message::init_messenger(connection.make_sender());

let InitializeParams {
initialization_options,
workspace_folders,
client_info,
..
} = init_params;

let mut all_settings = AllSettings::from_value(
init_params
.initialization_options
initialization_options
.unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::default())),
);
if let Some(preview) = preview {
all_settings.set_preview(preview);
}
let AllSettings {
global_settings,
mut workspace_settings,
workspace_settings,
} = all_settings;

crate::trace::init_tracing(
Expand All @@ -91,34 +103,13 @@ impl Server {
.log_level
.unwrap_or(crate::trace::LogLevel::Info),
global_settings.tracing.log_file.as_deref(),
init_params.client_info.as_ref(),
client_info.as_ref(),
);

let mut workspace_for_url = |url: lsp_types::Url| {
let Some(workspace_settings) = workspace_settings.as_mut() else {
return (url, ClientSettings::default());
};
let settings = workspace_settings.remove(&url).unwrap_or_else(|| {
tracing::warn!("No workspace settings found for {}", url);
ClientSettings::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())
.or_else(|| {
tracing::warn!("No workspace(s) were provided during initialization. Using the current working directory as a default workspace...");
let uri = types::Url::from_file_path(std::env::current_dir().ok()?).ok()?;
Some(vec![workspace_for_url(uri)])
})
.ok_or_else(|| {
anyhow::anyhow!("Failed to get the current working directory while creating a default workspace.")
})?;
let workspaces = Workspaces::from_workspace_folders(
workspace_folders,
workspace_settings.unwrap_or_default(),
)?;

Ok(Self {
connection,
Expand All @@ -127,7 +118,7 @@ impl Server {
&client_capabilities,
position_encoding,
global_settings,
workspaces,
&workspaces,
)?,
client_capabilities,
})
Expand Down Expand Up @@ -462,3 +453,122 @@ impl FromStr for SupportedCommand {
})
}
}

#[derive(Debug)]
pub struct Workspaces(Vec<Workspace>);

impl Workspaces {
pub fn new(workspaces: Vec<Workspace>) -> Self {
Self(workspaces)
}

/// Create the workspaces from the provided workspace folders as provided by the client during
/// initialization.
fn from_workspace_folders(
workspace_folders: Option<Vec<WorkspaceFolder>>,
mut workspace_settings: WorkspaceSettingsMap,
) -> std::result::Result<Workspaces, WorkspacesError> {
let mut client_settings_for_url = |url: &Url| {
workspace_settings.remove(url).unwrap_or_else(|| {
tracing::info!(
"No workspace settings found for {}, using default settings",
url
);
ClientSettings::default()
})
};

let workspaces =
if let Some(folders) = workspace_folders.filter(|folders| !folders.is_empty()) {
folders
.into_iter()
.map(|folder| {
let settings = client_settings_for_url(&folder.uri);
Workspace::new(folder.uri).with_settings(settings)
})
.collect()
} else {
let current_dir = std::env::current_dir().map_err(WorkspacesError::Io)?;
tracing::info!(
"No workspace(s) were provided during initialization. \
Using the current working directory as a default workspace: {}",
current_dir.display()
);
Comment on lines +492 to +496
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've also changed this from warn to info. I don't think it's a warning i.e., not something that the user should be concerned of.

let uri = Url::from_file_path(current_dir)
.map_err(|()| WorkspacesError::InvalidCurrentDir)?;
let settings = client_settings_for_url(&uri);
vec![Workspace::default(uri).with_settings(settings)]
};

Ok(Workspaces(workspaces))
}
}

impl Deref for Workspaces {
type Target = [Workspace];

fn deref(&self) -> &Self::Target {
&self.0
}
}

#[derive(Error, Debug)]
enum WorkspacesError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("Failed to create a URL from the current working directory")]
InvalidCurrentDir,
}

#[derive(Debug)]
pub struct Workspace {
/// The [`Url`] pointing to the root of the workspace.
url: Url,
/// The client settings for this workspace.
settings: Option<ClientSettings>,
/// Whether this is the default workspace as created by the server. This will be the case when
/// no workspace folders were provided during initialization.
is_default: bool,
}

impl Workspace {
/// Create a new workspace with the given root URL.
pub fn new(url: Url) -> Self {
Self {
url,
settings: None,
is_default: false,
}
}

/// Create a new default workspace with the given root URL.
pub fn default(url: Url) -> Self {
Self {
url,
settings: None,
is_default: true,
}
}

/// Set the client settings for this workspace.
#[must_use]
pub fn with_settings(mut self, settings: ClientSettings) -> Self {
self.settings = Some(settings);
self
}

/// Returns the root URL of the workspace.
pub(crate) fn url(&self) -> &Url {
&self.url
}

/// Returns the client settings for this workspace.
pub(crate) fn settings(&self) -> Option<&ClientSettings> {
self.settings.as_ref()
}

/// Returns true if this is the default workspace.
pub(crate) fn is_default(&self) -> bool {
self.is_default
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl super::SyncNotificationHandler for DidChangeWorkspace {
) -> Result<()> {
for types::WorkspaceFolder { uri, .. } in params.event.added {
session
.open_workspace_folder(&uri)
.open_workspace_folder(uri)
.with_failure_code(lsp_server::ErrorCode::InvalidParams)?;
}
for types::WorkspaceFolder { uri, .. } in params.event.removed {
Expand Down
9 changes: 5 additions & 4 deletions crates/ruff_server/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ use std::sync::Arc;
use lsp_types::{ClientCapabilities, NotebookDocumentCellChange, Url};

use crate::edit::{DocumentKey, DocumentVersion, NotebookDocument};
use crate::server::Workspaces;
use crate::{PositionEncoding, TextDocument};

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::settings::{AllSettings, WorkspaceSettingsMap};

mod capabilities;
mod index;
Expand Down Expand Up @@ -42,11 +43,11 @@ impl Session {
client_capabilities: &ClientCapabilities,
position_encoding: PositionEncoding,
global_settings: ClientSettings,
workspace_folders: Vec<(Url, ClientSettings)>,
workspaces: &Workspaces,
) -> crate::Result<Self> {
Ok(Self {
position_encoding,
index: index::Index::new(workspace_folders, &global_settings)?,
index: index::Index::new(workspaces, &global_settings)?,
global_settings,
resolved_client_capabilities: Arc::new(ResolvedClientCapabilities::new(
client_capabilities,
Expand Down Expand Up @@ -136,7 +137,7 @@ impl Session {
}

/// Open a workspace folder at the given `url`.
pub(crate) fn open_workspace_folder(&mut self, url: &Url) -> crate::Result<()> {
pub(crate) fn open_workspace_folder(&mut self, url: Url) -> crate::Result<()> {
self.index.open_workspace_folder(url, &self.global_settings)
}

Expand Down
23 changes: 14 additions & 9 deletions crates/ruff_server/src/session/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use rustc_hash::FxHashMap;
pub(crate) use ruff_settings::RuffSettings;

use crate::edit::LanguageId;
use crate::server::{Workspace, Workspaces};
use crate::{
edit::{DocumentKey, DocumentVersion, NotebookDocument},
PositionEncoding, TextDocument,
Expand Down Expand Up @@ -67,12 +68,12 @@ pub enum DocumentQuery {

impl Index {
pub(super) fn new(
workspace_folders: Vec<(Url, ClientSettings)>,
workspaces: &Workspaces,
global_settings: &ClientSettings,
) -> crate::Result<Self> {
let mut settings = WorkspaceSettingsIndex::default();
for (url, workspace_settings) in workspace_folders {
settings.register_workspace(&url, Some(workspace_settings), global_settings)?;
for workspace in &**workspaces {
settings.register_workspace(workspace, global_settings)?;
}

Ok(Self {
Expand Down Expand Up @@ -167,11 +168,12 @@ impl Index {

pub(super) fn open_workspace_folder(
&mut self,
url: &Url,
url: Url,
global_settings: &ClientSettings,
) -> crate::Result<()> {
// TODO(jane): Find a way for workspace client settings to be added or changed dynamically.
self.settings.register_workspace(url, None, global_settings)
self.settings
.register_workspace(&Workspace::new(url), global_settings)
}

pub(super) fn num_documents(&self) -> usize {
Expand Down Expand Up @@ -284,6 +286,7 @@ impl Index {
settings.ruff_settings = ruff_settings::RuffSettingsIndex::new(
root,
settings.client_settings.editor_settings(),
false,
);
}
}
Expand Down Expand Up @@ -398,10 +401,10 @@ impl WorkspaceSettingsIndex {
/// workspace. Otherwise, the global settings are used exclusively.
fn register_workspace(
&mut self,
workspace_url: &Url,
workspace_settings: Option<ClientSettings>,
workspace: &Workspace,
global_settings: &ClientSettings,
) -> crate::Result<()> {
let workspace_url = workspace.url();
if workspace_url.scheme() != "file" {
tracing::info!("Ignoring non-file workspace URL: {workspace_url}");
show_warn_msg!("Ruff does not support non-file workspaces; Ignoring {workspace_url}");
Expand All @@ -411,17 +414,19 @@ impl WorkspaceSettingsIndex {
anyhow!("Failed to convert workspace URL to file path: {workspace_url}")
})?;

let client_settings = if let Some(workspace_settings) = workspace_settings {
ResolvedClientSettings::with_workspace(&workspace_settings, global_settings)
let client_settings = if let Some(workspace_settings) = workspace.settings() {
ResolvedClientSettings::with_workspace(workspace_settings, global_settings)
} else {
ResolvedClientSettings::global(global_settings)
};

let workspace_settings_index = ruff_settings::RuffSettingsIndex::new(
&workspace_path,
client_settings.editor_settings(),
workspace.is_default(),
);

tracing::info!("Registering workspace: {}", workspace_path.display());
self.insert(
workspace_path,
WorkspaceSettings {
Expand Down
Loading
Loading