first attempt at fzf for find and ls

pull/29/head
deadc0de6 2 years ago
parent 0422ad9229
commit 2176ac6127

@ -27,6 +27,7 @@ CATALOGPATH = '{}.catalog'.format(NAME)
GRAPHPATH = '/tmp/{}.dot'.format(NAME) GRAPHPATH = '/tmp/{}.dot'.format(NAME)
SEPARATOR = '/' SEPARATOR = '/'
WILD = '*' WILD = '*'
FORMATS = ['native', 'csv', 'fzf-native', 'fzf-csv']
BANNER = """ +-+-+-+-+-+-+ BANNER = """ +-+-+-+-+-+-+
|c|a|t|c|l|i| |c|a|t|c|l|i|
@ -37,8 +38,8 @@ USAGE = """
Usage: Usage:
{1} ls [--catalog=<path>] [--format=<fmt>] [-aBCrVSs] [<path>] {1} ls [--catalog=<path>] [--format=<fmt>] [-aBCrVSs] [<path>]
{1} find [--catalog=<path>] [--format=<fmt>] [-aBCbdVsP] [--path=<path>] <term> {1} find [--catalog=<path>] [--format=<fmt>] [-aBCbdVsP] [--path=<path>] [<term>]
{1} tree [--catalog=<path>] [--format=<fmt>] [-aBCVSsH] [<path>] {1} tree [--catalog=<path>] [-aBCVSsH] [<path>]
{1} index [--catalog=<path>] [--meta=<meta>...] [-aBCcfnV] <name> <path> {1} index [--catalog=<path>] [--meta=<meta>...] [-aBCcfnV] <name> <path>
{1} update [--catalog=<path>] [-aBCcfnV] [--lpath=<path>] <name> <path> {1} update [--catalog=<path>] [-aBCcfnV] [--lpath=<path>] <name> <path>
{1} rm [--catalog=<path>] [-BCfV] <storage> {1} rm [--catalog=<path>] [-BCfV] <storage>
@ -174,14 +175,15 @@ def cmd_find(args, noder, top):
startpath = args['--path'] startpath = args['--path']
fmt = args['--format'] fmt = args['--format']
raw = args['--raw-size'] raw = args['--raw-size']
return noder.find_name(top, args['<term>'], script=args['--script'], script = args['--script']
search_for = args['<term>']
return noder.find_name(top, search_for, script=script,
startpath=startpath, directory=directory, startpath=startpath, directory=directory,
parentfromtree=fromtree, fmt=fmt, raw=raw) parentfromtree=fromtree, fmt=fmt, raw=raw)
def cmd_tree(args, noder, top): def cmd_tree(args, noder, top):
path = args['<path>'] path = args['<path>']
fmt = args['--format']
hdr = args['--header'] hdr = args['--header']
raw = args['--raw-size'] raw = args['--raw-size']
@ -192,7 +194,7 @@ def cmd_tree(args, noder, top):
if node: if node:
# print the tree # print the tree
noder.print_tree(node, fmt=fmt, header=hdr, raw=raw) noder.print_tree(top, node, header=hdr, raw=raw)
def cmd_graph(args, noder, top): def cmd_graph(args, noder, top):
@ -240,6 +242,14 @@ def banner():
Logger.out_err("") Logger.out_err("")
def print_supported_formats():
print('"native" : native format')
print('"csv" : CSV format')
print(' {}'.format(Noder.CSV_HEADER))
print('"fzf-native" : fzf with native output for selected entries')
print('"fzf-csv" : fzf with native output for selected entries')
def main(): def main():
args = docopt(USAGE, version=VERSION) args = docopt(USAGE, version=VERSION)
@ -248,15 +258,14 @@ def main():
return True return True
if args['print_supported_formats']: if args['print_supported_formats']:
print('"native": native format') print_supported_formats()
print('"csv" : CSV format')
print(' {}'.format(Noder.CSV_HEADER))
return True return True
# check format # check format
fmt = args['--format'] fmt = args['--format']
if fmt != 'native' and fmt != 'csv': if fmt not in FORMATS:
Logger.err('bad format: {}'.format(fmt)) Logger.err('bad format: {}'.format(fmt))
print_supported_formats()
return False return False
if args['--verbose']: if args['--verbose']:

@ -9,6 +9,7 @@ import os
import anytree import anytree
import shutil import shutil
import time import time
from pyfzf.pyfzf import FzfPrompt
# local imports # local imports
from . import __version__ as VERSION from . import __version__ as VERSION
@ -435,10 +436,10 @@ class Noder:
else: else:
Logger.err('bad node encountered: {}'.format(node)) Logger.err('bad node encountered: {}'.format(node))
def print_tree(self, node, style=anytree.ContRoundStyle(), def print_tree(self, top, node, style=anytree.ContRoundStyle(),
fmt='native', header=False, raw=False): fmt='native', header=False, raw=False):
''' '''
print the tree similar to unix tool "tree" print the tree in different format
@node: start node @node: start node
@style: when fmt=native, defines the tree style @style: when fmt=native, defines the tree style
@fmt: output format @fmt: output format
@ -446,11 +447,16 @@ class Noder:
@raw: print the raw size rather than human readable @raw: print the raw size rather than human readable
''' '''
if fmt == 'native': if fmt == 'native':
# "tree" style
rend = anytree.RenderTree(node, childiter=self._sort_tree) rend = anytree.RenderTree(node, childiter=self._sort_tree)
for pre, fill, node in rend: for pre, fill, node in rend:
self._print_node(node, pre=pre, withdepth=True, raw=raw) self._print_node(node, pre=pre, withdepth=True, raw=raw)
elif fmt == 'csv': elif fmt == 'csv':
# csv output
self._to_csv(node, with_header=header, raw=raw) self._to_csv(node, with_header=header, raw=raw)
elif fmt.startswith('fzf'):
# flat
self._to_fzf(top, node)
def _to_csv(self, node, with_header=False, raw=False): def _to_csv(self, node, with_header=False, raw=False):
'''print the tree to csv''' '''print the tree to csv'''
@ -460,6 +466,41 @@ class Noder:
for _, _, node in rend: for _, _, node in rend:
self._node_to_csv(node, raw=raw) self._node_to_csv(node, raw=raw)
def _fzf_prompt(self, strings):
# prompt with fzf
fzf = FzfPrompt()
selected = fzf.prompt(strings)
return selected
def _to_fzf(self, top, node, fmt):
"""
print node to fzf
@top: top node
@node: node to start with
@fmt: output format for selected nodes
"""
rend = anytree.RenderTree(node, childiter=self._sort_tree)
nodes = dict()
# construct node names list
for _, _, node in rend:
if not node:
continue
parents = self._get_parents(node)
storage = self._get_storage(node)
fullpath = os.path.join(storage.name, parents)
nodes[fullpath] = node
# prompt with fzf
self._fzf_prompt(nodes.keys())
# print the resulting tree
subfmt = fmt.replace('fzf-', '')
for path in paths:
if not path:
continue
if path not in nodes:
continue
node = nodes[path]
self.print_tree(top, node, fmt=subfmt)
def to_dot(self, node, path='tree.dot'): def to_dot(self, node, path='tree.dot'):
'''export to dot for graphing''' '''export to dot for graphing'''
anytree.exporter.DotExporter(node).to_dotfile(path) anytree.exporter.DotExporter(node).to_dotfile(path)
@ -469,25 +510,34 @@ class Noder:
############################################################### ###############################################################
# searching # searching
############################################################### ###############################################################
def find_name(self, root, key, def find_name(self, top, key,
script=False, directory=False, script=False, directory=False,
startpath=None, parentfromtree=False, startpath=None, parentfromtree=False,
fmt='native', raw=False): fmt='native', raw=False):
''' '''
find files based on their names find files based on their names
@top: top node
@key: term to search for
@script: output script @script: output script
@directory: only search for directories @directory: only search for directories
@startpath: node to start with @startpath: node to start with
@parentfromtree: get path from parent instead of stored relpath @parentfromtree: get path from parent instead of stored relpath
@fmt: output format @fmt: output format
@raw: raw size output
''' '''
self._debug('searching for \"{}\"'.format(key)) self._debug('searching for \"{}\"'.format(key))
start = root if not key:
# nothing to search for
return None
start = top
if startpath: if startpath:
start = self.get_node(root, startpath) start = self.get_node(top, startpath)
self.term = key found = anytree.findall(start, filter_=self._callback_find_name(key))
found = anytree.findall(start, filter_=self._find_name) self._debug(f'found {len(found)} node(s)')
paths = []
# compile found nodes
paths = dict()
nodes = []
for f in found: for f in found:
if f.type == self.TYPE_STORAGE: if f.type == self.TYPE_STORAGE:
# ignore storage nodes # ignore storage nodes
@ -495,58 +545,74 @@ class Noder:
if directory and f.type != self.TYPE_DIR: if directory and f.type != self.TYPE_DIR:
# ignore non directory # ignore non directory
continue continue
nodes.append(f)
# print the node if parentfromtree:
if fmt == 'native': paths[self._get_parents(f)] = f
self._print_node(f, withpath=True, else:
paths[f.relpath] = f
if fmt == 'native':
for n in nodes:
self._print_node(n, withpath=True,
withdepth=True, withdepth=True,
withstorage=True, withstorage=True,
recalcparent=parentfromtree, recalcparent=parentfromtree,
raw=raw) raw=raw)
elif fmt == 'csv': elif fmt == 'csv':
self._node_to_csv(f, raw=raw) for n in nodes:
self._node_to_csv(n, raw=raw)
if parentfromtree: elif fmt.startswith('fzf'):
paths.append(self._get_parents(f)) selected = self._fzf_prompt(paths)
else: newpaths = dict()
paths.append(f.relpath) subfmt = fmt.replace('fzf-', '')
for s in selected:
if s not in paths:
continue
newpaths[s] = paths[s]
self.print_tree(top, newpaths[s], fmt=subfmt)
paths = newpaths
if script: if script:
tmp = ['${source}/' + x for x in paths] tmp = ['${source}/' + x for x in paths.keys()]
cmd = 'op=file; source=/media/mnt; $op {}'.format(' '.join(tmp)) cmd = 'op=file; source=/media/mnt; $op {}'.format(' '.join(tmp))
Logger.info(cmd) Logger.info(cmd)
return found return found
def _find_name(self, node): def _callback_find_name(self, term):
'''callback for finding files''' '''callback for finding files'''
if self.term.lower() in node.name.lower(): def find_name(node):
return True if term.lower() in node.name.lower():
return False return True
return False
return find_name
############################################################### ###############################################################
# climbing # climbing
############################################################### ###############################################################
def walk(self, root, path, rec=False, fmt='native', raw=False): def walk(self, top, path, rec=False, fmt='native',
raw=False):
''' '''
walk the tree for ls based on names walk the tree for ls based on names
@root: start node @top: start node
@rec: recursive walk @rec: recursive walk
@fmt: output format @fmt: output format
@raw: print raw size
''' '''
self._debug('walking path: \"{}\"'.format(path)) self._debug('walking path: \"{}\"'.format(path))
r = anytree.resolver.Resolver('name') r = anytree.resolver.Resolver('name')
found = [] found = []
try: try:
found = r.glob(root, path) found = r.glob(top, path)
if len(found) < 1: if len(found) < 1:
# nothing found # nothing found
return [] return []
if rec: if rec:
# print the entire tree # print the entire tree
self.print_tree(found[0].parent, fmt=fmt, raw=raw) self.print_tree(top, found[0].parent, fmt=fmt, raw=raw)
return found return found
# sort found nodes # sort found nodes
@ -558,6 +624,8 @@ class Noder:
withpath=False, withdepth=True, raw=raw) withpath=False, withdepth=True, raw=raw)
elif fmt == 'csv': elif fmt == 'csv':
self._node_to_csv(found[0].parent, raw=raw) self._node_to_csv(found[0].parent, raw=raw)
elif fmt == 'fzf':
pass
# print all found nodes # print all found nodes
for f in found: for f in found:
@ -568,6 +636,8 @@ class Noder:
raw=raw) raw=raw)
elif fmt == 'csv': elif fmt == 'csv':
self._node_to_csv(f, raw=raw) self._node_to_csv(f, raw=raw)
elif fmt == 'fzf':
self._to_fzf(top, f)
except anytree.resolver.ChildResolverError: except anytree.resolver.ChildResolverError:
pass pass
@ -638,6 +708,8 @@ class Noder:
'''get all parents recursively''' '''get all parents recursively'''
if node.type == self.TYPE_STORAGE: if node.type == self.TYPE_STORAGE:
return '' return ''
if node.type == self.TYPE_TOP:
return ''
parent = self._get_parents(node.parent) parent = self._get_parents(node.parent)
if parent: if parent:
return os.sep.join([parent, node.name]) return os.sep.join([parent, node.name])

@ -1,2 +1,3 @@
docopt; python_version >= '3.0' docopt; python_version >= '3.0'
anytree; python_version >= '3.0' anytree; python_version >= '3.0'
pyfzf; python_version >= '3.0'

@ -76,7 +76,7 @@ class TestIndexing(unittest.TestCase):
self.assertTrue(nod.md5 == f4_md5) self.assertTrue(nod.md5 == f4_md5)
# print catalog # print catalog
noder.print_tree(top) noder.print_tree(top, top)
# add some files and directories # add some files and directories
new1 = create_rnd_file(d1, 'newf1') new1 = create_rnd_file(d1, 'newf1')
@ -118,7 +118,7 @@ class TestIndexing(unittest.TestCase):
# print catalog # print catalog
# print(read_from_file(catalogpath)) # print(read_from_file(catalogpath))
noder.print_tree(top) noder.print_tree(top, top)
# explore the top node to find all nodes # explore the top node to find all nodes
self.assertTrue(len(top.children) == 1) self.assertTrue(len(top.children) == 1)

Loading…
Cancel
Save