diff --git a/pubs/commands/add_cmd.py b/pubs/commands/add_cmd.py index f83a6ca..f7c371c 100644 --- a/pubs/commands/add_cmd.py +++ b/pubs/commands/add_cmd.py @@ -6,6 +6,8 @@ from .. import repo from .. import paper from .. import templates from .. import apis +from .. import color +from .. import pretty def parser(subparsers): @@ -75,7 +77,7 @@ def command(args): bibdata_raw = apis.doi2bibtex(args.doi) bibdata = rp.databroker.verify(bibdata_raw) if bibdata is None: - ui.error('invalid doi {} or unable to retreive bibfile.'.format(args.doi)) + ui.error('invalid doi {} or unable to retrieve bibfile.'.format(args.doi)) ui.exit(1) # TODO distinguish between cases, offer to open the error page in a webbrowser. # TODO offer to confirm/change citekey @@ -118,8 +120,9 @@ def command(args): if docfile is not None: rp.push_doc(p.citekey, docfile, copy=args.copy) if args.copy: - if ui.input_yn('The file {} has been copied to the pubs repository. Should the original be removed?'.format(docfile)): + if ui.input_yn('{} has been copied into pubs; should the original be removed?'.format(color.dye(docfile, color.bold))): content.remove_file(docfile) + ui.print_('{}\nwas added to pubs.'.format(pretty.paper_oneliner(p))) except ValueError as v: ui.error(v.message) ui.exit(1) diff --git a/pubs/commands/attach_cmd.py b/pubs/commands/attach_cmd.py index fc7ba17..d29eb7e 100644 --- a/pubs/commands/attach_cmd.py +++ b/pubs/commands/attach_cmd.py @@ -1,4 +1,5 @@ from .. import repo +from .. import color from ..configs import config from ..uis import get_ui @@ -30,6 +31,11 @@ def command(args): try: document = args.document document = rp.push_doc(paper.citekey, document, copy=args.copy) + if args.copy: + if ui.input_yn('{} has been copied into pubs; should the original be removed?'.format(color.dye(document, color.bold))): + content.remove_file(docfile) + ui.print_('{} attached to {}'.format(color.dye(document, color.bold), color.dye(paper.citekey, color.citekey))) + except ValueError as v: ui.error(v.message) ui.exit(1) diff --git a/pubs/uis.py b/pubs/uis.py index 441dfb0..64d8062 100644 --- a/pubs/uis.py +++ b/pubs/uis.py @@ -48,12 +48,12 @@ class UI: self._stdout = codecs.getwriter(self.encoding)(_get_raw_stdout(), errors='replace') - def print_(self, *strings): + def print_(self, *strings, **kwargs): """Like print, but rather than raising an error when a character is not in the terminal's encoding's character set, just silently replaces it. """ - print(' '.join(strings), file=self._stdout) + print(' '.join(strings), file=self._stdout, **kwargs) def input(self): try: @@ -63,6 +63,48 @@ class UI: self.exit(1) return data + def input_choice_ng(self, options, option_chars=None, default=None, question=''): + """Ask the user to chose between a set of options. The iser is asked + to input a char corresponding to the option he choses. + + :param options: list of strings + list of options + :param default: int + default if no option is accepted, if None answer is required + :param question: string + :returns: int + the index of the chosen option + """ + char_color = color.bold + option_chars = [s[0] for s in options] + displayed_chars = [c.upper() if i == default else c + for i, c in enumerate(option_chars)] + + if len(set(option_chars)) != len(option_chars): # duplicate chars, char choices are deactivated. #FIXME: should only deactivate ambiguous chars + option_chars = [] + char_color = color.end + + option_str = '/'.join(["{}{}".format(color.dye(c, color.bold), s[1:]) + for c, s in zip(displayed_chars, options)]) + + self.print_('{} {}: '.format(question, option_str), end='') + while True: + answer = self.input() + if answer is None or answer == '': + if default is not None: + return default + else: + try: + return options.index(answer.lower()) + except ValueError: + try: # FIXME options handling !!! + return option_chars.index(answer.lower()) + except ValueError: + pass + self.print_('Incorrect option.', option_str) + + + def input_choice(self, options, option_chars, default=None, question=''): """Ask the user to chose between a set of options. The iser is asked to input a char corresponding to the option he choses. @@ -97,8 +139,8 @@ class UI: def input_yn(self, question='', default='y'): d = 0 if default in (True, 'y', 'yes') else 1 - return (True, False)[self.input_choice(['yes', 'no'], ['y', 'n'], - default=d, question=question)] + answer = self.input_choice_ng(['yes', 'no'], default=d, question=question) + return [True, False][answer] def exit(self, error_code=1): sys.exit(error_code) diff --git a/pubs/utils.py b/pubs/utils.py index 7a9f934..de6497b 100644 --- a/pubs/utils.py +++ b/pubs/utils.py @@ -1,22 +1,25 @@ # Function here may belong somewhere else. In the mean time... +from . import color + def resolve_citekey(repo, citekey, ui=None, exit_on_fail=True): """Check that a citekey exists, or autocompletes it if not ambiguous.""" # FIXME. Make me optionally non ui interactive/exiting citekeys = repo.citekeys_from_prefix(citekey) if len(citekeys) == 0: if ui is not None: - ui.error("No citekey named or beginning with '{}".format(citekey)) + ui.error("no citekey named or beginning with '{}'".format(color.dye(citekey, color.citekey))) if exit_on_fail: ui.exit() elif len(citekeys) == 1: if citekeys[0] != citekey: if ui is not None: - ui.print_("Provided citekey '{}' has been autocompleted into '{}'".format(citekey, citekeys[0])) + ui.warning("provided citekey '{}' has been autocompleted into '{}'".format(color.dye(citekey, color.citekey), color.dye(citekeys[0], color.citekey))) citekey = citekeys[0] elif citekey not in citekeys: if ui is not None: - ui.error("Be more specific. Provided citekey '{}' matches multiples citekeys: {}".format(citekey, ', '.join(citekeys))) + ui.error("be more specific; provided citekey '{}' matches multiples citekeys: {}".format( + citekey, ', '.join(color.dye(citekey, color.citekey) for citekey in citekeys))) if exit_on_fail: ui.exit() return citekey