jazzy 0.14.4 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +6 -5
  3. data/.rubocop.yml +13 -0
  4. data/CHANGELOG.md +40 -0
  5. data/CONTRIBUTING.md +1 -1
  6. data/Gemfile.lock +62 -47
  7. data/README.md +115 -5
  8. data/bin/sourcekitten +0 -0
  9. data/js/package-lock.json +6 -6
  10. data/lib/jazzy/config.rb +156 -24
  11. data/lib/jazzy/doc.rb +2 -2
  12. data/lib/jazzy/doc_builder.rb +55 -29
  13. data/lib/jazzy/doc_index.rb +185 -0
  14. data/lib/jazzy/docset_builder/info_plist.mustache +1 -1
  15. data/lib/jazzy/docset_builder.rb +44 -13
  16. data/lib/jazzy/extensions/katex/css/katex.min.css +1 -1
  17. data/lib/jazzy/extensions/katex/js/katex.min.js +1 -1
  18. data/lib/jazzy/gem_version.rb +1 -1
  19. data/lib/jazzy/grouper.rb +130 -0
  20. data/lib/jazzy/podspec_documenter.rb +1 -1
  21. data/lib/jazzy/source_declaration/type.rb +10 -2
  22. data/lib/jazzy/source_declaration.rb +69 -8
  23. data/lib/jazzy/source_document.rb +5 -1
  24. data/lib/jazzy/source_module.rb +13 -11
  25. data/lib/jazzy/sourcekitten.rb +231 -237
  26. data/lib/jazzy/symbol_graph/ext_key.rb +37 -0
  27. data/lib/jazzy/symbol_graph/ext_node.rb +23 -6
  28. data/lib/jazzy/symbol_graph/graph.rb +31 -19
  29. data/lib/jazzy/symbol_graph/relationship.rb +21 -3
  30. data/lib/jazzy/symbol_graph/sym_node.rb +10 -22
  31. data/lib/jazzy/symbol_graph/symbol.rb +28 -0
  32. data/lib/jazzy/symbol_graph.rb +19 -16
  33. data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +10 -7
  34. data/lib/jazzy/themes/apple/assets/js/typeahead.jquery.js +3 -2
  35. data/lib/jazzy/themes/apple/templates/doc.mustache +8 -1
  36. data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +5 -5
  37. data/lib/jazzy/themes/fullwidth/assets/js/typeahead.jquery.js +3 -2
  38. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +8 -1
  39. data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +6 -5
  40. data/lib/jazzy/themes/jony/templates/doc.mustache +9 -2
  41. data/spec/integration_spec.rb +8 -1
  42. metadata +5 -2
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Jazzy
4
- VERSION = '0.14.4' unless defined? Jazzy::VERSION
4
+ VERSION = '0.15.0' unless defined? Jazzy::VERSION
5
5
  end
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jazzy
4
+ # This module deals with arranging top-level declarations and guides into
5
+ # groups automatically and/or using a custom list.
6
+ module Grouper
7
+ extend Config::Mixin
8
+
9
+ # Group root-level docs by custom categories (if any) and type or module
10
+ def self.group_docs(docs, doc_index)
11
+ custom_categories, docs = group_custom_categories(docs, doc_index)
12
+ unlisted_prefix = config.custom_categories_unlisted_prefix
13
+ type_category_prefix = custom_categories.any? ? unlisted_prefix : ''
14
+ all_categories =
15
+ custom_categories +
16
+ if config.merge_modules == :all
17
+ group_docs_by_type(docs, type_category_prefix)
18
+ else
19
+ group_docs_by_module(docs, type_category_prefix)
20
+ end
21
+ merge_consecutive_marks(all_categories)
22
+ end
23
+
24
+ # Group root-level docs by type
25
+ def self.group_docs_by_type(docs, type_category_prefix)
26
+ type_groups = SourceDeclaration::Type.all.map do |type|
27
+ children, docs = docs.partition { _1.type == type }
28
+ make_type_group(children, type, type_category_prefix)
29
+ end
30
+ merge_categories(type_groups.compact) + docs
31
+ end
32
+
33
+ # Group root-level docs by module name
34
+ def self.group_docs_by_module(docs, type_category_prefix)
35
+ guide_categories, docs = group_guides(docs, type_category_prefix)
36
+
37
+ module_categories = docs
38
+ .group_by(&:doc_module_name)
39
+ .map do |name, module_docs|
40
+ make_group(
41
+ module_docs,
42
+ name,
43
+ "The following declarations are provided by module #{name}.",
44
+ )
45
+ end
46
+
47
+ guide_categories + module_categories
48
+ end
49
+
50
+ def self.group_custom_categories(docs, doc_index)
51
+ group = config.custom_categories.map do |category|
52
+ children = category['children'].filter_map do |name|
53
+ unless doc = doc_index.lookup(name)
54
+ warn 'WARNING: No documented top-level declarations match ' \
55
+ "name \"#{name}\" specified in categories file"
56
+ next nil
57
+ end
58
+
59
+ unless doc.parent_in_code.nil?
60
+ warn "WARNING: Declaration \"#{doc.fully_qualified_module_name}\" " \
61
+ 'specified in categories file exists but is not top-level and ' \
62
+ 'cannot be included here'
63
+ next nil
64
+ end
65
+
66
+ docs.delete(doc)
67
+ end
68
+ # Category config overrides alphabetization
69
+ children.each.with_index { |child, i| child.nav_order = i }
70
+ make_group(children, category['name'], '')
71
+ end
72
+ [group.compact, docs]
73
+ end
74
+
75
+ def self.group_guides(docs, prefix)
76
+ guides, others = docs.partition { _1.type.markdown? }
77
+ return [[], others] unless guides.any?
78
+
79
+ [[make_type_group(guides, guides.first.type, prefix)], others]
80
+ end
81
+
82
+ def self.make_type_group(docs, type, type_category_prefix)
83
+ make_group(
84
+ docs,
85
+ type_category_prefix + type.plural_name,
86
+ "The following #{type.plural_name.downcase} are available globally.",
87
+ type_category_prefix + type.plural_url_name,
88
+ )
89
+ end
90
+
91
+ # Join categories with the same name (eg. ObjC and Swift classes)
92
+ def self.merge_categories(categories)
93
+ merged = []
94
+ categories.each do |new_category|
95
+ if existing = merged.find { _1.name == new_category.name }
96
+ existing.children += new_category.children
97
+ else
98
+ merged.append(new_category)
99
+ end
100
+ end
101
+ merged
102
+ end
103
+
104
+ def self.make_group(group, name, abstract, url_name = nil)
105
+ group.reject! { _1.name.empty? }
106
+ unless group.empty?
107
+ SourceDeclaration.new.tap do |sd|
108
+ sd.type = SourceDeclaration::Type.overview
109
+ sd.name = name
110
+ sd.url_name = url_name
111
+ sd.abstract = Markdown.render(abstract)
112
+ sd.children = group
113
+ end
114
+ end
115
+ end
116
+
117
+ # Merge consecutive sections with the same mark into one section
118
+ # Needed because of pulling various decls into groups
119
+ def self.merge_consecutive_marks(docs)
120
+ prev_mark = nil
121
+ docs.each do |doc|
122
+ if prev_mark&.can_merge?(doc.mark)
123
+ doc.mark = prev_mark
124
+ end
125
+ prev_mark = doc.mark
126
+ merge_consecutive_marks(doc.children)
127
+ end
128
+ end
129
+ end
130
+ end
@@ -52,7 +52,7 @@ module Jazzy
52
52
  config.author_name = author_name(podspec)
