# frozen_string_literal: true # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "bigdecimal" module Gapic ## # Various string formatting utils # module FormattingUtils @brace_detector = /\A(?
[^`]*(`[^`]*`[^`]*)*[^`\\])?\{(?[^\s][^}]*)\}(? .*)\z/m @xref_detector = /\A(? [^`]*(`[^`]*`[^`]*)*)?\[(?[\w\. `-]+)\]\[(? [\w\.]+)\](? .*)\z/m @list_element_detector = /\A\s*(\*|\+|-|[0-9a-zA-Z]+\.)\s/ class << self ## # Given an enumerable of lines, performs yardoc formatting, including: # * Interpreting cross-references identified as described in AIP 192 # * Escaping literal braces that look like yardoc type links # # Tries to be smart about exempting preformatted text blocks. # # @param api [Gapic::Schema::Api] # @param lines [Enumerable ] # @param disable_xrefs [Boolean] (default is `false`) Disable linking to # cross-references, and render them simply as text. This can be used if # it is known that the targets are not present in the current library. # @return [Enumerable ] # def format_doc_lines api, lines, disable_xrefs: false # To detect preformatted blocks, this tracks the "expected" base indent # according to Markdown. Specifically, this is the effective indent of # previous block, which is normally 0 except if we're in a list item. # Then, if a block is indented at least 4 spaces past that expected # indent (and as long as it remains so), those lines are considered # preformatted. in_block = nil base_indent = 0 lines.map do |line| indent = line_indent line if indent.nil? in_block = nil else in_block, base_indent = update_indent_state in_block, base_indent, line, indent if in_block == false line = escape_line_braces line line = format_line_xrefs api, line, disable_xrefs end end line end end ## # Given a number, format it in such a way that Rubocop will be happy. # Specifically, we add underscores if the magnitude is at least 10_000. # This works for both integers and floats. # # @param value [Numeric] # @return [String] # def format_number value return value.to_s if value.abs < 10_000 str = value.is_a?(Integer) ? value.to_s : BigDecimal(value.to_f.to_s).to_s("F") re = /^(-?\d+)(\d\d\d)([_\.][_\.\d]+)?$/ while (m = re.match str) str = "#{m[1]}_#{m[2]}#{m[3]}" end str end private def update_indent_state in_block, base_indent, line, indent if in_block != true && @list_element_detector =~ line in_block = false indent = base_indent if indent > base_indent base_indent = (indent + 7) / 4 * 4 else in_block = indent >= base_indent + 4 unless in_block == false base_indent = indent / 4 * 4 if in_block == false && indent < base_indent end [in_block, base_indent] end def line_indent line m = /^( *)\S/.match line m.nil? ? nil : m[1].length end def escape_line_braces line while (m = @brace_detector.match line) line = "#{m[:pre]}\\\\{#{m[:inside]}}#{m[:post]}" end line end def format_line_xrefs api, line, disable_xrefs while (m = @xref_detector.match line) entity = api.lookup m[:addr] return line if entity.nil? text = m[:text] yard_link = disable_xrefs ? text : yard_link_for_entity(entity, text) return line if yard_link.nil? line = "#{m[:pre]}#{yard_link}#{m[:post]}" end line end ## # Generate a YARD-style cross-reference for the given entity. # # @param entity [Gapic::Schema::Proto] the entity to link to # @param text [String] the text for the link # @return [String] YARD cross-reference syntax # def yard_link_for_entity entity, text # As a special case, omit the service "google.longrunning.Operations" # and its methods. This is because the generator creates # service-specific copies of the operations client, rather than a # Google::Longrunning::Operations::Client class, and there is in # general no way to tell what the actual service-specific namespace is. return text if entity.address[0, 3] == ["google", "longrunning", "Operations"] case entity when Gapic::Schema::Service "{::#{convert_address_to_ruby entity}::Client #{text}}" when Gapic::Schema::Method "{::#{convert_address_to_ruby entity.parent}::Client##{entity.name.underscore} #{text}}" when Gapic::Schema::Message, Gapic::Schema::Enum "{::#{convert_address_to_ruby entity} #{text}}" when Gapic::Schema::EnumValue "{::#{convert_address_to_ruby entity.parent}::#{entity.name} #{text}}" when Gapic::Schema::Field "{::#{convert_address_to_ruby entity.parent}##{entity.name} #{text}}" end end def convert_address_to_ruby entity file = entity.containing_file api = file.containing_api address = entity.address address = address.join "." if address.is_a? Array address = address.sub file.package, file.ruby_package if file.ruby_package&.present? address.split(/\.|::/).reject(&:empty?).map(&:camelize).map { |node| api.fix_namespace node }.join("::") end end end end