1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use utils::make_indent;
13 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
17 // All items on one row.
19 // Try Horizontal layout, if that fails then vertical
21 // Pack as many items as possible per row over (possibly) many rows.
25 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
26 pub enum SeparatorTactic {
32 // TODO having some helpful ctors for ListFormatting would be nice.
33 pub struct ListFormatting<'a> {
34 pub tactic: ListTactic,
35 pub separator: &'a str,
36 pub trailing_separator: SeparatorTactic,
38 // Available width if we layout horizontally.
40 // Available width if we layout vertically
44 // Format a list of strings into a string.
45 // Precondition: all strings in items are trimmed.
46 pub fn write_list<'b>(items: &[(String, String)], formatting: &ListFormatting<'b>) -> String {
51 let mut tactic = formatting.tactic;
53 // Conservatively overestimates because of the changing separator tactic.
54 let sep_count = if formatting.trailing_separator != SeparatorTactic::Never {
59 let sep_len = formatting.separator.len();
60 let total_sep_len = (sep_len + 1) * sep_count;
61 let total_width = calculate_width(items);
62 let fits_single = total_width + total_sep_len <= formatting.h_width;
64 // Check if we need to fallback from horizontal listing, if possible.
65 if tactic == ListTactic::HorizontalVertical {
66 debug!("write_list: total_width: {}, total_sep_len: {}, h_width: {}",
67 total_width, total_sep_len, formatting.h_width);
68 tactic = if fits_single {
69 ListTactic::Horizontal
75 // Check if we can fit everything on a single line in mixed mode.
76 // The horizontal tactic does not break after v_width columns.
77 if tactic == ListTactic::Mixed && fits_single {
78 tactic = ListTactic::Horizontal;
81 // Now that we know how we will layout, we can decide for sure if there
82 // will be a trailing separator.
83 let trailing_separator = needs_trailing_separator(formatting.trailing_separator, tactic);
85 // Create a buffer for the result.
86 // TODO could use a StringBuffer or rope for this
87 let alloc_width = if tactic == ListTactic::Horizontal {
88 total_width + total_sep_len
90 total_width + items.len() * (formatting.indent + 1)
92 let mut result = String::with_capacity(alloc_width);
95 let indent_str = &make_indent(formatting.indent);
96 for (i, &(ref item, ref comment)) in items.iter().enumerate() {
98 let separate = i != items.len() - 1 || trailing_separator;
101 ListTactic::Horizontal if !first => {
104 ListTactic::Vertical if !first => {
106 result.push_str(indent_str);
108 ListTactic::Mixed => {
109 let mut item_width = item.len();
111 item_width += sep_len;
114 if line_len > 0 && line_len + item_width > formatting.v_width {
116 result.push_str(indent_str);
125 line_len += item_width;
130 result.push_str(item);
132 if tactic != ListTactic::Vertical && comment.len() > 0 {
133 if !comment.starts_with('\n') {
136 result.push_str(comment);
140 result.push_str(formatting.separator);
143 if tactic == ListTactic::Vertical && comment.len() > 0 {
144 if !comment.starts_with('\n') {
147 result.push_str(comment);
154 fn needs_trailing_separator(separator_tactic: SeparatorTactic, list_tactic: ListTactic) -> bool {
155 match separator_tactic {
156 SeparatorTactic::Always => true,
157 SeparatorTactic::Vertical => list_tactic == ListTactic::Vertical,
158 SeparatorTactic::Never => false,
162 fn calculate_width(items:&[(String, String)]) -> usize {
163 let missed_width = items.iter().map(|&(_, ref s)| {
164 let text_len = s.trim().len();
166 // We'll put a space before any comment.
171 }).fold(0, |a, l| a + l);
172 let item_width = items.iter().map(|&(ref s, _)| s.len()).fold(0, |a, l| a + l);
173 missed_width + item_width