From 84ae475d556ca807574007202eeb6759a3ca6638 Mon Sep 17 00:00:00 2001 From: Bob Aman Date: Fri, 2 Dec 2011 17:24:56 +0300 Subject: [PATCH] Added super-hacky script to generate the wiki reference pages. --- tasks/wiki.rake | 54 +- yard/bin/yard-wiki | 9 + yard/lib/yard-google-code.rb | 12 + yard/lib/yard/cli/wiki.rb | 44 ++ yard/lib/yard/rake/wikidoc_task.rb | 27 + yard/lib/yard/serializers/wiki_serializer.rb | 68 +++ .../lib/yard/templates/helpers/wiki_helper.rb | 502 ++++++++++++++++++ yard/templates/default/class/setup.rb | 43 ++ yard/templates/default/docstring/setup.rb | 54 ++ yard/templates/default/method/setup.rb | 8 + .../templates/default/method_details/setup.rb | 8 + yard/templates/default/module/setup.rb | 133 +++++ yard/templates/default/tags/setup.rb | 55 ++ 13 files changed, 1010 insertions(+), 7 deletions(-) create mode 100755 yard/bin/yard-wiki create mode 100644 yard/lib/yard-google-code.rb create mode 100644 yard/lib/yard/cli/wiki.rb create mode 100644 yard/lib/yard/rake/wikidoc_task.rb create mode 100644 yard/lib/yard/serializers/wiki_serializer.rb create mode 100644 yard/lib/yard/templates/helpers/wiki_helper.rb create mode 100644 yard/templates/default/class/setup.rb create mode 100644 yard/templates/default/docstring/setup.rb create mode 100644 yard/templates/default/method/setup.rb create mode 100644 yard/templates/default/method_details/setup.rb create mode 100644 yard/templates/default/module/setup.rb create mode 100644 yard/templates/default/tags/setup.rb diff --git a/tasks/wiki.rake b/tasks/wiki.rake index 0bba431fa..8dd998a5b 100644 --- a/tasks/wiki.rake +++ b/tasks/wiki.rake @@ -1,11 +1,21 @@ +$LOAD_PATH.unshift( + File.expand_path(File.join(File.dirname(__FILE__), '../yard/lib')) +) +$LOAD_PATH.unshift(File.expand_path('.')) +$LOAD_PATH.uniq! + require 'google/api_client' +require 'rake' +require 'rake/clean' + +CLOBBER.include('wiki') CACHE_PREFIX = "http://www.gmodules.com/gadgets/proxy/container=default&debug=0&nocache=0/" namespace :wiki do desc 'Autogenerate wiki pages' - task :generate do + task :supported_apis do output = <<-WIKI #summary The list of supported APIs @@ -23,12 +33,15 @@ WIKI end end for api_name, api in preferred_apis - output += ( - "||#{CACHE_PREFIX}#{api['icons']['x16']}||" + - "[#{api.documentation} #{api.title}]||" + - "#{api.description}||\n" - ) + if api.documentation.to_s != "" && api.title != "" + output += ( + "||#{CACHE_PREFIX}#{api['icons']['x16']}||" + + "[#{api.documentation} #{api.title}]||" + + "#{api.description}||\n" + ) + end end + output.gsub!(/-32\./, "-16.") wiki_path = File.expand_path( File.join(File.dirname(__FILE__), '../wiki/')) Dir.mkdir(wiki_path) if !File.exist?(wiki_path) @@ -36,6 +49,33 @@ WIKI file.write(output) end end + + task 'generate' => ['wiki:supported_apis'] end -# task 'clobber' => ['wiki:clobber_wiki'] +begin + require 'yard' + require 'yard/rake/wikidoc_task' + + namespace :wiki do + desc 'Generate Wiki Documentation with YARD' + YARD::Rake::WikidocTask.new do |yardoc| + yardoc.name = 'reference' + yardoc.options = [ + '--verbose', + '-e', 'yard/lib/yard-google-code.rb', + '-p', 'yard/templates', + '-f', 'wiki', + '-o', 'wiki' + ] + yardoc.files = [ + 'lib/**/*.rb', 'ext/**/*.c', '-', 'README.md', 'CHANGELOG.md' + ] + end + + task 'generate' => ['wiki:reference', 'wiki:supported_apis'] + end +rescue LoadError + # If yard isn't available, it's not the end of the world + warn('YARD unavailable. Cannot fully generate wiki.') +end diff --git a/yard/bin/yard-wiki b/yard/bin/yard-wiki new file mode 100755 index 000000000..61416750e --- /dev/null +++ b/yard/bin/yard-wiki @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +$LOAD_PATH.unshift( + File.expand_path(File.join(File.dirname(__FILE__), '../lib')) +) +$LOAD_PATH.uniq! + +require 'yard/cli/wiki' + +YARD::CLI::Wiki.run(*ARGV) diff --git a/yard/lib/yard-google-code.rb b/yard/lib/yard-google-code.rb new file mode 100644 index 000000000..cd4eba834 --- /dev/null +++ b/yard/lib/yard-google-code.rb @@ -0,0 +1,12 @@ +$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) +$LOAD_PATH.uniq! + +YARD::Templates::Engine.register_template_path File.dirname(__FILE__) + '/../templates' +require 'yard/templates/template' +require 'yard/templates/helpers/wiki_helper' + +::YARD::Templates::Template.extra_includes |= [ + YARD::Templates::Helpers::WikiHelper +] + +require 'yard/serializers/wiki_serializer' diff --git a/yard/lib/yard/cli/wiki.rb b/yard/lib/yard/cli/wiki.rb new file mode 100644 index 000000000..2c1739319 --- /dev/null +++ b/yard/lib/yard/cli/wiki.rb @@ -0,0 +1,44 @@ +require 'yard' +require 'yard/serializers/wiki_serializer' +require 'yard/cli/yardoc' + +module YARD + module CLI + class Wiki < Yardoc + # Creates a new instance of the commandline utility + def initialize + super + @options = SymbolHash.new(false) + @options.update( + :format => :html, + :template => :default, + :markup => :rdoc, # default is :rdoc but falls back on :none + :serializer => YARD::Serializers::WikiSerializer.new, # Sigh. :-( + :default_return => "Object", + :hide_void_return => false, + :no_highlight => false, + :files => [], + :verifier => Verifier.new + ) + @visibilities = [:public] + @assets = {} + @excluded = [] + @files = [] + @hidden_tags = [] + @use_cache = false + @use_yardopts_file = true + @use_document_file = true + @generate = true + @options_file = DEFAULT_YARDOPTS_FILE + @statistics = true + @list = false + @save_yardoc = true + @has_markup = false + + if defined?(::Encoding) && ::Encoding.respond_to?(:default_external=) + ::Encoding.default_external, ::Encoding.default_internal = 'utf-8', 'utf-8' + end + end + end + end +end diff --git a/yard/lib/yard/rake/wikidoc_task.rb b/yard/lib/yard/rake/wikidoc_task.rb new file mode 100644 index 000000000..573bfb4d3 --- /dev/null +++ b/yard/lib/yard/rake/wikidoc_task.rb @@ -0,0 +1,27 @@ +require 'rake' +require 'rake/tasklib' +require 'yard/rake/yardoc_task' +require 'yard/cli/wiki' + +module YARD + module Rake + # The rake task to run {CLI::Yardoc} and generate documentation. + class WikidocTask < YardocTask + protected + + # Defines the rake task + # @return [void] + def define + desc "Generate Wiki Documentation with YARD" + task(name) do + before.call if before.is_a?(Proc) + yardoc = YARD::CLI::Wiki.new + yardoc.parse_arguments *(options + files) + yardoc.options[:verifier] = verifier if verifier + yardoc.run + after.call if after.is_a?(Proc) + end + end + end + end +end diff --git a/yard/lib/yard/serializers/wiki_serializer.rb b/yard/lib/yard/serializers/wiki_serializer.rb new file mode 100644 index 000000000..469c4736e --- /dev/null +++ b/yard/lib/yard/serializers/wiki_serializer.rb @@ -0,0 +1,68 @@ +# encoding: utf-8 + +require 'yard/serializers/file_system_serializer' + +module YARD + module Serializers + ## + # Subclass required to get correct filename for the top level namespace. + # :-( + class WikiSerializer < FileSystemSerializer + # Post-process the data before serializing. + # Strip unnecessary whitespace. + # Convert stuff into more wiki-friendly stuff. + # FULL OF HACKS! + def serialize(object, data) + data = data.encode("UTF-8") + if object == "Sidebar.wiki" + data = data.gsub(/^#sidebar Sidebar\n/, "") + end + data = data.gsub(/\n\s*\n/, "\n") + # ASCII/UTF-8 erb error work-around. + data = data.gsub(/--/, "—") + data = data.gsub(/——/, "----") + data = data.gsub(/----\n----/, "----") + # HACK! Google Code Wiki treats blocks like
 blocks.
