Module: Jazzy::SourceKitten
- Defined in:
- lib/jazzy/sourcekitten.rb
Overview
This module interacts with the sourcekitten command-line executable
Class Method Summary collapse
- .deduplicate_declarations(declarations) ⇒ Object
-
.doc_coverage ⇒ Object
rubocop:enable Metrics/PerceivedComplexity rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/MethodLength.
- .documented_child?(doc) ⇒ Boolean
- .filter_excluded_files(json) ⇒ Object
-
.group_docs(docs, type) ⇒ Object
Group root-level docs by type and add as children to a group doc element.
- .make_default_doc_info(declaration) ⇒ Object
- .make_doc_info(doc, declaration) ⇒ Object
-
.make_doc_urls(docs, parents) ⇒ Hash
Generate doc URL by prepending its parents URLs.
- .make_paragraphs(doc, key) ⇒ Object
-
.make_source_declarations(docs) ⇒ Object
rubocop:disable Metrics/MethodLength rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity.
- .make_substructure(doc, declaration) ⇒ Object
- .parameters(doc) ⇒ Object
-
.parse(sourcekitten_output, min_acl, skip_undocumented) ⇒ Hash
Parse sourcekitten STDOUT output as JSON.
- .process_undocumented_token(doc, declaration) ⇒ Object
-
.run_sourcekitten(arguments) ⇒ Object
Run sourcekitten with given arguments and return STDOUT.
- .should_document?(doc) ⇒ Boolean
- .string_until_first_rest_definition(string) ⇒ Object
Class Method Details
.deduplicate_declarations(declarations) ⇒ Object
213 214 215 216 217 218 219 220 |
# File 'lib/jazzy/sourcekitten.rb', line 213 def self.deduplicate_declarations(declarations) duplicates = declarations.group_by { |d| [d.usr, d.type.kind] }.values duplicates.map do |decls| decls.first.tap do |d| d.children = deduplicate_declarations(decls.flat_map(&:children).uniq) end end end |
.doc_coverage ⇒ Object
rubocop:enable Metrics/PerceivedComplexity rubocop:enable Metrics/CyclomaticComplexity rubocop:enable Metrics/MethodLength
207 208 209 210 211 |
# File 'lib/jazzy/sourcekitten.rb', line 207 def self.doc_coverage return 0 if @documented_count == 0 && @undocumented_tokens.count == 0 (100 * @documented_count) / (@undocumented_tokens.count + @documented_count) end |
.documented_child?(doc) ⇒ Boolean
65 66 67 68 |
# File 'lib/jazzy/sourcekitten.rb', line 65 def self.documented_child?(doc) return false unless doc['key.substructure'] doc['key.substructure'].any? { |child| documented_child?(child) } end |
.filter_excluded_files(json) ⇒ Object
222 223 224 225 226 227 228 |
# File 'lib/jazzy/sourcekitten.rb', line 222 def self.filter_excluded_files(json) excluded_files = Config.instance.excluded_files json.map do |doc| key = doc.keys.first doc[key] unless excluded_files.include?(key) end.compact end |
.group_docs(docs, type) ⇒ Object
Group root-level docs by type and add as children to a group doc element
18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/jazzy/sourcekitten.rb', line 18 def self.group_docs(docs, type) group, docs = docs.partition { |doc| doc.type == type } docs << SourceDeclaration.new.tap do |sd| sd.type = SourceDeclaration::Type.overview sd.name = type.plural_name sd.abstract = "The following #{type.plural_name.downcase} are " \ 'available globally.' sd.children = group end if group.count > 0 docs end |
.make_default_doc_info(declaration) ⇒ Object
56 57 58 59 60 61 62 63 |
# File 'lib/jazzy/sourcekitten.rb', line 56 def self.make_default_doc_info(declaration) # @todo: Fix these declaration.line = 0 declaration.column = 0 declaration.abstract = 'Undocumented' declaration.parameters = [] declaration.children = [] end |
.make_doc_info(doc, declaration) ⇒ Object
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/jazzy/sourcekitten.rb', line 124 def self.make_doc_info(doc, declaration) return unless should_document?(doc) unless doc['key.doc.full_as_xml'] return process_undocumented_token(doc, declaration) end declaration.line = doc['key.doc.line'] declaration.column = doc['key.doc.column'] declaration.declaration = Highlighter.highlight( doc['key.parsed_declaration'] || doc['key.doc.declaration'], 'swift', ) stripped_comment = string_until_first_rest_definition( doc['key.doc.comment'], ) || '' declaration.abstract = Jazzy.markdown.render(stripped_comment) declaration.discussion = '' declaration.return = make_paragraphs(doc, 'key.doc.result_discussion') declaration.parameters = parameters(doc) @documented_count += 1 end |
.make_doc_urls(docs, parents) ⇒ Hash
Generate doc URL by prepending its parents URLs
32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/jazzy/sourcekitten.rb', line 32 def self.make_doc_urls(docs, parents) docs.each do |doc| if doc.children.count > 0 # Create HTML page for this doc if it has children parents_slash = parents.count > 0 ? '/' : '' doc.url = parents.join('/') + parents_slash + doc.name + '.html' doc.children = make_doc_urls(doc.children, parents + [doc.name]) else # Don't create HTML page for this doc if it doesn't have children # Instead, make its link a hash-link on its parent's page doc.url = parents.join('/') + '.html#/' + doc.usr end end end |
.make_paragraphs(doc, key) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/jazzy/sourcekitten.rb', line 98 def self.make_paragraphs(doc, key) return nil unless doc[key] doc[key].map do |p| if para = p['Para'] Jazzy.markdown.render(para) elsif verbatim = p['Verbatim'] Jazzy.markdown.render("```\n#{verbatim}```\n") else warn "Jazzy could not recognize the `#{p.keys.first}` tag. " \ 'Please report this by filing an issue at ' \ 'https://github.com/realm/jazzy/issues along with the comment ' \ 'including this tag.' Jazzy.markdown.render(p.values.first) end end.join end |
.make_source_declarations(docs) ⇒ Object
rubocop:disable Metrics/MethodLength rubocop:disable Metrics/CyclomaticComplexity rubocop:disable Metrics/PerceivedComplexity
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/jazzy/sourcekitten.rb', line 167 def self.make_source_declarations(docs) declarations = [] current_mark = SourceMark.new docs.each do |doc| if doc.key?('key.diagnostic_stage') declarations += make_source_declarations(doc['key.substructure']) next end declaration = SourceDeclaration.new declaration.type = SourceDeclaration::Type.new(doc['key.kind']) if declaration.type.mark? && doc['key.name'].start_with?('MARK: ') current_mark = SourceMark.new(doc['key.name']) end next unless declaration.type.should_document? unless declaration.type.name raise 'Please file an issue at ' \ 'https://github.com/realm/jazzy/issues about adding support ' \ "for `#{declaration.type.kind}`." end declaration.file = Pathname(doc['key.filepath']) if doc['key.filepath'] declaration.usr = doc['key.usr'] declaration.name = doc['key.name'] declaration.mark = current_mark declaration.access_control_level = SourceDeclaration::AccessControlLevel.from_doc(doc) declaration.start_line = doc['key.parsed_scope.start'] declaration.end_line = doc['key.parsed_scope.end'] next unless make_doc_info(doc, declaration) make_substructure(doc, declaration) declarations << declaration end declarations end |
.make_substructure(doc, declaration) ⇒ Object
154 155 156 157 158 159 160 161 162 |
# File 'lib/jazzy/sourcekitten.rb', line 154 def self.make_substructure(doc, declaration) if doc['key.substructure'] declaration.children = make_source_declarations( doc['key.substructure'], ) else declaration.children = [] end end |
.parameters(doc) ⇒ Object
115 116 117 118 119 120 121 122 |
# File 'lib/jazzy/sourcekitten.rb', line 115 def self.parameters(doc) (doc['key.doc.parameters'] || []).map do |p| { name: p['name'], discussion: make_paragraphs(p, 'discussion'), } end end |
.parse(sourcekitten_output, min_acl, skip_undocumented) ⇒ Hash
Parse sourcekitten STDOUT output as JSON
232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/jazzy/sourcekitten.rb', line 232 def self.parse(sourcekitten_output, min_acl, skip_undocumented) @min_acl = min_acl @skip_undocumented = skip_undocumented sourcekitten_json = filter_excluded_files(JSON.parse(sourcekitten_output)) docs = make_source_declarations(sourcekitten_json) docs = deduplicate_declarations(docs) SourceDeclaration::Type.all.each do |type| docs = group_docs(docs, type) end [make_doc_urls(docs, []), doc_coverage, @undocumented_tokens] end |
.process_undocumented_token(doc, declaration) ⇒ Object
88 89 90 91 92 93 94 95 96 |
# File 'lib/jazzy/sourcekitten.rb', line 88 def self.process_undocumented_token(doc, declaration) source_directory = Config.instance.source_directory.to_s filepath = doc['key.filepath'] if filepath && filepath.start_with?(source_directory) @undocumented_tokens << doc end return nil if !documented_child?(doc) && @skip_undocumented make_default_doc_info(declaration) end |
.run_sourcekitten(arguments) ⇒ Object
Run sourcekitten with given arguments and return STDOUT
48 49 50 51 52 53 54 |
# File 'lib/jazzy/sourcekitten.rb', line 48 def self.run_sourcekitten(arguments) xcode = XCInvoke::Xcode.find_swift_version(Config.instance.swift_version) bin_path = Pathname(__FILE__).parent + 'SourceKitten/sourcekitten' output, _ = Executable.execute_command(bin_path, arguments, true, env: xcode.as_env) output end |
.should_document?(doc) ⇒ Boolean
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/jazzy/sourcekitten.rb', line 70 def self.should_document?(doc) unless doc['key.usr'] warn "`#{doc['key.name']}` has no USR. First make sure all modules " \ 'used in your project have been imported. If all used modules ' \ 'are imported, please report this problem by filing an issue ' \ 'at https://github.com/realm/jazzy/issues along with your ' \ 'Xcode project. If this token is declared in an `#if` block, ' \ 'please ignore this message.' return false end return false if doc['key.doc.comment'].to_s.include?(':nodoc:') # Always document extensions, since we can't tell what ACL they are return true if doc['key.kind'] == 'source.lang.swift.decl.extension' SourceDeclaration::AccessControlLevel.from_doc(doc) >= @min_acl end |
.string_until_first_rest_definition(string) ⇒ Object
148 149 150 151 152 |
# File 'lib/jazzy/sourcekitten.rb', line 148 def self.string_until_first_rest_definition(string) matches = /^\s*:[^\s]+:/.match(string) return string unless matches string[0...matches.begin(0)] end |