mirror of
https://github.com/wting/autojump
synced 2024-10-27 20:34:07 +00:00
Implement approximate matching via Levenshtein distance.
This commit is contained in:
parent
2a93e3c570
commit
9b977379eb
72
autojump
72
autojump
@ -30,6 +30,7 @@ import getopt
|
|||||||
from sys import argv, stderr, version_info, exit
|
from sys import argv, stderr, version_info, exit
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
from copy import copy
|
||||||
import os
|
import os
|
||||||
MAX_KEYWEIGHT = 1000
|
MAX_KEYWEIGHT = 1000
|
||||||
MAX_STORED_PATHS = 600
|
MAX_STORED_PATHS = 600
|
||||||
@ -88,29 +89,61 @@ def clean_dict(sorted_dirs, path_dict):
|
|||||||
return True
|
return True
|
||||||
else: return False
|
else: return False
|
||||||
|
|
||||||
def match(path, pattern, ignore_case=False, only_end=False):
|
def approximatch(pat, text):
|
||||||
"""Check whether a path matches a particular pattern"""
|
prev_col = list(range(0, len(pat)+1))
|
||||||
if only_end:
|
col = [0] * (len(pat) + 1)
|
||||||
|
errors = len(pat)
|
||||||
|
for char1 in text:
|
||||||
|
col[0] = 0
|
||||||
|
for i, char2 in enumerate(pat):
|
||||||
|
if char1 == char2:
|
||||||
|
col[i+1] = prev_col[i]
|
||||||
|
else:
|
||||||
|
col[i+1] = 1 + min(col[i], prev_col[i+1], prev_col[i])
|
||||||
|
prev_col = copy(col)
|
||||||
|
errors = min(errors, col[-1])
|
||||||
|
return errors
|
||||||
|
|
||||||
|
def find_matches(dirs, patterns, result_list, ignore_case, approx, max_matches):
|
||||||
|
"""Find max_matches paths that match the pattern,
|
||||||
|
and add them to the result_list"""
|
||||||
|
|
||||||
|
def get_pattern_and_match(patterns, path):
|
||||||
|
#For the last pattern, only match the end of the pattern
|
||||||
|
for n, pattern in enumerate(patterns):
|
||||||
|
if n == len(patterns) - 1:
|
||||||
match_string = "/".join(path.split('/')[-1-pattern.count('/'):])
|
match_string = "/".join(path.split('/')[-1-pattern.count('/'):])
|
||||||
else:
|
else:
|
||||||
match_string = path
|
match_string = path
|
||||||
if ignore_case:
|
if ignore_case:
|
||||||
does_match = (match_string.lower().find(pattern.lower()) != -1)
|
pattern = pattern.lower()
|
||||||
else:
|
match_string = match_string.lower()
|
||||||
does_match = (match_string.find(pattern) != -1)
|
yield (pattern, match_string)
|
||||||
#return True if there is a match and the path exists
|
|
||||||
#(useful in the case of external drives, for example)
|
|
||||||
return does_match and os.path.exists(path)
|
|
||||||
|
|
||||||
def find_matches(dirs, patterns, result_list, ignore_case, max_matches):
|
if approx:
|
||||||
"""Find max_matches paths that match the pattern,
|
one_error_paths = []
|
||||||
and add them to the result_list"""
|
two_error_paths = []
|
||||||
|
for path, count in dirs:
|
||||||
|
if len(one_error_paths) >= max_matches:
|
||||||
|
break
|
||||||
|
errors = sum(approximatch(pattern, match_string)
|
||||||
|
for pattern, match_string in get_pattern_and_match(patterns, path))
|
||||||
|
#Verify that the path exists
|
||||||
|
#(useful in the case of external drives, for example)
|
||||||
|
if errors <= 2 and os.path.exists(path):
|
||||||
|
if errors == 1:
|
||||||
|
uniqadd(one_error_paths, path)
|
||||||
|
elif errors == 2:
|
||||||
|
uniqadd(two_error_paths, path)
|
||||||
|
result_list.extend(one_error_paths)
|
||||||
|
result_list.extend(two_error_paths[:max_matches-len(one_error_paths)])
|
||||||
|
else:
|
||||||
for path, count in dirs:
|
for path, count in dirs:
|
||||||
if len(result_list) >= max_matches:
|
if len(result_list) >= max_matches:
|
||||||
break
|
break
|
||||||
#For the last pattern, only match the end of the pattern
|
if all(match_string.find(pattern) != -1
|
||||||
if all(match(path, p, ignore_case,
|
for pattern, match_string in
|
||||||
only_end=(n == len(patterns)-1)) for n, p in enumerate(patterns)):
|
get_pattern_and_match(patterns, path)) and os.path.exists(path):
|
||||||
uniqadd(result_list, path)
|
uniqadd(result_list, path)
|
||||||
|
|
||||||
def open_dic(dic_file, error_recovery=False):
|
def open_dic(dic_file, error_recovery=False):
|
||||||
@ -206,12 +239,17 @@ def shell_utility():
|
|||||||
max_matches = 9
|
max_matches = 9
|
||||||
else:
|
else:
|
||||||
max_matches = 1
|
max_matches = 1
|
||||||
find_matches(dirs, patterns, results, False, max_matches)
|
find_matches(dirs, patterns, results, False, False, max_matches)
|
||||||
# If not found, try ignoring case.
|
# If not found, try ignoring case.
|
||||||
# On completion always show all results
|
# On completion always show all results
|
||||||
if completion or not results:
|
if completion or not results:
|
||||||
find_matches(dirs, patterns, results,
|
find_matches(dirs, patterns, results,
|
||||||
ignore_case=True, max_matches=max_matches)
|
ignore_case=True, approx=False, max_matches=max_matches)
|
||||||
|
|
||||||
|
if not results:
|
||||||
|
find_matches(dirs, patterns, results,
|
||||||
|
ignore_case=True, approx=True, max_matches=max_matches)
|
||||||
|
|
||||||
# Keep the database to a reasonable size
|
# Keep the database to a reasonable size
|
||||||
if not completion and clean_dict(dirs, path_dict):
|
if not completion and clean_dict(dirs, path_dict):
|
||||||
save(path_dict, dic_file)
|
save(path_dict, dic_file)
|
||||||
|
Loading…
Reference in New Issue
Block a user