def choices_left(input, csrange): """Assembles a list of terms that allow a valid input line given the prefix passed as `input` has been typed in so far. Those terms can include command keywords and argument values and depend on the command syntax to which the input line so far typed in matches, as well as legal argument values for that incomplete input line. The resulting list will only contain keywords or argument values that either have been started typing in, or those that may legally extend the current prefix, in case the latter doesn't end with an incomplete keyword or argument value. """ # TODO: read carefully for potential logical flaws! # TODO: read carefully for potential logical flaws! # TODO: read carefully for potential logical flaws! if not input: input = '' # range of relevant input (beginning of incomplete word # and cursor position) beg, end = csrange # begin traversing language tree as long as it matches current input level = cmdict level_down = level # split input string into single terms # BUT only up to cursor position! terms = trmex.findall(input[:end]) # append empty string if line ends on whitespace. thus the next # keyword/value in order can be determined later if re.match('.*\s+\Z', input[:end]) or end-beg<1: terms.append('') util.log('autocomplete: detected tokens are {}'.format(terms)) #print '\nfind choices for:',terms # #########################3 # parse incomplete input # word by word for term in terms: # walk downwards level = level_down if term in level: #print 'fitting:', term level_down = level.get(term) else: # if not a keyword, term might be an attribute value argnames = [t for t in level.keys() if argex.search(t)] resolved = False if len(argnames)>0: # check if term satisfies any attribute value requirements # TODO: auch hier das problem, dasz sich mit dem erstbesten # TODO zufrieden gegeben wird..? for a in argnames: if not resolved: arg = argex.findall(a)[0] # print '(argument to match is {})'.format(a) if arguments.validate(arg, term): # value matches attribute. proceed level_down = level.get(a) resolved=True if not resolved: # here is probably where the input breaks up. # also possibly where input goes on invalidly # nevertheless, we have to proceed until there # is no input left at all. otherwise, we might # end up with autocomplete suggestions for terms # in the middle of nowhere. If an input turns out # to be invalid way before it is done parsing, # then we simply can't provide autocompletion # for that input. # TODO: or can we? how exactly is cursor position # handled by readline module??? break # incomplete input line has been matched against known # commands as far as possible. what do we have here? # possibility 1): input ends w potential or partly typed keyword # possibility 2): input ends where a value should follow # or is partly typed in #print 'fragment, keys:', term, level.keys() choices1 = level.keys()[:] util.log('autocomplete: possible input is {}.'.format(choices1)) #print term, ':', choices1 choices = [] for c in choices1: # resolve argument, if any if argex.search(c): a = argex.findall(c)[0] util.log('Apply for completion candidates for arg {}.'.format(a)) choices.extend(arguments.get_suggestions(a, term)) else: # if not expecting argument: check if keyword can be # completed if c.startswith(term): choices.append(c) # by default, append whitespace behind completion choice # if completion is ultimate (e.g. command names). # If choice ends on ;, this means it can possibly be # extended further, but is not meant to stay like it is # (e.g. directory names: even if completed, you can still # go deeper in file structure) choices = [''.join(c[:-1])+[c[-1]+' ',''][int(c[-1]==';')] for c in choices if len(c) > 0] util.log('Suggest {} completion candidates:'.format(len(choices))) util.log(', '.join(choices)) #print 'suggestions:',choices, '' return choices
def choices_left(input, csrange): """Assembles a list of terms that allow a valid input line given the prefix passed as `input` has been typed in so far. Those terms can include command keywords and argument values and depend on the command syntax to which the input line so far typed in matches, as well as legal argument values for that incomplete input line. The resulting list will only contain keywords or argument values that either have been started typing in, or those that may legally extend the current prefix, in case the latter doesn't end with an incomplete keyword or argument value. """ # TODO: read carefully for potential logical flaws! # TODO: read carefully for potential logical flaws! # TODO: read carefully for potential logical flaws! if not input: input = '' # range of relevant input (beginning of incomplete word # and cursor position) beg, end = csrange # begin traversing language tree as long as it matches current input level = cmdict level_down = level # split input string into single terms # BUT only up to cursor position! terms = trmex.findall(input[:end]) # append empty string if line ends on whitespace. thus the next # keyword/value in order can be determined later if re.match('.*\s+\Z', input[:end]) or end - beg < 1: terms.append('') util.log('autocomplete: detected tokens are {}'.format(terms)) #print '\nfind choices for:',terms # #########################3 # parse incomplete input # word by word for term in terms: # walk downwards level = level_down if term in level: #print 'fitting:', term level_down = level.get(term) else: # if not a keyword, term might be an attribute value argnames = [t for t in level.keys() if argex.search(t)] resolved = False if len(argnames) > 0: # check if term satisfies any attribute value requirements # TODO: auch hier das problem, dasz sich mit dem erstbesten # TODO zufrieden gegeben wird..? for a in argnames: if not resolved: arg = argex.findall(a)[0] # print '(argument to match is {})'.format(a) if arguments.validate(arg, term): # value matches attribute. proceed level_down = level.get(a) resolved = True if not resolved: # here is probably where the input breaks up. # also possibly where input goes on invalidly # nevertheless, we have to proceed until there # is no input left at all. otherwise, we might # end up with autocomplete suggestions for terms # in the middle of nowhere. If an input turns out # to be invalid way before it is done parsing, # then we simply can't provide autocompletion # for that input. # TODO: or can we? how exactly is cursor position # handled by readline module??? break # incomplete input line has been matched against known # commands as far as possible. what do we have here? # possibility 1): input ends w potential or partly typed keyword # possibility 2): input ends where a value should follow # or is partly typed in #print 'fragment, keys:', term, level.keys() choices1 = level.keys()[:] util.log('autocomplete: possible input is {}.'.format(choices1)) #print term, ':', choices1 choices = [] for c in choices1: # resolve argument, if any if argex.search(c): a = argex.findall(c)[0] util.log('Apply for completion candidates for arg {}.'.format(a)) choices.extend(arguments.get_suggestions(a, term)) else: # if not expecting argument: check if keyword can be # completed if c.startswith(term): choices.append(c) # by default, append whitespace behind completion choice # if completion is ultimate (e.g. command names). # If choice ends on ;, this means it can possibly be # extended further, but is not meant to stay like it is # (e.g. directory names: even if completed, you can still # go deeper in file structure) choices = [ ''.join(c[:-1]) + [c[-1] + ' ', ''][int(c[-1] == ';')] for c in choices if len(c) > 0 ] util.log('Suggest {} completion candidates:'.format(len(choices))) util.log(', '.join(choices)) #print 'suggestions:',choices, '' return choices
def execute(input): """\ Tests given input string against currently registered command syntaxes. If input turns out to be a valid command, a corresponding handler function is called. If matching syntax contains argument placeholders (`"command <arg>"`), their respective values are extracted from the input and passed to the handler function. """ # split input string into single terms terms = trmex.findall(input) # init path log for recreation of generic command path = [] args = [] kwargs = {} # return message msg = '' # check if terms match known commands: # begin at language tree root level = cmdict # walk down as long as input seems to be # a valid command language word, i.e. input # term reachable by walking along its predecessors term = '' for term in terms: #print ' '.join(level.keys()) # still on track? if term in level: level = level.get(term) path.append(term) else: # might be just an argument value that we # need to recognize, validate and extract # first, get all argument ids that # might be applicable argnames = [t for t in level.keys() if argex.search(t)] resolved = False if len(argnames)>0: # then test if found input is valid for # at least one of them #TODO: we will probably have to recurse #TODO here, because we might get on the # wrong track by picking just any matching # placeholder here for a in argnames: if not resolved: arg = argex.findall(a)[0] if arguments.validate(arg, term): #print 'reading value for argument', #print '{}: {}.'.format(a,term) # input valid! collect value args.append(term) kwargs[arg] = term # proceed level = level.get(a) path.append(a) resolved=True if not resolved: # path lost -> input not in language # not a valid command msg = term term = None break # do we have a match? or not? if term is None: return '!!Syntax error!!: term "{}" not recognized.'.format(msg) else: # if EOL code terminates term sequence, we are good. # if not, input is incomplete if level.get('') is None: msg = msg_incomplete_cmd(level.keys()) # return a hint on expected input return msg else: # we have a match! # command is valid! get handler! # print ' '.join(path) func = level.get('') #print 'Calling {}'.format(func.__name__) ret = func(*args, **kwargs) # log argument values for arg,v in kwargs.items(): arguments.to_history(arg,v) return ret
def execute(input): """\ Tests given input string against currently registered command syntaxes. If input turns out to be a valid command, a corresponding handler function is called. If matching syntax contains argument placeholders (`"command <arg>"`), their respective values are extracted from the input and passed to the handler function. """ # split input string into single terms terms = trmex.findall(input) # init path log for recreation of generic command path = [] args = [] kwargs = {} # return message msg = '' # check if terms match known commands: # begin at language tree root level = cmdict # walk down as long as input seems to be # a valid command language word, i.e. input # term reachable by walking along its predecessors term = '' for term in terms: #print ' '.join(level.keys()) # still on track? if term in level: level = level.get(term) path.append(term) else: # might be just an argument value that we # need to recognize, validate and extract # first, get all argument ids that # might be applicable argnames = [t for t in level.keys() if argex.search(t)] resolved = False if len(argnames) > 0: # then test if found input is valid for # at least one of them #TODO: we will probably have to recurse #TODO here, because we might get on the # wrong track by picking just any matching # placeholder here for a in argnames: if not resolved: arg = argex.findall(a)[0] if arguments.validate(arg, term): #print 'reading value for argument', #print '{}: {}.'.format(a,term) # input valid! collect value args.append(term) kwargs[arg] = term # proceed level = level.get(a) path.append(a) resolved = True if not resolved: # path lost -> input not in language # not a valid command msg = term term = None break # do we have a match? or not? if term is None: return '!!Syntax error!!: term "{}" not recognized.'.format(msg) else: # if EOL code terminates term sequence, we are good. # if not, input is incomplete if level.get('') is None: msg = msg_incomplete_cmd(level.keys()) # return a hint on expected input return msg else: # we have a match! # command is valid! get handler! # print ' '.join(path) func = level.get('') #print 'Calling {}'.format(func.__name__) ret = func(*args, **kwargs) # log argument values for arg, v in kwargs.items(): arguments.to_history(arg, v) return ret