+        data = data.gsub(/\(.+)\<\/code\>/, "`\\1`")
+        super(object, data)
+      end
+
+      def serialized_path(object)
+        return object if object.is_a?(String)
+
+        if object.is_a?(CodeObjects::ExtraFileObject)
+          fspath = ['file.' + object.name + (extension.empty? ? '' : ".#{extension}")]
+        else
+          # This line is the only change of significance.
+          # Changed from 'top-level-namespace' to 'TopLevelNamespace' to
+          # conform to wiki word page naming convention.
+          objname = object != YARD::Registry.root ? object.name.to_s : "TopLevelNamespace"
+          objname += '_' + object.scope.to_s[0,1] if object.is_a?(CodeObjects::MethodObject)
+          fspath = [objname + (extension.empty? ? '' : ".#{extension}")]
+          if object.namespace && object.namespace.path != ""
+            fspath.unshift(*object.namespace.path.split(CodeObjects::NSEP))
+          end
+        end
+
+        # Don't change the filenames, it just makes it more complicated
+        # to figure out the original name.
+        #fspath.map! do |p|
+        #  p.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
+        #end
+
+        # Remove special chars from filenames.
+        # Windows disallows \ / : * ? " < > | but we will just remove any
+        # non alphanumeric (plus period, underscore and dash).
+        fspath.map! do |p|
+          p.gsub(/[^\w\.-]/) do |x|
+            encoded = '_'
+
+            x.each_byte { |b| encoded << ("%X" % b) }
+            encoded
+          end
+        end
+        fspath.join("")
+      end
+    end
+  end
+end
diff --git a/yard/lib/yard/templates/helpers/wiki_helper.rb b/yard/lib/yard/templates/helpers/wiki_helper.rb
new file mode 100644
index 000000000..e03dfb668
--- /dev/null
+++ b/yard/lib/yard/templates/helpers/wiki_helper.rb
@@ -0,0 +1,502 @@
+require 'cgi'
+require 'rdiscount'
+
+module YARD
+  module Templates::Helpers
+    # The helper module for HTML templates.
+    module WikiHelper
+      include MarkupHelper
+
+      # @return [String] escapes text
+      def h(text)
+        out = ""
+        text = text.split(/\n/)
+        text.each_with_index do |line, i|
+          out <<
+          case line
+          when /^\s*$/; "\n\n"
+          when /^\s+\S/, /^=/; line + "\n"
+          else; line + (text[i + 1] =~ /^\s+\S/ ? "\n" : " ")
+          end
+        end
+        out.strip
+      end
+
+      # @return [String] wraps text at +col+ columns.
+      def wrap(text, col = 72)
+        text.strip.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/, "\\1\\3\n")
+      end
+
+      # Escapes a URL
+      # 
+      # @param [String] text the URL
+      # @return [String] the escaped URL
+      def urlencode(text)
+        CGI.escape(text.to_s)
+      end
+
+      def indent(text, len = 2)
+        text.gsub(/^/, ' ' * len)
+      end
+
+      def unindent(text)
+        lines = text.split("\n", -1)
+        min_indent_size = text.size
+        for line in lines
+          indent_size = (line.gsub("\t", "  ") =~ /[^\s]/) || text.size
+          min_indent_size = indent_size if indent_size < min_indent_size
+        end
+        text.gsub("\t", "  ").gsub(Regexp.new("^" + " " * min_indent_size), '')
+      end
+
+      # @group Converting Markup to HTML
+
+      # Turns text into HTML using +markup+ style formatting.
+      #
+      # @param [String] text the text to format
+      # @param [Symbol] markup examples are +:markdown+, +:textile+, +:rdoc+.
+      #   To add a custom markup type, see {MarkupHelper}
+      # @return [String] the HTML
+      def htmlify(text, markup = options[:markup])
+        markup_meth = "html_markup_#{markup}"
+        return text unless respond_to?(markup_meth)
+        return "" unless text
+        return text unless markup
+        load_markup_provider(markup)
+        html = send(markup_meth, text)
+        if html.respond_to?(:encode)
+          html = html.force_encoding(text.encoding) # for libs that mess with encoding
+          html = html.encode(:invalid => :replace, :replace => '?')
+        end
+        html = resolve_links(html)
+        html = html.gsub(/
(?:\s*)?(.+?)(?:<\/code>\s*)?<\/pre>/m) do
+          str = unindent($1).strip
+          str = html_syntax_highlight(CGI.unescapeHTML(str)) unless options[:no_highlight]
+          str
+        end unless markup == :text
+        html
+      end
+
+      # Converts Markdown to HTML
+      # @param [String] text input Markdown text
+      # @return [String] output HTML
+      # @since 0.6.0
+      def html_markup_markdown(text)
+        Markdown.new(text).to_html
+      end
+
+      # Converts Textile to HTML
+      # @param [String] text the input Textile text
+      # @return [String] output HTML
+      # @since 0.6.0
+      def html_markup_textile(text)
+        doc = markup_class(:textile).new(text)
+        doc.hard_breaks = false if doc.respond_to?(:hard_breaks=)
+        doc.to_html
+      end
+
+      # Converts plaintext to HTML
+      # @param [String] text the input text
+      # @return [String] the output HTML
+      # @since 0.6.0
+      def html_markup_text(text)
+        "
" + text + "
" + end + + # Converts HTML to HTML + # @param [String] text input html + # @return [String] output HTML + # @since 0.6.0 + def html_markup_html(text) + text + end + + # @return [String] HTMLified text as a single line (paragraphs removed) + def htmlify_line(*args) + htmlify(*args) + end + + # Fixes RDoc behaviour with ++ only supporting alphanumeric text. + # + # @todo Refactor into own SimpleMarkup subclass + def fix_typewriter(text) + text.gsub(/\+(?! )([^\n\+]{1,900})(?! )\+/) do + type_text, pre_text, no_match = $1, $`, $& + pre_match = pre_text.scan(%r()) + if pre_match.last.nil? || pre_match.last.include?('/') + '`' + h(type_text) + '`' + else + no_match + end + end + end + + # Don't allow -- to turn into — element. The chances of this being + # some --option is far more likely than the typographical meaning. + # + # @todo Refactor into own SimpleMarkup subclass + def fix_dash_dash(text) + text.gsub(/—(?=\S)/, '--') + end + + # @group Syntax Highlighting Source Code + + # Syntax highlights +source+ in language +type+. + # + # @note To support a specific language +type+, implement the method + # +html_syntax_highlight_TYPE+ in this class. + # + # @param [String] source the source code to highlight + # @param [Symbol] type the language type (:ruby, :plain, etc). Use + # :plain for no syntax highlighting. + # @return [String] the highlighted source + def html_syntax_highlight(source, type = nil) + return "" unless source + return "{{{\n#{source}\n}}}" + end + + # @return [String] unhighlighted source + def html_syntax_highlight_plain(source) + return "" unless source + return "{{{\n#{source}\n}}}" + end + + # @group Linking Objects and URLs + + # Resolves any text in the form of +{Name}+ to the object specified by + # Name. Also supports link titles in the form +{Name title}+. + # + # @example Linking to an instance method + # resolve_links("{MyClass#method}") # => "MyClass#method" + # @example Linking to a class with a title + # resolve_links("{A::B::C the C class}") # => "the c class" + # @param [String] text the text to resolve links in + # @return [String] HTML with linkified references + def resolve_links(text) + code_tags = 0 + text.gsub(/<(\/)?(pre|code|tt)|\{(\S+?)(?:\s(.*?\S))?\}(?=[\W<]|.+<\/|$)/) do |str| + closed, tag, name, title, match = $1, $2, $3, $4, $& + if tag + code_tags += (closed ? -1 : 1) + next str + end + next str unless code_tags == 0 + + next(match) if name[0,1] == '|' + if object.is_a?(String) + object + else + link = linkify(name, title) + if link == name || link == title + match = /(.+)?(\{#{Regexp.quote name}(?:\s.*?)?\})(.+)?/.match(text) + file = (@file ? @file : object.file) || '(unknown)' + line = (@file ? 1 : (object.docstring.line_range ? object.docstring.line_range.first : 1)) + (match ? $`.count("\n") : 0) + log.warn "In file `#{file}':#{line}: Cannot resolve link to #{name} from text" + (match ? ":" : ".") + log.warn((match[1] ? '...' : '') + match[2].gsub("\n","") + (match[3] ? '...' : '')) if match + end + + link + end + end + end + + def unlink(value) + value.gsub(/\b(([A-Z][a-z]+){2,99})\b/, "!\\1") + end + + # (see BaseHelper#link_file) + def link_file(filename, title = nil, anchor = nil) + link_url(url_for_file(filename, anchor), title) + end + + # (see BaseHelper#link_include_object) + def link_include_object(obj) + htmlify(obj.docstring) + end + + # (see BaseHelper#link_object) + def link_object(obj, otitle = nil, anchor = nil, relative = true) + return otitle if obj.nil? + obj = Registry.resolve(object, obj, true, true) if obj.is_a?(String) + if !otitle && obj.root? + title = "Top Level Namespace" + elsif otitle + # title = "`" + otitle.to_s + "`" + title = otitle.to_s + elsif object.is_a?(CodeObjects::Base) + # title = "`" + h(object.relative_path(obj)) + "`" + title = h(object.relative_path(obj)) + else + # title = "`" + h(obj.to_s) + "`" + title = h(obj.to_s) + end + unless serializer + return unlink(title) + end + return unlink(title) if obj.is_a?(CodeObjects::Proxy) + + link = url_for(obj, anchor, relative) + if link + link_url(link, title, :formatted => false) + else + unlink(title) + end + end + + # (see BaseHelper#link_url) + def link_url(url, title = nil, params = {}) + title ||= url + if url.to_s == "" + title + else + if params[:formatted] + "#{title}" + else + "[#{url} #{title}]" + end + end + end + + # @group URL Helpers + + # @param [CodeObjects::Base] object the object to get an anchor for + # @return [String] the anchor for a specific object + def anchor_for(object) + # Method:_Google::APIClient#execute! + case object + when CodeObjects::MethodObject + if object.scope == :instance + "Method:_#{object.path}" + elsif object.scope == :class + "Method:_#{object.path}" + end + when CodeObjects::ClassVariableObject + "#{object.name.to_s.gsub('@@', '')}-#{object.type}" + when CodeObjects::Base + "#{object.name}-#{object.type}" + when CodeObjects::Proxy + object.path + else + object.to_s + end + end + + # Returns the URL for an object. + # + # @param [String, CodeObjects::Base] obj the object (or object path) to link to + # @param [String] anchor the anchor to link to + # @param [Boolean] relative use a relative or absolute link + # @return [String] the URL location of the object + def url_for(obj, anchor = nil, relative = true) + link = nil + return link unless serializer + if obj.kind_of?(CodeObjects::Base) && obj.root? + return 'TopLevelNamespace' + end + + if obj.is_a?(CodeObjects::Base) && !obj.is_a?(CodeObjects::NamespaceObject) + # If the obj is not a namespace obj make it the anchor. + anchor, obj = obj, obj.namespace + end + + objpath = serializer.serialized_path(obj) + return link unless objpath + + if relative + fromobj = object + if object.is_a?(CodeObjects::Base) && + !object.is_a?(CodeObjects::NamespaceObject) + fromobj = fromobj.namespace + end + + from = serializer.serialized_path(fromobj) + link = File.relative_path(from, objpath) + else + link = objpath + end + + return ( + link.gsub(/\.html$/, '').gsub(/\.wiki$/, '') + + (anchor ? '#' + urlencode(anchor_for(anchor)) : '') + ) + end + + # Returns the URL for a specific file + # + # @param [String] filename the filename to link to + # @param [String] anchor optional anchor + # @return [String] the URL pointing to the file + def url_for_file(filename, anchor = nil) + fromobj = object + if CodeObjects::Base === fromobj && !fromobj.is_a?(CodeObjects::NamespaceObject) + fromobj = fromobj.namespace + end + from = serializer.serialized_path(fromobj) + if filename == options[:readme] + filename = 'Documentation' + else + filename = File.basename(filename).gsub(/\.[^.]+$/, '').capitalize + end + link = File.relative_path(from, filename) + return ( + link.gsub(/\.html$/, '').gsub(/\.wiki$/, '') + + (anchor ? '#' + urlencode(anchor) : '') + ) + end + + # @group Formatting Objects and Attributes + + # Formats a list of objects and links them + # @return [String] a formatted list of objects + def format_object_name_list(objects) + objects.sort_by {|o| o.name.to_s.downcase }.map do |o| + "" + linkify(o, o.name) + "" + end.join(", ") + end + + # Formats a list of types from a tag. + # + # @param [Array, FalseClass] typelist + # the list of types to be formatted. + # + # @param [Boolean] brackets omits the surrounding + # brackets if +brackets+ is set to +false+. + # + # @return [String] the list of types formatted + # as [Type1, Type2, ...] with the types linked + # to their respective descriptions. + # + def format_types(typelist, brackets = true) + return unless typelist.is_a?(Array) + list = typelist.map do |type| + type = type.gsub(/([<>])/) { h($1) } + type = type.gsub(/([\w:]+)/) do + $1 == "lt" || $1 == "gt" ? "`#{$1}`" : linkify($1, $1) + end + end + list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", ")) + end + + # Get the return types for a method signature. + # + # @param [CodeObjects::MethodObject] meth the method object + # @param [Boolean] link whether to link the types + # @return [String] the signature types + # @since 0.5.3 + def signature_types(meth, link = true) + meth = convert_method_to_overload(meth) + + type = options[:default_return] || "" + if meth.tag(:return) && meth.tag(:return).types + types = meth.tags(:return).map {|t| t.types ? t.types : [] }.flatten.uniq + first = link ? h(types.first) : format_types([types.first], false) + if types.size == 2 && types.last == 'nil' + type = first + '?' + elsif types.size == 2 && types.last =~ /^(Array)?<#{Regexp.quote types.first}>$/ + type = first + '+' + elsif types.size > 2 + type = [first, '...'].join(', ') + elsif types == ['void'] && options[:hide_void_return] + type = "" + else + type = link ? h(types.join(", ")) : format_types(types, false) + end + elsif !type.empty? + type = link ? h(type) : format_types([type], false) + end + type = "(#{type.to_s.strip}) " unless type.empty? + type + end + + # Formats the signature of method +meth+. + # + # @param [CodeObjects::MethodObject] meth the method object to list + # the signature of + # @param [Boolean] link whether to link the method signature to the details view + # @param [Boolean] show_extras whether to show extra meta-data (visibility, attribute info) + # @param [Boolean] full_attr_name whether to show the full attribute name + # ("name=" instead of "name") + # @return [String] the formatted method signature + def signature(meth, link = true, show_extras = true, full_attr_name = true) + meth = convert_method_to_overload(meth) + + type = signature_types(meth, link) + name = full_attr_name ? meth.name : meth.name.to_s.gsub(/^(\w+)=$/, '\1') + blk = format_block(meth) + args = !full_attr_name && meth.writer? ? "" : format_args(meth) + extras = [] + extras_text = '' + if show_extras + if rw = meth.attr_info + attname = [rw[:read] ? 'read' : nil, rw[:write] ? 'write' : nil].compact + attname = attname.size == 1 ? attname.join('') + 'only' : nil + extras << attname if attname + end + extras << meth.visibility if meth.visibility != :public + extras_text = ' (' + extras.join(", ") + ')' unless extras.empty? + end + title = "%s *`%s`* `%s` `%s`" % [type, h(name.to_s).strip, args, blk] + title.gsub!(//, "") + title.gsub!(/<\/tt>/, "") + title.gsub!(/`\s*`/, "") + title.strip! + if link + if meth.is_a?(YARD::CodeObjects::MethodObject) + link_title = + "#{h meth.name(true)} (#{meth.scope} #{meth.type})" + else + link_title = "#{h name} (#{meth.type})" + end + # This has to be raw HTML, can't wiki-format a link title otherwise. + "#{title}#{extras_text}" + else + title + extras_text + end + end + + # @group Getting the Character Encoding + + # Returns the current character set. The default value can be overridden + # by setting the +LANG+ environment variable or by overriding this + # method. In Ruby 1.9 you can also modify this value by setting + # +Encoding.default_external+. + # + # @return [String] the current character set + # @since 0.5.4 + def charset + return 'utf-8' unless RUBY19 || lang = ENV['LANG'] + if RUBY19 + lang = Encoding.default_external.name.downcase + else + lang = lang.downcase.split('.').last + end + case lang + when "ascii-8bit", "us-ascii", "ascii-7bit"; 'iso-8859-1' + else; lang + end + end + + # @endgroup + + private + + # Converts a set of hash options into HTML attributes for a tag + # + # @param [Hash{String => String}] opts the tag options + # @return [String] the tag attributes of an HTML tag + def tag_attrs(opts = {}) + opts.sort_by {|k, v| k.to_s }.map {|k,v| "#{k}=#{v.to_s.inspect}" if v }.join(" ") + end + + # Converts a {CodeObjects::MethodObject} into an overload object + # @since 0.5.3 + def convert_method_to_overload(meth) + # use first overload tag if it has a return type and method itself does not + if !meth.tag(:return) && meth.tags(:overload).size == 1 && meth.tag(:overload).tag(:return) + return meth.tag(:overload) + end + meth + end + end + end +end diff --git a/yard/templates/default/class/setup.rb b/yard/templates/default/class/setup.rb new file mode 100644 index 000000000..0b4dc12f8 --- /dev/null +++ b/yard/templates/default/class/setup.rb @@ -0,0 +1,43 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +include T('default/module') + +def init + super + sections.place(:subclasses).before(:children) + sections.place(:constructor_details, [T('method_details')]).before(:methodmissing) + # Weird bug w/ doubled sections + sections.uniq! +end + +def constructor_details + ctors = object.meths(:inherited => true, :included => true) + return unless @ctor = ctors.find {|o| o.name == :initialize } + return if prune_method_listing([@ctor]).empty? + erb(:constructor_details) +end + +def subclasses + return if object.path == "Object" # don't show subclasses for Object + unless globals.subclasses + globals.subclasses = {} + list = run_verifier Registry.all(:class) + list.each do |o| + (globals.subclasses[o.superclass.path] ||= []) << o if o.superclass + end + end + + @subclasses = globals.subclasses[object.path] + return if @subclasses.nil? || @subclasses.empty? + @subclasses = @subclasses.sort_by {|o| o.path }.map do |child| + name = child.path + if object.namespace + name = object.relative_path(child) + end + [name, child] + end + erb(:subclasses) +end \ No newline at end of file diff --git a/yard/templates/default/docstring/setup.rb b/yard/templates/default/docstring/setup.rb new file mode 100644 index 000000000..63a5877fb --- /dev/null +++ b/yard/templates/default/docstring/setup.rb @@ -0,0 +1,54 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +def init + return if object.docstring.blank? && !object.has_tag?(:api) + sections :index, [:private, :deprecated, :abstract, :todo, :note, :returns_void, :text], T('tags') +end + +def private + return unless object.has_tag?(:api) && object.tag(:api).text == 'private' + erb(:private) +end + +def abstract + return unless object.has_tag?(:abstract) + erb(:abstract) +end + +def deprecated + return unless object.has_tag?(:deprecated) + erb(:deprecated) +end + +def todo + return unless object.has_tag?(:todo) + erb(:todo) +end + +def note + return unless object.has_tag?(:note) + erb(:note) +end + +def returns_void + return unless object.type == :method + return if object.name == :initialize && object.scope == :instance + return unless object.tags(:return).size == 1 && object.tag(:return).types == ['void'] + erb(:returns_void) +end + +def docstring_text + text = "" + unless object.tags(:overload).size == 1 && object.docstring.empty? + text = object.docstring + end + + if text.strip.empty? && object.tags(:return).size == 1 && object.tag(:return).text + text = object.tag(:return).text.gsub(/\A([a-z])/) {|x| x.upcase } + end + + text.strip +end \ No newline at end of file diff --git a/yard/templates/default/method/setup.rb b/yard/templates/default/method/setup.rb new file mode 100644 index 000000000..a6ed29924 --- /dev/null +++ b/yard/templates/default/method/setup.rb @@ -0,0 +1,8 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +def init + sections :header, [T('method_details')] +end \ No newline at end of file diff --git a/yard/templates/default/method_details/setup.rb b/yard/templates/default/method_details/setup.rb new file mode 100644 index 000000000..e3bfea003 --- /dev/null +++ b/yard/templates/default/method_details/setup.rb @@ -0,0 +1,8 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +def init + sections :header, [:method_signature, T('docstring')] +end diff --git a/yard/templates/default/module/setup.rb b/yard/templates/default/module/setup.rb new file mode 100644 index 000000000..d2559eaa4 --- /dev/null +++ b/yard/templates/default/module/setup.rb @@ -0,0 +1,133 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +include Helpers::ModuleHelper + +def init + sections :header, :box_info, :pre_docstring, T('docstring'), :children, + :constant_summary, [T('docstring')], :inherited_constants, + :inherited_methods, + :methodmissing, [T('method_details')], + :attribute_details, [T('method_details')], + :method_details_list, [T('method_details')] +end + +def pre_docstring + return if object.docstring.blank? + erb(:pre_docstring) +end + +def children + @inner = [[:modules, []], [:classes, []]] + object.children.each do |child| + @inner[0][1] << child if child.type == :module + @inner[1][1] << child if child.type == :class + end + @inner.map! {|v| [v[0], run_verifier(v[1].sort_by {|o| o.name.to_s })] } + return if (@inner[0][1].size + @inner[1][1].size) == 0 + erb(:children) +end + +def methodmissing + mms = object.meths(:inherited => true, :included => true) + return unless @mm = mms.find {|o| o.name == :method_missing && o.scope == :instance } + erb(:methodmissing) +end + +def method_listing(include_specials = true) + return @smeths ||= method_listing.reject {|o| special_method?(o) } unless include_specials + return @meths if @meths + @meths = object.meths(:inherited => false, :included => false) + @meths = sort_listing(prune_method_listing(@meths)) + @meths +end + +def special_method?(meth) + return true if meth.name(true) == '#method_missing' + return true if meth.constructor? + false +end + +def attr_listing + return @attrs if @attrs + @attrs = [] + [:class, :instance].each do |scope| + object.attributes[scope].each do |name, rw| + @attrs << (rw[:read] || rw[:write]) + end + end + @attrs = sort_listing(prune_method_listing(@attrs, false)) +end + +def constant_listing + return @constants if @constants + @constants = object.constants(:included => false, :inherited => false) + @constants += object.cvars + @constants = run_verifier(@constants) + @constants +end + +def sort_listing(list) + list.sort_by {|o| [o.scope.to_s, o.name.to_s.downcase] } +end + +def docstring_full(obj) + docstring = "" + if obj.tags(:overload).size == 1 && obj.docstring.empty? + docstring = obj.tag(:overload).docstring + else + docstring = obj.docstring + end + + if docstring.summary.empty? && obj.tags(:return).size == 1 && obj.tag(:return).text + docstring = Docstring.new(obj.tag(:return).text.gsub(/\A([a-z])/) {|x| x.upcase }.strip) + end + + docstring +end + +def docstring_summary(obj) + docstring_full(obj).summary +end + +def groups(list, type = "Method") + if groups_data = object.groups + others = list.select {|m| !m.group } + groups_data.each do |name| + items = list.select {|m| m.group == name } + yield(items, name) unless items.empty? + end + else + others = [] + group_data = {} + list.each do |meth| + if meth.group + (group_data[meth.group] ||= []) << meth + else + others << meth + end + end + group_data.each {|group, items| yield(items, group) unless items.empty? } + end + + scopes(others) {|items, scope| yield(items, "#{scope.to_s.capitalize} #{type} Summary") } +end + +def scopes(list) + [:class, :instance].each do |scope| + items = list.select {|m| m.scope == scope } + yield(items, scope) unless items.empty? + end +end + +def mixed_into(object) + unless globals.mixed_into + globals.mixed_into = {} + list = run_verifier Registry.all(:class, :module) + list.each {|o| o.mixins.each {|m| (globals.mixed_into[m.path] ||= []) << o } } + end + + globals.mixed_into[object.path] || [] +end diff --git a/yard/templates/default/tags/setup.rb b/yard/templates/default/tags/setup.rb new file mode 100644 index 000000000..33dc42cac --- /dev/null +++ b/yard/templates/default/tags/setup.rb @@ -0,0 +1,55 @@ +lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')) +$LOAD_PATH.unshift(lib_dir) +$LOAD_PATH.uniq! +require 'yard-google-code' + +def init + tags = Tags::Library.visible_tags - [:abstract, :deprecated, :note, :todo] + create_tag_methods(tags - [:example, :option, :overload, :see]) + sections :index, tags + sections.any(:overload).push(T('docstring')) +end + +def return + if object.type == :method + return if object.name == :initialize && object.scope == :instance + return if object.tags(:return).size == 1 && object.tag(:return).types == ['void'] + end + tag(:return) +end + +private + +def tag(name, opts = nil) + return unless object.has_tag?(name) + opts ||= options_for_tag(name) + @no_names = true if opts[:no_names] + @no_types = true if opts[:no_types] + @name = name + out = erb('tag') + @no_names, @no_types = nil, nil + out +end + +def create_tag_methods(tags) + tags.each do |tag| + next if respond_to?(tag) + instance_eval(<<-eof, __FILE__, __LINE__ + 1) + def #{tag}; tag(#{tag.inspect}) end + eof + end +end + +def options_for_tag(tag) + opts = {:no_types => true, :no_names => true} + case Tags::Library.factory_method_for(tag) + when :with_types + opts[:no_types] = false + when :with_types_and_name + opts[:no_types] = false + opts[:no_names] = false + when :with_name + opts[:no_names] = false + end + opts +end