Module: Jazzy::SourceKitten
- Defined in:
- lib/jazzy/sourcekitten.rb
Overview
This module interacts with the sourcekitten command-line executable
Class Method Summary collapse
- .assert_swift_version ⇒ Object
- .assert_xcode_location ⇒ Object
- .deduplicate_declarations(declarations) ⇒ Object
-
.doc_coverage ⇒ Object
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.
- .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
- .xcode_developer_directory ⇒ Object
Class Method Details
.assert_swift_version ⇒ Object
72 73 74 75 76 77 |
# File 'lib/jazzy/sourcekitten.rb', line 72 def self.assert_swift_version swift_version = `xcrun swift --version` =~ /Swift version ([\d\.]+)/ && Regexp.last_match[1] return if swift_version == @expected_swift_version raise "Jazzy only works with Swift #{@expected_swift_version}." end |
.assert_xcode_location ⇒ Object
57 58 59 60 61 62 63 64 65 |
# File 'lib/jazzy/sourcekitten.rb', line 57 def self.assert_xcode_location expected_xcode_select_path = Pathname('/Applications/Xcode.app/Contents/Developer') return if xcode_developer_directory == expected_xcode_select_path raise 'Please install or symlink Xcode 6.3 in ' \ "#{expected_xcode_select_path} and set as active developer " \ 'directory by running `sudo xcode-select -s ' \ "#{expected_xcode_select_path}`" end |
.deduplicate_declarations(declarations) ⇒ Object
234 235 236 237 238 239 240 241 |
# File 'lib/jazzy/sourcekitten.rb', line 234 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/CyclomaticComplexity rubocop:enable Metrics/MethodLength
228 229 230 231 232 |
# File 'lib/jazzy/sourcekitten.rb', line 228 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
97 98 99 100 |
# File 'lib/jazzy/sourcekitten.rb', line 97 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
243 244 245 246 247 248 249 |
# File 'lib/jazzy/sourcekitten.rb', line 243 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
88 89 90 91 92 93 94 95 |
# File 'lib/jazzy/sourcekitten.rb', line 88 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
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/jazzy/sourcekitten.rb', line 147 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 46 47 48 49 50 51 52 53 54 55 |
# 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 id = doc.usr unless id id = doc.name || 'unknown' warn "`#{id}` 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.' end doc.url = parents.join('/') + '.html#/' + id end end end |
.make_paragraphs(doc, key) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/jazzy/sourcekitten.rb', line 121 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
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
# File 'lib/jazzy/sourcekitten.rb', line 189 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 = 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
177 178 179 180 181 182 183 184 185 |
# File 'lib/jazzy/sourcekitten.rb', line 177 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
138 139 140 141 142 143 144 145 |
# File 'lib/jazzy/sourcekitten.rb', line 138 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
253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/jazzy/sourcekitten.rb', line 253 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
111 112 113 114 115 116 117 118 119 |
# File 'lib/jazzy/sourcekitten.rb', line 111 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
80 81 82 83 84 85 86 |
# File 'lib/jazzy/sourcekitten.rb', line 80 def self.run_sourcekitten(arguments) assert_xcode_location assert_swift_version bin_path = Pathname(__FILE__).parent + 'SourceKitten/sourcekitten' output, _ = Executable.execute_command(bin_path, arguments, true) output end |
.should_document?(doc) ⇒ Boolean
102 103 104 105 106 107 108 109 |
# File 'lib/jazzy/sourcekitten.rb', line 102 def self.should_document?(doc) 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
171 172 173 174 175 |
# File 'lib/jazzy/sourcekitten.rb', line 171 def self.string_until_first_rest_definition(string) matches = /^\s*:[^\s]+:/.match(string) return string unless matches string[0...matches.begin(0)] end |
.xcode_developer_directory ⇒ Object
67 68 69 70 |
# File 'lib/jazzy/sourcekitten.rb', line 67 def self.xcode_developer_directory dir = Pathname(`xcode-select -p`.chomp) dir.directory? ? dir. : nil end |