コード例 #1
0
def Modules(directory):
  """Creates modules from a plugin directory.

  Note that there can be many, if a plugin has standalone parts that merit their
  own helpfiles.

  Args:
    directory: The plugin directory.
  Yields:
    Module objects as necessary.
  """
  directory = directory.rstrip(os.path.sep)
  addon_info = None
  # Check for module metadata in addon-info.json (if it exists).
  addon_info_path = os.path.join(directory, 'addon-info.json')
  if os.path.isfile(addon_info_path):
    try:
      with open(addon_info_path, 'r') as addon_info_file:
        addon_info = json.loads(addon_info_file.read())
    except (IOError, ValueError) as e:
      warnings.warn(
          'Failed to read file {}. Error was: {}'.format(addon_info_path, e),
          error.InvalidAddonInfo)
  plugin_name = None
  # Use plugin name from addon-info.json if available. Fall back to dir name.
  addon_info = addon_info or {}
  plugin_name = addon_info.get(
      'name', os.path.basename(os.path.abspath(directory)))
  plugin = VimPlugin(plugin_name)

  # Set module metadata from addon-info.json.
  if addon_info is not None:
    # Valid addon-info.json. Apply addon metadata.
    if 'author' in addon_info:
      plugin.author = addon_info['author']
    if 'description' in addon_info:
      plugin.tagline = addon_info['description']

  # Crawl plugin dir and collect parsed blocks for each file path.
  paths_and_blocks = []
  standalone_paths = []
  autoloaddir = os.path.join(directory, 'autoload')
  for (root, dirs, files) in os.walk(directory):
    # Visit files in a stable order, since the ordering of e.g. the Maktaba
    # flags below depends upon the order that we visit the files.
    dirs.sort()
    files.sort()

    # Prune non-standard top-level dirs like 'test'.
    if root == directory:
      dirs[:] = [x for x in dirs if x in DOC_SUBDIRS + ['after']]
    if root == os.path.join(directory, 'after'):
      dirs[:] = [x for x in dirs if x in DOC_SUBDIRS]
    for f in files:
      filename = os.path.join(root, f)
      if os.path.splitext(filename)[1] == '.vim':
        relative_path = os.path.relpath(filename, directory)
        with open(filename) as filehandle:
          lines = list(filehandle)
          blocks = list(parser.ParseBlocks(lines, filename))
          # Define implicit maktaba flags for files that call
          # maktaba#plugin#Enter. These flags have to be special-cased here
          # because there aren't necessarily associated doc comment blocks and
          # the name is computed from the file name.
          if (not relative_path.startswith('autoload' + os.path.sep)
              and relative_path != os.path.join('instant', 'flags.vim')):
            if ContainsMaktabaPluginEnterCall(lines):
              flagpath = relative_path
              if flagpath.startswith('after' + os.path.sep):
                flagpath = os.path.relpath(flagpath, 'after')
              flagblock = Block(vimdoc.FLAG, is_default=True)
              name_parts = os.path.splitext(flagpath)[0].split(os.path.sep)
              flagname = name_parts.pop(0)
              flagname += ''.join('[' + p + ']' for p in name_parts)
              flagblock.Local(name=flagname)
              flagblock.AddLine(
                  'Configures whether {} should be loaded.'.format(
                      relative_path))
              default = 0 if flagname == 'plugin[mappings]' else 1
              # Use unbulleted list to make sure it's on its own line. Use
              # backtick to avoid helpfile syntax highlighting.
              flagblock.AddLine(' - Default: {} `'.format(default))
              blocks.append(flagblock)
        paths_and_blocks.append((relative_path, blocks))
        if filename.startswith(autoloaddir):
          if blocks and blocks[0].globals.get('standalone'):
            standalone_paths.append(relative_path)

  docdir = os.path.join(directory, 'doc')
  if not os.path.isdir(docdir):
    os.mkdir(docdir)

  modules = []

  main_module = Module(plugin_name, plugin)
  for (path, blocks) in paths_and_blocks:
    # Skip standalone paths.
    if GetMatchingStandalonePath(path, standalone_paths) is not None:
      continue
    namespace = None
    if path.startswith('autoload' + os.path.sep):
      namespace = GetAutoloadNamespace(os.path.relpath(path, 'autoload'))
    for block in blocks:
      main_module.Merge(block, namespace=namespace)
  modules.append(main_module)

  # Process standalone modules.
  standalone_modules = {}
  for (path, blocks) in paths_and_blocks:
    standalone_path = GetMatchingStandalonePath(path, standalone_paths)
    # Skip all but standalone paths.
    if standalone_path is None:
      continue
    assert path.startswith('autoload' + os.path.sep)
    namespace = GetAutoloadNamespace(os.path.relpath(path, 'autoload'))
    standalone_module = standalone_modules.get(standalone_path)
    # Initialize module if this is the first file processed from it.
    if standalone_module is None:
      standalone_module = Module(namespace.rstrip('#'), plugin)
      standalone_modules[standalone_path] = standalone_module
      modules.append(standalone_module)
    for block in blocks:
      standalone_module.Merge(block, namespace=namespace)

  for module in modules:
    module.Close()
    yield module
