├── .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 |
--------------------------------------------------------------------------------