From f799d22b7d7f83fd5f0608d12bb7eb7e6ec7d4c6 Mon Sep 17 00:00:00 2001 From: tilakpatidar Date: Sat, 15 Dec 2018 09:18:41 +0530 Subject: [PATCH] pr: add multi column printing --- src/pr/pr.rs | 128 ++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 92 insertions(+), 36 deletions(-) diff --git a/src/pr/pr.rs b/src/pr/pr.rs index 403fbc3bb..c7a833402 100644 --- a/src/pr/pr.rs +++ b/src/pr/pr.rs @@ -67,7 +67,7 @@ struct OutputOptions { } struct ColumnModeOptions { - width: usize, + width: Option, columns: usize, column_separator: String, } @@ -78,6 +78,25 @@ impl AsRef for OutputOptions { } } +impl OutputOptions { + fn get_columns(&self) -> usize { + self.as_ref() + .column_mode_options.as_ref() + .map(|i| i.columns) + .unwrap_or(1) + } + + fn lines_to_read_for_page(&self) -> usize { + let content_lines_per_page = &self.as_ref().content_lines_per_page; + let columns = self.get_columns(); + if self.as_ref().double_space { + (content_lines_per_page / 2) * columns + } else { + content_lines_per_page * columns + } + } +} + struct NumberingMode { /// Line numbering mode width: usize, @@ -393,13 +412,13 @@ fn build_options(matches: &Matches, header: &String, path: &String) -> Result()) { - Some(res) => res?, - _ => DEFAULT_COLUMN_WIDTH + Some(res) => Some(res?), + _ => None }; let column_mode_options = match matches.opt_str(COLUMN_OPTION).map(|i| { @@ -408,7 +427,10 @@ fn build_options(matches: &Matches, header: &String, path: &String) -> Result { Some(ColumnModeOptions { columns: res?, - width: column_width, + width: match column_width { + Some(x) => Some(x), + None => Some(DEFAULT_COLUMN_WIDTH) + }, column_separator: DEFAULT_COLUMN_SEPARATOR.to_string(), }) } @@ -452,21 +474,16 @@ fn pr(path: &str, options: &OutputOptions) -> Result { let mut i = 0; let mut page: usize = 0; let mut buffered_content: Vec = Vec::new(); - let content_lines_per_page = options.as_ref().content_lines_per_page; - let columns = _get_columns(options); - let lines_per_page = if options.as_ref().double_space { - (content_lines_per_page / 2) * columns - } else { - content_lines_per_page * columns - }; + let read_lines_per_page = options.lines_to_read_for_page(); + let mut line_number = 0; for line in BufReader::with_capacity(READ_BUFFER_SIZE, open(path)?).lines() { - if i == lines_per_page { + if i == read_lines_per_page { page = page + 1; i = 0; if !_is_within_page_range(options, &page) { return Ok(0); } - print_page(&buffered_content, options, &page)?; + line_number += print_page(&buffered_content, options, &page, &line_number)?; buffered_content = Vec::new(); } buffered_content.push(line?); @@ -478,39 +495,33 @@ fn pr(path: &str, options: &OutputOptions) -> Result { if !_is_within_page_range(options, &page) { return Ok(0); } - print_page(&buffered_content, options, &page)?; + print_page(&buffered_content, options, &page, &line_number)?; } return Ok(0); } -fn _get_columns(options: &OutputOptions) -> usize { - options.as_ref().column_mode_options.as_ref().map(|i| i.columns).unwrap_or(1) -} - fn _is_within_page_range(options: &OutputOptions, page: &usize) -> bool { let start_page = options.as_ref().start_page.as_ref(); let last_page = options.as_ref().end_page.as_ref(); (start_page.is_none() || page >= start_page.unwrap()) && (last_page.is_none() || page <= last_page.unwrap()) } -fn print_page(lines: &Vec, options: &OutputOptions, page: &usize) -> Result { +fn print_page(lines: &Vec, options: &OutputOptions, page: &usize, line_number: &usize) -> Result { let page_separator = options.as_ref().page_separator_char.as_bytes(); let header: Vec = header_content(options, page); let trailer_content: Vec = trailer_content(options); let out: &mut Stdout = &mut stdout(); let line_separator = options.as_ref().line_separator.as_bytes(); - let mut lines_written = 0; out.lock(); for x in header { out.write(x.as_bytes())?; out.write(line_separator)?; - lines_written += 1; } - lines_written += write_columns(lines, options, page_separator, out, line_separator, page)?; + let lines_written = write_columns(lines, options, out, line_number)?; for index in 0..trailer_content.len() { let x: &String = trailer_content.get(index).unwrap(); @@ -520,43 +531,88 @@ fn print_page(lines: &Vec, options: &OutputOptions, page: &usize) -> Res } else { out.write(line_separator)?; } - lines_written += 1; } out.flush()?; Ok(lines_written) } -fn write_columns(lines: &Vec, options: &OutputOptions, page_separator: &[u8], out: &mut Stdout, line_separator: &[u8], page: &usize) -> Result { +fn write_columns(lines: &Vec, options: &OutputOptions, out: &mut Stdout, line_number: &usize) -> Result { + let line_separator = options.as_ref().line_separator.as_bytes(); + let page_separator = options.as_ref().page_separator_char.as_bytes(); let content_lines_per_page = options.as_ref().content_lines_per_page; - let prev_lines = content_lines_per_page * (page - 1); let width: usize = options.as_ref() .number.as_ref() .map(|i| i.width) .unwrap_or(0); - let separator: String = options.as_ref() + let number_separator: String = options.as_ref() .number.as_ref() .map(|i| i.separator.to_string()) .unwrap_or(NumberingMode::default().separator); - let mut i = 0; - for x in lines { - if options.number.is_none() { - out.write(x.as_bytes())?; - } else { - let fmtd_line_number: String = get_fmtd_line_number(&width, prev_lines + i, &separator); - out.write(format!("{}{}", fmtd_line_number, x).as_bytes())?; - } + let blank_line = "".to_string(); + let columns = options.get_columns(); + let col_sep: &String = options.as_ref() + .column_mode_options.as_ref() + .map(|i| &i.column_separator) + .unwrap_or(&blank_line); + + let col_width: Option = options.as_ref() + .column_mode_options.as_ref() + .map(|i| i.width) + .unwrap_or(None); + + let mut i = 0; + let is_number_mode = options.number.is_some(); + for start in 0..content_lines_per_page { + let indexes: Vec = get_indexes(start, content_lines_per_page, columns); + let mut line = String::new(); + for index in indexes { + let read_line: &String = lines.get(index).unwrap_or(&blank_line); + let next_line_number = line_number + index + 1; + let trimmed_line = get_line_for_printing( + next_line_number, &width, + &number_separator, columns, + col_sep, col_width, + read_line, is_number_mode); + line.push_str(&trimmed_line); + i += 1; + } + out.write(line.as_bytes())?; if i == lines.len() { out.write(page_separator)?; } else { out.write(line_separator)?; } - i += 1; } Ok(i) } +fn get_line_for_printing(line_number: usize, width: &usize, + separator: &String, columns: usize, col_sep: &String, col_width: Option, + read_line: &String, is_number_mode: bool) -> String { + let fmtd_line_number: String = if is_number_mode { + get_fmtd_line_number(&width, line_number, &separator) + } else { + "".to_string() + }; + let complete_line = format!("{}{}{}", fmtd_line_number, read_line, col_sep); + // TODO Adjust the width according to -n option + // TODO Line has less content than the column width + col_width.map(|i| complete_line.chars().take(i / columns).collect()).unwrap_or(complete_line) +} + +fn get_indexes(start: usize, content_lines_per_page: usize, columns: usize) -> Vec { + let mut indexes: Vec = Vec::new(); + let mut offset = start; + indexes.push(offset); + for _col in 1..columns { + offset += content_lines_per_page; + indexes.push(offset); + } + indexes +} + fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String { let line_str = line_number.to_string(); if line_str.len() >= *width {