Skip to content

Commit

Permalink
Merge remote-tracking branch origin/option-pars-ng
Browse files Browse the repository at this point in the history
This merges in exa’s own new options parser, which has the following features:

- You can specify an option twice and it’ll use the second one, making aliases usable for defaults (fixes #144)
- Lets arguments be specified more than once (fixes #125)

Strict mode is not done yet; I just wanted to merge this in because it’s been a while, and there’s work that needs to be done on master so I don’t want them drifting apart any further.

It’s likely that you’ll find cases where multiple arguments doesn’t work or where the wrong value is being used. There aren’t tests for *everything* yet, and it still uses global environment variables.

# Conflicts:
#	src/options/view.rs
  • Loading branch information
ogham committed Aug 5, 2017
2 parents ad8c3c4 + 6759a5f commit b5bcf22
Show file tree
Hide file tree
Showing 20 changed files with 1,679 additions and 637 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -16,6 +16,7 @@ license = "MIT"
[[bin]]
name = "exa"
path = "src/bin/main.rs"
doc = false

[lib]
name = "exa"
Expand Down
4 changes: 2 additions & 2 deletions Vagrantfile
Expand Up @@ -59,8 +59,8 @@ Vagrant.configure(2) do |config|
config.vm.provision :shell, privileged: true, inline: <<-EOF
set -xe
echo -e "#!/bin/sh\n/home/#{developer}/target/debug/exa \\$*" > /usr/bin/exa
echo -e "#!/bin/sh\n/home/#{developer}/target/release/exa \\$*" > /usr/bin/rexa
echo -e "#!/bin/sh\n/home/#{developer}/target/debug/exa \"\\$*\"" > /usr/bin/exa
echo -e "#!/bin/sh\n/home/#{developer}/target/release/exa \"\\$*\"" > /usr/bin/rexa
chmod +x /usr/bin/{exa,rexa}
EOF

Expand Down
5 changes: 3 additions & 2 deletions src/bin/main.rs
@@ -1,14 +1,15 @@
extern crate exa;
use exa::Exa;

use std::ffi::OsString;
use std::env::args_os;
use std::io::{stdout, stderr, Write, ErrorKind};
use std::process::exit;


fn main() {
let args = args_os().skip(1);
match Exa::new(args, &mut stdout()) {
let args: Vec<OsString> = args_os().skip(1).collect();
match Exa::new(args.iter(), &mut stdout()) {
Ok(mut exa) => {
match exa.run() {
Ok(exit_status) => exit(exit_status),
Expand Down
27 changes: 13 additions & 14 deletions src/exa.rs
Expand Up @@ -3,7 +3,6 @@

extern crate ansi_term;
extern crate datetime;
extern crate getopts;
extern crate glob;
extern crate libc;
extern crate locale;
Expand All @@ -23,16 +22,16 @@ extern crate term_size;
extern crate lazy_static;


use std::ffi::OsStr;
use std::ffi::{OsStr, OsString};
use std::io::{stderr, Write, Result as IOResult};
use std::path::{Component, PathBuf};

use ansi_term::{ANSIStrings, Style};

use fs::{Dir, File};
use options::{Options, View, Mode};
use options::Options;
pub use options::Misfire;
use output::{escape, lines, grid, grid_details, details};
use output::{escape, lines, grid, grid_details, details, View, Mode};

mod fs;
mod info;
Expand All @@ -41,7 +40,7 @@ mod output;


/// The main program wrapper.
pub struct Exa<'w, W: Write + 'w> {
pub struct Exa<'args, 'w, W: Write + 'w> {

/// List of command-line options, having been successfully parsed.
pub options: Options,
Expand All @@ -53,12 +52,12 @@ pub struct Exa<'w, W: Write + 'w> {

/// List of the free command-line arguments that should correspond to file
/// names (anything that isn’t an option).
pub args: Vec<String>,
pub args: Vec<&'args OsStr>,
}

impl<'w, W: Write + 'w> Exa<'w, W> {
pub fn new<C>(args: C, writer: &'w mut W) -> Result<Exa<'w, W>, Misfire>
where C: IntoIterator, C::Item: AsRef<OsStr> {
impl<'args, 'w, W: Write + 'w> Exa<'args, 'w, W> {
pub fn new<I>(args: I, writer: &'w mut W) -> Result<Exa<'args, 'w, W>, Misfire>
where I: Iterator<Item=&'args OsString> {
Options::getopts(args).map(move |(options, args)| {
Exa { options, writer, args }
})
Expand All @@ -71,20 +70,20 @@ impl<'w, W: Write + 'w> Exa<'w, W> {

// List the current directory by default, like ls.
if self.args.is_empty() {
self.args.push(".".to_owned());
self.args = vec![ OsStr::new(".") ];
}

for file_name in &self.args {
match File::new(PathBuf::from(file_name), None, None) {
for file_path in &self.args {
match File::new(PathBuf::from(file_path), None, None) {
Err(e) => {
exit_status = 2;
writeln!(stderr(), "{}: {}", file_name, e)?;
writeln!(stderr(), "{:?}: {}", file_path, e)?;
},
Ok(f) => {
if f.is_directory() && !self.options.dir_action.treat_dirs_as_files() {
match f.to_dir(self.options.should_scan_for_git()) {
Ok(d) => dirs.push(d),
Err(e) => writeln!(stderr(), "{}: {}", file_name, e)?,
Err(e) => writeln!(stderr(), "{:?}: {}", file_path, e)?,
}
}
else {
Expand Down
64 changes: 64 additions & 0 deletions src/fs/dir_action.rs
@@ -0,0 +1,64 @@
/// What to do when encountering a directory?
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum DirAction {

/// This directory should be listed along with the regular files, instead
/// of having its contents queried.
AsFile,

/// This directory should not be listed, and should instead be opened and
/// *its* files listed separately. This is the default behaviour.
List,

/// This directory should be listed along with the regular files, and then
/// its contents should be listed afterward. The recursive contents of
/// *those* contents are dictated by the options argument.
Recurse(RecurseOptions),
}

impl DirAction {

/// Gets the recurse options, if this dir action has any.
pub fn recurse_options(&self) -> Option<RecurseOptions> {
match *self {
DirAction::Recurse(opts) => Some(opts),
_ => None,
}
}

/// Whether to treat directories as regular files or not.
pub fn treat_dirs_as_files(&self) -> bool {
match *self {
DirAction::AsFile => true,
DirAction::Recurse(RecurseOptions { tree, .. }) => tree,
_ => false,
}
}
}


/// The options that determine how to recurse into a directory.
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct RecurseOptions {

/// Whether recursion should be done as a tree or as multiple individual
/// views of files.
pub tree: bool,

/// The maximum number of times that recursion should descend to, if one
/// is specified.
pub max_depth: Option<usize>,
}

impl RecurseOptions {

/// Returns whether a directory of the given depth would be too deep.
pub fn is_too_deep(&self, depth: usize) -> bool {
match self.max_depth {
None => false,
Some(d) => {
d <= depth
}
}
}
}

0 comments on commit b5bcf22

Please sign in to comment.