<commit_before>#![feature(phase, globs)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate ansi_term;
extern crate unicode;

use std::os;
use std::io::fs;
use std::io::FileType::TypeDirectory;

use file::File;
use dir::Dir;
use column::Column;
use column::Alignment::Left;
use options::{Options, View};
use unix::Unix;

use ansi_term::{Plain, strip_formatting};

pub mod column;
pub mod dir;
pub mod format;
pub mod file;
pub mod filetype;
pub mod unix;
pub mod options;
pub mod sort;
pub mod term;

fn main() {
    let args: Vec<String> = os::args();

    match Options::getopts(args) {
        Err(error_code) => os::set_exit_status(error_code),
        Ok(options) => exa(&options),
    };
}

fn exa(opts: &Options) {
	let mut dirs: Vec<String> = vec![];
	let mut files: Vec<File> = vec![];
	
	// Separate the user-supplied paths into directories and files.
	// Files are shown first, and then each directory is expanded
	// and listed second.
	for file in opts.path_strs.iter() {
		let path = Path::new(file);
		match fs::stat(&path) {
			Ok(stat) => {
				if !opts.list_dirs && stat.kind == TypeDirectory {
					dirs.push(file.clone());
				}
				else {
					// May as well reuse the stat result from earlier
					// instead of just using File::from_path().
					files.push(File::with_stat(stat, path, None));
				}
			}
			Err(e) => println!("{}: {}", file, e),
		}
	}

    // It's only worth printing out directory names if the user supplied
    // more than one of them.
    let print_dir_names = opts.path_strs.len() > 1;
	let mut first = files.is_empty();

	if !files.is_empty() {
		view(opts, files);
	}
    
    for dir_name in dirs.into_iter() {
        if first {
            first = false;
        }
        else {
            print!("\n");
        }

        match Dir::readdir(Path::new(dir_name.clone())) {
            Ok(dir) => {
                let unsorted_files = dir.files();
                let files: Vec<File> = opts.transform_files(unsorted_files);

                if print_dir_names {
                    println!("{}:", dir_name);
                }

				view(opts, files);
            }
            Err(e) => {
                println!("{}: {}", dir_name, e);
                return;
            }
        };
    }
}

fn view(options: &Options, files: Vec<File>) {
	match options.view {
		View::Details(ref cols) => details_view(options, cols, files),
		View::Lines => lines_view(files),
		View::Grid(across, width) => grid_view(across, width, files),
	}
}

fn lines_view(files: Vec<File>) {
    for file in files.iter() {
        println!("{}", file.file_name());
    }
}

fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
    let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
    let num_columns = (console_width + 1) / (max_column_length + 1);
    let count = files.len();

    let mut num_rows = count / num_columns;
    if count % num_columns != 0 {
        num_rows += 1;
    }
    
    for y in range(0, num_rows) {
        for x in range(0, num_columns) {<commit_msg>Display files on a single line if possible<commit_after>#![feature(phase, globs)]
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
extern crate ansi_term;
extern crate unicode;

use std::os;
use std::io::fs;
use std::io::FileType::TypeDirectory;
use std::iter::AdditiveIterator;
use std::str::StrVector;

use file::File;
use dir::Dir;
use column::Column;
use column::Alignment::Left;
use options::{Options, View};
use unix::Unix;

use ansi_term::{Plain, strip_formatting};

pub mod column;
pub mod dir;
pub mod format;
pub mod file;
pub mod filetype;
pub mod unix;
pub mod options;
pub mod sort;
pub mod term;

fn main() {
    let args: Vec<String> = os::args();

    match Options::getopts(args) {
        Err(error_code) => os::set_exit_status(error_code),
        Ok(options) => exa(&options),
    };
}

fn exa(opts: &Options) {
    let mut dirs: Vec<String> = vec![];
    let mut files: Vec<File> = vec![];
    
    // Separate the user-supplied paths into directories and files.
    // Files are shown first, and then each directory is expanded
    // and listed second.
    for file in opts.path_strs.iter() {
        let path = Path::new(file);
        match fs::stat(&path) {
            Ok(stat) => {
                if !opts.list_dirs && stat.kind == TypeDirectory {
                    dirs.push(file.clone());
                }
                else {
                    // May as well reuse the stat result from earlier
                    // instead of just using File::from_path().
                    files.push(File::with_stat(stat, path, None));
                }
            }
            Err(e) => println!("{}: {}", file, e),
        }
    }

    // It's only worth printing out directory names if the user supplied
    // more than one of them.
    let print_dir_names = opts.path_strs.len() > 1;
    let mut first = files.is_empty();

    if !files.is_empty() {
        view(opts, files);
    }
    
    for dir_name in dirs.into_iter() {
        if first {
            first = false;
        }
        else {
            print!("\n");
        }

        match Dir::readdir(Path::new(dir_name.clone())) {
            Ok(dir) => {
                let unsorted_files = dir.files();
                let files: Vec<File> = opts.transform_files(unsorted_files);

                if print_dir_names {
                    println!("{}:", dir_name);
                }

                view(opts, files);
            }
            Err(e) => {
                println!("{}: {}", dir_name, e);
                return;
            }
        };
    }
}

fn view(options: &Options, files: Vec<File>) {
    match options.view {
        View::Details(ref cols) => details_view(options, cols, files),
        View::Lines => lines_view(files),
        View::Grid(across, width) => grid_view(across, width, files),
    }
}

fn lines_view(files: Vec<File>) {
    for file in files.iter() {
        println!("{}", file.file_name());
    }
}

fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
    // Check if all the files can be displayed on one line, and do
    // that if possible.
    let count = files.len();
    let spacing = 2;
    if files.iter().map(|ref f| f.name.len() + spacing).sum() + (count - 1) * spacing <= console_width {
        let names: Vec<String> = files.iter().map(|ref f| f.file_name().to_string()).collect();
        println!("{}", names.connect("  "));
        return;
    }

    // Otherwise, contort them into a grid.
    let max_column_length = files.iter().map(|f| f.file_name_width()).max().unwrap_or(0);
    let num_columns = (console_width + 1) / (max_column_length + 1);

    let mut num_rows = count / num_columns;
    if count % num_columns != 0 {
        num_rows += 1;
    }
    
    for y in range(0, num_rows) {
        for x in range(0, num_columns) {
{'fraction_non_alphanumeric': 0.14634851771511206, 'fraction_numerical': 0.0026030368763557484, 'mean_word_length': 2.3378378378378377, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 38, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2909996', 'n_tokens_mistral': 2321, 'n_tokens_neox': 2212, 'n_words': 604}