def visitcommand(self, node, parts): assert parts # look for the first WordNode, which might not be at parts[0] idxwordnode = parser.findfirstkind(parts, 'word') if idxwordnode == -1: logger.info('no words found in command (probably contains only redirects)') return # we're mutating the parts of node, causing the visitor to skip the nodes # we're popping wordnode = parts.pop(idxwordnode) name = 'command%d' % len([g for g in self.groups if g.name.startswith('command')]) startpos, endpos = wordnode.pos try: mps = self.findmanpages(wordnode.word) except errors.ProgramDoesNotExist, e: logger.info('no manpage found for %r', wordnode.word) mg = matchgroup(name) mg.error = e mg.manpage = None mg.suggestions = None self.groups.append(mg) self.matches.append(matchresult(startpos, endpos, None, None)) return
def startcommand(self, parts, endword, addunknown=True): logger.info('startcommand parts=%r, endword=%r, addunknown=%s', parts, endword, addunknown) idxwordnode = parser.findfirstkind(parts, 'word') assert idxwordnode != -1 wordnode = parts.pop(idxwordnode) name = 'command%d' % len( [g for g in self.groups if g.name.startswith('command')]) startpos, endpos = wordnode.pos try: mps = self.findmanpages(wordnode.word) except errors.ProgramDoesNotExist, e: if addunknown: logger.info('no manpage found for %r, marking it unknown', wordnode.word) mg = matchgroup(name) mg.error = e mg.manpage = None mg.suggestions = None self.groups.append(mg) self.groupstack.append((mg, endword)) self.matches.append(matchresult(startpos, endpos, None, None)) return False
def startcommand(self, parts, endword, addunknown=True): logger.info('startcommand parts=%r, endword=%r, addunknown=%s', parts, endword, addunknown) idxwordnode = parser.findfirstkind(parts, 'word') assert idxwordnode != -1 wordnode = parts.pop(idxwordnode) name = 'command%d' % len([g for g in self.groups if g.name.startswith('command')]) startpos, endpos = wordnode.pos try: mps = self.findmanpages(wordnode.word) except errors.ProgramDoesNotExist, e: if addunknown: logger.info('no manpage found for %r, marking it unknown', wordnode.word) mg = matchgroup(name) mg.error = e mg.manpage = None mg.suggestions = None self.groups.append(mg) self.groupstack.append((mg, endword)) self.matches.append(matchresult(startpos, endpos, None, None)) return False
def visitcommand(self, node, parts): assert parts # look for the first WordNode, which might not be at parts[0] idxwordnode = parser.findfirstkind(parts, 'word') if idxwordnode == -1: logger.info('no words found in command (probably contains only redirects)') return self.startcommand(parts, None) return len(self.groupstack)
class matcher(parser.NodeVisitor): '''parse a command line and return a list of matchresults describing each token. ''' def __init__(self, s, store): self.s = s.encode('latin1', 'replace') self.store = store self._prevoption = self._currentoption = None self.groups = [matchgroup('shell')] # a stack to manage nested command groups: whenever a new simple command # is started, we push it to the stack and when it ends we pop it. the second # item in the tuple is used to end the top-most command when it is equal # to the current word. this is used when a flag starts a new command, e.g. # find -exec. self.groupstack = [(self.groups[-1], None)] @property def matches(self): '''return the list of results from the most recently created group''' return self.groupstack[-1][0].results @property def allmatches(self): return list( itertools.chain.from_iterable(g.results for g in self.groups)) @property def manpage(self): return self.groupstack[-1][0].manpage def find_option(self, opt): self._currentoption = self.manpage.find_option(opt) logger.debug('looking up option %r, got %r', opt, self._currentoption) return self._currentoption def findmanpages(self, prog): prog = prog.decode('latin1') logger.info('looking up %r in store', prog) manpages = self.store.findmanpage(prog) logger.info('found %r in store, got: %r, using %r', prog, manpages, manpages[0]) return manpages def unknown(self, token, start, end): logger.debug('nothing to do with token %r', token) return matchresult(start, end, None, None) def visitnegate(self, node): helptext = helpconstants.NEGATE self.groups[0].results.append( matchresult(node.pos[0], node.pos[1], helptext, None)) def visitoperator(self, node, op): helptext = helpconstants.OPERATORS[op] self.groups[0].results.append( matchresult(node.pos[0], node.pos[1], helptext, None)) def visitpipe(self, node, pipe): self.groups[0].results.append( matchresult(node.pos[0], node.pos[1], helpconstants.PIPELINES, None)) def visitredirect(self, node, input, type, output): helptext = [helpconstants.REDIRECTION] if type in helpconstants.REDIRECTION_KIND: helptext.append(helpconstants.REDIRECTION_KIND[type]) self.groups[0].results.append( matchresult(node.pos[0], node.pos[1], '\n\n'.join(helptext), None)) def visitcompound(self, node, group, list, redirects): helptext = helpconstants.COMPOUND[group] # we add a matchresult for the start and end of the compound command self.groups[0].results.append( matchresult(node.pos[0], node.pos[0] + 1, helptext, None)) self.groups[0].results.append( matchresult(node.pos[1] - 1, node.pos[1], helptext, None)) def visitcommand(self, node, parts): assert parts # look for the first WordNode, which might not be at parts[0] idxwordnode = parser.findfirstkind(parts, 'word') if idxwordnode == -1: logger.info( 'no words found in command (probably contains only redirects)') return self.startcommand(parts, None) return len(self.groupstack) def visitcommandend(self, node, parts, prevstackdepth): # it's possible for visitcommand/end to be called without a command group # being pushed if it contains only redirect nodes if len(self.groupstack) > 1: if prevstackdepth != len(self.groupstack): logger.info('group %s is a result of a nested command', self.groupstack[-1]) self.endcommand() self.endcommand() def startcommand(self, parts, endword, addunknown=True): logger.info('startcommand parts=%r, endword=%r, addunknown=%s', parts, endword, addunknown) idxwordnode = parser.findfirstkind(parts, 'word') assert idxwordnode != -1 wordnode = parts.pop(idxwordnode) name = 'command%d' % len( [g for g in self.groups if g.name.startswith('command')]) startpos, endpos = wordnode.pos try: mps = self.findmanpages(wordnode.word) except errors.ProgramDoesNotExist, e: if addunknown: logger.info('no manpage found for %r, marking it unknown', wordnode.word) mg = matchgroup(name) mg.error = e mg.manpage = None mg.suggestions = None self.groups.append(mg) self.groupstack.append((mg, endword)) self.matches.append(matchresult(startpos, endpos, None, None)) return False manpage = mps[0] idxnextwordnode = parser.findfirstkind(parts, 'word') if manpage.multicommand and idxnextwordnode != -1: nextwordnode = parts[idxnextwordnode] try: multi = '%s %s' % (wordnode.word, nextwordnode.word) logger.info( '%r is a multicommand, trying to get another token and look up %r', manpage, multi) mps = self.findmanpages(multi) manpage = mps[0] parts.pop(idxnextwordnode) endpos = nextwordnode.pos[1] except errors.ProgramDoesNotExist: logger.info('no manpage %r for multicommand %r', multi, manpage) # create a new matchgroup for the current command mg = matchgroup(name) mg.manpage = manpage mg.suggestions = mps[1:] self.groups.append(mg) self.groupstack.append((mg, endword)) self.matches.append( matchresult(startpos, endpos, manpage.synopsis, None)) return True