Skip to content

Commit

Permalink
feat: provide more information to external formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
g-plane committed Aug 13, 2024
1 parent cfa00b7 commit 92417a1
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 192 deletions.
50 changes: 18 additions & 32 deletions dprint_plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use dprint_core::{
};
use markup_fmt::{
config::{FormatOptions, Quotes},
detect_language, format_text, FormatError,
detect_language, format_text, FormatError, Hints,
};
use std::path::Path;

Expand Down Expand Up @@ -83,8 +83,8 @@ impl SyncPluginHandler<FormatOptions> for MarkupFmtPluginHandler {
std::str::from_utf8(&file_text)?,
language,
config,
|path, code, print_width| {
let additional_config = build_additional_config(path, print_width, config);
|path, code, hints| {
let additional_config = build_additional_config(hints, config);
format_with_host(path, code.into(), &additional_config).and_then(|result| {
match result {
Some(code) => String::from_utf8(code)
Expand Down Expand Up @@ -116,39 +116,25 @@ impl SyncPluginHandler<FormatOptions> for MarkupFmtPluginHandler {
generate_plugin_code!(MarkupFmtPluginHandler, MarkupFmtPluginHandler);

#[doc(hidden)]
pub fn build_additional_config(
path: &Path,
print_width: usize,
config: &FormatOptions,
) -> ConfigKeyMap {
pub fn build_additional_config(hints: Hints, config: &FormatOptions) -> ConfigKeyMap {
let mut additional_config = ConfigKeyMap::new();
additional_config.insert("lineWidth".into(), (print_width as i32).into());
additional_config.insert("printWidth".into(), (print_width as i32).into());
additional_config.insert("lineWidth".into(), (hints.print_width as i32).into());
additional_config.insert("printWidth".into(), (hints.print_width as i32).into());

let file_name = path.file_name().and_then(|s| s.to_str());
match &file_name {
Some("expr.ts" | "binding.ts" | "type_params.ts") => {
// dprint-plugin-typescript
additional_config.insert("semiColons".into(), "asi".into());
// Biome
additional_config.insert("semicolons".into(), "asNeeded".into());
}
Some("attr_expr.tsx") => {
// Only for dprint-plugin-typescript currently,
// because it conflicts with the `quoteStyle` option in Biome.
match config.language.quotes {
Quotes::Double => {
additional_config.insert("quoteStyle".into(), "alwaysSingle".into());
}
Quotes::Single => {
additional_config.insert("quoteStyle".into(), "alwaysDouble".into());
}
if hints.attr {
// Only for dprint-plugin-typescript currently,
// because it conflicts with the `quoteStyle` option in Biome.
match config.language.quotes {
Quotes::Double => {
additional_config.insert("quoteStyle".into(), "alwaysSingle".into());
}
Quotes::Single => {
additional_config.insert("quoteStyle".into(), "alwaysDouble".into());
}
}
Some("style_attr.css") => {
additional_config.insert("singleLineTopLevelDeclarations".into(), true.into());
}
_ => {}
}
if hints.ext == "css" {
additional_config.insert("singleLineTopLevelDeclarations".into(), true.into());
}

additional_config
Expand Down
8 changes: 4 additions & 4 deletions dprint_plugin/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ fn integration_with_dprint_ts_snapshot() {
&input,
detect_language(path).unwrap(),
&options,
|path, code, print_width| -> anyhow::Result<Cow<str>> {
|path, code, hints| -> anyhow::Result<Cow<str>> {
let additional_config =
dprint_plugin_markup::build_additional_config(path, print_width, &options);
dprint_plugin_markup::build_additional_config(hints, &options);
if let Some(syntax) = malva::detect_syntax(path) {
malva::format_text(
code,
Expand Down Expand Up @@ -100,9 +100,9 @@ fn integration_with_biome_snapshot() {
&input,
detect_language(path).unwrap(),
&options,
|path, code, print_width| -> anyhow::Result<Cow<str>> {
|path, code, hints| -> anyhow::Result<Cow<str>> {
let additional_config =
dprint_plugin_markup::build_additional_config(path, print_width, &options);
dprint_plugin_markup::build_additional_config(hints, &options);
if let Some(syntax) = malva::detect_syntax(path) {
malva::format_text(
code,
Expand Down
151 changes: 100 additions & 51 deletions markup_fmt/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const QUOTES: [&str; 3] = ["\"", "\"", "'"];

pub(crate) struct Ctx<'b, E, F>
where
F: for<'a> FnMut(&Path, &'a str, usize) -> Result<Cow<'a, str>, E>,
F: for<'a> FnMut(&Path, &'a str, Hints<'b>) -> Result<Cow<'a, str>, E>,
{
pub(crate) source: &'b str,
pub(crate) language: Language,
Expand All @@ -29,7 +29,7 @@ where

impl<'b, E, F> Ctx<'b, E, F>
where
F: for<'a> FnMut(&Path, &'a str, usize) -> Result<Cow<'a, str>, E>,
F: for<'a> FnMut(&Path, &'a str, Hints<'b>) -> Result<Cow<'a, str>, E>,
{
pub(crate) fn script_indent(&self) -> bool {
match self.language {
Expand Down Expand Up @@ -97,12 +97,12 @@ where
}

pub(crate) fn format_general_expr(&mut self, code: &str, start: usize) -> String {
self.format_expr(code, Path::new("expr.tsx"), start)
self.format_expr(code, Path::new("expr.tsx"), false, start)
}

pub(crate) fn format_attr_expr(&mut self, code: &str, start: usize) -> String {
let code = UNESCAPING_AC.replace_all(code, &QUOTES);
let formatted = self.format_expr(&code, Path::new("attr_expr.tsx"), start);
let formatted = self.format_expr(&code, Path::new("attr_expr.tsx"), true, start);
if memchr(b'\'', formatted.as_bytes()).is_some()
&& memchr(b'"', formatted.as_bytes()).is_some()
{
Expand All @@ -115,7 +115,7 @@ where
}
}

fn format_expr(&mut self, code: &str, path: &Path, start: usize) -> String {
fn format_expr(&mut self, code: &str, path: &Path, attr: bool, start: usize) -> String {
if code.trim().is_empty() {
String::new()
} else {
Expand All @@ -134,9 +134,14 @@ where
path,
wrapped,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(2), // this is technically wrong, just workaround
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(2), // this is technically wrong, just workaround
attr,
ext: "tsx",
},
);
let formatted = formatted.trim_matches(|c: char| c.is_ascii_whitespace() || c == ';');
let formatted = formatted
Expand Down Expand Up @@ -170,9 +175,14 @@ where
Path::new("binding.ts"),
wrapped,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(2), // this is technically wrong, just workaround
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(2), // this is technically wrong, just workaround
attr: false,
ext: "ts",
},
);
let formatted = formatted.trim_matches(|c: char| c.is_ascii_whitespace() || c == ';');
formatted
Expand All @@ -199,9 +209,14 @@ where
Path::new("type_params.ts"),
wrapped,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(TYPE_PARAMS_INDENT), // this is technically wrong, just workaround
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(TYPE_PARAMS_INDENT), // this is technically wrong, just workaround
attr: true,
ext: "ts",
},
);
let formatted = formatted.trim_matches(|c: char| c.is_ascii_whitespace() || c == ';');
formatted
Expand All @@ -221,9 +236,14 @@ where
Path::new("stmt_header.js"),
wrapped,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(keyword.len() + 1), // this is technically wrong, just workaround
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(keyword.len() + 1), // this is technically wrong, just workaround
attr: false,
ext: "js",
},
);
formatted
.strip_prefix(keyword)
Expand All @@ -240,7 +260,7 @@ where
pub(crate) fn format_script<'a>(
&mut self,
code: &'a str,
lang: &str,
lang: &'b str,
start: usize,
) -> Cow<'a, str> {
self.format_with_external_formatter(
Expand All @@ -251,20 +271,25 @@ where
.replace(|c: char| !c.is_ascii_whitespace(), " ")
+ code,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.script_indent() {
self.indent_width
} else {
0
}),
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.script_indent() {
self.indent_width
} else {
0
}),
attr: false,
ext: lang,
},
)
}

pub(crate) fn format_style<'a>(
&mut self,
code: &'a str,
lang: &str,
lang: &'b str,
start: usize,
) -> Cow<'a, str> {
self.format_with_external_formatter(
Expand All @@ -275,13 +300,18 @@ where
.replace(|c: char| !c.is_ascii_whitespace(), " ")
+ code,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.style_indent() {
self.indent_width
} else {
0
}),
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.style_indent() {
self.indent_width
} else {
0
}),
attr: false,
ext: lang,
},
)
}

Expand All @@ -294,13 +324,18 @@ where
.replace(|c: char| !c.is_ascii_whitespace(), " ")
+ code,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.style_indent() {
self.indent_width
} else {
0
}),
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.style_indent() {
self.indent_width
} else {
0
}),
attr: true,
ext: "css",
},
)
.trim()
.to_owned()
Expand All @@ -315,13 +350,18 @@ where
.replace(|c: char| !c.is_ascii_whitespace(), " ")
+ code,
code,
self.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.script_indent() {
self.indent_width
} else {
0
}),
Hints {
print_width: self
.print_width
.saturating_sub(self.indent_level)
.saturating_sub(if self.script_indent() {
self.indent_width
} else {
0
}),
attr: false,
ext: "json",
},
)
}

Expand All @@ -330,9 +370,9 @@ where
path: &Path,
code: String,
_original_code: &'a str,
print_width: usize,
hints: Hints<'b>,
) -> Cow<'a, str> {
match (self.external_formatter)(path, &code, print_width) {
match (self.external_formatter)(path, &code, hints) {
Ok(Cow::Owned(formatted)) => Cow::from(formatted),
Ok(Cow::Borrowed(..)) => Cow::from(code),
Err(e) => {
Expand All @@ -343,16 +383,25 @@ where
}
}

/// Hints provide some useful additional information to the external formatter.
pub struct Hints<'s> {
pub print_width: usize,
/// Whether the code is inside attribute.
pub attr: bool,
/// Fake file extension.
pub ext: &'s str,
}

