Skip to content

Include document specific debug info #16215

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

Merged
merged 3 commits into from
Feb 18, 2025
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
95 changes: 80 additions & 15 deletions crates/ruff_server/src/server/api/requests/execute_command.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::fmt::Write;
use std::str::FromStr;

use crate::edit::WorkspaceEditTracker;
use crate::server::api::LSPResult;
use crate::server::schedule::Task;
use crate::server::{client, SupportedCommand};
use crate::session::Session;
use crate::DIAGNOSTIC_NAME;
use crate::{edit::DocumentVersion, server};
use crate::{DocumentKey, DIAGNOSTIC_NAME};
use lsp_server::ErrorCode;
use lsp_types::{self as types, request as req};
use lsp_types::{self as types, request as req, TextDocumentIdentifier};
use serde::Deserialize;

pub(crate) struct ExecuteCommand;
Expand All @@ -19,6 +20,17 @@ struct Argument {
version: DocumentVersion,
}

/// The argument schema for the `ruff.printDebugInformation` command.
#[derive(Default, Deserialize)]
#[serde(rename_all = "camelCase")]
struct DebugCommandArgument {
/// The URI of the document to print debug information for.
///
/// When provided, both document-specific debug information and global information are printed.
/// If not provided ([None]), only global debug information is printed.
text_document: Option<TextDocumentIdentifier>,
}

