Skip to content

Commit a947eef

Browse files
committed
feat: allow external formatter to cause formatting error
1 parent 6b10713 commit a947eef

4 files changed

Lines changed: 46 additions & 24 deletions

File tree

src/format_text.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,20 @@ pub fn format_parsed_source(source: &ParsedSource, config: &Configuration, exter
9393
}
9494

9595
fn inner_format(parsed_source: &ParsedSource, config: &Configuration, external_formatter: Option<&ExternalFormatter>) -> Result<Option<String>> {
96+
let mut maybe_err: Box<Option<anyhow::Error>> = Box::new(None);
9697
let result = dprint_core::formatting::format(
97-
|| {
98-
#[allow(clippy::let_and_return)]
99-
let print_items = generate(parsed_source, config, external_formatter);
100-
// println!("{}", print_items.get_as_text());
101-
print_items
98+
|| match generate(parsed_source, config, external_formatter) {
99+
Ok(print_itmes) => print_itmes,
100+
Err(e) => {
101+
maybe_err.replace(e);
102+
PrintItems::default()
103+
}
102104
},
103105
config_to_print_options(parsed_source.text(), config),
104106
);
107+
if let Some(e) = maybe_err.take() {
108+
return Err(e);
109+
}
105110
if result == parsed_source.text().as_ref() {
106111
Ok(None)
107112
} else {

src/generation/context.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,12 @@ use crate::utils::Stack;
4545
/// cases the templates will be left as they are.
4646
///
4747
/// Only templates with no interpolation are supported.
48-
pub type ExternalFormatter = dyn Fn(MediaType, String, &Configuration) -> Option<String>;
48+
pub type ExternalFormatter = dyn Fn(MediaType, String, &Configuration) -> anyhow::Result<Option<String>>;
49+
50+
pub(crate) struct GenerateDiagnostic {
51+
pub message: String,
52+
pub range: SourceRange,
53+
}
4954

5055
pub struct Context<'a> {
5156
pub media_type: MediaType,
@@ -70,6 +75,7 @@ pub struct Context<'a> {
7075
/// Used for ensuring nodes are parsed in order.
7176
#[cfg(debug_assertions)]
7277
pub last_generated_node_pos: SourcePos,
78+
pub diagnostics: Vec<GenerateDiagnostic>,
7379
}
7480

7581
impl<'a> Context<'a> {
@@ -102,6 +108,7 @@ impl<'a> Context<'a> {
102108
expr_stmt_single_line_parent_brace_ref: None,
103109
#[cfg(debug_assertions)]
104110
last_generated_node_pos: deno_ast::SourceTextInfoProvider::text_info(&program).range().start.into(),
111+
diagnostics: Vec::new(),
105112
}
106113
}
107114

src/generation/generate.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use super::*;
2525
use crate::configuration::*;
2626
use crate::utils;
2727

28-
pub fn generate(parsed_source: &ParsedSource, config: &Configuration, external_formatter: Option<&ExternalFormatter>) -> PrintItems {
28+
pub fn generate(parsed_source: &ParsedSource, config: &Configuration, external_formatter: Option<&ExternalFormatter>) -> anyhow::Result<PrintItems> {
2929
// eprintln!("Leading: {:?}", parsed_source.comments().leading_map());
3030
// eprintln!("Trailing: {:?}", parsed_source.comments().trailing_map());
3131

@@ -49,10 +49,14 @@ pub fn generate(parsed_source: &ParsedSource, config: &Configuration, external_f
4949
#[cfg(debug_assertions)]
5050
context.assert_end_of_file_state();
5151

52+
if let Some(diagnostic) = context.diagnostics.pop() {
53+
return Err(anyhow::anyhow!("{}: {:?}", diagnostic.message, diagnostic.range));
54+
}
55+
5256
if config.file_indent_level > 0 {
53-
with_indent_times(items, config.file_indent_level)
57+
Ok(with_indent_times(items, config.file_indent_level))
5458
} else {
55-
items
59+
Ok(items)
5660
}
5761
})
5862
}
@@ -3034,7 +3038,16 @@ fn maybe_gen_tagged_tpl_with_external_formatter<'a>(node: &TaggedTpl<'a>, contex
30343038
.unwrap();
30353039

30363040
// Then formats the text with the external formatter.
3037-
let formatted_tpl = external_formatter(media_type, text, context.config)?;
3041+
let formatted_tpl = match external_formatter(media_type, text, context.config) {
3042+
Ok(formatted_tpl) => formatted_tpl?,
3043+
Err(err) => {
3044+
context.diagnostics.push(context::GenerateDiagnostic {
3045+
message: format!("Error formatting tagged template literal: {}", err),
3046+
range: node.range(),
3047+
});
3048+
return None;
3049+
}
3050+
};
30383051

30393052
let mut items = PrintItems::new();
30403053
items.push_sc(sc!("`"));

tests/spec_test.rs

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::path::{Path, PathBuf};
22
use std::sync::Arc;
33

4+
use anyhow::Result;
45
use deno_ast::MediaType;
56
use dprint_core::configuration::*;
67
use dprint_development::*;
78
use dprint_plugin_typescript::configuration::*;
89
use dprint_plugin_typescript::*;
910

10-
fn external_formatter(media_type: MediaType, text: String, config: &Configuration) -> Option<String> {
11+
fn external_formatter(media_type: MediaType, text: String, config: &Configuration) -> Result<Option<String>> {
1112
match media_type {
1213
MediaType::Css => format_embedded_css(&text, config),
1314
MediaType::Html => format_html(&text, config),
@@ -16,7 +17,7 @@ fn external_formatter(media_type: MediaType, text: String, config: &Configuratio
1617
}
1718
}
1819

19-
fn format_embedded_css(text: &str, config: &Configuration) -> Option<String> {
20+
fn format_embedded_css(text: &str, config: &Configuration) -> Result<Option<String>> {
2021
use malva::config;
2122
let options = config::FormatOptions {
2223
layout: config::LayoutOptions {
@@ -27,9 +28,7 @@ fn format_embedded_css(text: &str, config: &Configuration) -> Option<String> {
2728
};
2829
// Wraps the text in a css block of `a { ... }`
2930
// to make it valid css (scss)
30-
let Ok(text) = malva::format_text(&format!("a{{\n{}\n}}", text), malva::Syntax::Scss, &options) else {
31-
return None;
32-
};
31+
let text = malva::format_text(&format!("a{{\n{}\n}}", text), malva::Syntax::Scss, &options)?;
3332
let mut buf = vec![];
3433
for (i, l) in text.lines().enumerate() {
3534
// skip the first line (a {)
@@ -47,10 +46,10 @@ fn format_embedded_css(text: &str, config: &Configuration) -> Option<String> {
4746
}
4847
buf.push(chars.as_str());
4948
}
50-
Some(buf.join("\n").to_string())
49+
Ok(Some(buf.join("\n").to_string()))
5150
}
5251

53-
fn format_html(text: &str, config: &Configuration) -> Option<String> {
52+
fn format_html(text: &str, config: &Configuration) -> Result<Option<String>> {
5453
use markup_fmt::config;
5554
let options = config::FormatOptions {
5655
layout: config::LayoutOptions {
@@ -59,19 +58,17 @@ fn format_html(text: &str, config: &Configuration) -> Option<String> {
5958
},
6059
..Default::default()
6160
};
62-
let Ok(text) = markup_fmt::format_text(text, markup_fmt::Language::Html, &options, |code, _| {
61+
let text = markup_fmt::format_text(text, markup_fmt::Language::Html, &options, |code, _| {
6362
Ok::<_, std::convert::Infallible>(code.into())
64-
}) else {
65-
return None;
66-
};
67-
Some(text.to_string())
63+
})?;
64+
Ok(Some(text.to_string()))
6865
}
6966

70-
fn format_sql(text: &str, config: &Configuration) -> Option<String> {
67+
fn format_sql(text: &str, config: &Configuration) -> Result<Option<String>> {
7168
let options = dprint_plugin_sql::configuration::ConfigurationBuilder::new()
7269
.indent_width(config.indent_width)
7370
.build();
74-
dprint_plugin_sql::format_text(Path::new("_path.sql"), text, &options).ok().flatten()
71+
dprint_plugin_sql::format_text(Path::new("_path.sql"), text, &options)
7572
}
7673

7774
fn main() {

0 commit comments

Comments
 (0)