grid.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. use std::io::{self, Write};
  2. use term_grid as tg;
  3. use crate::fs::filter::FileFilter;
  4. use crate::fs::File;
  5. use crate::output::file_name::{Classify, Options as FileStyle};
  6. use crate::output::file_name::{EmbedHyperlinks, ShowIcons};
  7. use crate::theme::Theme;
  8. #[derive(PartialEq, Eq, Debug, Copy, Clone)]
  9. pub struct Options {
  10. pub across: bool,
  11. }
  12. impl Options {
  13. pub fn direction(self) -> tg::Direction {
  14. if self.across {
  15. tg::Direction::LeftToRight
  16. } else {
  17. tg::Direction::TopToBottom
  18. }
  19. }
  20. }
  21. pub struct Render<'a> {
  22. pub files: Vec<File<'a>>,
  23. pub theme: &'a Theme,
  24. pub file_style: &'a FileStyle,
  25. pub opts: &'a Options,
  26. pub console_width: usize,
  27. pub filter: &'a FileFilter,
  28. }
  29. impl<'a> Render<'a> {
  30. pub fn render<W: Write>(mut self, w: &mut W) -> io::Result<()> {
  31. let mut grid = tg::Grid::new(tg::GridOptions {
  32. direction: self.opts.direction(),
  33. filling: tg::Filling::Spaces(2),
  34. });
  35. grid.reserve(self.files.len());
  36. self.filter.sort_files(&mut self.files);
  37. for file in &self.files {
  38. let filename = self.file_style.for_file(file, self.theme);
  39. // Calculate classification width
  40. let classification_width =
  41. if let Classify::AddFileIndicators = filename.options.classify {
  42. match filename.classify_char(file) {
  43. Some(s) => s.len(),
  44. None => 0,
  45. }
  46. } else {
  47. 0
  48. };
  49. let space_filename_offset = if file.name.contains(' ') || file.name.contains('\'') {
  50. 2
  51. } else {
  52. 0
  53. };
  54. let contents = filename.paint();
  55. #[rustfmt::skip]
  56. let width = match (
  57. filename.options.embed_hyperlinks,
  58. filename.options.show_icons,
  59. ) {
  60. ( EmbedHyperlinks::On, ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing) )
  61. => filename.bare_width() + classification_width + 1 + (spacing as usize) + space_filename_offset,
  62. ( EmbedHyperlinks::On, ShowIcons::Never )
  63. => filename.bare_width() + classification_width + space_filename_offset,
  64. ( EmbedHyperlinks::Off, ShowIcons::Always(spacing) | ShowIcons::Automatic(spacing) )
  65. => filename.bare_width() + 1 + (spacing as usize) + space_filename_offset,
  66. ( EmbedHyperlinks::Off, _ )
  67. => *contents.width(),
  68. };
  69. grid.add(tg::Cell {
  70. contents: contents.strings().to_string(),
  71. // with hyperlink escape sequences,
  72. // the actual *contents.width() is larger than actually needed, so we take only the filename
  73. width,
  74. });
  75. }
  76. if let Some(display) = grid.fit_into_width(self.console_width) {
  77. write!(w, "{display}")
  78. } else {
  79. // File names too long for a grid - drop down to just listing them!
  80. // This isn’t *quite* the same as the lines view, which also
  81. // displays full link paths.
  82. for file in &self.files {
  83. let name_cell = self.file_style.for_file(file, self.theme).paint();
  84. writeln!(w, "{}", name_cell.strings())?;
  85. }
  86. Ok(())
  87. }
  88. }
  89. }