mirror of
https://github.com/wting/autojump
synced 2024-10-27 20:34:07 +00:00
reformat doc strings, move config into own function, add global variable for testing purposes
This commit is contained in:
parent
79e644bd63
commit
13e385a0ef
133
bin/autojump
133
bin/autojump
@ -35,49 +35,78 @@ MAX_STORED_PATHS = 1000
|
||||
COMPLETION_SEPARATOR = '__'
|
||||
ARGS = None
|
||||
|
||||
# load config from environmental variables
|
||||
if 'AUTOJUMP_DATA_DIR' in os.environ:
|
||||
CONFIG_DIR = None
|
||||
DB_FILE = None
|
||||
|
||||
TESTING = False
|
||||
KEEP_ALL_ENTRIES = False
|
||||
IGNORE_CASE = False
|
||||
|
||||
def config(testing=False):
|
||||
global TESTING, CONFIG_DIR, KEEP_ALL_ENTRIES, DB_FILE
|
||||
TESTING = testing
|
||||
|
||||
# load config from environmental variables
|
||||
if 'AUTOJUMP_DATA_DIR' in os.environ:
|
||||
CONFIG_DIR = os.environ.get('AUTOJUMP_DATA_DIR')
|
||||
else:
|
||||
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')
|
||||
|
||||
KEEP_ALL_ENTRIES = False
|
||||
if 'AUTOJUMP_KEEP_ALL_ENTRIES' in os.environ and os.environ.get('AUTOJUMP_KEEP_ALL_ENTRIES') == '1':
|
||||
if 'AUTOJUMP_KEEP_ALL_ENTRIES' in os.environ and os.environ.get('AUTOJUMP_KEEP_ALL_ENTRIES') == '1':
|
||||
KEEP_ALL_ENTRIES = True
|
||||
|
||||
ALWAYS_IGNORE_CASE = False
|
||||
if 'AUTOJUMP_IGNORE_CASE' in os.environ and os.environ.get('AUTOJUMP_IGNORE_CASE') == '1':
|
||||
if 'AUTOJUMP_IGNORE_CASE' in os.environ and os.environ.get('AUTOJUMP_IGNORE_CASE') == '1':
|
||||
ALWAYS_IGNORE_CASE = True
|
||||
|
||||
if CONFIG_DIR == os.path.expanduser('~'):
|
||||
if CONFIG_DIR == os.path.expanduser('~'):
|
||||
DB_FILE = CONFIG_DIR + '/.autojump.txt'
|
||||
else:
|
||||
else:
|
||||
DB_FILE = CONFIG_DIR + '/autojump.txt'
|
||||
|
||||
class Database:
|
||||
""" Object for interfacing with autojump database. """
|
||||
"""
|
||||
Object for interfacing with autojump database.
|
||||
"""
|
||||
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.data = {}
|
||||
self.load()
|
||||
|
||||
def add(self, path, increment=1):
|
||||
""" Increment existing paths or initialize new ones to 0. """
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def add(self, path, increment = 10):
|
||||
"""
|
||||
Increment existing paths or initialize new ones to 0.
|
||||
"""
|
||||
if path not in self.data:
|
||||
self.data[path] = 10
|
||||
self.data[path] = increment
|
||||
else:
|
||||
import math
|
||||
self.data[path] = math.sqrt((self.data[path]**2)+100)
|
||||
self.data[path] = math.sqrt((self.data[path]**2)+(increment**2))
|
||||
|
||||
def decay(self):
|
||||
""" Decay database entries. """
|
||||
"""
|
||||
Decay database entries.
|
||||
"""
|
||||
for k in self.data.keys():
|
||||
self.data[k] *= 0.9
|
||||
|
||||
def get_weight(self, path):
|
||||
"""
|
||||
Return path weight.
|
||||
"""
|
||||
if path in self.data:
|
||||
return self.data[path]
|
||||
else:
|
||||
return 0
|
||||
|
||||
def load(self, error_recovery = False):
|
||||
""" Try to open the database file, recovering from backup if needed. """
|
||||
"""
|
||||
Try to open the database file, recovering from backup if needed.
|
||||
"""
|
||||
try:
|
||||
with open(self.filename, 'r') as aj_file:
|
||||
for line in aj_file.readlines():
|
||||
@ -116,7 +145,9 @@ class Database:
|
||||
return {} # if everything fails, return an empty dictionary
|
||||
|
||||
def maintenance(self):
|
||||
""" Trims and decays database entries when exceeding settings. """
|
||||
"""
|
||||
Trims and decays database entries when exceeding settings.
|
||||
"""
|
||||
if sum(self.data.values()) > MAX_KEYWEIGHT:
|
||||
self.decay()
|
||||
if len(self.data) > MAX_STORED_PATHS:
|
||||
@ -124,7 +155,9 @@ class Database:
|
||||
self.save()
|
||||
|
||||
def purge(self):
|
||||
""" Deletes all entries that no longer exist on system. """
|
||||
"""
|
||||
Deletes all entries that no longer exist on system.
|
||||
"""
|
||||
removed = []
|
||||
for path in self.data.keys():
|
||||
if not os.path.exists(path):
|
||||
@ -134,7 +167,9 @@ class Database:
|
||||
return removed
|
||||
|
||||
def save(self):
|
||||
""" Save database atomically and preserve backup. """
|
||||
"""
|
||||
Save database atomically and preserve backup.
|
||||
"""
|
||||
# check file existence and permissions
|
||||
if ((not os.path.exists(self.filename)) or
|
||||
os.name == 'nt' or
|
||||
@ -166,17 +201,21 @@ class Database:
|
||||
print("Error while creating backup autojump file. (%s)" %
|
||||
ex, file=sys.stderr)
|
||||
|
||||
def trim(self):
|
||||
""" If database has exceeded MAX_STORED_PATHS, removes bottom 10%. """
|
||||
def trim(self, percent=0.1):
|
||||
"""
|
||||
If database has exceeded MAX_STORED_PATHS, removes bottom 10%.
|
||||
"""
|
||||
dirs = list(self.data.items())
|
||||
dirs.sort(key=itemgetter(1))
|
||||
remove_cnt = .1 * MAX_STORED_PATHS
|
||||
remove_cnt = int(percent * len(dirs))
|
||||
for path, _ in dirs[:remove_cnt]:
|
||||
del self.data[path]
|
||||
|
||||
|
||||
def get_db_file(filename = "autojump.txt"):
|
||||
""" Retrieve full database path. """
|
||||
"""
|
||||
Retrieve full database path.
|
||||
"""
|
||||
# TODO: Remove when migration code is removed.
|
||||
if CONFIG_DIR == os.path.expanduser("~"):
|
||||
return CONFIG_DIR + "/." + filename
|
||||
@ -184,9 +223,9 @@ def get_db_file(filename = "autojump.txt"):
|
||||
return CONFIG_DIR + "/" + filename
|
||||
|
||||
def options():
|
||||
""" Parse command line options. """
|
||||
global ARGS
|
||||
|
||||
"""
|
||||
Parse command line options.
|
||||
"""
|
||||
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='',
|
||||
@ -235,7 +274,9 @@ def options():
|
||||
return False
|
||||
|
||||
def decode(text, encoding=None, errors="strict"):
|
||||
""" Decoding step for Python 2 which does not default to unicode. """
|
||||
"""
|
||||
Decoding step for Python 2 which does not default to unicode.
|
||||
"""
|
||||
if sys.version_info[0] > 2:
|
||||
return text
|
||||
else:
|
||||
@ -244,8 +285,10 @@ def decode(text, encoding=None, errors="strict"):
|
||||
return text.decode(encoding, errors)
|
||||
|
||||
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. """
|
||||
"""
|
||||
Wrapper for the print function, using the filesystem encoding by default
|
||||
to minimize encoding mismatch problems in directory names.
|
||||
"""
|
||||
if sys.version_info[0] > 2:
|
||||
print(unicode_text)
|
||||
else:
|
||||
@ -254,16 +297,20 @@ def output(unicode_text, encoding=None):
|
||||
print(unicode_text.encode(encoding))
|
||||
|
||||
def unico(text):
|
||||
""" If Python 2, convert to a unicode object. """
|
||||
"""
|
||||
If Python 2, convert to a unicode object.
|
||||
"""
|
||||
if sys.version_info[0] > 2:
|
||||
return text
|
||||
else:
|
||||
return unicode(text)
|
||||
|
||||
def match_last(pattern):
|
||||
""" If the last pattern contains a full path, jump there.
|
||||
"""
|
||||
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. """
|
||||
"j wo jo__3__/home/joel/workspace/joel" for zsh.
|
||||
"""
|
||||
last_pattern_path = re.sub("(.*)"+COMPLETION_SEPARATOR, "", pattern[-1])
|
||||
if (len(last_pattern_path) > 0 and
|
||||
last_pattern_path[0] == "/" and
|
||||
@ -274,8 +321,10 @@ def match_last(pattern):
|
||||
return False
|
||||
|
||||
def match(path, pattern, only_end=False, ignore_case=False):
|
||||
""" Check whether a path matches a particular pattern, and return
|
||||
the remaining part of the string. """
|
||||
"""
|
||||
Check whether a path matches a particular pattern, and return
|
||||
the remaining part of the string.
|
||||
"""
|
||||
if only_end:
|
||||
match_path = "/".join(path.split('/')[-1-pattern.count('/'):])
|
||||
else:
|
||||
@ -293,7 +342,9 @@ def match(path, pattern, only_end=False, ignore_case=False):
|
||||
return (False, path[find_idx+len(pattern):])
|
||||
|
||||
def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
||||
""" Find max_matches paths that match the pattern, and add them to the result_list. """
|
||||
"""
|
||||
Find max_matches paths that match the pattern, and add them to the result_list.
|
||||
"""
|
||||
try:
|
||||
current_dir = decode(os.path.realpath(os.curdir))
|
||||
except OSError:
|
||||
@ -314,7 +365,7 @@ def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
||||
end = d[0].split('/')[-1]
|
||||
|
||||
# collisions: ignore lower weight paths
|
||||
if end not in end_dirs and os.path.exists(d[0]):
|
||||
if end not in end_dirs and (os.path.exists(d[0]) or TESTING):
|
||||
end_dirs[end] = d[0]
|
||||
|
||||
# find the first match (heighest weight)
|
||||
@ -340,7 +391,7 @@ def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
||||
found, tmp = match(tmp, p, False, ignore_case)
|
||||
if not found: break
|
||||
|
||||
if found and os.path.exists(path):
|
||||
if found and (os.path.exists(path) or TESTING):
|
||||
if path not in results:
|
||||
results.append(path)
|
||||
if len(results) >= max_matches:
|
||||
@ -348,8 +399,14 @@ def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
||||
return results
|
||||
|
||||
def shell_utility():
|
||||
""" Run this when autojump is called as a shell utility. """
|
||||
"""
|
||||
Run this when autojump is called as a shell utility.
|
||||
"""
|
||||
if options(): return True
|
||||
config()
|
||||
|
||||
global ARGS, COMPLETION_SEPARATOR, DB_FILE, \
|
||||
ALWAYS_IGNORE_CASE, KEEP_ALL_ENTRIES
|
||||
db = Database(DB_FILE)
|
||||
|
||||
# if no directories, add empty string
|
||||
|
Loading…
Reference in New Issue
Block a user