Skip to content

Commit 51be9f4

Browse files
committed
Introduce EXA_ICON_SPACING environment variable
This commit remove the extra space that was added between icons and file names in commit 128fadd, and adds an option to put them back. Re-fixes GH-619 and fixes GH-541.
1 parent c833592 commit 51be9f4

18 files changed

+389
-271
lines changed

man/exa.1.md

+6
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ Limits the grid-details view (‘`exa --grid --long`’) so it’s only activate
208208
With widescreen displays, it’s possible for the grid to look very wide and sparse, on just one or two lines with none of the columns lining up.
209209
By specifying a minimum number of rows, you can only use the view if it’s going to be worth using.
210210

211+
## `EXA_ICON_SPACING`
212+
213+
Specifies the number of spaces to print between an icon (see the ‘`--icons`’ option) and its file name.
214+
215+
Different terminals display icons differently, as they usually take up more than one character width on screen, so there’s no “standard” number of spaces that exa can use to separate an icon from text. One space may place the icon too close to the text, and two spaces may place it too far away. So the choice is left up to the user to configure depending on their terminal emulator.
216+
211217
## `LS_COLORS`, `EXA_COLORS`
212218

213219
Specifies the colour scheme used to highlight files based on their name and kind, as well as highlighting metadata and parts of the UI.

src/options/file_name.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
use crate::options::{flags, OptionsError};
22
use crate::options::parser::MatchedFlags;
3+
use crate::options::vars::{self, Vars};
34

4-
use crate::output::file_name::{Options, Classify};
5+
use crate::output::file_name::{Options, Classify, ShowIcons};
56

67

78
impl Options {
8-
pub fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
9+
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
910
let classify = Classify::deduce(matches)?;
10-
let icons = matches.has(&flags::ICONS)?;
11+
let show_icons = ShowIcons::deduce(matches, vars)?;
1112

12-
Ok(Self { classify, icons })
13+
Ok(Self { classify, show_icons })
1314
}
1415
}
1516

@@ -21,3 +22,20 @@ impl Classify {
2122
else { Ok(Self::JustFilenames) }
2223
}
2324
}
25+
26+
impl ShowIcons {
27+
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
28+
if ! matches.has(&flags::ICONS)? {
29+
Ok(Self::Off)
30+
}
31+
else if let Some(columns) = vars.get(vars::EXA_ICON_SPACING).and_then(|s| s.into_string().ok()) {
32+
match columns.parse() {
33+
Ok(width) => Ok(Self::On(width)),
34+
Err(e) => Err(OptionsError::FailedParse(e)),
35+
}
36+
}
37+
else {
38+
Ok(Self::On(1))
39+
}
40+
}
41+
}

src/options/vars.rs

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ pub static EXA_DEBUG: &str = "EXA_DEBUG";
3939
/// number of rows of output.
4040
pub static EXA_GRID_ROWS: &str = "EXA_GRID_ROWS";
4141

42+
/// Environment variable used to specify how many spaces to print between an
43+
/// icon and its file name. Different terminals display icons differently,
44+
/// with 1 space bringing them too close together or 2 spaces putting them too
45+
/// far apart, so this may be necessary depending on how they are shown.
46+
pub static EXA_ICON_SPACING: &str = "EXA_ICON_SPACING";
47+
4248

4349
/// Mockable wrapper for `std::env::var_os`.
4450
pub trait Vars {

src/options/view.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ impl View {
1212
pub fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
1313
let mode = Mode::deduce(matches, vars)?;
1414
let width = TerminalWidth::deduce(vars)?;
15-
let file_style = FileStyle::deduce(matches)?;
15+
let file_style = FileStyle::deduce(matches, vars)?;
1616
Ok(Self { mode, width, file_style })
1717
}
1818
}

src/output/file_name.rs

+28-4
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct Options {
1818
pub classify: Classify,
1919

2020
/// Whether to prepend icon characters before file names.
21-
pub icons: bool,
21+
pub show_icons: ShowIcons,
2222
}
2323

