21700: Install Bundler system-wide in Rails postinst
[arvados.git] / sdk / ruby-google-api-client / yard / lib / yard / templates / helpers / wiki_helper.rb
1 require 'cgi'
2 require 'rdiscount'
3
4 module YARD
5   module Templates::Helpers
6     # The helper module for HTML templates.
7     module WikiHelper
8       include MarkupHelper
9
10       # @return [String] escapes text
11       def h(text)
12         out = ""
13         text = text.split(/\n/)
14         text.each_with_index do |line, i|
15           out <<
16           case line
17           when /^\s*$/; "\n\n"
18           when /^\s+\S/, /^=/; line + "\n"
19           else; line + (text[i + 1] =~ /^\s+\S/ ? "\n" : " ")
20           end
21         end
22         out.strip
23       end
24
25       # @return [String] wraps text at +col+ columns.
26       def wrap(text, col = 72)
27         text.strip.gsub(/(.{1,#{col}})( +|$\n?)|(.{1,#{col}})/, "\\1\\3\n")
28       end
29
30       # Escapes a URL
31       # 
32       # @param [String] text the URL
33       # @return [String] the escaped URL
34       def urlencode(text)
35         CGI.escape(text.to_s)
36       end
37
38       def indent(text, len = 2)
39         text.gsub(/^/, ' ' * len)
40       end
41
42       def unindent(text)
43         lines = text.split("\n", -1)
44         min_indent_size = text.size
45         for line in lines
46           indent_size = (line.gsub("\t", "  ") =~ /[^\s]/) || text.size
47           min_indent_size = indent_size if indent_size < min_indent_size
48         end
49         text.gsub("\t", "  ").gsub(Regexp.new("^" + " " * min_indent_size), '')
50       end
51
52       # @group Converting Markup to HTML
53
54       # Turns text into HTML using +markup+ style formatting.
55       #
56       # @param [String] text the text to format
57       # @param [Symbol] markup examples are +:markdown+, +:textile+, +:rdoc+.
58       #   To add a custom markup type, see {MarkupHelper}
59       # @return [String] the HTML
60       def htmlify(text, markup = options[:markup])
61         markup_meth = "html_markup_#{markup}"
62         return text unless respond_to?(markup_meth)
63         return "" unless text
64         return text unless markup
65         load_markup_provider(markup)
66         html = send(markup_meth, text)
67         if html.respond_to?(:encode)
68           html = html.force_encoding(text.encoding) # for libs that mess with encoding
69           html = html.encode(:invalid => :replace, :replace => '?')
70         end
71         html = resolve_links(html)
72         html = html.gsub(/<pre>(?:\s*<code>)?(.+?)(?:<\/code>\s*)?<\/pre>/m) do
73           str = unindent($1).strip
74           str = html_syntax_highlight(CGI.unescapeHTML(str)) unless options[:no_highlight]
75           str
76         end unless markup == :text
77         html
78       end
79
80       # Converts Markdown to HTML
81       # @param [String] text input Markdown text
82       # @return [String] output HTML
83       # @since 0.6.0
84       def html_markup_markdown(text)
85         Markdown.new(text).to_html
86       end
87
88       # Converts Textile to HTML
89       # @param [String] text the input Textile text
90       # @return [String] output HTML
91       # @since 0.6.0
92       def html_markup_textile(text)
93         doc = markup_class(:textile).new(text)
94         doc.hard_breaks = false if doc.respond_to?(:hard_breaks=)
95         doc.to_html
96       end
97
98       # Converts plaintext to HTML
99       # @param [String] text the input text
100       # @return [String] the output HTML
101       # @since 0.6.0
102       def html_markup_text(text)
103         "<pre>" + text + "</pre>"
104       end
105
106       # Converts HTML to HTML
107       # @param [String] text input html
108       # @return [String] output HTML
109       # @since 0.6.0
110       def html_markup_html(text)
111         text
112       end
113
114       # @return [String] HTMLified text as a single line (paragraphs removed)
115       def htmlify_line(*args)
116         htmlify(*args)
117       end
118
119       # Fixes RDoc behaviour with ++ only supporting alphanumeric text.
120       #
121       # @todo Refactor into own SimpleMarkup subclass
122       def fix_typewriter(text)
123         text.gsub(/\+(?! )([^\n\+]{1,900})(?! )\+/) do
124           type_text, pre_text, no_match = $1, $`, $&
125           pre_match = pre_text.scan(%r(</?(?:pre|tt|code).*?>))
126           if pre_match.last.nil? || pre_match.last.include?('/')
127             '`' + h(type_text) + '`'
128           else
129             no_match
130           end
131         end
132       end
133
134       # Don't allow -- to turn into &#8212; element. The chances of this being
135       # some --option is far more likely than the typographical meaning.
136       #
137       # @todo Refactor into own SimpleMarkup subclass
138       def fix_dash_dash(text)
139         text.gsub(/&#8212;(?=\S)/, '--')
140       end
141
142       # @group Syntax Highlighting Source Code
143
144       # Syntax highlights +source+ in language +type+.
145       #
146       # @note To support a specific language +type+, implement the method
147       #   +html_syntax_highlight_TYPE+ in this class.
148       #
149       # @param [String] source the source code to highlight
150       # @param [Symbol] type the language type (:ruby, :plain, etc). Use
151       #   :plain for no syntax highlighting.
152       # @return [String] the highlighted source
153       def html_syntax_highlight(source, type = nil)
154         return "" unless source
155         return "{{{\n#{source}\n}}}"
156       end
157
158       # @return [String] unhighlighted source
159       def html_syntax_highlight_plain(source)
160         return "" unless source
161         return "{{{\n#{source}\n}}}"
162       end
163
164       # @group Linking Objects and URLs
165
166       # Resolves any text in the form of +{Name}+ to the object specified by
167       # Name. Also supports link titles in the form +{Name title}+.
168       #
169       # @example Linking to an instance method
170       #   resolve_links("{MyClass#method}") # => "<a href='...'>MyClass#method</a>"
171       # @example Linking to a class with a title
172       #   resolve_links("{A::B::C the C class}") # => "<a href='...'>the c class</a>"
173       # @param [String] text the text to resolve links in
174       # @return [String] HTML with linkified references
175       def resolve_links(text)
176         code_tags = 0
177         text.gsub(/<(\/)?(pre|code|tt)|\{(\S+?)(?:\s(.*?\S))?\}(?=[\W<]|.+<\/|$)/) do |str|
178           closed, tag, name, title, match = $1, $2, $3, $4, $&
179           if tag
180             code_tags += (closed ? -1 : 1)
181             next str
182           end
183           next str unless code_tags == 0
184
185           next(match) if name[0,1] == '|'
186           if object.is_a?(String)
187             object
188           else
189             link = linkify(name, title)
190             if link == name || link == title
191               match = /(.+)?(\{#{Regexp.quote name}(?:\s.*?)?\})(.+)?/.match(text)
192               file = (@file ? @file : object.file) || '(unknown)'
193               line = (@file ? 1 : (object.docstring.line_range ? object.docstring.line_range.first : 1)) + (match ? $`.count("\n") : 0)
194               log.warn "In file `#{file}':#{line}: Cannot resolve link to #{name} from text" + (match ? ":" : ".")
195               log.warn((match[1] ? '...' : '') + match[2].gsub("\n","") + (match[3] ? '...' : '')) if match
196             end
197
198             link
199           end
200         end
201       end
202
203       def unlink(value)
204         value.gsub(/\b(([A-Z][a-z]+){2,99})\b/, "!\\1")
205       end
206
207       # (see BaseHelper#link_file)
208       def link_file(filename, title = nil, anchor = nil)
209         link_url(url_for_file(filename, anchor), title)
210       end
211
212       # (see BaseHelper#link_include_object)
213       def link_include_object(obj)
214         htmlify(obj.docstring)
215       end
216
217       # (see BaseHelper#link_object)
218       def link_object(obj, otitle = nil, anchor = nil, relative = true)
219         return otitle if obj.nil?
220         obj = Registry.resolve(object, obj, true, true) if obj.is_a?(String)
221         if !otitle && obj.root?
222           title = "Top Level Namespace"
223         elsif otitle
224           # title = "`" + otitle.to_s + "`"
225           title = otitle.to_s
226         elsif object.is_a?(CodeObjects::Base)
227           # title = "`" + h(object.relative_path(obj)) + "`"
228           title = h(object.relative_path(obj))
229         else
230           # title = "`" + h(obj.to_s) + "`"
231           title = h(obj.to_s)
232         end
233         unless serializer
234           return unlink(title)
235         end
236         return unlink(title) if obj.is_a?(CodeObjects::Proxy)
237
238         link = url_for(obj, anchor, relative)
239         if link
240           link_url(link, title, :formatted => false)
241         else
242           unlink(title)
243         end
244       end
245
246       # (see BaseHelper#link_url)
247       def link_url(url, title = nil, params = {})
248         title ||= url
249         if url.to_s == ""
250           title
251         else
252           if params[:formatted]
253             "<a href=\"#{url}\">#{title}</a>"
254           else
255             "[#{url} #{title}]"
256           end
257         end
258       end
259
260       # @group URL Helpers
261
262       # @param [CodeObjects::Base] object the object to get an anchor for
263       # @return [String] the anchor for a specific object
264       def anchor_for(object)
265         # Method:_Google::APIClient#execute!
266         case object
267         when CodeObjects::MethodObject
268           if object.scope == :instance
269             "Method:_#{object.path}"
270           elsif object.scope == :class
271             "Method:_#{object.path}"
272           end
273         when CodeObjects::ClassVariableObject
274           "#{object.name.to_s.gsub('@@', '')}-#{object.type}"
275         when CodeObjects::Base
276           "#{object.name}-#{object.type}"
277         when CodeObjects::Proxy
278           object.path
279         else
280           object.to_s
281         end
282       end
283
284       # Returns the URL for an object.
285       #
286       # @param [String, CodeObjects::Base] obj the object (or object path) to link to
287       # @param [String] anchor the anchor to link to
288       # @param [Boolean] relative use a relative or absolute link
289       # @return [String] the URL location of the object
290       def url_for(obj, anchor = nil, relative = true)
291         link = nil
292         return link unless serializer
293         if obj.kind_of?(CodeObjects::Base) && obj.root?
294           return 'TopLevelNamespace'
295         end
296
297         if obj.is_a?(CodeObjects::Base) && !obj.is_a?(CodeObjects::NamespaceObject)
298           # If the obj is not a namespace obj make it the anchor.
299           anchor, obj = obj, obj.namespace
300         end
301
302         objpath = serializer.serialized_path(obj)
303         return link unless objpath
304
305         if relative
306           fromobj = object
307           if object.is_a?(CodeObjects::Base) &&
308               !object.is_a?(CodeObjects::NamespaceObject)
309             fromobj = fromobj.namespace
310           end
311
312           from = serializer.serialized_path(fromobj)
313           link = File.relative_path(from, objpath)
314         else
315           link = objpath
316         end
317
318         return (
319           link.gsub(/\.html$/, '').gsub(/\.wiki$/, '') +
320           (anchor ? '#' + urlencode(anchor_for(anchor)) : '')
321         )
322       end
323
324       # Returns the URL for a specific file
325       #
326       # @param [String] filename the filename to link to
327       # @param [String] anchor optional anchor
328       # @return [String] the URL pointing to the file
329       def url_for_file(filename, anchor = nil)
330         fromobj = object
331         if CodeObjects::Base === fromobj && !fromobj.is_a?(CodeObjects::NamespaceObject)
332           fromobj = fromobj.namespace
333         end
334         from = serializer.serialized_path(fromobj)
335         if filename == options[:readme]
336           filename = 'Documentation'
337         else
338           filename = File.basename(filename).gsub(/\.[^.]+$/, '').capitalize
339         end
340         link = File.relative_path(from, filename)
341         return (
342           link.gsub(/\.html$/, '').gsub(/\.wiki$/, '') +
343           (anchor ? '#' + urlencode(anchor) : '')
344         )
345       end
346
347       # @group Formatting Objects and Attributes
348
349       # Formats a list of objects and links them
350       # @return [String] a formatted list of objects
351       def format_object_name_list(objects)
352         objects.sort_by {|o| o.name.to_s.downcase }.map do |o|
353           "<span class='name'>" + linkify(o, o.name) + "</span>"
354         end.join(", ")
355       end
356
357       # Formats a list of types from a tag.
358       #
359       # @param [Array<String>, FalseClass] typelist
360       #   the list of types to be formatted.
361       #
362       # @param [Boolean] brackets omits the surrounding
363       #   brackets if +brackets+ is set to +false+.
364       #
365       # @return [String] the list of types formatted
366       #   as [Type1, Type2, ...] with the types linked
367       #   to their respective descriptions.
368       #
369       def format_types(typelist, brackets = true)
370         return unless typelist.is_a?(Array)
371         list = typelist.map do |type|
372           type = type.gsub(/([<>])/) { h($1) }
373           type = type.gsub(/([\w:]+)/) do
374             $1 == "lt" || $1 == "gt" ? "`#{$1}`" : linkify($1, $1)
375           end
376         end
377         list.empty? ? "" : (brackets ? "(#{list.join(", ")})" : list.join(", "))
378       end
379
380       # Get the return types for a method signature.
381       #
382       # @param [CodeObjects::MethodObject] meth the method object
383       # @param [Boolean] link whether to link the types
384       # @return [String] the signature types
385       # @since 0.5.3
386       def signature_types(meth, link = true)
387         meth = convert_method_to_overload(meth)
388
389         type = options[:default_return] || ""
390         if meth.tag(:return) && meth.tag(:return).types
391           types = meth.tags(:return).map {|t| t.types ? t.types : [] }.flatten.uniq
392           first = link ? h(types.first) : format_types([types.first], false)
393           if types.size == 2 && types.last == 'nil'
394             type = first + '<sup>?</sup>'
395           elsif types.size == 2 && types.last =~ /^(Array)?<#{Regexp.quote types.first}>$/
396             type = first + '<sup>+</sup>'
397           elsif types.size > 2
398             type = [first, '...'].join(', ')
399           elsif types == ['void'] && options[:hide_void_return]
400             type = ""
401           else
402             type = link ? h(types.join(", ")) : format_types(types, false)
403           end
404         elsif !type.empty?
405           type = link ? h(type) : format_types([type], false)
406         end
407         type = "(#{type.to_s.strip}) " unless type.empty?
408         type
409       end
410
411       # Formats the signature of method +meth+.
412       #
413       # @param [CodeObjects::MethodObject] meth the method object to list
414       #   the signature of
415       # @param [Boolean] link whether to link the method signature to the details view
416       # @param [Boolean] show_extras whether to show extra meta-data (visibility, attribute info)
417       # @param [Boolean] full_attr_name whether to show the full attribute name
418       #   ("name=" instead of "name")
419       # @return [String] the formatted method signature
420       def signature(meth, link = true, show_extras = true, full_attr_name = true)
421         meth = convert_method_to_overload(meth)
422
423         type = signature_types(meth, link)
424         name = full_attr_name ? meth.name : meth.name.to_s.gsub(/^(\w+)=$/, '\1')
425         blk = format_block(meth)
426         args = !full_attr_name && meth.writer? ? "" : format_args(meth)
427         extras = []
428         extras_text = ''
429         if show_extras
430           if rw = meth.attr_info
431             attname = [rw[:read] ? 'read' : nil, rw[:write] ? 'write' : nil].compact
432             attname = attname.size == 1 ? attname.join('') + 'only' : nil
433             extras << attname if attname
434           end
435           extras << meth.visibility if meth.visibility != :public
436           extras_text = ' <span class="extras">(' + extras.join(", ") + ')</span>' unless extras.empty?
437         end
438         title = "%s *`%s`* `%s` `%s`" % [type, h(name.to_s).strip, args, blk]
439         title.gsub!(/<tt>/, "")
440         title.gsub!(/<\/tt>/, "")
441         title.gsub!(/`\s*`/, "")
442         title.strip!
443         if link
444           if meth.is_a?(YARD::CodeObjects::MethodObject)
445             link_title =
446               "#{h meth.name(true)} (#{meth.scope} #{meth.type})"
447           else
448             link_title = "#{h name} (#{meth.type})"
449           end
450           # This has to be raw HTML, can't wiki-format a link title otherwise.
451           "<a href=\"#{url_for(meth)}\">#{title}</a>#{extras_text}"
452         else
453           title + extras_text
454         end
455       end
456
457       # @group Getting the Character Encoding
458
459       # Returns the current character set. The default value can be overridden
460       # by setting the +LANG+ environment variable or by overriding this
461       # method. In Ruby 1.9 you can also modify this value by setting
462       # +Encoding.default_external+.
463       #
464       # @return [String] the current character set
465       # @since 0.5.4
466       def charset
467         return 'utf-8' unless RUBY19 || lang = ENV['LANG']
468         if RUBY19
469           lang = Encoding.default_external.name.downcase
470         else
471           lang = lang.downcase.split('.').last
472         end
473         case lang
474         when "ascii-8bit", "us-ascii", "ascii-7bit"; 'iso-8859-1'
475         else; lang
476         end
477       end
478
479       # @endgroup
480
481       private
482
483       # Converts a set of hash options into HTML attributes for a tag
484       #
485       # @param [Hash{String => String}] opts the tag options
486       # @return [String] the tag attributes of an HTML tag
487       def tag_attrs(opts = {})
488         opts.sort_by {|k, v| k.to_s }.map {|k,v| "#{k}=#{v.to_s.inspect}" if v }.join(" ")
489       end
490
491       # Converts a {CodeObjects::MethodObject} into an overload object
492       # @since 0.5.3
493       def convert_method_to_overload(meth)
494         # use first overload tag if it has a return type and method itself does not
495         if !meth.tag(:return) && meth.tags(:overload).size == 1 && meth.tag(:overload).tag(:return)
496           return meth.tag(:overload)
497         end
498         meth
499       end
500     end
501   end
502 end