Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion lib/jazzy/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,13 @@ def expand_path(path)
description: ['Custom navigation categories to replace the standard '\
'“Classes, Protocols, etc.”', 'Types not explicitly named '\
'in a custom category appear in generic groups at the end.',
'Example: https://git.io/v4Bcp'],
'You can add another category in the children array '\
'instead of using a type name, to create ',
'subcategories. This can be repeated ad infinitum, '\
'provided your theme supports it.',
'Currently all integrated themes support a maximum ',
'of 3 levels.',
'Example: https://git.io/fNvGB'],
default: []

config_attr :custom_head,
Expand Down
67 changes: 54 additions & 13 deletions lib/jazzy/doc_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,36 @@ def self.doc_structure_for_docs(docs)
children = doc.children
.sort_by { |c| [c.nav_order, c.name, c.usr || ''] }
.flat_map do |child|
# FIXME: include arbitrarily nested extensible types
[{ name: child.name, url: child.url }] +
Array(child.children.select do |sub_child|
sub_child.type.swift_extensible? || sub_child.type.extension?
end).map do |sub_child|
{ name: "– #{sub_child.name}", url: sub_child.url }
end
if child.type.overview?
doc_structure_for_docs([child])
else
doc_structure_for_child(child)
end
end

{
section: doc.name,
url: doc.url,
children: children,
level: doc.level,
}
end
end

# Generate structure for children of a category in sidebar navigation.
# @return [Array] doc structure compromised of
# name and url of the child as well as
# any possible nested children.
def self.doc_structure_for_child(child)
# FIXME: include arbitrarily nested extensible types
[{ name: child.name, url: child.url, children: nil }] +
Array(child.children.select do |sub_child|
sub_child.type.swift_extensible? || sub_child.type.extension?
end).map do |sub_child|
{ name: "– #{sub_child.name}", url: sub_child.url, children: nil }
end
end

# Build documentation from the given options
# @param [Config] options
# @return [SourceModule] the documented source module
Expand Down Expand Up @@ -383,6 +397,34 @@ def self.render_tasks(source_module, children)
end
end

def self.render_subsections(subsections, source_module)
subsections.map do |subsection|
overview = render_overview(subsection)
tasks = render_tasks(
source_module,
subsection.children.reject { |c| c.type.overview? },
)

{
name: subsection.name,
overview: overview,
uid: URI.encode(subsection.name),
url: subsection.url,
level: subsection.level,
tasks: tasks,
}
end
end

def self.render_overview(doc)
overview = (doc.abstract || '') + (doc.discussion || '')
alternative_abstract = doc.alternative_abstract
if alternative_abstract
overview = render(doc, alternative_abstract) + overview
end
overview
end

# rubocop:disable Metrics/MethodLength
# Build Mustache document from single parsed doc
# @param [Config] options Build options
Expand All @@ -395,11 +437,7 @@ def self.document(source_module, doc_model, path_to_root)
return document_markdown(source_module, doc_model, path_to_root)
end

overview = (doc_model.abstract || '') + (doc_model.discussion || '')
alternative_abstract = doc_model.alternative_abstract
if alternative_abstract
overview = render(doc_model, alternative_abstract) + overview
end
overview = render_overview(doc_model)

doc = Doc.new # Mustache model instance
doc[:custom_head] = Config.instance.custom_head
Expand All @@ -412,7 +450,10 @@ def self.document(source_module, doc_model, path_to_root)
doc[:declaration] = doc_model.display_declaration
doc[:overview] = overview
doc[:structure] = source_module.doc_structure
doc[:tasks] = render_tasks(source_module, doc_model.children)
categories, children =
doc_model.children.partition { |c| c.type.overview? }
doc[:tasks] = render_tasks(source_module, children)
doc[:subsections] = render_subsections(categories, source_module)
doc[:module_name] = source_module.name
doc[:author_name] = source_module.author_name
doc[:github_url] = source_module.github_url
Expand Down
77 changes: 77 additions & 0 deletions lib/jazzy/source_category.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
require 'jazzy/source_declaration'
require 'jazzy/config'
require 'jazzy/source_mark'
require 'jazzy/jazzy_markdown'

module Jazzy
# Category (group, contents) pages generated by jazzy
class SourceCategory < SourceDeclaration
extend Config::Mixin

def initialize(group, name, abstract, url_name, level = 1)
super()
self.type = SourceDeclaration::Type.overview
self.name = name
self.url_name = url_name
self.abstract = Markdown.render(abstract)
self.children = group
self.parameters = []
self.level = level
end

# Group root-level docs into custom categories or by type
def self.group_docs(docs)
custom_categories, docs =
group_custom_categories(docs, config.custom_categories)
type_categories, uncategorized = group_type_categories(
docs, custom_categories.any? ? 'Other ' : ''
)
custom_categories + type_categories + uncategorized
end

def self.group_custom_categories(docs, categories, level = 1)
group = categories.map do |category|
children = category['children'].flat_map do |child|
if child.is_a?(Hash)
# Nested category, recurse
children, docs = group_custom_categories(docs, [child], level + 1)
Copy link
Author