pub(crate) trait NestWithCtx {
fn nest_with_ctx<'b, E, F>(self, ctx: &mut Ctx<'b, E, F>) -> Self
where
F: for<'a> FnMut(&Path, &'a str, usize) -> Result<Cow<'a, str>, E>;
F: for<'a> FnMut(&Path, &'a str, Hints<'b>) -> Result<Cow<'a, str>, E>;
}

impl NestWithCtx for Doc<'_> {
fn nest_with_ctx<'b, E, F>(self, ctx: &mut Ctx<'b, E, F>) -> Self
where
F: for<'a> FnMut(&Path, &'a str, usize) -> Result<Cow<'a, str>, E>,
F: for<'a> FnMut(&Path, &'a str, Hints<'b>) -> Result<Cow<'a, str>, E>,
{
ctx.indent_level += ctx.indent_width;
let doc = self.nest(ctx.indent_width);
Expand Down
4 changes: 2 additions & 2 deletions markup_fmt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mod printer;
mod state;

use crate::{config::FormatOptions, ctx::Ctx, parser::Parser, printer::DocGen, state::State};
pub use crate::{error::*, parser::Language};
pub use crate::{ctx::Hints, error::*, parser::Language};
use std::{borrow::Cow, path::Path};
use tiny_pretty::{IndentKind, PrintOptions};

Expand Down Expand Up @@ -56,7 +56,7 @@ pub fn format_text<E, F>(
external_formatter: F,
) -> Result<String, FormatError<E>>
where
F: for<'a> FnMut(&Path, &'a str, usize) -> Result<Cow<'a, str>, E>,
F: for<'a> FnMut(&Path, &'a str, Hints) -> Result<Cow<'a, str>, E>,
{
let mut parser = Parser::new(code, language.clone());
let ast = parser.parse_root().map_err(FormatError::Syntax)?;
Expand Down
Loading

0 comments on commit 92417a1

Please sign in to comment.