colours.rs 18 KB

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