grid_details.rs 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. use std::io::{Write, Result as IOResult};
  2. use ansi_term::ANSIStrings;
  3. use term_grid as grid;
  4. use fs::{Dir, File};
  5. use fs::feature::xattr::FileAttributes;
  6. use options::FileFilter;
  7. use output::cell::TextCell;
  8. use output::column::Column;
  9. use output::colours::Colours;
  10. use output::details::{Options as DetailsOptions, Row as DetailsRow, Render as DetailsRender};
  11. use output::grid::Options as GridOptions;
  12. use output::file_name::{FileName, LinkStyle, Classify};
  13. use output::table::{Table, Environment, Row as TableRow};
  14. pub struct Render<'a> {
  15. pub dir: Option<&'a Dir>,
  16. pub files: Vec<File<'a>>,
  17. pub colours: &'a Colours,
  18. pub classify: Classify,
  19. pub grid: &'a GridOptions,
  20. pub details: &'a DetailsOptions,
  21. pub filter: &'a FileFilter,
  22. }
  23. impl<'a> Render<'a> {
  24. pub fn details(&self) -> DetailsRender<'a> {
  25. DetailsRender {
  26. dir: self.dir.clone(),
  27. files: Vec::new(),
  28. colours: self.colours,
  29. classify: self.classify,
  30. opts: self.details,
  31. recurse: None,
  32. filter: self.filter,
  33. }
  34. }
  35. pub fn render<W: Write>(&self, w: &mut W) -> IOResult<()> {
  36. let columns_for_dir = match self.details.columns {
  37. Some(cols) => cols.for_dir(self.dir),
  38. None => Vec::new(),
  39. };
  40. let env = Environment::default();
  41. let drender = self.clone().details();
  42. let (mut first_table, _) = self.make_table(&env, &columns_for_dir, &drender);
  43. let rows = self.files.iter()
  44. .map(|file| first_table.row_for_file(file, file_has_xattrs(file)))
  45. .collect::<Vec<TableRow>>();
  46. let file_names = self.files.iter()
  47. .map(|file| FileName::new(file, LinkStyle::JustFilenames, self.classify, self.colours).paint().promote())
  48. .collect::<Vec<TextCell>>();
  49. let mut last_working_table = self.make_grid(&env, 1, &columns_for_dir, &file_names, rows.clone(), &drender);
  50. for column_count in 2.. {
  51. let grid = self.make_grid(&env, column_count, &columns_for_dir, &file_names, rows.clone(), &drender);
  52. let the_grid_fits = {
  53. let d = grid.fit_into_columns(column_count);
  54. d.is_complete() && d.width() <= self.grid.console_width
  55. };
  56. if the_grid_fits {
  57. last_working_table = grid;
  58. }
  59. else {
  60. return write!(w, "{}", last_working_table.fit_into_columns(column_count - 1));
  61. }
  62. }
  63. Ok(())
  64. }
  65. fn make_table<'t>(&'a self, env: &'a Environment, columns_for_dir: &'a [Column], drender: &DetailsRender) -> (Table<'a>, Vec<DetailsRow>) {
  66. let mut table = Table::new(columns_for_dir, self.colours, env);
  67. let mut rows = Vec::new();
  68. if self.details.header {
  69. rows.push(drender.render_header(table.header_row()));
  70. }
  71. (table, rows)
  72. }
  73. fn make_grid(&'a self, env: &'a Environment, column_count: usize, columns_for_dir: &'a [Column], file_names: &[TextCell], rows: Vec<TableRow>, drender: &DetailsRender) -> grid::Grid {
  74. let mut tables = Vec::new();
  75. for _ in 0 .. column_count {
  76. tables.push(self.make_table(env.clone(), columns_for_dir, drender));
  77. }
  78. let mut num_cells = rows.len();
  79. if self.details.header {
  80. num_cells += column_count;
  81. }
  82. let original_height = divide_rounding_up(rows.len(), column_count);
  83. let height = divide_rounding_up(num_cells, column_count);
  84. for (i, (file_name, row)) in file_names.iter().zip(rows.into_iter()).enumerate() {
  85. let index = if self.grid.across {
  86. i % column_count
  87. }
  88. else {
  89. i / original_height
  90. };
  91. let (ref mut table, ref mut rows) = tables[index];
  92. table.add_widths(&row);
  93. let details_row = drender.render_file(row, file_name.clone(), 0, false);
  94. rows.push(details_row);
  95. }
  96. let columns: Vec<_> = tables.into_iter().map(|(table, details_rows)| {
  97. drender.iterate(&table, details_rows).collect::<Vec<_>>()
  98. }).collect();
  99. let direction = if self.grid.across { grid::Direction::LeftToRight }
  100. else { grid::Direction::TopToBottom };
  101. let mut grid = grid::Grid::new(grid::GridOptions {
  102. direction: direction,
  103. filling: grid::Filling::Spaces(4),
  104. });
  105. if self.grid.across {
  106. for row in 0 .. height {
  107. for column in &columns {
  108. if row < column.len() {
  109. let cell = grid::Cell {
  110. contents: ANSIStrings(&column[row].contents).to_string(),
  111. width: *column[row].width,
  112. };
  113. grid.add(cell);
  114. }
  115. }
  116. }
  117. }
  118. else {
  119. for column in &columns {
  120. for cell in column.iter() {
  121. let cell = grid::Cell {
  122. contents: ANSIStrings(&cell.contents).to_string(),
  123. width: *cell.width,
  124. };
  125. grid.add(cell);
  126. }
  127. }
  128. }
  129. grid
  130. }
  131. }
  132. fn divide_rounding_up(a: usize, b: usize) -> usize {
  133. let mut result = a / b;
  134. if a % b != 0 { result += 1; }
  135. result
  136. }
  137. fn file_has_xattrs(file: &File) -> bool {
  138. match file.path.attributes() {
  139. Ok(attrs) => !attrs.is_empty(),
  140. Err(_) => false,
  141. }
  142. }