2424
impl Options {
@@ -72,6 +72,19 @@ impl Default for Classify {
7272
}
7373

7474

75+
/// Whether and how to show icons.
76+
#[derive(PartialEq, Debug, Copy, Clone)]
77+
pub enum ShowIcons {
78+
79+
/// Don’t show icons at all.
80+
Off,
81+
82+
/// Show icons next to file names, with the given number of spaces between
83+
/// the icon and the file name.
84+
On(u32),
85+
}
86+
87+
7588
/// A **file name** holds all the information necessary to display the name
7689
/// of the given file. This is used in all of the views.
7790
pub struct FileName<'a, 'dir, C> {
@@ -112,12 +125,17 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
112125
pub fn paint(&self) -> TextCellContents {
113126
let mut bits = Vec::new();
114127

115-
if self.options.icons {
128+
if let ShowIcons::On(spaces_count) = self.options.show_icons {
116129
let style = iconify_style(self.style());
117130
let file_icon = icon_for_file(self.file).to_string();
118131

119132
bits.push(style.paint(file_icon));
120-
bits.push(Style::default().paint(" "));
133+
134+
match spaces_count {
135+
1 => bits.push(Style::default().paint(" ")),
136+
2 => bits.push(Style::default().paint(" ")),
137+
n => bits.push(Style::default().paint(spaces(n))),
138+
}
121139
}
122140

123141
if self.file.parent_dir.is_none() {
@@ -152,7 +170,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> {
152170
if ! target.name.is_empty() {
153171
let target_options = Options {
154172
classify: Classify::JustFilenames,
155-
icons: false,
173+
show_icons: ShowIcons::Off,
156174
};
157175

158176
let target = FileName {
@@ -320,3 +338,9 @@ pub trait Colours: FiletypeColours {
320338

321339
fn colour_file(&self, file: &File<'_>) -> Style;
322340
}
341+
342+
343+
/// Generate a string made of `n` spaces.
344+
fn spaces(width: u32) -> String {
345+
(0 .. width).into_iter().map(|_| ' ').collect()
346+
}

xtests/icons.toml

+35
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,38 @@ stdout = { file = "outputs/links_oneline_icons.ansitxt" }
7777
stderr = { empty = true }
7878
status = 0
7979
tags = [ 'oneline', 'icons' ]
80+
81+
82+
# icon spacing tests
83+
84+
[[cmd]]
85+
name = "‘EXA_ICON_SPACING=0 exa -1 --icons’ puts no spaces between icons and file names"
86+
shell = "EXA_ICON_SPACING=0 exa -1 --icons /testcases/links"
87+
stdout = { file = "outputs/links_oneline_icons_width0.ansitxt" }
88+
stderr = { empty = true }
89+
status = 0
90+
tags = [ 'env', 'oneline', 'icons' ]
91+
92+
[[cmd]]
93+
name = "‘EXA_ICON_SPACING=1 exa -1 --icons’ puts one space between icons and file names"
94+
shell = "EXA_ICON_SPACING=1 exa -1 --icons /testcases/links"
95+
stdout = { file = "outputs/links_oneline_icons.ansitxt" } # same as the default
96+
stderr = { empty = true }
97+
status = 0
98+
tags = [ 'env', 'oneline', 'icons' ]
99+
100+
[[cmd]]
101+
name = "‘EXA_ICON_SPACING=2 exa -1 --icons’ puts two spaces between icons and file names"
102+
shell = "EXA_ICON_SPACING=2 exa -1 --icons /testcases/links"
103+
stdout = { file = "outputs/links_oneline_icons_width2.ansitxt" }
104+
stderr = { empty = true }
105+
status = 0
106+
tags = [ 'env', 'oneline', 'icons' ]
107+
108+
[[cmd]]
109+
name = "‘EXA_ICON_SPACING=3 exa -1 --icons’ puts three spaces between icons and file names"
110+
shell = "EXA_ICON_SPACING=3 exa -1 --icons /testcases/links"
111+
stdout = { file = "outputs/links_oneline_icons_width3.ansitxt" }
112+
stderr = { empty = true }
113+
status = 0
114+
tags = [ 'env', 'oneline', 'icons' ]
+26-26
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
 #SAVEFILE#
2-
 backup~
3-
 compiled.class
4-
compiled.coffee
5-
 compiled.js
6-
 compiled.o
7-
 compressed.deb
8-
 compressed.tar.gz
9-
 compressed.tar.xz
10-
 compressed.tgz
11-
 compressed.txz
12-
 COMPRESSED.ZIP
13-
 crypto.asc
14-
 crypto.signature
15-
 document.pdf
16-
 DOCUMENT.XLSX
17-
 file.tmp
18-
 IMAGE.PNG
19-
 image.svg
20-
 lossless.flac
21-
 lossless.wav
22-
 Makefile
23-
 music.mp3
24-
 MUSIC.OGG
25-
 VIDEO.AVI
26-
 video.wmv
1+
 #SAVEFILE#
2+
 backup~
3+
 compiled.class
4+
 compiled.coffee
5+
 compiled.js
6+
 compiled.o
7+
 compressed.deb
8+
 compressed.tar.gz
9+
 compressed.tar.xz
10+
 compressed.tgz
11+
 compressed.txz
12+
 COMPRESSED.ZIP
13+
 crypto.asc
14+
 crypto.signature
15+
 document.pdf
16+
 DOCUMENT.XLSX
17+
 file.tmp
18+
 IMAGE.PNG
19+
 image.svg
20+
 lossless.flac
21+
 lossless.wav
22+
 Makefile
23+
 music.mp3
24+
 MUSIC.OGG
25+
 VIDEO.AVI
26+
 video.wmv
+6-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
 1_bytes  3_KiB  5_MiB  8_bytes  10_KiB  12_MiB
2-
 1_KiB  3_MiB  6_bytes  8_KiB  10_MiB  13_bytes
3-
 1_MiB  4_bytes  6_KiB  8_MiB  11_bytes  13_KiB
4-
 2_bytes  4_KiB  6_MiB  9_bytes  11_KiB  13_MiB
5-
 2_KiB  4_MiB  7_bytes  9_KiB  11_MiB
6-
 2_MiB  5_bytes  7_KiB  9_MiB  12_bytes
7-
 3_bytes  5_KiB  7_MiB  10_bytes  12_KiB
1+
 1_bytes  3_bytes  5_bytes  7_bytes  9_bytes  11_bytes  13_bytes
2+
 1_KiB  3_KiB  5_KiB  7_KiB  9_KiB  11_KiB  13_KiB
3+
 1_MiB  3_MiB  5_MiB  7_MiB  9_MiB  11_MiB  13_MiB
4+
 2_bytes  4_bytes  6_bytes  8_bytes  10_bytes  12_bytes
5+
 2_KiB  4_KiB  6_KiB  8_KiB  10_KiB  12_KiB
6+
 2_MiB  4_MiB  6_MiB  8_MiB  10_MiB  12_MiB

0 commit comments

Comments
 (0)