Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
vnotepad
Exa
提交
7acc1b09
E
Exa
项目概览
vnotepad
/
Exa
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
Exa
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
7acc1b09
编写于
2月 01, 2015
作者:
B
Ben S
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'recursion'
上级
c7e8a28e
a2020c64
变更
8
显示空白变更内容
内联
并排
Showing
8 changed file
with
105 addition
and
62 deletion
+105
-62
Cargo.lock
Cargo.lock
+12
-12
Cargo.toml
Cargo.toml
+1
-1
README.md
README.md
+1
-0
src/dir.rs
src/dir.rs
+8
-9
src/file.rs
src/file.rs
+12
-2
src/main.rs
src/main.rs
+33
-13
src/options.rs
src/options.rs
+32
-19
src/output.rs
src/output.rs
+6
-6
未找到文件。
Cargo.lock
浏览文件 @
7acc1b09
...
...
@@ -2,17 +2,17 @@
name = "exa"
version = "0.1.0"
dependencies = [
"ansi_term 0.4.
2
(registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.
1.4
(registry+https://github.com/rust-lang/crates.io-index)",
"ansi_term 0.4.
3
(registry+https://github.com/rust-lang/crates.io-index)",
"getopts 0.
2.0
(registry+https://github.com/rust-lang/crates.io-index)",
"git2 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"natord 1.0.
6
(registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.
1
(registry+https://github.com/rust-lang/crates.io-index)",
"users 0.2.
1
(registry+https://github.com/rust-lang/crates.io-index)",
"natord 1.0.
7
(registry+https://github.com/rust-lang/crates.io-index)",
"number_prefix 0.2.
2
(registry+https://github.com/rust-lang/crates.io-index)",
"users 0.2.
2
(registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ansi_term"
version = "0.4.
2
"
version = "0.4.
3
"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"regex 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
...
...
@@ -26,7 +26,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "getopts"
version = "0.
1.4
"
version = "0.
2.0
"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
...
...
@@ -55,7 +55,7 @@ name = "libressl-pnacl-sys"
version = "2.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"pnacl-build-helper 1.3.
1
(registry+https://github.com/rust-lang/crates.io-index)",
"pnacl-build-helper 1.3.
2
(registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
...
...
@@ -83,12 +83,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "natord"
version = "1.0.
6
"
version = "1.0.
7
"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "number_prefix"
version = "0.2.
1
"
version = "0.2.
2
"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
...
...
@@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "pnacl-build-helper"
version = "1.3.
1
"
version = "1.3.
2
"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
...
...
@@ -139,6 +139,6 @@ dependencies = [
[[package]]
name = "users"
version = "0.2.
1
"
version = "0.2.
2
"
source = "registry+https://github.com/rust-lang/crates.io-index"
Cargo.toml
浏览文件 @
7acc1b09
...
...
@@ -8,7 +8,7 @@ name = "exa"
[dependencies]
ansi_term
=
"0.4.2"
getopts
=
"0.
1.4
"
getopts
=
"0.
2.0
"
natord
=
"1.0.6"
number_prefix
=
"0.2.1"
users
=
"0.2.1"
...
...
README.md
浏览文件 @
7acc1b09
...
...
@@ -21,6 +21,7 @@ exa is a replacement for `ls` written in Rust.
-
**-i**
,
**--inode**
: show inode number column
-
**-l**
,
**--long**
: display extended details and attributes
-
**-r**
,
**--reverse**
: reverse sort order
-
**-R**
,
**--recurse**
: recurse into subdirectories
-
**-s**
,
**--sort=(field)**
: field to sort by
-
**-S**
,
**--blocks**
: show number of file system blocks
-
**-x**
,
**--across**
: sort multi-column view entries across
...
...
src/dir.rs
浏览文件 @
7acc1b09
...
...
@@ -21,11 +21,11 @@ impl Dir {
/// Create a new Dir object filled with all the files in the directory
/// pointed to by the given path. Fails if the directory can't be read, or
/// isn't actually a directory.
pub
fn
readdir
(
path
:
Path
)
->
IoResult
<
Dir
>
{
fs
::
readdir
(
&
path
)
.map
(|
paths
|
Dir
{
pub
fn
readdir
(
path
:
&
Path
)
->
IoResult
<
Dir
>
{
fs
::
readdir
(
path
)
.map
(|
paths
|
Dir
{
contents
:
paths
,
path
:
path
.clone
(),
git
:
Git
::
scan
(
&
path
)
.ok
(),
git
:
Git
::
scan
(
path
)
.ok
(),
})
}
...
...
@@ -102,12 +102,11 @@ impl Git {
/// path that gets passed in. This is used for getting the status of
/// directories, which don't really have an 'official' status.
fn
dir_status
(
&
self
,
dir
:
&
Path
)
->
String
{
let
s
tatus
=
self
.statuses
.iter
()
let
s
=
self
.statuses
.iter
()
.filter
(|
p
|
p
.0
.starts_with
(
dir
.as_vec
()))
.fold
(
git2
::
Status
::
empty
(),
|
a
,
b
|
a
|
b
.1
);
match
status
{
s
=>
format!
(
"{}{}"
,
Git
::
index_status
(
s
),
Git
::
working_tree_status
(
s
)),
}
format!
(
"{}{}"
,
Git
::
index_status
(
s
),
Git
::
working_tree_status
(
s
))
}
/// The character to display if the file has been modified, but not staged.
...
...
src/file.rs
浏览文件 @
7acc1b09
...
...
@@ -45,8 +45,18 @@ impl<'a> File<'a> {
/// Create a new File object from the given Stat result, and other data.
pub
fn
with_stat
(
stat
:
io
::
FileStat
,
path
:
&
Path
,
parent
:
Option
<&
'a
Dir
>
)
->
File
<
'a
>
{
let
v
=
path
.filename
()
.unwrap
();
// fails if / or . or ..
let
filename
=
String
::
from_utf8_lossy
(
v
);
// The filename to display is the last component of the path. However,
// the path has no components for `.`, `..`, and `/`, so in these
// cases, the entire path is used.
let
bytes
=
match
path
.components
()
.last
()
{
Some
(
b
)
=>
b
,
None
=>
path
.as_vec
(),
};
// Convert the string to UTF-8, replacing any invalid characters with
// replacement characters.
let
filename
=
String
::
from_utf8_lossy
(
bytes
);
File
{
path
:
path
.clone
(),
...
...
src/main.rs
浏览文件 @
7acc1b09
#![feature(collections,
core,
io,
libc,
os,
path,
std_misc)]
extern
crate
ansi_term
;
extern
crate
getopts
;
extern
crate
natord
;
extern
crate
number_prefix
;
extern
crate
users
;
...
...
@@ -12,7 +14,7 @@ use std::os::{args, set_exit_status};
use
dir
::
Dir
;
use
file
::
File
;
use
options
::
Options
;
use
options
::
{
Options
,
DirAction
}
;
pub
mod
column
;
pub
mod
dir
;
...
...
@@ -23,7 +25,7 @@ pub mod output;
pub
mod
term
;
fn
exa
(
options
:
&
Options
)
{
let
mut
dirs
:
Vec
<
String
>
=
vec!
[];
let
mut
dirs
:
Vec
<
Path
>
=
vec!
[];
let
mut
files
:
Vec
<
File
>
=
vec!
[];
// It's only worth printing out directory names if the user supplied
...
...
@@ -33,16 +35,14 @@ fn exa(options: &Options) {
// 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
options
.path_str
ings
()
{
for
file
in
options
.path_str
s
.iter
()
{
let
path
=
Path
::
new
(
file
);
match
fs
::
stat
(
&
path
)
{
Ok
(
stat
)
=>
{
if
!
options
.list_dirs
&&
stat
.kind
==
FileType
::
Directory
{
dirs
.push
(
file
.clone
()
);
if
stat
.kind
==
FileType
::
Directory
&&
options
.dir_action
!=
DirAction
::
AsFile
{
dirs
.push
(
path
);
}
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
));
}
}
...
...
@@ -55,10 +55,19 @@ fn exa(options: &Options) {
let
mut
first
=
files
.is_empty
();
if
!
files
.is_empty
()
{
options
.view
(
None
,
files
);
options
.view
(
None
,
&
files
[]
);
}
for
dir_name
in
dirs
.iter
()
{
// Directories are put on a stack rather than just being iterated through,
// as the vector can change as more directories are added.
loop
{
let
dir_path
=
match
dirs
.pop
()
{
None
=>
break
,
Some
(
f
)
=>
f
,
};
// Put a gap between directories, or between the list of files and the
// first directory.
if
first
{
first
=
false
;
}
...
...
@@ -66,19 +75,30 @@ fn exa(options: &Options) {
print!
(
"
\n
"
);
}
match
Dir
::
readdir
(
Path
::
new
(
dir_name
.clone
())
)
{
match
Dir
::
readdir
(
&
dir_path
)
{
Ok
(
ref
dir
)
=>
{
let
unsorted_files
=
dir
.files
();
let
files
:
Vec
<
File
>
=
options
.transform_files
(
unsorted_files
);
// When recursing, add any directories to the dirs stack
// backwards: the *last* element of the stack is used each
// time, so by inserting them backwards, they get displayed in
// the correct sort order.
if
options
.dir_action
==
DirAction
::
Recurse
{
for
dir
in
files
.iter
()
.filter
(|
f
|
f
.stat.kind
==
FileType
::
Directory
)
.rev
()
{
dirs
.push
(
dir
.path
.clone
());
}
}
if
count
>
1
{
println!
(
"{}:"
,
dir_
name
);
println!
(
"{}:"
,
dir_
path
.display
()
);
}
count
+=
1
;
options
.view
(
Some
(
dir
),
files
);
options
.view
(
Some
(
dir
),
&
files
[]
);
}
Err
(
e
)
=>
{
println!
(
"{}: {}"
,
dir_
name
,
e
);
println!
(
"{}: {}"
,
dir_
path
.display
()
,
e
);
return
;
}
};
...
...
src/options.rs
浏览文件 @
7acc1b09
extern
crate
getopts
;
extern
crate
natord
;
use
dir
::
Dir
;
use
file
::
File
;
use
column
::{
Column
,
SizeFormat
};
...
...
@@ -11,7 +8,9 @@ use term::dimensions;
use
std
::
ascii
::
AsciiExt
;
use
std
::
cmp
::
Ordering
;
use
std
::
fmt
;
use
std
::
slice
::
Iter
;
use
getopts
;
use
natord
;
use
self
::
Misfire
::
*
;
...
...
@@ -19,8 +18,8 @@ use self::Misfire::*;
/// command-line options.
#[derive(PartialEq,
Debug)]
pub
struct
Options
{
pub
list_dirs
:
bool
,
path_strs
:
Vec
<
String
>
,
pub
dir_action
:
DirAction
,
p
ub
p
ath_strs
:
Vec
<
String
>
,
reverse
:
bool
,
show_invisibles
:
bool
,
sort_field
:
SortField
,
...
...
@@ -43,6 +42,7 @@ impl Options {
getopts
::
optflag
(
"l"
,
"long"
,
"display extended details and attributes"
),
getopts
::
optflag
(
"i"
,
"inode"
,
"show each file's inode number"
),
getopts
::
optflag
(
"r"
,
"reverse"
,
"reverse order of files"
),
getopts
::
optflag
(
"R"
,
"recurse"
,
"recurse into directories"
),
getopts
::
optopt
(
"s"
,
"sort"
,
"field to sort by"
,
"WORD"
),
getopts
::
optflag
(
"S"
,
"blocks"
,
"show number of file system blocks"
),
getopts
::
optflag
(
"x"
,
"across"
,
"sort multi-column view entries across"
),
...
...
@@ -64,7 +64,7 @@ impl Options {
};
Ok
(
Options
{
list_dirs
:
matches
.opt_present
(
"list-dirs"
),
dir_action
:
try!
(
dir_action
(
&
matches
)
),
path_strs
:
if
matches
.free
.is_empty
()
{
vec!
[
"."
.to_string
()
]
}
else
{
matches
.free
.clone
()
},
reverse
:
matches
.opt_present
(
"reverse"
),
show_invisibles
:
matches
.opt_present
(
"all"
),
...
...
@@ -73,13 +73,8 @@ impl Options {
})
}
/// Iterate over the non-option arguments left oven from getopts.
pub
fn
path_strings
(
&
self
)
->
Iter
<
String
>
{
self
.path_strs
.iter
()
}
/// Display the files using this Option's View.
pub
fn
view
(
&
self
,
dir
:
Option
<&
Dir
>
,
files
:
Vec
<
File
>
)
{
pub
fn
view
(
&
self
,
dir
:
Option
<&
Dir
>
,
files
:
&
[
File
]
)
{
self
.view
.view
(
dir
,
files
)
}
...
...
@@ -113,7 +108,13 @@ impl Options {
}
}
/// User-supplied field to sort by
/// What to do when encountering a directory?
#[derive(PartialEq,
Debug,
Copy)]
pub
enum
DirAction
{
AsFile
,
List
,
Recurse
}
/// User-supplied field to sort by.
#[derive(PartialEq,
Debug,
Copy)]
pub
enum
SortField
{
Unsorted
,
Name
,
Extension
,
Size
,
FileInode
...
...
@@ -238,6 +239,18 @@ fn file_size(matches: &getopts::Matches) -> Result<SizeFormat, Misfire> {
}
}
fn
dir_action
(
matches
:
&
getopts
::
Matches
)
->
Result
<
DirAction
,
Misfire
>
{
let
recurse
=
matches
.opt_present
(
"recurse"
);
let
list
=
matches
.opt_present
(
"list-dirs"
);
match
(
recurse
,
list
)
{
(
true
,
true
)
=>
Err
(
Misfire
::
Conflict
(
"recurse"
,
"list-dirs"
)),
(
true
,
false
)
=>
Ok
(
DirAction
::
Recurse
),
(
false
,
true
)
=>
Ok
(
DirAction
::
AsFile
),
(
false
,
false
)
=>
Ok
(
DirAction
::
List
),
}
}
#[derive(PartialEq,
Copy,
Debug)]
pub
struct
Columns
{
size_format
:
SizeFormat
,
...
...
@@ -332,15 +345,15 @@ mod test {
#[test]
fn
files
()
{
let
opts
=
Options
::
getopts
(
&
[
"this file"
.to_string
(),
"that file"
.to_string
()
])
.unwrap
();
let
args
:
Vec
<
&
String
>
=
opts
.path_strings
()
.collect
()
;
assert_eq!
(
args
,
vec!
[
&
"this file"
.to_string
(),
&
"that file"
.to_string
()
])
let
args
:
Vec
<
String
>
=
opts
.path_strs
;
assert_eq!
(
args
,
vec!
[
"this file"
.to_string
(),
"that file"
.to_string
()
])
}
#[test]
fn
no_args
()
{
let
opts
=
Options
::
getopts
(
&
[])
.unwrap
();
let
args
:
Vec
<
&
String
>
=
opts
.path_strings
()
.collect
()
;
assert_eq!
(
args
,
vec!
[
&
"."
.to_string
()
])
let
args
:
Vec
<
String
>
=
opts
.path_strs
;
assert_eq!
(
args
,
vec!
[
"."
.to_string
()
])
}
#[test]
...
...
src/output.rs
浏览文件 @
7acc1b09
...
...
@@ -18,7 +18,7 @@ pub enum View {
}
impl
View
{
pub
fn
view
(
&
self
,
dir
:
Option
<&
Dir
>
,
files
:
Vec
<
File
>
)
{
pub
fn
view
(
&
self
,
dir
:
Option
<&
Dir
>
,
files
:
&
[
File
]
)
{
match
*
self
{
View
::
Grid
(
across
,
width
)
=>
grid_view
(
across
,
width
,
files
),
View
::
Details
(
ref
cols
,
header
)
=>
details_view
(
&*
cols
.for_dir
(
dir
),
files
,
header
),
...
...
@@ -28,13 +28,13 @@ impl View {
}
/// The lines view literally just displays each file, line-by-line.
fn
lines_view
(
files
:
Vec
<
File
>
)
{
fn
lines_view
(
files
:
&
[
File
]
)
{
for
file
in
files
.iter
()
{
println!
(
"{}"
,
file
.file_name_view
()
.text
);
}
}
fn
fit_into_grid
(
across
:
bool
,
console_width
:
usize
,
files
:
&
Vec
<
File
>
)
->
Option
<
(
usize
,
Vec
<
usize
>
)
>
{
fn
fit_into_grid
(
across
:
bool
,
console_width
:
usize
,
files
:
&
[
File
]
)
->
Option
<
(
usize
,
Vec
<
usize
>
)
>
{
// TODO: this function could almost certainly be optimised...
// surely not *all* of the numbers of lines are worth searching through!
...
...
@@ -86,8 +86,8 @@ fn fit_into_grid(across: bool, console_width: usize, files: &Vec<File>) -> Optio
return
None
;
}
fn
grid_view
(
across
:
bool
,
console_width
:
usize
,
files
:
Vec
<
File
>
)
{
if
let
Some
((
num_lines
,
widths
))
=
fit_into_grid
(
across
,
console_width
,
&
files
)
{
fn
grid_view
(
across
:
bool
,
console_width
:
usize
,
files
:
&
[
File
]
)
{
if
let
Some
((
num_lines
,
widths
))
=
fit_into_grid
(
across
,
console_width
,
files
)
{
for
y
in
range
(
0
,
num_lines
)
{
for
x
in
range
(
0
,
widths
.len
())
{
let
num
=
if
across
{
...
...
@@ -122,7 +122,7 @@ fn grid_view(across: bool, console_width: usize, files: Vec<File>) {
}
}
fn
details_view
(
columns
:
&
[
Column
],
files
:
Vec
<
File
>
,
header
:
bool
)
{
fn
details_view
(
columns
:
&
[
Column
],
files
:
&
[
File
]
,
header
:
bool
)
{
// The output gets formatted into columns, which looks nicer. To
// do this, we have to write the results into a table, instead of
// displaying each file immediately, then calculating the maximum
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录