mirror of
https://github.com/wting/autojump
synced 2026-03-02 03:49:26 +00:00
restructure files, working make doc and make release
This commit is contained in:
23
bin/_j
Normal file
23
bin/_j
Normal file
@@ -0,0 +1,23 @@
|
||||
#compdef j
|
||||
|
||||
#Copyright Joel Schaerer 2008, 2009
|
||||
#This file is part of autojump
|
||||
|
||||
#autojump is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#autojump is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
cur=${words[2, -1]}
|
||||
|
||||
autojump --completion ${=cur[*]} | while read i; do
|
||||
compadd -U "$i";
|
||||
done
|
||||
330
bin/autojump
Executable file
330
bin/autojump
Executable file
@@ -0,0 +1,330 @@
|
||||
#!/usr/bin/env python
|
||||
"""Copyright Joel Schaerer 2008-2012
|
||||
This file is part of autojump
|
||||
|
||||
autojump is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
autojump is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Autojump is a small tool that maintains a database of your most
|
||||
used directories, and finds the best match to help you jump to
|
||||
frequently used places."""
|
||||
|
||||
from __future__ import division, print_function
|
||||
|
||||
import argparse
|
||||
from sys import argv, stderr, version_info, exit, getfilesystemencoding
|
||||
from tempfile import NamedTemporaryFile
|
||||
from operator import itemgetter
|
||||
import os
|
||||
import shutil
|
||||
|
||||
AUTOJUMP_VERSION = "release-v20"
|
||||
MAX_KEYWEIGHT = 1000
|
||||
MAX_STORED_PATHS = 600
|
||||
COMPLETION_SEPARATOR = '__'
|
||||
|
||||
if "AUTOJUMP_DATA_DIR" in os.environ:
|
||||
CONFIG_DIR = os.environ.get("AUTOJUMP_DATA_DIR")
|
||||
else:
|
||||
xdg_data_dir = os.environ.get('XDG_DATA_HOME') or os.path.join(os.environ['HOME'], '.local', 'share')
|
||||
CONFIG_DIR=os.path.join(xdg_data_dir, 'autojump')
|
||||
|
||||
def uniqadd(collection, key):
|
||||
"""Adds a key to a list only if it is not already present"""
|
||||
if key not in collection:
|
||||
collection.append(key)
|
||||
|
||||
def dicadd(dic, key, increment=1):
|
||||
"""Increment a value in a dic, set it to 0
|
||||
if is is not already present"""
|
||||
dic[key] = dic.get(key, 0.)+increment
|
||||
|
||||
def output(unicode_text,encoding=None):
|
||||
"""Wrapper for the print function, using the filesystem encoding by default
|
||||
to minimize encoding mismatch problems in directory names"""
|
||||
if version_info[0] > 2:
|
||||
print(unicode_text)
|
||||
else:
|
||||
if encoding is None:
|
||||
encoding = getfilesystemencoding()
|
||||
print(unicode_text.encode(encoding))
|
||||
|
||||
def decode(text,encoding=None,errors="strict"):
|
||||
"""Decoding step for python2.x which does not default to unicode"""
|
||||
if version_info[0] > 2:
|
||||
return text
|
||||
else:
|
||||
if encoding is None:
|
||||
encoding = getfilesystemencoding()
|
||||
return text.decode(encoding,errors)
|
||||
|
||||
def unico(text):
|
||||
"""if python2, convert to a unicode object"""
|
||||
if version_info[0] > 2:
|
||||
return text
|
||||
else:
|
||||
return unicode(text)
|
||||
|
||||
def save(path_dict, dic_file):
|
||||
"""Save the database in an atomic way, and preserve
|
||||
a backup file."""
|
||||
# If the dic_file exists and os supports permissions, check that dic_file belongs to us
|
||||
# Otherwise, fail quietly
|
||||
if (not os.path.exists(dic_file)) or os.name == 'nt' or os.getuid() == os.stat(dic_file)[4]:
|
||||
temp = NamedTemporaryFile(dir=CONFIG_DIR, delete=False)
|
||||
for path,weight in sorted(path_dict.items(),key=itemgetter(1),reverse=True):
|
||||
# the db is stored in utf-8
|
||||
temp.write((unico("%s\t%s\n")%(weight,path)).encode("utf-8"))
|
||||
|
||||
# Catching disk errors and skipping save since file handle can't be closed.
|
||||
try:
|
||||
#cf. http://thunk.org/tytso/blog/2009/03/15/dont-fear-the-fsync/
|
||||
temp.flush()
|
||||
os.fsync(temp)
|
||||
temp.close()
|
||||
except IOError as ex:
|
||||
print("Error while saving autojump database (disk full?)" %
|
||||
ex, file=stderr)
|
||||
return
|
||||
|
||||
# Use shutil.move instead of os.rename because windows doesn't support
|
||||
# using rename to overwrite files
|
||||
shutil.move(temp.name, dic_file)
|
||||
try: #backup file
|
||||
import time
|
||||
if (not os.path.exists(dic_file+".bak") or
|
||||
time.time()-os.path.getmtime(dic_file+".bak")>86400):
|
||||
shutil.copy(dic_file, dic_file+".bak")
|
||||
except OSError as ex:
|
||||
print("Error while creating backup autojump file. (%s)" %
|
||||
ex, file=stderr)
|
||||
|
||||
def open_dic(dic_file, error_recovery=False):
|
||||
"""Try hard to open the database file, recovering
|
||||
from backup if needed. """
|
||||
try:
|
||||
path_dict = {}
|
||||
with open(dic_file, 'r') as aj_file:
|
||||
for l in aj_file.readlines():
|
||||
weight,path = l[:-1].split("\t",1)
|
||||
# the db is stored in utf-8
|
||||
path = decode(path,"utf-8")
|
||||
path_dict[path] = float(weight)
|
||||
return path_dict
|
||||
except (IOError, EOFError):
|
||||
if not error_recovery and os.path.exists(dic_file+".bak"):
|
||||
print('Problem with autojump database,\
|
||||
trying to recover from backup...', file=stderr)
|
||||
shutil.copy(dic_file+".bak", dic_file)
|
||||
return open_dic(dic_file, True)
|
||||
else:
|
||||
# Temporary migration code
|
||||
old_dic_file = get_dic_file("autojump_py")
|
||||
if os.path.exists(old_dic_file):
|
||||
try: # fix to get optimised pickle in python < 3
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
try:
|
||||
with open(old_dic_file, 'rb') as aj_file:
|
||||
if version_info[0] > 2:
|
||||
#encoding is only specified for python2.x compatibility
|
||||
path_dict = pickle.load(aj_file, encoding="utf-8")
|
||||
else:
|
||||
path_dict = pickle.load(aj_file)
|
||||
unicode_dict = {} #we now use unicode internally
|
||||
for k,v in path_dict.items():
|
||||
print(k)
|
||||
unicode_dict[decode(k,errors="replace")] = v
|
||||
return unicode_dict
|
||||
except (IOError, EOFError, pickle.UnpicklingError):
|
||||
pass
|
||||
return {} #if everything fails, return an empty file
|
||||
|
||||
|
||||
def forget(path_dict, dic_file):
|
||||
"""Gradually forget about directories. Only call
|
||||
from the actual jump since it can take time"""
|
||||
keyweight = sum(path_dict.values())
|
||||
if keyweight > MAX_KEYWEIGHT:
|
||||
for k in path_dict.keys():
|
||||
path_dict[k] *= 0.9 * MAX_KEYWEIGHT / keyweight
|
||||
save(path_dict, dic_file)
|
||||
|
||||
def clean_dict(sorted_dirs, path_dict):
|
||||
"""Limits the sized of the path_dict to MAX_STORED_PATHS.
|
||||
Returns True if keys were deleted"""
|
||||
if len(sorted_dirs) > MAX_STORED_PATHS:
|
||||
#remove 25 more than needed, to avoid doing it every time
|
||||
for path, dummy in sorted_dirs[MAX_STORED_PATHS-25:]:
|
||||
del path_dict[path]
|
||||
return True
|
||||
else: return False
|
||||
|
||||
def match(path, pattern, ignore_case=False, only_end=False):
|
||||
"""Check whether a path matches a particular pattern, and return
|
||||
the remaning part of the string"""
|
||||
if only_end:
|
||||
match_string = "/".join(path.split('/')[-1-pattern.count('/'):])
|
||||
else:
|
||||
match_string = path
|
||||
if ignore_case:
|
||||
find_idx = match_string.lower().find(pattern.lower())
|
||||
else:
|
||||
find_idx = match_string.find(pattern)
|
||||
does_match = (find_idx != -1)
|
||||
# Eat the path to avoid two patterns matching the same part of the string
|
||||
if does_match:
|
||||
eaten_path = path[find_idx+len(pattern):]
|
||||
else:
|
||||
eaten_path = path
|
||||
return (does_match, eaten_path)
|
||||
|
||||
def find_matches(dirs, patterns, result_list, ignore_case, max_matches, current_dir):
|
||||
"""Find max_matches paths that match the pattern,
|
||||
and add them to the result_list"""
|
||||
for path, count in dirs:
|
||||
# Don't jump to where we alread are
|
||||
if current_dir == path :
|
||||
continue
|
||||
does_match, eaten_path = True, path
|
||||
for n,p in enumerate(patterns):
|
||||
#For the last pattern, only match the end of the pattern
|
||||
does_match, eaten_path = match(eaten_path, p, ignore_case, only_end=(n == len(patterns)-1))
|
||||
if not does_match: break
|
||||
#If a path doesn't exist, don't jump there
|
||||
#We still keep it in db in case it's from a removable drive
|
||||
if does_match and os.path.exists(path):
|
||||
uniqadd(result_list, path)
|
||||
if len(result_list) >= max_matches :
|
||||
break
|
||||
|
||||
def get_dic_file(filename="autojump.txt"):
|
||||
if CONFIG_DIR == os.path.expanduser("~"):
|
||||
dic_file = CONFIG_DIR+"/." + filename
|
||||
else:
|
||||
dic_file = CONFIG_DIR+"/" + filename
|
||||
return dic_file
|
||||
|
||||
def shell_utility():
|
||||
"""Run this when autojump is called as a shell utility"""
|
||||
parser = argparse.ArgumentParser(description='Automatically jump to directory passed as an argument.',
|
||||
epilog="Please see autojump(1) man pages for full documentation.")
|
||||
parser.add_argument('directory', metavar='DIR', nargs='*', default='',
|
||||
help='directory to jump to')
|
||||
parser.add_argument('-a', '--add', metavar='DIR',
|
||||
help='manually add path to database')
|
||||
parser.add_argument('-b', '--bash', action="store_true", default=False,
|
||||
help='enclose directory quotes to prevent errors')
|
||||
parser.add_argument('--completion', action="store_true", default=False,
|
||||
help='prevent key weight decay over time')
|
||||
parser.add_argument('--stat', action="store_true", default=False,
|
||||
help='show database entries and their key weights')
|
||||
parser.add_argument('--version', action="version", version="%(prog)s " + AUTOJUMP_VERSION,
|
||||
help='show version information and exit')
|
||||
|
||||
args = parser.parse_args()
|
||||
dic_file = get_dic_file()
|
||||
path_dict = open_dic(dic_file)
|
||||
|
||||
# The home dir can be reached quickly by "cd" and may interfere with other directories
|
||||
if (args.add):
|
||||
if(args.add != os.path.expanduser("~")):
|
||||
dicadd(path_dict, decode(args.add))
|
||||
save(path_dict, dic_file)
|
||||
return True
|
||||
|
||||
if (args.stat):
|
||||
paths = list(path_dict.items())
|
||||
paths.sort(key=itemgetter(1))
|
||||
for path, count in paths[-100:]:
|
||||
output(unico("%.1f:\t%s") % (count, path))
|
||||
print("Total key weight: %d. Number of stored paths: %d" %
|
||||
(sum(path_dict.values()), len(paths)))
|
||||
return True
|
||||
|
||||
import re
|
||||
#userchoice is i if the pattern is __pattern__i, otherwise -1
|
||||
userchoice = -1
|
||||
results = []
|
||||
|
||||
#default: gradually forget about old directories
|
||||
if (not args.completion): forget(path_dict, dic_file)
|
||||
|
||||
if (args.directory == ''):
|
||||
patterns = [unico("")]
|
||||
else:
|
||||
patterns = [decode(a) for a in args.directory]
|
||||
|
||||
# If the last pattern contains a full path, jump there
|
||||
# The regexp is because we need to support stuff like
|
||||
# "j wo jo__3__/home/joel/workspace/joel" for zsh
|
||||
last_pattern_path = re.sub("(.*)"+COMPLETION_SEPARATOR, "", patterns[-1])
|
||||
if (len(last_pattern_path)>0 and
|
||||
last_pattern_path[0] == "/" and
|
||||
os.path.exists(last_pattern_path)):
|
||||
if not args.completion: output(last_pattern_path)
|
||||
else:
|
||||
#check for ongoing completion, and act accordingly
|
||||
endmatch = re.search(COMPLETION_SEPARATOR+"([0-9]+)", patterns[-1])
|
||||
if endmatch: #user has selected a completion
|
||||
userchoice = int(endmatch.group(1))
|
||||
patterns[-1] = re.sub(COMPLETION_SEPARATOR+"[0-9]+.*",
|
||||
"", patterns[-1])
|
||||
else: #user hasn't selected a completion, display the same choices again
|
||||
endmatch = re.match("(.*)"+COMPLETION_SEPARATOR, patterns[-1])
|
||||
if endmatch: patterns[-1] = endmatch.group(1)
|
||||
|
||||
dirs = list(path_dict.items())
|
||||
dirs.sort(key=itemgetter(1), reverse=True)
|
||||
if args.completion or userchoice != -1:
|
||||
max_matches = 9
|
||||
else:
|
||||
max_matches = 1
|
||||
|
||||
# Don't jump to the current directory
|
||||
try:
|
||||
current_dir = decode(os.path.realpath(os.curdir))
|
||||
#Sometimes the current path doesn't exist anymore.
|
||||
#In that case, jump if possible.
|
||||
except OSError:
|
||||
current_dir = None
|
||||
find_matches(dirs, patterns, results, False, max_matches, current_dir)
|
||||
# If not found, try ignoring case.
|
||||
# On completion always show all results
|
||||
if args.completion or not results:
|
||||
find_matches(dirs, patterns, results,
|
||||
ignore_case=True,
|
||||
max_matches=max_matches, current_dir=current_dir)
|
||||
# Keep the database to a reasonable size
|
||||
if not args.completion and clean_dict(dirs, path_dict):
|
||||
save(path_dict, dic_file)
|
||||
|
||||
if args.completion and args.bash: quotes = "'"
|
||||
else: quotes = ""
|
||||
|
||||
if userchoice != -1:
|
||||
if len(results) > userchoice-1 :
|
||||
output(unico("%s%s%s") % (quotes,results[userchoice-1],quotes))
|
||||
elif len(results) > 1 and args.completion:
|
||||
output("\n".join(("%s%s%d%s%s" % (patterns[-1],
|
||||
COMPLETION_SEPARATOR, n+1, COMPLETION_SEPARATOR, r)
|
||||
for n, r in enumerate(results[:8]))))
|
||||
elif results: output(unico("%s%s%s")%(quotes,results[0],quotes))
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
success=shell_utility()
|
||||
if not success: exit(1)
|
||||
95
bin/autojump.bash
Normal file
95
bin/autojump.bash
Normal file
@@ -0,0 +1,95 @@
|
||||
#Copyright Joel Schaerer 2008, 2009
|
||||
#This file is part of autojump
|
||||
|
||||
#autojump is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#autojump is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#This shell snippet sets the prompt command and the necessary aliases
|
||||
_autojump()
|
||||
{
|
||||
local cur
|
||||
cur=${COMP_WORDS[*]:1}
|
||||
comps=$(autojump --bash --completion $cur)
|
||||
while read i
|
||||
do
|
||||
COMPREPLY=("${COMPREPLY[@]}" "${i}")
|
||||
done <<EOF
|
||||
$comps
|
||||
EOF
|
||||
}
|
||||
complete -F _autojump j
|
||||
|
||||
_autojump_files()
|
||||
{
|
||||
if [[ ${COMP_WORDS[COMP_CWORD]} == *__* ]]; then
|
||||
local cur
|
||||
#cur=${COMP_WORDS[*]:1}
|
||||
cur=${COMP_WORDS[COMP_CWORD]}
|
||||
comps=$(autojump --bash --completion $cur)
|
||||
while read i
|
||||
do
|
||||
COMPREPLY=("${COMPREPLY[@]}" "${i}")
|
||||
done <<EOF
|
||||
$comps
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
complete -o default -o bashdefault -F _autojump_files cp mv meld diff kdiff3 vim emacs
|
||||
|
||||
#determine the data directory according to the XDG Base Directory Specification
|
||||
if [[ -n ${XDG_DATA_HOME} ]] && [[ ${XDG_DATA_HOME} =~ ${USER} ]]; then
|
||||
export AUTOJUMP_DATA_DIR="${XDG_DATA_HOME}/autojump"
|
||||
else
|
||||
export AUTOJUMP_DATA_DIR=~/.local/share/autojump
|
||||
fi
|
||||
|
||||
if [ ! -e "${AUTOJUMP_DATA_DIR}" ]; then
|
||||
mkdir -p "${AUTOJUMP_DATA_DIR}"
|
||||
mv ~/.autojump_py "${AUTOJUMP_DATA_DIR}/autojump_py" 2>>/dev/null #migration
|
||||
mv ~/.autojump_py.bak "${AUTOJUMP_DATA_DIR}/autojump_py.bak" 2>>/dev/null
|
||||
mv ~/.autojump_errors "${AUTOJUMP_DATA_DIR}/autojump_errors" 2>>/dev/null
|
||||
fi
|
||||
|
||||
# set paths if necessary for local installations
|
||||
if [ -d ~/.autojump/ ]; then
|
||||
export PATH=~/.autojump/bin:"${PATH}"
|
||||
fi
|
||||
|
||||
export AUTOJUMP_HOME=${HOME}
|
||||
if [ "${AUTOJUMP_KEEP_SYMLINKS}" == "1" ]
|
||||
then
|
||||
_PWD_ARGS=""
|
||||
else
|
||||
_PWD_ARGS="-P"
|
||||
fi
|
||||
AUTOJUMP='{ [[ "$AUTOJUMP_HOME" == "$HOME" ]] && (autojump -a "$(pwd ${_PWD_ARGS})"&)>/dev/null 2>>"${AUTOJUMP_DATA_DIR}/.autojump_errors";} 2>/dev/null'
|
||||
|
||||
case $PROMPT_COMMAND in
|
||||
*autojump*) ;;
|
||||
*) export PROMPT_COMMAND="$AUTOJUMP ; ${PROMPT_COMMAND:-:}";;
|
||||
esac
|
||||
|
||||
alias jumpstat="autojump --stat"
|
||||
|
||||
function j {
|
||||
new_path="$(autojump $@)"
|
||||
|
||||
if [ -d "${new_path}" ]; then
|
||||
echo -e "\\033[31m${new_path}\\033[0m"
|
||||
cd "${new_path}"
|
||||
else
|
||||
echo "autojump: directory '${@}' not found"
|
||||
echo "Try \`autojump --help\` for more information."
|
||||
false
|
||||
fi
|
||||
}
|
||||
29
bin/autojump.sh
Normal file
29
bin/autojump.sh
Normal file
@@ -0,0 +1,29 @@
|
||||
# Source autojump on BASH or ZSH depending on the shell
|
||||
#Copyright Joel Schaerer 2008, 2009
|
||||
#This file is part of autojump
|
||||
|
||||
#autojump is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#autojump is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
if [ "$BASH_VERSION" ] && [ -n "$PS1" ] && echo $SHELLOPTS | grep -v posix >>/dev/null; then
|
||||
if [ -f ~/.autojump/etc/profile.d/autojump.bash ]; then
|
||||
source ~/.autojump/etc/profile.d/autojump.bash
|
||||
elif [ -f /etc/profile.d/autojump.bash ]; then
|
||||
source /etc/profile.d/autojump.bash
|
||||
fi
|
||||
elif [ "$ZSH_VERSION" ] && [ -n "$PS1" ]; then
|
||||
if [ -f ~/.autojump/etc/profile.d/autojump.zsh ]; then
|
||||
source ~/.autojump/etc/profile.d/autojump.zsh
|
||||
elif [ -f /etc/profile.d/autojump.zsh ]; then
|
||||
source /etc/profile.d/autojump.zsh
|
||||
fi
|
||||
fi
|
||||
66
bin/autojump.zsh
Normal file
66
bin/autojump.zsh
Normal file
@@ -0,0 +1,66 @@
|
||||
#Copyright Joel Schaerer 2008, 2009
|
||||
#This file is part of autojump
|
||||
|
||||
#autojump is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#autojump is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# determine the data directory according to the XDG Base Directory Specification
|
||||
if [[ -n ${XDG_DATA_HOME} ]] && [[ ${XDG_DATA_HOME} =~ ${USER} ]]; then
|
||||
export AUTOJUMP_DATA_DIR="${XDG_DATA_HOME}/autojump"
|
||||
else
|
||||
export AUTOJUMP_DATA_DIR=${HOME}/.local/share/autojump
|
||||
fi
|
||||
|
||||
if [[ ! -e ${AUTOJUMP_DATA_DIR} ]]; then
|
||||
mkdir -p "${AUTOJUMP_DATA_DIR}"
|
||||
mv ${HOME}/.autojump_py "${AUTOJUMP_DATA_DIR}/autojump_py" 2>>/dev/null #migration
|
||||
mv ${HOME}/.autojump_py.bak "${AUTOJUMP_DATA_DIR}/autojump_py.bak" 2>>/dev/null
|
||||
mv ${HOME}/.autojump_errors "${AUTOJUMP_DATA_DIR}/autojump_errors" 2>>/dev/null
|
||||
fi
|
||||
|
||||
# set paths if necessary for local installations
|
||||
if [[ -d ${HOME}/.autojump ]]; then
|
||||
path=(${HOME}/.autojump/bin ${path})
|
||||
fpath=(${HOME}/.autojump/functions/ ${fpath})
|
||||
fi
|
||||
# set fpath if necessary for homebrew installation
|
||||
if [[ -d "`brew --prefix 2>/dev/null`/share/zsh/functions" ]]; then
|
||||
fpath=(`brew --prefix`/share/zsh/functions ${fpath})
|
||||
fi
|
||||
|
||||
function autojump_preexec() {
|
||||
if [[ "${AUTOJUMP_KEEP_SYMLINKS}" == "1" ]]; then
|
||||
_PWD_ARGS=""
|
||||
else
|
||||
_PWD_ARGS="-P"
|
||||
fi
|
||||
{ (autojump -a "$(pwd ${_PWD_ARGS})"&)>/dev/null 2>>|${AUTOJUMP_DATA_DIR}/.autojump_errors ; } 2>/dev/null
|
||||
}
|
||||
|
||||
typeset -ga preexec_functions
|
||||
preexec_functions+=autojump_preexec
|
||||
|
||||
alias jumpstat="autojump --stat"
|
||||
|
||||
function j {
|
||||
local new_path="$(autojump $@)"
|
||||
|
||||
if [ -d "${new_path}" ]; then
|
||||
echo -e "\\033[31m${new_path}\\033[0m"
|
||||
cd "${new_path}"
|
||||
else
|
||||
echo "autojump: directory '${@}' not found"
|
||||
echo "Try \`autojump --help\` for more information."
|
||||
false
|
||||
fi
|
||||
}
|
||||
BIN
bin/icon.png
Normal file
BIN
bin/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 594 B |
306
bin/jumpapplet
Executable file
306
bin/jumpapplet
Executable file
@@ -0,0 +1,306 @@
|
||||
#!/usr/bin/env python2
|
||||
|
||||
#Copyright Joel Schaerer and Pierre Gueth 2008, 2009
|
||||
#This file is part of autojump
|
||||
|
||||
#autojump is free software: you can redistribute it and/or modify
|
||||
#it under the terms of the GNU General Public License as published by
|
||||
#the Free Software Foundation, either version 3 of the License, or
|
||||
#(at your option) any later version.
|
||||
#
|
||||
#autojump is distributed in the hope that it will be useful,
|
||||
#but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
#GNU General Public License for more details.
|
||||
#
|
||||
#You should have received a copy of the GNU General Public License
|
||||
#along with autojump. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import subprocess
|
||||
import cPickle
|
||||
import os.path
|
||||
import os
|
||||
import sys
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gtk
|
||||
from autojump import open_dic,get_dic_file
|
||||
|
||||
defaults={}
|
||||
actions={}
|
||||
|
||||
#directory finding helpers, conforming to the XDG Base Directory Specification
|
||||
def data_dir():
|
||||
xdg_data_dir = os.environ.get('XDG_DATA_HOME') or os.path.join(os.environ['HOME'], '.local', 'share')
|
||||
return os.path.join(xdg_data_dir, 'autojump')
|
||||
|
||||
def config_dir():
|
||||
xdg_config_dir = os.environ.get('XDG_CONFIG_HOME') or os.path.join(os.environ['HOME'], '.config')
|
||||
return os.path.join(xdg_config_dir, 'autojump')
|
||||
|
||||
#decorator truff
|
||||
def action(validator,name=None):
|
||||
if name is None:
|
||||
def wrapper(action):
|
||||
actions[action.__name__]=(action,validator)
|
||||
else:
|
||||
def wrapper(action):
|
||||
actions[name]=(action,validator)
|
||||
return wrapper
|
||||
|
||||
#validator helper
|
||||
def has_child_dir(dirname,recursion=0):
|
||||
def wrapper(path):
|
||||
k=recursion
|
||||
ii=""
|
||||
while k>=0:
|
||||
if os.path.isdir(os.path.join(path,ii,dirname)): return True
|
||||
k-=1
|
||||
ii=os.path.join("..",ii)
|
||||
return False
|
||||
return wrapper
|
||||
|
||||
|
||||
def has_child_file(filename):
|
||||
def wrapper(path):
|
||||
return os.path.isfile(os.path.join(path,filename))
|
||||
return wrapper
|
||||
|
||||
def load_paths(maxpath=10):
|
||||
path_dict=open_dic(get_dic_file())
|
||||
path_dict=path_dict.items()
|
||||
path_dict.sort(key=lambda x: x[1],reverse=True)
|
||||
return [path for path,score in path_dict[:maxpath]]
|
||||
|
||||
def load_settings_file():
|
||||
filename = os.path.join(config_dir(), 'jumpapplet_py')
|
||||
#migration from old location
|
||||
old_filename = os.path.join(os.environ['HOME'], '.jumpapplet_py')
|
||||
if not os.path.exists(filename) and os.path.exists(old_filename):
|
||||
os.rename(old_filename, filename)
|
||||
|
||||
print "loading settings"
|
||||
global defaults
|
||||
dic_file = filename
|
||||
|
||||
try:
|
||||
aj_file=open(dic_file,'r')
|
||||
defaults=cPickle.load(aj_file)
|
||||
aj_file.close()
|
||||
except IOError:
|
||||
print "no config file"
|
||||
pass
|
||||
|
||||
if not "terminal" in defaults: defaults["terminal"]="gnome-terminal"
|
||||
if not "navigator" in defaults: defaults["navigator"]="nautilus"
|
||||
if not "maxpath" in defaults: defaults["maxpath"]=15
|
||||
if not "invert" in defaults: defaults["invert"]=False
|
||||
if not "collapse" in defaults: defaults["collapse"]=True
|
||||
|
||||
create_actions()
|
||||
|
||||
def save_settings_file(filename=None):
|
||||
directory = config_dir()
|
||||
if not filename:
|
||||
filename = os.path.join(directory, 'jumpapplet_py')
|
||||
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
print "saving settings"
|
||||
dic_file= filename
|
||||
|
||||
aj_file=open(dic_file,'w')
|
||||
cPickle.dump(defaults,aj_file)
|
||||
aj_file.close()
|
||||
|
||||
|
||||
def get_actions(path):
|
||||
return [(name,action) for name,(action,validator) in actions.items() if validator(path)]
|
||||
|
||||
def popup(sender,button,activation):
|
||||
paths=load_paths(maxpath=defaults["maxpath"])
|
||||
|
||||
if defaults["collapse"]:
|
||||
def collapse_home(path):
|
||||
return path.replace(os.path.expanduser('~'),"~")
|
||||
else:
|
||||
def collapse_home(path):
|
||||
return path
|
||||
|
||||
menu=gtk.Menu()
|
||||
if defaults["invert"]:
|
||||
item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
|
||||
item.connect("activate",quit)
|
||||
menu.append(item)
|
||||
item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
|
||||
item.connect("activate",settings)
|
||||
menu.append(item)
|
||||
|
||||
menu.append(gtk.SeparatorMenuItem())
|
||||
|
||||
for path in reversed(paths):
|
||||
actions=get_actions(path)
|
||||
if not actions: continue
|
||||
|
||||
item=gtk.MenuItem(collapse_home(path),use_underline=False)
|
||||
submenu=gtk.Menu()
|
||||
item.set_submenu(submenu)
|
||||
for name,action in actions:
|
||||
subitem=gtk.MenuItem(name)
|
||||
subitem.connect("activate",action,path)
|
||||
submenu.append(subitem)
|
||||
menu.append(item)
|
||||
else:
|
||||
for path in paths:
|
||||
actions=get_actions(path)
|
||||
if not actions: continue
|
||||
|
||||
item=gtk.MenuItem(collapse_home(path),use_underline=False)
|
||||
submenu=gtk.Menu()
|
||||
item.set_submenu(submenu)
|
||||
for name,action in actions:
|
||||
subitem=gtk.MenuItem(name)
|
||||
subitem.connect("activate",action,path)
|
||||
submenu.append(subitem)
|
||||
menu.append(item)
|
||||
|
||||
menu.append(gtk.SeparatorMenuItem())
|
||||
|
||||
item=gtk.ImageMenuItem(stock_id=gtk.STOCK_PREFERENCES)
|
||||
item.connect("activate",settings)
|
||||
menu.append(item)
|
||||
item=gtk.ImageMenuItem(stock_id=gtk.STOCK_QUIT)
|
||||
item.connect("activate",quit)
|
||||
menu.append(item)
|
||||
|
||||
menu.show_all()
|
||||
menu.popup(None,None,gtk.status_icon_position_menu,button,activation,sender)
|
||||
|
||||
def settings(sender):
|
||||
window=gtk.Dialog("jump applet preferences",None,gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_SAVE,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
|
||||
window.set_border_width(3)
|
||||
window.set_resizable(False)
|
||||
if os.path.isfile("icon.png"): window.set_icon_from_file("icon.png")
|
||||
elif os.path.isfile("/usr/share/autojump/icon.png"): window.set_icon_from_file("/usr/share/autojump/icon.png")
|
||||
|
||||
vbox=gtk.Table(5,2)
|
||||
vbox.set_row_spacings(3)
|
||||
window.get_child().add(vbox)
|
||||
def add_string_setting(name,label,nsettings):
|
||||
label=gtk.Label(label+' ')
|
||||
label.set_alignment(1.,.5)
|
||||
entry=gtk.Entry()
|
||||
if name in defaults: entry.set_text(defaults[name])
|
||||
|
||||
vbox.attach(label,0,1,nsettings,nsettings+1)
|
||||
vbox.attach(entry,1,2,nsettings,nsettings+1)
|
||||
return (name,entry)
|
||||
|
||||
def add_integer_setting(name,label,nsettings):
|
||||
label=gtk.Label(label+' ')
|
||||
label.set_alignment(1.,.5)
|
||||
entry=gtk.SpinButton()
|
||||
entry.set_range(5,35)
|
||||
entry.set_numeric(True)
|
||||
entry.set_increments(1,5)
|
||||
entry.set_snap_to_ticks(True)
|
||||
if name in defaults: entry.set_value(defaults[name])
|
||||
|
||||
vbox.attach(label,0,1,nsettings,nsettings+1)
|
||||
vbox.attach(entry,1,2,nsettings,nsettings+1)
|
||||
return (name,entry)
|
||||
|
||||
def add_bool_setting(name,label,nsettings):
|
||||
entry=gtk.CheckButton(label=label,use_underline=False)
|
||||
if name in defaults: entry.set_active(defaults[name])
|
||||
|
||||
vbox.attach(entry,0,2,nsettings,nsettings+1)
|
||||
return (name,entry)
|
||||
|
||||
entries=[]
|
||||
entries.append(add_string_setting("terminal","Terminal program",0))
|
||||
entries.append(add_string_setting("navigator","Navigator program",1))
|
||||
entries.append(add_integer_setting("maxpath","Number of directories",2))
|
||||
entries.append(add_bool_setting("invert","List directories in reverse order",3))
|
||||
entries.append(add_bool_setting("collapse","Collapse home directory to ~",4))
|
||||
|
||||
window.connect("response",save_settings,entries,window)
|
||||
window.show_all();
|
||||
|
||||
def save_settings(sender,response,entries,window):
|
||||
window.hide_all()
|
||||
if response!=gtk.RESPONSE_OK: return
|
||||
|
||||
global defaults
|
||||
for name,entry in entries:
|
||||
try:
|
||||
defaults[name]=int(entry.get_text())
|
||||
except (ValueError,AttributeError):
|
||||
try:
|
||||
defaults[name]=entry.get_active()
|
||||
except AttributeError:
|
||||
defaults[name]=entry.get_text()
|
||||
|
||||
save_settings_file()
|
||||
create_actions()
|
||||
|
||||
def init():
|
||||
load_settings_file()
|
||||
if os.path.isfile("icon.png"): icon=gtk.status_icon_new_from_file("icon.png")
|
||||
elif os.path.isfile("/usr/share/autojump/icon.png"): icon=gtk.status_icon_new_from_file("/usr/share/autojump/icon.png")
|
||||
else: icon=gtk.status_icon_new_from_icon_name("help")
|
||||
icon.set_visible(True)
|
||||
icon.connect("popup-menu",popup)
|
||||
|
||||
def quit(sender):
|
||||
gtk.main_quit()
|
||||
|
||||
######################################################
|
||||
#insert other actions here using the action decorator#
|
||||
######################################################
|
||||
def create_actions():
|
||||
global actions
|
||||
actions={}
|
||||
|
||||
@action(has_child_dir(".git",recursion=3))
|
||||
def gitk(sender,path):
|
||||
if not os.fork():
|
||||
os.chdir(path)
|
||||
subprocess.Popen(['gitk']).wait()
|
||||
sys.exit()
|
||||
|
||||
@action(has_child_file("CMakeCache.txt"),"configure")
|
||||
def cmake(sender,path):
|
||||
if not os.fork():
|
||||
os.chdir(path)
|
||||
subprocess.Popen(['cmake-gui','.']).wait()
|
||||
sys.exit()
|
||||
|
||||
@action(os.path.isdir)
|
||||
def terminal(sender,path):
|
||||
print "launch terminal '%s'" % defaults["terminal"]
|
||||
if not os.fork():
|
||||
try:
|
||||
if defaults["terminal"]=="konsole":
|
||||
subprocess.Popen([defaults["terminal"],"--workdir=%s"%path]).wait()
|
||||
else:
|
||||
os.chdir(path)
|
||||
subprocess.Popen([defaults["terminal"]]).wait()
|
||||
except OSError:
|
||||
pass
|
||||
sys.exit()
|
||||
|
||||
@action(os.path.isdir)
|
||||
def navigator(sender,path):
|
||||
print "launch navigator '%s'" % defaults["navigator"]
|
||||
if not os.fork():
|
||||
try:
|
||||
subprocess.Popen([defaults["navigator"],path]).wait()
|
||||
except OSError:
|
||||
pass
|
||||
sys.exit()
|
||||
|
||||
if __name__=='__main__':
|
||||
init()
|
||||
gtk.main()
|
||||
Reference in New Issue
Block a user