aboutsummaryrefslogtreecommitdiff
path: root/textwrap/src/indentation.rs
diff options
context:
space:
mode:
authorRobin Krahl <robin.krahl@ireas.org>2020-01-07 11:18:04 +0000
committerDaniel Mueller <deso@posteo.net>2020-01-08 09:20:25 -0800
commit5e20a29b4fdc8a2d442d1093681b396dcb4b816b (patch)
tree55ab083fa8999d2ccbb5e921c1ffe52560dca152 /textwrap/src/indentation.rs
parent203e691f46d591a2cc8acdfd850fa9f5b0fb8a98 (diff)
downloadnitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.gz
nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.bz2
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
Diffstat (limited to 'textwrap/src/indentation.rs')
-rw-r--r--textwrap/src/indentation.rs294
1 files changed, 294 insertions, 0 deletions
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);
+ }
+}