You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

367 lines
10 KiB

import cmd
import glob
import logging
import os
import re
import readline
import sys
import pkg_resources
from datetime import datetime
from textwrap import dedent
logger = logging.getLogger(__name__)
__version__ = '0.1.11'
DEFAULT_COLORS = {
'1-now': 31, #red
'2-next': 34, #blue
'3-soon': 92, #light green
'4-later': 32, #green
'5-someday': 90, #dark gray
'6-waiting': 95, # light pink?
}
TAG_PATTERN = r'\[(.*)\]'
PRIORITIES = {
'1':'1-now',
'2':'2-next',
'3':'3-soon',
'4':'4-later',
'5':'5-someday',
'6':'6-waiting',
}
class Filename:
def __init__(self, filename):
self.tags = []
tags_found = re.search(TAG_PATTERN, filename)
if tags_found:
self.tags.extend(tags_found.groups()[0].split())
if '1-now' in self.tags:
self._priority = '1-now'
elif '2-next' in self.tags:
self._priority = '2-next'
elif '3-soon' in self.tags:
self._priority = '3-soon'
elif '4-later' in self.tags:
self._priority = '4-later'
elif '5-someday' in self.tags:
self._priority = '5-someday'
elif '6-waiting' in self.tags:
self._priority = '6-waiting'
else:
self._priority = None
self.filename = filename
@property
def colorized(self):
filename = self.filename
for tag in self.tags or []:
color = DEFAULT_COLORS.get(tag, '32')
colorized = f'\x1b[{color}m{tag}\x1b[0m'
filename = filename.replace(tag, colorized)
return filename
@property
def priority(self):
return self._priority
@priority.setter
def priority(self, value):
if self._priority:
head, _, tail = self.filename.rpartition(self._priority)
filename = f'{head}{value}{tail}'
self.tags.remove(self._priority)
else:
head, _, tail = self.filename.rpartition(']')
filename = f'{head} {value}]{tail}'
self._priority = value
self.tags.append(value)
os.rename(self.filename, filename)
self.filename = filename
class Shibboleth(cmd.Cmd):
def __init__(self):
super().__init__()
self.prompt = f'\N{RIGHTWARDS HARPOON WITH BARB UPWARDS}\x1b[34mshibboleth\x1b[0m:{os.getcwd()}\n>'
self.selected = None
readline.set_completion_display_matches_hook(self.display_completion)
readline.set_completer_delims(
readline.get_completer_delims().replace('-', '')
)
self.editor = os.environ.get('EDITOR', 'vim')
self.intro = dedent(f'''
Welcome to Shibboleth {__version__}, the tool designed to be *your*
secret weapon.
Your editor is currently {self.editor}. If you don't like that, you
should change or set your EDITOR environment variable.
''')
def display_completion(self, substitution, matches, longest_match_length):
logger.debug('>>display_completion')
print()
print(' '.join(matches))
print(self.prompt, end='')
print(readline.get_line_buffer(), end='')
sys.stdout.flush()
def complete(self, *args, **kwargs):
logger.debug('>>complete')
logger.debug('%r %r', args, kwargs)
cmd = readline.get_line_buffer().split(None, maxsplit=1)[0]
if cmd in ('sel', 'select', 'e', 'edit'):
pass
#else:
res = super().complete(*args, **kwargs)
logger.debug('Result: %r', res)
return res
def cmdloop(self):
logger.debug('>>cmdloop')
while True:
try:
super().cmdloop()
break
except KeyboardInterrupt:
print()
print('^C caught - use `q` to quit')
def postcmd(self, stop, line):
logger.debug('>>postcmd')
self.prompt = f'\N{RIGHTWARDS HARPOON WITH BARB UPWARDS}\x1b[34mshibboleth\x1b[0m:{os.getcwd()}\n>'
if self.selected:
self.prompt = f'\N{RIGHTWARDS HARPOON WITH BARB UPWARDS}\x1b[34mshibboleth\x1b[0m:{self.selected.colorized}\n>'
return stop
def do_cd(self, line):
logger.debug('>>do_cd')
try:
os.chdir(line)
except Exception as e:
print(e)
def complete_cd(self, text, line, begidx, endidx):
logger.debug('>>complete_cd')
paths = glob.glob(text+'*')
return paths
def do_pls(self, line):
logger.debug('>>do_pls')
'''
Priority list - list files in the folder that have the
specified priority.
'''
files = [Filename(name) for name in os.listdir(os.path.curdir)]
targets = {
'1': '1-now',
'2': '2-next',
'3': '3-soon',
'4': '4-later',
'5': '5-someday',
'6': '6-waiting',
}
if line:
try:
target = targets[line]
except KeyError:
print(f'Unknown priority {line!r}')
target = None
for file_ in files:
if not line or line == '1':
if '1-now' in file_.tags:
print(file_.colorized)
elif target:
if target in file_.tags:
print(file_.colorized)
def do_now(self, line):
logger.debug('>>do_now')
self.do_pls(line='1')
def do_next(self, line):
logger.debug('>>do_next')
self.do_pls(line='2')
def do_soon(self, line):
logger.debug('>>do_soon')
self.do_pls(line='3')
def do_later(self, line):
logger.debug('>>do_later')
self.do_pls(line='4')
def do_someday(self, line):
logger.debug('>>do_someday')
self.do_pls(line='5')
def do_waiting(self, line):
logger.debug('>>do_waiting')
self.do_pls(line='6')
def do_deselect(self, line):
logger.debug('>>do_deselect')
self.selected = None
def do_select(self, line):
logger.debug('>>do_select')
if not line:
self.do_deselect()
else:
if not os.path.isfile(line):
print(f'Unknown file {line!r}')
else:
self.selected = Filename(os.path.abspath(line))
def complete_select(self, text, line, begidx, endidx):
logger.debug('>>complete_select')
paths = glob.glob(text+'*')
logger.debug('Possible paths: %r', paths)
return paths
def complete_edit(self, text, line, begidx, endidx):
logger.debug('>>complete_edit')
return complete_select
def do_priority(self, line):
logger.debug('>>do_priority')
if not self.selected:
print('Select a file first and try again')
else:
try:
self.selected.priority = PRIORITIES[line]
except KeyError:
print(f'Unknown priority {line!r}')
def do_ls(self, line):
logger.debug('>>do_ls')
line = os.path.expanduser(line)
if not line:
files = os.listdir(os.path.curdir)
elif os.path.isdir(line):
files = os.listdir(line)
else:
files = glob.glob(line)
for filename in files:
print(Filename(filename).colorized)
def do_show(self, line):
logger.debug('>>do_show')
if not self.selected or line:
print('Select a file and try again')
else:
filename = self.selected.filename if self.selected else line
print('*'*80)
with open(filename) as f:
print(f.read())
print('*'*80)
def do_edit(self, line):
logger.debug('>>do_edit')
if not (self.selected or line):
print('Select a file and try again')
else:
filename = self.selected.filename if self.selected else line
flags = ''
if self.editor in ('vi', 'vim'):
flags = '-n'
os.system(f'{self.editor} {flags} "{filename}"')
def do_complete(self, line):
logger.debug('>>do_complete')
if not self.selected or line:
print('Select a file and try again')
else:
filename = self.selected.filename if self.selected else line
os.rename(
filename,
os.path.join(
os.path.dirname(filename),
'completed',
os.path.basename(filename),
),
)
self.selected = None
def do_new(self, line):
logger.debug('>>do_new')
if line:
title = line.replace(' ', '-')
else:
title = input('Title: ').strip().replace(' ', '-')
filename = f'{title}[{datetime.now():%Y%m%d~%H%M%S}].md'
self.selected = None
self.do_edit(filename)
self.do_select(filename)
def do_exit(self, line):
logger.debug('>>do_exit')
print('Goodbye!')
return True
def do_EOF(self, line):
logger.debug('>>do_EOF')
print()
return self.do_exit(line)
def do__debug(self, line):
logger.debug('>>do__debug')
import pdb; pdb.set_trace()
def do_log(self, line):
logger.debug('>>do_log')
action, *rest = line.split(None, maxsplit=1)
if action == 'off':
logger.info('Turning logging off')
logger.handlers.clear()
elif action == 'on':
level = getattr(logging, ''.join(rest).upper() or 'DEBUG')
logger.handlers.clear()
h = logging.FileHandler(
'shibboleth.log'
)
h.setLevel(level)
logger.setLevel(level)
logger.addHandler(h)
logger.info('Logging turned on, level - %r', level)
# Aliases
do_p = do_priority
do_e = do_edit
do_sel = do_select
do_quit = do_exit
do_q = do_exit
do_done = do_complete
do_stop = do_deselect
complete_sel = complete_select
complete_e = complete_edit
def colorfy(filename):
logger.debug('>>colorfy')
tags_found = re.search(tag_pattern, filename)
tags = None
if tags_found:
tags = tags_found.groups()[0].split()
for tag in tags:
color = DEFAULT_COLORS.get(tag, '32')
colorized = f'\x1b[{color}m{tag}\x1b[0m'
filename = filename.replace(tag, colorized)
return filename, tags
def run():
# I'll never ever write a song about the shibby
shibby = Shibboleth()
if sys.argv[1:]:
shibby.onecmd(' '.join(sys.argv[1:]))
else:
shibby.cmdloop()
if __name__ == '__main__':
run()