mirror of
				https://github.com/nvbn/thefuck.git
				synced 2025-11-04 00:52:04 +00:00 
			
		
		
		
	Compare commits
	
		
			39 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					a5aadc6e90 | ||
| 
						 | 
					18ce062300 | ||
| 
						 | 
					73bc6c0184 | ||
| 
						 | 
					0296a4a46d | ||
| 
						 | 
					54a9769c10 | ||
| 
						 | 
					abc7238d14 | ||
| 
						 | 
					710a72ee8c | ||
| 
						 | 
					e09c6530e5 | ||
| 
						 | 
					b1da6a883a | ||
| 
						 | 
					a9e3b22fa4 | ||
| 
						 | 
					9debcdf676 | ||
| 
						 | 
					718cadb85a | ||
| 
						 | 
					910e6f4759 | ||
| 
						 | 
					d3146aa0ac | ||
| 
						 | 
					190e47ecdb | ||
| 
						 | 
					84a28d8c73 | ||
| 
						 | 
					551e35e3b6 | ||
| 
						 | 
					2bebfabf8d | ||
| 
						 | 
					675317b247 | ||
| 
						 | 
					6cf430cc23 | ||
| 
						 | 
					d088dac0f4 | ||
| 
						 | 
					c65fdd0f81 | ||
| 
						 | 
					e7d7b80c09 | ||
| 
						 | 
					f986df23d5 | ||
| 
						 | 
					cf5f18bf23 | ||
| 
						 | 
					44c06c483e | ||
| 
						 | 
					1f48d5e12a | ||
| 
						 | 
					2c3df1ad47 | ||
| 
						 | 
					5319871326 | ||
| 
						 | 
					d6ce5e1e62 | ||
| 
						 | 
					c42898dde3 | ||
| 
						 | 
					17b9104939 | ||
| 
						 | 
					02d9613618 | ||
| 
						 | 
					b63ce26853 | ||
| 
						 | 
					ce6855fd97 | ||
| 
						 | 
					7e55041963 | ||
| 
						 | 
					fc364b99b9 | ||
| 
						 | 
					742f6f9c94 | ||
| 
						 | 
					cd1bee9cb0 | 
							
								
								
									
										30
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								README.md
									
									
									
									
									
								
							@@ -145,40 +145,52 @@ sudo pip install thefuck --upgrade
 | 
			
		||||
The Fuck tries to match a rule for the previous command, creates a new command
 | 
			
		||||
using the matched rule and runs it. Rules enabled by default are as follows:
 | 
			
		||||
 | 
			
		||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
 | 
			
		||||
* `cpp11` – add missing `-std=c++11` to `g++` or `clang++`;
 | 
			
		||||
* `cd_parent` – changes `cd..` to `cd ..`;
 | 
			
		||||
* `cd_correction` – spellchecks and correct failed cd commands;
 | 
			
		||||
* `cd_mkdir` – creates directories before cd'ing into them;
 | 
			
		||||
* `cd_parent` – changes `cd..` to `cd ..`;
 | 
			
		||||
* `composer_not_command` – fixes composer command name;
 | 
			
		||||
* `cp_omitting_directory` – adds `-a` when you `cp` directory;
 | 
			
		||||
* `cpp11` – add missing `-std=c++11` to `g++` or `clang++`;
 | 
			
		||||
* `dry` – fix repetitions like "git git push";
 | 
			
		||||
* `django_south_ghost` – adds `--delete-ghost-migrations` to failed because ghosts django south migration;
 | 
			
		||||
* `django_south_merge` – adds `--merge` to inconsistent django south migration;
 | 
			
		||||
* `fix_alt_space` – replaces Alt+Space with Space character;
 | 
			
		||||
* `javac` – appends missing `.java` when compiling Java files;
 | 
			
		||||
* `java` – removes `.java` extension when running Java programs;
 | 
			
		||||
* `git_add` – fix *"Did you forget to 'git add'?"*;
 | 
			
		||||
* `git_checkout` – creates the branch before checking-out;
 | 
			
		||||
* `git_no_command` – fixes wrong git commands like `git brnch`;
 | 
			
		||||
* `git_pull` – sets upstream before executing previous `git pull`;
 | 
			
		||||
* `git_push` – adds `--set-upstream origin $branch` to previous failed `git push`;
 | 
			
		||||
* `git_stash` – stashes you local modifications before rebasing or switching branch;
 | 
			
		||||
* `grep_recursive` – adds `-r` when you trying to grep directory; 
 | 
			
		||||
* `has_exists_script` – prepends `./` when script/binary exists;
 | 
			
		||||
* `lein_not_task` – fixes wrong `lein` tasks like `lein rpl`;
 | 
			
		||||
* `ls_lah` – adds -lah to ls;
 | 
			
		||||
* `man` – change manual section;
 | 
			
		||||
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
 | 
			
		||||
* `mkdir_p` – adds `-p` when you trying to create directory without parent;
 | 
			
		||||
* `no_command` – fixes wrong console commands, for example `vom/vim`;
 | 
			
		||||
* `no_such_file` – creates missing directories with `mv` and `cp` commands;
 | 
			
		||||
* `man_no_space` – fixes man commands without spaces, for example `mandiff`;
 | 
			
		||||
* `pacman` – installs app with `pacman` or `yaourt` if it is not installed;
 | 
			
		||||
* `open` – prepends `http` to address passed to `open`;
 | 
			
		||||
* `pip_unknown_command` – fixes wrong pip commands, for example `pip instatl/pip install`;
 | 
			
		||||
