colours.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. use ansi_term::Style;
  2. use ansi_term::Colour::{Red, Green, Yellow, Blue, Cyan, Purple, Fixed};
  3. use output::render;
  4. use output::file_name::Colours as FileNameColours;
  5. use style::lsc::Pair;
  6. #[derive(Debug, Default, PartialEq)]
  7. pub struct Colours {
  8. pub colourful: bool,
  9. pub scale: bool,
  10. pub filekinds: FileKinds,
  11. pub perms: Permissions,
  12. pub size: Size,
  13. pub users: Users,
  14. pub links: Links,
  15. pub git: Git,
  16. pub punctuation: Style,
  17. pub date: Style,
  18. pub inode: Style,
  19. pub blocks: Style,
  20. pub header: Style,
  21. pub symlink_path: Style,
  22. pub control_char: Style,
  23. pub broken_symlink: Style,
  24. pub broken_path_overlay: Style,
  25. }
  26. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  27. pub struct FileKinds {
  28. pub normal: Style,
  29. pub directory: Style,
  30. pub symlink: Style,
  31. pub pipe: Style,
  32. pub block_device: Style,
  33. pub char_device: Style,
  34. pub socket: Style,
  35. pub special: Style,
  36. pub executable: Style,
  37. }
  38. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  39. pub struct Permissions {
  40. pub user_read: Style,
  41. pub user_write: Style,
  42. pub user_execute_file: Style,
  43. pub user_execute_other: Style,
  44. pub group_read: Style,
  45. pub group_write: Style,
  46. pub group_execute: Style,
  47. pub other_read: Style,
  48. pub other_write: Style,
  49. pub other_execute: Style,
  50. pub special_user_file: Style,
  51. pub special_other: Style,
  52. pub attribute: Style,
  53. }
  54. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  55. pub struct Size {
  56. pub numbers: Style,
  57. pub unit: Style,
  58. pub major: Style,
  59. pub minor: Style,
  60. pub scale_byte: Style,
  61. pub scale_kilo: Style,
  62. pub scale_mega: Style,
  63. pub scale_giga: Style,
  64. pub scale_huge: Style,
  65. }
  66. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  67. pub struct Users {
  68. pub user_you: Style,
  69. pub user_someone_else: Style,
  70. pub group_yours: Style,
  71. pub group_not_yours: Style,
  72. }
  73. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  74. pub struct Links {
  75. pub normal: Style,
  76. pub multi_link_file: Style,
  77. }
  78. #[derive(Clone, Copy, Debug, Default, PartialEq)]
  79. pub struct Git {
  80. pub new: Style,
  81. pub modified: Style,
  82. pub deleted: Style,
  83. pub renamed: Style,
  84. pub typechange: Style,
  85. pub ignored: Style,
  86. }
  87. impl Colours {
  88. pub fn plain() -> Colours {
  89. Colours::default()
  90. }
  91. pub fn colourful(scale: bool) -> Colours {
  92. Colours {
  93. colourful: true,
  94. scale,
  95. filekinds: FileKinds {
  96. normal: Style::default(),
  97. directory: Blue.bold(),
  98. symlink: Cyan.normal(),
  99. pipe: Yellow.normal(),
  100. block_device: Yellow.bold(),
  101. char_device: Yellow.bold(),
  102. socket: Red.bold(),
  103. special: Yellow.normal(),
  104. executable: Green.bold(),
  105. },
  106. perms: Permissions {
  107. user_read: Yellow.bold(),
  108. user_write: Red.bold(),
  109. user_execute_file: Green.bold().underline(),
  110. user_execute_other: Green.bold(),
  111. group_read: Yellow.normal(),
  112. group_write: Red.normal(),
  113. group_execute: Green.normal(),
  114. other_read: Yellow.normal(),
  115. other_write: Red.normal(),
  116. other_execute: Green.normal(),
  117. special_user_file: Purple.normal(),
  118. special_other: Purple.normal(),
  119. attribute: Style::default(),
  120. },
  121. size: Size {
  122. numbers: Green.bold(),
  123. unit: Green.normal(),
  124. major: Green.bold(),
  125. minor: Green.normal(),
  126. scale_byte: Fixed(118).normal(),
  127. scale_kilo: Fixed(190).normal(),
  128. scale_mega: Fixed(226).normal(),
  129. scale_giga: Fixed(220).normal(),
  130. scale_huge: Fixed(214).normal(),
  131. },
  132. users: Users {
  133. user_you: Yellow.bold(),
  134. user_someone_else: Style::default(),
  135. group_yours: Yellow.bold(),
  136. group_not_yours: Style::default(),
  137. },
  138. links: Links {
  139. normal: Red.bold(),
  140. multi_link_file: Red.on(Yellow),
  141. },
  142. git: Git {
  143. new: Green.normal(),
  144. modified: Blue.normal(),
  145. deleted: Red.normal(),
  146. renamed: Yellow.normal(),
  147. typechange: Purple.normal(),
  148. ignored: Style::default().dimmed(),
  149. },
  150. punctuation: Fixed(244).normal(),
  151. date: Blue.normal(),
  152. inode: Purple.normal(),
  153. blocks: Cyan.normal(),
  154. header: Style::default().underline(),
  155. symlink_path: Cyan.normal(),
  156. control_char: Red.normal(),
  157. broken_symlink: Red.normal(),
  158. broken_path_overlay: Style::default().underline(),
  159. }
  160. }
  161. }
  162. /// Some of the styles are **overlays**: although they have the same attribute
  163. /// set as regular styles (foreground and background colours, bold, underline,
  164. /// etc), they’re intended to be used to *amend* existing styles.
  165. ///
  166. /// For example, the target path of a broken symlink is displayed in a red,
  167. /// underlined style by default. Paths can contain control characters, so
  168. /// these control characters need to be underlined too, otherwise it looks
  169. /// weird. So instead of having four separate configurable styles for “link
  170. /// path”, “broken link path”, “control character” and “broken control
  171. /// character”, there are styles for “link path”, “control character”, and
  172. /// “broken link overlay”, the latter of which is just set to override the
  173. /// underline attribute on the other two.
  174. fn apply_overlay(mut base: Style, overlay: Style) -> Style {
  175. if let Some(fg) = overlay.foreground { base.foreground = Some(fg); }
  176. if let Some(bg) = overlay.background { base.background = Some(bg); }
  177. if overlay.is_bold { base.is_bold = true; }
  178. if overlay.is_dimmed { base.is_dimmed = true; }
  179. if overlay.is_italic { base.is_italic = true; }
  180. if overlay.is_underline { base.is_underline = true; }
  181. if overlay.is_blink { base.is_blink = true; }
  182. if overlay.is_reverse { base.is_reverse = true; }
  183. if overlay.is_hidden { base.is_hidden = true; }
  184. if overlay.is_strikethrough { base.is_strikethrough = true; }
  185. base
  186. }
  187. // TODO: move this function to the ansi_term crate
  188. impl Colours {
  189. /// Sets a value on this set of colours using one of the keys understood
  190. /// by the `LS_COLORS` environment variable. Invalid keys set nothing, but
  191. /// return false.
  192. pub fn set_ls(&mut self, pair: &Pair) -> bool {
  193. match pair.key {
  194. "di" => self.filekinds.directory = pair.to_style(), // DIR
  195. "ex" => self.filekinds.executable = pair.to_style(), // EXEC
  196. "fi" => self.filekinds.normal = pair.to_style(), // FILE
  197. "pi" => self.filekinds.pipe = pair.to_style(), // FIFO
  198. "so" => self.filekinds.socket = pair.to_style(), // SOCK
  199. "bd" => self.filekinds.block_device = pair.to_style(), // BLK
  200. "cd" => self.filekinds.char_device = pair.to_style(), // CHR
  201. "ln" => self.filekinds.symlink = pair.to_style(), // LINK
  202. "or" => self.broken_symlink = pair.to_style(), // ORPHAN
  203. _ => return false,
  204. // Codes we don’t do anything with:
  205. // MULTIHARDLINK, DOOR, SETUID, SETGID, CAPABILITY,
  206. // STICKY_OTHER_WRITABLE, OTHER_WRITABLE, STICKY, MISSING
  207. }
  208. true
  209. }
  210. /// Sets a value on this set of colours using one of the keys understood
  211. /// by the `EXA_COLORS` environment variable. Invalid keys set nothing,
  212. /// but return false. This doesn’t take the `LS_COLORS` keys into account,
  213. /// so `set_ls` should have been run first.
  214. pub fn set_exa(&mut self, pair: &Pair) -> bool {
  215. match pair.key {
  216. "ur" => self.perms.user_read = pair.to_style(),
  217. "uw" => self.perms.user_write = pair.to_style(),
  218. "ux" => self.perms.user_execute_file = pair.to_style(),
  219. "ue" => self.perms.user_execute_other = pair.to_style(),
  220. "gr" => self.perms.group_read = pair.to_style(),
  221. "gw" => self.perms.group_write = pair.to_style(),
  222. "gx" => self.perms.group_execute = pair.to_style(),
  223. "tr" => self.perms.other_read = pair.to_style(),
  224. "tw" => self.perms.other_write = pair.to_style(),
  225. "tx" => self.perms.other_execute = pair.to_style(),
  226. "su" => self.perms.special_user_file = pair.to_style(),
  227. "sf" => self.perms.special_other = pair.to_style(),
  228. "xa" => self.perms.attribute = pair.to_style(),
  229. "sn" => self.size.numbers = pair.to_style(),
  230. "sb" => self.size.unit = pair.to_style(),
  231. "df" => self.size.major = pair.to_style(),
  232. "ds" => self.size.minor = pair.to_style(),
  233. "uu" => self.users.user_you = pair.to_style(),
  234. "un" => self.users.user_someone_else = pair.to_style(),
  235. "gu" => self.users.group_yours = pair.to_style(),
  236. "gn" => self.users.group_not_yours = pair.to_style(),
  237. "lc" => self.links.normal = pair.to_style(),
  238. "lm" => self.links.multi_link_file = pair.to_style(),
  239. "ga" => self.git.new = pair.to_style(),
  240. "gm" => self.git.modified = pair.to_style(),
  241. "gd" => self.git.deleted = pair.to_style(),
  242. "gv" => self.git.renamed = pair.to_style(),
  243. "gt" => self.git.typechange = pair.to_style(),
  244. "xx" => self.punctuation = pair.to_style(),
  245. "da" => self.date = pair.to_style(),
  246. "in" => self.inode = pair.to_style(),
  247. "bl" => self.blocks = pair.to_style(),
  248. "hd" => self.header = pair.to_style(),
  249. "lp" => self.symlink_path = pair.to_style(),
  250. "cc" => self.control_char = pair.to_style(),
  251. "bO" => self.broken_path_overlay = pair.to_style(),
  252. _ => return false,
  253. }
  254. true
  255. }
  256. }
  257. impl render::BlocksColours for Colours {
  258. fn block_count(&self) -> Style { self.blocks }
  259. fn no_blocks(&self) -> Style { self.punctuation }
  260. }
  261. impl render::FiletypeColours for Colours {
  262. fn normal(&self) -> Style { self.filekinds.normal }
  263. fn directory(&self) -> Style { self.filekinds.directory }
  264. fn pipe(&self) -> Style { self.filekinds.pipe }
  265. fn symlink(&self) -> Style { self.filekinds.symlink }
  266. fn block_device(&self) -> Style { self.filekinds.block_device }
  267. fn char_device(&self) -> Style { self.filekinds.char_device }
  268. fn socket(&self) -> Style { self.filekinds.socket }
  269. fn special(&self) -> Style { self.filekinds.special }
  270. }
  271. impl render::GitColours for Colours {
  272. fn not_modified(&self) -> Style { self.punctuation }
  273. fn new(&self) -> Style { self.git.new }
  274. fn modified(&self) -> Style { self.git.modified }
  275. fn deleted(&self) -> Style { self.git.deleted }
  276. fn renamed(&self) -> Style { self.git.renamed }
  277. fn type_change(&self) -> Style { self.git.typechange }
  278. fn ignored(&self) -> Style { self.git.ignored }
  279. }
  280. impl render::GroupColours for Colours {
  281. fn yours(&self) -> Style { self.users.group_yours }
  282. fn not_yours(&self) -> Style { self.users.group_not_yours }
  283. }
  284. impl render::LinksColours for Colours {
  285. fn normal(&self) -> Style { self.links.normal }
  286. fn multi_link_file(&self) -> Style { self.links.multi_link_file }
  287. }
  288. impl render::PermissionsColours for Colours {
  289. fn dash(&self) -> Style { self.punctuation }
  290. fn user_read(&self) -> Style { self.perms.user_read }
  291. fn user_write(&self) -> Style { self.perms.user_write }
  292. fn user_execute_file(&self) -> Style { self.perms.user_execute_file }
  293. fn user_execute_other(&self) -> Style { self.perms.user_execute_other }
  294. fn group_read(&self) -> Style { self.perms.group_read }
  295. fn group_write(&self) -> Style { self.perms.group_write }
  296. fn group_execute(&self) -> Style { self.perms.group_execute }
  297. fn other_read(&self) -> Style { self.perms.other_read }
  298. fn other_write(&self) -> Style { self.perms.other_write }
  299. fn other_execute(&self) -> Style { self.perms.other_execute }
  300. fn special_user_file(&self) -> Style { self.perms.special_user_file }
  301. fn special_other(&self) -> Style { self.perms.special_other }
  302. fn attribute(&self) -> Style { self.perms.attribute }
  303. }
  304. impl render::SizeColours for Colours {
  305. fn size(&self, size: u64) -> Style {
  306. if self.scale {
  307. if size < 1024 {
  308. self.size.scale_byte
  309. }
  310. else if size < 1024 * 1024 {
  311. self.size.scale_kilo
  312. }
  313. else if size < 1024 * 1024 * 1024 {
  314. self.size.scale_mega
  315. }
  316. else if size < 1024 * 1024 * 1024 * 1024 {
  317. self.size.scale_giga
  318. }
  319. else {
  320. self.size.scale_huge
  321. }
  322. }
  323. else {
  324. self.size.numbers
  325. }
  326. }
  327. fn unit(&self) -> Style { self.size.unit }
  328. fn no_size(&self) -> Style { self.punctuation }
  329. fn major(&self) -> Style { self.size.major }
  330. fn comma(&self) -> Style { self.punctuation }
  331. fn minor(&self) -> Style { self.size.minor }
  332. }
  333. impl render::UserColours for Colours {
  334. fn you(&self) -> Style { self.users.user_you }
  335. fn someone_else(&self) -> Style { self.users.user_someone_else }
  336. }
  337. impl FileNameColours for Colours {
  338. fn normal_arrow(&self) -> Style { self.punctuation }
  339. fn broken_symlink(&self) -> Style { self.broken_symlink }
  340. fn broken_filename(&self) -> Style { apply_overlay(self.broken_symlink, self.broken_path_overlay) }
  341. fn broken_control_char(&self) -> Style { apply_overlay(self.control_char, self.broken_path_overlay) }
  342. fn control_char(&self) -> Style { self.control_char }
  343. fn symlink_path(&self) -> Style { self.symlink_path }
  344. fn executable_file(&self) -> Style { self.filekinds.executable }
  345. }