Skip to content

Commit

Permalink
Include document specific debug info (#16215)
Browse files Browse the repository at this point in the history
## Summary

Related astral-sh/ruff-vscode#692.

## Test Plan

**When there's no active text document:**

```
[Info  - 10:57:03 PM] Global:
executable = /Users/dhruv/work/astral/ruff/target/debug/ruff
version = 0.9.6
position_encoding = UTF16
workspace_root_folders = [
    "/Users/dhruv/playground/ruff",
]
indexed_configuration_files = [
    "/Users/dhruv/playground/ruff/pyproject.toml",
    "/Users/dhruv/playground/ruff/formatter/ruff.toml",
]
open_documents = 0
client_capabilities = ResolvedClientCapabilities {
    code_action_deferred_edit_resolution: true,
    apply_edit: true,
    document_changes: true,
    workspace_refresh: true,
    pull_diagnostics: true,
}

global_client_settings = ResolvedClientSettings {
    fix_all: true,
    organize_imports: true,
    lint_enable: true,
    disable_rule_comment_enable: true,
    fix_violation_enable: true,
    show_syntax_errors: true,
    editor_settings: ResolvedEditorSettings {
        configuration: None,
        lint_preview: None,
        format_preview: None,
        select: None,
        extend_select: None,
        ignore: None,
        exclude: None,
        line_length: None,
        configuration_preference: EditorFirst,
    },
}
```

**When there's an active text document that's been passed as param:**

```
[Info  - 10:53:33 PM] Global:
executable = /Users/dhruv/work/astral/ruff/target/debug/ruff
version = 0.9.6
position_encoding = UTF16
workspace_root_folders = [
    "/Users/dhruv/playground/ruff",
]
indexed_configuration_files = [
    "/Users/dhruv/playground/ruff/pyproject.toml",
    "/Users/dhruv/playground/ruff/formatter/ruff.toml",
]
open_documents = 1
client_capabilities = ResolvedClientCapabilities {
    code_action_deferred_edit_resolution: true,
    apply_edit: true,
    document_changes: true,
    workspace_refresh: true,
    pull_diagnostics: true,
}

Document:
uri = file:///Users/dhruv/playground/ruff/lsp/play.py
kind = Text
version = 1
client_settings = ResolvedClientSettings {
    fix_all: true,
    organize_imports: true,
    lint_enable: true,
    disable_rule_comment_enable: true,
    fix_violation_enable: true,
    show_syntax_errors: true,
    editor_settings: ResolvedEditorSettings {
        configuration: None,
        lint_preview: None,
        format_preview: None,
        select: None,
        extend_select: None,
        ignore: None,
        exclude: None,
        line_length: None,
        configuration_preference: EditorFirst,
    },
}
config_path = Some("/Users/dhruv/playground/ruff/pyproject.toml")

...
```

Replace `...` at the end with the output of `ruff check --show-settings
path.py`
  • Loading branch information
dhruvmanila authored Feb 18, 2025
1 parent bb2a712 commit ed9c18d
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 45 deletions.
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

0 comments on commit ed9c18d

Please sign in to comment.