* `python_command` – prepends `python` when you trying to run not executable/without `./` python script;
 | 
			
		||||
* `sl_ls` – changes `sl` to `ls`;
 | 
			
		||||
* `rm_dir` – adds `-rf` when you trying to remove directory;
 | 
			
		||||
* `sl_ls` – changes `sl` to `ls`;
 | 
			
		||||
* `ssh_known_hosts` – removes host from `known_hosts` on warning;
 | 
			
		||||
* `sudo` – prepends `sudo` to previous command if it failed because of permissions;
 | 
			
		||||
* `switch_layout` – switches command from your local layout to en;
 | 
			
		||||
* `whois` – fixes `whois` command;
 | 
			
		||||
* `whois` – fixes `whois` command.
 | 
			
		||||
 | 
			
		||||
Enabled by default only on specific platforms:
 | 
			
		||||
 | 
			
		||||
* `apt_get` – installs app from apt if it not installed;
 | 
			
		||||
* `brew_install` – fixes formula name for `brew install`;
 | 
			
		||||
* `composer_not_command` – fixes composer command name.
 | 
			
		||||
* `brew_unknown_command` – fixes wrong brew commands, for example `brew docto/brew doctor`;
 | 
			
		||||
* `pacman` – installs app with `pacman` or `yaourt` if it is not installed.
 | 
			
		||||
 | 
			
		||||
Bundled, but not enabled by default:
 | 
			
		||||
 | 
			
		||||
* `ls_lah` – adds -lah to ls;
 | 
			
		||||
* `rm_root` – adds `--no-preserve-root` to `rm -rf /` command.
 | 
			
		||||
 | 
			
		||||
## Creating your own rules
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								setup.py
									
									
									
									
									
								
							@@ -1,7 +1,7 @@
 | 
			
		||||
from setuptools import setup, find_packages
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VERSION = '1.41'
 | 
			
		||||