53
53
  config.author_name_configured = true
54
54
  end
55
- unless config.module_name_configured
55
+ unless config.module_name_known?
56
56
  config.module_name = podspec.module_name
57
57
  config.module_name_configured = true
58
58
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support'
3
4
  require 'active_support/inflector'
4
5
 
5
6
  module Jazzy
@@ -163,8 +164,14 @@ module Jazzy
163
164
  kind == 'sourcekitten.source.lang.objc.decl.unexposed'
164
165
  end
165
166
 
167
+ OVERVIEW_KIND = 'Overview'
168
+
166
169
  def self.overview
167
- Type.new('Overview')
170
+ Type.new(OVERVIEW_KIND)
171
+ end
172
+
173
+ def overview?
174
+ kind == OVERVIEW_KIND
168
175
  end
169
176
 
170
177
  MARKDOWN_KIND = 'document.markdown'
@@ -193,7 +200,8 @@ module Jazzy
193
200
  dash: 'Guide',
194
201
  }.freeze,
195
202
 
196
- 'Overview' => {
203
+ # Group/Overview
204
+ OVERVIEW_KIND => {
197
205
  jazzy: nil,
198
206
  dash: 'Section',
199
207
  }.freeze,
@@ -62,6 +62,7 @@ module Jazzy
62
62
  end
63
63
  end
64
64
 
65
+ # 'OuterType.NestedType.method(arg:)'
65
66
  def fully_qualified_name
66
67
  namespace_path.map(&:name).join('.')
67
68
  end
@@ -74,6 +75,21 @@ module Jazzy
74
75
  .join('(?:<.*?>)?\.'))
