From 5e20a29b4fdc8a2d442d1093681b396dcb4b816b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 7 Jan 2020 11:18:04 +0000 Subject: Add structopt dependency in version 0.3.7 This patch series replaces argparse with structopt in the argument handling code. As a first step, we need structopt as a dependency. Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296 Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7 Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1 Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2 Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3 Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2 Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3 Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393 Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7 --- textwrap/src/indentation.rs | 294 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 294 insertions(+) create mode 100644 textwrap/src/indentation.rs (limited to 'textwrap/src/indentation.rs') diff --git a/textwrap/src/indentation.rs b/textwrap/src/indentation.rs new file mode 100644 index 0000000..276ba10 --- /dev/null +++ b/textwrap/src/indentation.rs @@ -0,0 +1,294 @@ +//! Functions related to adding and removing indentation from lines of +//! text. +//! +//! The functions here can be used to uniformly indent or dedent +//! (unindent) word wrapped lines of text. + +/// Add prefix to each non-empty line. +/// +/// ``` +/// use textwrap::indent; +/// +/// assert_eq!(indent(" +/// Foo +/// Bar +/// ", " "), " +/// Foo +/// Bar +/// "); +/// ``` +/// +/// Empty lines (lines consisting only of whitespace) are not indented +/// and the whitespace is replaced by a single newline (`\n`): +/// +/// ``` +/// use textwrap::indent; +/// +/// assert_eq!(indent(" +/// Foo +/// +/// Bar +/// \t +/// Baz +/// ", "->"), " +/// ->Foo +/// +/// ->Bar +/// +/// ->Baz +/// "); +/// ``` +/// +/// Leading and trailing whitespace on non-empty lines is kept +/// unchanged: +/// +/// ``` +/// use textwrap::indent; +/// +/// assert_eq!(indent(" \t Foo ", "->"), "-> \t Foo \n"); +/// ``` +pub fn indent(s: &str, prefix: &str) -> String { + let mut result = String::new(); + for line in s.lines() { + if line.chars().any(|c| !c.is_whitespace()) { + result.push_str(prefix); + result.push_str(line); + } + result.push('\n'); + } + result +} + +/// Removes common leading whitespace from each line. +/// +/// This function will look at each non-empty line and determine the +/// maximum amount of whitespace that can be removed from all lines: +/// +/// ``` +/// use textwrap::dedent; +/// +/// assert_eq!(dedent(" +/// 1st line +/// 2nd line +/// 3rd line +/// "), " +/// 1st line +/// 2nd line +/// 3rd line +/// "); +/// ``` +pub fn dedent(s: &str) -> String { + let mut prefix = ""; + let mut lines = s.lines(); + + // We first search for a non-empty line to find a prefix. + for line in &mut lines { + let mut whitespace_idx = line.len(); + for (idx, ch) in line.char_indices() { + if !ch.is_whitespace() { + whitespace_idx = idx; + break; + } + } + + // Check if the line had anything but whitespace + if whitespace_idx < line.len() { + prefix = &line[..whitespace_idx]; + break; + } + } + + // We then continue looking through the remaining lines to + // possibly shorten the prefix. + for line in &mut lines { + let mut whitespace_idx = line.len(); + for ((idx, a), b) in line.char_indices().zip(prefix.chars()) { + if a != b { + whitespace_idx = idx; + break; + } + } + + // Check if the line had anything but whitespace and if we + // have found a shorter prefix + if whitespace_idx < line.len() && whitespace_idx < prefix.len() { + prefix = &line[..whitespace_idx]; + } + } + + // We now go over the lines a second time to build the result. + let mut result = String::new(); + for line in s.lines() { + if line.starts_with(&prefix) && line.chars().any(|c| !c.is_whitespace()) { + let (_, tail) = line.split_at(prefix.len()); + result.push_str(tail); + } + result.push('\n'); + } + + if result.ends_with('\n') && !s.ends_with('\n') { + let new_len = result.len() - 1; + result.truncate(new_len); + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Add newlines. Ensures that the final line in the vector also + /// has a newline. + fn add_nl(lines: &[&str]) -> String { + lines.join("\n") + "\n" + } + + #[test] + fn indent_empty() { + assert_eq!(indent("\n", " "), "\n"); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn indent_nonempty() { + let x = vec![" foo", + "bar", + " baz"]; + let y = vec!["// foo", + "//bar", + "// baz"]; + assert_eq!(indent(&add_nl(&x), "//"), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn indent_empty_line() { + let x = vec![" foo", + "bar", + "", + " baz"]; + let y = vec!["// foo", + "//bar", + "", + "// baz"]; + assert_eq!(indent(&add_nl(&x), "//"), add_nl(&y)); + } + + #[test] + fn dedent_empty() { + assert_eq!(dedent(""), ""); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_multi_line() { + let x = vec![" foo", + " bar", + " baz"]; + let y = vec![" foo", + "bar", + " baz"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_empty_line() { + let x = vec![" foo", + " bar", + " ", + " baz"]; + let y = vec![" foo", + "bar", + "", + " baz"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_blank_line() { + let x = vec![" foo", + "", + " bar", + " foo", + " bar", + " baz"]; + let y = vec!["foo", + "", + " bar", + " foo", + " bar", + " baz"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_whitespace_line() { + let x = vec![" foo", + " ", + " bar", + " foo", + " bar", + " baz"]; + let y = vec!["foo", + "", + " bar", + " foo", + " bar", + " baz"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_mixed_whitespace() { + let x = vec!["\tfoo", + " bar"]; + let y = vec!["\tfoo", + " bar"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_tabbed_whitespace() { + let x = vec!["\t\tfoo", + "\t\t\tbar"]; + let y = vec!["foo", + "\tbar"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_mixed_tabbed_whitespace() { + let x = vec!["\t \tfoo", + "\t \t\tbar"]; + let y = vec!["foo", + "\tbar"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_mixed_tabbed_whitespace2() { + let x = vec!["\t \tfoo", + "\t \tbar"]; + let y = vec!["\tfoo", + " \tbar"]; + assert_eq!(dedent(&add_nl(&x)), add_nl(&y)); + } + + #[test] + #[cfg_attr(rustfmt, rustfmt_skip)] + fn dedent_preserve_no_terminating_newline() { + let x = vec![" foo", + " bar"].join("\n"); + let y = vec!["foo", + " bar"].join("\n"); + assert_eq!(dedent(&x), y); + } +} -- cgit v1.2.1