VERSION = '1.44'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
setup(name='thefuck',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										53
									
								
								tests/rules/test_django_south_ghost.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								tests/rules/test_django_south_ghost.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,53 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.django_south_ghost import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def stderr():
 | 
			
		||||
    return '''Traceback (most recent call last):
 | 
			
		||||
  File "/home/nvbn/work/.../bin/python", line 42, in <module>
 | 
			
		||||
    exec(compile(__file__f.read(), __file__, "exec"))
 | 
			
		||||
  File "/home/nvbn/work/.../app/manage.py", line 34, in <module>
 | 
			
		||||
    execute_from_command_line(sys.argv)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 443, in execute_from_command_line
 | 
			
		||||
    utility.execute()
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 382, in execute
 | 
			
		||||
    self.fetch_command(subcommand).run_from_argv(self.argv)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/base.py", line 196, in run_from_argv
 | 
			
		||||
    self.execute(*args, **options.__dict__)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/base.py", line 232, in execute
 | 
			
		||||
    output = self.handle(*args, **options)
 | 
			
		||||
  File "/home/nvbn/work/.../app/lib/south/management/commands/migrate.py", line 108, in handle
 | 
			
		||||
    ignore_ghosts = ignore_ghosts,
 | 
			
		||||
  File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 193, in migrate_app
 | 
			
		||||
    applied_all = check_migration_histories(applied_all, delete_ghosts, ignore_ghosts)
 | 
			
		||||
  File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 88, in check_migration_histories
 | 
			
		||||
    raise exceptions.GhostMigrations(ghosts)
 | 
			
		||||
south.exceptions.GhostMigrations: 
 | 
			
		||||
 | 
			
		||||
 ! These migrations are in the database but not on disk:
 | 
			
		||||
    <app1: 0033_auto__...>
 | 
			
		||||
    <app1: 0034_fill_...>
 | 
			
		||||
    <app1: 0035_rename_...>
 | 
			
		||||
    <app2: 0003_add_...>
 | 
			
		||||
    <app2: 0004_denormalize_...>
 | 
			
		||||
    <app1: 0033_auto....>
 | 
			
		||||
    <app1: 0034_fill...>
 | 
			
		||||
 ! I'm not trusting myself; either fix this yourself by fiddling
 | 
			
		||||
 ! with the south_migrationhistory table, or pass --delete-ghost-migrations
 | 
			
		||||
 ! to South to have it delete ALL of these records (this may not be good).
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_match(stderr):
 | 
			
		||||
    assert match(Command('./manage.py migrate', stderr=stderr), None)
 | 
			
		||||
    assert match(Command('python manage.py migrate', stderr=stderr), None)
 | 
			
		||||
    assert not match(Command('./manage.py migrate'), None)
 | 
			
		||||
    assert not match(Command('app migrate', stderr=stderr), None)
 | 
			
		||||
    assert not match(Command('./manage.py test', stderr=stderr), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_new_command():
 | 
			
		||||
    assert get_new_command(Command('./manage.py migrate auth'), None)\
 | 
			
		||||
        == './manage.py migrate auth --delete-ghost-migrations'
 | 
			
		||||
							
								
								
									
										43
									
								
								tests/rules/test_django_south_merge.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								tests/rules/test_django_south_merge.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,43 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.django_south_merge import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def stderr():
 | 
			
		||||
    return '''Running migrations for app:
 | 
			
		||||
 ! Migration app:0003_auto... should not have been applied before app:0002_auto__add_field_query_due_date_ but was.
 | 
			
		||||
Traceback (most recent call last):
 | 
			
		||||
  File "/home/nvbn/work/.../bin/python", line 42, in <module>
 | 
			
		||||
    exec(compile(__file__f.read(), __file__, "exec"))
 | 
			
		||||
  File "/home/nvbn/work/.../app/manage.py", line 34, in <module>
 | 
			
		||||
    execute_from_command_line(sys.argv)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 443, in execute_from_command_line
 | 
			
		||||
    utility.execute()
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/__init__.py", line 382, in execute
 | 
			
		||||
    self.fetch_command(subcommand).run_from_argv(self.argv)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/base.py", line 196, in run_from_argv
 | 
			
		||||
    self.execute(*args, **options.__dict__)
 | 
			
		||||
  File "/home/nvbn/work/.../lib/django/core/management/base.py", line 232, in execute
 | 
			
		||||
    output = self.handle(*args, **options)
 | 
			
		||||
  File "/home/nvbn/work/.../app/lib/south/management/commands/migrate.py", line 108, in handle
 | 
			
		||||
    ignore_ghosts = ignore_ghosts,
 | 
			
		||||
  File "/home/nvbn/work/.../app/lib/south/migration/__init__.py", line 207, in migrate_app
 | 
			
		||||
    raise exceptions.InconsistentMigrationHistory(problems)
 | 
			
		||||
south.exceptions.InconsistentMigrationHistory: Inconsistent migration history
 | 
			
		||||
The following options are available:
 | 
			
		||||
    --merge: will just attempt the migration ignoring any potential dependency conflicts.
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_match(stderr):
 | 
			
		||||
    assert match(Command('./manage.py migrate', stderr=stderr), None)
 | 
			
		||||
    assert match(Command('python manage.py migrate', stderr=stderr), None)
 | 
			
		||||
    assert not match(Command('./manage.py migrate'), None)
 | 
			
		||||
    assert not match(Command('app migrate', stderr=stderr), None)
 | 
			
		||||
    assert not match(Command('./manage.py test', stderr=stderr), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_new_command():
 | 
			
		||||
    assert get_new_command(Command('./manage.py migrate auth'), None) \
 | 
			
		||||
           == './manage.py migrate auth --merge'
 | 
			
		||||
							
								
								
									
										29
									
								
								tests/rules/test_git_pull.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								tests/rules/test_git_pull.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.git_pull import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def stderr():
 | 
			
		||||
    return '''There is no tracking information for the current branch.
 | 
			
		||||
Please specify which branch you want to merge with.
 | 
			
		||||
See git-pull(1) for details
 | 
			
		||||
 | 
			
		||||
    git pull <remote> <branch>
 | 
			
		||||
 | 
			
		||||
If you wish to set tracking information for this branch you can do so with:
 | 
			
		||||
 | 
			
		||||
    git branch --set-upstream-to=<remote>/<branch> master
 | 
			
		||||
 | 
			
		||||
'''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_match(stderr):
 | 
			
		||||
    assert match(Command('git pull', stderr=stderr), None)
 | 
			
		||||
    assert not match(Command('git pull'), None)
 | 
			
		||||
    assert not match(Command('ls', stderr=stderr), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_new_command(stderr):
 | 
			
		||||
    assert get_new_command(Command('git pull', stderr=stderr), None) \
 | 
			
		||||
           == "git branch --set-upstream-to=origin/master master && git pull"
 | 
			
		||||
							
								
								
									
										17
									
								
								tests/rules/test_java.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/rules/test_java.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.java import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command', [
 | 
			
		||||
	Command(script='java foo.java'),
 | 
			
		||||
	Command(script='java bar.java')])
 | 
			
		||||
def test_match(command):
 | 
			
		||||
	assert match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command, new_command', [
 | 
			
		||||
	(Command('java foo.java'), 'java foo'),
 | 
			
		||||
	(Command('java bar.java'), 'java bar')])
 | 
			
		||||
def test_get_new_command(command, new_command):
 | 
			
		||||
	assert get_new_command(command, None) == new_command
 | 
			
		||||
							
								
								
									
										17
									
								
								tests/rules/test_javac.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								tests/rules/test_javac.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.javac import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command', [
 | 
			
		||||
	Command(script='javac foo'), 
 | 
			
		||||
	Command(script='javac bar')])
 | 
			
		||||
def test_match(command):
 | 
			
		||||
	assert match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command, new_command', [
 | 
			
		||||
	(Command('javac foo'), 'javac foo.java'),
 | 
			
		||||
	(Command('javac bar'), 'javac bar.java')])
 | 
			
		||||
def test_get_new_command(command, new_command):
 | 
			
		||||
	assert get_new_command(command, None) == new_command
 | 
			
		||||
							
								
								
									
										34
									
								
								tests/rules/test_man.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								tests/rules/test_man.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,34 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.man import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command', [
 | 
			
		||||
    Command('man read'),
 | 
			
		||||
    Command('man 2 read'),
 | 
			
		||||
    Command('man 3 read'),
 | 
			
		||||
    Command('man -s2 read'),
 | 
			
		||||
    Command('man -s3 read'),
 | 
			
		||||
    Command('man -s 2 read'),
 | 
			
		||||
    Command('man -s 3 read')])
 | 
			
		||||
def test_match(command):
 | 
			
		||||
    assert match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command', [
 | 
			
		||||
    Command('man'),
 | 
			
		||||
    Command('man ')])
 | 
			
		||||
