diff --git a/docs/config.md b/docs/config.md index 3b482d9..7680ff0 100644 --- a/docs/config.md +++ b/docs/config.md @@ -192,6 +192,26 @@ Control whether trailing whitespaces should be trimmed or not. Default option is `true`. +## `trimTrailingZero` + +Control whether trailing zeros should be removed or not. + +Default option is `false`. + +### Example for `false` + +```yaml +- 1.20 +- 1.0 +``` + +### Example for `true` + +```yaml +- 1.2 +- 1 +``` + ## `ignoreCommentDirective` Text directive for ignoring formatting specific content. diff --git a/dprint_plugin/deployment/schema.json b/dprint_plugin/deployment/schema.json index b674673..20b20d3 100644 --- a/dprint_plugin/deployment/schema.json +++ b/dprint_plugin/deployment/schema.json @@ -87,6 +87,11 @@ "type": "boolean", "default": true }, + "trimTrailingZero": { + "description": "Control whether trailing zeros should be removed or not.", + "type": "boolean", + "default": false + }, "ignoreCommentDirective": { "description": "Text directive for ignoring formatting specific content.", "type": "string", diff --git a/dprint_plugin/src/config.rs b/dprint_plugin/src/config.rs index a4d3341..0cb010c 100644 --- a/dprint_plugin/src/config.rs +++ b/dprint_plugin/src/config.rs @@ -100,6 +100,7 @@ pub(crate) fn resolve_config( true, &mut diagnostics, ), + trim_trailing_zero: get_value(&mut config, "trimTrailingZero", false, &mut diagnostics), ignore_comment_directive: get_value( &mut config, "ignoreCommentDirective", diff --git a/pretty_yaml/src/config.rs b/pretty_yaml/src/config.rs index c6b09db..1aac576 100644 --- a/pretty_yaml/src/config.rs +++ b/pretty_yaml/src/config.rs @@ -106,6 +106,10 @@ pub struct LanguageOptions { /// See [`trimTrailingWhitespaces`](https://github.com/g-plane/pretty_yaml/blob/main/docs/config.md#trimtrailingwhitespaces) on GitHub pub trim_trailing_whitespaces: bool, + #[cfg_attr(feature = "config_serde", serde(alias = "trimTrailingZero"))] + /// See [`trimTrailingZero`](https://github.com/g-plane/pretty_yaml/blob/main/docs/config.md#trimtrailingzero) on GitHub + pub trim_trailing_zero: bool, + #[cfg_attr(feature = "config_serde", serde(alias = "ignoreCommentDirective"))] /// See [`ignoreCommentDirective`](https://github.com/g-plane/pretty_yaml/blob/main/docs/config.md#ignorecommentdirective) on GitHub pub ignore_comment_directive: String, @@ -122,6 +126,7 @@ impl Default for LanguageOptions { bracket_spacing: false, dash_spacing: DashSpacing::default(), trim_trailing_whitespaces: true, + trim_trailing_zero: false, ignore_comment_directive: "pretty-yaml-ignore".into(), } } diff --git a/pretty_yaml/src/printer.rs b/pretty_yaml/src/printer.rs index 04e74ca..f0b8814 100644 --- a/pretty_yaml/src/printer.rs +++ b/pretty_yaml/src/printer.rs @@ -1,5 +1,6 @@ use crate::config::{LanguageOptions, Quotes}; use rowan::Direction; +use std::ops::Range; use tiny_pretty::Doc; use yaml_parser::{ast::*, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken}; @@ -360,8 +361,33 @@ impl DocGen for Flow { format_quoted_scalar(text, quotes_option, &mut docs, ctx); docs.push(Doc::text(quote)); } else if let Some(plain) = self.plain_scalar() { - let lines = plain.text().lines().map(|s| s.trim().to_owned()); - intersperse_lines(&mut docs, lines); + let token_text = plain.text(); + 'a: { + if ctx.options.trim_trailing_zero { + let ranges = parse_float(token_text); + if let Some((range_int, range_fraction, fraction)) = ranges.and_then(|ranges| { + token_text + .get(ranges.1.clone()) + .filter(|fraction| fraction.ends_with('0')) + .map(|fraction| (ranges.0, ranges.1, fraction)) + }) { + let mut token_text = token_text.to_owned(); + let trimmed_fraction = fraction.trim_end_matches('0'); + if trimmed_fraction == "." { + if token_text.get(range_int.clone()).is_some_and(str::is_empty) { + token_text.replace_range(range_int, "0"); + } + token_text.replace_range(range_fraction, ""); + } else { + token_text.replace_range(range_fraction, trimmed_fraction); + } + docs.push(Doc::text(token_text)); + break 'a; + } + } + let lines = token_text.lines().map(|s| s.trim().to_owned()); + intersperse_lines(&mut docs, lines); + } } else if let Some(flow_seq) = self.flow_seq() { docs.push(flow_seq.doc(ctx)); } else if let Some(flow_map) = self.flow_map() { @@ -1205,6 +1231,27 @@ fn can_omit_question_mark(key: &SyntaxNode) -> bool { }) } +fn parse_float(literal: &str) -> Option<(Range, Range)> { + let mut s = literal.strip_prefix(['+', '-']).unwrap_or(literal); + let int_start = literal.len() - s.len(); + s = s.trim_start_matches(|c: char| c.is_ascii_digit()); + let int_end = literal.len() - s.len(); + + let fraction_start = literal.len() - s.len(); + let mut fraction_end = literal.len(); + s = s.strip_prefix('.')?; + s = s.trim_start_matches(|c: char| c.is_ascii_digit()); + + if let Some(mut rest) = s.strip_prefix(['e', 'E']) { + fraction_end = literal.len() - s.len(); + rest = rest.strip_prefix(['+', '-']).unwrap_or(rest); + if !rest.chars().all(|c| c.is_ascii_digit()) { + return None; + } + } + Some((int_start..int_end, fraction_start..fraction_end)) +} + fn intersperse_lines(docs: &mut Vec>, mut lines: impl Iterator) { if let Some(line) = lines.next() { docs.push(Doc::text(line)); diff --git a/pretty_yaml/tests/fmt/trim-trailing-zero/config.toml b/pretty_yaml/tests/fmt/trim-trailing-zero/config.toml new file mode 100644 index 0000000..3e73c12 --- /dev/null +++ b/pretty_yaml/tests/fmt/trim-trailing-zero/config.toml @@ -0,0 +1,2 @@ +[enabled] +trimTrailingZero = true diff --git a/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.enabled.snap b/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.enabled.snap new file mode 100644 index 0000000..5e8862f --- /dev/null +++ b/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.enabled.snap @@ -0,0 +1,51 @@ +--- +source: pretty_yaml/tests/fmt.rs +--- +- { + a: 1.23, + b: 1.2, + c: .23, + d: .2, + e: 1, + f: 0, + g: +1, + h: -1, + } +- - "a": 1.23 + - "b": 1.2 + - "c": .23 + - "d": .2 + - "e": 1 + - "f": 0 + - "g": +1 + - "h": -1 + - "i": +0 + - "j": -0 + +- 1.23e20 +- 1.2e20 +- .23e20 +- .2e20 +- 1e20 +- 0e20 +- +1e20 +- -1e20 +- +0e20 +- -0e20 +- 1.23E20 +- 1.2E20 +- .23E20 +- .2E20 +- 1E20 +- 0E20 +- +1E20 +- -1E20 +- +0E20 +- -0E20 +... + +%YAML 1.1 +--- +a: 1_2.3_0 +b: 1_2.3_0_0 +c: 1_2.3_4_0__0_e30 diff --git a/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.yaml b/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.yaml new file mode 100644 index 0000000..878b65b --- /dev/null +++ b/pretty_yaml/tests/fmt/trim-trailing-zero/invalid.yaml @@ -0,0 +1,49 @@ +- { + a: 1.23, + b: 1.20, + c: .23, + d: .20, + e: 1.0, + f: .0, + g: +1.0, + h: -1.0 + } +- + - "a": 1.23 + - "b": 1.20 + - "c": .23 + - "d": .20 + - "e": 1.0 + - "f": .0 + - "g": +1.0 + - "h": -1.0 + - "i": +.0 + - "j": -.0 + +- 1.23e20 +- 1.20e20 +- .23e20 +- .20e20 +- 1.0e20 +- .0e20 +- +1.0e20 +- -1.0e20 +- +.0e20 +- -.0e20 +- 1.23E20 +- 1.20E20 +- .23E20 +- .20E20 +- 1.0E20 +- .0E20 +- +1.0E20 +- -1.0E20 +- +.0E20 +- -.0E20 +... + +%YAML 1.1 +--- +a: 1_2.3_0 +b: 1_2.3_0_0 +c: 1_2.3_4_0__0_e30 diff --git a/pretty_yaml/tests/fmt/trim-trailing-zero/valid.enabled.snap b/pretty_yaml/tests/fmt/trim-trailing-zero/valid.enabled.snap new file mode 100644 index 0000000..667d009 --- /dev/null +++ b/pretty_yaml/tests/fmt/trim-trailing-zero/valid.enabled.snap @@ -0,0 +1,40 @@ +--- +source: pretty_yaml/tests/fmt.rs +--- +- { + a: 1.23, + b: "1.20", + c: .23, + d: .2, + e: "1.0", + f: ".0", + g: "+1.0", + h: "-1.0", + } +- - "a": 1.23 + - "b": "1.20" + - "c": .23 + - "d": .2 + - "e": "1.0" + - "f": ".0" + - "g": "+1.0" + - "h": "-1.0" + - "i": "+.0" + - "j": "-.0" + +- 1.23e20 +- "1.20e20" +- .23e20 +- .2e20 +- "1.0e20" +- ".0e20" +- "+1.0e20" +- "-1.0e20" +- "+.0e20" +- "-.0e20" +... + +%YAML 1.1 +--- +a: 1_2.3_4 +b: 1_2.3_4e30 diff --git a/pretty_yaml/tests/fmt/trim-trailing-zero/valid.yaml b/pretty_yaml/tests/fmt/trim-trailing-zero/valid.yaml new file mode 100644 index 0000000..74ebd6a --- /dev/null +++ b/pretty_yaml/tests/fmt/trim-trailing-zero/valid.yaml @@ -0,0 +1,38 @@ +- { + a: 1.23, + b: "1.20", + c: .23, + d: .2, + e: "1.0", + f: ".0", + g: "+1.0", + h: "-1.0" + } +- + - "a": 1.23 + - "b": "1.20" + - "c": .23 + - "d": .2 + - "e": "1.0" + - "f": ".0" + - "g": "+1.0" + - "h": "-1.0" + - "i": "+.0" + - "j": "-.0" + +- 1.23e20 +- "1.20e20" +- .23e20 +- .2e20 +- "1.0e20" +- ".0e20" +- "+1.0e20" +- "-1.0e20" +- "+.0e20" +- "-.0e20" +... + +%YAML 1.1 +--- +a: 1_2.3_4 +b: 1_2.3_4e30