コード例 #2
0
  def Close(self):
    """Closes the module.

    All default sections that have not been overridden will be created.
    """
    # ----------------------------------------------------------
    # Add default sections.

    # type : (id, name)
    default_sections = [
        (vimdoc.FUNCTION, 'functions', 'Functions'),
        (vimdoc.EXCEPTION, 'exceptions', 'Exceptions'),
        (vimdoc.COMMAND, 'commands', 'Commands'),
        (vimdoc.DICTIONARY, 'dicts', 'Dictionaries'),
        (vimdoc.FLAG, 'config', 'Configuration'),
        (vimdoc.SETTING, 'config', 'Configuration'),
      ]

    for (typ, id, name) in default_sections:
      if typ == vimdoc.FLAG:
        self._AddMaktabaFlagHelp()

      if self.GetCollection(typ) and (id not in self.sections):
        # Create the section if it does not exist.
        block = Block(vimdoc.SECTION)
        block.Local(id=id, name=name)
        self.Merge(block)


    # ----------------------------------------------------------
    # Add backmatter.

    for backmatter in self.backmatters:
      if backmatter not in self.sections:
        raise error.NoSuchSection(backmatter)

    # ----------------------------------------------------------
    # Expand child sections and order the section list for output.

    # Use explicit order as partial ordering and merge with default section
    # ordering. All custom sections must be ordered explicitly.
    self.order = self._GetSectionOrder(self.order, self.sections)

    # Child section collection
    to_delete = []
    for key in self.sections:
      section = self.sections[key]
      parent_id = section.locals.get('parent_id', None)
      if parent_id:
        if parent_id not in self.sections:
          raise error.NoSuchParentSection(
                  section.locals['name'], parent_id)
        parent = self.sections[parent_id]
        parent.locals.setdefault('children', []).append(section)
        to_delete.append(key)

    for key in to_delete:
      self.sections.pop(key)

    # Check that all top-level sections are included in ordering.
    known = set(self.sections)
    neglected = sorted(known.difference(self.order))
    if neglected:
      raise error.NeglectedSections(neglected, self.order)

    # Reinsert top-level sections in the correct order, expanding the tree of
    # child sections along the way so we have a linear list of sections to pass
    # to the output functions.

    # Helper function to recursively add children to self.sections.
    # We add a 'level' variable to locals so that WriteTableOfContents can keep
    # track of the nesting.
    def _AddChildSections(section):
      section.locals.setdefault('level', 0)
      if 'children' in section.locals:
        sort_key = lambda s: s.locals['name']
        for child in sorted(section.locals['children'], key=sort_key):
          child.locals['level'] = section.locals['level'] + 1
          self.sections[child.locals['id']] = child
          _AddChildSections(child)

    # Insert sections according to the @order directive
    for key in self.order:
      if key in self.sections:
        section = self.sections.pop(key)
        if 'parent_id' in section.locals and section.locals['parent_id']:
          raise error.OrderedChildSections(section.locals['id'], self.order)
        # Move to end.
        self.sections[key] = section
        _AddChildSections(section)