This commit makes adding icons to file names something that the file name renderer does, rather than something that each individual view does. This is now possible thanks to the previous commit a1869f2, which moved the option to do this into the same module. The repeated code has been removed.
It happens to fix a bug where the width of each column was being incorrectly calculated for the grid-details view, making lines slightly too long for the terminal because the icon wasn't being taken into account.
All four of the view mode command-line argument parsers tested for the --icons option. Because it was common, the behaviour has been moved to the struct that handles file styles, meaning it can be parsed in one place.
This is a better place for it, as the icons are to do with the file name, not the view. It also means that the lines view has no options left for it, which is fitting.
This commit significantly refactors the way that options are parsed. It introduces the Theme type which contains both styling and extension configuration, converts the option-parsing process into a being a pure function, and removes some rather gnarly old code.
The main purpose of the refactoring is to fix GH-318, "Tests fail when not connected to a terminal". Even though exa was compiling fine on my machine and on Travis, it was failing for automated build scripts. This was because of what the option-parsing code was trying to accomplish: it wasn't just providing a struct of the user's settings, it was also checking the terminal, providing a View directly.
This has been changed so that the options module now _only_ looks at the command-line arguments and environment variables. Instead of returning a View, it returns the user's _preference_, and it's then up to the 'main' module to examine the terminal width and figure out if the view is doable, downgrading it if necessary.
The code that used to determine the view was horrible and I'm pleased it can be cut out. Also, the terminal width used to be in a lazy_static because it was queried multiple times, and now it's not in one because it's only queried once, which is a good sign for things going in the right direction.
There are also some naming and organisational changes around themes. The blanket terms "Colours" and "Styles" have been yeeted in favour of "Theme", which handles both extensions and UI colours. The FileStyle struct has been replaced with file_name::Options, making it similar to the views in how it has an Options struct and a Render struct.
Finally, eight unit tests have been removed because they turned out to be redundant (testing --colour and --color) after examining the tangled code, and the default theme has been put in its own file in preparation for more themes.
I read through every file and applied a couple of rustfmt suggestions. The brace placement and alignment of items on similar lines has been made consistent, even if neither are rustfmt's default style (a file has been put in place to enforce this). Other changes are:
• Alphabetical imports and modules
• Comma placement at the end of match blocks
• Use newlines and indentation judiciously
• Spaces around associated types
• Spaces after negations (it makes it more clear imho)
• Comment formatting
• Use early-returns and Optional `?` where appropriate
This commit makes changes to the way variables are referenced:
• Make types Copy when possible
• Make methods take `self` instead of `&self` where possible (trivially_copy_pass_by_ref)
• Remove unnecessary borrowing (needless_ref)
• Remove unnecessary cloning (clone_on_copy)
• Remove `ref` from match arms where possible (new Rust match ergonomics)
This commit replaces the “two normal cases” of showing a link’s target or not with “one default and one special case” of preferring to hide them, displaying the link targets by setting a flag instead.
Doing this simplifies the file name constructor, which gets to remove an argument.
The new FileStyles value will contain all the fields necessary to “style” a file’s name. Right now this is only the Classify field, but there can be more later. The benefit of this is that when we add more, we won’t need to update all the places where file names are displayed.
Instead of having render methods on the types that are now called Options, create new Render structs (one per view) and execute them. This means that it’s easier to extract methods from them — some of them are pretty long.
Also, remove the GridDetails struct, which got consumed by Mode (mostly)
By introducing another indirection between the structs that command-line options get parsed into and the structs that get rendered, it should be easier to refactor that horrible function in view.rs.
All four view types — lines, grid, details, and grid-details — held their own colours and classify flags.
This didn’t make any sense for the grid-details view, which had to pick which one to use: the values were in there twice.
It also gave the Table in the details view access to more information than it really should have had.
Now, those two flags are returned separately from the view “mode”, which is the new term for one of those four things.
For some reason, the code that calculated the width of a cell with a path in counted the width of the path twice: once from the ANSIStrings containing it, and once more added on afterwards. This meant that the grid view thought that columns were wider than they really were, meaning fewer could be fit into a grid.
This turns `file` into `self.file` and `colours` into `self.colours`, but it means we don’t need to pass arguments everywhere, which will be more of a problem the more functions there are.
Most of the code has just been indented.
exa deals with cells and widths a lot: the items in a grid need to be aligned according to their *contents’* widths, rather than the length of their strings, which often included ANSI escape characters. As an optimisation, it used to calculate this separately based on the filename, and dealing with any extra characters (such as the classify ones) in that function too.
Recently, though, file names have become a lot more complicated. Classification added zero to one extra characters, and now with escaped control characters in file names, it’s not so easy to calculate the display width of a filename.
This commit removes the function that calculated the width, in favour of building the output string (it’s going to be displayed anyway) and just getting the width of what it displays instead.
This commit moves file, dir, and the feature modules into one parent 'fs' module. Now there are three main 'areas' of the code: main and options, the filesystem-touching code, and the output-displaying code.
It should be the case that nothing in 'output' touches 'std::fs'.
This commit changes all the views to accommodate printing each path's prefix, if it has one.
Previously, each file was stripped of its ancestry, leaving only its file name to be displayed. So running "exa /usr/bin/*" would display only filenames, while running "ls /usr/bin/*" would display each file prefixed with "/usr/bin/". But running "ls /usr/bin/" -- without the glob -- would run ls on just the directory, printing out the file names with no prefix or anything.
This functionality turned out to be useful in quite a few situations: firstly, if the user passes in files from different directories, it would be hard to tell where they came from (especially if they have the same name, such as find | xargs). Secondly, this also applied when following symlinks, making it unclear exactly which file a symlink would be pointing to.
The reason that it did it this way beforehand was that I didn't think of these use-cases, rather than for any technical reason; this new method should not have any drawbacks save making the output slightly wider in a few cases. Compatibility with ls is also a big plus.
Fixes#104, and relates to #88 and #92.
This commit moves the colours module to be a sub-module of the output one.
This makes sense because finding which colour a certain file should be is only
done during output, and (I think) the only places that the `Colours` struct's
fields are ever queried is from the output module.
The only casualty was that the `file_colour` from the filetype module had to
be moved, as determining colours is no longer part of that module - only
determining filetype is. So it now reflects its name!
By removing the `File#file_name_width` method, we can make the file module
have no dependency on the output module -- in other words, the model (file)
and the view (output) are now separate again!
This commit introduces the `output::cell::DisplayWidth` struct, which
encapsulates the Unicode *display width* of a string in a struct that makes it
less easily confused with the *length* of a string.
The use of this type means that it's now harder to accidentally use a string's
length-in-bytes as its width. I've fixed at least one case in the code where
this was being done!
The only casualty is that it introduces a dependency on the output module from
the file module, which will be removed next commit.
Changes to the way ANSIStrings work mean we need to dereference the strings before putting them in an ANSIString. There's more that can be done here, but this gets it to compile for now.
Colours are now disabled when output is not to a terminal. Fixes#53!
This required some internal restructuring - colours are now in their own object that gets passed around everywhere it's needed.
Filenames with characters that need multiple bytes in UTF-8, and/or
control characters like combining diacritics, would break the grid.
`StrExt::width` seems to do the right thing, and there's conveniently a
function here (which was otherwise unused) to call that.
- Turn the views and main program loop into structs, rather than just as one gigantic function
- Separate views into their own files
The addition of the git column and the tree view meant that a lot of functions now just took extra arguments that didn't seem to fit. For example, it didn't really work to have only one 'view' method that printed out everything, as the different view options now all take different parameters.