Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for columns #205

Closed
wucke13 opened this issue Sep 15, 2020 · 6 comments · Fixed by #282
Closed

Support for columns #205

wucke13 opened this issue Sep 15, 2020 · 6 comments · Fixed by #282

Comments

@wucke13
Copy link

wucke13 commented Sep 15, 2020

Hi, I would love to be able to wrap a text into mutiple columns.

Something like this quick draft could be the API for it.

pub fn columns (s: &str, columns: usize, width: usize, outer_gap: usize, inner_gap: usize );
@mgeisler
Copy link
Owner

Hi @wucke13! Oh, that's a cool idea!

I guess the function should return a String? A string with the text already formatted into the columns? So if your text above is wrapped into 12 columns, it should return a string looking like this:

  Hi, I would   │  Something
  love to be    │  like this
  able to wrap  │  quick draft
  a text into   │  could be the
  mutiple       │  API for it.
  columns.      │

@wucke13
Copy link
Author

wucke13 commented Sep 17, 2020

Yes! No, your example is what I would expect from two columns.

I think the interspace between the columns should be an &str Argument. That users can easily decide what type of seperation they desire (just spaces, pipes, UTF-8 magic, you name it). The space before the first column and after the last one maybe should be configurable as well? Other than that, I think only the maximum width and the ammount of columns must be configurable.

So, new API proposal

fn columns(
    text: &str,
    columns: usize,
    total_width: usize,
    left_gap: &str,
    middle_gaps: &str,
    right_gap: &str,
)->String;

Is it clear what I mean?

Also: I already wrote code for a specific use case of this: Formatting a HashMap<String, String> to multiple columns. The code is close to what we need for this, but not quite the same. Consider the following a release to the public domain:

let max_key_length = mapping
    .iter()
    .map(|(k, _)| k.to_string().len())
    .max()
    .unwrap_or(0);
let max_value_length = mapping.iter().map(|(_, v)| v.len()).max().unwrap_or(0);
let joint = " = ";
let cols = width / (max_key_length + max_value_length + joint.len() + 1);
let mut rows = mapping.len() / cols;
if mapping.len() % cols != 0 {
    rows += 1;
}
                                                                                
(0..rows)
    .map(|initial_offset| {
        mapping
            .iter()
            .skip(initial_offset)
            .step_by(rows)
            .enumerate()
            .map(|(i, (k, v))| {
                format!(
                    "{}{:k_width$}{}{:^v_width$}",
                    if i % cols == 0 { '\n' } else { ' ' },
                    k,
                    joint,
                    v,
                    k_width = max_key_length,
                    v_width = max_value_length
                )
            })
    })
    .flatten()
    .collect()

@mgeisler
Copy link
Owner

Hi @wucke13 — I'm sorry, I forgot to reply back to you!

Yes, I think it's pretty clear what you mean and I think your function signature is great! I like the general &str arguments for the gaps. I would call it wrap_columns to clearly relate it to the existing wrap function.

The way I generated the example above was to wrap the text at 12 columns and then simply copy-paste the wrapped lines from the second half after the first half.

So I think it should be possible to implement this by first wrapping the text using the column with given (so total_width: usize should actually be options: WrapOptions now, just like wrap). Then collect the wrapped lines into a Vec<Cow<'_, str>>, count the number of lines finally rearrange the lines. I don't know if your HashMap<String, String> code above does something similar?

Here is a sketch of what I have in mind:

let wrapped_lines = wrap(text, options).collect::<Vec<_>>();
let lines_per_column = wrapped_lines.len() / columns; // Need to round this up 
let columns = Vec::new();
for line_no in 0..lines_per_column {
    let mut line = String::from(left_gap);
    for column_no in 0..columns {
        line.push_str(lines[line_no + column_no * lines_per_column]);
        if column_no < columns - 1 {
            line.push_str(mid_gap);
        }
    }
    line.push_str(right_gap);
    columns.push(line);
}

There are certainly rounding errors in this 😄 Also, it just occured to me that the lines would need padding, just like you do above with format!...

Would you be up for implementing this? It would need some documentation, tests, etc...

@wucke13
Copy link
Author

wucke13 commented Oct 26, 2020

Hi, currently I've got a somewhat hefty workload, so I don't want to make any promises. But I just put in on my todo list, let's see whether I will come up with results in the next couple of weeks.

@mgeisler
Copy link
Owner

That's perfectly fine, no promises needed! I'll simply let the issue be open in case you find time 😄

mgeisler added a commit that referenced this issue Jan 24, 2021
This is a little utility function which shows how to wrap text into
columns.

Fixes #205.
mgeisler added a commit that referenced this issue Jan 24, 2021
This is a little utility function which shows how to wrap text into
columns.

Fixes #205.
mgeisler added a commit that referenced this issue Jan 24, 2021
This is a little utility function which shows how to wrap text into
columns.

Fixes #205.
@mgeisler
Copy link
Owner

I went ahead and wrote a little column wrapping function, see #282.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants