Skip to content

Commit

Permalink
feat: introduces formatting to wdl-lsp
Browse files Browse the repository at this point in the history
  • Loading branch information
claymcleod committed Nov 4, 2024
1 parent 263e095 commit 1897ba3
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 5 deletions.
9 changes: 5 additions & 4 deletions wdl-analysis/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* Added functions for getting type information of task requirements and hints (#[241](https://github.com/stjude-rust-labs/wdl/pull/241)).
* Exposed information about workflow calls from an analyzed document (#[239](https://github.com/stjude-rust-labs/wdl/pull/239)).
* Added functions for getting type information of task requirements and hints ([#241](https://github.com/stjude-rust-labs/wdl/pull/241)).
* Exposed information about workflow calls from an analyzed document ([#239](https://github.com/stjude-rust-labs/wdl/pull/239)).
* Added formatting to the analyzer ([#247](https://github.com/stjude-rust-labs/wdl/pull/247)).

## 0.5.0 - 10-22-2024

### Changed

* Refactored the `DocumentScope` API to simply `Document` and exposed more
information about tasks and workflows such as their inputs and outputs (#[232](https://github.com/stjude-rust-labs/wdl/pull/232)).
information about tasks and workflows such as their inputs and outputs ([#232](https://github.com/stjude-rust-labs/wdl/pull/232)).
* Switched to `rustls-tls` for TLS implementation rather than relying on
OpenSSL for Linux builds (#[228](https://github.com/stjude-rust-labs/wdl/pull/228)).
OpenSSL for Linux builds ([#228](https://github.com/stjude-rust-labs/wdl/pull/228)).

## 0.4.0 - 10-16-2024

Expand Down
2 changes: 2 additions & 0 deletions wdl-analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ documentation = "https://docs.rs/wdl-analysis"

[dependencies]
wdl-ast = { path = "../wdl-ast", version = "0.9.0" }
wdl-format = { path = "../wdl-format", version = "0.3.0" }

anyhow = { workspace = true }
rowan = { workspace = true }
url = { workspace = true }
Expand Down
18 changes: 18 additions & 0 deletions wdl-analysis/src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use crate::graph::ParseState;
use crate::queue::AddRequest;
use crate::queue::AnalysisQueue;
use crate::queue::AnalyzeRequest;
use crate::queue::FormatRequest;
use crate::queue::NotifyChangeRequest;
use crate::queue::NotifyIncrementalChangeRequest;
use crate::queue::RemoveRequest;
Expand Down Expand Up @@ -724,6 +725,23 @@ where
anyhow!("failed to receive response from analysis queue because the channel has closed")
})?
}

/// Formats a document.
pub async fn format_document(&self, document: Url) -> Result<Option<(u32, u32, String)>> {
let (tx, rx) = oneshot::channel();
self.sender
.send(Request::Format(FormatRequest {
document,
completed: tx,
}))
.map_err(|_| {
anyhow!("failed to send format request to the queue because the channel has closed")
})?;

rx.await.map_err(|_| {
anyhow!("failed to send format request to the queue because the channel has closed")
})
}
}

impl<C> Drop for Analyzer<C> {
Expand Down
71 changes: 71 additions & 0 deletions wdl-analysis/src/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ use tracing::info;
use url::Url;
use wdl_ast::Ast;
use wdl_ast::AstToken;
use wdl_ast::Node;
use wdl_ast::Severity;
use wdl_format::Formatter;
use wdl_format::element::node::AstNodeFormatExt as _;

use crate::AnalysisResult;
use crate::DiagnosticsConfig;
Expand Down Expand Up @@ -51,6 +55,8 @@ pub enum Request<Context> {
NotifyIncrementalChange(NotifyIncrementalChangeRequest),
/// A request to process a document's change.
NotifyChange(NotifyChangeRequest),
/// A request to format a document.
Format(FormatRequest),
}

/// Represents a request to add documents to the graph.
Expand Down Expand Up @@ -97,6 +103,20 @@ pub struct NotifyChangeRequest {
pub discard_pending: bool,
}

/// Represents a request to format a document.
pub struct FormatRequest {
/// The document to be formatted.
pub document: Url,
/// The sender for completing the request.
///
/// The return type is an option format result, meaning (in order):
///
/// * The line of the last character in the document,
/// * The column of the last character in the document, and
/// * The formatted document to replace the entire file with.
pub completed: oneshot::Sender<Option<(u32, u32, String)>>,
}

/// A simple enumeration to signal a cancellation to the caller.
enum Cancelable<T> {
/// The operation completed and yielded a value.
Expand Down Expand Up @@ -239,6 +259,57 @@ where
graph.get_mut(node).notify_change(discard_pending);
}
}
Request::Format(FormatRequest {
document,
completed,
}) => {
let graph = self.graph.read();

let result = graph
.get_index(&document)
.and_then(|index| {
graph.get(index).document().and_then(|document| {
match graph.get(index).parse_state() {
// NOTE: if we haven't parsed the document yet, then
// we don't have the line lengths of the document,
// so we can't proceed with formatting and we should
// just silently return.
ParseState::NotParsed | ParseState::Error(_) => None,
ParseState::Parsed {
version: _,
root: _,
lines,
diagnostics,
} => {
// If there are any diagnostics that are
// errors, we shouldn't attempt to format the
// document.
if diagnostics.as_ref().iter().any(|diagnositic| {
diagnositic.severity() == Severity::Error
}) {
return None;
}

let line_col = lines.line_col(lines.len());
Some((line_col.line, line_col.col, document))
}
}
})
})
.and_then(|(line, col, document)| {
document.ast().into_v1().and_then(|ast| {
let formatter = Formatter::default();
let element = Node::Ast(ast).into_format_element();

formatter
.format(&element)
.ok()
.map(|formatted| (line, col, formatted))
})
});

completed.send(result).ok();
}
}
}

Expand Down
4 changes: 4 additions & 0 deletions wdl-lsp/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## 0.5.0 - 10-22-2024

### Added

* Added formatting to the LSP ([#247](https://github.com/stjude-rust-labs/wdl/pull/247)).

### Fixed

* Fixed an issue on Windows where request URIs were not being normalized ([#231](https://github.com/stjude-rust-labs/wdl/pull/231)).
Expand Down
4 changes: 3 additions & 1 deletion wdl-lsp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ description = "Language Server Protocol implementation for WDL"
documentation = "https://docs.rs/wdl-lsp"

[dependencies]
wdl-analysis = { path = "../wdl-analysis", version = "0.5.0" }
wdl-ast = { path = "../wdl-ast", version = "0.9.0" }
wdl-format = { path = "../wdl-format", version = "0.3.0" }
wdl-lint = { path = "../wdl-lint", version = "0.8.0" }
wdl-analysis = { path = "../wdl-analysis", version = "0.5.0" }

anyhow = { workspace = true }
tokio = { workspace = true }
tower-lsp = { workspace = true }
Expand Down
39 changes: 39 additions & 0 deletions wdl-lsp/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ impl LanguageServer for Server {
..Default::default()
},
)),
document_formatting_provider: Some(OneOf::Left(true)),
..Default::default()
},
server_info: Some(ServerInfo {
Expand Down Expand Up @@ -669,4 +670,42 @@ impl LanguageServer for Server {
}
}
}

async fn formatting(
&self,
mut params: DocumentFormattingParams,
) -> RpcResult<Option<Vec<TextEdit>>> {
normalize_uri_path(&mut params.text_document.uri);

debug!("received `textDocument/formatting` request: {params:#?}");

let result = self
.analyzer
.format_document(params.text_document.uri)
.await
.map_err(|e| RpcError {
code: ErrorCode::InternalError,
message: e.to_string().into(),
data: None,
})?
.map(|(end_line, end_col, formatted)| {
vec![TextEdit {
range: Range {
// NOTE: always replace the full set of text starting at the
// very first position.
start: Position {
line: 0,
character: 0,
},
end: Position {
line: end_line,
character: end_col,
},
},
new_text: formatted,
}]
});

Ok(result)
}
}

0 comments on commit 1897ba3

Please sign in to comment.