file_name.rs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. use ansi_term::{ANSIString, Style};
  2. use fs::{File, FileTarget};
  3. use output::Colours;
  4. use output::cell::TextCellContents;
  5. pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents {
  6. let mut bits = Vec::new();
  7. // TODO: This long function could do with some splitting up.
  8. if file.dir.is_none() {
  9. if let Some(parent) = file.path.parent() {
  10. let coconut = parent.components().count();
  11. if coconut == 1 && parent.has_root() {
  12. bits.push(colours.symlink_path.paint("/"));
  13. }
  14. else if coconut >= 1 {
  15. bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
  16. bits.push(colours.symlink_path.paint("/"));
  17. }
  18. }
  19. }
  20. if !file.name.is_empty() {
  21. for bit in coloured_file_name(file, colours) {
  22. bits.push(bit);
  23. }
  24. }
  25. if links && file.is_link() {
  26. match file.link_target() {
  27. FileTarget::Ok(target) => {
  28. bits.push(Style::default().paint(" "));
  29. bits.push(colours.punctuation.paint("->"));
  30. bits.push(Style::default().paint(" "));
  31. if let Some(parent) = target.path.parent() {
  32. let coconut = parent.components().count();
  33. if coconut == 1 && parent.has_root() {
  34. bits.push(colours.symlink_path.paint("/"));
  35. }
  36. else if coconut >= 1 {
  37. bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string()));
  38. bits.push(colours.symlink_path.paint("/"));
  39. }
  40. }
  41. if !target.name.is_empty() {
  42. bits.push(file_colour(colours, &target).paint(target.name));
  43. }
  44. },
  45. FileTarget::Broken(broken_path) => {
  46. bits.push(Style::default().paint(" "));
  47. bits.push(colours.broken_arrow.paint("->"));
  48. bits.push(Style::default().paint(" "));
  49. bits.push(colours.broken_filename.paint(broken_path.display().to_string()));
  50. },
  51. FileTarget::Err(_) => {
  52. // Do nothing -- the error gets displayed on the next line
  53. }
  54. }
  55. } else if classify {
  56. if file.is_executable_file() {
  57. bits.push(Style::default().paint("*"));
  58. } else if file.is_directory() {
  59. bits.push(Style::default().paint("/"));
  60. } else if file.is_pipe() {
  61. bits.push(Style::default().paint("|"));
  62. } else if file.is_link() {
  63. bits.push(Style::default().paint("@"));
  64. } else if file.is_socket() {
  65. bits.push(Style::default().paint("="));
  66. }
  67. }
  68. bits.into()
  69. }
  70. /// Returns at least one ANSI-highlighted string representing this file’s
  71. /// name using the given set of colours.
  72. ///
  73. /// Ordinarily, this will be just one string: the file’s complete name,
  74. /// coloured according to its file type. If the name contains control
  75. /// characters such as newlines or escapes, though, we can’t just print them
  76. /// to the screen directly, because then there’ll be newlines in weird places.
  77. ///
  78. /// So in that situation, those characters will be escaped and highlighted in
  79. /// a different colour.
  80. fn coloured_file_name<'a>(file: &File, colours: &Colours) -> Vec<ANSIString<'a>> {
  81. let colour = file_colour(colours, file);
  82. let mut bits = Vec::new();
  83. if file.name.chars().all(|c| c >= 0x20 as char) {
  84. bits.push(colour.paint(file.name.clone()));
  85. }
  86. else {
  87. for c in file.name.chars() {
  88. // The `escape_default` method on `char` is *almost* what we want here, but
  89. // it still escapes non-ASCII UTF-8 characters, which are still printable.
  90. if c >= 0x20 as char {
  91. // TODO: This allocates way too much,
  92. // hence the `all` check above.
  93. let mut s = String::new();
  94. s.push(c);
  95. bits.push(colour.paint(s));
  96. } else {
  97. let s = c.escape_default().collect::<String>();
  98. bits.push(colours.control_char.paint(s));
  99. }
  100. }
  101. }
  102. bits
  103. }
  104. pub fn file_colour(colours: &Colours, file: &File) -> Style {
  105. match file {
  106. f if f.is_directory() => colours.filetypes.directory,
  107. f if f.is_executable_file() => colours.filetypes.executable,
  108. f if f.is_link() => colours.filetypes.symlink,
  109. f if f.is_pipe() => colours.filetypes.pipe,
  110. f if f.is_char_device()
  111. | f.is_block_device() => colours.filetypes.device,
  112. f if f.is_socket() => colours.filetypes.socket,
  113. f if !f.is_file() => colours.filetypes.special,
  114. f if f.is_immediate() => colours.filetypes.immediate,
  115. f if f.is_image() => colours.filetypes.image,
  116. f if f.is_video() => colours.filetypes.video,
  117. f if f.is_music() => colours.filetypes.music,
  118. f if f.is_lossless() => colours.filetypes.lossless,
  119. f if f.is_crypto() => colours.filetypes.crypto,
  120. f if f.is_document() => colours.filetypes.document,
  121. f if f.is_compressed() => colours.filetypes.compressed,
  122. f if f.is_temp() => colours.filetypes.temp,
  123. f if f.is_compiled() => colours.filetypes.compiled,
  124. _ => colours.filetypes.normal,
  125. }
  126. }