jazzy 0.14.4 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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