@galli-leo galli-leo Jul 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnfairh I renamed docs_with_name to children to a) make rubocop happy and b) make it's purpose clearer.

Is that ok?

else
# Doc name, find it
children, docs = docs.partition { |doc| doc.name == child }
if children.empty?
STDERR.puts(
'WARNING: No documented top-level declarations match ' \
"name \"#{child}\" specified in categories file",
)
end
end
children
end
# Category config overrides alphabetization
children.each.with_index { |child, i| child.nav_order = i }
make_group(children, category['name'], '', nil, level)
end
[group.compact, docs]
end

def self.group_type_categories(docs, type_category_prefix)
group = SourceDeclaration::Type.all.map do |type|
children, docs = docs.partition { |doc| doc.type == type }
make_group(
children,
type_category_prefix + type.plural_name,
"The following #{type.plural_name.downcase} are available globally.",
type_category_prefix + type.plural_url_name,
)
end
[group.compact, docs]
end

def self.make_group(group, name, abstract, url_name = nil, level = 1)
group.reject! { |doc| doc.name.empty? }
unless group.empty?
SourceCategory.new(group, name, abstract, url_name, level)
end
end
end
end
14 changes: 14 additions & 0 deletions lib/jazzy/source_declaration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,19 @@ def namespace_ancestors
end
end

# Chain of parent_in_docs from top level to self. (Includes self.)
def documentation_path
documentation_ancestors + [self]
end

def documentation_ancestors
if parent_in_docs
parent_in_docs.documentation_path
else
[]
end
end

def fully_qualified_name
namespace_path.map(&:name).join('.')
end
Expand Down Expand Up @@ -99,6 +112,7 @@ def display_other_language_declaration
attr_accessor :end_line
attr_accessor :nav_order
attr_accessor :url_name
attr_accessor :level

def alternative_abstract
if file = alternative_abstract_file
Expand Down
4 changes: 4 additions & 0 deletions lib/jazzy/source_declaration/type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ def objc_unexposed?
kind == 'sourcekitten.source.lang.objc.decl.unexposed'
end

def overview?
Type.overview == self
end

def self.overview
Type.new('Overview')
end
Expand Down
61 changes: 7 additions & 54 deletions lib/jazzy/sourcekitten.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'jazzy/source_declaration'
require 'jazzy/source_mark'
require 'jazzy/stats'
require 'jazzy/source_category'

ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'.freeze

Expand Down Expand Up @@ -59,58 +60,6 @@ def self.undocumented_abstract
).freeze
end

# Group root-level docs by custom categories (if any) and type
def self.group_docs(docs)
custom_categories, docs = group_custom_categories(docs)
type_categories, uncategorized = group_type_categories(
docs, custom_categories.any? ? 'Other ' : ''
)
custom_categories + type_categories + uncategorized
end

def self.group_custom_categories(docs)
group = Config.instance.custom_categories.map do |category|
children = category['children'].flat_map do |name|
docs_with_name, docs = docs.partition { |doc| doc.name == name }
if docs_with_name.empty?
STDERR.puts 'WARNING: No documented top-level declarations match ' \
"name \"#{name}\" specified in categories file"
end
docs_with_name
end
# Category config overrides alphabetization
children.each.with_index { |child, i| child.nav_order = i }
make_group(children, category['name'], '')
end
[group.compact, docs]
end

def self.group_type_categories(docs, type_category_prefix)
group = SourceDeclaration::Type.all.map do |type|
children, docs = docs.partition { |doc| doc.type == type }
make_group(
children,
type_category_prefix + type.plural_name,
"The following #{type.plural_name.downcase} are available globally.",
type_category_prefix + type.plural_url_name,
)
end
[group.compact, docs]
end

def self.make_group(group, name, abstract, url_name = nil)
group.reject! { |doc| doc.name.empty? }
unless group.empty?
SourceDeclaration.new.tap do |sd|
sd.type = SourceDeclaration::Type.overview
sd.name = name
sd.url_name = url_name
sd.abstract = Markdown.render(abstract)
sd.children = group
end
end
end

def self.sanitize_filename(doc)
unsafe_filename = doc.url_name || doc.name
sanitzation_enabled = Config.instance.use_safe_filenames
Expand Down Expand Up @@ -168,8 +117,12 @@ def self.subdir_for_doc(doc)
# File program elements under top ancestor’s type (Struct, Class, etc.)
[top_level_decl.type.plural_url_name] +
doc.namespace_ancestors.map(&:name)
else
elsif doc.type == SourceDeclaration::Type.overview
# Categories live in their own directory
# But subcategories live in a directory named after their parent
doc.documentation_ancestors.map(&:name)
else
# Anything else
[]
end
end
Expand Down Expand Up @@ -806,7 +759,7 @@ def self.parse(sourcekitten_output, min_acl, skip_undocumented, inject_docs)
docs = docs.reject { |doc| doc.type.swift_enum_element? }
end
ungrouped_docs = docs
docs = group_docs(docs)
docs = SourceCategory.group_docs(docs)
make_doc_urls(docs)
autolink(docs, ungrouped_docs)
[docs, @stats]
Expand Down
Loading