lib/yard/templates/helpers/html_helper.rb in yard-0.5.8 vs lib/yard/templates/helpers/html_helper.rb in yard-0.6.0
- old
+ new
@@ -1,15 +1,18 @@
require 'cgi'
module YARD
module Templates::Helpers
+ # The helper module for HTML templates.
module HtmlHelper
include MarkupHelper
include HtmlSyntaxHighlightHelper
SimpleMarkupHtml = RDoc::Markup::ToHtml.new rescue SM::ToHtml.new
+ # @group Escaping Template Data
+
# Escapes HTML entities
#
# @param [String] text the text to escape
# @return [String] the HTML with escaped entities
def h(text)
@@ -22,69 +25,87 @@
# @return [String] the escaped URL
def urlencode(text)
CGI.escape(text.to_s)
end
- # 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
- 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
+ # @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)
-
- # TODO: other libraries might be more complex
- case markup
- when :markdown
- html = markup_class(markup).new(text).to_html
- when :textile
- doc = markup_class(markup).new(text)
- doc.hard_breaks = false if doc.respond_to?(:hard_breaks=)
- html = doc.to_html
- when :rdoc
-
- begin
- SimpleMarkupHtml.instance_variable_set("@from_path", url_for(object))
- html = MarkupHelper::SimpleMarkup.convert(text, SimpleMarkupHtml)
- end
-
- html = fix_dash_dash(html)
- html = fix_typewriter(html)
+ 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.force_encoding(Encoding.default_external) if RUBY19
html = resolve_links(html)
html = html.gsub(/<pre>(?:\s*<code>)?(.+?)(?:<\/code>\s*)?<\/pre>/m) do
str = $1
str = html_syntax_highlight(CGI.unescapeHTML(str)) unless options[:no_highlight]
%Q{<pre class="code">#{str}</pre>}
end
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)
+ # TODO: other libraries might be more complex
+ markup_class(: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 RDoc formatting (SimpleMarkup) to HTML
+ # @param [String] text the input RDoc formatted text
+ # @return [String] output HTML
+ # @since 0.6.0
+ def html_markup_rdoc(text)
+ begin
+ SimpleMarkupHtml.instance_variable_set("@from_path", url_for(object))
+ html = MarkupHelper::SimpleMarkup.convert(text, SimpleMarkupHtml)
+ end
+
+ html = fix_dash_dash(html)
+ html = fix_typewriter(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)
+ "<pre>" + text + "</pre>"
+ 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)
"<div class='inline'>" + htmlify(*args) + "</div>"
end
@@ -108,11 +129,45 @@
#
# @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 h(source) if options[:no_highlight]
+ type ||= object.source_type || :ruby
+
+ # handle !!!LANG prefix to send to html_syntax_highlight_LANG
+ if source =~ /\A(?:[ \t]*\r?\n)?[ \t]*!!!([\w.+-]+)[ \t]*\r?\n/
+ type, source = $1, $'
+ source = $'
+ end
+
+ meth = "html_syntax_highlight_#{type}"
+ respond_to?(meth) ? send(meth, source) : h(source)
+ end
+
+ # @return [String] unhighlighted source
+ def html_syntax_highlight_plain(source)
+ h(source)
+ 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}") # => "<a href='...'>MyClass#method</a>"
@@ -121,68 +176,46 @@
# @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 = $1, $2, $3, $4
+ 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 = text[/(.{0,20}\{.*?#{Regexp.quote name}.*?\}.{0,20})/, 1]
- log.warn "In file `#{object.file}':#{object.line}: Cannot resolve link to #{name} from text" + (match ? ":" : ".")
- log.warn '...' + match.gsub(/\n/,"\n\t") + '...' if match
+ 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
- if name =~ %r{://} || name =~ /^(mailto|file):/
- link
- else
- "<tt>" + link + "</tt>"
- end
+ link
end
end
end
-
- def format_object_name_list(objects)
- objects.sort_by {|o| o.name.to_s.downcase }.map do |o|
- "<span class='name'>" + linkify(o, o.name) + "</span>"
- end.join(", ")
- end
- # Formats a list of types from a tag.
- #
- # @param [Array<String>, 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:]+)/) { $1 == "lt" || $1 == "gt" ? $1 : linkify($1, $1) }
- "<tt>" + type + "</tt>"
- end
- list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
- 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"
@@ -195,26 +228,29 @@
end
return title unless serializer
return title if obj.is_a?(CodeObjects::Proxy)
link = url_for(obj, anchor, relative)
- link ? link_url(link, title, :title => "#{obj.path} (#{obj.type})") : title
+ link = link ? link_url(link, title, :title => "#{obj.path} (#{obj.type})") : title
+ "<span class='object_link'>" + link + "</span>"
end
+ # (see BaseHelper#link_url)
def link_url(url, title = nil, params = {})
title ||= url
params = SymbolHash.new(false).update(
:href => url,
:title => h(title)
).update(params)
+ params[:target] ||= '_parent' if url =~ /^(\w+):\/\//
"<a #{tag_attrs(params)}>#{title}</a>"
end
- def tag_attrs(opts = {})
- opts.sort_by {|k, v| k.to_s }.map {|k,v| "#{k}=#{v.to_s.inspect}" if v }.join(" ")
- 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)
case object
when CodeObjects::MethodObject
"#{object.name}-#{object.scope}_#{object.type}"
when CodeObjects::ClassVariableObject
@@ -226,10 +262,16 @@
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.is_a?(CodeObjects::Base) && !obj.is_a?(CodeObjects::NamespaceObject)
@@ -254,10 +296,15 @@
end
link + (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
@@ -269,16 +316,54 @@
end
link = File.relative_path(from, filename)
link + '.html' + (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|
+ "<span class='name'>" + linkify(o, o.name) + "</span>"
+ end.join(", ")
+ end
+
+ # Formats a list of types from a tag.
+ #
+ # @param [Array<String>, 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:]+)/) { $1 == "lt" || $1 == "gt" ? $1 : linkify($1, $1) }
+ "<tt>" + type + "</tt>"
+ 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
+ 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 + '<sup>?</sup>'
elsif types.size == 2 && types.last =~ /^(Array)?<#{Regexp.quote types.first}>$/
type = first + '<sup>+</sup>'
@@ -294,10 +379,19 @@
end
type = "(#{type}) " 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)
scope = meth.scope == :class ? "+" : "-"
@@ -326,29 +420,45 @@
else
title + extras_text
end
end
- def html_syntax_highlight(source, type = :ruby)
- return "" unless source
- return h(source) if options[:no_highlight]
-
- # handle !!!LANG prefix to send to html_syntax_highlight_LANG
- if source =~ /\A(?:[ \t]*\r?\n)?[ \t]*!!!([\w.+-]+)[ \t]*\r?\n/
- type, source = $1, $'
- source = $'
+ # @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
-
- meth = "html_syntax_highlight_#{type}"
- respond_to?(meth) ? send(meth, source) : h(source)
+ case lang
+ when "ascii-8bit", "us-ascii", "ascii-7bit"; 'iso-8859-1'
+ else; lang
+ end
end
- def html_syntax_highlight_plain(source)
- h(source)
- 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