on master help sign in
Merge pull request #102 from eth-p/master

Added text wrapping. (Fixes #54)

commit2eee68599df671f7fd067a1121a4d01039c49d8e authorDavid Peter <sharkdp@users.noreply.github.com> dateWed 16 May 2018, 08:41 (8 years ago) parent54b33c8d569693
12 files changed+493−243
src/app.rs+20−1 src/decorations.rs+154 src/main.rs+1 src/printer.rs+151−75 src/style.rs+6 src/terminal.rs+20−26 tests/snapshots/output/changes,grid,header.snapshot.txt+25−25 tests/snapshots/output/changes,grid.snapshot.txt+22−22 tests/snapshots/output/grid,header,numbers.snapshot.txt+25−25 tests/snapshots/output/grid,header.snapshot.txt+25−25 tests/snapshots/output/grid,numbers.snapshot.txt+22−22 tests/snapshots/output/grid.snapshot.txt+22−22
src/app.rs+20−1
@@ -4,7 +4,7 @@ use console::Term;
44use errors::*;
55use std::collections::HashSet;
66use std::env;
7−use style::{OutputComponent, OutputComponents};
7+use style::{OutputComponent, OutputComponents, OutputWrap};
88
99#[cfg(windows)]
1010use ansi_term;
@@ -86,6 +86,14 @@ impl App {
8686 .default_value("auto")
8787 .help("When to use the pager"),
8888 )
89+ .arg(
90+ Arg::with_name("wrap")
91+ .long("wrap")
92+ .takes_value(true)
93+ .possible_values(&["character", "never"])
94+ .default_value("character")
95+ .help("When to wrap text"),
96+ )
8997 .arg(
9098 Arg::with_name("list-languages")
9199 .long("list-languages")
@@ -141,6 +149,16 @@ impl App {
141149 true_color: is_truecolor_terminal(),
142150 output_components: self.output_components()?,
143151 language: self.matches.value_of("language"),
152+ output_wrap: if !self.interactive_output {
153+ // We don't have the tty width when piping to another program.
154+ // There's no point in wrapping when this is the case.
155+ OutputWrap::None
156+ } else {
157+ match self.matches.value_of("wrap") {
158+ Some("character") => OutputWrap::Character,
159+ Some("never") | _ => OutputWrap::None,
160+ }
161+ },
144162 colored_output: match self.matches.value_of("color") {
145163 Some("always") => true,
146164 Some("never") => false,
@@ -197,6 +215,7 @@ impl App {
197215
198216pub struct Config<'a> {
199217 pub true_color: bool,
218+ pub output_wrap: OutputWrap,
200219 pub output_components: OutputComponents,
201220 pub language: Option<&'a str>,
202221 pub colored_output: bool,
src/decorations.rsnew file+154
@@ -0,0 +1,154 @@
1+use ansi_term::Style;
2+use diff::LineChange;
3+use printer::Printer;
4+use Colors;
5+
6+#[derive(Clone)]
7+pub struct DecorationText {
8+ pub width: usize,
9+ pub text: String,
10+}
11+
12+pub trait Decoration {
13+ fn generate(&self, line_number: usize, continuation: bool, printer: &Printer)
14+ -> DecorationText;
15+ fn width(&self) -> usize;
16+}
17+
18+// Line number decoration.
19+pub struct LineNumberDecoration {
20+ color: Style,
21+ cached_wrap: DecorationText,
22+ cached_wrap_invalid_at: usize,
23+}
24+
25+impl LineNumberDecoration {
26+ pub fn new(colors: &Colors) -> Self {
27+ LineNumberDecoration {
28+ color: colors.line_number,
29+ cached_wrap_invalid_at: 10000,
30+ cached_wrap: DecorationText {
31+ text: colors.line_number.paint(" ".repeat(4)).to_string(),
32+ width: 4,
33+ },
34+ }
35+ }
36+}
37+
38+impl Decoration for LineNumberDecoration {
39+ fn generate(
40+ &self,
41+ line_number: usize,
42+ continuation: bool,
43+ _printer: &Printer,
44+ ) -> DecorationText {
45+ if continuation {
46+ if line_number > self.cached_wrap_invalid_at {
47+ let new_width = self.cached_wrap.width + 1;
48+ return DecorationText {
49+ text: self.color.paint(" ".repeat(new_width)).to_string(),
50+ width: new_width,
51+ };
52+ }
53+
54+ self.cached_wrap.clone()
55+ } else {
56+ let plain: String = format!("{:4}", line_number);
57+ DecorationText {
58+ width: plain.len(),
59+ text: self.color.paint(plain).to_string(),
60+ }
61+ }
62+ }
63+
64+ fn width(&self) -> usize {
65+ 4
66+ }
67+}
68+
69+// Line changes decoration.
70+pub struct LineChangesDecoration {
71+ cached_none: DecorationText,
72+ cached_added: DecorationText,
73+ cached_removed_above: DecorationText,
74+ cached_removed_below: DecorationText,
75+ cached_modified: DecorationText,
76+}
77+
78+impl LineChangesDecoration {
79+ #[inline]
80+ fn generate_cached(style: Style, text: &str) -> DecorationText {
81+ DecorationText {
82+ text: style.paint(text).to_string(),
83+ width: text.chars().count(),
84+ }
85+ }
86+
87+ pub fn new(colors: &Colors) -> Self {
88+ LineChangesDecoration {
89+ cached_none: Self::generate_cached(Style::default(), " "),
90+ cached_added: Self::generate_cached(colors.git_added, "+"),
91+ cached_removed_above: Self::generate_cached(colors.git_removed, "‾"),
92+ cached_removed_below: Self::generate_cached(colors.git_removed, "_"),
93+ cached_modified: Self::generate_cached(colors.git_modified, "~"),
94+ }
95+ }
96+}
97+
98+impl Decoration for LineChangesDecoration {
99+ fn generate(
100+ &self,
101+ line_number: usize,
102+ continuation: bool,
103+ printer: &Printer,
104+ ) -> DecorationText {
105+ if !continuation {
106+ if let Some(ref changes) = printer.line_changes {
107+ return match changes.get(&(line_number as u32)) {
108+ Some(&LineChange::Added) => self.cached_added.clone(),
109+ Some(&LineChange::RemovedAbove) => self.cached_removed_above.clone(),
110+ Some(&LineChange::RemovedBelow) => self.cached_removed_below.clone(),
111+ Some(&LineChange::Modified) => self.cached_modified.clone(),
112+ _ => self.cached_none.clone(),
113+ };
114+ }
115+ }
116+
117+ self.cached_none.clone()
118+ }
119+
120+ fn width(&self) -> usize {
121+ self.cached_none.width
122+ }
123+}
124+
125+// Grid border decoration.
126+pub struct GridBorderDecoration {
127+ cached: DecorationText,
128+}
129+
130+impl GridBorderDecoration {
131+ pub fn new(colors: &Colors) -> Self {
132+ GridBorderDecoration {
133+ cached: DecorationText {
134+ text: colors.grid.paint("│").to_string(),
135+ width: 1,
136+ },
137+ }
138+ }
139+}
140+
141+impl Decoration for GridBorderDecoration {
142+ fn generate(
143+ &self,
144+ _line_number: usize,
145+ _continuation: bool,
146+ _printer: &Printer,
147+ ) -> DecorationText {
148+ self.cached.clone()
149+ }
150+
151+ fn width(&self) -> usize {
152+ self.cached.width
153+ }
154+}
src/main.rs+1
@@ -19,6 +19,7 @@ extern crate syntect;
1919
2020mod app;
2121mod assets;
22+mod decorations;
2223mod diff;
2324mod printer;
2425mod style;
src/printer.rs+151−75
@@ -1,18 +1,21 @@
1−use ansi_term::Style;
21use app::Config;
3−use diff::{LineChange, LineChanges};
2+use decorations::{Decoration, GridBorderDecoration, LineChangesDecoration, LineNumberDecoration};
3+use diff::LineChanges;
44use errors::*;
5+use std::boxed::Box;
56use std::io::Write;
7+use std::vec::Vec;
8+use style::OutputWrap;
69use syntect::highlighting;
710use terminal::as_terminal_escaped;
811use Colors;
912
10−const PANEL_WIDTH: usize = 7;
11−
1213pub struct Printer<'a> {
1314 handle: &'a mut Write,
1415 colors: Colors,
1516 config: &'a Config<'a>,
17+ decorations: Vec<Box<Decoration>>,
18+ panel_width: usize,
1619 pub line_changes: Option<LineChanges>,
1720}
1821
@@ -24,10 +27,43 @@ impl<'a> Printer<'a> {
2427 Colors::plain()
2528 };
2629
30+ // Create decorations.
31+ let mut decorations: Vec<Box<Decoration>> = Vec::new();
32+
33+ if config.output_components.numbers() {
34+ decorations.push(Box::new(LineNumberDecoration::new(&colors)));
35+ }
36+
37+ if config.output_components.changes() {
38+ decorations.push(Box::new(LineChangesDecoration::new(&colors)));
39+ }
40+
41+ let mut panel_width: usize =
42+ decorations.len() + decorations.iter().fold(0, |a, x| a + x.width());
43+
44+ // The grid border decoration isn't added until after the panel_width calculation, since the
45+ // print_horizontal_line, print_header, and print_footer functions all assume the panel
46+ // width is without the grid border.
47+ if config.output_components.grid() && decorations.len() > 0 {
48+ decorations.push(Box::new(GridBorderDecoration::new(&colors)));
49+ }
50+
51+ // Disable the panel if the terminal is too small (i.e. can't fit 5 characters with the
52+ // panel showing).
53+ if config.term_width
54+ < (decorations.len() + decorations.iter().fold(0, |a, x| a + x.width())) + 5
55+ {
56+ decorations.clear();
57+ panel_width = 0;
58+ }
59+
60+ // Create printer.
2761 Printer {
62+ panel_width,
2863 handle,
2964 colors,
3065 config,
66+ decorations,
3167 line_changes: None,
3268 }
3369 }
@@ -43,8 +79,10 @@ impl<'a> Printer<'a> {
4379 write!(
4480 self.handle,
4581 "{}{} ",
46− " ".repeat(PANEL_WIDTH),
47− self.colors.grid.paint("│"),
82+ " ".repeat(self.panel_width),
83+ self.colors
84+ .grid
85+ .paint(if self.panel_width > 0 { "│" } else { "" }),
4886 )?;
4987 }
5088
@@ -75,85 +113,123 @@ impl<'a> Printer<'a> {
75113 line_number: usize,
76114 regions: &[(highlighting::Style, &str)],
77115 ) -> Result<()> {
78− let decorations = vec![
79− self.print_line_number(line_number),
80− self.print_git_marker(line_number),
81− self.print_line_border(),
82− Some(as_terminal_escaped(
83− &regions,
84− self.config.true_color,
85− self.config.colored_output,
86− )),
87− ];
88−
89− let grid_requested = self.config.output_components.grid();
90− write!(
91− self.handle,
92− "{}",
93− decorations
94− .into_iter()
95− .filter_map(|dec| if grid_requested {
96− Some(dec.unwrap_or_else(|| " ".to_owned()))
97− } else {
98− dec
99− })
100− .collect::<Vec<_>>()
101− .join(" ")
102− )?;
116+ let mut cursor: usize = 0;
117+ let mut cursor_max: usize = self.config.term_width;
118+ let mut panel_wrap: Option<String> = None;
119+
120+ // Line decorations.
121+ if self.panel_width > 0 {
122+ let decorations = self
123+ .decorations
124+ .iter()
125+ .map(|ref d| d.generate(line_number, false, self))
126+ .collect::<Vec<_>>();
127+
128+ for deco in decorations {
129+ write!(self.handle, "{} ", deco.text)?;
130+ cursor_max -= deco.width + 1;
131+ }
132+ }
103133
104− Ok(())
105− }
134+ // Line contents.
135+ if self.config.output_wrap == OutputWrap::None {
136+ let true_color = self.config.true_color;
137+ let colored_output = self.config.colored_output;
106138
107− fn print_line_number(&self, line_number: usize) -> Option<String> {
108− if self.config.output_components.numbers() {
109− Some(
110− self.colors
111− .line_number
112− .paint(format!("{:4}", line_number))
113− .to_string(),
114− )
115− } else if self.config.output_components.grid() {
116− Some(" ".to_owned())
139+ write!(
140+ self.handle,
141+ "{}",
142+ regions
143+ .iter()
144+ .map(|&(style, text)| as_terminal_escaped(
145+ style,
146+ text,
147+ true_color,
148+ colored_output,
149+ ))
150+ .collect::<Vec<_>>()
151+ .join("")
152+ )?;
117153 } else {
118− None
119− }
120− }
154+ for &(style, text) in regions.iter() {
155+ let text = text.trim_right_matches(|c| c == '\r' || c == '\n');
156+ let mut chars = text.chars();
157+ let mut remaining = text.chars().count();
158+
159+ while remaining > 0 {
160+ let available = cursor_max - cursor;
161+
162+ // It fits.
163+ if remaining <= available {
164+ let text = chars.by_ref().take(remaining).collect::<String>();
165+ cursor += remaining;
166+
167+ write!(
168+ self.handle,
169+ "{}",
170+ as_terminal_escaped(
171+ style,
172+ &*text,
173+ self.config.true_color,
174+ self.config.colored_output,
175+ )
176+ )?;
177+ break;
178+ }
121179
122− fn print_git_marker(&self, line_number: usize) -> Option<String> {
123− if self.config.output_components.changes() {
124− Some(
125− if let Some(ref changes) = self.line_changes {
126− match changes.get(&(line_number as u32)) {
127− Some(&LineChange::Added) => self.colors.git_added.paint("+"),
128− Some(&LineChange::RemovedAbove) => self.colors.git_removed.paint("‾"),
129− Some(&LineChange::RemovedBelow) => self.colors.git_removed.paint("_"),
130− Some(&LineChange::Modified) => self.colors.git_modified.paint("~"),
131− _ => Style::default().paint(" "),
180+ // Generate wrap padding if not already generated.
181+ if panel_wrap.is_none() {
182+ panel_wrap = if self.panel_width > 0 {
183+ Some(format!(
184+ "{} ",
185+ self.decorations
186+ .iter()
187+ .map(|ref d| d.generate(line_number, true, self).text)
188+ .collect::<Vec<String>>()
189+ .join(" ")
190+ ))
191+ } else {
192+ Some("".to_string())
193+ }
132194 }
133− } else {
134− Style::default().paint(" ")
135− }.to_string(),
136− )
137− } else if self.config.output_components.grid() {
138− Some(" ".to_owned())
139− } else {
140− None
141− }
142− }
143195
144− fn print_line_border(&self) -> Option<String> {
145− if self.config.output_components.grid() {
146− Some(self.colors.grid.paint("│").to_string())
147− } else {
148− None
196+ // It wraps.
197+ let text = chars.by_ref().take(available).collect::<String>();
198+ cursor = 0;
199+ remaining -= available;
200+
201+ write!(
202+ self.handle,
203+ "{}\n{}",
204+ as_terminal_escaped(
205+ style,
206+ &*text,
207+ self.config.true_color,
208+ self.config.colored_output,
209+ ),
210+ panel_wrap.clone().unwrap()
211+ )?;
212+ }
213+ }
214+
215+ write!(self.handle, "\n")?;
149216 }
217+
218+ Ok(())
150219 }
151220
152221 fn print_horizontal_line(&mut self, grid_char: char) -> Result<()> {
153− let hline = "─".repeat(self.config.term_width - (PANEL_WIDTH + 1));
154− let hline = format!("{}{}{}", "─".repeat(PANEL_WIDTH), grid_char, hline);
155−
156− writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
222+ if self.panel_width == 0 {
223+ writeln!(
224+ self.handle,
225+ "{}",
226+ self.colors.grid.paint("─".repeat(self.config.term_width))
227+ )?;
228+ } else {
229+ let hline = "─".repeat(self.config.term_width - (self.panel_width + 1));
230+ let hline = format!("{}{}{}", "─".repeat(self.panel_width), grid_char, hline);
231+ writeln!(self.handle, "{}", self.colors.grid.paint(hline))?;
232+ }
157233
158234 Ok(())
159235 }
src/style.rs+6
@@ -13,6 +13,12 @@ pub enum OutputComponent {
1313 Plain,
1414}
1515
16+#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
17+pub enum OutputWrap {
18+ Character,
19+ None,
20+}
21+
1622impl OutputComponent {
1723 pub fn components(&self, interactive_terminal: bool) -> &'static [OutputComponent] {
1824 match *self {
src/terminal.rs+20−26
@@ -1,5 +1,3 @@
1−use std::fmt::Write;
2−
31use ansi_term::Colour::{Fixed, RGB};
42use ansi_term::Style;
53use syntect::highlighting::{self, FontStyle};
@@ -27,37 +25,33 @@ fn rgb2ansi(r: u8, g: u8, b: u8) -> u8 {
2725}
2826
2927pub fn as_terminal_escaped(
30− v: &[(highlighting::Style, &str)],
28+ style: highlighting::Style,
29+ text: &str,
3130 true_color: bool,
3231 colored: bool,
3332) -> String {
34− let mut s: String = String::new();
35− for &(ref style, text) in v.iter() {
36− let style = if !colored {
37− Style::default()
33+ let style = if !colored {
34+ Style::default()
35+ } else {
36+ let color = if true_color {
37+ RGB(style.foreground.r, style.foreground.g, style.foreground.b)
3838 } else {
39− let color = if true_color {
40− RGB(style.foreground.r, style.foreground.g, style.foreground.b)
41− } else {
42− let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b);
43− Fixed(ansi)
44− };
45−
46− if style.font_style.contains(FontStyle::BOLD) {
47− color.bold()
48− } else if style.font_style.contains(FontStyle::UNDERLINE) {
49− color.underline()
50− } else if style.font_style.contains(FontStyle::ITALIC) {
51− color.italic()
52− } else {
53− color.normal()
54− }
39+ let ansi = rgb2ansi(style.foreground.r, style.foreground.g, style.foreground.b);
40+ Fixed(ansi)
5541 };
5642
57− write!(s, "{}", style.paint(text)).unwrap();
58− }
43+ if style.font_style.contains(FontStyle::BOLD) {
44+ color.bold()
45+ } else if style.font_style.contains(FontStyle::UNDERLINE) {
46+ color.underline()
47+ } else if style.font_style.contains(FontStyle::ITALIC) {
48+ color.italic()
49+ } else {
50+ color.normal()
51+ }
52+ };
5953
60− s
54+ style.paint(text).to_string()
6155}
6256
6357#[test]
tests/snapshots/output/changes,grid,header.snapshot.txt+25−25
@@ -1,25 +1,25 @@
1−───────┬────────────────────────────────────────────────────────────────────────
2− │ File: sample.rs
3−───────┼────────────────────────────────────────────────────────────────────────
4− │ struct Rectangle {
5− │ width: u32,
6− │ height: u32,
7− │ }
8− │
9− _ │ fn main() {
10− │ let rect1 = Rectangle { width: 30, height: 50 };
11− │
12− │ println!(
13− ~ │ "The perimeter of the rectangle is {} pixels.",
14− ~ │ perimeter(&rect1)
15− │ );
16− │ }
17− │
18− │ fn area(rectangle: &Rectangle) -> u32 {
19− │ rectangle.width * rectangle.height
20− │ }
21− + │
22− + │ fn perimeter(rectangle: &Rectangle) -> u32 {
23− + │ (rectangle.width + rectangle.height) * 2
24− + │ }
25−───────┴────────────────────────────────────────────────────────────────────────
1+──┬─────────────────────────────────────────────────────────────────────────────
2+ │ File: sample.rs
3+──┼─────────────────────────────────────────────────────────────────────────────
4+ │ struct Rectangle {
5+ │ width: u32,
6+ │ height: u32,
7+ │ }
8+ │
9+_ │ fn main() {
10+ │ let rect1 = Rectangle { width: 30, height: 50 };
11+ │
12+ │ println!(
13+~ │ "The perimeter of the rectangle is {} pixels.",
14+~ │ perimeter(&rect1)
15+ │ );
16+ │ }
17+ │
18+ │ fn area(rectangle: &Rectangle) -> u32 {
19+ │ rectangle.width * rectangle.height
20+ │ }
21++ │
22++ │ fn perimeter(rectangle: &Rectangle) -> u32 {
23++ │ (rectangle.width + rectangle.height) * 2
24++ │ }
25+──┴─────────────────────────────────────────────────────────────────────────────
tests/snapshots/output/changes,grid.snapshot.txt+22−22
@@ -1,22 +1,22 @@
1− │ struct Rectangle {
2− │ width: u32,
3− │ height: u32,
4− │ }
5− │
6− _ │ fn main() {
7− │ let rect1 = Rectangle { width: 30, height: 50 };
8− │
9− │ println!(
10− ~ │ "The perimeter of the rectangle is {} pixels.",
11− ~ │ perimeter(&rect1)
12− │ );
13− │ }
14− │
15− │ fn area(rectangle: &Rectangle) -> u32 {
16− │ rectangle.width * rectangle.height
17− │ }
18− + │
19− + │ fn perimeter(rectangle: &Rectangle) -> u32 {
20− + │ (rectangle.width + rectangle.height) * 2
21− + │ }
22−───────┴────────────────────────────────────────────────────────────────────────
1+ │ struct Rectangle {
2+ │ width: u32,
3+ │ height: u32,
4+ │ }
5+ │
6+_ │ fn main() {
7+ │ let rect1 = Rectangle { width: 30, height: 50 };
8+ │
9+ │ println!(
10+~ │ "The perimeter of the rectangle is {} pixels.",
11+~ │ perimeter(&rect1)
12+ │ );
13+ │ }
14+ │
15+ │ fn area(rectangle: &Rectangle) -> u32 {
16+ │ rectangle.width * rectangle.height
17+ │ }
18++ │
19++ │ fn perimeter(rectangle: &Rectangle) -> u32 {
20++ │ (rectangle.width + rectangle.height) * 2
21++ │ }
22+──┴─────────────────────────────────────────────────────────────────────────────
tests/snapshots/output/grid,header,numbers.snapshot.txt+25−25
@@ -1,25 +1,25 @@
1−───────┬────────────────────────────────────────────────────────────────────────
2− │ File: sample.rs
3−───────┼────────────────────────────────────────────────────────────────────────
4− 1 │ struct Rectangle {
5− 2 │ width: u32,
6− 3 │ height: u32,
7− 4 │ }
8− 5 │
9− 6 │ fn main() {
10− 7 │ let rect1 = Rectangle { width: 30, height: 50 };
11− 8 │
12− 9 │ println!(
13− 10 │ "The perimeter of the rectangle is {} pixels.",
14− 11 │ perimeter(&rect1)
15− 12 │ );
16− 13 │ }
17− 14 │
18− 15 │ fn area(rectangle: &Rectangle) -> u32 {
19− 16 │ rectangle.width * rectangle.height
20− 17 │ }
21− 18 │
22− 19 │ fn perimeter(rectangle: &Rectangle) -> u32 {
23− 20 │ (rectangle.width + rectangle.height) * 2
24− 21 │ }
25−───────┴────────────────────────────────────────────────────────────────────────
1+─────┬──────────────────────────────────────────────────────────────────────────
2+ │ File: sample.rs
3+─────┼──────────────────────────────────────────────────────────────────────────
4+ 1 │ struct Rectangle {
5+ 2 │ width: u32,
6+ 3 │ height: u32,
7+ 4 │ }
8+ 5 │
9+ 6 │ fn main() {
10+ 7 │ let rect1 = Rectangle { width: 30, height: 50 };
11+ 8 │
12+ 9 │ println!(
13+ 10 │ "The perimeter of the rectangle is {} pixels.",
14+ 11 │ perimeter(&rect1)
15+ 12 │ );
16+ 13 │ }
17+ 14 │
18+ 15 │ fn area(rectangle: &Rectangle) -> u32 {
19+ 16 │ rectangle.width * rectangle.height
20+ 17 │ }
21+ 18 │
22+ 19 │ fn perimeter(rectangle: &Rectangle) -> u32 {
23+ 20 │ (rectangle.width + rectangle.height) * 2
24+ 21 │ }
25+─────┴──────────────────────────────────────────────────────────────────────────
tests/snapshots/output/grid,header.snapshot.txt+25−25
@@ -1,25 +1,25 @@
1−───────┬────────────────────────────────────────────────────────────────────────
2− │ File: sample.rs
3−───────┼────────────────────────────────────────────────────────────────────────
4− │ struct Rectangle {
5− │ width: u32,
6− │ height: u32,
7− │ }
8− │
9− │ fn main() {
10− │ let rect1 = Rectangle { width: 30, height: 50 };
11− │
12− │ println!(
13− │ "The perimeter of the rectangle is {} pixels.",
14− │ perimeter(&rect1)
15− │ );
16− │ }
17− │
18− │ fn area(rectangle: &Rectangle) -> u32 {
19− │ rectangle.width * rectangle.height
20− │ }
21− │
22− │ fn perimeter(rectangle: &Rectangle) -> u32 {
23− │ (rectangle.width + rectangle.height) * 2
24− │ }
25−───────┴────────────────────────────────────────────────────────────────────────
1+────────────────────────────────────────────────────────────────────────────────
2+ File: sample.rs
3+────────────────────────────────────────────────────────────────────────────────
4+struct Rectangle {
5+ width: u32,
6+ height: u32,
7+}
8+
9+fn main() {
10+ let rect1 = Rectangle { width: 30, height: 50 };
11+
12+ println!(
13+ "The perimeter of the rectangle is {} pixels.",
14+ perimeter(&rect1)
15+ );
16+}
17+
18+fn area(rectangle: &Rectangle) -> u32 {
19+ rectangle.width * rectangle.height
20+}
21+
22+fn perimeter(rectangle: &Rectangle) -> u32 {
23+ (rectangle.width + rectangle.height) * 2
24+}
25+────────────────────────────────────────────────────────────────────────────────
tests/snapshots/output/grid,numbers.snapshot.txt+22−22
@@ -1,22 +1,22 @@
1− 1 │ struct Rectangle {
2− 2 │ width: u32,
3− 3 │ height: u32,
4− 4 │ }
5− 5 │
6− 6 │ fn main() {
7− 7 │ let rect1 = Rectangle { width: 30, height: 50 };
8− 8 │
9− 9 │ println!(
10− 10 │ "The perimeter of the rectangle is {} pixels.",
11− 11 │ perimeter(&rect1)
12− 12 │ );
13− 13 │ }
14− 14 │
15− 15 │ fn area(rectangle: &Rectangle) -> u32 {
16− 16 │ rectangle.width * rectangle.height
17− 17 │ }
18− 18 │
19− 19 │ fn perimeter(rectangle: &Rectangle) -> u32 {
20− 20 │ (rectangle.width + rectangle.height) * 2
21− 21 │ }
22−───────┴────────────────────────────────────────────────────────────────────────
1+ 1 │ struct Rectangle {
2+ 2 │ width: u32,
3+ 3 │ height: u32,
4+ 4 │ }
5+ 5 │
6+ 6 │ fn main() {
7+ 7 │ let rect1 = Rectangle { width: 30, height: 50 };
8+ 8 │
9+ 9 │ println!(
10+ 10 │ "The perimeter of the rectangle is {} pixels.",
11+ 11 │ perimeter(&rect1)
12+ 12 │ );
13+ 13 │ }
14+ 14 │
15+ 15 │ fn area(rectangle: &Rectangle) -> u32 {
16+ 16 │ rectangle.width * rectangle.height
17+ 17 │ }
18+ 18 │
19+ 19 │ fn perimeter(rectangle: &Rectangle) -> u32 {
20+ 20 │ (rectangle.width + rectangle.height) * 2
21+ 21 │ }
22+─────┴──────────────────────────────────────────────────────────────────────────
tests/snapshots/output/grid.snapshot.txt+22−22
@@ -1,22 +1,22 @@
1− │ struct Rectangle {
2− │ width: u32,
3− │ height: u32,
4− │ }
5− │
6− │ fn main() {
7− │ let rect1 = Rectangle { width: 30, height: 50 };
8− │
9− │ println!(
10− │ "The perimeter of the rectangle is {} pixels.",
11− │ perimeter(&rect1)
12− │ );
13− │ }
14− │
15− │ fn area(rectangle: &Rectangle) -> u32 {
16− │ rectangle.width * rectangle.height
17− │ }
18− │
19− │ fn perimeter(rectangle: &Rectangle) -> u32 {
20− │ (rectangle.width + rectangle.height) * 2
21− │ }
22−───────┴────────────────────────────────────────────────────────────────────────
1+struct Rectangle {
2+ width: u32,
3+ height: u32,
4+}
5+
6+fn main() {
7+ let rect1 = Rectangle { width: 30, height: 50 };
8+
9+ println!(
10+ "The perimeter of the rectangle is {} pixels.",
11+ perimeter(&rect1)
12+ );
13+}
14+
15+fn area(rectangle: &Rectangle) -> u32 {
16+ rectangle.width * rectangle.height
17+}
18+
19+fn perimeter(rectangle: &Rectangle) -> u32 {
20+ (rectangle.width + rectangle.height) * 2
21+}
22+────────────────────────────────────────────────────────────────────────────────