def test_not_match(command):
 | 
			
		||||
    assert not match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command, new_command', [
 | 
			
		||||
    (Command('man read'), 'man 3 read'),
 | 
			
		||||
    (Command('man 2 read'), 'man 3 read'),
 | 
			
		||||
    (Command('man 3 read'), 'man 2 read'),
 | 
			
		||||
    (Command('man -s2 read'), 'man -s3 read'),
 | 
			
		||||
    (Command('man -s3 read'), 'man -s2 read'),
 | 
			
		||||
    (Command('man -s 2 read'), 'man -s 3 read'),
 | 
			
		||||
    (Command('man -s 3 read'), 'man -s 2 read')])
 | 
			
		||||
def test_get_new_command(command, new_command):
 | 
			
		||||
    assert get_new_command(command, None) == new_command
 | 
			
		||||
@@ -3,7 +3,7 @@ from thefuck.rules.no_command import match, get_new_command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_match():
 | 
			
		||||
    with patch('thefuck.rules.no_command._get_all_bins',
 | 
			
		||||
    with patch('thefuck.rules.no_command._get_all_callables',
 | 
			
		||||
               return_value=['vim', 'apt-get']):
 | 
			
		||||
        assert match(Mock(stderr='vom: not found', script='vom file.py'), None)
 | 
			
		||||
        assert not match(Mock(stderr='qweqwe: not found', script='qweqwe'), None)
 | 
			
		||||
@@ -11,7 +11,7 @@ def test_match():
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_get_new_command():
 | 
			
		||||
    with patch('thefuck.rules.no_command._get_all_bins',
 | 
			
		||||
    with patch('thefuck.rules.no_command._get_all_callables',
 | 
			
		||||
               return_value=['vim', 'apt-get']):
 | 
			
		||||
        assert get_new_command(
 | 
			
		||||
            Mock(stderr='vom: not found',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								tests/rules/test_open.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								tests/rules/test_open.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,25 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from thefuck.rules.open import match, get_new_command
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command', [
 | 
			
		||||
	Command(script='open foo.com'), 
 | 
			
		||||
	Command(script='open foo.ly'), 
 | 
			
		||||
	Command(script='open foo.org'), 
 | 
			
		||||
	Command(script='open foo.net'), 
 | 
			
		||||
	Command(script='open foo.se'), 
 | 
			
		||||
	Command(script='open foo.io')])
 | 
			
		||||
def test_match(command):
 | 
			
		||||
	assert match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command, new_command', [
 | 
			
		||||
	(Command('open foo.com'), 'open http://foo.com'),
 | 
			
		||||
	(Command('open foo.ly'), 'open http://foo.ly'),
 | 
			
		||||
	(Command('open foo.org'), 'open http://foo.org'),
 | 
			
		||||
	(Command('open foo.net'), 'open http://foo.net'),
 | 
			
		||||
	(Command('open foo.se'), 'open http://foo.se'),
 | 
			
		||||
	(Command('open foo.io'), 'open http://foo.io')])
 | 
			
		||||
def test_get_new_command(command, new_command):
 | 
			
		||||
	assert get_new_command(command, None) == new_command
 | 
			
		||||
