Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 4 additions & 70 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ wasm = ["serde_json", "dprint-core/wasm"]
tracing = ["dprint-core/tracing"]

[dependencies]
dprint-core = { version = "0.68.1", features = ["formatting"], default-features = false }
dprint-core = { version = "0.69.0", features = ["formatting"], default-features = false }
dprint-core-macros = "0.1.0"
monch = "0.6.0"
serde = { version = "1.0.144", features = ["derive"] }
Expand Down
10 changes: 10 additions & 0 deletions deployment/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,16 @@
"description": "Whether to always break a HEALTHCHECK command onto its own continuation line when the instruction has options, even if it would fit on one line.",
"default": false,
"type": "boolean"
},
"indentStages": {
"description": "Whether to indent the body of each build stage—the instructions and comments that follow a FROM—by indentWidth spaces. The indentation is always spaces; useTabs is not honored.",
"default": false,
"type": "boolean"
},
"indentWidth": {
"description": "The number of spaces for an indent. Only used when indentStages is enabled.",
"default": 2,
"type": "number"
}
}
}
35 changes: 33 additions & 2 deletions src/configuration/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,21 @@ impl ConfigurationBuilder {
self.insert("healthcheckCmdNewLine", value.into())
}

/// Whether to indent the body of each build stage - the instructions and
/// comments that follow a `FROM` - by `indent_width` spaces. The indentation
/// is always spaces; `useTabs` is not honored.
/// Default: `false`
pub fn indent_stages(&mut self, value: bool) -> &mut Self {
self.insert("indentStages", value.into())
}

/// The number of spaces for an indent. Only used when `indent_stages` is
/// enabled.
/// Default: 2
pub fn indent_width(&mut self, value: u8) -> &mut Self {
self.insert("indentWidth", (value as i32).into())
}

#[cfg(test)]
pub(super) fn get_inner_config(&self) -> ConfigKeyMap {
self.config.clone()
Expand All @@ -95,10 +110,12 @@ mod tests {
config
.new_line_kind(NewLineKind::CarriageReturnLineFeed)
.line_width(90)
.healthcheck_cmd_new_line(true);
.healthcheck_cmd_new_line(true)
.indent_stages(true)
.indent_width(4);

let inner_config = config.get_inner_config();
assert_eq!(inner_config.len(), 3);
assert_eq!(inner_config.len(), 5);
let diagnostics = resolve_config(inner_config, &Default::default()).diagnostics;
assert_eq!(diagnostics.len(), 0);
}
Expand All @@ -108,12 +125,14 @@ mod tests {
let mut global_config = ConfigKeyMap::new();
global_config.insert(String::from("lineWidth"), 90.into());
global_config.insert(String::from("newLineKind"), "crlf".into());
global_config.insert(String::from("indentWidth"), 4.into());
global_config.insert(String::from("useTabs"), true.into());
let global_config = resolve_global_config(&mut global_config).config;
let mut config_builder = ConfigurationBuilder::new();
let config = config_builder.global_config(global_config).build();
assert_eq!(config.line_width, 90);
assert_eq!(config.new_line_kind, NewLineKind::CarriageReturnLineFeed);
assert_eq!(config.indent_width, 4);
}

#[test]
Expand All @@ -122,5 +141,17 @@ mod tests {
let mut config_builder = ConfigurationBuilder::new();
let config = config_builder.global_config(global_config).build();
assert_eq!(config.new_line_kind, NewLineKind::LineFeed);
assert_eq!(config.indent_width, 2);
assert!(!config.indent_stages);
}

#[test]
fn override_global_indent_width() {
let mut global_config = ConfigKeyMap::new();
global_config.insert(String::from("indentWidth"), 4.into());
let global_config = resolve_global_config(&mut global_config).config;
let mut config_builder = ConfigurationBuilder::new();
let config = config_builder.global_config(global_config).indent_width(8).build();
assert_eq!(config.indent_width, 8);
}
}
7 changes: 7 additions & 0 deletions src/configuration/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ pub struct Configuration {
/// Whether to always break a `HEALTHCHECK` command onto its own continuation
/// line when the instruction has options, even if it would fit on one line.
pub healthcheck_cmd_new_line: bool,
/// Whether to indent the body of each build stage - the instructions and
/// comments that follow a `FROM` - by `indent_width` spaces. The indentation
/// is always spaces; `useTabs` is not honored.
pub indent_stages: bool,
/// The number of spaces for an indent. Only used when `indent_stages` is
/// enabled.
pub indent_width: u8,
}
7 changes: 7 additions & 0 deletions src/configuration/resolve_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration)
&mut diagnostics,
),
healthcheck_cmd_new_line: get_value(&mut config, "healthcheckCmdNewLine", false, &mut diagnostics),
indent_stages: get_value(&mut config, "indentStages", false, &mut diagnostics),
indent_width: get_value(
&mut config,
"indentWidth",
global_config.indent_width.unwrap_or(RECOMMENDED_GLOBAL_CONFIGURATION.indent_width),
&mut diagnostics,
),
};

