jazzy 0.13.7 → 0.14.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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/Tests.yml +2 -2
  3. data/.rubocop.yml +123 -24
  4. data/CHANGELOG.md +34 -0
  5. data/Dangerfile +11 -8
  6. data/Gemfile +3 -1
  7. data/Gemfile.lock +49 -34
  8. data/README.md +23 -11
  9. data/Rakefile +13 -12
  10. data/bin/jazzy +3 -2
  11. data/jazzy.gemspec +8 -6
  12. data/lib/jazzy.rb +2 -0
  13. data/lib/jazzy/config.rb +116 -69
  14. data/lib/jazzy/doc.rb +3 -1
  15. data/lib/jazzy/doc_builder.rb +63 -81
  16. data/lib/jazzy/docset_builder.rb +3 -1
  17. data/lib/jazzy/documentation_generator.rb +6 -2
  18. data/lib/jazzy/executable.rb +3 -0
  19. data/lib/jazzy/extensions/bitbucket/img/bitbucket.svg +11 -0
  20. data/lib/jazzy/{themes/apple/assets → extensions/github}/img/gh.png +0 -0
  21. data/lib/jazzy/extensions/gitlab/img/gitlab.svg +23 -0
  22. data/lib/jazzy/gem_version.rb +3 -1
  23. data/lib/jazzy/highlighter.rb +5 -3
  24. data/lib/jazzy/jazzy_markdown.rb +63 -30
  25. data/lib/jazzy/podspec_documenter.rb +14 -16
  26. data/lib/jazzy/search_builder.rb +4 -3
  27. data/lib/jazzy/source_declaration.rb +9 -3
  28. data/lib/jazzy/source_declaration/access_control_level.rb +7 -5
  29. data/lib/jazzy/source_declaration/type.rb +3 -1
  30. data/lib/jazzy/source_document.rb +8 -5
  31. data/lib/jazzy/source_host.rb +111 -0
  32. data/lib/jazzy/source_mark.rb +8 -6
  33. data/lib/jazzy/source_module.rb +6 -6
  34. data/lib/jazzy/sourcekitten.rb +98 -72
  35. data/lib/jazzy/stats.rb +4 -2
  36. data/lib/jazzy/symbol_graph.rb +15 -15
  37. data/lib/jazzy/symbol_graph/constraint.rb +5 -1
  38. data/lib/jazzy/symbol_graph/ext_node.rb +3 -1
  39. data/lib/jazzy/symbol_graph/graph.rb +13 -11
  40. data/lib/jazzy/symbol_graph/relationship.rb +3 -0
  41. data/lib/jazzy/symbol_graph/sym_node.rb +11 -6
  42. data/lib/jazzy/symbol_graph/symbol.rb +18 -15
  43. data/lib/jazzy/themes/apple/assets/css/highlight.css.scss +63 -59
  44. data/lib/jazzy/themes/apple/assets/css/jazzy.css.scss +5 -1
  45. data/lib/jazzy/themes/apple/assets/js/jazzy.js +4 -0
  46. data/lib/jazzy/themes/apple/assets/js/jazzy.search.js +4 -0
  47. data/lib/jazzy/themes/apple/templates/doc.mustache +3 -3
  48. data/lib/jazzy/themes/apple/templates/footer.mustache +1 -1
  49. data/lib/jazzy/themes/apple/templates/header.mustache +3 -3
  50. data/lib/jazzy/themes/apple/templates/task.mustache +3 -3
  51. data/lib/jazzy/themes/fullwidth/assets/css/highlight.css.scss +63 -59
  52. data/lib/jazzy/themes/fullwidth/assets/css/jazzy.css.scss +6 -2
  53. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.js +4 -0
  54. data/lib/jazzy/themes/fullwidth/assets/js/jazzy.search.js +4 -0
  55. data/lib/jazzy/themes/fullwidth/templates/doc.mustache +3 -3
  56. data/lib/jazzy/themes/fullwidth/templates/footer.mustache +1 -1
  57. data/lib/jazzy/themes/fullwidth/templates/header.mustache +5 -5
  58. data/lib/jazzy/themes/fullwidth/templates/task.mustache +3 -3
  59. data/lib/jazzy/themes/jony/assets/css/highlight.css.scss +63 -59
  60. data/lib/jazzy/themes/jony/assets/css/jazzy.css.scss +5 -1
  61. data/lib/jazzy/themes/jony/assets/js/jazzy.js +4 -0
  62. data/lib/jazzy/themes/jony/templates/doc.mustache +3 -3
  63. data/lib/jazzy/themes/jony/templates/footer.mustache +1 -1
  64. data/lib/jazzy/themes/jony/templates/header.mustache +5 -5
  65. data/lib/jazzy/themes/jony/templates/task.mustache +3 -3
  66. data/spec/integration_spec.rb +39 -36
  67. data/spec/spec_helper.rb +3 -1
  68. data/spec/spec_helper/pre_flight.rb +2 -0
  69. metadata +27 -13
  70. data/lib/jazzy/themes/fullwidth/assets/img/gh.png +0 -0
  71. data/lib/jazzy/themes/jony/assets/img/gh.png +0 -0
  72. data/spec/sourcekitten_spec.rb +0 -6
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tmpdir'
2
4
  require 'json'
