mirror of
https://github.com/wting/autojump
synced 2024-10-27 20:34:07 +00:00
Compare commits
96 Commits
release-v2
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
ff75f542ae | ||
|
96ae111824 | ||
|
06e082c918 | ||
|
af23852374 | ||
|
8fffbad95a | ||
|
daa496bc39 | ||
|
cf358d8fd3 | ||
|
bc4ea61546 | ||
|
8eace445a7 | ||
|
26c81fc39f | ||
|
900ce8e092 | ||
|
89eb573306 | ||
|
0cbfd764f5 | ||
|
a876bc1f39 | ||
|
fc43204ca1 | ||
|
965296f6b2 | ||
|
5ab005ea86 | ||
|
7494d142d5 | ||
|
4f84e755f5 | ||
|
b4e8474769 | ||
|
5e6bb82835 | ||
|
6a529f4f92 | ||
|
b06d686bfe | ||
|
a0719f488e | ||
|
0e4d15ace6 | ||
|
e7aebe69bf | ||
|
9ff654d41d | ||
|
46329ac744 | ||
|
37b336bf98 | ||
|
e8f3cbd874 | ||
|
d5a4fd270d | ||
|
f59e398c48 | ||
|
13a63fbf3a | ||
|
0bf1ad5ba9 | ||
|
3f0eae1be7 | ||
|
b0bc534838 | ||
|
865476ed56 | ||
|
3e089a3b32 | ||
|
2e2ffcfadb | ||
|
4f238bdd46 | ||
|
b2f243ee17 | ||
|
8eb094220b | ||
|
df935c664c | ||
|
cacbb68591 | ||
|
39990fff58 | ||
|
332698fdc5 | ||
|
c4d82b1888 | ||
|
7874719e4a | ||
|
9a26c1f17b | ||
|
851f3e3b2d | ||
|
242936f258 | ||
|
215cee3a93 | ||
|
b9e70a0eea | ||
|
7c7865ea7e | ||
|
5e550267c7 | ||
|
dc9e11e7d5 | ||
|
3cb4e8a28c | ||
|
cf013c6875 | ||
|
eafd6ac451 | ||
|
737563570e | ||
|
218d779b4d | ||
|
7922a9013d | ||
|
797d97c9bf | ||
|
2e60fa2892 | ||
|
180c5d8402 | ||
|
e75d4d4d11 | ||
|
be5b703996 | ||
|
94dae01b49 | ||
|
d1822bc11a | ||
|
a24d199161 | ||
|
9241b4b20f | ||
|
a32f237043 | ||
|
9622659099 | ||
|
61af2bf4c5 | ||
|
6df88c33d2 | ||
|
68e10ae402 | ||
|
3090963beb | ||
|
ecd272e979 | ||
|
0bce510c7e | ||
|
9a6e2869b1 | ||
|
9cf647bc6d | ||
|
25ef9c6a3c | ||
|
d529790278 | ||
|
e57956cc1c | ||
|
a9c892f713 | ||
|
db1c32ff6e | ||
|
377eadbad3 | ||
|
ed7ec4dbdd | ||
|
0b184f3bf0 | ||
|
5985ce604a | ||
|
a5e4b83e82 | ||
|
4230bbe2d1 | ||
|
aa366c3a4f | ||
|
3e5cec67b9 | ||
|
b71612eea4 | ||
|
d3dd3f34e6 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,8 +1,10 @@
|
||||
.cache
|
||||
.coverage
|
||||
*.pyc
|
||||
__pycache__
|
||||
*~
|
||||
*.tar.gz
|
||||
*.patch
|
||||
.pytest_cache
|
||||
.tox
|
||||
tags
|
||||
|
51
.pre-commit-config.yaml
Normal file
51
.pre-commit-config.yaml
Normal file
@ -0,0 +1,51 @@
|
||||
repos:
|
||||
- repo: git@github.com:pre-commit/pre-commit-hooks.git
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: autopep8-wrapper
|
||||
language_version: python2
|
||||
args:
|
||||
- --in-place
|
||||
- --aggressive
|
||||
- --aggressive
|
||||
- --ignore=E731
|
||||
- --max-line-length=131
|
||||
- id: check-added-large-files
|
||||
language_version: python2
|
||||
- id: check-ast
|
||||
language_version: python2
|
||||
- id: check-case-conflict
|
||||
language_version: python2
|
||||
- id: check-docstring-first
|
||||
language_version: python2
|
||||
- id: debug-statements
|
||||
language_version: python2
|
||||
- id: double-quote-string-fixer
|
||||
language_version: python2
|
||||
- id: end-of-file-fixer
|
||||
language_version: python2
|
||||
exclude_types: [batch, lua]
|
||||
- id: fix-encoding-pragma
|
||||
language_version: python2
|
||||
- id: flake8
|
||||
language_version: python2
|
||||
args:
|
||||
- --max-complexity=10
|
||||
- --max-line-length=131
|
||||
- --ignore=E402,E731
|
||||
- --exclude=bin/autojump_argparse.py
|
||||
- id: name-tests-test
|
||||
language_version: python2
|
||||
- id: requirements-txt-fixer
|
||||
language_version: python2
|
||||
- id: trailing-whitespace
|
||||
language_version: python2
|
||||
- repo: git@github.com:asottile/reorder_python_imports.git
|
||||
rev: v1.1.1
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
language_version: python2
|
||||
- repo: git@github.com:asottile/add-trailing-comma
|
||||
rev: v0.7.0
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
@ -1,5 +1,8 @@
|
||||
## Summary of release changes, see commit history for more details:
|
||||
## https://github.com/joelthelion/autojump/commits/master/
|
||||
## https://github.com/wting/autojump/commits/master/
|
||||
|
||||
### Release v22.4.0:
|
||||
- minor zsh performance improvement
|
||||
|
||||
### Release v22.3.0:
|
||||
- use colors only if stdout is a terminal
|
||||
|
39
Makefile
39
Makefile
@ -1,21 +1,31 @@
|
||||
VERSION = $(shell grep -oE "[0-9]+\.[0-9]+\.[0-9]+" bin/autojump)
|
||||
TAGNAME = release-v$(VERSION)
|
||||
|
||||
.PHONY: docs install uninstall lint tar test
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
./install.py
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
./uninstall.py
|
||||
|
||||
.PHONY: docs
|
||||
docs:
|
||||
@echo "% autojump(1) ${TAGNAME}" >docs/manpage_header.md
|
||||
@echo "%" >>docs/manpage_header.md
|
||||
@echo "% $(shell \date +%Y-%m-%d)" >>docs/manpage_header.md
|
||||
pandoc -s -w man docs/manpage_header.md docs/header.md docs/body.md -o docs/autojump.1
|
||||
pandoc -s -w markdown docs/header.md docs/install.md docs/body.md -o README.md
|
||||
|
||||
lint:
|
||||
@flake8 ./ --config=tox.ini
|
||||
.PHONY: lint
|
||||
lint: pre-commit
|
||||
@tox -e pre-commit -- run --all-files
|
||||
|
||||
.PHONY: pre-commit
|
||||
pre-commit:
|
||||
@tox -e pre-commit -- install -f --install-hooks
|
||||
|
||||
.PHONY: release
|
||||
release: docs
|
||||
# Check for tag existence
|
||||
# git describe release-$(VERSION) 2>&1 >/dev/null || exit 1
|
||||
@ -33,11 +43,26 @@ release: docs
|
||||
git archive --format=tar --prefix autojump_v$(VERSION)/ $(TAGNAME) | gzip > autojump_v$(VERSION).tar.gz
|
||||
sha1sum autojump_v$(VERSION).tar.gz
|
||||
|
||||
.PHONY: tar
|
||||
tar:
|
||||
# Create tagged archive
|
||||
git archive --format=tar --prefix autojump_v$(VERSION)/ $(TAGNAME) | gzip > autojump_v$(VERSION).tar.gz
|
||||
sha1sum autojump_v$(VERSION).tar.gz
|
||||
|
||||
test: lint
|
||||
@find . -type f -iname "*.pyc" -delete
|
||||
tox
|
||||
.PHONY: test
|
||||
test: pre-commit
|
||||
@tox
|
||||
|
||||
.PHONY: test-xfail
|
||||
test-xfail: pre-commit
|
||||
@tox -- --runxfail
|
||||
|
||||
.PHONY: test-fast
|
||||
test-fast: pre-commit
|
||||
@tox -e py27
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@find . -type f -iname '*.py[co]' -delete
|
||||
@find . -type d -iname '__pycache__' -delete
|
||||
@rm -fr .tox
|
||||
|
87
README.md
87
README.md
@ -25,8 +25,8 @@ can be used with `autojump` can be used with `j` and vice versa.
|
||||
- Jump To A Child Directory:
|
||||
|
||||
Sometimes it's convenient to jump to a child directory
|
||||
(sub-directory of current directory) rather than typing out the full
|
||||
name.
|
||||
(sub-directory of current directory) rather than typing out the
|
||||
full name.
|
||||
|
||||
jc bar
|
||||
|
||||
@ -49,10 +49,10 @@ can be used with `autojump` can be used with `j` and vice versa.
|
||||
30 /home/user/mail/inbox
|
||||
10 /home/user/work/inbox
|
||||
|
||||
`j in` would jump into /home/user/mail/inbox as the higher weighted
|
||||
entry. However you can pass multiple arguments to autojump to prefer
|
||||
a different entry. In the above example, `j w in` would then change
|
||||
directory to /home/user/work/inbox.
|
||||
`j in` would jump into /home/user/mail/inbox as the higher
|
||||
weighted entry. However you can pass multiple arguments to autojump
|
||||
to prefer a different entry. In the above example, `j w in` would
|
||||
then change directory to /home/user/work/inbox.
|
||||
|
||||
For more options refer to help:
|
||||
|
||||
@ -63,29 +63,61 @@ INSTALLATION
|
||||
|
||||
### REQUIREMENTS
|
||||
|
||||
- Python v2.6+
|
||||
- Supported shells:
|
||||
- bash v4.0+
|
||||
- zsh
|
||||
- fish
|
||||
- tcsh (experimental)
|
||||
- clink (Windows, experimental)
|
||||
- Python v2.6+ or Python v3.3+
|
||||
- Supported shells
|
||||
- bash - first class support
|
||||
- zsh - first class support
|
||||
- fish - community supported
|
||||
- tcsh - community supported
|
||||
- clink - community supported
|
||||
- Supported platforms
|
||||
- Linux - first class support
|
||||
- OS X - first class support
|
||||
- Windows - community supported
|
||||
- BSD - community supported
|
||||
- Supported installation methods
|
||||
- source code - first class support
|
||||
- Debian and derivatives - first class support
|
||||
- ArchLinux / Gentoo / openSUSE / RedHat and derivatives -
|
||||
community supported
|
||||
- Homebrew / MacPorts - community supported
|
||||
|
||||
Due to limited time and resources, only "first class support" items will
|
||||
be maintained by the primary committers. All "community supported" items
|
||||
will be updated based on pull requests submitted by the general public.
|
||||
|
||||
Please continue opening issues and providing feedback for community
|
||||
supported items since consolidating information helps other users
|
||||
troubleshoot and submit enhancements and fixes.
|
||||
|
||||
### MANUAL
|
||||
|
||||
Grab a copy of autojump:
|
||||
|
||||
git clone git://github.com/wting/autojump.git
|
||||
|
||||
Run the installation script and follow on screen instructions.
|
||||
|
||||
cd autojump
|
||||
./install.py or ./uninstall.py
|
||||
|
||||
### AUTOMATIC
|
||||
|
||||
#### Linux
|
||||
|
||||
autojump is included in the following distro repositories, please use
|
||||
relevant package management utilities to install (e.g. yum, apt-get,
|
||||
etc):
|
||||
relevant package management utilities to install (e.g. apt-get, yum,
|
||||
pacman, etc):
|
||||
|
||||
- Debian testing/unstable, Ubuntu, Linux Mint
|
||||
- Debian, Ubuntu, Linux Mint
|
||||
|
||||
All Debian-derived distros require manual activation for policy
|
||||
reasons, please see `/usr/share/doc/autojump/README.Debian`.
|
||||
|
||||
- RedHat, Fedora, CentOS (install `autojump-zsh` for zsh,
|
||||
`autojump-fish` for fish, etc.)
|
||||
- RedHat, Fedora, CentOS
|
||||
|
||||
Install `autojump-zsh` for zsh, `autojump-fish` for fish, etc.
|
||||
|
||||
- ArchLinux
|
||||
- Gentoo
|
||||
- Frugalware
|
||||
@ -97,7 +129,7 @@ Homebrew is the recommended installation method for Mac OS X:
|
||||
|
||||
brew install autojump
|
||||
|
||||
MacPorts also available:
|
||||
MacPorts is also available:
|
||||
|
||||
port install autojump
|
||||
|
||||
@ -107,17 +139,6 @@ Windows
|
||||
Windows support is enabled by [clink](https://mridgers.github.io/clink/)
|
||||
which should be installed prior to installing autojump.
|
||||
|
||||
### MANUAL
|
||||
|
||||
Grab a copy of autojump:
|
||||
|
||||
git clone git://github.com/joelthelion/autojump.git
|
||||
|
||||
Run the installation script and follow on screen instructions.
|
||||
|
||||
cd autojump
|
||||
./install.py or ./uninstall.py
|
||||
|
||||
KNOWN ISSUES
|
||||
------------
|
||||
|
||||
@ -128,7 +149,7 @@ KNOWN ISSUES
|
||||
|
||||
export PROMPT_COMMAND="history -a"
|
||||
|
||||
Instead append to the end of the existing $PROMPT\_COMMAND:
|
||||
Instead append to the end of the existing \$PROMPT\_COMMAND:
|
||||
|
||||
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND ;} history -a"
|
||||
|
||||
@ -137,7 +158,7 @@ REPORTING BUGS
|
||||
|
||||
For any questions or issues please visit:
|
||||
|
||||
https://github.com/joelthelion/autojump/issues
|
||||
https://github.com/wting/autojump/issues
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
@ -148,7 +169,7 @@ maintained by William Ting. More contributors can be found in `AUTHORS`.
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Copyright © 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL
|
||||
Copyright © 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL
|
||||
version 3 or later <http://gnu.org/licenses/gpl.html>. This is free
|
||||
software: you are free to change and redistribute it. There is NO
|
||||
WARRANTY, to the extent permitted by law.
|
||||
|
252
bin/autojump
252
bin/autojump
@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright © 2008-2012 Joel Schaerer
|
||||
Copyright © 2012-2014 William Ting
|
||||
Copyright © 2012-2016 William Ting
|
||||
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -18,17 +18,14 @@
|
||||
along with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from difflib import SequenceMatcher
|
||||
import os
|
||||
import sys
|
||||
from itertools import chain
|
||||
from math import sqrt
|
||||
from operator import attrgetter
|
||||
from operator import itemgetter
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
ifilter = filter
|
||||
@ -38,13 +35,21 @@ else:
|
||||
from itertools import ifilter
|
||||
from itertools import imap
|
||||
|
||||
# Vendorized argparse for Python 2.6 support
|
||||
from autojump_argparse import ArgumentParser
|
||||
|
||||
# autojump is not a standard python package but rather installed as a mixed
|
||||
# shell + Python app with no outside dependencies (except Python). As a
|
||||
# consequence we use relative imports and depend on file prefixes to prevent
|
||||
# module conflicts.
|
||||
from autojump_data import dictify
|
||||
from autojump_data import entriefy
|
||||
from autojump_data import Entry
|
||||
from autojump_data import load
|
||||
from autojump_data import save
|
||||
from autojump_match import match_anywhere
|
||||
from autojump_match import match_consecutive
|
||||
from autojump_match import match_fuzzy
|
||||
from autojump_utils import first
|
||||
from autojump_utils import get_pwd
|
||||
from autojump_utils import get_tab_entry_info
|
||||
@ -60,7 +65,7 @@ from autojump_utils import sanitize
|
||||
from autojump_utils import take
|
||||
from autojump_utils import unico
|
||||
|
||||
VERSION = '22.2.4'
|
||||
VERSION = '22.5.3'
|
||||
FUZZY_MATCH_THRESHOLD = 0.6
|
||||
TAB_ENTRIES_COUNT = 9
|
||||
TAB_SEPARATOR = '__'
|
||||
@ -70,14 +75,9 @@ def set_defaults():
|
||||
config = {}
|
||||
|
||||
if is_osx():
|
||||
data_home = os.path.join(
|
||||
os.path.expanduser('~'),
|
||||
'Library',
|
||||
'autojump')
|
||||
data_home = os.path.join(os.path.expanduser('~'), 'Library')
|
||||
elif is_windows():
|
||||
data_home = os.path.join(
|
||||
os.getenv('APPDATA'),
|
||||
'autojump')
|
||||
data_home = os.getenv('APPDATA')
|
||||
else:
|
||||
data_home = os.getenv(
|
||||
'XDG_DATA_HOME',
|
||||
@ -85,10 +85,10 @@ def set_defaults():
|
||||
os.path.expanduser('~'),
|
||||
'.local',
|
||||
'share',
|
||||
'autojump'))
|
||||
|
||||
config['data_path'] = os.path.join(data_home, 'autojump.txt')
|
||||
config['backup_path'] = os.path.join(data_home, 'autojump.txt.bak')
|
||||
),
|
||||
)
|
||||
config['data_path'] = os.path.join(data_home, 'autojump', 'autojump.txt')
|
||||
config['backup_path'] = os.path.join(data_home, 'autojump', 'autojump.txt.bak')
|
||||
|
||||
return config
|
||||
|
||||
@ -96,33 +96,42 @@ def set_defaults():
|
||||
def parse_arguments():
|
||||
parser = 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='DIRECTORY', nargs='*', default='',
|
||||
help='directory to jump to')
|
||||
help='directory to jump to',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-a', '--add', metavar='DIRECTORY',
|
||||
help='add path')
|
||||
help='add path',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-i', '--increase', metavar='WEIGHT', nargs='?', type=int,
|
||||
const=10, default=False,
|
||||
help='increase current directory weight')
|
||||
help='increase current directory weight',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--decrease', metavar='WEIGHT', nargs='?', type=int,
|
||||
const=15, default=False,
|
||||
help='decrease current directory weight')
|
||||
help='decrease current directory weight',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--complete', action="store_true", default=False,
|
||||
help='used for tab completion')
|
||||
'--complete', action='store_true', default=False,
|
||||
help='used for tab completion',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--purge', action="store_true", default=False,
|
||||
help='remove non-existent paths from database')
|
||||
'--purge', action='store_true', default=False,
|
||||
help='remove non-existent paths from database',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s', '--stat', action="store_true", default=False,
|
||||
help='show database entries and their key weights')
|
||||
'-s', '--stat', action='store_true', default=False,
|
||||
help='show database entries and their key weights',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v', '--version', action="version", version="%(prog)s v" +
|
||||
VERSION, help='show version information')
|
||||
'-v', '--version', action='version', version='%(prog)s v' +
|
||||
VERSION, help='show version information',
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@ -162,7 +171,7 @@ def detect_smartcase(needles):
|
||||
def find_matches(entries, needles, check_entries=True):
|
||||
"""Return an iterator to matching entries."""
|
||||
# TODO(wting|2014-02-24): replace assertion with unit test
|
||||
assert isinstance(needles, list), "Needles must be a list."
|
||||
assert isinstance(needles, list), 'Needles must be a list.'
|
||||
ignore_case = detect_smartcase(needles)
|
||||
|
||||
try:
|
||||
@ -181,15 +190,18 @@ def find_matches(entries, needles, check_entries=True):
|
||||
|
||||
data = sorted(
|
||||
entries,
|
||||
key=attrgetter('weight'),
|
||||
reverse=True)
|
||||
key=attrgetter('weight', 'path'),
|
||||
reverse=True,
|
||||
)
|
||||
|
||||
return ifilter(
|
||||
lambda entry: not is_cwd(entry) and path_exists(entry),
|
||||
chain(
|
||||
match_consecutive(needles, data, ignore_case),
|
||||
match_fuzzy(needles, data, ignore_case),
|
||||
match_anywhere(needles, data, ignore_case)))
|
||||
match_anywhere(needles, data, ignore_case),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def handle_tab_completion(needle, entries):
|
||||
@ -201,129 +213,33 @@ def handle_tab_completion(needle, entries):
|
||||
get_ith_path = lambda i, iterable: last(take(i, iterable)).path
|
||||
print_local(get_ith_path(
|
||||
tab_index,
|
||||
find_matches(entries, [tab_needle], check_entries=False)))
|
||||
find_matches(entries, [tab_needle], check_entries=False),
|
||||
))
|
||||
elif tab_needle:
|
||||
# found partial tab completion entry
|
||||
print_tab_menu(
|
||||
tab_needle,
|
||||
take(TAB_ENTRIES_COUNT, find_matches(
|
||||
entries,
|
||||
[tab_needle],
|
||||
check_entries=False)),
|
||||
TAB_SEPARATOR)
|
||||
take(
|
||||
TAB_ENTRIES_COUNT, find_matches(
|
||||
entries,
|
||||
[tab_needle],
|
||||
check_entries=False,
|
||||
),
|
||||
),
|
||||
TAB_SEPARATOR,
|
||||
)
|
||||
else:
|
||||
print_tab_menu(
|
||||
needle,
|
||||
take(TAB_ENTRIES_COUNT, find_matches(
|
||||
entries,
|
||||
[needle],
|
||||
check_entries=False)),
|
||||
TAB_SEPARATOR)
|
||||
|
||||
|
||||
def match_anywhere(needles, haystack, ignore_case=False):
|
||||
"""
|
||||
Matches needles anywhere in the path as long as they're in the same (but
|
||||
not necessary consecutive) order.
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'baz']
|
||||
regex needle = r'.*foo.*baz.*'
|
||||
haystack = [
|
||||
(path="/foo/bar/baz", weight=10),
|
||||
(path="/baz/foo/bar", weight=10),
|
||||
(path="/foo/baz", weight=10)]
|
||||
|
||||
result = [
|
||||
(path="/moo/foo/baz", weight=10),
|
||||
(path="/foo/baz", weight=10)]
|
||||
"""
|
||||
regex_needle = '.*' + '.*'.join(needles).replace('\\', '\\\\') + '.*'
|
||||
regex_flags = re.IGNORECASE | re.UNICODE if ignore_case else re.UNICODE
|
||||
found = lambda haystack: re.search(
|
||||
regex_needle,
|
||||
haystack.path,
|
||||
flags=regex_flags)
|
||||
return ifilter(found, haystack)
|
||||
|
||||
|
||||
def match_consecutive(needles, haystack, ignore_case=False):
|
||||
"""
|
||||
Matches consecutive needles at the end of a path.
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'baz']
|
||||
haystack = [
|
||||
(path="/foo/bar/baz", weight=10),
|
||||
(path="/foo/baz/moo", weight=10),
|
||||
(path="/moo/foo/baz", weight=10),
|
||||
(path="/foo/baz", weight=10)]
|
||||
|
||||
regex_needle = re.compile(r'''
|
||||
foo # needle #1
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
/ # os.sep
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
baz # needle #2
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
$ # end of string
|
||||
''')
|
||||
|
||||
result = [
|
||||
(path="/moo/foo/baz", weight=10),
|
||||
(path="/foo/baz", weight=10)]
|
||||
"""
|
||||
# The normal \\ separator needs to be escaped again for use in regex.
|
||||
sep = '\\\\' if is_windows() else os.sep
|
||||
regex_no_sep = '[^' + sep + ']*'
|
||||
regex_no_sep_end = regex_no_sep + '$'
|
||||
regex_one_sep = regex_no_sep + sep + regex_no_sep
|
||||
# can't use compiled regex because of flags
|
||||
regex_needle = regex_one_sep.join(needles).replace('\\', '\\\\') + regex_no_sep_end # noqa
|
||||
regex_flags = re.IGNORECASE | re.UNICODE if ignore_case else re.UNICODE
|
||||
found = lambda entry: re.search(
|
||||
regex_needle,
|
||||
entry.path,
|
||||
flags=regex_flags)
|
||||
return ifilter(found, haystack)
|
||||
|
||||
|
||||
def match_fuzzy(needles, haystack, ignore_case=False):
|
||||
"""
|
||||
Performs an approximate match with the last needle against the end of
|
||||
every path past an acceptable threshold (FUZZY_MATCH_THRESHOLD).
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'bar']
|
||||
haystack = [
|
||||
(path="/foo/bar/baz", weight=11),
|
||||
(path="/foo/baz/moo", weight=10),
|
||||
(path="/moo/foo/baz", weight=10),
|
||||
(path="/foo/baz", weight=10),
|
||||
(path="/foo/bar", weight=10)]
|
||||
|
||||
result = [
|
||||
(path="/foo/bar/baz", weight=11),
|
||||
(path="/moo/foo/baz", weight=10),
|
||||
(path="/foo/baz", weight=10),
|
||||
(path="/foo/bar", weight=10)]
|
||||
|
||||
This is a weak heuristic and used as a last resort to find matches.
|
||||
"""
|
||||
end_dir = lambda path: last(os.path.split(path))
|
||||
if ignore_case:
|
||||
needle = last(needles).lower()
|
||||
match_percent = lambda entry: SequenceMatcher(
|
||||
a=needle,
|
||||
b=end_dir(entry.path.lower())).ratio()
|
||||
else:
|
||||
needle = last(needles)
|
||||
match_percent = lambda entry: SequenceMatcher(
|
||||
a=needle,
|
||||
b=end_dir(entry.path)).ratio()
|
||||
meets_threshold = lambda entry: match_percent(entry) >= \
|
||||
FUZZY_MATCH_THRESHOLD
|
||||
return ifilter(meets_threshold, haystack)
|
||||
take(
|
||||
TAB_ENTRIES_COUNT, find_matches(
|
||||
entries,
|
||||
[needle],
|
||||
check_entries=False,
|
||||
),
|
||||
),
|
||||
TAB_SEPARATOR,
|
||||
)
|
||||
|
||||
|
||||
def purge_missing_paths(entries):
|
||||
@ -336,25 +252,26 @@ def print_stats(data, data_path):
|
||||
for path, weight in sorted(data.items(), key=itemgetter(1)):
|
||||
print_entry(Entry(path, weight))
|
||||
|
||||
print("________________________________________\n")
|
||||
print("%d:\t total weight" % sum(data.values()))
|
||||
print("%d:\t number of entries" % len(data))
|
||||
print('________________________________________\n')
|
||||
print('%d:\t total weight' % sum(data.values()))
|
||||
print('%d:\t number of entries' % len(data))
|
||||
|
||||
try:
|
||||
print_local(
|
||||
"%.2f:\t current directory weight" % data.get(os.getcwdu(), 0))
|
||||
'%.2f:\t current directory weight' % data.get(os.getcwdu(), 0),
|
||||
)
|
||||
except OSError:
|
||||
# current directory no longer exists
|
||||
pass
|
||||
|
||||
print("\ndata:\t %s" % data_path)
|
||||
print('\ndata:\t %s' % data_path)
|
||||
|
||||
|
||||
def main(args): # noqa
|
||||
if not is_autojump_sourced() and not is_windows():
|
||||
print("Please source the correct autojump file in your shell's")
|
||||
print("startup file. For more information, please reinstall autojump")
|
||||
print("and read the post installation instructions.")
|
||||
print('startup file. For more information, please reinstall autojump')
|
||||
print('and read the post installation instructions.')
|
||||
return 1
|
||||
|
||||
config = set_defaults()
|
||||
@ -365,7 +282,8 @@ def main(args): # noqa
|
||||
elif args.complete:
|
||||
handle_tab_completion(
|
||||
needle=first(chain(sanitize(args.directory), [''])),
|
||||
entries=entriefy(load(config)))
|
||||
entries=entriefy(load(config)),
|
||||
)
|
||||
elif args.decrease:
|
||||
data, entry = decrease_path(load(config), get_pwd(), args.decrease)
|
||||
save(config, data)
|
||||
@ -378,7 +296,7 @@ def main(args): # noqa
|
||||
old_data = load(config)
|
||||
new_data = dictify(purge_missing_paths(entriefy(old_data)))
|
||||
save(config, new_data)
|
||||
print("Purged %d entries." % (len(old_data) - len(new_data)))
|
||||
print('Purged %d entries.' % (len(old_data) - len(new_data)))
|
||||
elif args.stat:
|
||||
print_stats(load(config), config['data_path'])
|
||||
elif not args.directory:
|
||||
@ -387,7 +305,8 @@ def main(args): # noqa
|
||||
print_local(first(chain(
|
||||
imap(attrgetter('path'), find_matches(entries, [''])),
|
||||
# always return a path to calling shell functions
|
||||
['.'])))
|
||||
['.'],
|
||||
)))
|
||||
else:
|
||||
entries = entriefy(load(config))
|
||||
needles = sanitize(args.directory)
|
||||
@ -406,15 +325,18 @@ def main(args): # noqa
|
||||
print_local(
|
||||
get_ith_path(
|
||||
tab_index,
|
||||
find_matches(entries, [tab_needle])))
|
||||
find_matches(entries, [tab_needle]),
|
||||
),
|
||||
)
|
||||
else:
|
||||
print_local(first(chain(
|
||||
imap(attrgetter('path'), find_matches(entries, needles)),
|
||||
# always return a path to calling shell functions
|
||||
['.'])))
|
||||
['.'],
|
||||
)))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(parse_arguments()))
|
||||
|
@ -61,11 +61,11 @@ j() {
|
||||
|
||||
output="$(autojump ${@})"
|
||||
if [[ -d "${output}" ]]; then
|
||||
if [ -t 1 ]; then # if stdout is a terminal, use colors
|
||||
echo -e "\\033[31m${output}\\033[0m"
|
||||
else
|
||||
echo -e "${output}"
|
||||
fi
|
||||
if [ -t 1 ]; then # if stdout is a terminal, use colors
|
||||
echo -e "\\033[31m${output}\\033[0m"
|
||||
else
|
||||
echo -e "${output}"
|
||||
fi
|
||||
cd "${output}"
|
||||
else
|
||||
echo "autojump: directory '${@}' not found"
|
||||
|
@ -1,2 +1,2 @@
|
||||
@echo off
|
||||
python %~dp0\autojump %*
|
||||
python "%~dp0\autojump" %*
|
||||
|
@ -1,4 +1,4 @@
|
||||
set -x AUTOJUMP_SOURCED 1
|
||||
set -gx AUTOJUMP_SOURCED 1
|
||||
|
||||
# set user installation path
|
||||
if test -d ~/.autojump
|
||||
@ -17,11 +17,11 @@ complete -x -c j -a '(autojump --complete (commandline -t))'
|
||||
|
||||
# set error file location
|
||||
if test (uname) = "Darwin"
|
||||
set -x AUTOJUMP_ERROR_PATH ~/Library/autojump/errors.log
|
||||
set -gx AUTOJUMP_ERROR_PATH ~/Library/autojump/errors.log
|
||||
else if test -d "$XDG_DATA_HOME"
|
||||
set -x AUTOJUMP_ERROR_PATH $XDG_DATA_HOME/autojump/errors.log
|
||||
set -gx AUTOJUMP_ERROR_PATH $XDG_DATA_HOME/autojump/errors.log
|
||||
else
|
||||
set -x AUTOJUMP_ERROR_PATH ~/.local/share/autojump/errors.log
|
||||
set -gx AUTOJUMP_ERROR_PATH ~/.local/share/autojump/errors.log
|
||||
end
|
||||
|
||||
if test ! -d (dirname $AUTOJUMP_ERROR_PATH)
|
||||
|
@ -1,13 +1,15 @@
|
||||
local AUTOJUMP_DIR = debug.getinfo(1, "S").source:match[[^@?(.*[\/])[^\/]-$]] .. "..\\AutoJump"
|
||||
local AUTOJUMP_BIN_DIR = AUTOJUMP_DIR .. "\\bin"
|
||||
local AUTOJUMP_BIN = (AUTOJUMP_BIN_DIR or clink.get_env("LOCALAPPDATA") .. "\\autojump\\bin") .. "\\autojump"
|
||||
|
||||
function autojump_add_to_database()
|
||||
os.execute("python " .. AUTOJUMP_BIN .. " --add " .. clink.get_cwd() .. " 2> " .. clink.get_env("TEMP") .. "\\autojump_error.txt")
|
||||
os.execute("python " .. "\"" .. AUTOJUMP_BIN .. "\"" .. " --add " .. "\"" .. clink.get_cwd() .. "\"" .. " 2> " .. clink.get_env("TEMP") .. "\\autojump_error.txt")
|
||||
end
|
||||
|
||||
clink.prompt.register_filter(autojump_add_to_database, 99)
|
||||
|
||||
function autojump_completion(word)
|
||||
for line in io.popen("python " .. AUTOJUMP_BIN .. " --complete " .. word):lines() do
|
||||
for line in io.popen("python " .. "\"" .. AUTOJUMP_BIN .. "\"" .. " --complete " .. word):lines() do
|
||||
clink.add_match(line)
|
||||
end
|
||||
return {}
|
||||
|
@ -10,8 +10,11 @@ fi
|
||||
|
||||
|
||||
# set homebrew installation paths
|
||||
if command -v brew &>/dev/null && [[ -d "$(brew --prefix)/share/zsh/site-functions" ]]; then
|
||||
fpath=("$(brew --prefix)/share/zsh/site-functions" ${fpath})
|
||||
if command -v brew &>/dev/null; then
|
||||
local brew_prefix=${BREW_PREFIX:-$(brew --prefix)}
|
||||
if [[ -d "${brew_prefix}/share/zsh/site-functions" ]]; then
|
||||
fpath=("${brew_prefix}/share/zsh/site-functions" ${fpath})
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@ -52,11 +55,11 @@ j() {
|
||||
setopt localoptions noautonamedirs
|
||||
local output="$(autojump ${@})"
|
||||
if [[ -d "${output}" ]]; then
|
||||
if [ -t 1 ]; then # if stdout is a terminal, use colors
|
||||
echo -e "\\033[31m${output}\\033[0m"
|
||||
else
|
||||
echo -e "${output}"
|
||||
fi
|
||||
if [ -t 1 ]; then # if stdout is a terminal, use colors
|
||||
echo -e "\\033[31m${output}\\033[0m"
|
||||
else
|
||||
echo -e "${output}"
|
||||
fi
|
||||
cd "${output}"
|
||||
else
|
||||
echo "autojump: directory '${@}' not found"
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
|
||||
# -*- coding: utf-8 -*-
|
||||
# Author: Steven J. Bethard <steven.bethard@gmail.com>.
|
||||
# flake8: noqa
|
||||
"""Command-line parsing library
|
||||
|
||||
@ -175,11 +175,13 @@ class HelpFormatter(object):
|
||||
provided by the class are considered an implementation detail.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
prog,
|
||||
indent_increment=2,
|
||||
max_help_position=24,
|
||||
width=None):
|
||||
def __init__(
|
||||
self,
|
||||
prog,
|
||||
indent_increment=2,
|
||||
max_help_position=24,
|
||||
width=None,
|
||||
):
|
||||
|
||||
# default setting for width
|
||||
if width is None:
|
||||
@ -286,8 +288,10 @@ class HelpFormatter(object):
|
||||
# update the maximum item length
|
||||
invocation_length = max([len(s) for s in invocations])
|
||||
action_length = invocation_length + self._current_indent
|
||||
self._action_max_length = max(self._action_max_length,
|
||||
action_length)
|
||||
self._action_max_length = max(
|
||||
self._action_max_length,
|
||||
action_length,
|
||||
)
|
||||
|
||||
# add the item to the list
|
||||
self._add_item(self._format_action, [action])
|
||||
@ -307,9 +311,11 @@ class HelpFormatter(object):
|
||||
return help
|
||||
|
||||
def _join_parts(self, part_strings):
|
||||
return ''.join([part
|
||||
for part in part_strings
|
||||
if part and part is not SUPPRESS])
|
||||
return ''.join([
|
||||
part
|
||||
for part in part_strings
|
||||
if part and part is not SUPPRESS
|
||||
])
|
||||
|
||||
def _format_usage(self, usage, actions, groups, prefix):
|
||||
if prefix is None:
|
||||
@ -508,8 +514,10 @@ class HelpFormatter(object):
|
||||
|
||||
def _format_action(self, action):
|
||||
# determine the required width and the entry label
|
||||
help_position = min(self._action_max_length + 2,
|
||||
self._max_help_position)
|
||||
help_position = min(
|
||||
self._action_max_length + 2,
|
||||
self._max_help_position,
|
||||
)
|
||||
help_width = self._width - help_position
|
||||
action_width = help_position - self._current_indent - 2
|
||||
action_header = self._format_action_invocation(action)
|
||||
@ -641,8 +649,10 @@ class HelpFormatter(object):
|
||||
|
||||
def _fill_text(self, text, width, indent):
|
||||
text = self._whitespace_matcher.sub(' ', text).strip()
|
||||
return _textwrap.fill(text, width, initial_indent=indent,
|
||||
subsequent_indent=indent)
|
||||
return _textwrap.fill(
|
||||
text, width, initial_indent=indent,
|
||||
subsequent_indent=indent,
|
||||
)
|
||||
|
||||
def _get_help_string(self, action):
|
||||
return action.help
|
||||
@ -724,8 +734,10 @@ class ArgumentError(Exception):
|
||||
format = '%(message)s'
|
||||
else:
|
||||
format = 'argument %(argument_name)s: %(message)s'
|
||||
return format % dict(message=self.message,
|
||||
argument_name=self.argument_name)
|
||||
return format % dict(
|
||||
message=self.message,
|
||||
argument_name=self.argument_name,
|
||||
)
|
||||
|
||||
|
||||
class ArgumentTypeError(Exception):
|
||||
@ -790,17 +802,19 @@ class Action(_AttributeHolder):
|
||||
help string. If None, the 'dest' value will be used as the name.
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
self.option_strings = option_strings
|
||||
self.dest = dest
|
||||
self.nargs = nargs
|
||||
@ -832,21 +846,25 @@ class Action(_AttributeHolder):
|
||||
|
||||
class _StoreAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
if nargs == 0:
|
||||
raise ValueError('nargs for store actions must be > 0; if you '
|
||||
'have nothing to store, actions such as store '
|
||||
'true or store const may be more appropriate')
|
||||
raise ValueError(
|
||||
'nargs for store actions must be > 0; if you '
|
||||
'have nothing to store, actions such as store '
|
||||
'true or store const may be more appropriate',
|
||||
)
|
||||
if const is not None and nargs != OPTIONAL:
|
||||
raise ValueError('nargs must be %r to supply const' % OPTIONAL)
|
||||
super(_StoreAction, self).__init__(
|
||||
@ -859,7 +877,8 @@ class _StoreAction(Action):
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
metavar=metavar,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, values)
|
||||
@ -867,14 +886,16 @@ class _StoreAction(Action):
|
||||
|
||||
class _StoreConstAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
const,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
const,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
super(_StoreConstAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
@ -882,7 +903,8 @@ class _StoreConstAction(Action):
|
||||
const=const,
|
||||
default=default,
|
||||
required=required,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
setattr(namespace, self.dest, self.const)
|
||||
@ -890,55 +912,65 @@ class _StoreConstAction(Action):
|
||||
|
||||
class _StoreTrueAction(_StoreConstAction):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=False,
|
||||
required=False,
|
||||
help=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=False,
|
||||
required=False,
|
||||
help=None,
|
||||
):
|
||||
super(_StoreTrueAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
const=True,
|
||||
default=default,
|
||||
required=required,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
|
||||
|
||||
class _StoreFalseAction(_StoreConstAction):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=True,
|
||||
required=False,
|
||||
help=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=True,
|
||||
required=False,
|
||||
help=None,
|
||||
):
|
||||
super(_StoreFalseAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
const=False,
|
||||
default=default,
|
||||
required=required,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
|
||||
|
||||
class _AppendAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
nargs=None,
|
||||
const=None,
|
||||
default=None,
|
||||
type=None,
|
||||
choices=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
if nargs == 0:
|
||||
raise ValueError('nargs for append actions must be > 0; if arg '
|
||||
'strings are not supplying the value to append, '
|
||||
'the append const action may be more appropriate')
|
||||
raise ValueError(
|
||||
'nargs for append actions must be > 0; if arg '
|
||||
'strings are not supplying the value to append, '
|
||||
'the append const action may be more appropriate',
|
||||
)
|
||||
if const is not None and nargs != OPTIONAL:
|
||||
raise ValueError('nargs must be %r to supply const' % OPTIONAL)
|
||||
super(_AppendAction, self).__init__(
|
||||
@ -951,7 +983,8 @@ class _AppendAction(Action):
|
||||
choices=choices,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
metavar=metavar,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
items = _copy.copy(_ensure_value(namespace, self.dest, []))
|
||||
@ -961,14 +994,16 @@ class _AppendAction(Action):
|
||||
|
||||
class _AppendConstAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
const,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
const,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
super(_AppendConstAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
@ -977,7 +1012,8 @@ class _AppendConstAction(Action):
|
||||
default=default,
|
||||
required=required,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
metavar=metavar,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
items = _copy.copy(_ensure_value(namespace, self.dest, []))
|
||||
@ -987,19 +1023,22 @@ class _AppendConstAction(Action):
|
||||
|
||||
class _CountAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest,
|
||||
default=None,
|
||||
required=False,
|
||||
help=None,
|
||||
):
|
||||
super(_CountAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
nargs=0,
|
||||
default=default,
|
||||
required=required,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
new_count = _ensure_value(namespace, self.dest, 0) + 1
|
||||
@ -1008,17 +1047,20 @@ class _CountAction(Action):
|
||||
|
||||
class _HelpAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
dest=SUPPRESS,
|
||||
default=SUPPRESS,
|
||||
help=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
dest=SUPPRESS,
|
||||
default=SUPPRESS,
|
||||
help=None,
|
||||
):
|
||||
super(_HelpAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
default=default,
|
||||
nargs=0,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
parser.print_help()
|
||||
@ -1027,18 +1069,21 @@ class _HelpAction(Action):
|
||||
|
||||
class _VersionAction(Action):
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
version=None,
|
||||
dest=SUPPRESS,
|
||||
default=SUPPRESS,
|
||||
help="show program's version number and exit"):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
version=None,
|
||||
dest=SUPPRESS,
|
||||
default=SUPPRESS,
|
||||
help="show program's version number and exit",
|
||||
):
|
||||
super(_VersionAction, self).__init__(
|
||||
option_strings=option_strings,
|
||||
dest=dest,
|
||||
default=default,
|
||||
nargs=0,
|
||||
help=help)
|
||||
help=help,
|
||||
)
|
||||
self.version = version
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
@ -1058,13 +1103,15 @@ class _SubParsersAction(Action):
|
||||
sup = super(_SubParsersAction._ChoicesPseudoAction, self)
|
||||
sup.__init__(option_strings=[], dest=name, help=help)
|
||||
|
||||
def __init__(self,
|
||||
option_strings,
|
||||
prog,
|
||||
parser_class,
|
||||
dest=SUPPRESS,
|
||||
help=None,
|
||||
metavar=None):
|
||||
def __init__(
|
||||
self,
|
||||
option_strings,
|
||||
prog,
|
||||
parser_class,
|
||||
dest=SUPPRESS,
|
||||
help=None,
|
||||
metavar=None,
|
||||
):
|
||||
|
||||
self._prog_prefix = prog
|
||||
self._parser_class = parser_class
|
||||
@ -1077,7 +1124,8 @@ class _SubParsersAction(Action):
|
||||
nargs=PARSER,
|
||||
choices=self._name_parser_map,
|
||||
help=help,
|
||||
metavar=metavar)
|
||||
metavar=metavar,
|
||||
)
|
||||
|
||||
def add_parser(self, name, **kwargs):
|
||||
# set prog from the existing prefix
|
||||
@ -1118,7 +1166,8 @@ class _SubParsersAction(Action):
|
||||
# store any unrecognized options on the object, so that the top
|
||||
# level parser can decide what to do with them
|
||||
namespace, arg_strings = parser.parse_known_args(
|
||||
arg_strings, namespace)
|
||||
arg_strings, namespace,
|
||||
)
|
||||
if arg_strings:
|
||||
vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, [])
|
||||
getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings)
|
||||
@ -1199,11 +1248,13 @@ class Namespace(_AttributeHolder):
|
||||
|
||||
class _ActionsContainer(object):
|
||||
|
||||
def __init__(self,
|
||||
description,
|
||||
prefix_chars,
|
||||
argument_default,
|
||||
conflict_handler):
|
||||
def __init__(
|
||||
self,
|
||||
description,
|
||||
prefix_chars,
|
||||
argument_default,
|
||||
conflict_handler,
|
||||
):
|
||||
super(_ActionsContainer, self).__init__()
|
||||
|
||||
self.description = description
|
||||
@ -1372,7 +1423,8 @@ class _ActionsContainer(object):
|
||||
title_group_map[group.title] = self.add_argument_group(
|
||||
title=group.title,
|
||||
description=group.description,
|
||||
conflict_handler=group.conflict_handler)
|
||||
conflict_handler=group.conflict_handler,
|
||||
)
|
||||
|
||||
# map the actions to their new group
|
||||
for action in group._group_actions:
|
||||
@ -1383,7 +1435,8 @@ class _ActionsContainer(object):
|
||||
# description= then this code will need to be expanded as above
|
||||
for group in container._mutually_exclusive_groups:
|
||||
mutex_group = self.add_mutually_exclusive_group(
|
||||
required=group.required)
|
||||
required=group.required,
|
||||
)
|
||||
|
||||
# map the actions to their new mutex group
|
||||
for action in group._group_actions:
|
||||
@ -1416,8 +1469,10 @@ class _ActionsContainer(object):
|
||||
for option_string in args:
|
||||
# error on strings that don't start with an appropriate prefix
|
||||
if not option_string[0] in self.prefix_chars:
|
||||
msg = _('invalid option string %r: '
|
||||
'must start with a character %r')
|
||||
msg = _(
|
||||
'invalid option string %r: '
|
||||
'must start with a character %r',
|
||||
)
|
||||
tup = option_string, self.prefix_chars
|
||||
raise ValueError(msg % tup)
|
||||
|
||||
@ -1473,9 +1528,11 @@ class _ActionsContainer(object):
|
||||
|
||||
def _handle_conflict_error(self, action, conflicting_actions):
|
||||
message = _('conflicting option string(s): %s')
|
||||
conflict_string = ', '.join([option_string
|
||||
for option_string, action
|
||||
in conflicting_actions])
|
||||
conflict_string = ', '.join([
|
||||
option_string
|
||||
for option_string, action
|
||||
in conflicting_actions
|
||||
])
|
||||
raise ArgumentError(action, message % conflict_string)
|
||||
|
||||
def _handle_conflict_resolve(self, action, conflicting_actions):
|
||||
@ -1565,19 +1622,21 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
- add_help -- Add a -h/-help option
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
prog=None,
|
||||
usage=None,
|
||||
description=None,
|
||||
epilog=None,
|
||||
version=None,
|
||||
parents=[],
|
||||
formatter_class=HelpFormatter,
|
||||
prefix_chars='-',
|
||||
fromfile_prefix_chars=None,
|
||||
argument_default=None,
|
||||
conflict_handler='error',
|
||||
add_help=True):
|
||||
def __init__(
|
||||
self,
|
||||
prog=None,
|
||||
usage=None,
|
||||
description=None,
|
||||
epilog=None,
|
||||
version=None,
|
||||
parents=[],
|
||||
formatter_class=HelpFormatter,
|
||||
prefix_chars='-',
|
||||
fromfile_prefix_chars=None,
|
||||
argument_default=None,
|
||||
conflict_handler='error',
|
||||
add_help=True,
|
||||
):
|
||||
|
||||
if version is not None:
|
||||
import warnings
|
||||
@ -1585,13 +1644,16 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
"""The "version" argument to ArgumentParser is deprecated. """
|
||||
"""Please use """
|
||||
""""add_argument(..., action='version', version="N", ...)" """
|
||||
"""instead""", DeprecationWarning)
|
||||
"""instead""", DeprecationWarning,
|
||||
)
|
||||
|
||||
superinit = super(ArgumentParser, self).__init__
|
||||
superinit(description=description,
|
||||
prefix_chars=prefix_chars,
|
||||
argument_default=argument_default,
|
||||
conflict_handler=conflict_handler)
|
||||
superinit(
|
||||
description=description,
|
||||
prefix_chars=prefix_chars,
|
||||
argument_default=argument_default,
|
||||
conflict_handler=conflict_handler,
|
||||
)
|
||||
|
||||
# default setting for prog
|
||||
if prog is None:
|
||||
@ -1625,13 +1687,15 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
self.add_argument(
|
||||
default_prefix + 'h', default_prefix * 2 + 'help',
|
||||
action='help', default=SUPPRESS,
|
||||
help=_('show this help message and exit'))
|
||||
help=_('show this help message and exit'),
|
||||
)
|
||||
if self.version:
|
||||
self.add_argument(
|
||||
default_prefix + 'v', default_prefix * 2 + 'version',
|
||||
action='version', default=SUPPRESS,
|
||||
version=self.version,
|
||||
help=_("show program's version number and exit"))
|
||||
help=_("show program's version number and exit"),
|
||||
)
|
||||
|
||||
# add parent arguments and defaults
|
||||
for parent in parents:
|
||||
@ -1700,14 +1764,18 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
return action
|
||||
|
||||
def _get_optional_actions(self):
|
||||
return [action
|
||||
for action in self._actions
|
||||
if action.option_strings]
|
||||
return [
|
||||
action
|
||||
for action in self._actions
|
||||
if action.option_strings
|
||||
]
|
||||
|
||||
def _get_positional_actions(self):
|
||||
return [action
|
||||
for action in self._actions
|
||||
if not action.option_strings]
|
||||
return [
|
||||
action
|
||||
for action in self._actions
|
||||
if not action.option_strings
|
||||
]
|
||||
|
||||
# =====================================
|
||||
# Command line argument parsing methods
|
||||
@ -1931,7 +1999,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
next_option_string_index = min([
|
||||
index
|
||||
for index in option_string_indices
|
||||
if index >= start_index])
|
||||
if index >= start_index
|
||||
])
|
||||
if start_index != next_option_string_index:
|
||||
positionals_end_index = consume_positionals(start_index)
|
||||
|
||||
@ -1980,9 +2049,11 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
|
||||
# if no actions were used, report the error
|
||||
else:
|
||||
names = [_get_action_name(action)
|
||||
for action in group._group_actions
|
||||
if action.help is not SUPPRESS]
|
||||
names = [
|
||||
_get_action_name(action)
|
||||
for action in group._group_actions
|
||||
if action.help is not SUPPRESS
|
||||
]
|
||||
msg = _('one of the arguments %s is required')
|
||||
self.error(msg % ' '.join(names))
|
||||
|
||||
@ -2046,8 +2117,10 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
result = []
|
||||
for i in range(len(actions), 0, -1):
|
||||
actions_slice = actions[:i]
|
||||
pattern = ''.join([self._get_nargs_pattern(action)
|
||||
for action in actions_slice])
|
||||
pattern = ''.join([
|
||||
self._get_nargs_pattern(action)
|
||||
for action in actions_slice
|
||||
])
|
||||
match = _re.match(pattern, arg_strings_pattern)
|
||||
if match is not None:
|
||||
result.extend([len(string) for string in match.groups()])
|
||||
@ -2088,7 +2161,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
# if multiple actions match, the option string was ambiguous
|
||||
if len(option_tuples) > 1:
|
||||
options = ', '.join(
|
||||
[option_string for action, option_string, explicit_arg in option_tuples])
|
||||
[option_string for action, option_string, explicit_arg in option_tuples],
|
||||
)
|
||||
tup = arg_string, options
|
||||
self.error(_('ambiguous option: %s could match %s') % tup)
|
||||
|
||||
@ -2218,8 +2292,10 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
|
||||
# when nargs='*' on a positional, if there were no command-line
|
||||
# args, use the default if it is anything other than None
|
||||
elif (not arg_strings and action.nargs == ZERO_OR_MORE and
|
||||
not action.option_strings):
|
||||
elif (
|
||||
not arg_strings and action.nargs == ZERO_OR_MORE and
|
||||
not action.option_strings
|
||||
):
|
||||
if action.default is not None:
|
||||
value = action.default
|
||||
else:
|
||||
@ -2287,16 +2363,20 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
# =======================
|
||||
def format_usage(self):
|
||||
formatter = self._get_formatter()
|
||||
formatter.add_usage(self.usage, self._actions,
|
||||
self._mutually_exclusive_groups)
|
||||
formatter.add_usage(
|
||||
self.usage, self._actions,
|
||||
self._mutually_exclusive_groups,
|
||||
)
|
||||
return formatter.format_help()
|
||||
|
||||
def format_help(self):
|
||||
formatter = self._get_formatter()
|
||||
|
||||
# usage
|
||||
formatter.add_usage(self.usage, self._actions,
|
||||
self._mutually_exclusive_groups)
|
||||
formatter.add_usage(
|
||||
self.usage, self._actions,
|
||||
self._mutually_exclusive_groups,
|
||||
)
|
||||
|
||||
# description
|
||||
formatter.add_text(self.description)
|
||||
@ -2319,7 +2399,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
warnings.warn(
|
||||
'The format_version method is deprecated -- the "version" '
|
||||
'argument to ArgumentParser is no longer supported.',
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
formatter = self._get_formatter()
|
||||
formatter.add_text(self.version)
|
||||
return formatter.format_help()
|
||||
@ -2345,7 +2426,8 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
|
||||
warnings.warn(
|
||||
'The print_version method is deprecated -- the "version" '
|
||||
'argument to ArgumentParser is no longer supported.',
|
||||
DeprecationWarning)
|
||||
DeprecationWarning,
|
||||
)
|
||||
self._print_message(self.format_version(), file)
|
||||
|
||||
def _print_message(self, message, file=None):
|
||||
|
@ -1,27 +1,28 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
|
||||
from codecs import open
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from codecs import open
|
||||
from collections import namedtuple
|
||||
from tempfile import NamedTemporaryFile
|
||||
from time import time
|
||||
|
||||
from autojump_utils import create_dir
|
||||
from autojump_utils import is_osx
|
||||
from autojump_utils import is_python3
|
||||
from autojump_utils import move_file
|
||||
from autojump_utils import unico
|
||||
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
ifilter = filter
|
||||
imap = map
|
||||
else:
|
||||
from itertools import ifilter
|
||||
from itertools import imap
|
||||
|
||||
from autojump_utils import create_dir
|
||||
from autojump_utils import unico
|
||||
from autojump_utils import is_osx
|
||||
from autojump_utils import is_python3
|
||||
from autojump_utils import move_file
|
||||
from itertools import ifilter # noqa
|
||||
from itertools import imap # noqa
|
||||
|
||||
|
||||
BACKUP_THRESHOLD = 24 * 60 * 60
|
||||
@ -54,7 +55,8 @@ def load(config):
|
||||
os.path.expanduser('~'),
|
||||
'.local',
|
||||
'share',
|
||||
'autojump')
|
||||
'autojump',
|
||||
)
|
||||
|
||||
if is_osx() and os.path.exists(xdg_aj_home):
|
||||
migrate_osx_xdg_data(config)
|
||||
@ -74,11 +76,14 @@ def load(config):
|
||||
with open(
|
||||
config['data_path'],
|
||||
'r', encoding='utf-8',
|
||||
errors='replace') as f:
|
||||
errors='replace',
|
||||
) as f:
|
||||
return dict(
|
||||
imap(
|
||||
tupleize,
|
||||
ifilter(correct_length, imap(parse, f))))
|
||||
ifilter(correct_length, imap(parse, f)),
|
||||
),
|
||||
)
|
||||
except (IOError, EOFError):
|
||||
return load_backup(config)
|
||||
|
||||
@ -95,7 +100,7 @@ def migrate_osx_xdg_data(config):
|
||||
Older versions incorrectly used Linux XDG_DATA_HOME paths on OS X. This
|
||||
migrates autojump files from ~/.local/share/autojump to ~/Library/autojump
|
||||
"""
|
||||
assert is_osx(), "This function should only be run on OS X."
|
||||
assert is_osx(), 'This function should only be run on OS X.'
|
||||
|
||||
xdg_data_home = os.path.join(os.path.expanduser('~'), '.local', 'share')
|
||||
xdg_aj_home = os.path.join(xdg_data_home, 'autojump')
|
||||
@ -115,22 +120,23 @@ def migrate_osx_xdg_data(config):
|
||||
|
||||
def save(config, data):
|
||||
"""Save data and create backup, creating a new data file if necessary."""
|
||||
create_dir(os.path.dirname(config['data_path']))
|
||||
data_dir = os.path.dirname(config['data_path'])
|
||||
create_dir(data_dir)
|
||||
|
||||
# atomically save by writing to temporary file and moving to destination
|
||||
try:
|
||||
temp = NamedTemporaryFile(delete=False)
|
||||
temp = NamedTemporaryFile(delete=False, dir=data_dir)
|
||||
# Windows cannot reuse the same open file name
|
||||
temp.close()
|
||||
|
||||
with open(temp.name, 'w', encoding='utf-8', errors='replace') as f:
|
||||
for path, weight in data.items():
|
||||
f.write(unico("%s\t%s\n" % (weight, path)))
|
||||
f.write(unico('%s\t%s\n' % (weight, path)))
|
||||
|
||||
f.flush()
|
||||
os.fsync(f)
|
||||
except IOError as ex:
|
||||
print("Error saving autojump data (disk full?)" % ex, file=sys.stderr)
|
||||
print('Error saving autojump data (disk full?)' % ex, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# move temp_file -> autojump.txt
|
||||
|
129
bin/autojump_match.py
Normal file
129
bin/autojump_match.py
Normal file
@ -0,0 +1,129 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import re
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
from autojump_utils import is_python3
|
||||
from autojump_utils import last
|
||||
|
||||
|
||||
if is_python3(): # pragma: no cover
|
||||
ifilter = filter
|
||||
imap = map
|
||||
os.getcwdu = os.getcwd
|
||||
else:
|
||||
from itertools import ifilter
|
||||
from itertools import imap
|
||||
|
||||
|
||||
def match_anywhere(needles, haystack, ignore_case=False):
|
||||
"""
|
||||
Matches needles anywhere in the path as long as they're in the same (but
|
||||
not necessary consecutive) order.
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'baz']
|
||||
regex needle = r'.*foo.*baz.*'
|
||||
haystack = [
|
||||
(path='/foo/bar/baz', weight=10),
|
||||
(path='/baz/foo/bar', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
]
|
||||
|
||||
result = [
|
||||
(path='/moo/foo/baz', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
]
|
||||
"""
|
||||
regex_needle = '.*' + '.*'.join(imap(re.escape, needles)) + '.*'
|
||||
regex_flags = re.IGNORECASE | re.UNICODE if ignore_case else re.UNICODE
|
||||
found = lambda haystack: re.search(
|
||||
regex_needle,
|
||||
haystack.path,
|
||||
flags=regex_flags,
|
||||
)
|
||||
return ifilter(found, haystack)
|
||||
|
||||
|
||||
def match_consecutive(needles, haystack, ignore_case=False):
|
||||
"""
|
||||
Matches consecutive needles at the end of a path.
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'baz']
|
||||
haystack = [
|
||||
(path='/foo/bar/baz', weight=10),
|
||||
(path='/foo/baz/moo', weight=10),
|
||||
(path='/moo/foo/baz', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
]
|
||||
|
||||
# We can't actually use re.compile because of re.UNICODE
|
||||
regex_needle = re.compile(r'''
|
||||
foo # needle #1
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
/ # os.sep
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
baz # needle #2
|
||||
[^/]* # all characters except os.sep zero or more times
|
||||
$ # end of string
|
||||
''')
|
||||
|
||||
result = [
|
||||
(path='/moo/foo/baz', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
]
|
||||
"""
|
||||
regex_no_sep = '[^' + os.sep + ']*'
|
||||
regex_no_sep_end = regex_no_sep + '$'
|
||||
regex_one_sep = regex_no_sep + os.sep + regex_no_sep
|
||||
regex_needle = regex_one_sep.join(imap(re.escape, needles)) + regex_no_sep_end
|
||||
regex_flags = re.IGNORECASE | re.UNICODE if ignore_case else re.UNICODE
|
||||
found = lambda entry: re.search(
|
||||
regex_needle,
|
||||
entry.path,
|
||||
flags=regex_flags,
|
||||
)
|
||||
return ifilter(found, haystack)
|
||||
|
||||
|
||||
def match_fuzzy(needles, haystack, ignore_case=False, threshold=0.6):
|
||||
"""
|
||||
Performs an approximate match with the last needle against the end of
|
||||
every path past an acceptable threshold.
|
||||
|
||||
For example:
|
||||
needles = ['foo', 'bar']
|
||||
haystack = [
|
||||
(path='/foo/bar/baz', weight=11),
|
||||
(path='/foo/baz/moo', weight=10),
|
||||
(path='/moo/foo/baz', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
(path='/foo/bar', weight=10),
|
||||
]
|
||||
|
||||
result = [
|
||||
(path='/foo/bar/baz', weight=11),
|
||||
(path='/moo/foo/baz', weight=10),
|
||||
(path='/foo/baz', weight=10),
|
||||
(path='/foo/bar', weight=10),
|
||||
]
|
||||
|
||||
This is a weak heuristic and used as a last resort to find matches.
|
||||
"""
|
||||
end_dir = lambda path: last(os.path.split(path))
|
||||
if ignore_case:
|
||||
needle = last(needles).lower()
|
||||
match_percent = lambda entry: SequenceMatcher(
|
||||
a=needle,
|
||||
b=end_dir(entry.path.lower()),
|
||||
).ratio()
|
||||
else:
|
||||
needle = last(needles)
|
||||
match_percent = lambda entry: SequenceMatcher(
|
||||
a=needle,
|
||||
b=end_dir(entry.path),
|
||||
).ratio()
|
||||
meets_threshold = lambda entry: match_percent(entry) >= threshold
|
||||
return ifilter(meets_threshold, haystack)
|
@ -1,16 +1,15 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import errno
|
||||
from itertools import islice
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import unicodedata
|
||||
from itertools import islice
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
imap = map
|
||||
@ -57,7 +56,8 @@ def get_tab_entry_info(entry, separator):
|
||||
match_index = re.search(separator + r'([0-9]{1})', entry)
|
||||
match_path = re.search(
|
||||
separator + r'[0-9]{1}' + separator + r'(.*)',
|
||||
entry)
|
||||
entry,
|
||||
)
|
||||
|
||||
if match_needle:
|
||||
needle = match_needle.group(1)
|
||||
@ -75,7 +75,7 @@ def get_pwd():
|
||||
try:
|
||||
return os.getcwdu()
|
||||
except OSError:
|
||||
print("Current directory no longer exists.", file=sys.stderr)
|
||||
print('Current directory no longer exists.', file=sys.stderr)
|
||||
raise
|
||||
|
||||
|
||||
@ -141,7 +141,7 @@ def move_file(src, dst):
|
||||
|
||||
|
||||
def print_entry(entry):
|
||||
print_local("%.1f:\t%s" % (entry.weight, entry.path))
|
||||
print_local('%.1f:\t%s' % (entry.weight, entry.path))
|
||||
|
||||
|
||||
def print_local(string):
|
||||
@ -164,7 +164,9 @@ def print_tab_menu(needle, tab_entries, separator):
|
||||
separator,
|
||||
i + 1,
|
||||
separator,
|
||||
entry.path))
|
||||
entry.path,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def sanitize(directories):
|
||||
|
16
bin/j.bat
16
bin/j.bat
@ -3,19 +3,19 @@ setlocal EnableDelayedExpansion
|
||||
|
||||
echo %*|>nul findstr /rx \-.*
|
||||
if ERRORLEVEL 1 (
|
||||
for /f %%i in ('python %~dp0\autojump %*') do set new_path=%%i
|
||||
for /f "delims=" %%i in ('python "%~dp0\autojump" %*') do set new_path=%%i
|
||||
if exist !new_path!\nul (
|
||||
echo !new_path!
|
||||
pushd !new_path!
|
||||
REM endlocal is necessary so that we can change directory for outside of this script
|
||||
REM but will automatically popd. We mush pushd twice to work around this.
|
||||
pushd !new_path!
|
||||
endlocal
|
||||
popd
|
||||
pushd !new_path!
|
||||
REM endlocal is necessary so that we can change directory for outside of this script
|
||||
REM but will automatically popd. We mush pushd twice to work around this.
|
||||
pushd !new_path!
|
||||
endlocal
|
||||
popd
|
||||
) else (
|
||||
echo autojump: directory %* not found
|
||||
echo try `autojump --help` for more information
|
||||
)
|
||||
) else (
|
||||
python %~dp0\autojump %*
|
||||
python "%~dp0\autojump" %*
|
||||
)
|
@ -2,7 +2,7 @@
|
||||
|
||||
echo %*|>nul findstr /rx \-.*
|
||||
if ERRORLEVEL 1 (
|
||||
%~dp0\j.bat %cd% %*
|
||||
"%~dp0\j.bat" "%cd%" %*
|
||||
) else (
|
||||
python %~dp0\autojump %*
|
||||
python "%~dp0\autojump" %*
|
||||
)
|
@ -2,7 +2,7 @@
|
||||
|
||||
echo %*|>nul findstr /rx \-.*
|
||||
if ERRORLEVEL 1 (
|
||||
%~dp0\jc.bat %cd% %*
|
||||
"%~dp0\jc.bat" "%cd%" %*
|
||||
) else (
|
||||
python %~dp0\autojump %*
|
||||
python "%~dp0\autojump" %*
|
||||
)
|
@ -3,13 +3,13 @@ setlocal EnableDelayedExpansion
|
||||
|
||||
echo %*|>nul findstr /rx \-.*
|
||||
if ERRORLEVEL 1 (
|
||||
for /f %%i in ('python %~dp0\autojump %*') do set new_path=%%i
|
||||
for /f "delims=" %%i in ('python "%~dp0\autojump" %*') do set new_path=%%i
|
||||
if exist !new_path!\nul (
|
||||
start !new_path!
|
||||
start "" "explorer" !new_path!
|
||||
) else (
|
||||
echo autojump: directory %* not found
|
||||
echo try `autojump --help` for more information
|
||||
)
|
||||
) else (
|
||||
python %~dp0\autojump %*
|
||||
python "%~dp0\autojump" %*
|
||||
)
|
@ -1,4 +0,0 @@
|
||||
flake8
|
||||
mock
|
||||
pytest
|
||||
tox
|
@ -1,7 +1,10 @@
|
||||
.TH autojump 1 "10 April 2012" "release-v22"
|
||||
.\" Automatically generated by Pandoc 1.16.0.2
|
||||
.\"
|
||||
.TH "autojump" "1" "2018\-09\-09" "release\-v22.5.3" ""
|
||||
.hy
|
||||
.SS NAME
|
||||
.PP
|
||||
autojump - a faster way to navigate your filesystem
|
||||
autojump \- a faster way to navigate your filesystem
|
||||
.SS DESCRIPTION
|
||||
.PP
|
||||
autojump is a faster way to navigate your filesystem.
|
||||
@ -29,7 +32,7 @@ Jump To A Child Directory:
|
||||
.RS 2
|
||||
.PP
|
||||
Sometimes it\[aq]s convenient to jump to a child directory
|
||||
(sub-directory of current directory) rather than typing out the full
|
||||
(sub\-directory of current directory) rather than typing out the full
|
||||
name.
|
||||
.IP
|
||||
.nf
|
||||
@ -43,8 +46,8 @@ Open File Manager To Directories (instead of jumping):
|
||||
.RS 2
|
||||
.PP
|
||||
Instead of jumping to a directory, you can open a file explorer window
|
||||
(Mac Finder, Windows Explorer, GNOME Nautilus, etc.)
|
||||
to the directory instead.
|
||||
(Mac Finder, Windows Explorer, GNOME Nautilus, etc.) to the directory
|
||||
instead.
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
@ -85,12 +88,12 @@ For more options refer to help:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
autojump\ --help
|
||||
autojump\ \-\-help
|
||||
\f[]
|
||||
.fi
|
||||
.SS KNOWN ISSUES
|
||||
.IP \[bu] 2
|
||||
autojump does not support directories that begin with \f[C]-\f[].
|
||||
autojump does not support directories that begin with \f[C]\-\f[].
|
||||
.IP \[bu] 2
|
||||
For bash users, autojump keeps track of directories by modifying
|
||||
\f[C]$PROMPT_COMMAND\f[].
|
||||
@ -99,7 +102,7 @@ Do not overwrite \f[C]$PROMPT_COMMAND\f[]:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
export\ PROMPT_COMMAND="history\ -a"
|
||||
export\ PROMPT_COMMAND="history\ \-a"
|
||||
\f[]
|
||||
.fi
|
||||
.PP
|
||||
@ -107,7 +110,7 @@ Instead append to the end of the existing $PROMPT_COMMAND:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
export\ PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND\ ;}\ history\ -a"
|
||||
export\ PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND\ ;}\ history\ \-a"
|
||||
\f[]
|
||||
.fi
|
||||
.RE
|
||||
@ -117,7 +120,7 @@ For any questions or issues please visit:
|
||||
.IP
|
||||
.nf
|
||||
\f[C]
|
||||
https://github.com/joelthelion/autojump/issues
|
||||
https://github.com/wting/autojump/issues
|
||||
\f[]
|
||||
.fi
|
||||
.SS AUTHORS
|
||||
@ -127,7 +130,7 @@ maintained by William Ting.
|
||||
More contributors can be found in \f[C]AUTHORS\f[].
|
||||
.SS COPYRIGHT
|
||||
.PP
|
||||
Copyright © 2012 Free Software Foundation, Inc.
|
||||
Copyright © 2016 Free Software Foundation, Inc.
|
||||
License GPLv3+: GNU GPL version 3 or later
|
||||
<http://gnu.org/licenses/gpl.html>.
|
||||
This is free software: you are free to change and redistribute it.
|
||||
|
@ -17,7 +17,7 @@ REPORTING BUGS
|
||||
|
||||
For any questions or issues please visit:
|
||||
|
||||
https://github.com/joelthelion/autojump/issues
|
||||
https://github.com/wting/autojump/issues
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
@ -28,7 +28,7 @@ William Ting. More contributors can be found in `AUTHORS`.
|
||||
COPYRIGHT
|
||||
---------
|
||||
|
||||
Copyright © 2012 Free Software Foundation, Inc. License GPLv3+: GNU GPL version
|
||||
Copyright © 2016 Free Software Foundation, Inc. License GPLv3+: GNU GPL version
|
||||
3 or later <http://gnu.org/licenses/gpl.html>. This is free software: you are
|
||||
free to change and redistribute it. There is NO WARRANTY, to the extent
|
||||
permitted by law.
|
||||
|
@ -2,28 +2,59 @@
|
||||
|
||||
### REQUIREMENTS
|
||||
|
||||
- Python v2.6+
|
||||
- Supported shells:
|
||||
- bash v4.0+
|
||||
- zsh
|
||||
- fish
|
||||
- tcsh (experimental)
|
||||
- clink (Windows, experimental)
|
||||
- Python v2.6+ or Python v3.3+
|
||||
- Supported shells
|
||||
- bash - first class support
|
||||
- zsh - first class support
|
||||
- fish - community supported
|
||||
- tcsh - community supported
|
||||
- clink - community supported
|
||||
- Supported platforms
|
||||
- Linux - first class support
|
||||
- OS X - first class support
|
||||
- Windows - community supported
|
||||
- BSD - community supported
|
||||
- Supported installation methods
|
||||
- source code - first class support
|
||||
- Debian and derivatives - first class support
|
||||
- ArchLinux / Gentoo / openSUSE / RedHat and derivatives - community supported
|
||||
- Homebrew / MacPorts - community supported
|
||||
|
||||
Due to limited time and resources, only "first class support" items will be
|
||||
maintained by the primary committers. All "community supported" items will be
|
||||
updated based on pull requests submitted by the general public.
|
||||
|
||||
Please continue opening issues and providing feedback for community supported
|
||||
items since consolidating information helps other users troubleshoot and submit
|
||||
enhancements and fixes.
|
||||
|
||||
### MANUAL
|
||||
|
||||
Grab a copy of autojump:
|
||||
|
||||
git clone git://github.com/wting/autojump.git
|
||||
|
||||
Run the installation script and follow on screen instructions.
|
||||
|
||||
cd autojump
|
||||
./install.py or ./uninstall.py
|
||||
|
||||
### AUTOMATIC
|
||||
|
||||
#### Linux
|
||||
|
||||
autojump is included in the following distro repositories, please use relevant
|
||||
package management utilities to install (e.g. yum, apt-get, etc):
|
||||
package management utilities to install (e.g. apt-get, yum, pacman, etc):
|
||||
|
||||
- Debian testing/unstable, Ubuntu, Linux Mint
|
||||
- Debian, Ubuntu, Linux Mint
|
||||
|
||||
All Debian-derived distros require manual activation for policy reasons,
|
||||
please see `/usr/share/doc/autojump/README.Debian`.
|
||||
|
||||
- RedHat, Fedora, CentOS (install `autojump-zsh` for zsh, `autojump-fish` for
|
||||
fish, etc.)
|
||||
- RedHat, Fedora, CentOS
|
||||
|
||||
Install `autojump-zsh` for zsh, `autojump-fish` for fish, etc.
|
||||
|
||||
- ArchLinux
|
||||
- Gentoo
|
||||
- Frugalware
|
||||
@ -35,22 +66,11 @@ Homebrew is the recommended installation method for Mac OS X:
|
||||
|
||||
brew install autojump
|
||||
|
||||
MacPorts also available:
|
||||
MacPorts is also available:
|
||||
|
||||
port install autojump
|
||||
|
||||
## Windows
|
||||
|
||||
Windows support is enabled by [clink](https://code.google.com/p/clink/) which
|
||||
Windows support is enabled by [clink](https://mridgers.github.io/clink/) which
|
||||
should be installed prior to installing autojump.
|
||||
|
||||
### MANUAL
|
||||
|
||||
Grab a copy of autojump:
|
||||
|
||||
git clone git://github.com/joelthelion/autojump.git
|
||||
|
||||
Run the installation script and follow on screen instructions.
|
||||
|
||||
cd autojump
|
||||
./install.py or ./uninstall.py
|
||||
|
@ -1,3 +1,3 @@
|
||||
% autojump(1) release-v22
|
||||
% autojump(1) release-v22.5.3
|
||||
%
|
||||
% 10 April 2012
|
||||
% 2018-09-09
|
||||
|
100
install.py
100
install.py
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import print_function
|
||||
|
||||
@ -8,13 +8,13 @@ import shutil
|
||||
import sys
|
||||
|
||||
sys.path.append('bin')
|
||||
from autojump_argparse import ArgumentParser
|
||||
from autojump_argparse import ArgumentParser # noqa
|
||||
|
||||
SUPPORTED_SHELLS = ('bash', 'zsh', 'fish', 'tcsh')
|
||||
|
||||
|
||||
def cp(src, dest, dryrun=False):
|
||||
print("copying file: %s -> %s" % (src, dest))
|
||||
print('copying file: %s -> %s' % (src, dest))
|
||||
if not dryrun:
|
||||
shutil.copy(src, dest)
|
||||
|
||||
@ -24,18 +24,18 @@ def get_shell():
|
||||
|
||||
|
||||
def mkdir(path, dryrun=False):
|
||||
print("creating directory:", path)
|
||||
print('creating directory:', path)
|
||||
if not dryrun and not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def modify_autojump_sh(etc_dir, share_dir, dryrun=False):
|
||||
"""Append custom installation path to autojump.sh"""
|
||||
custom_install = "\
|
||||
custom_install = '\
|
||||
\n# check custom install \
|
||||
\nif [ -s %s/autojump.${shell} ]; then \
|
||||
\n source %s/autojump.${shell} \
|
||||
\nfi\n" % (share_dir, share_dir)
|
||||
\nfi\n' % (share_dir, share_dir)
|
||||
|
||||
with open(os.path.join(etc_dir, 'autojump.sh'), 'a') as f:
|
||||
f.write(custom_install)
|
||||
@ -44,8 +44,9 @@ def modify_autojump_sh(etc_dir, share_dir, dryrun=False):
|
||||
def modify_autojump_lua(clink_dir, bin_dir, dryrun=False):
|
||||
"""Prepend custom AUTOJUMP_BIN_DIR definition to autojump.lua"""
|
||||
custom_install = "local AUTOJUMP_BIN_DIR = \"%s\"\n" % bin_dir.replace(
|
||||
"\\",
|
||||
"\\\\")
|
||||
'\\',
|
||||
'\\\\',
|
||||
)
|
||||
clink_file = os.path.join(clink_dir, 'autojump.lua')
|
||||
with open(clink_file, 'r') as f:
|
||||
original = f.read()
|
||||
@ -57,11 +58,13 @@ def parse_arguments(): # noqa
|
||||
if platform.system() == 'Windows':
|
||||
default_user_destdir = os.path.join(
|
||||
os.getenv('LOCALAPPDATA', ''),
|
||||
'autojump')
|
||||
'autojump',
|
||||
)
|
||||
else:
|
||||
default_user_destdir = os.path.join(
|
||||
os.path.expanduser("~"),
|
||||
'.autojump')
|
||||
os.path.expanduser('~'),
|
||||
'.autojump',
|
||||
)
|
||||
default_user_prefix = ''
|
||||
default_user_zshshare = 'functions'
|
||||
default_system_destdir = '/'
|
||||
@ -71,49 +74,63 @@ def parse_arguments(): # noqa
|
||||
|
||||
parser = ArgumentParser(
|
||||
description='Installs autojump globally for root users, otherwise \
|
||||
installs in current user\'s home directory.')
|
||||
installs in current user\'s home directory.'
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n', '--dryrun', action="store_true", default=False,
|
||||
help='simulate installation')
|
||||
'-n', '--dryrun', action='store_true', default=False,
|
||||
help='simulate installation',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-f', '--force', action="store_true", default=False,
|
||||
help='skip root user, shell type, Python version checks')
|
||||
'-f', '--force', action='store_true', default=False,
|
||||
help='skip root user, shell type, Python version checks',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--destdir', metavar='DIR', default=default_user_destdir,
|
||||
help='set destination to DIR')
|
||||
help='set destination to DIR',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--prefix', metavar='DIR', default=default_user_prefix,
|
||||
help='set prefix to DIR')
|
||||
help='set prefix to DIR',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-z', '--zshshare', metavar='DIR', default=default_user_zshshare,
|
||||
help='set zsh share destination to DIR')
|
||||
help='set zsh share destination to DIR',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--clinkdir', metavar='DIR', default=default_clink_dir,
|
||||
help='set clink directory location to DIR (Windows only)')
|
||||
help='set clink directory location to DIR (Windows only)',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-s', '--system', action="store_true", default=False,
|
||||
help='install system wide for all users')
|
||||
'-s', '--system', action='store_true', default=False,
|
||||
help='install system wide for all users',
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.force:
|
||||
if sys.version_info[0] == 2 and sys.version_info[1] < 6:
|
||||
print("Python v2.6+ or v3.0+ required.", file=sys.stderr)
|
||||
print('Python v2.6+ or v3.0+ required.', file=sys.stderr)
|
||||
sys.exit(1)
|
||||
if args.system:
|
||||
if platform.system() == 'Windows':
|
||||
print("System-wide installation is not supported on Windows.",
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'System-wide installation is not supported on Windows.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
elif os.geteuid() != 0:
|
||||
print("Please rerun as root for system-wide installation.",
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'Please rerun as root for system-wide installation.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if platform.system() != 'Windows' \
|
||||
and get_shell() not in SUPPORTED_SHELLS:
|
||||
print("Unsupported shell: %s" % os.getenv('SHELL'),
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'Unsupported shell: %s' % os.getenv('SHELL'),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if args.destdir != default_user_destdir \
|
||||
@ -125,8 +142,10 @@ def parse_arguments(): # noqa
|
||||
|
||||
if args.system:
|
||||
if args.custom_install:
|
||||
print("Custom paths incompatible with --system option.",
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'Custom paths incompatible with --system option.',
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
args.destdir = default_system_destdir
|
||||
@ -138,34 +157,34 @@ def parse_arguments(): # noqa
|
||||
|
||||
def show_post_installation_message(etc_dir, share_dir, bin_dir):
|
||||
if platform.system() == 'Windows':
|
||||
print("\nPlease manually add %s to your user path" % bin_dir)
|
||||
print('\nPlease manually add %s to your user path' % bin_dir)
|
||||
else:
|
||||
if get_shell() == 'fish':
|
||||
aj_shell = '%s/autojump.fish' % share_dir
|
||||
source_msg = "if test -f %s; . %s; end" % (aj_shell, aj_shell)
|
||||
source_msg = 'if test -f %s; . %s; end' % (aj_shell, aj_shell)
|
||||
rcfile = '~/.config/fish/config.fish'
|
||||
else:
|
||||
aj_shell = '%s/autojump.sh' % etc_dir
|
||||
source_msg = "[[ -s %s ]] && source %s" % (aj_shell, aj_shell)
|
||||
source_msg = '[[ -s %s ]] && source %s' % (aj_shell, aj_shell)
|
||||
|
||||
if platform.system() == 'Darwin' and get_shell() == 'bash':
|
||||
rcfile = '~/.profile'
|
||||
else:
|
||||
rcfile = '~/.%src' % get_shell()
|
||||
|
||||
print("\nPlease manually add the following line(s) to %s:" % rcfile)
|
||||
print('\nPlease manually add the following line(s) to %s:' % rcfile)
|
||||
print('\n\t' + source_msg)
|
||||
if get_shell() == 'zsh':
|
||||
print("\n\tautoload -U compinit && compinit -u")
|
||||
print('\n\tautoload -U compinit && compinit -u')
|
||||
|
||||
print("\nPlease restart terminal(s) before running autojump.\n")
|
||||
print('\nPlease restart terminal(s) before running autojump.\n')
|
||||
|
||||
|
||||
def main(args):
|
||||
if args.dryrun:
|
||||
print("Installing autojump to %s (DRYRUN)..." % args.destdir)
|
||||
print('Installing autojump to %s (DRYRUN)...' % args.destdir)
|
||||
else:
|
||||
print("Installing autojump to %s ..." % args.destdir)
|
||||
print('Installing autojump to %s ...' % args.destdir)
|
||||
|
||||
bin_dir = os.path.join(args.destdir, args.prefix, 'bin')
|
||||
etc_dir = os.path.join(args.destdir, 'etc', 'profile.d')
|
||||
@ -181,6 +200,7 @@ def main(args):
|
||||
cp('./bin/autojump', bin_dir, args.dryrun)
|
||||
cp('./bin/autojump_argparse.py', bin_dir, args.dryrun)
|
||||
cp('./bin/autojump_data.py', bin_dir, args.dryrun)
|
||||
cp('./bin/autojump_match.py', bin_dir, args.dryrun)
|
||||
cp('./bin/autojump_utils.py', bin_dir, args.dryrun)
|
||||
cp('./bin/icon.png', share_dir, args.dryrun)
|
||||
cp('./docs/autojump.1', doc_dir, args.dryrun)
|
||||
@ -212,5 +232,5 @@ def main(args):
|
||||
show_post_installation_message(etc_dir, share_dir, bin_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(parse_arguments()))
|
||||
|
131
tests/unit/autojump_match_test.py
Normal file
131
tests/unit/autojump_match_test.py
Normal file
@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.append(os.path.join(os.getcwd(), 'bin')) # noqa
|
||||
from autojump_data import Entry
|
||||
from autojump_match import match_anywhere
|
||||
from autojump_match import match_consecutive
|
||||
|
||||
|
||||
class TestMatchAnywhere(object):
|
||||
|
||||
entry1 = Entry('/foo/bar/baz', 10)
|
||||
entry2 = Entry('/baz/foo/bar', 10)
|
||||
entry3 = Entry('/foo/baz', 10)
|
||||
entry4 = Entry('/中/zhong/国/guo', 10)
|
||||
entry5 = Entry('/is\'t/this/a/b*tchin/edge/case?', 10)
|
||||
win_entry1 = Entry('C:\\foo\\bar\\baz', 10)
|
||||
win_entry2 = Entry('D:\Program Files (x86)\GIMP', 10)
|
||||
win_entry3 = Entry('C:\Windows\System32', 10)
|
||||
|
||||
@pytest.fixture
|
||||
def haystack(self):
|
||||
return [
|
||||
self.entry1,
|
||||
self.entry2,
|
||||
self.entry3,
|
||||
self.entry4,
|
||||
self.entry5,
|
||||
]
|
||||
|
||||
@pytest.fixture
|
||||
def windows_haystack(self):
|
||||
return [self.win_entry1, self.win_entry2, self.win_entry3]
|
||||
|
||||
def test_single_needle(self, haystack):
|
||||
assert list(match_anywhere(['bar'], haystack)) == [self.entry1, self.entry2]
|
||||
|
||||
def test_consecutive(self, haystack):
|
||||
assert list(match_anywhere(['foo', 'bar'], haystack)) \
|
||||
== [self.entry1, self.entry2]
|
||||
assert list(match_anywhere(['bar', 'foo'], haystack)) == []
|
||||
|
||||
def test_skip(self, haystack):
|
||||
assert list(match_anywhere(['baz', 'bar'], haystack)) == [self.entry2]
|
||||
assert list(match_anywhere(['中', '国'], haystack)) == [self.entry4]
|
||||
|
||||
def test_ignore_case(self, haystack):
|
||||
assert list(match_anywhere(['bAz', 'bAR'], haystack, ignore_case=True)) \
|
||||
== [self.entry2]
|
||||
|
||||
def test_backslashes_for_windows_paths(self, windows_haystack):
|
||||
# https://github.com/wting/autojump/issues/281
|
||||
assert list(match_anywhere(['foo', 'baz'], windows_haystack)) \
|
||||
== [self.win_entry1]
|
||||
assert list(match_anywhere(['program', 'gimp'], windows_haystack, True)) \
|
||||
== [self.win_entry2]
|
||||
assert list(match_anywhere(['win', '32'], windows_haystack, True)) \
|
||||
== [self.win_entry3]
|
||||
|
||||
def test_wildcard_in_needle(self, haystack):
|
||||
# https://github.com/wting/autojump/issues/402
|
||||
assert list(match_anywhere(['*', 'this'], haystack)) == []
|
||||
assert list(match_anywhere(['this', '*'], haystack)) == [self.entry5]
|
||||
|
||||
|
||||
class TestMatchConsecutive(object):
|
||||
|
||||
entry1 = Entry('/foo/bar/baz', 10)
|
||||
entry2 = Entry('/baz/foo/bar', 10)
|
||||
entry3 = Entry('/foo/baz', 10)
|
||||
entry4 = Entry('/中/zhong/国/guo', 10)
|
||||
entry5 = Entry('/日/本', 10)
|
||||
entry6 = Entry('/is\'t/this/a/b*tchin/edge/case?', 10)
|
||||
win_entry1 = Entry('C:\Foo\Bar\Baz', 10)
|
||||
win_entry2 = Entry('D:\Program Files (x86)\GIMP', 10)
|
||||
win_entry3 = Entry('C:\Windows\System32', 10)
|
||||
|
||||
@pytest.fixture
|
||||
def haystack(self):
|
||||
return [
|
||||
self.entry1,
|
||||
self.entry2,
|
||||
self.entry3,
|
||||
self.entry4,
|
||||
self.entry5,
|
||||
]
|
||||
|
||||
@pytest.fixture
|
||||
def windows_haystack(self):
|
||||
return [self.win_entry1, self.win_entry2, self.win_entry3]
|
||||
|
||||
def test_single_needle(self, haystack):
|
||||
assert list(match_consecutive(['baz'], haystack)) == [self.entry1, self.entry3]
|
||||
assert list(match_consecutive(['本'], haystack)) == [self.entry5]
|
||||
|
||||
def test_consecutive(self, haystack):
|
||||
assert list(match_consecutive(['bar', 'baz'], haystack)) == [self.entry1]
|
||||
assert list(match_consecutive(['foo', 'bar'], haystack)) == [self.entry2]
|
||||
assert list(match_consecutive(['国', 'guo'], haystack)) == [self.entry4]
|
||||
assert list(match_consecutive(['bar', 'foo'], haystack)) == []
|
||||
|
||||
def test_ignore_case(self, haystack):
|
||||
assert list(match_consecutive(['FoO', 'bAR'], haystack, ignore_case=True)) \
|
||||
== [self.entry2]
|
||||
|
||||
def test_windows_ignore_case(self, windows_haystack):
|
||||
assert list(match_consecutive(['gimp'], windows_haystack, True)) == [self.win_entry2]
|
||||
|
||||
@pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/418')
|
||||
def test_backslashes_for_windows_paths(self, windows_haystack):
|
||||
assert list(match_consecutive(['program', 'gimp'], windows_haystack, True)) \
|
||||
== [self.win_entry2]
|
||||
|
||||
@pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/418')
|
||||
def test_foo_bar_baz(self, windows_haystack):
|
||||
assert list(match_consecutive(['bar', 'baz'], windows_haystack, ignore_case=True)) \
|
||||
== [self.win_entry1]
|
||||
|
||||
@pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/402')
|
||||
def test_thing(self, windows_haystack):
|
||||
assert list(match_consecutive(['win', '32'], windows_haystack, True)) \
|
||||
== [self.win_entry3]
|
||||
|
||||
@pytest.mark.xfail(reason='https://github.com/wting/autojump/issues/402')
|
||||
def test_wildcard_in_needle(self, haystack):
|
||||
assert list(match_consecutive(['*', 'this'], haystack)) == []
|
||||
assert list(match_consecutive(['*', 'edge', 'case'], haystack)) == [self.entry6]
|
@ -6,7 +6,7 @@ import sys
|
||||
import mock
|
||||
import pytest
|
||||
|
||||
sys.path.append(os.path.join(os.getcwd(), 'bin'))
|
||||
sys.path.append(os.path.join(os.getcwd(), 'bin')) # noqa
|
||||
import autojump_utils
|
||||
from autojump_utils import encode_local
|
||||
from autojump_utils import first
|
||||
@ -37,27 +37,27 @@ def u(string):
|
||||
|
||||
|
||||
# strings
|
||||
@pytest.mark.skipif(is_python3(), reason="Unicode sucks.")
|
||||
@pytest.mark.skipif(is_python3(), reason='Unicode sucks.')
|
||||
@mock.patch.object(sys, 'getfilesystemencoding', return_value='ascii')
|
||||
def test_encode_local_ascii(_):
|
||||
assert encode_local(u('foo')) == b'foo'
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_python3(), reason="Unicode sucks.")
|
||||
@pytest.mark.xfail(reason="disabled due to pytest bug: https://bitbucket.org/hpk42/pytest/issue/534/pytest-fails-to-catch-unicodedecodeerrors") # noqa
|
||||
@pytest.mark.skipif(is_python3(), reason='Unicode sucks.')
|
||||
@pytest.mark.xfail(reason='disabled due to pytest bug: https://bitbucket.org/hpk42/pytest/issue/534/pytest-fails-to-catch-unicodedecodeerrors') # noqa
|
||||
@mock.patch.object(sys, 'getfilesystemencoding', return_value='ascii')
|
||||
def test_encode_local_ascii_fails(_):
|
||||
with pytest.raises(UnicodeDecodeError):
|
||||
encode_local(u('日本語'))
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_python3(), reason="Unicode sucks.")
|
||||
@pytest.mark.skipif(is_python3(), reason='Unicode sucks.')
|
||||
@mock.patch.object(sys, 'getfilesystemencoding', return_value=None)
|
||||
def test_encode_local_empty(_):
|
||||
assert encode_local(b'foo') == u('foo')
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_python3(), reason="Unicode sucks.")
|
||||
@pytest.mark.skipif(is_python3(), reason='Unicode sucks.')
|
||||
@mock.patch.object(sys, 'getfilesystemencoding', return_value='utf-8')
|
||||
def test_encode_local_unicode(_):
|
||||
assert encode_local(b'foo') == u('foo')
|
||||
@ -86,7 +86,7 @@ def test_sanitize():
|
||||
assert sanitize([r'/foo/bar/', r'/']) == [u('/foo/bar'), u('/')]
|
||||
|
||||
|
||||
@pytest.mark.skipif(is_python3(), reason="Unicode sucks.")
|
||||
@pytest.mark.skipif(is_python3(), reason='Unicode sucks.')
|
||||
def test_unico():
|
||||
assert unico(str('blah')) == u('blah')
|
||||
assert unico(str('日本語')) == u('日本語')
|
@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
IPython autojump magic
|
||||
|
||||
@ -13,8 +14,8 @@ And copy this file into the "startup" folder of your new profile (e.g.
|
||||
|
||||
@TODO: extend %cd to call "autojump -a"
|
||||
"""
|
||||
from subprocess import Popen
|
||||
from subprocess import PIPE
|
||||
from subprocess import Popen
|
||||
|
||||
from IPython.core.magic import register_line_magic
|
||||
|
||||
@ -25,12 +26,14 @@ ip = get_ipython() # noqa
|
||||
def j(path):
|
||||
cmd = ['autojump'] + path.split()
|
||||
newpath = Popen(
|
||||
cmd,
|
||||
stdout=PIPE,
|
||||
shell=False).communicate()[0].strip()
|
||||
cmd,
|
||||
stdout=PIPE,
|
||||
shell=False,
|
||||
).communicate()[0].strip()
|
||||
|
||||
if newpath:
|
||||
ip.magic('cd %s' % newpath.decode('utf-8'))
|
||||
|
||||
|
||||
# remove from namespace
|
||||
del j
|
||||
|
43
tox.ini
43
tox.ini
@ -1,32 +1,33 @@
|
||||
[tox]
|
||||
envlist =
|
||||
py26,
|
||||
py27,
|
||||
py32,
|
||||
py33,
|
||||
py34
|
||||
py26,
|
||||
py27,
|
||||
py33,
|
||||
py34,
|
||||
py35
|
||||
# ignore missing setup.py
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
deps = -rdev-requirements.txt
|
||||
commands = py.test -rsxX -q
|
||||
setenv =
|
||||
PYTHONDONTWRITEBYTECODE = 1
|
||||
deps =
|
||||
mock
|
||||
coverage
|
||||
ipdb
|
||||
ipython
|
||||
pytest >= 2.9
|
||||
commands =
|
||||
coverage run --source=bin/ --omit=bin/autojump_argparse.py -m \
|
||||
py.test -vv -rxs --tb native -s --strict {posargs:tests}
|
||||
coverage report -m
|
||||
|
||||
[testenv:flake8]
|
||||
deps = flake8
|
||||
commands = flake8 .
|
||||
|
||||
[flake8]
|
||||
filename =
|
||||
*.py,
|
||||
autojump
|
||||
ignore =
|
||||
E126,
|
||||
E128
|
||||
max-line-length = 79
|
||||
max-complexity = 10
|
||||
show-pep8 = True
|
||||
[testenv:pre-commit]
|
||||
deps =
|
||||
pre-commit>=0.7.0
|
||||
commands =
|
||||
pre-commit {posargs}
|
||||
|
||||
[pytest]
|
||||
addopts = -rsxX -q
|
||||
norecursedirs = .git .tox docs
|
||||
|
87
uninstall.py
87
uninstall.py
@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
@ -9,7 +8,7 @@ import shutil
|
||||
import sys
|
||||
|
||||
sys.path.append('bin')
|
||||
from autojump_argparse import ArgumentParser
|
||||
from autojump_argparse import ArgumentParser # noqa
|
||||
|
||||
|
||||
def is_empty_dir(path):
|
||||
@ -26,24 +25,31 @@ def parse_arguments():
|
||||
default_clink_dir = os.path.join(os.getenv('LOCALAPPDATA', ''), 'clink')
|
||||
|
||||
parser = ArgumentParser(
|
||||
description='Uninstalls autojump.')
|
||||
description='Uninstalls autojump.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n', '--dryrun', action="store_true", default=False,
|
||||
help='simulate installation')
|
||||
'-n', '--dryrun', action='store_true', default=False,
|
||||
help='simulate installation',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-u', '--userdata', action="store_true", default=False,
|
||||
help='delete user data')
|
||||
'-u', '--userdata', action='store_true', default=False,
|
||||
help='delete user data',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-d', '--destdir', metavar='DIR',
|
||||
help='custom destdir')
|
||||
help='custom destdir',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-p', '--prefix', metavar='DIR', default='',
|
||||
help='custom prefix')
|
||||
help='custom prefix',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-z', '--zshshare', metavar='DIR', default='functions',
|
||||
help='custom zshshare')
|
||||
help='custom zshshare',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-c', '--clinkdir', metavar='DIR', default=default_clink_dir)
|
||||
'-c', '--clinkdir', metavar='DIR', default=default_clink_dir,
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
@ -61,7 +67,7 @@ def remove_custom_installation(args, dryrun=False):
|
||||
if not os.path.exists(share_dir):
|
||||
return
|
||||
|
||||
print("\nFound custom installation...")
|
||||
print('\nFound custom installation...')
|
||||
rm(os.path.join(bin_dir, 'autojump'), dryrun)
|
||||
rm(os.path.join(bin_dir, 'autojump_data.py'), dryrun)
|
||||
rm(os.path.join(bin_dir, 'autojump_utils.py'), dryrun)
|
||||
@ -94,19 +100,32 @@ def remove_system_installation(dryrun=False):
|
||||
default_zshshare = '/usr/share/zsh/site-functions'
|
||||
|
||||
bin_dir = os.path.join(default_destdir, default_prefix, 'bin')
|
||||
doc_dir = os.path.join(default_destdir, default_prefix, 'share', 'man', 'man1')
|
||||
doc_dir = os.path.join(
|
||||
default_destdir,
|
||||
default_prefix,
|
||||
'share',
|
||||
'man',
|
||||
'man1',
|
||||
)
|
||||
etc_dir = os.path.join(default_destdir, 'etc', 'profile.d')
|
||||
share_dir = os.path.join(default_destdir, default_prefix, 'share', 'autojump')
|
||||
share_dir = os.path.join(
|
||||
default_destdir,
|
||||
default_prefix,
|
||||
'share',
|
||||
'autojump',
|
||||
)
|
||||
zshshare_dir = os.path.join(default_destdir, default_zshshare)
|
||||
|
||||
if not os.path.exists(share_dir):
|
||||
return
|
||||
|
||||
print("\nFound system installation...")
|
||||
print('\nFound system installation...')
|
||||
|
||||
if os.geteuid() != 0:
|
||||
print("Please rerun as root for system-wide uninstall, skipping...",
|
||||
file=sys.stderr)
|
||||
print(
|
||||
'Please rerun as root for system-wide uninstall, skipping...',
|
||||
file=sys.stderr,
|
||||
)
|
||||
return
|
||||
|
||||
rm(os.path.join(bin_dir, 'autojump'), dryrun)
|
||||
@ -127,11 +146,13 @@ def remove_user_data(dryrun=False):
|
||||
data_home = os.path.join(
|
||||
os.path.expanduser('~'),
|
||||
'Library',
|
||||
'autojump')
|
||||
'autojump',
|
||||
)
|
||||
elif platform.system() == 'Windows':
|
||||
data_home = os.path.join(
|
||||
os.getenv('APPDATA'),
|
||||
'autojump')
|
||||
'autojump',
|
||||
)
|
||||
else:
|
||||
data_home = os.getenv(
|
||||
'XDG_DATA_HOME',
|
||||
@ -139,23 +160,27 @@ def remove_user_data(dryrun=False):
|
||||
os.path.expanduser('~'),
|
||||
'.local',
|
||||
'share',
|
||||
'autojump'))
|
||||
'autojump',
|
||||
),
|
||||
)
|
||||
|
||||
if os.path.exists(data_home):
|
||||
print("\nFound user data...")
|
||||
print('\nFound user data...')
|
||||
rmdir(data_home, dryrun)
|
||||
|
||||
|
||||
def remove_user_installation(dryrun=False):
|
||||
if platform.system() == 'Windows':
|
||||
default_destdir = os.path.join(os.getenv('LOCALAPPDATA', ''),
|
||||
'autojump')
|
||||
default_destdir = os.path.join(
|
||||
os.getenv('LOCALAPPDATA', ''),
|
||||
'autojump',
|
||||
)
|
||||
clink_dir = os.path.join(os.getenv('LOCALAPPDATA', ''), 'clink')
|
||||
else:
|
||||
default_destdir = os.path.join(os.path.expanduser("~"), '.autojump')
|
||||
default_destdir = os.path.join(os.path.expanduser('~'), '.autojump')
|
||||
|
||||
if os.path.exists(default_destdir):
|
||||
print("\nFound user installation...")
|
||||
print('\nFound user installation...')
|
||||
rmdir(default_destdir, dryrun)
|
||||
if platform.system() == 'Windows' and os.path.exists(clink_dir):
|
||||
rm(os.path.join(clink_dir, 'autojump.lua'), dryrun)
|
||||
@ -163,23 +188,23 @@ def remove_user_installation(dryrun=False):
|
||||
|
||||
def rm(path, dryrun):
|
||||
if os.path.exists(path):
|
||||
print("deleting file:", path)
|
||||
print('deleting file:', path)
|
||||
if not dryrun:
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def rmdir(path, dryrun):
|
||||
if os.path.exists(path):
|
||||
print("deleting directory:", path)
|
||||
print('deleting directory:', path)
|
||||
if not dryrun:
|
||||
shutil.rmtree(path)
|
||||
|
||||
|
||||
def main(args):
|
||||
if args.dryrun:
|
||||
print("Uninstalling autojump (DRYRUN)...")
|
||||
print('Uninstalling autojump (DRYRUN)...')
|
||||
else:
|
||||
print("Uninstalling autojump...")
|
||||
print('Uninstalling autojump...')
|
||||
|
||||
remove_user_installation(args.dryrun)
|
||||
remove_system_installation(args.dryrun)
|
||||
@ -188,5 +213,5 @@ def main(args):
|
||||
remove_user_data(args.dryrun)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(parse_arguments()))
|
||||
|
Loading…
Reference in New Issue
Block a user