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
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
137 additions
and
135 deletions
+137
-135
README.md
plugins/scd/README.md
+1
-2
scd
plugins/scd/scd
+136
-133
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,51 +196,41 @@ if [[ -n $opt_unindex ]]; then
...
@@ -190,51 +196,41 @@ 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
a
=
${
(k)dalias[(r)
${
d
}
]
}
print
-r
--
"#
$a
"
print
-r
--
$d
done
elif
[[
$#
==
1
]]
;
then
if
[[
-z
$SCD_SCRIPT
&&
-n
$RUNNING_AS_COMMAND
]]
;
then
if
[[
-z
$SCD_SCRIPT
&&
-n
$RUNNING_AS_COMMAND
]]
;
then
print
-u2
"Warning: running as command with SCD_SCRIPT undefined."
print
-u2
"Warning: running as command with SCD_SCRIPT undefined."
fi
fi
[[
-n
$SCD_SCRIPT
]]
&&
(
umask
077
;
if
[[
-n
$SCD_SCRIPT
]]
;
then
print
-r
"cd
${
(q)1
}
"
>
|
$SCD_SCRIPT
)
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
## single argument that is an existing directory
_scd_Y19oug_match
()
{
if
[[
$#
==
1
&&
-d
$1
&&
-x
$1
]]
;
then
## single argument that is an existing directory or directory alias
_scd_Y19oug_action
$1
if
[[
$#
==
1
]]
&&
\
return
$?
[[
-d
${
d
::
=
$1
}
||
-d
${
d
::
=
${
nameddirs
[
$1
]
}}
]]
&&
[[
-x
$d
]]
;
## single argument that is an alias
then
elif
[[
$#
==
1
&&
-d
${
d
::
=
${
nameddirs
[
$1
]
}}
]]
;
then
_scd_Y19oug_abspath dmatching
$d
_scd_Y19oug_action
$d
drank[
${
dmatching
[1]
}
]=
1
return
$?
return
fi
fi
# ignore case unless there is an argument with an uppercase letter
# ignore case unless there is an argument with an uppercase letter
[[
"
$*
"
==
*
[[
:upper:]]
*
]]
||
ICASE
=
'(#i)'
[[
"
$*
"
==
*
[[
:upper:]]
*
]]
||
ICASE
=
'(#i)'
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
# calculate rank of all directories in the SCD_HISTFILE and keep it as drank
# include a dummy entry for splitting of an empty string is buggy
# include a dummy entry for splitting of an empty string is buggy
[[
-s
$SCD_HISTFILE
]]
&&
drank
=(
${
(f)
"
$(
[[
-s
$SCD_HISTFILE
]]
&&
drank
=(
${
(f)
"
$(
print
-l
/dev/null
-10
print
-l
/dev/null
-10
<
$SCD_HISTFILE
\
<
$SCD_HISTFILE
\
awk
-v
epochseconds
=
$EPOCHSECONDS
-v
meanlife
=
$SCD_MEANLIFE
'
awk
-v
epochseconds
=
$EPOCHSECONDS
-v
meanlife
=
$SCD_MEANLIFE
'
...
@@ -248,103 +244,110 @@ fi
...
@@ -248,103 +244,110 @@ fi
}
}
END { for (di in ptot) { print di; print ptot[di]; } }'
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
fi
# keep at most SCD_MENUSIZE of matching and valid directories
# keep at most SCD_MENUSIZE of matching and valid directories
m
=(
)
m
=(
)
for
d
in
$dmatching
;
do
for
d
in
$dmatching
;
do
[[
${#
m
}
==
$SCD_MENUSIZE
]]
&&
break
[[
${#
m
}
==
$SCD_MENUSIZE
]]
&&
break
[[
-d
$d
&&
-x
$d
]]
&&
m+
=
$d
[[
-d
$d
&&
-x
$d
]]
&&
m+
=
$d
done
done
dmatching
=(
$m
)
dmatching
=(
$m
)
# find the maximum rank
# find the maximum rank
maxrank
=
0.0
maxrank
=
0.0
for
d
in
$dmatching
;
do
for
d
in
$dmatching
;
do
[[
${
drank
[
$d
]
}
-lt
maxrank
]]
||
maxrank
=
${
drank
[
$d
]
}
[[
${
drank
[
$d
]
}
-lt
maxrank
]]
||
maxrank
=
${
drank
[
$d
]
}
done
done
# discard all directories below the rank threshold
threshold
=
$((
maxrank
*
SCD_THRESHOLD
))
dmatching
=(
${
^dmatching
}
(
Ne:
'(( ${drank[$REPLY]} >= threshold ))'
:
)
)
}
# discard all directories below the rank threshold
_scd_Y19oug_match
$*
threshold
=
$((
maxrank
*
SCD_THRESHOLD
))
dmatching
=(
${
^dmatching
}
(
Ne:
'(( ${drank[$REPLY]} >= threshold ))'
:
)
)
## process whatever directories that remained
## process whatever directories that remained
case
${#
dmatching
}
in
if
[[
${#
dmatching
}
==
0
]]
;
then
(
0
)
print
-u2
"No matching directory."
print
-u2
"no matching directory"
$EXIT
1
return
1
fi
;;
(
1
)
## build formatted directory aliases for selection menu or list display
_scd_Y19oug_action
$dmatching
for
d
in
$dmatching
;
do
return
$?
if
[[
-n
${
opt_verbose
}
]]
;
then
;;
dalias[
$d
]=
$(
printf
"%.3g %s"
${
drank
[
$d
]
}
$d
)
(
*
)
else
# build a list of strings to be displayed in the selection menu
dalias[
$d
]=
$(
print
-Dr
--
$d
)
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
fi
# finally use the selection menu to get the answer
done
a
=(
{
a-z
}
{
A-Z
}
)
p
=(
)
## process the --list option
for
i
in
{
1..
${#
m
}
}
;
do
if
[[
-n
$opt_list
]]
;
then
[[
-n
${
a
[i]
}
]]
||
break
for
d
in
$dmatching
;
do
dkey[
${
a
[i]
}
]=
${
dalias
[
$m
[i]]
}
print
-r
--
"#
${
dalias
[
$d
]
}
"
p
+
=
"
${
a
[i]
}
)
${
m
[i]
}
"
p
rint
-r
--
$d
done
done
print
-c
-r
--
$p
$EXIT
if
read
-s
-k
1 d
&&
[[
-n
${
dkey
[
$d
]
}
]]
;
then
fi
_scd_Y19oug_action
${
dkey
[
$d
]
}
fi
## process single directory match
return
$?
if
[[
${#
dmatching
}
==
1
]]
;
then
esac
_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
print
-c
-r
--
$p
if
read
-s
-k
1 d
&&
[[
${
i
::
=
${
a
[(I)
$d
]
}}
-gt
0
]]
;
then
_scd_Y19oug_action
${
dmatching
[i]
}
$EXIT
$?
fi
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