Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
O
oh-my-zsh
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
github
oh-my-zsh
Commits
c1c107cf
Commit
c1c107cf
authored
Mar 17, 2014
by
Pavol Juhas
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add scd plugin for smart change of directory.
Synced with the scd-tracker branch pavoljuhas/oh-my-zsh@2f78243cad3509058d142aa4b70a604e75ec741e.
parent
6952105b
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
165 additions
and
163 deletions
+165
-163
README.md
plugins/scd/README.md
+1
-2
scd
plugins/scd/scd
+164
-161
No files found.
plugins/scd/README.md
View file @
c1c107cf
...
@@ -111,8 +111,7 @@ SCD_MEANLIFE</dt><dd>
...
@@ -111,8 +111,7 @@ SCD_MEANLIFE</dt><dd>
SCD_THRESHOLD
</dt><dd>
SCD_THRESHOLD
</dt><dd>
threshold for cumulative directory likelihood. Directories with
threshold for cumulative directory likelihood. Directories with
lower likelihood are excluded unless they are the only match to
a lower likelihood compared to the best match are excluded (0.005).
scd patterns.
</dd><dt>
</dd><dt>
SCD_SCRIPT
</dt><dd>
SCD_SCRIPT
</dt><dd>
...
...
plugins/scd/scd
View file @
c1c107cf
#!/bin/zsh -f
#!/bin/zsh -f
emulate
-L
zsh
emulate
-L
zsh
local
EXIT
=
return
if
[[
$(
whence
-w
$0
)
==
*
:
' '
command
]]
;
then
if
[[
$(
whence
-w
$0
)
==
*
:
' '
command
]]
;
then
emulate
-R
zsh
emulate
-R
zsh
alias
return
=
exit
local
RUNNING_AS_COMMAND
=
1
local
RUNNING_AS_COMMAND
=
1
EXIT
=
exit
fi
fi
local
DOC
=
'scd -- smart change to a recently used directory
local
DOC
=
'scd -- smart change to a recently used directory
...
@@ -37,8 +38,9 @@ local SCD_ALIAS=~/.scdalias.zsh
...
@@ -37,8 +38,9 @@ local SCD_ALIAS=~/.scdalias.zsh
local
ICASE a d m p i tdir maxrank threshold
local
ICASE a d m p i tdir maxrank threshold
local
opt_help opt_add opt_unindex opt_recursive opt_verbose
local
opt_help opt_add opt_unindex opt_recursive opt_verbose
local
opt_alias opt_unalias opt_list
local
opt_alias opt_unalias opt_list
local
-A
drank dalias
dkey
local
-A
drank dalias
local
dmatching
local
dmatching
local
last_directory
setopt extendedhistory extendedglob noautonamedirs brace_ccl
setopt extendedhistory extendedglob noautonamedirs brace_ccl
...
@@ -56,11 +58,11 @@ zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
...
@@ -56,11 +58,11 @@ zparseopts -D -- a=opt_add -add=opt_add -unindex=opt_unindex \
r
=
opt_recursive
-recursive
=
opt_recursive
\
r
=
opt_recursive
-recursive
=
opt_recursive
\
-alias
:
=
opt_alias
-unalias
=
opt_unalias
-list
=
opt_list
\
-alias
:
=
opt_alias
-unalias
=
opt_unalias
-list
=
opt_list
\
v
=
opt_verbose
-verbose
=
opt_verbose
h
=
opt_help
-help
=
opt_help
\
v
=
opt_verbose
-verbose
=
opt_verbose
h
=
opt_help
-help
=
opt_help
\
||
return
$?
||
$EXIT
$?
if
[[
-n
$opt_help
]]
;
then
if
[[
-n
$opt_help
]]
;
then
print
$DOC
print
$DOC
return
$EXIT
fi
fi
# load directory aliases if they exist
# load directory aliases if they exist
...
@@ -79,8 +81,8 @@ _scd_Y19oug_abspath() {
...
@@ -79,8 +81,8 @@ _scd_Y19oug_abspath() {
# define directory alias
# define directory alias
if
[[
-n
$opt_alias
]]
;
then
if
[[
-n
$opt_alias
]]
;
then
if
[[
-n
$1
&&
!
-d
$1
]]
;
then
if
[[
-n
$1
&&
!
-d
$1
]]
;
then
print
-u2
"'
$1
' is not a directory"
print
-u2
"'
$1
' is not a directory
.
"
return
1
$EXIT
1
fi
fi
a
=
${
opt_alias
[-1]#=
}
a
=
${
opt_alias
[-1]#=
}
_scd_Y19oug_abspath d
${
1
:-
$PWD
}
_scd_Y19oug_abspath d
${
1
:-
$PWD
}
...
@@ -93,19 +95,19 @@ if [[ -n $opt_alias ]]; then
...
@@ -93,19 +95,19 @@ if [[ -n $opt_alias ]]; then
hash
-d
--
$a
=
$d
hash
-d
--
$a
=
$d
hash
-dL
>
|
$SCD_ALIAS
hash
-dL
>
|
$SCD_ALIAS
)
)
return
$?
$EXIT
$?
fi
fi
# undefine directory alias
# undefine directory alias
if
[[
-n
$opt_unalias
]]
;
then
if
[[
-n
$opt_unalias
]]
;
then
if
[[
-n
$1
&&
!
-d
$1
]]
;
then
if
[[
-n
$1
&&
!
-d
$1
]]
;
then
print
-u2
"'
$1
' is not a directory"
print
-u2
"'
$1
' is not a directory
.
"
return
1
$EXIT
1
fi
fi
_scd_Y19oug_abspath a
${
1
:-
$PWD
}
_scd_Y19oug_abspath a
${
1
:-
$PWD
}
a
=
$(
print
-rD
${
a
})
a
=
$(
print
-rD
${
a
})
if
[[
$a
!=
[
~][^/]##
]]
;
then
if
[[
$a
!=
[
~][^/]##
]]
;
then
return
$EXIT
fi
fi
a
=
${
a
#[~]
}
a
=
${
a
#[~]
}
# unalias in the current shell, update alias file if successful
# unalias in the current shell, update alias file if successful
...
@@ -118,35 +120,39 @@ if [[ -n $opt_unalias ]]; then
...
@@ -118,35 +120,39 @@ if [[ -n $opt_unalias ]]; then
hash
-dL
>
|
$SCD_ALIAS
hash
-dL
>
|
$SCD_ALIAS
)
)
fi
fi
return
$?
$EXIT
$?
fi
fi
# Rewrite
the history file
if it is at least 20% oversized
# Rewrite
directory index
if it is at least 20% oversized
if
[[
-s
$SCD_HISTFILE
]]
&&
\
if
[[
-s
$SCD_HISTFILE
]]
&&
\
((
$(
wc
-l
<
$SCD_HISTFILE
)
>
1.2
*
$SCD_HISTSIZE
))
;
then
((
$(
wc
-l
<
$SCD_HISTFILE
)
>
1.2
*
$SCD_HISTSIZE
))
;
then
m
=(
${
(f)
"
$(
<
$SCD_HISTFILE
)
"
}
)
m
=(
${
(f)
"
$(
<
$SCD_HISTFILE
)
"
}
)
print
-lr
--
${
m
[-
$SCD_HISTSIZE
,-1]
}
>
|
${
SCD_HISTFILE
}
print
-lr
--
${
m
[-
$SCD_HISTSIZE
,-1]
}
>
|
${
SCD_HISTFILE
}
fi
fi
# Determine the last recorded directory
if
[[
-s
${
SCD_HISTFILE
}
]]
;
then
last_directory
=
${
"
$(
tail
-1
${
SCD_HISTFILE
})
"
#*;
}
fi
# Internal functions are prefixed with "_scd_Y19oug_".
# Internal functions are prefixed with "_scd_Y19oug_".
# The "record" function adds a non-repeating directory to the history
# The "record" function adds its arguments to the directory index.
# and turns on history writing.
_scd_Y19oug_record
()
{
_scd_Y19oug_record
()
{
while
[[
-n
$
1
&&
$1
==
${
history
[
$HISTCMD
]
}
]]
;
do
while
[[
-n
$
last_directory
&&
$1
==
$last_directory
]]
;
do
shift
shift
done
done
if
[[
$#
!=
0
]]
;
then
if
[[
$#
-gt
0
]]
;
then
(
umask
077
;
:
>>
|
$SCD_HISTFILE
)
(
umask
077
p
=
":
${
EPOCHSECONDS
}
:0;"
p
=
":
${
EPOCHSECONDS
}
:0;"
print
-lr
--
${
p
}${
^*
}
>>
$SCD_HISTFILE
print
-lr
--
${
p
}${
^*
}
>>
|
$SCD_HISTFILE
)
fi
fi
}
}
if
[[
-n
$opt_add
]]
;
then
if
[[
-n
$opt_add
]]
;
then
for
a
;
do
for
d
;
do
if
[[
!
-d
$
a
]]
;
then
if
[[
!
-d
$
d
]]
;
then
print
-u
2
"Directory
$a
does not exist
"
print
-u
2
"Directory '
$d
' does not exist.
"
return
2
$EXIT
2
fi
fi
done
done
_scd_Y19oug_abspath m
${
*
:-
$PWD
}
_scd_Y19oug_abspath m
${
*
:-
$PWD
}
...
@@ -158,13 +164,13 @@ if [[ -n $opt_add ]]; then
...
@@ -158,13 +164,13 @@ if [[ -n $opt_add ]]; then
print
"[done]"
print
"[done]"
done
done
fi
fi
return
$EXIT
fi
fi
# take care of removing entries from the directory index
# take care of removing entries from the directory index
if
[[
-n
$opt_unindex
]]
;
then
if
[[
-n
$opt_unindex
]]
;
then
if
[[
!
-s
$SCD_HISTFILE
]]
;
then
if
[[
!
-s
$SCD_HISTFILE
]]
;
then
return
$EXIT
fi
fi
# expand existing directories in the argument list
# expand existing directories in the argument list
for
i
in
{
1..
$#}
;
do
for
i
in
{
1..
$#}
;
do
...
@@ -190,161 +196,158 @@ if [[ -n $opt_unindex ]]; then
...
@@ -190,161 +196,158 @@ if [[ -n $opt_unindex ]]; then
}
}
}
}
{ print $0 }
{ print $0 }
'
$SCD_HISTFILE
${
*
:-
$PWD
}
)
"
||
return
$?
'
$SCD_HISTFILE
${
*
:-
$PWD
}
)
"
||
$EXIT
$?
:
>
|
${
SCD_HISTFILE
}
:
>
|
${
SCD_HISTFILE
}
[[
${#
m
}
==
0
]]
||
print
-r
--
$m
>>
${
SCD_HISTFILE
}
[[
${#
m
}
==
0
]]
||
print
-r
--
$m
>>
${
SCD_HISTFILE
}
return
$EXIT
fi
fi
# The "action" function is called when there is just one target directory.
# The "action" function is called when there is just one target directory.
_scd_Y19oug_action
()
{
_scd_Y19oug_action
()
{
if
[[
-n
$opt_list
]]
;
then
cd
$1
||
return
$?
for
d
;
do
if
[[
-z
$SCD_SCRIPT
&&
-n
$RUNNING_AS_COMMAND
]]
;
then
a
=
${
(k)dalias[(r)
${
d
}
]
}
print
-u2
"Warning: running as command with SCD_SCRIPT undefined."
print
-r
--
"#
$a
"
fi
print
-r
--
$d
if
[[
-n
$SCD_SCRIPT
]]
;
then
done
print
-r
"cd
${
(q)1
}
"
>
|
$SCD_SCRIPT
elif
[[
$#
==
1
]]
;
then
if
[[
-z
$SCD_SCRIPT
&&
-n
$RUNNING_AS_COMMAND
]]
;
then
print
-u2
"Warning: running as command with SCD_SCRIPT undefined."
fi
[[
-n
$SCD_SCRIPT
]]
&&
(
umask
077
;
print
-r
"cd
${
(q)1
}
"
>
|
$SCD_SCRIPT
)
[[
-N
$SCD_HISTFILE
]]
&&
touch
-a
$SCD_HISTFILE
cd
$1
# record the new directory unless already done in some chpwd hook
[[
-N
$SCD_HISTFILE
]]
||
_scd_Y19oug_record
$PWD
fi
fi
}
}
# handle different argument scenarios ----------------------------------------
# Match and rank patterns to the index file
# set global arrays dmatching and drank
_scd_Y19oug_match
()
{
## single argument that is an existing directory or directory alias
if
[[
$#
==
1
]]
&&
\
[[
-d
${
d
::
=
$1
}
||
-d
${
d
::
=
${
nameddirs
[
$1
]
}}
]]
&&
[[
-x
$d
]]
;
then
_scd_Y19oug_abspath dmatching
$d
drank[
${
dmatching
[1]
}
]=
1
return
fi
## single argument that is an existing directory
# ignore case unless there is an argument with an uppercase letter
if
[[
$#
==
1
&&
-d
$1
&&
-x
$1
]]
;
then
[[
"
$*
"
==
*
[[
:upper:]]
*
]]
||
ICASE
=
'(#i)'
_scd_Y19oug_action
$1
return
$?
## single argument that is an alias
elif
[[
$#
==
1
&&
-d
${
d
::
=
${
nameddirs
[
$1
]
}}
]]
;
then
_scd_Y19oug_action
$d
return
$?
fi
# ignore case unless there is an argument with an uppercase letter
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
[[
"
$*
"
==
*
[[
:upper:]]
*
]]
||
ICASE
=
'(#i)'
# include a dummy entry for splitting of an empty string is buggy
[[
-s
$SCD_HISTFILE
]]
&&
drank
=(
${
(f)
"
$(
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
print
-l
/dev/null
-10
# include a dummy entry for splitting of an empty string is buggy
<
$SCD_HISTFILE
\
[[
-s
$SCD_HISTFILE
]]
&&
drank
=(
${
(f)
"
$(
awk
-v
epochseconds
=
$EPOCHSECONDS
-v
meanlife
=
$SCD_MEANLIFE
'
print
-l
/dev/null
-10
BEGIN { FS = "[:;]"; }
<
$SCD_HISTFILE
\
length($0) < 4096 && $2 > 0 {
awk
-v
epochseconds
=
$EPOCHSECONDS
-v
meanlife
=
$SCD_MEANLIFE
'
tau = 1.0 * ($2 - epochseconds) / meanlife;
BEGIN { FS = "[:;]"; }
if (tau < -4.61) tau = -4.61;
length($0) < 4096 && $2 > 0 {
prec = exp(tau);
tau = 1.0 * ($2 - epochseconds) / meanlife;
sub(/^[^;]*;/, "");
if (tau < -4.61) tau = -4.61;
if (NF) ptot[$0] += prec;
prec = exp(tau);
}
sub(/^[^;]*;/, "");
END { for (di in ptot) { print di; print ptot[di]; } }'
if (NF) ptot[$0] += prec;
)
"
}
}
)
END { for (di in ptot) { print di; print ptot[di]; } }'
unset
"drank[/dev/null]"
)
"
}
)
unset
"drank[/dev/null]"
# filter drank to the entries that match all arguments
# filter drank to the entries that match all arguments
for
a
;
do
for
a
;
do
p
=
${
ICASE
}
"*
${
a
}
*"
p
=
${
ICASE
}
"*
${
a
}
*"
drank
=(
${
(kv)drank[(I)
${
~p
}
]
}
)
drank
=(
${
(kv)drank[(I)
${
~p
}
]
}
)
done
done
# build a list of matching directories reverse-sorted by their probabilities
# build a list of matching directories reverse-sorted by their probabilities
dmatching
=(
${
(f)
"
$(
dmatching
=(
${
(f)
"
$(
for
d p
in
${
(kv)drank
}
;
do
for
d p
in
${
(kv)drank
}
;
do
print
-r
--
"
$p
$d
"
;
print
-r
--
"
$p
$d
"
;
done
|
sort
-grk1
|
cut
-d
' '
-f
2-
done
|
sort
-grk1
|
cut
-d
' '
-f
2-
)
"
}
)
"
}
)
)
# if some directory paths match all patterns in order, discard all others
# if some directory paths match all patterns in order, discard all others
p
=
${
ICASE
}
"*
${
(j
:
*
:
)argv
}
*"
p
=
${
ICASE
}
"*
${
(j
:
*
:
)argv
}
*"
m
=(
${
(M)dmatching
:#
${
~p
}}
)
m
=(
${
(M)dmatching
:#
${
~p
}}
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
# if some directory names match last pattern, discard all others
# if some directory names match last pattern, discard all others
p
=
${
ICASE
}
"*
${
(j
:
*
:
)argv
}
[^/]#"
p
=
${
ICASE
}
"*
${
(j
:
*
:
)argv
}
[^/]#"
m
=(
${
(M)dmatching
:#
${
~p
}}
)
m
=(
${
(M)dmatching
:#
${
~p
}}
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
# if some directory names match all patterns, discard all others
# if some directory names match all patterns, discard all others
m
=(
$dmatching
)
m
=(
$dmatching
)
for
a
;
do
for
a
;
do
p
=
${
ICASE
}
"*/[^/]#
${
a
}
[^/]#"
p
=
${
ICASE
}
"*/[^/]#
${
a
}
[^/]#"
m
=(
${
(M)m
:#
${
~p
}}
)
m
=(
${
(M)m
:#
${
~p
}}
)
done
done
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
# if some directory names match all patterns in order, discard all others
# if some directory names match all patterns in order, discard all others
p
=
${
ICASE
}
"/*
${
(j
:[^/]#:
)argv
}
[^/]#"
p
=
${
ICASE
}
"/*
${
(j
:[^/]#:
)argv
}
[^/]#"
m
=(
${
(M)dmatching
:#
${
~p
}}
)
m
=(
${
(M)dmatching
:#
${
~p
}}
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
[[
-d
${
m
[1]
}
]]
&&
dmatching
=(
$m
)
# do not match $HOME or $PWD when run without arguments
# do not match $HOME or $PWD when run without arguments
if
[[
$#
==
0
]]
;
then
if
[[
$#
==
0
]]
;
then
dmatching
=(
${
dmatching
:#
(
${
HOME
}
|
${
PWD
}
)
}
)
dmatching
=(
${
dmatching
:#
(
${
HOME
}
|
${
PWD
}
)
}
)
fi
# keep at most SCD_MENUSIZE of matching and valid directories
m
=(
)
for
d
in
$dmatching
;
do
[[
${#
m
}
==
$SCD_MENUSIZE
]]
&&
break
[[
-d
$d
&&
-x
$d
]]
&&
m+
=
$d
done
dmatching
=(
$m
)
# find the maximum rank
maxrank
=
0.0
for
d
in
$dmatching
;
do
[[
${
drank
[
$d
]
}
-lt
maxrank
]]
||
maxrank
=
${
drank
[
$d
]
}
done
# discard all directories below the rank threshold
threshold
=
$((
maxrank
*
SCD_THRESHOLD
))
dmatching
=(
${
^dmatching
}
(
Ne:
'(( ${drank[$REPLY]} >= threshold ))'
:
)
)
}
_scd_Y19oug_match
$*
## process whatever directories that remained
if
[[
${#
dmatching
}
==
0
]]
;
then
print
-u2
"No matching directory."
$EXIT
1
fi
fi
# keep at most SCD_MENUSIZE of matching and valid directories
## build formatted directory aliases for selection menu or list display
m
=(
)
for
d
in
$dmatching
;
do
for
d
in
$dmatching
;
do
[[
${#
m
}
==
$SCD_MENUSIZE
]]
&&
break
if
[[
-n
${
opt_verbose
}
]]
;
then
[[
-d
$d
&&
-x
$d
]]
&&
m+
=
$d
dalias[
$d
]=
$(
printf
"%.3g %s"
${
drank
[
$d
]
}
$d
)
else
dalias[
$d
]=
$(
print
-Dr
--
$d
)
fi
done
done
dmatching
=(
$m
)
# find the maximum rank
## process the --list option
maxrank
=
0.0
if
[[
-n
$opt_list
]]
;
then
for
d
in
$dmatching
;
do
for
d
in
$dmatching
;
do
[[
${
drank
[
$d
]
}
-lt
maxrank
]]
||
maxrank
=
${
drank
[
$d
]
}
print
-r
--
"#
${
dalias
[
$d
]
}
"
print
-r
--
$d
done
$EXIT
fi
## process single directory match
if
[[
${#
dmatching
}
==
1
]]
;
then
_scd_Y19oug_action
$dmatching
$EXIT
$?
fi
## here we have multiple matches - display selection menu
a
=(
{
a-z
}
{
A-Z
}
)
p
=(
)
for
i
in
{
1..
${#
dmatching
}
}
;
do
[[
-n
${
a
[i]
}
]]
||
break
p+
=
"
${
a
[i]
}
)
${
dalias
[
${
dmatching
[i]
}
]
}
"
done
done
# discard all directories below the rank threshold
print
-c
-r
--
$p
threshold
=
$((
maxrank
*
SCD_THRESHOLD
))
dmatching
=(
${
^dmatching
}
(
Ne:
'(( ${drank[$REPLY]} >= threshold ))'
:
)
)
## process whatever directories that remained
if
read
-s
-k
1 d
&&
[[
${
i
::
=
${
a
[(I)
$d
]
}}
-gt
0
]]
;
then
case
${#
dmatching
}
in
_scd_Y19oug_action
${
dmatching
[i]
}
(
0
)
$EXIT
$?
print
-u2
"no matching directory"
fi
return
1
;;
(
1
)
_scd_Y19oug_action
$dmatching
return
$?
;;
(
*
)
# build a list of strings to be displayed in the selection menu
m
=(
${
(f)
"
$(
print
-lD
${
dmatching
})
"
}
)
if
[[
-n
$opt_verbose
]]
;
then
for
i
in
{
1..
${#
dmatching
}
}
;
do
d
=
${
dmatching
[i]
}
m[i]
=
$(
printf
"%.3g %s"
${
drank
[
$d
]
}
$d
)
done
fi
# build a map of string names to actual directory paths
for
i
in
{
1..
${#
m
}
}
;
dalias[
${
m
[i]
}
]=
${
dmatching
[i]
}
# opt_list - output matching directories and exit
if
[[
-n
$opt_list
]]
;
then
_scd_Y19oug_action
${
dmatching
}
return
fi
# finally use the selection menu to get the answer
a
=(
{
a-z
}
{
A-Z
}
)
p
=(
)
for
i
in
{
1..
${#
m
}
}
;
do
[[
-n
${
a
[i]
}
]]
||
break
dkey[
${
a
[i]
}
]=
${
dalias
[
$m
[i]]
}
p+
=
"
${
a
[i]
}
)
${
m
[i]
}
"
done
print
-c
-r
--
$p
if
read
-s
-k
1 d
&&
[[
-n
${
dkey
[
$d
]
}
]]
;
then
_scd_Y19oug_action
${
dkey
[
$d
]
}
fi
return
$?
esac
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment