├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── push-test.yml │ └── python-publish.yml ├── LICENSE ├── MANIFEST.in ├── README.md ├── requirements.txt ├── setup.py └── xontrib └── onepath.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.xsh text linguist-language=Python 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | 2 | # These are supported funding model platforms 3 | 4 | #github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 5 | #patreon: xonssh # Replace with a single Patreon username 6 | #open_collective: # Replace with a single Open Collective username 7 | #ko_fi: # Replace with a single Ko-fi username 8 | #tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 9 | #community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 10 | #liberapay: # Replace with a single Liberapay username 11 | #issuehunt: # Replace with a single IssueHunt username 12 | #otechie: # Replace with a single Otechie username 13 | custom: ['https://github.com/anki-code', 'https://www.buymeacoffee.com/xxh', 'https://github.com/xonsh/xonsh#the-xonsh-shell-community'] 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/push-test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: push 4 | 5 | jobs: 6 | deploy: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - name: Set up Python 11 | uses: actions/setup-python@v2 12 | with: 13 | python-version: '3.8' 14 | - name: Install xonsh 15 | run: pip install xonsh 16 | - name: Install xontrib 17 | run: pip install . 18 | - name: Test 19 | run: xonsh -c 'xontrib load onepath' 20 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | deploy: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up Python 18 | uses: actions/setup-python@v2 19 | with: 20 | python-version: '3.x' 21 | - name: Install dependencies 22 | run: | 23 | python -m pip install --upgrade pip 24 | pip install setuptools wheel twine 25 | - name: Build and publish 26 | env: 27 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 28 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 29 | run: | 30 | python setup.py sdist bdist_wheel 31 | twine upload dist/* 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2020, anki-code 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include LICENSE 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | When you click to a file or folder in graphical OS they will be opened in associated app. The xontrib-onepath brings the same logic to the xonsh shell. Type the filename or path without preceding command and an associated action will be executed. The actions are customizable. 3 |

4 | 5 |

6 | If you like the idea click ⭐ on the repo and tweet. 7 |

8 | 9 | ## Install 10 | ```bash 11 | xpip install -U xontrib-onepath 12 | echo 'xontrib load onepath' >> ~/.xonshrc 13 | # Reload xonsh 14 | ``` 15 | 16 | ## Examples 17 | ```bash 18 | # Run: # Action that will be executed: 19 | / # cd / 20 | executable_script # ./executable_script 21 | .xonshrc # vim ~/.xonshrc 22 | ~/Downloads/logo.png # xdg-open ~/Downloads/logo.png 23 | git # git 24 | ./git # cd ./git 25 | ``` 26 | 27 | ## Default actions 28 | If typed command is a registered name (i.e. `git`) and `which` command returns the path (i.e. `/usr/bin/git`) it will be executed. 29 | In other case the typed path will be used to make action. Default actions: 30 | 31 | | Priority | Type | Action | 32 | |---|---|---| 33 | | 1 | `` | `cd` | 34 | | 2 | `` | `` | 35 | | 3 | `text/` | `vim`| 36 | | 4 | `image/` | `xdg-open` | 37 | 38 | #### Help to add more types and best default actions. PRs are welcome! 39 | 40 | You can add platform and tool depend actions: 41 | 42 | ```xsh 43 | import shutil as _shutil 44 | from xonsh.platform import ON_LINUX #, ON_DARWIN, ON_WINDOWS, ON_WSL, IN_APPIMAGE, ON_CYGWIN, ON_MSYS, ON_POSIX, ON_FREEBSD, ON_DRAGONFLY, ON_NETBSD, ON_OPENBSD 45 | 46 | xontrib load onepath 47 | 48 | if ON_LINUX and _shutil.which('nvim'): 49 | $XONTRIB_ONEPATH_ACTIONS['text/'] = 'nvim' 50 | ``` 51 | 52 | ## File types 53 | 54 | | # | Type | Example | 55 | |---|---------|----------| 56 | | 1 | Full path to file. | `~/.xonshrc` | 57 | | 2 | File name. | `file.txt` | 58 | | 3 | File extension. | `*.txt` | 59 | | 4 | MIME type/subtype and extension. | `text/plain.txt` | 60 | | 5 | MIME type/subtype. | `text/plain` | 61 | | 6 | MIME type. | `text/` | 62 | | 7 | Any file. | `` (constant) | 63 | | 8 | Any executable file. | `` (constant)| 64 | | 9 | Any directory. | `` (constant) | 65 | | 10| Any file or directory. | `*` (constant) | 66 | 67 | To get MIME type for the file run `file --mime-type --brief `. 68 | 69 | ## Examples of actions 70 | 71 | Use `XONTRIB_ONEPATH_ACTIONS` environment variable to add new actions. 72 | If you need more complex actions use [callable xonsh aliases](https://xon.sh/tutorial.html#callable-aliases). 73 | 74 | ### Simple actions 75 | ```python 76 | $XONTRIB_ONEPATH_ACTIONS['text/'] = 'vim' # Use vim to open any text file 77 | $XONTRIB_ONEPATH_ACTIONS['.xonshrc'] = 'vim' # vim for `.xonshrc` file 78 | $XONTRIB_ONEPATH_ACTIONS['*.log'] = 'tail' # tail for text type *.log files 79 | $XONTRIB_ONEPATH_ACTIONS['text/plain.txt'] = 'less' # less for plain text *.txt files 80 | $XONTRIB_ONEPATH_ACTIONS[''] = 'ls' # list the files in the directory 81 | $XONTRIB_ONEPATH_ACTIONS['application/zip'] = 'als' # list files in zip file using atool 82 | ``` 83 | 84 | #### Run xsh regardless the execution permissions 85 | 86 | ```python 87 | $XONTRIB_ONEPATH_ACTIONS = {'*.xsh':'xonsh', **$XONTRIB_ONEPATH_ACTIONS} # Insert on top 88 | xontrib load onepath 89 | script.xsh # the same as: chmod +x script.xsh && ./script.xsh 90 | ``` 91 | 92 | #### View CSV tables using pandas 93 | ```python 94 | import pandas as pd 95 | def _view_csv_with_pandas(args): 96 | print(pd.read_csv(args[0])) 97 | 98 | aliases['view_csv_with_pandas'] = _view_csv_with_pandas 99 | del _view_csv_with_pandas 100 | 101 | $XONTRIB_ONEPATH_ACTIONS['application/csv'] = 'view_csv_with_pandas' 102 | ``` 103 | 104 | #### cd & ls 105 | ```python 106 | def _cdls(args): 107 | cd @(args[0]) 108 | if int($(ls | wc -l).strip()) < 100: 109 | ls --group-directories-first -a --color 110 | aliases['cdls'] = _cdls 111 | del _cdls 112 | 113 | $XONTRIB_ONEPATH_ACTIONS[''] = 'cdls' 114 | ``` 115 | 116 | ## Debug 117 | 118 | To enable debug mode run `$XONTRIB_ONEPATH_DEBUG = True`. 119 | 120 | ## Known issues 121 | 122 | ### ImportError: failed to find libmagic 123 | 124 | Install libmagic: `brew install libmagic`. 125 | 126 | ### NTFS in Linux: all files have execute permission 127 | If you mount NTFS partition with default permissions then all files will have execute permission 128 | and `onepath` will execute them instead of action. The right way 129 | is to [change default `/etc/fstab` settings](https://askubuntu.com/questions/113733/how-do-i-correctly-mount-a-ntfs-partition-in-etc-fstab). 130 | Example: 131 | ```bash 132 | sudo umount /d 133 | sudo mount -o uid=1000,gid=1000,dmask=027,fmask=137 /d 134 | ls -la /d 135 | ``` 136 | 137 | ## Links 138 | * This package is the part of [ergopack](https://github.com/anki-code/xontrib-ergopack) - the pack of ergonomic xontribs. 139 | * This package was created with [xontrib template](https://github.com/xonsh/xontrib-template). 140 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | xonsh 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | try: 5 | with open('README.md', 'r', encoding='utf-8') as fh: 6 | long_description = fh.read() 7 | except (IOError, OSError): 8 | long_description = '' 9 | 10 | setup( 11 | name='xontrib-onepath', 12 | version='0.4.0', 13 | license='BSD', 14 | author='anki', 15 | author_email='author@example.com', 16 | description="Associate files with app or alias and run it without preceding commands in xonsh shell.", 17 | long_description=long_description, 18 | long_description_content_type='text/markdown', 19 | python_requires='>=3.6', 20 | install_requires=['python-magic'], 21 | packages=['xontrib'], 22 | package_dir={'xontrib': 'xontrib'}, 23 | package_data={'xontrib': ['*.py']}, 24 | platforms='any', 25 | url='https://github.com/anki-code/xontrib-onepath', 26 | project_urls={ 27 | "Documentation": "https://github.com/anki-code/xontrib-onepath/blob/master/README.md", 28 | "Code": "https://github.com/anki-code/xontrib-onepath", 29 | "Issue tracker": "https://github.com/anki-code/xontrib-onepath/issues", 30 | }, 31 | classifiers=[ 32 | 'Environment :: Console', 33 | 'Intended Audience :: End Users/Desktop', 34 | 'Operating System :: OS Independent', 35 | 'Programming Language :: Python', 36 | "Programming Language :: Unix Shell", 37 | "Topic :: System :: Shells", 38 | "Topic :: System :: System Shells", 39 | "Topic :: Terminals", 40 | "Topic :: System :: Networking", 41 | "License :: OSI Approved :: BSD License" 42 | ] 43 | ) 44 | -------------------------------------------------------------------------------- /xontrib/onepath.py: -------------------------------------------------------------------------------- 1 | """Associate files and directories with app or alias and run it without preceding commands in xonsh shell. """ 2 | 3 | import os, shlex 4 | from magic import from_file as mime 5 | from pathlib import Path 6 | from shutil import which 7 | from pprint import pprint 8 | 9 | _env_actions = __xonsh__.env.get('XONTRIB_ONEPATH_ACTIONS') 10 | if not _env_actions or type(_env_actions) != dict: 11 | __xonsh__.env['XONTRIB_ONEPATH_ACTIONS'] = { 12 | '': 'cd', 13 | '': '', 14 | } 15 | 16 | 17 | def _get_subproc_output(cmds, debug=False): 18 | cmds = [str(c) for c in cmds] 19 | if not debug: 20 | cmds += ['2>', '/dev/null'] 21 | result = __xonsh__.subproc_captured_object(cmds) 22 | result.rtn # workaround https://github.com/xonsh/xonsh/issues/3394 23 | return result.output 24 | 25 | 26 | @events.on_transform_command 27 | def onepath(cmd, **kw): 28 | try: 29 | args = shlex.split(cmd) 30 | except: 31 | return cmd 32 | if len(args) != 1 or which(args[0]) or args[0] in aliases: 33 | return cmd 34 | 35 | debug = __xonsh__.env.get('XONTRIB_ONEPATH_DEBUG', False) 36 | path = Path(args[0]).expanduser().resolve() 37 | if not path.exists(): 38 | return cmd 39 | 40 | if __xonsh__.env.get('XONTRIB_ONEPATH_SUBPROC_FILE', False): 41 | file_type = _get_subproc_output(['file', '--mime-type', '--brief', path], debug).strip() 42 | else: 43 | try: 44 | file_type = mime(str(path), mime=True) 45 | except IsADirectoryError: 46 | file_type = 'inode/directory' 47 | 48 | path_suffix = path.suffix 49 | file_types = { 50 | 'full_path': str(path), 51 | 'path_filename': None if path.is_dir() else path.name, 52 | 'path_suffix_key': '*' + path.suffix, 53 | 'file_type_suffix': file_type + path_suffix, 54 | 'file_type': '' if file_type == 'inode/directory' else file_type, 55 | 'file_type_group': file_type.split('/')[0] + '/' if '/' in file_type else None, 56 | 'file_or_dir': '' if path.is_file() else '', 57 | 'xfile': '' if path.is_file() and os.access(path, os.X_OK) else '', 58 | 'any': '*' 59 | } 60 | 61 | if debug: 62 | print(f'xontrib-onepath: types for {path}:') 63 | pprint(file_types, sort_dicts=False) 64 | 65 | action = None 66 | for k in __xonsh__.env['XONTRIB_ONEPATH_ACTIONS']: 67 | for name, tp in file_types.items(): 68 | if k == tp: 69 | action = __xonsh__.env['XONTRIB_ONEPATH_ACTIONS'][k] 70 | if debug: 71 | print(f'xontrib-onepath: selected action for {path}: name={repr(name)}, type={repr(k)}, action={repr(action)}') 72 | break 73 | if action: 74 | break 75 | 76 | 77 | if action: 78 | if action == '': 79 | return f'{shlex.quote(str(path))}\n' 80 | else: 81 | return f'{action} {shlex.quote(str(path))}\n' 82 | else: 83 | return cmd 84 | --------------------------------------------------------------------------------