75
76
  end
76
77
 
78
+ def fully_qualified_module_name_parts
79
+ path = namespace_path
80
+ path.map(&:name).prepend(path.first.module_name).compact
81
+ end
82
+
83
+ # 'MyModule.OuterType.NestedType.method(arg:)'
84
+ def fully_qualified_module_name
85
+ fully_qualified_module_name_parts.join('.')
86
+ end
87
+
88
+ # List of doc_parent decls, .last is self
89
+ def docs_path
90
+ (parent_in_docs&.docs_path || []) + [self]
91
+ end
92
+
77
93
  # If this declaration is an objc category, returns an array with the name
78
94
  # of the extended objc class and the category name itself, i.e.
79
95
  # ["NSString", "MyMethods"], nil otherwise.
@@ -114,7 +130,7 @@ module Jazzy
114
130
  attr_accessor :column
115
131
  attr_accessor :usr
116
132
  attr_accessor :type_usr
117
- attr_accessor :modulename
133
+ attr_accessor :module_name
118
134
  attr_accessor :name
119
135
  attr_accessor :objc_name
120
136
  attr_accessor :declaration
@@ -140,6 +156,11 @@ module Jazzy
140
156
  attr_accessor :inherited_types
141
157
  attr_accessor :async
142
158
 
159
+ # The name of the module being documented that contains this
160
+ # declaration. Only different from module_name when this is
161
+ # an extension of a type from another module. Nil for guides.
162
+ attr_accessor :doc_module_name
163
+
143
164
  def usage_discouraged?
144
165
  unavailable || deprecated
145
166
  end
@@ -183,20 +204,60 @@ module Jazzy
183
204
  inherited_types.any? { |t| !unwanted.include?(t) }
184
205
  end
185
206
 
186
- # Pre-Swift 5.6: SourceKit only sets modulename for imported modules
187
- # Swift 5.6+: modulename is always set
207
+ # Pre-Swift 5.6: SourceKit only sets module_name for imported modules
208
+ # Swift 5.6+: module_name is always set
188
209
  def type_from_doc_module?
189
210
  !type.extension? ||