@@ -11,6 +11,10 @@ def test_match(command):
 | 
			
		||||
    assert match(command, None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_not_match():
 | 
			
		||||
    assert not match(Command(script='whois'), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.parametrize('command, new_command', [
 | 
			
		||||
    (Command('whois https://en.wikipedia.org/wiki/Main_Page'), 'whois en.wikipedia.org'),
 | 
			
		||||
    (Command('whois https://en.wikipedia.org/'), 'whois en.wikipedia.org'),
 | 
			
		||||
 
 | 
			
		||||
@@ -13,19 +13,33 @@ def isfile(mocker):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestGeneric(object):
 | 
			
		||||
    def test_from_shell(self):
 | 
			
		||||
        assert shells.Generic().from_shell('pwd') == 'pwd'
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def shell(self):
 | 
			
		||||
        return shells.Generic()
 | 
			
		||||
 | 
			
		||||
    def test_to_shell(self):
 | 
			
		||||
        assert shells.Generic().to_shell('pwd') == 'pwd'
 | 
			
		||||
    def test_from_shell(self, shell):
 | 
			
		||||
        assert shell.from_shell('pwd') == 'pwd'
 | 
			
		||||
 | 
			
		||||
    def test_put_to_history(self, builtins_open):
 | 
			
		||||
        assert shells.Generic().put_to_history('ls') is None
 | 
			
		||||
    def test_to_shell(self, shell):
 | 
			
		||||
        assert shell.to_shell('pwd') == 'pwd'
 | 
			
		||||
 | 
			
		||||
    def test_put_to_history(self, builtins_open, shell):
 | 
			
		||||
        assert shell.put_to_history('ls') is None
 | 
			
		||||
        assert builtins_open.call_count == 0
 | 
			
		||||
 | 
			
		||||
    def test_and_(self, shell):
 | 
			
		||||
        assert shell.and_('ls', 'cd') == 'ls && cd'
 | 
			
		||||
 | 
			
		||||
    def test_get_aliases(self, shell):
 | 
			
		||||
        assert shell.get_aliases() == {}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures('isfile')
 | 
			
		||||
class TestBash(object):
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def shell(self):
 | 
			
		||||
        return shells.Bash()
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(autouse=True)
 | 
			
		||||
    def Popen(self, mocker):
 | 
			
		||||
        mock = mocker.patch('thefuck.shells.Popen')
 | 
			
		||||
@@ -38,42 +52,81 @@ class TestBash(object):
 | 
			
		||||
    @pytest.mark.parametrize('before, after', [
 | 
			
		||||
        ('pwd', 'pwd'),
 | 
			
		||||
        ('ll', 'ls -alF')])
 | 
			
		||||
    def test_from_shell(self, before, after):
 | 
			
		||||
        assert shells.Bash().from_shell(before) == after
 | 
			
		||||
    def test_from_shell(self, before, after, shell):
 | 
			
		||||
        assert shell.from_shell(before) == after
 | 
			
		||||
 | 
			
		||||
    def test_to_shell(self):
 | 
			
		||||
        assert shells.Bash().to_shell('pwd') == 'pwd'
 | 
			
		||||
    def test_to_shell(self, shell):
 | 
			
		||||
        assert shell.to_shell('pwd') == 'pwd'
 | 
			
		||||
 | 
			
		||||
    def test_put_to_history(self, builtins_open):
 | 
			
		||||
        shells.Bash().put_to_history('ls')
 | 
			
		||||
    def test_put_to_history(self, builtins_open, shell):
 | 
			
		||||
        shell.put_to_history('ls')
 | 
			
		||||
        builtins_open.return_value.__enter__.return_value. \
 | 
			
		||||
            write.assert_called_once_with('ls\n')
 | 
			
		||||
 | 
			
		||||
    def test_and_(self, shell):
 | 
			
		||||
        assert shell.and_('ls', 'cd') == 'ls && cd'
 | 
			
		||||
 | 
			
		||||
    def test_get_aliases(self, shell):
 | 
			
		||||
        assert shell.get_aliases() == {'l': 'ls -CF',
 | 
			
		||||
                                       'la': 'ls -A',
 | 
			
		||||
                                       'll': 'ls -alF'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures('isfile')
 | 
			
		||||
class TestFish(object):
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def shell(self):
 | 
			
		||||
        return shells.Fish()
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(autouse=True)
 | 
			
		||||
    def Popen(self, mocker):
 | 
			
		||||
        mock = mocker.patch('thefuck.shells.Popen')
 | 
			
		||||
        mock.return_value.stdout.read.return_value = (
 | 
			
		||||
            b'fish_config\nfuck\nfunced\nfuncsave\ngrep\nhistory\nll\nmath')
 | 
			
		||||
        return mock
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.parametrize('before, after', [
 | 
			
		||||
        ('pwd', 'pwd'),
 | 
			
		||||
        ('ll', 'll')])  # Fish has no aliases but functions
 | 
			
		||||
    def test_from_shell(self, before, after):
 | 
			
		||||
        assert shells.Fish().from_shell(before) == after
 | 
			
		||||
        ('fuck', 'fish -ic "fuck"'),
 | 
			
		||||
        ('find', 'find'),
 | 
			
		||||
        ('funced', 'fish -ic "funced"'),
 | 
			
		||||
        ('awk', 'awk'),
 | 
			
		||||
        ('math "2 + 2"', r'fish -ic "math \"2 + 2\""'),
 | 
			
		||||
        ('vim', 'vim'),
 | 
			
		||||
        ('ll', 'fish -ic "ll"')])  # Fish has no aliases but functions
 | 
			
		||||
    def test_from_shell(self, before, after, shell):
 | 
			
		||||
        assert shell.from_shell(before) == after
 | 
			
		||||
 | 
			
		||||
    def test_to_shell(self):
 | 
			
		||||
        assert shells.Fish().to_shell('pwd') == 'pwd'
 | 
			
		||||
    def test_to_shell(self, shell):
 | 
			
		||||
        assert shell.to_shell('pwd') == 'pwd'
 | 
			
		||||
 | 
			
		||||
    def test_put_to_history(self, builtins_open, mocker):
 | 
			
		||||
    def test_put_to_history(self, builtins_open, mocker, shell):
 | 
			
		||||
        mocker.patch('thefuck.shells.time',
 | 
			
		||||
                     return_value=1430707243.3517463)
 | 
			
		||||
        shells.Fish().put_to_history('ls')
 | 
			
		||||
        shell.put_to_history('ls')
 | 
			
		||||
        builtins_open.return_value.__enter__.return_value. \
 | 
			
		||||
            write.assert_called_once_with('- cmd: ls\n   when: 1430707243\n')
 | 
			
		||||
 | 
			
		||||
    def test_and_(self):
 | 
			
		||||
        assert shells.Fish().and_('foo', 'bar') == 'foo; and bar'
 | 
			
		||||
    def test_and_(self, shell):
 | 
			
		||||
        assert shell.and_('foo', 'bar') == 'foo; and bar'
 | 
			
		||||
 | 
			
		||||
    def test_get_aliases(self, shell):
 | 
			
		||||
        assert shell.get_aliases() == {'fish_config': 'fish_config',
 | 
			
		||||
                                       'fuck': 'fuck',
 | 
			
		||||
                                       'funced': 'funced',
 | 
			
		||||
                                       'funcsave': 'funcsave',
 | 
			
		||||
                                       'grep': 'grep',
 | 
			
		||||
                                       'history': 'history',
 | 
			
		||||
                                       'll': 'll',
 | 
			
		||||
                                       'math': 'math'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures('isfile')
 | 
			
		||||
class TestZsh(object):
 | 
			
		||||
    @pytest.fixture
 | 
			
		||||
    def shell(self):
 | 
			
		||||
        return shells.Zsh()
 | 
			
		||||
 | 
			
		||||
    @pytest.fixture(autouse=True)
 | 
			
		||||
    def Popen(self, mocker):
 | 
			
		||||
        mock = mocker.patch('thefuck.shells.Popen')
 | 
			
		||||
@@ -86,15 +139,23 @@ class TestZsh(object):
 | 
			
		||||
    @pytest.mark.parametrize('before, after', [
 | 
			
		||||
        ('pwd', 'pwd'),
 | 
			
		||||
        ('ll', 'ls -alF')])
 | 
			
		||||
    def test_from_shell(self, before, after):
 | 
			
		||||
        assert shells.Zsh().from_shell(before) == after
 | 
			
		||||
    def test_from_shell(self, before, after, shell):
 | 
			
		||||
        assert shell.from_shell(before) == after
 | 
			
		||||
 | 
			
		||||
    def test_to_shell(self):
 | 
			
		||||
        assert shells.Zsh().to_shell('pwd') == 'pwd'
 | 
			
		||||
    def test_to_shell(self, shell):
 | 
			
		||||
        assert shell.to_shell('pwd') == 'pwd'
 | 
			
		||||
 | 
			
		||||
    def test_put_to_history(self, builtins_open, mocker):
 | 
			
		||||
    def test_put_to_history(self, builtins_open, mocker, shell):
 | 
			
		||||
        mocker.patch('thefuck.shells.time',
 | 
			
		||||
                     return_value=1430707243.3517463)
 | 
			
		||||
        shells.Zsh().put_to_history('ls')
 | 
			
		||||
        shell.put_to_history('ls')
 | 
			
		||||
        builtins_open.return_value.__enter__.return_value. \
 | 
			
		||||
            write.assert_called_once_with(': 1430707243:0;ls\n')
 | 
			
		||||
 | 
			
		||||
    def test_and_(self, shell):
 | 
			
		||||
        assert shell.and_('ls', 'cd') == 'ls && cd'
 | 
			
		||||
 | 
			
		||||
    def test_get_aliases(self, shell):
 | 
			
		||||
        assert shell.get_aliases() == {'l': 'ls -CF',
 | 
			
		||||
                                       'la': 'ls -A',
 | 
			
		||||
                                       'll': 'ls -alF'}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
import pytest
 | 
			
		||||
from mock import Mock
 | 
			
		||||
from thefuck.utils import sudo_support, wrap_settings
 | 
			
		||||
from thefuck.utils import sudo_support, wrap_settings, memoize
 | 
			
		||||
from thefuck.types import Settings
 | 
			
		||||
from tests.utils import Command
 | 
			
		||||
 | 
			
		||||
@@ -24,3 +24,11 @@ def test_sudo_support(return_value, command, called, result):
 | 
			
		||||
    fn = Mock(return_value=return_value, __name__='')
 | 
			
		||||
    assert sudo_support(fn)(Command(command), None) == result
 | 
			
		||||
    fn.assert_called_once_with(Command(called), None)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_memoize():
 | 
			
		||||
    fn = Mock(__name__='fn')
 | 
			
		||||
    memoized = memoize(fn)
 | 
			
		||||
    memoized()
 | 
			
		||||
    memoized()
 | 
			
		||||
    fn.assert_called_once_with()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								thefuck/rules/django_south_ghost.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								thefuck/rules/django_south_ghost.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return 'manage.py' in command.script and \
 | 
			
		||||
           'migrate' in command.script \
 | 
			
		||||
           and 'or pass --delete-ghost-migrations' in command.stderr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
    return u'{} --delete-ghost-migrations'.format(command.script)
 | 
			
		||||
							
								
								
									
										8
									
								
								thefuck/rules/django_south_merge.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								thefuck/rules/django_south_merge.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return 'manage.py' in command.script and \
 | 
			
		||||
           'migrate' in command.script \
 | 
			
		||||
           and '--merge: will just attempt the migration' in command.stderr
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
    return u'{} --merge'.format(command.script)
 | 
			
		||||
							
								
								
									
										12
									
								
								thefuck/rules/git_pull.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								thefuck/rules/git_pull.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return ('git' in command.script
 | 
			
		||||
            and 'pull' in command.script
 | 
			
		||||
            and 'set-upstream' in command.stderr)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
    line = command.stderr.split('\n')[-3].strip()
 | 
			
		||||
    branch = line.split(' ')[-1]
 | 
			
		||||
    set_upstream = line.replace('<remote>', 'origin')\
 | 
			
		||||
                       .replace('<branch>', branch)
 | 
			
		||||
    return u'{} && {}'.format(set_upstream, command.script)
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
	return (command.script.startswith('grep')
 | 
			
		||||
    return (command.script.startswith('grep')
 | 
			
		||||
            and 'is a directory' in command.stderr.lower())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								thefuck/rules/java.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								thefuck/rules/java.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
# Fixes common java command mistake
 | 
			
		||||
# 
 | 
			
		||||
# Example:
 | 
			
		||||
# > java foo.java
 | 
			
		||||
# Error: Could not find or load main class foo.java
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
	return (command.script.startswith ('java ')
 | 
			
		||||
		and command.script.endswith ('.java'))
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
	return command.script[:-5]
 | 
			
		||||
							
								
								
									
										15
									
								
								thefuck/rules/javac.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								thefuck/rules/javac.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
# Appends .java when compiling java files
 | 
			
		||||
# 
 | 
			
		||||
# Example:
 | 
			
		||||
# > javac foo
 | 
			
		||||
# error: Class names, 'foo', are only accepted if annotation 
 | 
			
		||||
# processing is explicitly requested
 | 
			
		||||
#
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
	return (command.script.startswith ('javac ')
 | 
			
		||||
			and not command.script.endswith('.java'))
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
	return command.script + '.java'
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
enabled_by_default = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return 'ls' in command.script and not ('ls -' in command.script)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								thefuck/rules/man.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								thefuck/rules/man.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return command.script.strip().startswith('man ')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
    if '3' in command.script:
 | 
			
		||||
        return command.script.replace("3", "2")
 | 
			
		||||
    if '2' in command.script:
 | 
			
		||||
        return command.script.replace("2", "3")
 | 
			
		||||
 | 
			
		||||
    split_cmd = command.script.split()
 | 
			
		||||
    split_cmd.insert(1, ' 3 ')
 | 
			
		||||
    return "".join(split_cmd)
 | 
			
		||||
@@ -2,6 +2,7 @@ from difflib import get_close_matches
 | 
			
		||||
import os
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from thefuck.utils import sudo_support
 | 
			
		||||
from thefuck.shells import get_aliases
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _safe(fn, fallback):
 | 
			
		||||
@@ -11,25 +12,25 @@ def _safe(fn, fallback):
 | 
			
		||||
        return fallback
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_all_bins():
 | 
			
		||||
def _get_all_callables():
 | 
			
		||||
    return [exe.name
 | 
			
		||||
            for path in os.environ.get('PATH', '').split(':')
 | 
			
		||||
            for exe in _safe(lambda: list(Path(path).iterdir()), [])
 | 
			
		||||
            if not _safe(exe.is_dir, True)]
 | 
			
		||||
            if not _safe(exe.is_dir, True)] + get_aliases()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@sudo_support
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
    return 'not found' in command.stderr and \
 | 
			
		||||
           bool(get_close_matches(command.script.split(' ')[0],
 | 
			
		||||
                                  _get_all_bins()))
 | 
			
		||||
                                  _get_all_callables()))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@sudo_support
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
    old_command = command.script.split(' ')[0]
 | 
			
		||||
    new_command = get_close_matches(old_command,
 | 
			
		||||
                                    _get_all_bins())[0]
 | 
			
		||||
                                    _get_all_callables())[0]
 | 
			
		||||
    return ' '.join([new_command] + command.script.split(' ')[1:])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										24
									
								
								thefuck/rules/open.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								thefuck/rules/open.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
# Opens URL's in the default web browser
 | 
			
		||||
# 
 | 
			
		||||
# Example:
 | 
			
		||||
# > open github.com
 | 
			
		||||
# The file ~/github.com does not exist.
 | 
			
		||||
# Perhaps you meant 'http://github.com'?
 | 
			
		||||
#
 | 
			
		||||
# 
 | 
			
		||||
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
	return (command.script.startswith ('open')
 | 
			
		||||
			and (
 | 
			
		||||
			# Wanted to use this:
 | 
			
		||||
			# 'http' in command.stderr
 | 
			
		||||
			'.com' in command.script
 | 
			
		||||
			or '.net' in command.script
 | 
			
		||||
			or '.org' in command.script
 | 
			
		||||
			or '.ly' in command.script
 | 
			
		||||
			or '.io' in command.script
 | 
			
		||||
			or '.se' in command.script
 | 
			
		||||
			or '.edu' in command.script))
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
	return 'open http://' + command.script[5:]
 | 
			
		||||
@@ -11,7 +11,8 @@ patterns = ['permission denied',
 | 
			
		||||
            'requested operation requires superuser privilege',
 | 
			
		||||
            'must be run as root',
 | 
			
		||||
            'must be superuser',
 | 
			
		||||
            'Need to be root']
 | 
			
		||||
            'Need to be root',
 | 
			
		||||
            'you must be root to run this program.']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def match(command, settings):
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ def match(command, settings):
 | 
			
		||||
        - www.google.fr → subdomain: www, domain: 'google.fr';
 | 
			
		||||
        - google.co.uk → subdomain: None, domain; 'google.co.uk'.
 | 
			
		||||
    """
 | 
			
		||||
    return 'whois' in command.script
 | 
			
		||||
    return 'whois' in command.script and len(command.script.split()) > 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_new_command(command, settings):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
"""Module with shell specific actions, each shell class should
 | 
			
		||||