diagnostics.extend(get_unknown_property_diagnostics(config));
Expand Down
37 changes: 37 additions & 0 deletions src/format_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,43 @@ mod test {
}
}

#[test]
fn indents_stages_with_carriage_returns() {
// a `\r` is never part of the indentation and never reaches the dedent
let text = format_with(
"FROM alpine\r\n RUN foo \\\r\n bar\r\n",
crate::configuration::ConfigurationBuilder::new().indent_stages(true),
);
assert_eq!(text, "FROM alpine\n RUN foo \\\n bar\n");
}

#[test]
fn indent_width_of_zero_does_not_indent_stages() {
let text = format_with(
"FROM alpine\nRUN echo hi\n",
crate::configuration::ConfigurationBuilder::new().indent_stages(true).indent_width(0),
);
assert_eq!(text, "FROM alpine\nRUN echo hi\n");
}

#[test]
fn huge_indent_width_is_capped_instead_of_overflowing_the_printer() {
// the printer stores its indent level in a `u8`, so the width is capped
let text = format_with(
"FROM alpine\nENV A=1 \\\n B=2\n",
crate::configuration::ConfigurationBuilder::new().indent_stages(true).indent_width(255),
);
let indent = " ".repeat(200);
assert_eq!(text, format!("FROM alpine\n{0}ENV A=1 \\\n{0} B=2\n", indent));
}

/// Formats the text, resolving the "already formatted" result to the input.
fn format_with(text: &str, builder: &mut crate::configuration::ConfigurationBuilder) -> String {
format_text(&std::path::PathBuf::from("Dockerfile"), text, &builder.build())
.unwrap()
.unwrap_or_else(|| text.to_string())
}

#[test]
fn comment_with_interior_tab_does_not_panic() {
// a tab inside a comment must be emitted as a tab signal, not a raw tab
Expand Down
8 changes: 6 additions & 2 deletions src/generation/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@ pub struct Context<'a> {
current_node: Option<Node<'a>>,
parent_stack: Vec<Node<'a>>,
pub gen_string_content: bool,
/// whether the current breakable string is shell content whose insignificant
/// Whether the current breakable string is shell content whose insignificant
/// whitespace runs should be collapsed
pub collapse_shell_ws: bool,
/// the quote currently open while collapsing shell whitespace (carried across
/// The quote currently open while collapsing shell whitespace (carried across
/// the breakable string's components), or `None` when outside a quote
pub shell_quote: Option<char>,
/// The amount of leading whitespace to strip from the continuation lines of
/// the current instruction so the printer can re-indent them
pub dedent_width: usize,
}

impl<'a> Context<'a> {
Expand All @@ -36,6 +39,7 @@ impl<'a> Context<'a> {
gen_string_content: false,
collapse_shell_ws: false,
shell_quote: None,
dedent_width: 0,
}
}

Expand Down
Loading