diff --git a/gapic-generator/lib/gapic/formatting_utils.rb b/gapic-generator/lib/gapic/formatting_utils.rb index b6627829b..3925843ae 100644 --- a/gapic-generator/lib/gapic/formatting_utils.rb +++ b/gapic-generator/lib/gapic/formatting_utils.rb @@ -21,16 +21,23 @@ 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/
@omit_lines = ["@InputOnly\n", "@OutputOnly\n"]
+ # Built-in YARD meta-data tags as documented in:
+ # https://rubydoc.info/gems/yard/file/docs/Tags.md#Tag_List
+ @known_yard_tags = [
+ "abstract", "api", "attr", "attr_reader", "attr_writer", "author", "deprecated", "example",
+ "note", "option", "overload", "param", "private", "raise", "return", "see", "since", "todo",
+ "version", "yield", "yieldparam", "yieldreturn"
+ ].freeze
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
+ # * Backticking unknown doc tags so they are not parsed as YARD tags
#
# Tries to be smart about exempting preformatted text blocks.
#
@@ -45,23 +52,27 @@ class << self
#
def format_doc_lines api, lines, disable_xrefs: false, transport: nil
transport ||= api&.default_transport || :grpc
- # 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.
+ # Tracks fenced blocks, multiline inline code spans, and indented code blocks.
+ in_fence = false
+ in_code_span = false
in_block = nil
base_indent = 0
(lines - @omit_lines).map do |line|
- indent = line_indent line
- if indent.nil?
+ if line =~ /^\s*(?:```|~~~)/
+ in_fence = !in_fence
+ in_code_span = false
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, transport
+ elsif !in_fence
+ indent = line_indent line
+ if indent.nil?
+ in_block = nil
+ in_code_span = false
+ else
+ in_block, base_indent = update_indent_state in_block, base_indent, line, indent
+ if in_block == false
+ line, in_code_span = format_line_content line, in_code_span
+ line = format_line_xrefs api, line, disable_xrefs, transport
+ end
end
end
line
@@ -106,11 +117,40 @@ def line_indent line
m[1].length
end
- def escape_line_braces line
- while (m = @brace_detector.match line)
- line = "#{m[:pre]}\\\\{#{m[:inside]}}#{m[:post]}"
+ def format_line_content line, in_code_span
+ parts = line.split("`", -1)
+ formatted_parts = parts.each_with_index.map do |part, idx|
+ if in_code_span
+ in_code_span = false if idx < parts.length - 1
+ part
+ else
+ is_followed_by_backtick = idx < parts.length - 1
+ in_code_span = true if is_followed_by_backtick
+ formatted = escape_prose_braces part, is_followed_by_backtick: is_followed_by_backtick
+ sanitize_prose_tags formatted
+ end
+ end
+ [formatted_parts.join("`"), in_code_span]
+ end
+
+ def escape_prose_braces text, is_followed_by_backtick: false
+ # Matches unescaped `{` outside backtick spans followed by non-whitespace.
+ # If `{` is at the end of a non-code chunk (is_followed_by_backtick: true), it is followed
+ # immediately by a backticked code span (starting with a non-whitespace backtick),
+ # so \z (end of string) is also matched.
+ pattern = is_followed_by_backtick ? /(?