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 = '__'
|
COMPLETION_SEPARATOR = '__'
|
||||||
ARGS = None
|
ARGS = None
|
||||||
|
|
||||||
# load config from environmental variables
|
CONFIG_DIR = None
|
||||||
if 'AUTOJUMP_DATA_DIR' in os.environ:
|
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')
|
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')
|
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')
|
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
|
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
|
ALWAYS_IGNORE_CASE = True
|
||||||
|
|
||||||
if CONFIG_DIR == os.path.expanduser('~'):
|
if CONFIG_DIR == os.path.expanduser('~'):
|
||||||
DB_FILE = CONFIG_DIR + '/.autojump.txt'
|
DB_FILE = CONFIG_DIR + '/.autojump.txt'
|
||||||
else:
|
else:
|
||||||
DB_FILE = CONFIG_DIR + '/autojump.txt'
|
DB_FILE = CONFIG_DIR + '/autojump.txt'
|
||||||
|
|
||||||
class Database:
|
class Database:
|
||||||
""" Object for interfacing with autojump database. """
|
"""
|
||||||
|
Object for interfacing with autojump database.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, filename):
|
def __init__(self, filename):
|
||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.data = {}
|
self.data = {}
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
def add(self, path, increment=1):
|
def __len__(self):
|
||||||
""" Increment existing paths or initialize new ones to 0. """
|
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:
|
if path not in self.data:
|
||||||
self.data[path] = 10
|
self.data[path] = increment
|
||||||
else:
|
else:
|
||||||
import math
|
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):
|
def decay(self):
|
||||||
""" Decay database entries. """
|
"""
|
||||||
|
Decay database entries.
|
||||||
|
"""
|
||||||
for k in self.data.keys():
|
for k in self.data.keys():
|
||||||
self.data[k] *= 0.9
|
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):
|
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:
|
try:
|
||||||
with open(self.filename, 'r') as aj_file:
|
with open(self.filename, 'r') as aj_file:
|
||||||
for line in aj_file.readlines():
|
for line in aj_file.readlines():
|
||||||
@ -116,7 +145,9 @@ class Database:
|
|||||||
return {} # if everything fails, return an empty dictionary
|
return {} # if everything fails, return an empty dictionary
|
||||||
|
|
||||||
def maintenance(self):
|
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:
|
if sum(self.data.values()) > MAX_KEYWEIGHT:
|
||||||
self.decay()
|
self.decay()
|
||||||
if len(self.data) > MAX_STORED_PATHS:
|
if len(self.data) > MAX_STORED_PATHS:
|
||||||
@ -124,7 +155,9 @@ class Database:
|
|||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
def purge(self):
|
def purge(self):
|
||||||
""" Deletes all entries that no longer exist on system. """
|
"""
|
||||||
|
Deletes all entries that no longer exist on system.
|
||||||
|
"""
|
||||||
removed = []
|
removed = []
|
||||||
for path in self.data.keys():
|
for path in self.data.keys():
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
@ -134,7 +167,9 @@ class Database:
|
|||||||
return removed
|
return removed
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
""" Save database atomically and preserve backup. """
|
"""
|
||||||
|
Save database atomically and preserve backup.
|
||||||
|
"""
|
||||||
# check file existence and permissions
|
# check file existence and permissions
|
||||||
if ((not os.path.exists(self.filename)) or
|
if ((not os.path.exists(self.filename)) or
|
||||||
os.name == 'nt' or
|
os.name == 'nt' or
|
||||||
@ -166,17 +201,21 @@ class Database:
|
|||||||
print("Error while creating backup autojump file. (%s)" %
|
print("Error while creating backup autojump file. (%s)" %
|
||||||
ex, file=sys.stderr)
|
ex, file=sys.stderr)
|
||||||
|
|
||||||
def trim(self):
|
def trim(self, percent=0.1):
|
||||||
""" If database has exceeded MAX_STORED_PATHS, removes bottom 10%. """
|
"""
|
||||||
|
If database has exceeded MAX_STORED_PATHS, removes bottom 10%.
|
||||||
|
"""
|
||||||
dirs = list(self.data.items())
|
dirs = list(self.data.items())
|
||||||
dirs.sort(key=itemgetter(1))
|
dirs.sort(key=itemgetter(1))
|
||||||
remove_cnt = .1 * MAX_STORED_PATHS
|
remove_cnt = int(percent * len(dirs))
|
||||||
for path, _ in dirs[:remove_cnt]:
|
for path, _ in dirs[:remove_cnt]:
|
||||||
del self.data[path]
|
del self.data[path]
|
||||||
|
|
||||||
|
|
||||||
def get_db_file(filename = "autojump.txt"):
|
def get_db_file(filename = "autojump.txt"):
|
||||||
""" Retrieve full database path. """
|
"""
|
||||||
|
Retrieve full database path.
|
||||||
|
"""
|
||||||
# TODO: Remove when migration code is removed.
|
# TODO: Remove when migration code is removed.
|
||||||
if CONFIG_DIR == os.path.expanduser("~"):
|
if CONFIG_DIR == os.path.expanduser("~"):
|
||||||
return CONFIG_DIR + "/." + filename
|
return CONFIG_DIR + "/." + filename
|
||||||
@ -184,9 +223,9 @@ def get_db_file(filename = "autojump.txt"):
|
|||||||
return CONFIG_DIR + "/" + filename
|
return CONFIG_DIR + "/" + filename
|
||||||
|
|
||||||
def options():
|
def options():
|
||||||
""" Parse command line options. """
|
"""
|
||||||
global ARGS
|
Parse command line options.
|
||||||
|
"""
|
||||||
parser = argparse.ArgumentParser(description='Automatically jump to directory passed as an argument.',
|
parser = argparse.ArgumentParser(description='Automatically jump to directory passed as an argument.',
|
||||||
epilog="Please see autojump(1) man pages for full documentation.")
|
epilog="Please see autojump(1) man pages for full documentation.")
|
||||||
parser.add_argument('directory', metavar='DIR', nargs='*', default='',
|
parser.add_argument('directory', metavar='DIR', nargs='*', default='',
|
||||||
@ -235,7 +274,9 @@ def options():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def decode(text, encoding=None, errors="strict"):
|
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:
|
if sys.version_info[0] > 2:
|
||||||
return text
|
return text
|
||||||
else:
|
else:
|
||||||
@ -244,8 +285,10 @@ def decode(text, encoding=None, errors="strict"):
|
|||||||
return text.decode(encoding, errors)
|
return text.decode(encoding, errors)
|
||||||
|
|
||||||
def output(unicode_text, encoding=None):
|
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:
|
if sys.version_info[0] > 2:
|
||||||
print(unicode_text)
|
print(unicode_text)
|
||||||
else:
|
else:
|
||||||
@ -254,16 +297,20 @@ def output(unicode_text, encoding=None):
|
|||||||
print(unicode_text.encode(encoding))
|
print(unicode_text.encode(encoding))
|
||||||
|
|
||||||
def unico(text):
|
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:
|
if sys.version_info[0] > 2:
|
||||||
return text
|
return text
|
||||||
else:
|
else:
|
||||||
return unicode(text)
|
return unicode(text)
|
||||||
|
|
||||||
def match_last(pattern):
|
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
|
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])
|
last_pattern_path = re.sub("(.*)"+COMPLETION_SEPARATOR, "", pattern[-1])
|
||||||
if (len(last_pattern_path) > 0 and
|
if (len(last_pattern_path) > 0 and
|
||||||
last_pattern_path[0] == "/" and
|
last_pattern_path[0] == "/" and
|
||||||
@ -274,8 +321,10 @@ def match_last(pattern):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def match(path, pattern, only_end=False, ignore_case=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:
|
if only_end:
|
||||||
match_path = "/".join(path.split('/')[-1-pattern.count('/'):])
|
match_path = "/".join(path.split('/')[-1-pattern.count('/'):])
|
||||||
else:
|
else:
|
||||||
@ -293,7 +342,9 @@ def match(path, pattern, only_end=False, ignore_case=False):
|
|||||||
return (False, path[find_idx+len(pattern):])
|
return (False, path[find_idx+len(pattern):])
|
||||||
|
|
||||||
def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
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:
|
try:
|
||||||
current_dir = decode(os.path.realpath(os.curdir))
|
current_dir = decode(os.path.realpath(os.curdir))
|
||||||
except OSError:
|
except OSError:
|
||||||
@ -314,7 +365,7 @@ def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
|||||||
end = d[0].split('/')[-1]
|
end = d[0].split('/')[-1]
|
||||||
|
|
||||||
# collisions: ignore lower weight paths
|
# 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]
|
end_dirs[end] = d[0]
|
||||||
|
|
||||||
# find the first match (heighest weight)
|
# 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)
|
found, tmp = match(tmp, p, False, ignore_case)
|
||||||
if not found: break
|
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:
|
if path not in results:
|
||||||
results.append(path)
|
results.append(path)
|
||||||
if len(results) >= max_matches:
|
if len(results) >= max_matches:
|
||||||
@ -348,8 +399,14 @@ def find_matches(db, patterns, max_matches=1, ignore_case=False, fuzzy=False):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
def shell_utility():
|
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
|
if options(): return True
|
||||||
|
config()
|
||||||
|
|
||||||
|
global ARGS, COMPLETION_SEPARATOR, DB_FILE, \
|
||||||
|
ALWAYS_IGNORE_CASE, KEEP_ALL_ENTRIES
|
||||||
db = Database(DB_FILE)
|
db = Database(DB_FILE)
|
||||||
|
|
||||||
# if no directories, add empty string
|
# if no directories, add empty string
|
||||||
|
Loading…
Reference in New Issue
Block a user