implement `from_shell`, `to_shell`, `app_alias` and `put_to_history`
 | 
			
		||||
implement `from_shell`, `to_shell`, `app_alias`, `put_to_history` and `get_aliases`
 | 
			
		||||
methods.
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
@@ -8,15 +8,16 @@ from subprocess import Popen, PIPE
 | 
			
		||||
from time import time
 | 
			
		||||
import os
 | 
			
		||||
from psutil import Process
 | 
			
		||||
from .utils import DEVNULL
 | 
			
		||||
from .utils import DEVNULL, memoize
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Generic(object):
 | 
			
		||||
    def _get_aliases(self):
 | 
			
		||||
 | 
			
		||||
    def get_aliases(self):
 | 
			
		||||
        return {}
 | 
			
		||||
 | 
			
		||||
    def _expand_aliases(self, command_script):
 | 
			
		||||
        aliases = self._get_aliases()
 | 
			
		||||
        aliases = self.get_aliases()
 | 
			
		||||
        binary = command_script.split(' ')[0]
 | 
			
		||||
        if binary in aliases:
 | 
			
		||||
            return command_script.replace(binary, aliases[binary], 1)
 | 
			
		||||
@@ -61,8 +62,10 @@ class Bash(Generic):
 | 
			
		||||
            value = value[1:-1]
 | 
			
		||||
        return name, value
 | 
			
		||||
 | 
			
		||||
    def _get_aliases(self):
 | 
			
		||||
        proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
 | 
			
		||||
    @memoize
 | 
			
		||||
    def get_aliases(self):
 | 
			
		||||
        proc = Popen('bash -ic alias', stdout=PIPE, stderr=DEVNULL,
 | 
			
		||||
                     shell=True)
 | 
			
		||||
        return dict(
 | 
			
		||||
            self._parse_alias(alias)
 | 
			
		||||
            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
			
		||||
@@ -91,6 +94,25 @@ class Fish(Generic):
 | 
			
		||||
                "    end\n"
 | 
			
		||||
                "end")
 | 
			
		||||
 | 
			
		||||
    @memoize
 | 
			
		||||
    def get_aliases(self):
 | 
			
		||||
        proc = Popen('fish -ic functions', stdout=PIPE, stderr=DEVNULL,
 | 
			
		||||
                     shell=True)
 | 
			
		||||
        functions = proc.stdout.read().decode('utf-8').strip().split('\n')
 | 
			
		||||
        return {function: function for function in functions}
 | 
			
		||||
 | 
			
		||||
    def _expand_aliases(self, command_script):
 | 
			
		||||
        aliases = self.get_aliases()
 | 
			
		||||
        binary = command_script.split(' ')[0]
 | 
			
		||||
        if binary in aliases:
 | 
			
		||||
            return 'fish -ic "{}"'.format(command_script.replace('"', r'\"'))
 | 
			
		||||
        else:
 | 
			
		||||
            return command_script
 | 
			
		||||
 | 
			
		||||
    def from_shell(self, command_script):
 | 
			
		||||
        """Prepares command before running in app."""
 | 
			
		||||
        return self._expand_aliases(command_script)
 | 
			
		||||
 | 
			
		||||
    def _get_history_file_name(self):
 | 
			
		||||
        return os.path.expanduser('~/.config/fish/fish_history')
 | 
			
		||||
 | 
			
		||||
