Example #1
0
    def Close(self):
        """Closes the module.

    All default sections that have not been overridden will be created.
    """
        if self.GetCollection(
                vimdoc.FUNCTION) and 'functions' not in self.sections:
            functions = Block(vimdoc.SECTION)
            functions.Local(id='functions', name='Functions')
            self.Merge(functions)
        if (self.GetCollection(vimdoc.EXCEPTION)
                and 'exceptions' not in self.sections):
            exceptions = Block(vimdoc.SECTION)
            exceptions.Local(id='exceptions', name='Exceptions')
            self.Merge(exceptions)
        if self.GetCollection(
                vimdoc.COMMAND) and 'commands' not in self.sections:
            commands = Block(vimdoc.SECTION)
            commands.Local(id='commands', name='Commands')
            self.Merge(commands)
        if self.GetCollection(
                vimdoc.DICTIONARY) and 'dicts' not in self.sections:
            dicts = Block(vimdoc.SECTION)
            dicts.Local(id='dicts', name='Dictionaries')
            self.Merge(dicts)
        if self.GetCollection(vimdoc.FLAG):
            # If any maktaba flags were documented, add a default configuration
            # section to explain how to use them.
            config = Block(vimdoc.SECTION, is_default=True)
            config.Local(id='config', name='Configuration')
            config.AddLine(
                'This plugin uses maktaba flags for configuration. Install Glaive'
                ' (https://github.com/google/glaive) and use the @command(Glaive)'
                ' command to configure them.')
            self.Merge(config)
        if ((self.GetCollection(vimdoc.FLAG)
             or self.GetCollection(vimdoc.SETTING))
                and 'config' not in self.sections):
            config = Block(vimdoc.SECTION)
            config.Local(id='config', name='Configuration')
            self.Merge(config)

        for backmatter in self.backmatters:
            if backmatter not in self.sections:
                raise error.NoSuchSection(backmatter)
        # 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)

        known = set(self.sections)
        neglected = sorted(known.difference(self.order))
        if neglected:
            raise error.NeglectedSections(neglected, self.order)
        # Sections are now in order.
        for key in self.order:
            if key in self.sections:
                # Move to end.
                self.sections[key] = self.sections.pop(key)
Example #2
0
 def _AddMaktabaFlagHelp(self):
   """If any maktaba flags were documented, add a default configuration section
    to explain how to use them.
   """
   if self.GetCollection(vimdoc.FLAG):
     block = Block(vimdoc.SECTION, is_default=True)
     block.Local(id='config', name='Configuration')
     block.AddLine(
         'This plugin uses maktaba flags for configuration. Install Glaive'
         ' (https://github.com/google/glaive) and use the @command(Glaive)'
         ' command to configure them.')
     self.Merge(block)
Example #3
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