diff --git a/ext_open/ext_open.py b/ext_open/ext_open.py index 0868152..987a1ac 100644 --- a/ext_open/ext_open.py +++ b/ext_open/ext_open.py @@ -3,6 +3,7 @@ import searcher import opener import argparse from util import picker +import pyperclip parser = argparse.ArgumentParser(prog='ext-open',prefix_chars=prefix_char) for opt in prefix: parser.add_argument(*opt.pop('ostring'),**opt) @@ -25,4 +26,19 @@ if __name__ == '__main__': matches = s.exact(args.query) else: matches = s.search(args.query,args.regex) - row = picker(matches \ No newline at end of file + if len(matches) == 0: + print('No matches') + quit() + if len(matches) == 1: + row = matches[0] + else: + row = picker(matches + path = row[-1] + if args.path: + print(path) + pyperclip.copy(path) + else: + if args.ext == 'exe': + pass #TODO + else: + pass #TODO \ No newline at end of file diff --git a/ext_open/opener.py b/ext_open/opener.py index b152aca..fe55797 100644 --- a/ext_open/opener.py +++ b/ext_open/opener.py @@ -5,7 +5,6 @@ import shlex import subprocess EDITOR_PATH = config.getpath('global','editor') -EXT = re.compile(r'\.\w+$') def get_ext(file): ext = os.path.splitext(file)[-1] if ext != '': @@ -34,7 +33,7 @@ def standalone(path,params=[],windowless =False,cwd = None): cmd = cmd = subprocess.list2cmdline([path]+params).replace('"','~') subprocess.check_output(['cscript.exe',path2windowless,cmd,cwd]) else: - + subprocess.Popen([path]+params,cwd=cwd) def dependant(path,params=[],mode = 'execute',cwd = None): diff --git a/ext_open/opts.json b/ext_open/opts.json new file mode 100644 index 0000000..9500e2e --- /dev/null +++ b/ext_open/opts.json @@ -0,0 +1,95 @@ +{ + "bools": [ + { + "action": "store_true", + "ostring": ["+i", "++case-insensitive"] + }, + { + "action": "store_true", + "help": "Search for exact application name", + "ostring": ["+E", "++exact"] + }, + { + "action": "store_true", + "help": "Use regex to search", + "ostring": ["+r", "++regex"] + }, + { + "action": "store_true", + "help": "Prints path instead of opening application", + "ostring": ["+p", "++path"] + }, + { + "action": "store_true", + "help": "Open in editor", + "ostring": ["+e", "++edit"] + } + ], + "exe": [ + { + "action": "store_true", + "help": "Open windowless", + "ostring": ["+w", "++windowless"] + }, + { + "help": "Directory to start in", + "metavar": "dir", + "ostring": ["+d", "++start-in"] + }, + { + "help": "Query to search for", + "ostring": ["query"] + }, + { + "action": "store", + "help": "Command-line args", + "nargs": "*", + "ostring": ["arg"] + } + ], + "exts": [ + { + "ext": "py", + "opts": [] + }, + { + "ext": "java", + "opts": [] + } + ], + "other": [ + { + "help": "Directory to start in", + "metavar": "dir", + "ostring": ["+d", "++start-in"] + }, + { + "help": "Query to search for", + "ostring": ["query"] + }, + { + "action": "store", + "help": "Command-line args", + "nargs": "*", + "ostring": ["arg"] + } + ], + "prefix": [ + { + "action": "store_true", + "help": "Update the listings for ext", + "ostring": ["+u", "++update"] + }, + { + "help": "Trim backup history, either all or N updates", + "metavar": "(all | N)", + "ostring": ["+t", "++trim-history"] + }, + { + "action": "store_true", + "help": "updates the modified times for paths in config\nuseful for ensuring all new files are found", + "ostring": ["+m", "++update-modified-times"] + } + ], + "prefix_char": "+" +} diff --git a/ext_open/opts.py b/ext_open/opts.py index 437f22c..c7067a7 100644 --- a/ext_open/opts.py +++ b/ext_open/opts.py @@ -1,53 +1,87 @@ -prefix_char = '+' -prefix = [ - { - 'ostring':['+u','++update'], - 'help': 'Update the listings for ext', - 'action':'store_true', - }, - { - 'ostring':['+t','++trim-history'], - 'help': 'Trim backup history, either all or N updates', - 'metavar': '(all | N)' - }, - { - 'ostring':['+m','++update-modified-times'], - 'action':'store_true', - 'help': '''updates the modified times for paths in config -useful for ensuring all new files are found''', - }, -] -bools = [ - {'action': 'store_true', 'ostring': ['+i', '++case-insensitive']}, - {'action': 'store_true', 'help': 'Search for exact application name', 'ostring': ['+E', '++exact']}, - {'action': 'store_true', 'help': 'Use regex to search', 'ostring': ['+r', '++regex']}, - {'action': 'store_true', 'help': 'Prints path instead of opening application', 'ostring': ['+p', '++path']}, - {'action': 'store_true', 'help': 'Open in editor', 'ostring': ['+e', '++edit']}, - ] - -other = [ - {'help': 'Directory to start in', 'ostring': ['+d', '++start-in'],'metavar':'dir'}, - {'ostring':['query'],'help':'Query to search for'}, - {'action': 'store', 'help': 'Command-line args', 'nargs': '*', 'ostring': ['arg']}, - ] +import json +import os +import copy +class ArgumentHandler: + def __init__(self,args_file): + + self.args_file = args_file + self.exts = set() + + with open(self.args_file) as file: + self.data = json.load(file) + + for _ext in self.data['exts']: + + ext = _ext['ext'] + self.exts.add(ext) + args = _ext['opts'] + + self.set(ext,ArgumentContainer(args)) + + for key,value in self.data.items(): + if key != 'exts' and isinstance(value,list): + self.set(key,ArgumentContainer(value)) + + self.prefix_char = self.data['prefix_char'] + + def get(self,item): + return self.__dict__[item] + + def set(self,item,value): + self.__dict__[item] = value + + def save(self): + data = [] + for ext in self.exts: + data.append({'ext':ext,'opts':self.get(ext).to_dict()}) + valid = set(self.__dict__.keys()) - self.exts + valid -= {'self.args_file'} + for key in valid: + value = self.get(key) + if isinstance(value,str): + self.data[key] = value + elif isinstance(value,ArgumentContainer): + self.data[key] = value.to_dict() + + def add_ext(self,ext): + self.set(ext,ArgumentContainer([])) + self.exts.add(ext) + + def commit(self): + backup_name = self.args_file + '.bak' + if os.path.exists(backup_name): + os.remove(backup_name) + os.copy(self.args_file,self.backup_name) + with open(self.args_file,'w') as file: + json.dump(self.data,file) + + def revert(self): + backup_name = self.args_file + '.bak' + if os.path.exists(backup_name): + os.remove(self.args_file) + os.rename(backup_name,self.args_file) + +class ArgumentContainer: + def __init__(self,l_of_args): + self.args = l_of_args + def add_argument(self,*args,**kwargs): + new_arg = { + 'ostring': args, + } + new_arg.update(kwargs) + self.args.append(new_arg) + + def to_dict(self): + return copy.deepcopy(self.args) + + def __str__(self): + ostrings = (opt['ostring'] for opt in self.args) + f_ostrings = ('({})'.format(', '.join(ostring)) for ostring in ostrings) + return '[{}]'.format(', '.join(f_ostrings)) + + def __repr__(self): + return str(self) + +cur = ArgumentHandler('opts.json') +globals().update(cur.data) common = bools+other -exts = [ - { - 'ext' : 'py', - 'opts' : [] - }, - { - 'ext' : 'java', - 'opts': [] - } -] -exe = [ - { - 'ostring':['+w','++windowless'], - 'help': 'Open windowless', - 'action': 'store_true', - }, - {'help': 'Directory to start in', 'ostring': ['+d', '++start-in'],'metavar':'dir'}, - {'ostring':['query'],'help':'Query to search for'}, - {'action': 'store', 'help': 'Command-line args', 'nargs': '*', 'ostring': ['arg']}, -] diff --git a/ext_open/searcher.py b/ext_open/searcher.py index 1e24c14..9fe6f8a 100644 --- a/ext_open/searcher.py +++ b/ext_open/searcher.py @@ -76,7 +76,7 @@ class Searcher: else: self.cur.execute('SELECT name,fullpath FROM {} WHERE INSIDE(?,name,?)=1;'.format(self.ext),[query,self.case]) return self.cur.fetchall() - + def exact(self,query): self.cur.execute('SELECT name,fullpath FROM {} WHERE name=?'.format(self.ext),[query]) return self.cur.fetchall() diff --git a/ext_open/util.py b/ext_open/util.py index df32a48..a3ab663 100644 --- a/ext_open/util.py +++ b/ext_open/util.py @@ -3,7 +3,8 @@ import datetime from pydoc import pager import os import re - +from config import config +windows_path = re.compile(r"[a-zA-Z]\:(\\[^/\\?\%\*\:\|\"\<\>]+)+") # https://stackoverflow.com/a/827397 def is_admin(): return windll.shell32.IsUserAnAdmin() != 0 @@ -33,7 +34,7 @@ def no_parents(paths): paths.append(path) return ret -def abbreviate(path,n=72): +def abbreviate(path,n=72,min_path = 2): if len(path) > n: elements = [] b = os.path.basename(path) @@ -47,19 +48,30 @@ def abbreviate(path,n=72): l = len(elements) prefix = elements[:l//2] postfix = elements[l//2:] - - while len(os.path.join(*prefix,'...',*postfix)) + res = os.path.join(*prefix,'...',*postfix) + while len(res) > n and (len(prefix)> min_path or len(postfix) > min_path): + if len(postfix) > len(prefix): + postfix = postfix[1:] + else: + prefix = prefix[:-1] + res = os.path.join(*prefix,'...',*postfix) + return res else: return path - + def str_coerce(s,**kwargs): if isinstance(s,datetime.datetime): return s.strftime(kwargs['datetime_format']) + elif isinstance(s,(int,float)): + return str(s) + elif windows_path.match(s): + return abbreviate(s,kwargs['path_length'],kwargs['min_path']) else: return str(s) def print2d(l,datetime_format = "%A, %B %e, %Y %H:%M:%S",seperator= ' | ',spacer = ' ',bottom = '=',l_end = '|',r_end = '|',interactive = False): - l = [[str_coerce(s,datetime_format = datetime_format) for s in row] for row in l] - + n = config.getint('global','path_length',fallback=80) + min_path = config.getint('global','min_path',fallback = 2) + l = [[str_coerce(s,datetime_format = datetime_format,path_length=n,min_path=min_path) for s in row] for row in l] max_col = [] for row in l: for i,col in enumerate(row): @@ -78,7 +90,6 @@ def print2d(l,datetime_format = "%A, %B %e, %Y %H:%M:%S",seperator= ' | ',spacer for row in l: content = seperator.join(col.ljust(max_col[i],spacer if i < len(row)-1 or r_end else ' ') for i,col in enumerate(row)) done.append(fmt_row.format(content = content)) - if bottom: bottom = bottom*len(done[0]) row_sep = ('\n'+bottom+'\n') @@ -97,25 +108,24 @@ def print2d(l,datetime_format = "%A, %B %e, %Y %H:%M:%S",seperator= ' | ',spacer def picker(rows,interactive=False): opts = { 'spacer':' ', - 'seperator':' ', + 'seperator':' | ', 'interactive': interactive, 'bottom':'=', 'l_end':'<', 'r_end':'>', } drows = ( (i,)+row for i,row in enumerate(rows)) - print2d(drows,**opts) + res = print2d(drows,**opts) + if not interactive: + print(res) which = None - while not which: + while which is None: try: which = input('Which one?: ') except KeyboardInterrupt: quit() try: - which = srt_keys[int(which)] + which = int(which) except ValueError: which = None return rows[which] - -if __name__ == "__main__": - abbreviate(os.getcwd()) \ No newline at end of file