lib/yard/templates/helpers/html_helper.rb in yard-0.9.5 vs lib/yard/templates/helpers/html_helper.rb in yard-0.9.6
- old
+ new
@@ -1,14 +1,18 @@
+# frozen_string_literal: true
require 'cgi'
module YARD
module Templates::Helpers
# The helper module for HTML templates.
module HtmlHelper
include MarkupHelper
include HtmlSyntaxHighlightHelper
+ # @private
+ URLMATCH = /[^\w\s~!\*'\(\):;@&=\$,\[\]<>-]/
+
# @group Escaping Template Data
# Escapes HTML entities
#
# @param [String] text the text to escape
@@ -20,13 +24,27 @@
# Escapes a URL
#
# @param [String] text the URL
# @return [String] the escaped URL
def urlencode(text)
- CGI.escape(text.to_s)
+ text = text.dup
+ enc = nil
+ if text.respond_to?(:force_encoding)
+ enc = text.encoding
+ text = text.force_encoding('binary')
+ end
+
+ text = text.gsub(/%[a-z0-9]{2}|#{URLMATCH}/i) do
+ $&.size > 1 ? $& : "%" + $&.ord.to_s(16).upcase
+ end.tr(' ', '+')
+
+ text = text.force_encoding(enc) if enc
+ text
end
+ module_function :urlencode
+
# @group Converting Markup to HTML
# Turns text into HTML using +markup+ style formatting.
#
# @param [String] text the text to format
@@ -36,11 +54,11 @@
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
- html = send(markup_meth, text)
+ html = send(markup_meth, text).dup
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)
@@ -186,39 +204,45 @@
# resolve_links("{A::B::C the C class}") # => "<a href='...'>the c class</a>"
# @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<]|.+<\/|$)/m) do |str|
- closed, tag, escape, name, title, match = $1, $2, $3, $4, $5, $&
+ text.gsub(%r{<(/)?(pre|code|tt)|(\\|!)?\{(?!\})(\S+?)(?:\s([^\}]*?\S))?\}(?=[\W<]|.+</|$)}m) do |str|
+ closed = $1
+ tag = $2
+ escape = $3
+ name = $4
+ title = $5
+ match = $&
if tag
code_tags += (closed ? -1 : 1)
next str
end
next str unless code_tags == 0
next(match[1..-1]) if escape
- next(match) if name[0,1] == '|'
+ next(match) if name[0, 1] == '|'
- if name == '<a' && title =~ /href=["'](.+?)["'].*>.*<\/a>\s*(.*)\Z/
- name, title = $1, $2
+ if name == '<a' && title =~ %r{href=["'](.+?)["'].*>.*</a>\s*(.*)\Z}
+ name = $1
+ title = $2
title = nil if title.empty?
end
name = CGI.unescapeHTML(name)
if object.is_a?(String)
object
else
link = linkify(name, title)
- if (link == name || link == title) && (name+' '+link !~ /\A<a\s.*>/)
+ if (link == name || link == title) && (name + ' ' + link !~ /\A<a\s.*>/)
match = /(.+)?(\{#{Regexp.quote name}(?:\s.*?)?\})(.+)?/.match(text)
- file = (@file ? @file.filename : 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
+ file = (defined?(@file) && @file ? @file.filename : object.file) || '(unknown)'
+ line = (defined?(@file) && @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 ? ":" : ".") +
+ "\n\t" + (match[1] ? '...' : '') + match[2].delete("\n") + (match[3] ? '...' : '') if match
end
link
end
end
@@ -250,11 +274,11 @@
insert_include(obj.docstring)
end
# Inserts an include link while respecting inlining
def insert_include(text, markup = options.markup)
- htmlify(text, markup).gsub(/\A\s*<p>|<\/p>\s*\Z/, '')
+ htmlify(text, markup).gsub(%r{\A\s*<p>|</p>\s*\Z}, '')
end
# (see BaseHelper#link_object)
def link_object(obj, title = nil, anchor = nil, relative = true)
return title if obj.nil?
@@ -284,16 +308,16 @@
end
# (see BaseHelper#link_url)
def link_url(url, title = nil, params = {})
title ||= url
- title.gsub!(/[\r\n]/, ' ')
+ title = title.gsub(/[\r\n]/, ' ')
params = SymbolHash.new(false).update(
:href => url,
- :title => h(title)
+ :title => h(title)
).update(params)
- params[:target] ||= '_parent' if url =~ /^(\w+):\/\//
+ params[:target] ||= '_parent' if url =~ %r{^(\w+)://}
"<a #{tag_attrs(params)}>#{title}</a>".gsub(/[\r\n]/, ' ')
end
# @group URL Helpers
@@ -325,21 +349,22 @@
return link unless serializer
return link if obj.is_a?(CodeObjects::Base) && run_verifier([obj]).empty?
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
+ anchor = obj
+ obj = obj.namespace
end
objpath = serializer.serialized_path(obj)
return link unless objpath
relative = false if object == Registry.root
if relative
fromobj = object
if object.is_a?(CodeObjects::Base) &&
- !object.is_a?(CodeObjects::NamespaceObject)
+ !object.is_a?(CodeObjects::NamespaceObject)
fromobj = owner
end
from = serializer.serialized_path(fromobj)
link = File.relative_path(from, objpath)
@@ -348,10 +373,13 @@
end
link + (anchor ? '#' + urlencode(anchor_for(anchor)) : '')
end
+ alias mtime_url url_for
+ def mtime(_file) nil end
+
# Returns the URL for a specific file
#
# @param [String, CodeObjects::ExtraFileObject] filename the filename to link to
# @param [String] anchor optional anchor
# @return [String] the URL pointing to the file
@@ -360,15 +388,12 @@
fromobj = object
if CodeObjects::Base === fromobj && !fromobj.is_a?(CodeObjects::NamespaceObject)
fromobj = fromobj.namespace
end
from = serializer.serialized_path(fromobj)
- if filename == options.readme
- path = 'index.html'
- else
- path = serializer.serialized_path(filename)
- end
+ path = filename == options.readme ?
+ 'index.html' : serializer.serialized_path(filename)
link = File.relative_path(from, path)
link += (anchor ? '#' + urlencode(anchor) : '')
link
end
@@ -490,11 +515,12 @@
blk = format_block(meth)
args = !full_attr_name && meth.writer? ? "" : format_args(meth)
extras = []
extras_text = ''
if show_extras
- if rw = meth.attr_info
+ rw = meth.attr_info
+ if rw
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
@@ -524,20 +550,22 @@
#
# @return [String] the current character set
# @since 0.5.4
def charset
has_encoding = defined?(::Encoding)
- if @file && has_encoding
+ if defined?(@file) && @file && has_encoding
lang = @file.contents.encoding.to_s
else
- return 'utf-8' unless has_encoding || lang = ENV['LANG']
- if has_encoding
- lang = ::Encoding.default_external.name.downcase
- else
- lang = lang.downcase.split('.').last
- end
+ return 'utf-8' unless has_encoding || ENV['LANG']
+ lang =
+ if has_encoding
+ ::Encoding.default_external.name.downcase
+ else
+ ENV['LANG'].downcase.split('.').last
+ end
end
+
case lang
when "ascii-8bit", "us-ascii", "ascii-7bit"; 'iso-8859-1'
when "utf8"; 'utf-8'
else; lang
end
@@ -550,11 +578,11 @@
# 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(" ")
+ 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)
@@ -573,11 +601,12 @@
# remaining source
# @since 0.7.5
def parse_lang_for_codeblock(source)
type = nil
if source =~ /\A(?:[ \t]*\r?\n)?[ \t]*!!!([\w.+-]+)[ \t]*\r?\n/
- type, source = $1, $'
+ type = $1
+ source = $'
end
[type, source]
end
@@ -586,22 +615,21 @@
#
# @param [String] html the html to search for code in
# @return [String] highlighted html
# @see #html_syntax_highlight
def parse_codeblocks(html)
- html.gsub(/<pre\s*(?:lang="(.+?)")?>(?:\s*<code\s*(?:class="(.+?)")?\s*>)?(.+?)(?:<\/code>\s*)?<\/pre>/m) do
+ html.gsub(%r{<pre\s*(?:lang="(.+?)")?>(?:\s*<code\s*(?:class="(.+?)")?\s*>)?(.+?)(?:</code>\s*)?</pre>}m) do
string = $3
# handle !!!LANG prefix to send to html_syntax_highlight_LANG
- language, _ = parse_lang_for_codeblock(string)
+ language, = parse_lang_for_codeblock(string)
language ||= $1 || $2 || object.source_type
if options.highlight
string = html_syntax_highlight(CGI.unescapeHTML(string), language)
end
classes = ['code', language].compact.join(' ')
- %Q{<pre class="#{classes}"><code class="#{language}">#{string}</code></pre>}
+ %(<pre class="#{classes}"><code class="#{language}">#{string}</code></pre>)
end
end
end
end
end
-