@@ -111,8 +133,10 @@ class Zsh(Generic):
 | 
			
		||||
            value = value[1:-1]
 | 
			
		||||
        return name, value
 | 
			
		||||
 | 
			
		||||
    def _get_aliases(self):
 | 
			
		||||
        proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
 | 
			
		||||
    @memoize
 | 
			
		||||
    def get_aliases(self):
 | 
			
		||||
        proc = Popen('zsh -ic alias', stdout=PIPE, stderr=DEVNULL,
 | 
			
		||||
                     shell=True)
 | 
			
		||||
        return dict(
 | 
			
		||||
            self._parse_alias(alias)
 | 
			
		||||
            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
			
		||||
@@ -134,8 +158,10 @@ class Tcsh(Generic):
 | 
			
		||||
        name, value = alias.split("\t", 1)
 | 
			
		||||
        return name, value
 | 
			
		||||
 | 
			
		||||
    def _get_aliases(self):
 | 
			
		||||
        proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL, shell=True)
 | 
			
		||||
    @memoize
 | 
			
		||||
    def get_aliases(self):
 | 
			
		||||
        proc = Popen('tcsh -ic alias', stdout=PIPE, stderr=DEVNULL,
 | 
			
		||||
                     shell=True)
 | 
			
		||||
        return dict(
 | 
			
		||||
            self._parse_alias(alias)
 | 
			
		||||
            for alias in proc.stdout.read().decode('utf-8').split('\n')
 | 
			
		||||
@@ -153,16 +179,16 @@ shells = defaultdict(lambda: Generic(), {
 | 
			
		||||
    'bash': Bash(),
 | 
			
		||||
    'fish': Fish(),
 | 
			
		||||
    'zsh': Zsh(),
 | 
			
		||||
    '-csh': Tcsh(),
 | 
			
		||||
    'csh': Tcsh(),
 | 
			
		||||
    'tcsh': Tcsh()})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _get_shell():
 | 
			
		||||
    try:
 | 
			
		||||
        shell = Process(os.getpid()).parent().cmdline()[0]
 | 
			
		||||
        shell = Process(os.getpid()).parent().name()
 | 
			
		||||
    except TypeError:
 | 
			
		||||
        shell = Process(os.getpid()).parent.cmdline[0]
 | 
			
		||||
    return shells[os.path.basename(shell)]
 | 
			
		||||
        shell = Process(os.getpid()).parent.name
 | 
			
		||||
    return shells[shell]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def from_shell(command):
 | 
			
		||||
@@ -183,3 +209,7 @@ def put_to_history(command):
 | 
			
		||||
 | 
			
		||||
def and_(*commands):
 | 
			
		||||
    return _get_shell().and_(*commands)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_aliases():
 | 
			
		||||
    return list(_get_shell().get_aliases().keys())
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
from functools import wraps
 | 
			
		||||
import os
 | 
			
		||||
import pickle
 | 
			
		||||
import six
 | 
			
		||||
from .types import Command
 | 
			
		||||
 | 
			
		||||
@@ -62,3 +63,18 @@ def sudo_support(fn):
 | 
			
		||||
        else:
 | 
			
		||||
            return result
 | 
			
		||||
    return wrapper
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def memoize(fn):
 | 
			
		||||
    """Caches previous calls to the function."""
 | 
			
		||||
    memo = {}
 | 
			
		||||
 | 
			
		||||
    @wraps(fn)
 | 
			
		||||
    def wrapper(*args, **kwargs):
 | 
			
		||||
        key = pickle.dumps((args, kwargs))
 | 
			
		||||
        if key not in memo:
 | 
			
		||||
            memo[key] = fn(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        return memo[key]
 | 
			
		||||
 | 
			
		||||
    return wrapper
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user