impl super::RequestHandler for ExecuteCommand {
type RequestType = req::ExecuteCommand;
}
Expand All @@ -34,7 +46,12 @@ impl super::SyncRequestHandler for ExecuteCommand {
.with_failure_code(ErrorCode::InvalidParams)?;

if command == SupportedCommand::Debug {
let output = debug_information(session);
let argument: DebugCommandArgument = params.arguments.into_iter().next().map_or_else(
|| Ok(DebugCommandArgument::default()),
|value| serde_json::from_value(value).with_failure_code(ErrorCode::InvalidParams),
)?;
let output = debug_information(session, argument.text_document)
.with_failure_code(ErrorCode::InternalError)?;
notifier
.notify::<types::notification::LogMessage>(types::LogMessageParams {
message: output.clone(),
Expand Down Expand Up @@ -134,23 +151,71 @@ fn apply_edit(
)
}

fn debug_information(session: &Session) -> String {
/// Returns a string with debug information about the session and the document at the given URI.
fn debug_information(
session: &Session,
text_document: Option<TextDocumentIdentifier>,
) -> crate::Result<String> {
let executable = std::env::current_exe()
.map(|path| format!("{}", path.display()))
.unwrap_or_else(|_| "<unavailable>".to_string());
format!(
"executable = {executable}

let mut buffer = String::new();

writeln!(
buffer,
"Global:
executable = {executable}
version = {version}
encoding = {encoding:?}
open_document_count = {doc_count}
active_workspace_count = {workspace_count}
configuration_files = {config_files:?}
{client_capabilities}",
position_encoding = {encoding:?}
workspace_root_folders = {workspace_folders:#?}
indexed_configuration_files = {config_files:#?}
open_documents_len = {open_documents_len}
client_capabilities = {client_capabilities:#?}
",
version = crate::version(),
encoding = session.encoding(),
workspace_folders = session.workspace_root_folders().collect::<Vec<_>>(),
config_files = session.config_file_paths().collect::<Vec<_>>(),
open_documents_len = session.open_documents_len(),
client_capabilities = session.resolved_client_capabilities(),
doc_count = session.num_documents(),
workspace_count = session.num_workspaces(),
config_files = session.list_config_files()
)
)?;

if let Some(TextDocumentIdentifier { uri }) = text_document {
let Some(snapshot) = session.take_snapshot(uri.clone()) else {
writeln!(buffer, "Unable to take a snapshot of the document at {uri}")?;
return Ok(buffer);
};
let query = snapshot.query();

writeln!(
buffer,
"Open document:
uri = {uri}
kind = {kind}
version = {version}
client_settings = {client_settings:#?}
config_path = {config_path:?}
{settings}
",
uri = uri.clone(),
kind = match session.key_from_url(uri) {
DocumentKey::Notebook(_) => "Notebook",
DocumentKey::NotebookCell(_) => "NotebookCell",
DocumentKey::Text(_) => "Text",
},
version = query.version(),
client_settings = snapshot.client_settings(),
config_path = query.settings().path(),
settings = query.settings(),
)?;
} else {
writeln!(
buffer,
"global_client_settings = {:#?}",
session.global_client_settings()
)?;
}

Ok(buffer)
}
30 changes: 20 additions & 10 deletions crates/ruff_server/src/session.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Data model, state management, and configuration resolution.

use std::path::Path;
use std::sync::Arc;

use lsp_types::{ClientCapabilities, FileEvent, NotebookDocumentCellChange, Url};
use settings::ResolvedClientSettings;

use crate::edit::{DocumentKey, DocumentVersion, NotebookDocument};
use crate::server::Workspaces;
Expand Down Expand Up @@ -147,24 +149,32 @@ impl Session {
Ok(())
}

pub(crate) fn num_documents(&self) -> usize {
self.index.num_documents()
pub(crate) fn resolved_client_capabilities(&self) -> &ResolvedClientCapabilities {
&self.resolved_client_capabilities
}

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

pub(crate) fn list_config_files(&self) -> Vec<&std::path::Path> {
self.index.list_config_files()
/// Returns an iterator over the paths to the configuration files in the index.
pub(crate) fn config_file_paths(&self) -> impl Iterator<Item = &Path> {
self.index.config_file_paths()
}

pub(crate) fn resolved_client_capabilities(&self) -> &ResolvedClientCapabilities {
&self.resolved_client_capabilities
/// Returns the resolved global client settings.
pub(crate) fn global_client_settings(&self) -> ResolvedClientSettings {
ResolvedClientSettings::global(&self.global_settings)
}

pub(crate) fn encoding(&self) -> PositionEncoding {
self.position_encoding
/// Returns the number of open documents in the session.
pub(crate) fn open_documents_len(&self) -> usize {
self.index.open_documents_len()
}

/// Returns an iterator over the workspace root folders in the session.
pub(crate) fn workspace_root_folders(&self) -> impl Iterator<Item = &Path> {
self.index.workspace_root_folders()
}
}

Expand Down
32 changes: 17 additions & 15 deletions crates/ruff_server/src/session/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,21 +177,6 @@ impl Index {
.register_workspace(&Workspace::new(url), global_settings)
}

pub(super) fn num_documents(&self) -> usize {
self.documents.len()
}

pub(super) fn num_workspaces(&self) -> usize {
self.settings.len()
}

pub(super) fn list_config_files(&self) -> Vec<&Path> {
self.settings
.values()
.flat_map(|WorkspaceSettings { ruff_settings, .. }| ruff_settings.list_files())
.collect()
}

pub(super) fn close_workspace_folder(&mut self, workspace_url: &Url) -> crate::Result<()> {
let workspace_path = workspace_url.to_file_path().map_err(|()| {
anyhow!("Failed to convert workspace URL to file path: {workspace_url}")
Expand Down Expand Up @@ -404,6 +389,23 @@ impl Index {
.next_back()
.map(|(_, settings)| settings)
}

/// Returns an iterator over the workspace root folders contained in this index.
pub(super) fn workspace_root_folders(&self) -> impl Iterator<Item = &Path> {
self.settings.keys().map(PathBuf::as_path)
}

/// Returns the number of open documents.
pub(super) fn open_documents_len(&self) -> usize {
self.documents.len()
}

/// Returns an iterator over the paths to the configuration files in the index.
pub(super) fn config_file_paths(&self) -> impl Iterator<Item = &Path> {
self.settings
.values()
.flat_map(|WorkspaceSettings { ruff_settings, .. }| ruff_settings.config_file_paths())
}
}

/// Maps a workspace folder root to its settings.
Expand Down
18 changes: 13 additions & 5 deletions crates/ruff_server/src/session/index/ruff_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use ruff_workspace::{

use crate::session::settings::{ConfigurationPreference, ResolvedEditorSettings};

#[derive(Debug)]
pub struct RuffSettings {
/// The path to this configuration file, used for debugging.
/// The default fallback configuration does not have a file path.
Expand All @@ -28,6 +29,12 @@ pub struct RuffSettings {
settings: Settings,
}

impl RuffSettings {
pub(crate) fn path(&self) -> Option<&Path> {
self.path.as_deref()
}
}

impl Deref for RuffSettings {
type Target = Settings;

Expand Down Expand Up @@ -298,15 +305,16 @@ impl RuffSettingsIndex {
.clone()
}

pub(crate) fn list_files(&self) -> impl Iterator<Item = &Path> {
pub(super) fn fallback(&self) -> Arc<RuffSettings> {
self.fallback.clone()
}

/// Returns an iterator over the paths to the configuration files in the index.
pub(crate) fn config_file_paths(&self) -> impl Iterator<Item = &Path> {
self.index
.values()
.filter_map(|settings| settings.path.as_deref())
}

pub(super) fn fallback(&self) -> Arc<RuffSettings> {
self.fallback.clone()
}
}

struct EditorConfigurationTransformer<'a>(&'a ResolvedEditorSettings, &'a Path);
Expand Down
Loading