190
211
  (swift? && usr &&
191
- (modulename.nil? || modulename == Config.instance.module_name))
212
+ (module_name.nil? || module_name == doc_module_name))
213
+ end
214
+
215
+ # Don't ask the user to write documentation for types being extended
216
+ # from other modules. Compile errors leave no docs and a `nil` USR.
217
+ def mark_undocumented?
218
+ !swift? || (usr && !extension_of_external_type?)
219
+ end
220
+
221
+ def extension_of_external_type?
222
+ !module_name.nil? &&
223
+ !Config.instance.module_name?(module_name)
224
+ end
225
+
226
+ # Is it unclear from context what module the (top-level) decl is from?
227
+ def ambiguous_module_name?(group_name)
228
+ extension_of_external_type? ||
229
+ (Config.instance.multiple_modules? &&
230
+ !module_name.nil? &&
231
+ group_name != module_name)
232
+ end
233
+
234
+ # Does the user need help understanding how to get this declaration?
235
+ def need_doc_module_note?
236
+ return false unless Config.instance.multiple_modules?
237
+ return false if docs_path.first.name == doc_module_name
238
+
239
+ if parent_in_code.nil?
240
+ # Top-level decls with no page of their own
241
+ !render_as_page?
242
+ else
243
+ # Members added by extension
244
+ parent_in_code.module_name != doc_module_name
245
+ end
192
246
  end
193
247
 
194
248
  # Info text for contents page by collapsed item name
195
249
  def declaration_note
196
- notes = [default_impl_abstract ? 'default implementation' : nil,
197
- from_protocol_extension ? 'extension method' : nil,
198
- async ? 'asynchronous' : nil].compact
199
- notes.join(', ').humanize unless notes.empty?
250
+ notes = [
251
+ default_impl_abstract ? 'default implementation' : nil,
252
+ from_protocol_extension ? 'extension method' : nil,
253
+ async ? 'asynchronous' : nil,
254
+ need_doc_module_note? ? "from #{doc_module_name}" : nil,
255
+ ].compact
256
+ notes.join(', ').upcase_first unless notes.empty?
257
+ end
258
+
259
+ def readme?
260
+ false
200
261
  end
201
262
 
202
263
  def alternative_abstract
@@ -27,6 +27,10 @@ module Jazzy
27
27
  end
28
28
  end
29
29
 
30
+ def readme?
31
+ url == 'index.html'
32
+ end
33
+
30
34
  def render_as_page?
31
35
  true
32
36
  end
@@ -89,7 +93,7 @@ pod '#{podspec.name}'
89
93
  README
90
94
  else
91
95
  <<-README
92
- # #{source_module.name}
96
+ # #{source_module.readme_title}
93
97
 
94
98
  ### Authors
95
99
 
@@ -7,28 +7,30 @@ require 'jazzy/source_declaration'
7
7
  require 'jazzy/source_host'
8
8
 
9
9
  module Jazzy
10
+ # A cache of info that is common across all page templating, gathered
11
+ # from other parts of the program.
10
12
  class SourceModule
11
- attr_accessor :name
13
+ include Config::Mixin
14
+
15
+ attr_accessor :readme_title
12
16
  attr_accessor :docs
13
17
  attr_accessor :doc_coverage
14
18
  attr_accessor :doc_structure
15
19
  attr_accessor :author_name
16
20
  attr_accessor :author_url
17
- attr_accessor :dash_url
21
+ attr_accessor :dash_feed_url
18
22
  attr_accessor :host
19
23
 
20
- def initialize(options, docs, doc_structure, doc_coverage)
24
+ def initialize(docs, doc_structure, doc_coverage, docset_builder)
21
25
  self.docs = docs
22
26
  self.doc_structure = doc_structure
23
27
  self.doc_coverage = doc_coverage
24
- self.name = options.module_name
25
- self.author_name = options.author_name
26
- self.author_url = options.author_url
27
- self.host = SourceHost.create(options)
28
- return unless options.dash_url
29
-
30
- self.dash_url =
31
- "dash-feed://#{ERB::Util.url_encode(options.dash_url.to_s)}"
28
+ title = config.readme_title || config.module_names.first
29
+ self.readme_title = title.empty? ? 'Index' : title
30
+ self.author_name = config.author_name
31
+ self.author_url = config.author_url
32
+ self.host = SourceHost.create(config)
33
+ self.dash_feed_url = docset_builder.dash_feed_url
32
34
  end
33
35
 
34
36
  def all_declarations