Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
vnotepad
Exa
提交
48d8a46d
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,发现更多精彩内容 >>
提交
48d8a46d
编写于
5月 26, 2014
作者:
B
Ben S
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Code cleanup (commenting the why)
上级
7091a42a
变更
5
显示空白变更内容
内联
并排
Showing
5 changed file
with
96 addition
and
67 deletion
+96
-67
colours.rs
colours.rs
+24
-11
exa.rs
exa.rs
+19
-6
file.rs
file.rs
+47
-44
format.rs
format.rs
+2
-2
options.rs
options.rs
+4
-4
未找到文件。
colours.rs
浏览文件 @
48d8a46d
pub
enum
Colour
{
// These are the standard numeric sequences.
// See http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
Black
=
30
,
Red
=
31
,
Green
=
32
,
Yellow
=
33
,
Blue
=
34
,
Purple
=
35
,
Cyan
=
36
,
White
=
37
,
}
// There are only three different styles: plain (no formatting), only
// a foreground colour, and a catch-all for anything more complicated
// than that. It's technically possible to write other cases such as
// "bold foreground", but probably isn't worth writing all the code.
pub
enum
Style
{
Plain
,
Foreground
(
Colour
),
Style
(
StyleStruct
),
}
// Having a struct inside an enum is currently unfinished in Rust, but
// should be put in there when that feature is complete.
pub
struct
StyleStruct
{
foreground
:
Colour
,
background
:
Option
<
Colour
>
,
...
...
@@ -28,8 +38,8 @@ impl Style {
};
let
bo
=
if
bold
{
"1;"
}
else
{
""
};
let
un
=
if
underline
{
"4;"
}
else
{
""
};
let
re
=
format!
(
"
\x1B
[{}{}{}{}m{}
\x1B
[0m"
,
bo
,
un
,
bg
,
foreground
as
int
,
input
.to_strbuf
());
return
re
.to_owned
();
let
painted
=
format!
(
"
\x1B
[{}{}{}{}m{}
\x1B
[0m"
,
bo
,
un
,
bg
,
foreground
as
int
,
input
.to_strbuf
());
return
painted
.to_owned
();
}
}
}
...
...
@@ -63,6 +73,9 @@ impl Style {
}
impl
Colour
{
// This is a short-cut so you don't have to use Blue.normal() just
// to turn Blue into a Style.
pub
fn
paint
(
&
self
,
input
:
&
str
)
->
String
{
let
re
=
format!
(
"
\x1B
[{}m{}
\x1B
[0m"
,
*
self
as
int
,
input
);
return
re
.to_owned
();
...
...
exa.rs
浏览文件 @
48d8a46d
...
...
@@ -16,15 +16,16 @@ pub mod unix;
pub
mod
options
;
fn
main
()
{
let
args
=
os
::
args
()
.iter
()
.map
(|
x
|
x
.to_strbuf
())
.collect
();
let
args
=
os
::
args
();
match
Options
::
getopts
(
args
)
{
Err
(
err
)
=>
println!
(
"Invalid options:
\n
{}"
,
err
.to_err_msg
()),
Ok
(
opts
)
=>
{
// Default to listing the current directory when a target
// isn't specified (mimic the behaviour of ls)
let
strs
=
if
opts
.dirs
.is_empty
()
{
vec!
(
".
/
"
.to_strbuf
())
vec!
(
"."
.to_strbuf
())
}
else
{
opts
.dirs
.clone
()
...
...
@@ -46,21 +47,33 @@ fn exa(options: &Options, path: Path) {
let
unordered_files
:
Vec
<
File
>
=
paths
.iter
()
.map
(|
path
|
File
::
from_path
(
path
))
.collect
();
let
files
:
Vec
<&
File
>
=
options
.transform_files
(
&
unordered_files
);
// 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
// width of each column based on the length of the results and
// padding the fields during output.
let
table
:
Vec
<
Vec
<
String
>>
=
files
.iter
()
.map
(|
f
|
options
.columns
.iter
()
.map
(|
c
|
f
.display
(
c
))
.collect
())
.collect
();
// Each column needs to have its invisible colour-formatting
// characters stripped before it has its width calculated, or the
// width will be incorrect and the columns won't line up properly.
// This is fairly expensive to do (it uses a regex), so the
// results are cached.
let
lengths
:
Vec
<
Vec
<
uint
>>
=
table
.iter
()
.map
(|
row
|
row
.iter
()
.map
(|
col
|
colours
::
strip_formatting
(
col
)
.len
())
.collect
())
.collect
();
let
maxe
s
:
Vec
<
uint
>
=
range
(
0
,
options
.columns
.len
())
let
column_width
s
:
Vec
<
uint
>
=
range
(
0
,
options
.columns
.len
())
.map
(|
n
|
lengths
.iter
()
.map
(|
row
|
*
row
.get
(
n
))
.max
()
.unwrap
())
.collect
();
for
(
field_lengths
,
row
)
in
lengths
.iter
()
.zip
(
table
.iter
())
{
let
mut
first
=
true
;
for
((
column_length
,
cell
),
field_length
)
in
maxe
s
.iter
()
.zip
(
row
.iter
())
.zip
(
field_lengths
.iter
())
{
for
((
column_length
,
cell
),
field_length
)
in
column_width
s
.iter
()
.zip
(
row
.iter
())
.zip
(
field_lengths
.iter
())
{
if
first
{
first
=
false
;
}
else
{
...
...
file.rs
浏览文件 @
48d8a46d
...
...
@@ -3,7 +3,7 @@ use std::io;
use
colours
::{
Plain
,
Style
,
Black
,
Red
,
Green
,
Yellow
,
Blue
,
Purple
,
Cyan
};
use
column
::{
Column
,
Permissions
,
FileName
,
FileSize
,
User
,
Group
};
use
format
::{
format
BinaryBytes
,
formatDecimalB
ytes
};
use
format
::{
format
_metric_bytes
,
format_IEC_b
ytes
};
use
unix
::{
get_user_name
,
get_group_name
};
static
MEDIA_TYPES
:
&
'static
[
&
'static
str
]
=
&
[
...
...
@@ -15,9 +15,13 @@ static COMPRESSED_TYPES: &'static [&'static str] = &[
"zip"
,
"tar"
,
"Z"
,
"gz"
,
"bz2"
,
"a"
,
"ar"
,
"7z"
,
"iso"
,
"dmg"
,
"tc"
,
"rar"
,
"par"
];
// Each file is definitely going to get `stat`ted at least once, if
// only to determine what kind of file it is, so carry the `stat`
// result around with the file for safe keeping.
// Instead of working with Rust's Paths, we have our own File object
// that holds the Path and various cached information. Each file is
// definitely going to have its filename used at least once, its stat
// information queried at least once, and its file extension extracted
// at least once, so we may as well carry around that information with
// the actual path.
pub
struct
File
<
'a
>
{
pub
name
:
&
'a
str
,
pub
ext
:
Option
<&
'a
str
>
,
...
...
@@ -27,12 +31,13 @@ pub struct File<'a> {
impl
<
'a
>
File
<
'a
>
{
pub
fn
from_path
(
path
:
&
'a
Path
)
->
File
<
'a
>
{
// Getting the string from a filename fails whenever it's not
// UTF-8 representable - just assume it is for now.
let
filename
:
&
str
=
path
.filename_str
()
.unwrap
();
// We have to use lstat here instad of file.stat(), as it
// doesn't follow symbolic links. Otherwise, the stat() call
// will fail if it encounters a link that's target is
// non-existent.
// Use lstat here instead of file.stat(), as it doesn't follow
// symbolic links. Otherwise, the stat() call will fail if it
// encounters a link that's target is non-existent.
let
stat
:
io
::
FileStat
=
match
fs
::
lstat
(
path
)
{
Ok
(
stat
)
=>
stat
,
Err
(
e
)
=>
fail
!
(
"Couldn't stat {}: {}"
,
filename
,
e
),
...
...
@@ -47,7 +52,10 @@ impl<'a> File<'a> {
}
fn
ext
(
name
:
&
'a
str
)
->
Option
<&
'a
str
>
{
let
re
=
regex!
(
r"\.(.+)$"
);
// The extension is the series of characters after a dot at
// the end of a filename. This deliberately also counts
// dotfiles - the ".git" folder has the extension "git".
let
re
=
regex!
(
r"\.([^.]+)$"
);
re
.captures
(
name
)
.map
(|
caps
|
caps
.at
(
1
))
}
...
...
@@ -57,27 +65,30 @@ impl<'a> File<'a> {
pub
fn
display
(
&
self
,
column
:
&
Column
)
->
String
{
match
*
column
{
Permissions
=>
self
.permissions
(),
Permissions
=>
self
.permissions
_string
(),
FileName
=>
self
.file_colour
()
.paint
(
self
.name
.as_slice
()),
FileSize
(
si
)
=>
self
.file_size
(
si
),
FileSize
(
use_iec
)
=>
self
.file_size
(
use_iec
),
// Display the ID if the user/group doesn't exist, which
// usually means it was deleted but its files weren't.
User
=>
get_user_name
(
self
.stat.unstable.uid
as
i32
)
.unwrap_or
(
self
.stat.unstable.uid
.to_str
()),
Group
=>
get_group_name
(
self
.stat.unstable.gid
as
u32
)
.unwrap_or
(
self
.stat.unstable.gid
.to_str
()),
}
}
fn
file_size
(
&
self
,
si
:
bool
)
->
String
{
fn
file_size
(
&
self
,
use_iec_prefixes
:
bool
)
->
String
{
// Don't report file sizes for directories. I've never looked
// at one of those numbers and gained any information from it.
if
self
.stat.kind
==
io
::
TypeDirectory
{
Black
.bold
()
.paint
(
"---"
)
}
else
{
let
size
Str
=
if
si
{
format
BinaryB
ytes
(
self
.stat.size
)
let
size
_str
=
if
use_iec_prefixes
{
format
_IEC_b
ytes
(
self
.stat.size
)
}
else
{
format
DecimalB
ytes
(
self
.stat.size
)
format
_metric_b
ytes
(
self
.stat.size
)
};
return
Green
.bold
()
.paint
(
size
S
tr
.as_slice
());
return
Green
.bold
()
.paint
(
size
_s
tr
.as_slice
());
}
}
...
...
@@ -116,38 +127,30 @@ impl<'a> File<'a> {
}
}
fn
permissions
(
&
self
)
->
String
{
fn
permissions
_string
(
&
self
)
->
String
{
let
bits
=
self
.stat.perm
;
return
format!
(
"{}{}{}{}{}{}{}{}{}{}"
,
self
.type_char
(),
File
::
bit
(
bits
,
io
::
UserRead
,
"r"
,
Yellow
.bold
()),
File
::
bit
(
bits
,
io
::
UserWrite
,
"w"
,
Red
.bold
()),
File
::
bit
(
bits
,
io
::
UserExecute
,
"x"
,
Green
.bold
()
.underline
()),
File
::
bit
(
bits
,
io
::
GroupRead
,
"r"
,
Yellow
.normal
()),
File
::
bit
(
bits
,
io
::
GroupWrite
,
"w"
,
Red
.normal
()),
File
::
bit
(
bits
,
io
::
GroupExecute
,
"x"
,
Green
.normal
()),
File
::
bit
(
bits
,
io
::
OtherRead
,
"r"
,
Yellow
.normal
()),
File
::
bit
(
bits
,
io
::
OtherWrite
,
"w"
,
Red
.normal
()),
File
::
bit
(
bits
,
io
::
OtherExecute
,
"x"
,
Green
.normal
()),
// The first three are bold because they're the ones used
// most often.
File
::
permission_bit
(
bits
,
io
::
UserRead
,
"r"
,
Yellow
.bold
()),
File
::
permission_bit
(
bits
,
io
::
UserWrite
,
"w"
,
Red
.bold
()),
File
::
permission_bit
(
bits
,
io
::
UserExecute
,
"x"
,
Green
.bold
()
.underline
()),
File
::
permission_bit
(
bits
,
io
::
GroupRead
,
"r"
,
Yellow
.normal
()),
File
::
permission_bit
(
bits
,
io
::
GroupWrite
,
"w"
,
Red
.normal
()),
File
::
permission_bit
(
bits
,
io
::
GroupExecute
,
"x"
,
Green
.normal
()),
File
::
permission_bit
(
bits
,
io
::
OtherRead
,
"r"
,
Yellow
.normal
()),
File
::
permission_bit
(
bits
,
io
::
OtherWrite
,
"w"
,
Red
.normal
()),
File
::
permission_bit
(
bits
,
io
::
OtherExecute
,
"x"
,
Green
.normal
()),
);
}
fn
bit
(
bits
:
io
::
FilePermission
,
bit
:
io
::
FilePermission
,
oth
er
:
&
'static
str
,
style
:
Style
)
->
String
{
fn
permission_bit
(
bits
:
io
::
FilePermission
,
bit
:
io
::
FilePermission
,
charact
er
:
&
'static
str
,
style
:
Style
)
->
String
{
if
bits
.contains
(
bit
)
{
style
.paint
(
oth
er
.as_slice
())
style
.paint
(
charact
er
.as_slice
())
}
else
{
Black
.bold
()
.paint
(
"-"
.as_slice
())
}
}
}
impl
<
'a
>
Clone
for
File
<
'a
>
{
fn
clone
(
&
self
)
->
File
<
'a
>
{
return
File
{
path
:
self
.path
,
stat
:
self
.stat
,
name
:
self
.name
.clone
(),
ext
:
self
.ext
.clone
(),
};
}
}
format.rs
浏览文件 @
48d8a46d
...
...
@@ -15,10 +15,10 @@ fn formatBytes(mut amount: u64, kilo: u64, prefixes: &[&str]) -> String {
format!
(
"{}{}"
,
amount
,
prefixes
[
prefix
])
}
pub
fn
format
BinaryB
ytes
(
amount
:
u64
)
->
String
{
pub
fn
format
_IEC_b
ytes
(
amount
:
u64
)
->
String
{
formatBytes
(
amount
,
1024
,
IEC_PREFIXES
)
}
pub
fn
format
DecimalB
ytes
(
amount
:
u64
)
->
String
{
pub
fn
format
_metric_b
ytes
(
amount
:
u64
)
->
String
{
formatBytes
(
amount
,
1000
,
METRIC_PREFIXES
)
}
options.rs
浏览文件 @
48d8a46d
...
...
@@ -65,7 +65,7 @@ impl Options {
return
columns
;
}
fn
sho
w
(
&
self
,
f
:
&
File
)
->
bool
{
fn
sho
uld_display
(
&
self
,
f
:
&
File
)
->
bool
{
if
self
.showInvisibles
{
true
}
else
{
...
...
@@ -75,7 +75,7 @@ impl Options {
pub
fn
transform_files
<
'a
>
(
&
self
,
unordered_files
:
&
'a
Vec
<
File
<
'a
>>
)
->
Vec
<&
'a
File
<
'a
>>
{
let
mut
files
:
Vec
<&
'a
File
<
'a
>>
=
unordered_files
.iter
()
.filter
(|
&
f
|
self
.sho
w
(
f
))
.filter
(|
&
f
|
self
.sho
uld_display
(
f
))
.collect
();
match
self
.sortField
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录