3
5
 
@@ -22,8 +24,8 @@ module Jazzy
22
24
  installer.install!
23
25
  stdout = Dir.chdir(sandbox.root) do
24
26
  targets = installer.pod_targets
25
- .select { |pt| pt.pod_name == podspec.root.name }
26
- .map(&:label)
27
+ .select { |pt| pt.pod_name == podspec.root.name }
28
+ .map(&:label)
27
29
 
28
30
  targets.map do |t|
29
31
  args = %W[doc --module-name #{podspec.module_name} -- -target #{t}]
@@ -42,8 +44,6 @@ module Jazzy
42
44
  end
43
45
  end
44
46
 
45
- # rubocop:disable Metrics/CyclomaticComplexity
46
- # rubocop:disable Metrics/PerceivedComplexity
47
47
  # rubocop:disable Metrics/MethodLength
48
48
  def self.apply_config_defaults(podspec, config)
49
49
  return unless podspec
@@ -64,9 +64,9 @@ module Jazzy
64
64
  config.version = podspec.version.to_s
65
65
  config.version_configured = true
66
66
  end
67
- unless config.github_file_prefix_configured
68
- config.github_file_prefix = github_file_prefix(podspec)
69
- config.github_file_prefix_configured = true
67
+ unless config.source_host_files_url_configured
68
+ config.source_host_files_url = github_file_prefix(podspec)
69
+ config.source_host_files_url_configured = true
70
70
  end
71
71
  unless config.swift_version_configured
72
72
  trunk_swift_build = podspec.attributes_hash['pushed_with_swift_version']
@@ -74,8 +74,6 @@ module Jazzy
74
74
  config.swift_version_configured = true
75
75
  end
76
76
  end
77
- # rubocop:enable Metrics/CyclomaticComplexity
78
- # rubocop:enable Metrics/PerceivedComplexity
79
77
  # rubocop:enable Metrics/MethodLength
80
78
 
81
79
  private
@@ -94,17 +92,17 @@ module Jazzy
94
92
 
95
93
  def self.github_file_prefix(podspec)
96
94
  return unless podspec.source[:url] =~ %r{github.com[:/]+(.+)/(.+)}
95
+
97
96
  org, repo = Regexp.last_match
98
- return unless org && repo
99
- repo.sub!(/\.git$/, '')
100
97
  return unless rev = podspec.source[:tag] || podspec.source[:commit]
101
- "https://github.com/#{org}/#{repo}/blob/#{rev}"
98
+
99
+ "https://github.com/#{org}/#{repo.sub(/\.git$/, '')}/blob/#{rev}"
102
100
  end
103
101
 
104
102
  private_class_method :github_file_prefix
105
103
 
106
104
  # Latest valid value for SWIFT_VERSION.
107
- LATEST_SWIFT_VERSION = '5'.freeze
105
+ LATEST_SWIFT_VERSION = '5'
108
106
 
109
107
  # All valid values for SWIFT_VERSION that are longer
110
108
  # than a major version number. Ordered ascending.
@@ -162,9 +160,8 @@ module Jazzy
162
160
  # Travis builds take too long when building docs for all available
163
161
  # platforms for the Moya integration spec, so we just document OSX.
164
162
  # TODO: remove once jazzy is fast enough.
165
- if ENV['JAZZY_INTEGRATION_SPECS']
166
- next if p.name != :osx
167
- end
163
+ next if ENV['JAZZY_INTEGRATION_SPECS'] && p.name != :osx
164
+
168
165
  target("Jazzy-#{ss.name.gsub('/', '__')}-#{p.name}") do
169
166
  use_frameworks!
170
167
  platform p.name, p.deployment_target
@@ -177,4 +174,5 @@ module Jazzy
177
174
  end
178
175
  # rubocop:enable Metrics/MethodLength
179
176
  end
177
+ # rubocop:enable Metrics/ClassLength
180
178
  end
@@ -1,18 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jazzy
2
4
  module SearchBuilder
3
5
  def self.build(source_module, output_dir)
4
6
  decls = source_module.all_declarations.select do |d|
5
7
  d.type && d.name && !d.name.empty?
6
8
  end
7
- index = Hash[decls.map do |d|
9
+ index = decls.map do |d|
8
10
  [d.url,
9
11
  {
10
12
  name: d.name,
11
13
  abstract: d.abstract && d.abstract.split(/\n/).map(&:strip).first,
12
14
  parent_name: d.parent_in_code && d.parent_in_code.name,
13
15
  }.reject { |_, v| v.nil? || v.empty? }]
14
- end
15
- ]
16
+ end.to_h
16
17
  File.open(File.join(output_dir, 'search.json'), 'w') do |f|
17
18
  f.write(index.to_json)
18
19
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'jazzy/source_declaration/access_control_level'
2
4
  require 'jazzy/source_declaration/type'
3
5
 
4
- # rubocop:disable Metrics/ClassLength
5
6
  module Jazzy
7
+ # rubocop:disable Metrics/ClassLength
6
8
  class SourceDeclaration
7
9
  # kind of declaration (e.g. class, variable, function)
8
10
  attr_accessor :type
@@ -39,7 +41,7 @@ module Jazzy
39
41
  attr_accessor :parent_in_docs
40
42
 
41
43
  # counterpart of parent_in_docs
42
- attr_accessor :children
44
+ attr_reader :children
43
45
 
44
46
  def children=(new_children)
45
47
  # Freeze to ensure that parent_in_docs stays in sync
@@ -76,7 +78,7 @@ module Jazzy
76
78
  # of the extended objc class and the category name itself, i.e.
77
79
  # ["NSString", "MyMethods"], nil otherwise.
78
80
  def objc_category_name
79
- name.split(/[\(\)]/) if type.objc_category?
81
+ name.split(/[()]/) if type.objc_category?
80
82
  end
81
83
 
82
84
  def swift_objc_extension?
@@ -151,6 +153,7 @@ module Jazzy
151
153
  # Workaround functions sharing names with
152
154
  # different argument types (f(a:Int) vs. f(a:String))
153
155
  return result unless type.swift_global_function?
156
+
154
157
  result + "_#{type_usr}"
155
158
  end
156
159
 
@@ -175,6 +178,7 @@ module Jazzy
175
178
  # Is there at least one inherited type that is not in the given list?
176
179
  def other_inherited_types?(unwanted)
177
180
  return false unless inherited_types?
181
+
178
182
  inherited_types.any? { |t| !unwanted.include?(t) }
179
183
  end
180
184
 
@@ -201,7 +205,9 @@ module Jazzy
201
205
  return [] unless
202
206
  Config.instance.abstract_glob_configured &&
203
207
  Config.instance.abstract_glob
208
+
204
209
  Config.instance.abstract_glob.select { |e| File.file? e }
205
210
  end
206
211
  end
212
+ # rubocop:enable Metrics/ClassLength
207
213
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jazzy
2
4
  class SourceDeclaration
3
5
  class AccessControlLevel
@@ -5,12 +7,12 @@ module Jazzy
5
7
 
6
8
  attr_reader :level
7
9
 
8
- ACCESSIBILITY_PRIVATE = 'source.lang.swift.accessibility.private'.freeze
10
+ ACCESSIBILITY_PRIVATE = 'source.lang.swift.accessibility.private'
9
11
  ACCESSIBILITY_FILEPRIVATE =
10
- 'source.lang.swift.accessibility.fileprivate'.freeze
11
- ACCESSIBILITY_INTERNAL = 'source.lang.swift.accessibility.internal'.freeze
12
- ACCESSIBILITY_PUBLIC = 'source.lang.swift.accessibility.public'.freeze
13
- ACCESSIBILITY_OPEN = 'source.lang.swift.accessibility.open'.freeze
12
+ 'source.lang.swift.accessibility.fileprivate'
13
+ ACCESSIBILITY_INTERNAL = 'source.lang.swift.accessibility.internal'
14
+ ACCESSIBILITY_PUBLIC = 'source.lang.swift.accessibility.public'
15
+ ACCESSIBILITY_OPEN = 'source.lang.swift.accessibility.open'
14
16
 
15
17
  def initialize(accessibility)
16
18
  @level = case accessibility
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/inflector'
2
4
 
3
5
  module Jazzy
@@ -151,7 +153,7 @@ module Jazzy
151
153
  Type.new('Overview')
152
154
  end
153
155
 
154
- MARKDOWN_KIND = 'document.markdown'.freeze
156
+ MARKDOWN_KIND = 'document.markdown'
155
157
 
156
158
  def self.markdown
157
159
  Type.new(MARKDOWN_KIND)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'pathname'
2
4
 
3
5
  require 'jazzy/jazzy_markdown'
@@ -43,6 +45,7 @@ module Jazzy
43
45
 
44
46
  def content(source_module)
45
47
  return readme_content(source_module) if name == 'index'
48
+
46
49
  overview
47
50
  end
48
51
 
@@ -51,7 +54,7 @@ module Jazzy
51
54
  end
52
55
 
53
56
  def config_readme
54
- readme_path.read if readme_path && readme_path.exist?
57
+ readme_path.read if readme_path&.exist?
55
58
  end
56
59
 
57
60
  def fallback_readme
@@ -67,7 +70,7 @@ module Jazzy
67
70
  ### License
68
71
 
69
72
  # <a href="#{license[:url]}">#{license[:license]}</a>
70
- <<-EOS
73
+ <<-README
71
74
  # #{podspec.name}
72
75
 
73
76
  ### #{podspec.summary}
@@ -83,15 +86,15 @@ pod '#{podspec.name}'
83
86
  ### Authors
84
87
 
85
88
  #{source_module.author_name}
86
- EOS
89
+ README
87
90
  else
88
- <<-EOS
91
+ <<-README
89
92
  # #{source_module.name}
90
93
 
91
94
  ### Authors
92
95
 
93
96
  #{source_module.author_name}
94
- EOS
97
+ README
95
98
  end
96
99
  end
97
100
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jazzy
4
+ # Deal with different source code repositories
5
+ module SourceHost
6
+ # Factory to create the right source host
7
+ def self.create(options)
8
+ return unless options.source_host_url || options.source_host_files_url
9
+
10
+ case options.source_host
11
+ when :github then GitHub.new
12
+ when :gitlab then GitLab.new
13
+ when :bitbucket then BitBucket.new
14
+ end
15
+ end
16
+
17
+ # Use GitHub as the default behaviour.
18
+ class GitHub
19
+ include Config::Mixin
20
+
21
+ # Human readable name, appears in UI
22
+ def name
23
+ 'GitHub'
24
+ end
25
+
26
+ # Jazzy extension with logo
27
+ def extension
28
+ name.downcase
29
+ end
30
+
31
+ # Logo image filename within extension
32
+ def image
33
+ 'gh.png'
34
+ end
35
+
36
+ # URL to link to from logo
37
+ def url
38
+ config.source_host_url
39
+ end
40
+
41
+ # URL to link to from a SourceDeclaration.
42
+ # Compare using `realpath` because `item.file` comes out of
43
+ # SourceKit/etc.
44
+ def item_url(item)
45
+ return unless files_url && item.file
46
+
47
+ realpath = item.file.realpath
48
+ return unless realpath.to_path.start_with?(local_root_realpath)
49
+
50
+ path = realpath.relative_path_from(local_root_realpath)
51
+ fragment =
52
+ if item.start_line && (item.start_line != item.end_line)
53
+ item_url_multiline_fragment(item.start_line, item.end_line)
54
+ else
55
+ item_url_line_fragment(item.line)
56
+ end
57
+
58
+ "#{files_url}/#{path}##{fragment}"
59
+ end
60
+
61
+ private
62
+
63
+ def files_url
64
+ config.source_host_files_url
65
+ end
66
+
67
+ def local_root_realpath
68
+ @local_root_realpath ||= config.source_directory.realpath.to_path
69
+ end
70
+
71
+ # Source host's line numbering link scheme
72
+ def item_url_line_fragment(line)
73
+ "L#{line}"
74
+ end
75
+
76
+ def item_url_multiline_fragment(start_line, end_line)
77
+ "L#{start_line}-L#{end_line}"
78
+ end
79
+ end
80
+
81
+ # GitLab very similar to GitHub
82
+ class GitLab < GitHub
83
+ def name
84
+ 'GitLab'
85
+ end
86
+
87
+ def image
88
+ 'gitlab.svg'
89
+ end
90
+ end
91
+
92
+ # BitBucket has its own line number system
93
+ class BitBucket < GitHub
94
+ def name
95
+ 'Bitbucket'
96
+ end
97
+
98
+ def image
99
+ 'bitbucket.svg'
100
+ end
101
+
102
+ def item_url_line_fragment(line)
103
+ "lines-#{line}"
104
+ end
105
+
106
+ def item_url_multiline_fragment(start_line, end_line)
107
+ "lines-#{start_line}:#{end_line}"
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Jazzy
2
4
  class SourceMark
3
5
  attr_accessor :name
@@ -8,24 +10,24 @@ module Jazzy
8
10
  return unless mark_string
9
11
 
10
12
  # Format: 'MARK: - NAME -' with dashes optional
11
- mark_string.sub!(/^MARK: /, '')
13
+ mark_content = mark_string.sub(/^MARK: /, '')
12
14
 
13
- if mark_string.empty?
15
+ if mark_content.empty?
14
16
  # Empty
15
17
  return
16
- elsif mark_string == '-'
18
+ elsif mark_content == '-'
17
19
  # Separator
18
20
  self.has_start_dash = true
19
21
  return
20
22
  end
21
23
 
22
- self.has_start_dash = mark_string.start_with?('- ')
23
- self.has_end_dash = mark_string.end_with?(' -')
24
+ self.has_start_dash = mark_content.start_with?('- ')
25
+ self.has_end_dash = mark_content.end_with?(' -')
24
26
 
25
27
  start_index = has_start_dash ? 2 : 0
26
28
  end_index = has_end_dash ? -3 : -1
27
29
 
28
- self.name = mark_string[start_index..end_index]
30
+ self.name = mark_content[start_index..end_index]
29
31
  end
30
32
 
31
33
  def self.new_generic_requirements(requirements)
@@ -1,32 +1,32 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
 
3
5
  require 'jazzy/config'
4
6
  require 'jazzy/source_declaration'
7
+ require 'jazzy/source_host'
5
8
 
6
9
  module Jazzy
7
10
  class SourceModule
8
11
  attr_accessor :name
9
- attr_accessor :root_path
10
12
  attr_accessor :docs
11
13
  attr_accessor :doc_coverage
12
14
  attr_accessor :doc_structure
13
15
  attr_accessor :author_name
14
- attr_accessor :github_url
15
- attr_accessor :github_file_prefix
16
16
  attr_accessor :author_url
17
17
  attr_accessor :dash_url
18
+ attr_accessor :host
18
19
 
19
20
  def initialize(options, docs, doc_structure, doc_coverage)
20
21
  self.docs = docs
21
- self.root_path = options.source_directory
22
22
  self.doc_structure = doc_structure
23
23
  self.doc_coverage = doc_coverage
24
24
  self.name = options.module_name
25
25
  self.author_name = options.author_name
26
- self.github_url = options.github_url
27
- self.github_file_prefix = options.github_file_prefix
28
26
  self.author_url = options.author_url
27
+ self.host = SourceHost.create(options)
29
28
  return unless options.dash_url
29
+
30
30
  self.dash_url =
31
31
  "dash-feed://#{ERB::Util.url_encode(options.dash_url.to_s)}"
32
32
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'json'
2
4
  require 'pathname'
3
5
  require 'shellwords'
@@ -12,7 +14,7 @@ require 'jazzy/source_declaration'
12
14
  require 'jazzy/source_mark'
13
15
  require 'jazzy/stats'
14
16
 
15
- ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'.freeze
17
+ ELIDED_AUTOLINK_TOKEN = '36f8f5912051ae747ef441d6511ca4cb'
16
18
 
17
19
  def autolink_regex(middle_regex, after_highlight)
18
20
  start_tag_re, end_tag_re =
@@ -29,16 +31,15 @@ class String
29
31
  gsub(autolink_regex(middle_regex, after_highlight)) do
30
32
  original = Regexp.last_match(0)
31
33
  start_tag, raw_name, end_tag = Regexp.last_match.captures
32
- link_target = yield(CGI.unescape_html(raw_name))
34
+ link_target, display_name = yield(CGI.unescape_html(raw_name))
33
35
 
34
36
  if link_target &&
35
37
  !link_target.type.extension? &&
36
38
  link_target.url &&
37
39
  link_target.url != doc_url.split('#').first && # Don't link to parent
38
40
  link_target.url != doc_url # Don't link to self
39
- start_tag +
40
- "<a href=\"#{ELIDED_AUTOLINK_TOKEN}#{link_target.url}\">" +
41
- raw_name + '</a>' + end_tag
41
+ "#{start_tag}<a href=\"#{ELIDED_AUTOLINK_TOKEN}#{link_target.url}\">" \
42
+ "#{CGI.escape_html(display_name)}</a>#{end_tag}"
42
43
  else
43
44
  original
44
45
  end
@@ -74,8 +75,8 @@ module Jazzy
74
75
  children = category['children'].flat_map do |name|
75
76
  docs_with_name, docs = docs.partition { |doc| doc.name == name }
76
77
  if docs_with_name.empty?
77
- STDERR.puts 'WARNING: No documented top-level declarations match ' \
78
- "name \"#{name}\" specified in categories file"
78
+ warn 'WARNING: No documented top-level declarations match ' \
79
+ "name \"#{name}\" specified in categories file"
79
80
  end
80
81
  docs_with_name
81
82
  end
@@ -129,7 +130,7 @@ module Jazzy
129
130
  def self.merge_consecutive_marks(docs)
130
131
  prev_mark = nil
131
132
  docs.each do |doc|
132
- if prev_mark && prev_mark.can_merge?(doc.mark)
133
+ if prev_mark&.can_merge?(doc.mark)
133
134
  doc.mark = prev_mark
134
135
  end
135
136
  prev_mark = doc.mark
@@ -141,9 +142,9 @@ module Jazzy
141
142
  unsafe_filename = doc.docs_filename
142
143
  sanitzation_enabled = Config.instance.use_safe_filenames
143
144
  if sanitzation_enabled && !doc.type.name_controlled_manually?
144
- return CGI.escape(unsafe_filename).gsub('_', '%5F').tr('%', '_')
145
+ CGI.escape(unsafe_filename).gsub('_', '%5F').tr('%', '_')
145
146
  else
146
- return unsafe_filename
147
+ unsafe_filename
147
148
  end
148
149
  end
149
150
 
@@ -162,11 +163,11 @@ module Jazzy
162
163
  # Don't create HTML page for this doc if it doesn't have children
163
164
  # Instead, make its link a hash-link on its parent's page
164
165
  if doc.typename == '<<error type>>'
165
- warn 'A compile error prevented ' + doc.fully_qualified_name +
166
- ' from receiving a unique USR. Documentation may be ' \
167
- 'incomplete. Please check for compile errors by running ' \
168
- '`xcodebuild` or `swift build` with arguments ' \
169
- "`#{Config.instance.build_tool_arguments.shelljoin}`."
166
+ warn "A compile error prevented #{doc.fully_qualified_name} " \
167
+ 'from receiving a unique USR. Documentation may be ' \
168
+ 'incomplete. Please check for compile errors by running ' \
169
+ '`xcodebuild` or `swift build` with arguments ' \
170
+ "`#{Config.instance.build_tool_arguments.shelljoin}`."
170
171
  end
171
172
  id = doc.usr
172
173
  unless id
@@ -174,11 +175,11 @@ module Jazzy
174
175
  warn "`#{id}` has no USR. First make sure all modules used in " \
175
176
  'your project have been imported. If all used modules are ' \
176
177
  'imported, please report this problem by filing an issue at ' \
177
- 'https://github.com/realm/jazzy/issues along with your Xcode ' \
178
- 'project. If this token is declared in an `#if` block, please ' \
179
- 'ignore this message.'
178
+ 'https://github.com/realm/jazzy/issues along with your ' \
179
+ 'Xcode project. If this token is declared in an `#if` block, ' \
180
+ 'please ignore this message.'
180
181
  end
181
- doc.url = doc.parent_in_docs.url + '#/' + id
182
+ doc.url = "#{doc.parent_in_docs.url}#/#{id}"
182
183
  end
183
184
  end
184
185
  end
@@ -189,6 +190,7 @@ module Jazzy
189
190
  # Declarations under outer namespace type (Structures, Classes, etc.)
190
191
  def self.subdir_for_doc(doc)
191
192
  return [] if doc.type.markdown?
193
+
192
194
  top_level_decl = doc.namespace_path.first
193
195
  if top_level_decl.type.name
194
196
  [top_level_decl.type.plural_url_name] +
@@ -258,6 +260,7 @@ module Jazzy
258
260
  unless xcode = XCInvoke::Xcode.find_swift_version(swift_version)
259
261
  raise "Unable to find an Xcode with swift version #{swift_version}."
260
262
  end
263
+
261
264
  env = xcode.as_env
262
265
  else
263
266
  env = ENV
@@ -269,8 +272,6 @@ module Jazzy
269
272
 
270
273
  def self.make_default_doc_info(declaration)
271
274
  # @todo: Fix these
272
- declaration.line = nil
273
- declaration.column = nil
274
275
  declaration.abstract = ''
275
276
  declaration.parameters = []
276
277
  declaration.children = []
@@ -278,13 +279,12 @@ module Jazzy
278
279
 
279
280
  def self.availability_attribute?(doc)
280
281
  return false unless doc['key.attributes']
282
+
281
283
  !doc['key.attributes'].select do |attribute|
282
284
  attribute.values.first == 'source.decl.attribute.available'
283
285
  end.empty?
284
286
  end
285
287
 
286
- # rubocop:disable Metrics/CyclomaticComplexity
287
- # rubocop:disable Metrics/PerceivedComplexity
288
288
  def self.should_document?(doc)
289
289
  return false if doc['key.doc.comment'].to_s.include?(':nodoc:')
290
290
 
@@ -314,8 +314,6 @@ module Jazzy
314
314
  end
315
315
  acl_ok
316
316
  end
317
- # rubocop:enable Metrics/CyclomaticComplexity
318
- # rubocop:enable Metrics/PerceivedComplexity
319
317
 
320
318
  def self.should_document_swift_extension?(doc)
321
319
  doc['key.inheritedtypes'] ||
@@ -337,6 +335,7 @@ module Jazzy
337
335
  if !declaration.swift? || should_mark_undocumented(declaration)
338
336
  @stats.add_undocumented(declaration)
339
337
  return nil if @skip_undocumented
338
+
340
339
  declaration.abstract = undocumented_abstract
341
340
  else
342
341
  declaration.abstract = Markdown.render(doc['key.doc.comment'] || '',
@@ -404,7 +403,7 @@ module Jazzy
404
403
  def self.xml_to_text(xml)
405
404
  document = REXML::Document.new(xml)
406
405
  REXML::XPath.match(document.root, '//text()').map(&:value).join
407
- rescue
406
+ rescue StandardError
408
407
  ''
409
408
  end
410
409
 
@@ -490,11 +489,10 @@ module Jazzy
490
489
  end
491
490
 
492
491
  # @available attrs only in compiler 'interface' style
493
- available_attrs = extract_availability(doc['key.doc.declaration'] || '')
494
-
495
- available_attrs.concat(extract_attributes(annotated_decl_attrs))
496
- .push(decl)
497
- .join("\n")
492
+ extract_availability(doc['key.doc.declaration'] || '')
493
+ .concat(extract_attributes(annotated_decl_attrs))
494
+ .push(decl)
495
+ .join("\n")
498
496
  end
499
497
 
500
498
  # Strip default property attributes because libclang
@@ -510,12 +508,14 @@ module Jazzy
510
508
  attrs = Regexp.last_match[1].split(',').map(&:strip) - DEFAULT_ATTRIBUTES
511
509
  attrs_text = attrs.empty? ? '' : " (#{attrs.join(', ')})"
512
510
 
513
- declaration.sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
514
- .gsub(/\s+/, ' ')
511
+ declaration
512
+ .sub(/(?<=@property)\s+\(.*?\)/, attrs_text)
513
+ .gsub(/\s+/, ' ')
515
514
  end
516
515
 
517
516
  def self.make_substructure(doc, declaration)
518
517
  return [] unless subdocs = doc['key.substructure']
518
+
519
519
  make_source_declarations(subdocs,
520
520
  declaration,
521
521
  declaration.mark_for_children)
@@ -558,8 +558,8 @@ module Jazzy
558
558
 
559
559
  unless declaration.type.name
560
560
  raise 'Please file an issue at ' \
561
- 'https://github.com/realm/jazzy/issues about adding support ' \
562
- "for `#{declaration.type.kind}`."
561
+ 'https://github.com/realm/jazzy/issues about adding support ' \
562
+ "for `#{declaration.type.kind}`."
563
563
  end
564
564
 
565
565
  declaration.file = Pathname(doc['key.filepath']) if doc['key.filepath']
@@ -570,8 +570,8 @@ module Jazzy
570
570
  declaration.mark = current_mark
571
571
  declaration.access_control_level =
572
572
  SourceDeclaration::AccessControlLevel.from_doc(doc)
573
- declaration.line = doc['key.doc.line']
574
- declaration.column = doc['key.doc.column']
573
+ declaration.line = doc['key.doc.line'] || doc['key.line']
574
+ declaration.column = doc['key.doc.column'] || doc['key.column']
575
575
  declaration.start_line = doc['key.parsed_scope.start']
576
576
  declaration.end_line = doc['key.parsed_scope.end']
577
577
  declaration.deprecated = doc['key.always_deprecated']
@@ -583,10 +583,12 @@ module Jazzy
583
583
  inherited_types.map { |type| type['key.name'] }.compact
584
584
 
585
585
  next unless make_doc_info(doc, declaration)
586
+
586
587
  declaration.children = make_substructure(doc, declaration)
587
588
  next if declaration.type.extension? &&
588
589
  declaration.children.empty? &&
589
590
  !declaration.inherited_types?
591
+
590
592
  declarations << declaration
591
593
  end
592
594
  declarations
@@ -598,6 +600,7 @@ module Jazzy
598
600
  def self.find_generic_requirements(parsed_declaration)
599
601
  parsed_declaration =~ /\bwhere\s+(.*)$/m
600
602
  return nil unless Regexp.last_match
603
+
601
604
  Regexp.last_match[1].gsub(/\s+/, ' ')
602
605
  end
603
606
 
@@ -619,6 +622,7 @@ module Jazzy
619
622
 
620
623
  def self.expand_extension(extension, name_parts, decls)
621
624
  return extension if name_parts.empty?
625
+
622
626
  name = name_parts.shift
623
627
  candidates = decls.select { |decl| decl.name == name }
624
628
  SourceDeclaration.new.tap do |decl|
@@ -644,8 +648,8 @@ module Jazzy
644
648
  # Merges redundant declarations when documenting podspecs.
645
649
  def self.deduplicate_declarations(declarations)
646
650
  duplicate_groups = declarations
647
- .group_by { |d| deduplication_key(d, declarations) }
648
- .values
651
+ .group_by { |d| deduplication_key(d, declarations) }
652
+ .values
649
653
 
650
654
  duplicate_groups.flat_map do |group|
651
655
  # Put extended type (if present) before extensions
@@ -688,15 +692,17 @@ module Jazzy
688
692
  end
689
693
 
690
694
  # rubocop:disable Metrics/MethodLength
695
+ # rubocop:disable Metrics/PerceivedComplexity
691
696
  # Merges all of the given types and extensions into a single document.
692
697
  def self.merge_declarations(decls)
693
698
  extensions, typedecls = decls.partition { |d| d.type.extension? }
694
699
 
695
700
  if typedecls.size > 1
701
+ info = typedecls
702
+ .map { |t| "#{t.type.name.downcase} #{t.name}" }
703
+ .join(', ')
696
704
  warn 'Found conflicting type declarations with the same name, which ' \
697
- 'may indicate a build issue or a bug in Jazzy: ' +
698
- typedecls.map { |t| "#{t.type.name.downcase} #{t.name}" }
699
- .join(', ')
705
+ "may indicate a build issue or a bug in Jazzy: #{info}"
700
706
  end
701
707
  typedecl = typedecls.first
702
708
 
@@ -712,13 +718,14 @@ module Jazzy
712
718
  end
713
719
 
714
720
  # Keep type-aliases separate from any extensions
715
- if typedecl && typedecl.type.swift_typealias?
721
+ if typedecl&.type&.swift_typealias?
716
722
  [merge_type_and_extensions(typedecls, []),
717
723
  merge_type_and_extensions([], extensions)]
718
724
  else
719
725
  merge_type_and_extensions(typedecls, extensions)
720
726
  end
721
727
  end
728
+ # rubocop:enable Metrics/PerceivedComplexity
722
729
  # rubocop:enable Metrics/MethodLength
723
730
 
724
731
  def self.merge_type_and_extensions(typedecls, extensions)
@@ -810,6 +817,7 @@ module Jazzy
810
817
  # (unless they already have a mark)
811
818
  def self.merge_objc_declaration_marks(typedecl, extensions)
812
819
  return unless typedecl.type.objc_class?
820
+
813
821
  extensions.each do |ext|
814
822
  _, category_name = ext.objc_category_name
815
823
  ext.children.each { |c| c.mark.name ||= category_name }
@@ -819,7 +827,8 @@ module Jazzy
819
827
  # For each extension to be merged, move any MARK from the extension
820
828
  # declaration down to the extension contents so it still shows up.
821
829
  def self.move_merged_extension_marks(decls)
822
- return unless to_be_merged = decls[1..-1]
830
+ return unless to_be_merged = decls[1..]
831
+
823
832
  to_be_merged.each do |ext|
824
833
  child = ext.children.first
825
834
  if child && child.mark.empty?
@@ -834,7 +843,7 @@ module Jazzy
834
843
  def self.merge_code_declaration(decls)
835
844
  first = decls.first
836
845
 
837
- declarations = decls[1..-1].select do |decl|
846
+ declarations = decls[1..].select do |decl|
838
847
  decl.type.swift_extension? &&
839
848
  (decl.other_inherited_types?(@inaccessible_protocols) ||
840
849
  (first.type.swift_extension? && decl.constrained_extension?))
@@ -879,9 +888,11 @@ module Jazzy
879
888
 
880
889
  def self.name_match(name_part, docs)
881
890
  return nil unless name_part
891
+
882
892
  wildcard_expansion = Regexp.escape(name_part)
883
- .gsub('\.\.\.', '[^)]*')
884
- .gsub(/<.*>/, '')
893
+ .gsub('\.\.\.', '[^)]*')
894
+ .gsub(/<.*>/, '')
895
+
885
896
  whole_name_pat = /\A#{wildcard_expansion}\Z/
886
897
  docs.find do |doc|
887
898
  whole_name_pat =~ doc.name
@@ -911,19 +922,26 @@ module Jazzy
911
922
  # - method signatures after they've been processed by the highlighter
912
923
  #
913
924
  # The `after_highlight` flag is used to differentiate between the two modes.
914
- def self.autolink_text(text, doc, root_decls, after_highlight = false)
925
+ #
926
+ # DocC link format - follow Xcode and don't display slash-separated parts.
927
+ # rubocop:disable Metrics/MethodLength
928
+ def self.autolink_text(text, doc, root_decls, after_highlight: false)
915
929
  text.autolink_block(doc.url, '[^\s]+', after_highlight) do |raw_name|
916
- parts = raw_name.sub(/^@/, '') # ignore for custom attribute ref
917
- .split(/(?<!\.)\.(?!\.)/) # dot with no neighboring dots
918
- .reject(&:empty?)
930
+ sym_name =
931
+ (raw_name[/^<doc:(.*)>$/, 1] || raw_name).sub(/(?<!^)-.+$/, '')
932
+
933
+ parts = sym_name
934
+ .sub(/^@/, '') # ignore for custom attribute ref
935
+ .split(%r{(?<!\.)[/.](?!\.)}) # dot or slash, but not '...'
936
+ .reject(&:empty?)
919
937
 
920
938
  # First dot-separated component can match any ancestor or top-level doc
921
939
  first_part = parts.shift
922
940
  name_root = ancestor_name_match(first_part, doc) ||
923
941
  name_match(first_part, root_decls)
924
942
 
925
- # Traverse children via subsequence components, if any
926
- name_traversal(parts, name_root)
943
+ # Traverse children via subsequent components, if any
944
+ [name_traversal(parts, name_root), sym_name.sub(%r{^.*/}, '')]
927
945
  end.autolink_block(doc.url, '[+-]\[\w+(?: ?\(\w+\))? [\w:]+\]',
928
946
  after_highlight) do |raw_name|
929
947
  match = raw_name.match(/([+-])\[(\w+(?: ?\(\w+\))?) ([\w:]+)\]/)
@@ -935,42 +953,50 @@ module Jazzy
935
953
 
936
954
  if name_root
937
955
  # Look up the verb in the subject’s children
938
- name_match(match[1] + match[3], name_root.children)
956
+ [name_match(match[1] + match[3], name_root.children), raw_name]
939
957
  end
940
958
  end.autolink_block(doc.url, '[+-]\w[\w:]*', after_highlight) do |raw_name|
941
- name_match(raw_name, doc.children)
959
+ [name_match(raw_name, doc.children), raw_name]
942
960
  end
943
961
  end
962
+ # rubocop:enable Metrics/MethodLength
944
963
 
945
964
  AUTOLINK_TEXT_FIELDS = %w[return
946
965
  abstract
947
966
  unavailable_message
948
967
  deprecation_message].freeze
949
968
 
969
+ def self.autolink_text_fields(doc, root_decls)
970
+ AUTOLINK_TEXT_FIELDS.each do |field|
971
+ if text = doc.send(field)
972
+ doc.send(field + '=', autolink_text(text, doc, root_decls))
973
+ end
974
+ end
975
+
976
+ (doc.parameters || []).each do |param|
977
+ param[:discussion] =
978
+ autolink_text(param[:discussion], doc, root_decls)
979
+ end
980
+ end
981
+
950
982
  AUTOLINK_HIGHLIGHT_FIELDS = %w[declaration
951
983
  other_language_declaration].freeze
952
984
 
985
+ def self.autolink_highlight_fields(doc, root_decls)
986
+ AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
987
+ if text = doc.send(field)
988
+ doc.send(field + '=',
989
+ autolink_text(text, doc, root_decls, after_highlight: true))
990
+ end
991
+ end
992
+ end
993
+
953
994
  def self.autolink(docs, root_decls)
954
995
  @autolink_root_decls = root_decls
955
996
  docs.each do |doc|
956
997
  doc.children = autolink(doc.children, root_decls)
957
-
958
- AUTOLINK_TEXT_FIELDS.each do |field|
959
- if text = doc.send(field)
960
- doc.send(field + '=', autolink_text(text, doc, root_decls))
961
- end
962
- end
963
-
964
- AUTOLINK_HIGHLIGHT_FIELDS.each do |field|
965
- if text = doc.send(field)
966
- doc.send(field + '=', autolink_text(text, doc, root_decls, true))
967
- end
968
- end
969
-
970
- (doc.parameters || []).each do |param|
971
- param[:discussion] =
972
- autolink_text(param[:discussion], doc, root_decls)
973
- end
998
+ autolink_text_fields(doc, root_decls)
999
+ autolink_highlight_fields(doc, root_decls)
974
1000
  end
975
1001
  end
976
1002