@ -0,0 +1,183 @@ | |||
# This file is part of ranger, the console file manager. | |||
# License: GNU GPL version 3, see the file "AUTHORS" for details. | |||
from __future__ import (absolute_import, division, print_function) | |||
from ranger.gui.colorscheme import ColorScheme | |||
from ranger.gui.color import ( | |||
black, blue, cyan, green, magenta, red, white, yellow, default, | |||
normal, bold, reverse, dim, BRIGHT, | |||
default_colors, | |||
) | |||
class Default(ColorScheme): | |||
progress_bar_color = blue | |||
def use(self, context): # pylint: disable=too-many-branches,too-many-statements | |||
fg, bg, attr = default_colors | |||
if context.reset: | |||
return default_colors | |||
elif context.in_browser: | |||
if context.selected: | |||
attr = reverse | |||
else: | |||
attr = normal | |||
if context.empty or context.error: | |||
bg = red | |||
if context.border: | |||
fg = default | |||
if context.media: | |||
if context.image: | |||
fg = yellow | |||
else: | |||
fg = magenta | |||
if context.container: | |||
fg = red | |||
if context.directory: | |||
attr |= bold | |||
fg = blue | |||
fg += BRIGHT | |||
elif context.executable and not \ | |||
any((context.media, context.container, | |||
context.fifo, context.socket)): | |||
attr |= bold | |||
fg = green | |||
fg += BRIGHT | |||
if context.socket: | |||
attr |= bold | |||
fg = magenta | |||
fg += BRIGHT | |||
if context.fifo or context.device: | |||
fg = yellow | |||
if context.device: | |||
attr |= bold | |||
fg += BRIGHT | |||
if context.link: | |||
fg = cyan if context.good else magenta | |||
if context.tag_marker and not context.selected: | |||
attr |= bold | |||
if fg in (red, magenta): | |||
fg = white | |||
else: | |||
fg = red | |||
fg += BRIGHT | |||
if not context.selected and (context.cut or context.copied): | |||
attr |= bold | |||
fg = black | |||
fg += BRIGHT | |||
# If the terminal doesn't support bright colors, use dim white | |||
# instead of black. | |||
if BRIGHT == 0: | |||
attr |= dim | |||
fg = white | |||
if context.main_column: | |||
# Doubling up with BRIGHT here causes issues because it's | |||
# additive not idempotent. | |||
if context.selected: | |||
attr |= bold | |||
if context.marked: | |||
attr |= bold | |||
fg = yellow | |||
if context.badinfo: | |||
if attr & reverse: | |||
bg = magenta | |||
else: | |||
fg = magenta | |||
if context.inactive_pane: | |||
fg = cyan | |||
elif context.in_titlebar: | |||
if context.hostname: | |||
fg = red if context.bad else green | |||
elif context.directory: | |||
fg = blue | |||
elif context.tab: | |||
if context.good: | |||
bg = green | |||
elif context.link: | |||
fg = cyan | |||
attr |= bold | |||
elif context.in_statusbar: | |||
if context.permissions: | |||
if context.good: | |||
fg = cyan | |||
elif context.bad: | |||
fg = magenta | |||
if context.marked: | |||
attr |= bold | reverse | |||
fg = yellow | |||
fg += BRIGHT | |||
if context.frozen: | |||
attr |= bold | reverse | |||
fg = cyan | |||
fg += BRIGHT | |||
if context.message: | |||
if context.bad: | |||
attr |= bold | |||
fg = red | |||
fg += BRIGHT | |||
if context.loaded: | |||
bg = self.progress_bar_color | |||
if context.vcsinfo: | |||
fg = blue | |||
attr &= ~bold | |||
if context.vcscommit: | |||
fg = yellow | |||
attr &= ~bold | |||
if context.vcsdate: | |||
fg = cyan | |||
attr &= ~bold | |||
if context.text: | |||
if context.highlight: | |||
attr |= reverse | |||
if context.in_taskview: | |||
if context.title: | |||
fg = blue | |||
if context.selected: | |||
attr |= reverse | |||
if context.loaded: | |||
if context.selected: | |||
fg = self.progress_bar_color | |||
else: | |||
bg = self.progress_bar_color | |||
if context.vcsfile and not context.selected: | |||
attr &= ~bold | |||
if context.vcsconflict: | |||
fg = magenta | |||
elif context.vcsuntracked: | |||
fg = cyan | |||
elif context.vcschanged: | |||
fg = red | |||
elif context.vcsunknown: | |||
fg = red | |||
elif context.vcsstaged: | |||
fg = green | |||
elif context.vcssync: | |||
fg = green | |||
elif context.vcsignored: | |||
fg = default | |||
elif context.vcsremote and not context.selected: | |||
attr &= ~bold | |||
if context.vcssync or context.vcsnone: | |||
fg = green | |||
elif context.vcsbehind: | |||
fg = red | |||
elif context.vcsahead: | |||
fg = blue | |||
elif context.vcsdiverged: | |||
fg = magenta | |||
elif context.vcsunknown: | |||
fg = red | |||
return fg, bg, attr |
@ -0,0 +1,189 @@ | |||
from ranger.api.commands import Command | |||
class mkcd(Command): | |||
""" | |||
:mkcd <dirname> | |||
Creates a directory with the name <dirname> and enters it. | |||
""" | |||
def execute(self): | |||
from os.path import join, expanduser, lexists | |||
from os import makedirs | |||
import re | |||
dirname = join(self.fm.thisdir.path, expanduser(self.rest(1))) | |||
if not lexists(dirname): | |||
makedirs(dirname) | |||
match = re.search('^/|^~[^/]*/', dirname) | |||
if match: | |||
self.fm.cd(match.group(0)) | |||
dirname = dirname[match.end(0):] | |||
for m in re.finditer('[^/]+', dirname): | |||
s = m.group(0) | |||
if s == '..' or (s.startswith('.') and not self.fm.settings['show_hidden']): | |||
self.fm.cd(s) | |||
else: | |||
## We force ranger to load content before calling `scout`. | |||
self.fm.thisdir.load_content(schedule=False) | |||
self.fm.execute_console('scout -ae ^{}$'.format(s)) | |||
else: | |||
self.fm.notify("file/directory exists!", bad=True) | |||
class toggle_flat(Command): | |||
""" | |||
:toggle_flat | |||
Flattens or unflattens the directory view. | |||
""" | |||
def execute(self): | |||
if self.fm.thisdir.flat == 0: | |||
self.fm.thisdir.unload() | |||
self.fm.thisdir.flat = -1 | |||
self.fm.thisdir.load_content() | |||
else: | |||
self.fm.thisdir.unload() | |||
self.fm.thisdir.flat = 0 | |||
self.fm.thisdir.load_content() | |||
class fzf_select(Command): | |||
""" | |||
:fzf_select | |||
Find a file using fzf. | |||
With a prefix argument to select only directories. | |||
See: https://github.com/junegunn/fzf | |||
""" | |||
def execute(self): | |||
import subprocess | |||
import os | |||
from ranger.ext.get_executables import get_executables | |||
if 'fzf' not in get_executables(): | |||
self.fm.notify('Could not find fzf in the PATH.', bad=True) | |||
return | |||
fd = None | |||
if 'fdfind' in get_executables(): | |||
fd = 'fdfind' | |||
elif 'fd' in get_executables(): | |||
fd = 'fd' | |||
if fd is not None: | |||
hidden = ('--hidden' if self.fm.settings.show_hidden else '') | |||
exclude = "--no-ignore-vcs --exclude '.git' --exclude '*.py[co]' --exclude '__pycache__'" | |||
only_directories = ('--type directory' if self.quantifier else '') | |||
fzf_default_command = '{} --follow {} {} {} --color=always'.format( | |||
fd, hidden, exclude, only_directories | |||
) | |||
else: | |||
hidden = ('-false' if self.fm.settings.show_hidden else r"-path '*/\.*' -prune") | |||
exclude = r"\( -name '\.git' -o -iname '\.*py[co]' -o -fstype 'dev' -o -fstype 'proc' \) -prune" | |||
only_directories = ('-type d' if self.quantifier else '') | |||
fzf_default_command = 'find -L . -mindepth 1 {} -o {} -o {} -print | cut -b3-'.format( | |||
hidden, exclude, only_directories | |||
) | |||
env = os.environ.copy() | |||
env['FZF_DEFAULT_COMMAND'] = fzf_default_command | |||
env['FZF_DEFAULT_OPTS'] = '--height=40% --layout=reverse --ansi --preview="{}"'.format(''' | |||
( | |||
batcat --color=always {} || | |||
bat --color=always {} || | |||
cat {} || | |||
tree -ahpCL 3 -I '.git' -I '*.py[co]' -I '__pycache__' {} | |||
) 2>/dev/null | head -n 100 | |||
''') | |||
fzf = self.fm.execute_command('fzf --no-multi', env=env, | |||
universal_newlines=True, stdout=subprocess.PIPE) | |||
stdout, _ = fzf.communicate() | |||
if fzf.returncode == 0: | |||
selected = os.path.abspath(stdout.strip()) | |||
if os.path.isdir(selected): | |||
self.fm.cd(selected) | |||
else: | |||
self.fm.select_file(selected) | |||
class fzf_rga_documents_search(Command): | |||
""" | |||
:fzf_rga_search_documents | |||
Search in PDFs, E-Books and Office documents in current directory. | |||
Allowed extensions: .epub, .odt, .docx, .fb2, .ipynb, .pdf. | |||
Usage: fzf_rga_search_documents <search string> | |||
""" | |||
def execute(self): | |||
if self.arg(1): | |||
search_string = self.rest(1) | |||
else: | |||
self.fm.notify("Usage: fzf_rga_search_documents <search string>", bad=True) | |||
return | |||
import subprocess | |||
import os.path | |||
from ranger.container.file import File | |||
command="rga '%s' . --rga-adapters=pandoc,poppler | fzf +m | awk -F':' '{print $1}'" % search_string | |||
fzf = self.fm.execute_command(command, universal_newlines=True, stdout=subprocess.PIPE) | |||
stdout, stderr = fzf.communicate() | |||
if fzf.returncode == 0: | |||
fzf_file = os.path.abspath(stdout.rstrip('\n')) | |||
self.fm.execute_file(File(fzf_file)) | |||
class tmsu_tag(Command): | |||
""":tmsu_tag | |||
Tags the current file with tmsu | |||
""" | |||
def execute(self): | |||
cf = self.fm.thisfile | |||
self.fm.run("tmsu tag \"{0}\" {1}".format(cf.basename, self.rest(1))) | |||
class YankContent(Command): | |||
""" | |||
Copy the content of image file and text file with xclip | |||
""" | |||
def execute(self): | |||
import os | |||
import subprocess | |||
from ranger.container.file import File | |||
from ranger.ext.get_executables import get_executables | |||
if 'xclip' not in get_executables(): | |||
self.fm.notify('xclip is not found.', bad=True) | |||
return | |||
arg = self.rest(1) | |||
if arg: | |||
if not os.path.isfile(arg): | |||
self.fm.notify('{} is not a file.'.format(arg)) | |||
return | |||
file = File(arg) | |||
else: | |||
file = self.fm.thisfile | |||
if not file.is_file: | |||
self.fm.notify('{} is not a file.'.format(file.relative_path)) | |||
return | |||
relative_path = file.relative_path | |||
cmd = ['xclip', '-selection', 'clipboard'] | |||
if not file.is_binary(): | |||
with open(file.path, 'rb') as fd: | |||
subprocess.check_call(cmd, stdin=fd) | |||
elif file.image: | |||
cmd += ['-t', file.mimetype, file.path] | |||
subprocess.check_call(cmd) | |||
self.fm.notify('Content of {} is copied to x clipboard'.format(relative_path)) | |||
else: | |||
self.fm.notify('{} is not an image file or a text file.'.format(relative_path)) | |||
def tab(self, tabnum): | |||
return self._tab_directory_content() |
@ -0,0 +1 @@ | |||
__pycache__ |
@ -0,0 +1,43 @@ | |||
import os | |||
from ranger.api.commands import * | |||
from ranger.core.loader import CommandLoader | |||
class compress(Command): | |||
def execute(self): | |||
""" Compress marked files to current directory """ | |||
cwd = self.fm.thisdir | |||
marked_files = cwd.get_selection() | |||
if not marked_files: | |||
return | |||
def refresh(_): | |||
cwd = self.fm.get_directory(original_path) | |||
cwd.load_content() | |||
original_path = cwd.path | |||
# Parsing arguments line | |||
parts = self.line.strip().split() | |||
if len(parts) > 1: | |||
au_flags = [' '.join(parts[1:])] | |||
else: | |||
au_flags = [os.path.basename(self.fm.thisdir.path) + '.zip'] | |||
# Making description line | |||
files_num = len(marked_files) | |||
files_num_str = str(files_num) + ' objects' if files_num > 1 else '1 object' | |||
descr = "Compressing " + files_num_str + " -> " + os.path.basename(au_flags[0]) | |||
# Creating archive | |||
obj = CommandLoader(args=['apack'] + au_flags + \ | |||
[os.path.relpath(f.path, cwd.path) for f in marked_files], descr=descr, read=True) | |||
obj.signal_bind('after', refresh) | |||
self.fm.loader.add(obj) | |||
def tab(self, tabnum): | |||
""" Complete with current folder name """ | |||
extension = ['.zip', '.tar.gz', '.rar', '.7z'] | |||
return ['compress ' + os.path.basename(self.fm.thisdir.path) + ext for ext in extension] |
@ -0,0 +1,33 @@ | |||
import os | |||
import tarfile | |||
from gnupg import GPG | |||
from ranger.api.commands import Command | |||
class decrypt(Command): | |||
""":decrypts | |||
Decrypts a file with gpg or a directory by extracting a tar file and decrypting it | |||
passing true as the false flag will not delete the origin gpg file | |||
""" | |||
def execute(self): | |||
gpg = GPG(gnupghome=os.path.join(os.path.expanduser('~'), '.gnupg')) | |||
paths = [os.path.basename(f.path) for f in self.fm.thistab.get_selection()] | |||
for p in [p for p in paths if p.endswith('gpg')]: | |||
with open(p, 'rb') as enc: | |||
dec_b = gpg.decrypt_file(enc) | |||
out_fname = os.path.splitext(p)[0] | |||
with open(out_fname, 'wb+') as dec_f: | |||
dec_f.write(dec_b.data) | |||
if self.arg(1) != 'true': | |||
os.remove(p) | |||
if tarfile.is_tarfile(out_fname): | |||
tarfile.open(out_fname).extractall(path='.') | |||
os.remove(out_fname) |
@ -0,0 +1 @@ | |||
Subproject commit e4c849867be272a9f4d092ecf45c8f8788bfe4c9 |
@ -0,0 +1,55 @@ | |||
import os | |||
import tarfile | |||
from gnupg import GPG | |||
from ranger.api.commands import Command | |||
from subprocess import run | |||
class encrypt(Command): | |||
""":encrypt | |||
Encrypts a file or dir (as a tar.gz) with the default gpg key | |||
""" | |||
def tardir(self, path): | |||
""":tardir | |||
tars a directory into a dir of the same name appended with .tar.gz | |||
returns the name of the tarfile | |||
""" | |||
output_path = path + '.tar.gz' | |||
with tarfile.open(output_path, "w:gz") as tar_handle: | |||
for root, dirs, files in os.walk(path): | |||
for file in files: | |||
tar_handle.add(os.path.join(root, file)) | |||
return output_path | |||
def execute(self): | |||
gpg_home = os.path.join(os.path.expanduser('~'), '.gnupg/') | |||
default_recpipient = os.environ['DEFAULT_RECIPIENT'] | |||
if not default_recpipient: | |||
self.fm.notify('DEFAULT_RECIPIENT environment variable must be set') | |||
return | |||
gpg = GPG(gpgbinary='/usr/bin/gpg', gnupghome=gpg_home) | |||
paths = [os.path.basename(f.path) for f in self.fm.thistab.get_selection()] | |||
for p in paths: | |||
if os.path.isdir(p): | |||
new_p = self.tardir(p) | |||
run(['rm', '-rf', p]) | |||
p = new_p | |||
with open(p, 'rb') as f: | |||
enc = gpg.encrypt_file(f, default_recpipient) | |||
with open(p + '.gpg', 'wb+') as out: | |||
out.write(enc.data) | |||
if os.path.isfile(p): | |||
os.remove(p) |
@ -0,0 +1,85 @@ | |||
import os | |||
from ranger.api.commands import * | |||
from ranger.core.loader import CommandLoader | |||
class extract(Command): | |||
def execute(self): | |||
"""Extract copied files to current directory or directory | |||
specified in a command line | |||
""" | |||
cwd = self.fm.thisdir | |||
copied_files = cwd.get_selection() | |||
if not copied_files: | |||
return | |||
def refresh(_): | |||
cwd = self.fm.get_directory(original_path) | |||
cwd.load_content() | |||
one_file = copied_files[0] | |||
cwd = self.fm.thisdir | |||
original_path = cwd.path | |||
line_args = self.line.split()[1:] | |||
if line_args: | |||
extraction_dir = os.path.join(cwd.path, "".join(line_args)) | |||
os.makedirs(extraction_dir, exist_ok=True) | |||
flags = ['-X', extraction_dir] | |||
flags += ['-e'] | |||
else: | |||
flags = ['-X', cwd.path] | |||
flags += ['-e'] | |||
self.fm.copy_buffer.clear() | |||
self.fm.cut_buffer = False | |||
if len(copied_files) == 1: | |||
descr = "Extracting: " + os.path.basename(one_file.path) | |||
else: | |||
descr = "Extracting files from: " + os.path.basename(one_file.dirname) | |||
obj = CommandLoader(args=['aunpack'] + flags \ | |||
+ [f.path for f in copied_files], descr=descr, read=True) | |||
obj.signal_bind('after', refresh) | |||
self.fm.loader.add(obj) | |||
class extract_to_dirs(Command): | |||
def execute(self): | |||
""" Extract copied files to a subdirectories """ | |||
cwd = self.fm.thisdir | |||
original_path = cwd.path | |||
copied_files = cwd.get_selection() | |||
if not copied_files: | |||
return | |||
def refresh(_): | |||
cwd = self.fm.get_directory(original_path) | |||
cwd.load_content() | |||
def make_flags(fn): | |||
fn_wo_ext = os.path.basename(os.path.splitext(fn)[0]) | |||
flags = ['-X', fn_wo_ext] | |||
return flags | |||
one_file = copied_files[0] | |||
self.fm.copy_buffer.clear() | |||
self.fm.cut_buffer = False | |||
# Making description line | |||
if len(copied_files) == 1: | |||
descr = "Extracting: " + os.path.basename(one_file.path) | |||
else: | |||
descr = "Extracting files from: " + os.path.basename(one_file.dirname) | |||
# Extracting files | |||
for f in copied_files: | |||
obj = CommandLoader( | |||
args=['aunpack'] + make_flags(f.path) + [f.path], | |||
descr=descr, read=True | |||
) | |||
obj.signal_bind('after', refresh) | |||
self.fm.loader.add(obj) |
@ -0,0 +1,29 @@ | |||
import ranger.api | |||
from ranger.api.commands import * | |||
from os import getenv | |||
import re | |||
class z(Command): | |||
""":z | |||
Uses .z file to set the current directory. | |||
""" | |||
def execute(self): | |||
# location of .z file | |||
z_loc = getenv("_Z_DATA") or getenv("HOME")+"/.z" | |||
with open(z_loc,"r") as fobj: | |||
flists = fobj.readlines() | |||
# user given directory | |||
req = re.compile(".*".join(self.args[1:]),re.IGNORECASE) | |||
directories = [] | |||
for i in flists: | |||
if req.search(i): | |||
directories.append(i.split("|")[0]) | |||
try: | |||
# smallest(length) directory will be the directory required | |||
self.fm.execute_console("cd " + min(directories,key=lambda x: len(x))) | |||
except Exception as e: | |||
raise Exception("Directory not found") |