From c7f9989832fcfea6023f41fa7ff0f52643878f86 Mon Sep 17 00:00:00 2001 From: Karl Bowden Date: Mon, 2 May 2016 22:29:25 +1000 Subject: [PATCH] [rebase] Integrated markdown parsing and guide support --- .gitignore | 1 + .gitmodules | 2 +- README.md | 32 +++++++++++++ lib/jazzy/config.rb | 10 ++++ lib/jazzy/doc_builder.rb | 26 ++++++---- lib/jazzy/documentation_generator.rb | 38 +++++++++++++++ lib/jazzy/source_declaration.rb | 29 ++++++++++- lib/jazzy/source_declaration/type.rb | 7 ++- lib/jazzy/source_document.rb | 72 ++++++++++++++++++++++++++++ lib/jazzy/source_module.rb | 2 +- lib/jazzy/sourcekitten.rb | 4 +- spec/integration_specs | 2 +- 12 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 lib/jazzy/documentation_generator.rb create mode 100644 lib/jazzy/source_document.rb diff --git a/.gitignore b/.gitignore index 96e7aeedb..c058fb27f 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ Icon Network Trash Folder Temporary Items .apdisk +vendor diff --git a/.gitmodules b/.gitmodules index 7c6a45b4e..9c813cb4e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/jpsim/SourceKitten.git [submodule "spec/integration_specs"] path = spec/integration_specs - url = https://github.com/Realm/jazzy-integration-specs.git + url = https://github.com/agentk/jazzy-integration-specs.git diff --git a/README.md b/README.md index 2fe1ed7b3..3a4363334 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,38 @@ You can specify which theme to use by passing in the `--theme` option. You can also provide your own custom theme by passing in the path to your theme directory. +### Guides + +| -- | -- | +| Command line option | `--documentation={file pattern}` | +| Example | `--documentation=Docs/*.md` | +| jazzy.yaml example | `documentation: Docs/*.md` | + +Using the `--documentation` option, extra markdown files can be integrated into the generated docs and sidebar navigation. + +Any files found matching the file pattern will be parsed and included as a document with the type 'Guide' when generated. If the files are not included using the `custom_categories` config option, they will be grouped under 'Other Guides' in the sidebar navigation. + +There are a few limitations: +- File names must be unique from source files. +- Readme should be specified separately using the `readme_path` option. + +### Section description abstracts + +| -- | -- | +| Command line option | `--abstract={file pattern}` | +| Example | `--abstract=Docs/Sections/*.md` | +| jazzy.yaml example | `abstract: Docs/Sections/*.md` | + +Using the `--abstract` options, extra markdown can be included after the heading of section overview pages. Think of it as a template include. + +The list of files matching the pattern is compared against the list of sections generated and if a match is found, it's contents will be included in that section before listing source output. + +Unlike the `--documentation` option, these files are not included in navigation and if a file does not match a section title, it is not included at all. + +This is very helpful when using `custom_categories` for grouping types and including relevant documentation in those sections. + +For an example of a project using both `--documentation` and `--abstract` see: [http://reswift.github.io/ReSwift/](http://reswift.github.io/ReSwift/) + ## Troubleshooting #### Swift diff --git a/lib/jazzy/config.rb b/lib/jazzy/config.rb index 06c49545a..ae7b8ac01 100644 --- a/lib/jazzy/config.rb +++ b/lib/jazzy/config.rb @@ -197,6 +197,16 @@ def expand_path(path) description: 'The path to a markdown README file', parse: ->(rp) { expand_path(rp) } + config_attr :documentation_glob, + command_line: '--documentation GLOB', + description: 'Glob that matches available documentation', + parse: ->(dg) { Pathname.glob(dg) } + + config_attr :abstract_glob, + command_line: '--abstract GLOB', + description: 'Glob that matches available abstracts for categories', + parse: ->(ag) { Pathname.glob(ag) } + config_attr :podspec, command_line: '--podspec FILEPATH', parse: ->(ps) { PodspecDocumenter.create_podspec(Pathname(ps)) if ps }, diff --git a/lib/jazzy/doc_builder.rb b/lib/jazzy/doc_builder.rb index 04934afc7..9351e4a28 100644 --- a/lib/jazzy/doc_builder.rb +++ b/lib/jazzy/doc_builder.rb @@ -7,10 +7,12 @@ require 'jazzy/config' require 'jazzy/doc' require 'jazzy/docset_builder' +require 'jazzy/documentation_generator' require 'jazzy/jazzy_markdown' require 'jazzy/podspec_documenter' require 'jazzy/readme_generator' require 'jazzy/source_declaration' +require 'jazzy/source_document' require 'jazzy/source_module' require 'jazzy/sourcekitten' @@ -91,7 +93,7 @@ def self.build_docs(output_dir, docs, source_module) def self.each_doc(output_dir, docs, &block) docs.each do |doc| - next if doc.name != 'index' && doc.children.count == 0 + next unless doc.render? # Assuming URL is relative to documentation root: path = output_dir + (doc.url || "#{doc.name}.html") block.call(doc, path) @@ -109,9 +111,11 @@ def self.build_site(docs, coverage, options) structure = doc_structure_for_docs(docs) - docs << SourceDeclaration.new.tap do |sd| + docs << SourceDocument.new.tap do |sd| sd.name = 'index' sd.children = [] + sd.type = SourceDeclaration::Type.new 'document.markdown' + sd.readme_path = options.readme_path end source_module = SourceModule.new(options, docs, structure, coverage) @@ -138,7 +142,7 @@ def self.build_docs_for_sourcekitten_output(sourcekitten_output, options) sourcekitten_output, options.min_acl, options.skip_undocumented, - ) + DocumentationGenerator.source_docs) prepare_output_dir(options.output, options.clean) write_lint_report(undocumented, options) @@ -238,15 +242,17 @@ def self.copy_assets(destination) end end - # Build index Mustache document + # Build Mustache document from a markdown source file # @param [Config] options Build options + # @param [Hash] doc_model Parsed doc. @see SourceKitten.parse # @param [String] path_to_root # @param [Array] doc_structure doc structure comprised of section names and # child names and URLs. @see doc_structure_for_docs - def self.document_index(source_module, path_to_root) + def self.document_markdown(source_module, doc_model, path_to_root) doc = Doc.new # Mustache model instance - doc[:name] = source_module.name - doc[:overview] = ReadmeGenerator.generate(source_module) + name = doc_model.name == 'index' ? source_module.name : doc_model.name + doc[:name] = name + doc[:overview] = Jazzy.markdown.render(doc_model.content(source_module)) doc[:custom_head] = Config.instance.custom_head doc[:doc_coverage] = source_module.doc_coverage unless Config.instance.hide_documentation_coverage @@ -326,7 +332,7 @@ def self.make_task(mark, uid, items) # @param [Config] options Build options # @param [Hash] doc_model Parsed doc. @see SourceKitten.parse def self.render_tasks(source_module, children) - marks = children.map(&:mark).uniq + marks = children.map(&:mark).uniq.compact mark_names_counts = {} marks.map do |mark| mark_children = children.select { |child| child.mark == mark } @@ -349,8 +355,8 @@ def self.render_tasks(source_module, children) # @param [Array] doc_structure doc structure comprised of section names and # child names and URLs. @see doc_structure_for_docs def self.document(source_module, doc_model, path_to_root) - if doc_model.name == 'index' - return document_index(source_module, path_to_root) + if doc_model.type.kind == 'document.markdown' + return document_markdown(source_module, doc_model, path_to_root) end doc = Doc.new # Mustache model instance diff --git a/lib/jazzy/documentation_generator.rb b/lib/jazzy/documentation_generator.rb new file mode 100644 index 000000000..6fa0a03e4 --- /dev/null +++ b/lib/jazzy/documentation_generator.rb @@ -0,0 +1,38 @@ +require 'pathname' + +require 'jazzy/jazzy_markdown' +require 'jazzy/source_document' + +module Jazzy + module DocumentationGenerator + extend Config::Mixin + + def self.source_docs + documentation_entries.map do |file_path| + SourceDocument.new.tap do |sd| + sd.name = File.basename(file_path, '.md') + sd.url = sd.name.downcase.strip + .tr(' ', '-').gsub(/[^\w-]/, '') + '.html' + sd.type = SourceDeclaration::Type.new 'document.markdown' + sd.children = [] + sd.overview = overview Pathname(file_path) + sd.usr = 'documentation.' + sd.name + sd.abstract = '' + sd.return = '' + sd.parameters = [] + end + end + end + + def self.overview(file_path) + return '' unless file_path && file_path.exist? + file_path.read + end + + def self.documentation_entries + return [] unless + config.documentation_glob_configured && config.documentation_glob + config.documentation_glob.select { |e| File.file? e } + end + end +end diff --git a/lib/jazzy/source_declaration.rb b/lib/jazzy/source_declaration.rb index 6565bae88..5901feb81 100644 --- a/lib/jazzy/source_declaration.rb +++ b/lib/jazzy/source_declaration.rb @@ -8,6 +8,14 @@ class SourceDeclaration # static type of declared element (e.g. String.Type -> ()) attr_accessor :typename + def type?(type_kind) + respond_to?(:type) && type.kind == type_kind + end + + def render? + type?('document.markdown') || children.count != 0 + end + # Element containing this declaration in the code attr_accessor :parent_in_code @@ -69,7 +77,26 @@ def objc_category_name attr_accessor :nav_order def overview - "#{abstract}\n\n#{discussion}".strip + alternative_abstract || "#{abstract}\n\n#{discussion}".strip + end + + def alternative_abstract + if file = alternative_abstract_file + Pathname(file).read + end + end + + def alternative_abstract_file + abstract_glob.select do |f| + File.basename(f).split('.').first == name + end.first + end + + def abstract_glob + return [] unless + Config.instance.abstract_glob_configured && + Config.instance.abstract_glob + Config.instance.abstract_glob.select { |e| File.file? e } end end end diff --git a/lib/jazzy/source_declaration/type.rb b/lib/jazzy/source_declaration/type.rb index ec8d9cbd4..aca586522 100644 --- a/lib/jazzy/source_declaration/type.rb +++ b/lib/jazzy/source_declaration/type.rb @@ -102,8 +102,13 @@ def ==(other) end TYPES = { - # Objective-C + # Markdown + 'document.markdown' => { + jazzy: 'Guide', + dash: 'Guide', + }.freeze, + # Objective-C 'sourcekitten.source.lang.objc.decl.unexposed' => { jazzy: 'Unexposed', dash: 'Unexposed', diff --git a/lib/jazzy/source_document.rb b/lib/jazzy/source_document.rb new file mode 100644 index 000000000..bc68c9e77 --- /dev/null +++ b/lib/jazzy/source_document.rb @@ -0,0 +1,72 @@ +require 'pathname' + +require 'jazzy/jazzy_markdown' + +module Jazzy + class SourceDocument < SourceDeclaration + attr_accessor :overview + attr_accessor :readme_path + + def config + Config.instance + end + + def url + name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '') + '.html' + end + + def content(source_module) + return readme_content(source_module) if name == 'index' + overview + end + + def readme_content(source_module) + config_readme || fallback_readme || generated_readme(source_module) + end + + def config_readme + readme_path.read if readme_path && readme_path.exist? + end + + def fallback_readme + %w(README.md README.markdown README.mdown README).each do |potential_name| + file = config.source_directory + potential_name + return file.read if file.exist? + end + false + end + + def generated_readme(source_module) + if podspec = config.podspec + ### License + + # #{license[:license]} + <<-EOS +# #{podspec.name} + +### #{podspec.summary} + +#{podspec.description} + +### Installation + +```ruby +pod '#{podspec.name}' +``` + +### Authors + +#{source_module.author_name} +EOS + else + <<-EOS +# #{source_module.name} + +### Authors + +#{source_module.author_name} +EOS + end + end + end +end diff --git a/lib/jazzy/source_module.rb b/lib/jazzy/source_module.rb index 3486cd4d4..dfe454f5d 100644 --- a/lib/jazzy/source_module.rb +++ b/lib/jazzy/source_module.rb @@ -37,7 +37,7 @@ def all_declarations d.map(&:children).each { |c| visitor[c] } end visitor[docs] - all_declarations + all_declarations.select { |doc| doc.name != 'index' } end end end diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index 71467327a..ce5d5a1d5 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -493,11 +493,11 @@ def self.reject_objc_enum_typedefs(docs) # Parse sourcekitten STDOUT output as JSON # @return [Hash] structured docs - def self.parse(sourcekitten_output, min_acl, skip_undocumented) + def self.parse(sourcekitten_output, min_acl, skip_undocumented, inject_docs) @min_acl = min_acl @skip_undocumented = skip_undocumented sourcekitten_json = filter_excluded_files(JSON.parse(sourcekitten_output)) - docs = make_source_declarations(sourcekitten_json) + docs = make_source_declarations(sourcekitten_json).concat inject_docs docs = ungrouped_docs = deduplicate_declarations(docs) docs = group_docs(docs) if Config.instance.objc_mode diff --git a/spec/integration_specs b/spec/integration_specs index 7d8348b57..06d2fdc07 160000 --- a/spec/integration_specs +++ b/spec/integration_specs @@ -1 +1 @@ -Subproject commit 7d8348b5756a1f21b0313486a361e976ade9d7f5 +Subproject commit 06d2fdc07ddb76d863b61201689ddf9424889c28