mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-17 02:53:01 +00:00
ruff server: Add support for documents not exist on disk (#11588)
Co-authored-by: T-256 <Tester@test.com> Co-authored-by: Micha Reiser <micha@reiser.io>
This commit is contained in:
parent
685d11a909
commit
5b500fc4dc
20 changed files with 380 additions and 366 deletions
|
|
@ -122,7 +122,7 @@ fn background_request_task<'a, R: traits::BackgroundDocumentRequestHandler>(
|
|||
let (id, params) = cast_request::<R>(req)?;
|
||||
Ok(Task::background(schedule, move |session: &Session| {
|
||||
// TODO(jane): we should log an error if we can't take a snapshot.
|
||||
let Some(snapshot) = session.take_snapshot(&R::document_url(¶ms)) else {
|
||||
let Some(snapshot) = session.take_snapshot(R::document_url(¶ms).into_owned()) else {
|
||||
return Box::new(|_, _| {});
|
||||
};
|
||||
Box::new(move |notifier, responder| {
|
||||
|
|
@ -152,7 +152,7 @@ fn background_notification_thread<'a, N: traits::BackgroundDocumentNotificationH
|
|||
let (id, params) = cast_notification::<N>(req)?;
|
||||
Ok(Task::background(schedule, move |session: &Session| {
|
||||
// TODO(jane): we should log an error if we can't take a snapshot.
|
||||
let Some(snapshot) = session.take_snapshot(&N::document_url(¶ms)) else {
|
||||
let Some(snapshot) = session.take_snapshot(N::document_url(¶ms).into_owned()) else {
|
||||
return Box::new(|_, _| {});
|
||||
};
|
||||
Box::new(move |notifier, _| {
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ impl super::SyncNotificationHandler for DidChange {
|
|||
content_changes,
|
||||
}: types::DidChangeTextDocumentParams,
|
||||
) -> Result<()> {
|
||||
let key = session
|
||||
.key_from_url(&uri)
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
let key = session.key_from_url(uri);
|
||||
|
||||
session
|
||||
.update_text_document(&key, content_changes, new_version)
|
||||
|
|
@ -37,7 +35,7 @@ impl super::SyncNotificationHandler for DidChange {
|
|||
|
||||
// Publish diagnostics if the client doesnt support pull diagnostics
|
||||
if !session.resolved_client_capabilities().pull_diagnostics {
|
||||
let snapshot = session.take_snapshot(&uri).unwrap();
|
||||
let snapshot = session.take_snapshot(key.into_url()).unwrap();
|
||||
publish_diagnostics_for_document(&snapshot, ¬ifier)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,16 +23,14 @@ impl super::SyncNotificationHandler for DidChangeNotebook {
|
|||
change: types::NotebookDocumentChangeEvent { cells, metadata },
|
||||
}: types::DidChangeNotebookDocumentParams,
|
||||
) -> Result<()> {
|
||||
let key = session
|
||||
.key_from_url(&uri)
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
let key = session.key_from_url(uri);
|
||||
session
|
||||
.update_notebook_document(&key, cells, metadata, version)
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
|
||||
// publish new diagnostics
|
||||
let snapshot = session
|
||||
.take_snapshot(&uri)
|
||||
.take_snapshot(key.into_url())
|
||||
.expect("snapshot should be available");
|
||||
publish_diagnostics_for_document(&snapshot, ¬ifier)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ impl super::SyncNotificationHandler for DidChangeWatchedFiles {
|
|||
params: types::DidChangeWatchedFilesParams,
|
||||
) -> Result<()> {
|
||||
for change in ¶ms.changes {
|
||||
session.reload_settings(&change.uri.to_file_path().unwrap());
|
||||
session.reload_settings(&change.uri);
|
||||
}
|
||||
|
||||
if !params.changes.is_empty() {
|
||||
|
|
@ -33,7 +33,7 @@ impl super::SyncNotificationHandler for DidChangeWatchedFiles {
|
|||
// publish diagnostics for text documents
|
||||
for url in session.text_document_urls() {
|
||||
let snapshot = session
|
||||
.take_snapshot(&url)
|
||||
.take_snapshot(url.clone())
|
||||
.expect("snapshot should be available");
|
||||
publish_diagnostics_for_document(&snapshot, ¬ifier)?;
|
||||
}
|
||||
|
|
@ -42,7 +42,7 @@ impl super::SyncNotificationHandler for DidChangeWatchedFiles {
|
|||
// always publish diagnostics for notebook files (since they don't use pull diagnostics)
|
||||
for url in session.notebook_document_urls() {
|
||||
let snapshot = session
|
||||
.take_snapshot(&url)
|
||||
.take_snapshot(url.clone())
|
||||
.expect("snapshot should be available");
|
||||
publish_diagnostics_for_document(&snapshot, ¬ifier)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ use crate::server::api::LSPResult;
|
|||
use crate::server::client::{Notifier, Requester};
|
||||
use crate::server::Result;
|
||||
use crate::session::Session;
|
||||
use anyhow::anyhow;
|
||||
use lsp_server::ErrorCode;
|
||||
use lsp_types as types;
|
||||
use lsp_types::notification as notif;
|
||||
|
||||
|
|
@ -20,21 +18,14 @@ impl super::SyncNotificationHandler for DidChangeWorkspace {
|
|||
_requester: &mut Requester,
|
||||
params: types::DidChangeWorkspaceFoldersParams,
|
||||
) -> Result<()> {
|
||||
for types::WorkspaceFolder { ref uri, .. } in params.event.added {
|
||||
let workspace_path = uri
|
||||
.to_file_path()
|
||||
.map_err(|()| anyhow!("expected document URI {uri} to be a valid file path"))
|
||||
.with_failure_code(ErrorCode::InvalidParams)?;
|
||||
|
||||
session.open_workspace_folder(workspace_path);
|
||||
}
|
||||
for types::WorkspaceFolder { ref uri, .. } in params.event.removed {
|
||||
let workspace_path = uri
|
||||
.to_file_path()
|
||||
.map_err(|()| anyhow!("expected document URI {uri} to be a valid file path"))
|
||||
.with_failure_code(ErrorCode::InvalidParams)?;
|
||||
for types::WorkspaceFolder { uri, .. } in params.event.added {
|
||||
session
|
||||
.close_workspace_folder(&workspace_path)
|
||||
.open_workspace_folder(&uri)
|
||||
.with_failure_code(lsp_server::ErrorCode::InvalidParams)?;
|
||||
}
|
||||
for types::WorkspaceFolder { uri, .. } in params.event.removed {
|
||||
session
|
||||
.close_workspace_folder(&uri)
|
||||
.with_failure_code(lsp_server::ErrorCode::InvalidParams)?;
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ impl super::SyncNotificationHandler for DidClose {
|
|||
) -> Result<()> {
|
||||
// Publish an empty diagnostic report for the document. This will de-register any existing diagnostics.
|
||||
let snapshot = session
|
||||
.take_snapshot(&uri)
|
||||
.take_snapshot(uri.clone())
|
||||
.ok_or_else(|| anyhow::anyhow!("Unable to take snapshot for document with URL {uri}"))
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
clear_diagnostics_for_document(snapshot.query(), ¬ifier)?;
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ impl super::SyncNotificationHandler for DidCloseNotebook {
|
|||
..
|
||||
}: types::DidCloseNotebookDocumentParams,
|
||||
) -> Result<()> {
|
||||
let key = session
|
||||
.key_from_url(&uri)
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
let key = session.key_from_url(uri);
|
||||
|
||||
session
|
||||
.close_document(&key)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ use crate::server::client::{Notifier, Requester};
|
|||
use crate::server::Result;
|
||||
use crate::session::Session;
|
||||
use crate::TextDocument;
|
||||
use anyhow::anyhow;
|
||||
use lsp_server::ErrorCode;
|
||||
use lsp_types as types;
|
||||
use lsp_types::notification as notif;
|
||||
|
||||
|
|
@ -23,26 +21,18 @@ impl super::SyncNotificationHandler for DidOpen {
|
|||
types::DidOpenTextDocumentParams {
|
||||
text_document:
|
||||
types::TextDocumentItem {
|
||||
ref uri,
|
||||
text,
|
||||
version,
|
||||
..
|
||||
uri, text, version, ..
|
||||
},
|
||||
}: types::DidOpenTextDocumentParams,
|
||||
) -> Result<()> {
|
||||
let document_path: std::path::PathBuf = uri
|
||||
.to_file_path()
|
||||
.map_err(|()| anyhow!("expected document URI {uri} to be a valid file path"))
|
||||
.with_failure_code(ErrorCode::InvalidParams)?;
|
||||
|
||||
let document = TextDocument::new(text, version);
|
||||
|
||||
session.open_text_document(document_path, document);
|
||||
session.open_text_document(uri.clone(), document);
|
||||
|
||||
// Publish diagnostics if the client doesnt support pull diagnostics
|
||||
if !session.resolved_client_capabilities().pull_diagnostics {
|
||||
let snapshot = session
|
||||
.take_snapshot(uri)
|
||||
.take_snapshot(uri.clone())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!("Unable to take snapshot for document with URL {uri}")
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use crate::server::api::LSPResult;
|
|||
use crate::server::client::{Notifier, Requester};
|
||||
use crate::server::Result;
|
||||
use crate::session::Session;
|
||||
use anyhow::anyhow;
|
||||
use lsp_server::ErrorCode;
|
||||
use lsp_types as types;
|
||||
use lsp_types::notification as notif;
|
||||
|
|
@ -40,16 +39,11 @@ impl super::SyncNotificationHandler for DidOpenNotebook {
|
|||
)
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
|
||||
let notebook_path = uri
|
||||
.to_file_path()
|
||||
.map_err(|()| anyhow!("expected notebook URI {uri} to be a valid file path"))
|
||||
.with_failure_code(ErrorCode::InvalidParams)?;
|
||||
|
||||
session.open_notebook_document(notebook_path, notebook);
|
||||
session.open_notebook_document(uri.clone(), notebook);
|
||||
|
||||
// publish diagnostics
|
||||
let snapshot = session
|
||||
.take_snapshot(&uri)
|
||||
.take_snapshot(uri)
|
||||
.expect("snapshot should be available");
|
||||
publish_diagnostics_for_document(&snapshot, ¬ifier)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl super::SyncRequestHandler for ExecuteCommand {
|
|||
let mut edit_tracker = WorkspaceEditTracker::new(session.resolved_client_capabilities());
|
||||
for Argument { uri, version } in arguments {
|
||||
let snapshot = session
|
||||
.take_snapshot(&uri)
|
||||
.take_snapshot(uri.clone())
|
||||
.ok_or(anyhow::anyhow!("Document snapshot not available for {uri}",))
|
||||
.with_failure_code(ErrorCode::InternalError)?;
|
||||
match command {
|
||||
|
|
|
|||
|
|
@ -1,18 +1,14 @@
|
|||
use std::path::Path;
|
||||
|
||||
use lsp_types::{self as types, request as req};
|
||||
use types::TextEdit;
|
||||
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_source_file::LineIndex;
|
||||
use ruff_workspace::resolver::match_any_exclusion;
|
||||
use ruff_workspace::{FileResolverSettings, FormatterSettings};
|
||||
|
||||
use crate::edit::{Replacement, ToRangeExt};
|
||||
use crate::fix::Fixes;
|
||||
use crate::server::api::LSPResult;
|
||||
use crate::server::{client::Notifier, Result};
|
||||
use crate::session::DocumentSnapshot;
|
||||
use crate::session::{DocumentQuery, DocumentSnapshot};
|
||||
use crate::{PositionEncoding, TextDocument};
|
||||
|
||||
pub(crate) struct Format;
|
||||
|
|
@ -37,34 +33,25 @@ pub(super) fn format_full_document(snapshot: &DocumentSnapshot) -> Result<Fixes>
|
|||
let mut fixes = Fixes::default();
|
||||
let query = snapshot.query();
|
||||
|
||||
if let Some(notebook) = snapshot.query().as_notebook() {
|
||||
for (url, text_document) in notebook
|
||||
.urls()
|
||||
.map(|url| (url.clone(), notebook.cell_document_by_uri(url).unwrap()))
|
||||
{
|
||||
if let Some(changes) = format_text_document(
|
||||
text_document,
|
||||
query.source_type(),
|
||||
query.file_path(),
|
||||
query.settings().file_resolver(),
|
||||
query.settings().formatter(),
|
||||
snapshot.encoding(),
|
||||
true,
|
||||
)? {
|
||||
fixes.insert(url, changes);
|
||||
match snapshot.query() {
|
||||
DocumentQuery::Notebook { notebook, .. } => {
|
||||
for (url, text_document) in notebook
|
||||
.urls()
|
||||
.map(|url| (url.clone(), notebook.cell_document_by_uri(url).unwrap()))
|
||||
{
|
||||
if let Some(changes) =
|
||||
format_text_document(text_document, query, snapshot.encoding(), true)?
|
||||
{
|
||||
fixes.insert(url, changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Some(changes) = format_text_document(
|
||||
query.as_single_document().unwrap(),
|
||||
query.source_type(),
|
||||
query.file_path(),
|
||||
query.settings().file_resolver(),
|
||||
query.settings().formatter(),
|
||||
snapshot.encoding(),
|
||||
false,
|
||||
)? {
|
||||
fixes.insert(snapshot.query().make_key().into_url(), changes);
|
||||
DocumentQuery::Text { document, .. } => {
|
||||
if let Some(changes) =
|
||||
format_text_document(document, query, snapshot.encoding(), false)?
|
||||
{
|
||||
fixes.insert(snapshot.query().make_key().into_url(), changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,10 +68,7 @@ pub(super) fn format_document(snapshot: &DocumentSnapshot) -> Result<super::Form
|
|||
let query = snapshot.query();
|
||||
format_text_document(
|
||||
text_document,
|
||||
query.source_type(),
|
||||
query.file_path(),
|
||||
query.settings().file_resolver(),
|
||||
query.settings().formatter(),
|
||||
query,
|
||||
snapshot.encoding(),
|
||||
query.as_notebook().is_some(),
|
||||
)
|
||||
|
|
@ -92,28 +76,31 @@ pub(super) fn format_document(snapshot: &DocumentSnapshot) -> Result<super::Form
|
|||
|
||||
fn format_text_document(
|
||||
text_document: &TextDocument,
|
||||
source_type: PySourceType,
|
||||
file_path: &Path,
|
||||
file_resolver_settings: &FileResolverSettings,
|
||||
formatter_settings: &FormatterSettings,
|
||||
query: &DocumentQuery,
|
||||
encoding: PositionEncoding,
|
||||
is_notebook: bool,
|
||||
) -> Result<super::FormatResponse> {
|
||||
let file_resolver_settings = query.settings().file_resolver();
|
||||
let formatter_settings = query.settings().formatter();
|
||||
|
||||
// If the document is excluded, return early.
|
||||
if let Some(exclusion) = match_any_exclusion(
|
||||
file_path,
|
||||
&file_resolver_settings.exclude,
|
||||
&file_resolver_settings.extend_exclude,
|
||||
None,
|
||||
Some(&formatter_settings.exclude),
|
||||
) {
|
||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
||||
return Ok(None);
|
||||
if let Some(file_path) = query.file_path() {
|
||||
if let Some(exclusion) = match_any_exclusion(
|
||||
&file_path,
|
||||
&file_resolver_settings.exclude,
|
||||
&file_resolver_settings.extend_exclude,
|
||||
None,
|
||||
Some(&formatter_settings.exclude),
|
||||
) {
|
||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
let source = text_document.contents();
|
||||
let mut formatted = crate::format::format(text_document, source_type, formatter_settings)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
let mut formatted =
|
||||
crate::format::format(text_document, query.source_type(), formatter_settings)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
// fast path - if the code is the same, return early
|
||||
if formatted == source {
|
||||
return Ok(None);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,11 @@
|
|||
use std::path::Path;
|
||||
|
||||
use lsp_types::{self as types, request as req, Range};
|
||||
|
||||
use ruff_python_ast::PySourceType;
|
||||
use ruff_workspace::resolver::match_any_exclusion;
|
||||
use ruff_workspace::{FileResolverSettings, FormatterSettings};
|
||||
|
||||
use crate::edit::{RangeExt, ToRangeExt};
|
||||
use crate::server::api::LSPResult;
|
||||
use crate::server::{client::Notifier, Result};
|
||||
use crate::session::DocumentSnapshot;
|
||||
use crate::session::{DocumentQuery, DocumentSnapshot};
|
||||
use crate::{PositionEncoding, TextDocument};
|
||||
|
||||
pub(crate) struct FormatRange;
|
||||
|
|
@ -39,45 +35,43 @@ fn format_document_range(
|
|||
.as_single_document()
|
||||
.expect("format should only be called on text documents or notebook cells");
|
||||
let query = snapshot.query();
|
||||
format_text_document_range(
|
||||
text_document,
|
||||
range,
|
||||
query.source_type(),
|
||||
query.file_path(),
|
||||
query.settings().file_resolver(),
|
||||
query.settings().formatter(),
|
||||
snapshot.encoding(),
|
||||
)
|
||||
format_text_document_range(text_document, range, query, snapshot.encoding())
|
||||
}
|
||||
|
||||
/// Formats the specified [`Range`] in the [`TextDocument`].
|
||||
fn format_text_document_range(
|
||||
text_document: &TextDocument,
|
||||
range: Range,
|
||||
source_type: PySourceType,
|
||||
file_path: &Path,
|
||||
file_resolver_settings: &FileResolverSettings,
|
||||
formatter_settings: &FormatterSettings,
|
||||
query: &DocumentQuery,
|
||||
encoding: PositionEncoding,
|
||||
) -> Result<super::FormatResponse> {
|
||||
let file_resolver_settings = query.settings().file_resolver();
|
||||
let formatter_settings = query.settings().formatter();
|
||||
|
||||
// If the document is excluded, return early.
|
||||
if let Some(exclusion) = match_any_exclusion(
|
||||
file_path,
|
||||
&file_resolver_settings.exclude,
|
||||
&file_resolver_settings.extend_exclude,
|
||||
None,
|
||||
Some(&formatter_settings.exclude),
|
||||
) {
|
||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
||||
return Ok(None);
|
||||
if let Some(file_path) = query.file_path() {
|
||||
if let Some(exclusion) = match_any_exclusion(
|
||||
&file_path,
|
||||
&file_resolver_settings.exclude,
|
||||
&file_resolver_settings.extend_exclude,
|
||||
None,
|
||||
Some(&formatter_settings.exclude),
|
||||
) {
|
||||
tracing::debug!("Ignored path via `{}`: {}", exclusion, file_path.display());
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
let text = text_document.contents();
|
||||
let index = text_document.index();
|
||||
let range = range.to_text_range(text, index, encoding);
|
||||
let formatted_range =
|
||||
crate::format::format_range(text_document, source_type, formatter_settings, range)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
let formatted_range = crate::format::format_range(
|
||||
text_document,
|
||||
query.source_type(),
|
||||
formatter_settings,
|
||||
range,
|
||||
)
|
||||
.with_failure_code(lsp_server::ErrorCode::InternalError)?;
|
||||
|
||||
Ok(Some(vec![types::TextEdit {
|
||||
range: formatted_range
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue