Module: RDoc::Generator::Markdown::Signatures

Included in:
RDoc::Generator::Markdown
Defined in:
lib/rdoc/generator/markdown/signatures.rb

Overview

Normalizes RDoc and RBS method signatures for headings.

Constant Summary collapse

DELIMITER_PAIRS =

Signature opening delimiters and their matching closers.

{
  "(" => ")", "[" => "]", "{" => "}"
}.freeze

Class Method Summary collapse

Class Method Details

.extract_parameter_name(parameter) ⇒ String?

Extracts a bare Ruby parameter name from a parameter fragment.

Parameters:

  • parameter (String)

    Parameter fragment.

Returns:

  • (String, nil)

    Parameter name, or nil when invalid.



160
161
162
163
# File 'lib/rdoc/generator/markdown/signatures.rb', line 160

def self.extract_parameter_name(parameter)
  match = parameter.match(/\A(?:\*\*|\*|&)?([a-z_]\w*):?\z/)
  match && match[1]
end

.merge_method_signature_arguments(signature, raw_params) ⇒ String

Merges RDoc parameter names into a type-only signature.

Parameters:

  • signature (String)

    Method signature from RDoc call sequence.

  • raw_params (String, nil)

    Method parameter list from RDoc.

Returns:

  • (String)

    Signature with names added when safe.



50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rdoc/generator/markdown/signatures.rb', line 50

def self.merge_method_signature_arguments(signature, raw_params)
  params = normalized_method_params(raw_params)

  signature_args, signature_suffix = split_signature_arguments_and_suffix(signature)
  return signature unless signature_args

  param_parts = split_signature_list(params)
  signature_parts = split_signature_list(signature_args)
  return signature unless param_parts.length.eql?(signature_parts.length)

  merged = merged_signature(param_parts, signature_parts, signature_suffix)
  merged || signature
end

.merged_signature(param_parts, signature_parts, signature_suffix) ⇒ String?

Merges matching parameter and signature fragments.

Parameters:

  • param_parts (Array<String>)

    RDoc parameter fragments.

  • signature_parts (Array<String>)

    Signature type fragments.

  • signature_suffix (String)

    Text following the argument list.

Returns:

  • (String, nil)

    Merged signature, or nil when merging is unsafe or unnecessary.



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rdoc/generator/markdown/signatures.rb', line 71

def self.merged_signature(param_parts, signature_parts, signature_suffix)
  param_names = param_parts.map { |part| extract_parameter_name(part) }
  return if param_names.any?(&:nil?)
  return if signature_parts.zip(param_names).all? { |part, name| signature_part_mentions_name?(part, name) }

  merged_args = param_parts.zip(signature_parts).map do |param, type|
    separator = param.end_with?(":") ? " " : ": "
    "#{param}#{separator}#{type}"
  end

  "(#{merged_args.join(", ")})#{signature_suffix}"
end

.normalized_method_params(raw_params) ⇒ String

Normalizes RDoc's raw parameter string.

Parameters:

  • raw_params (String, nil)

    Parameter list from RDoc.

Returns:

  • (String)

    Parameter list without outer parentheses.



89
90
91
92
93
94
# File 'lib/rdoc/generator/markdown/signatures.rb', line 89

def self.normalized_method_params(raw_params)
  params = raw_params.to_s.strip
  params = params[1...-1] if params.start_with?("(") && params.end_with?(")")

  params
end

.render_method_signature(method, store) ⇒ String

Builds a method signature from RDoc and store metadata.

Parameters:

  • method (RDoc::AnyMethod)

    Method object to render.

  • store (RDoc::Store)

    Documentation store with sidecar signatures.

Returns:

  • (String)

    Normalized method signature.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/rdoc/generator/markdown/signatures.rb', line 27

def self.render_method_signature(method, store)
  signatures = method.type_signature_lines || store.rbs_signature_for(method) || [method.param_seq]

  signatures = signatures.filter_map do |signature|
    next unless signature&.match?(/\S/)

    signature = signature.gsub("->", " -> ")
    signature = signature.gsub(/\s+/, " ").strip
    signature = " #{signature}" if signature.start_with?("->")
    merge_method_signature_arguments(signature, method.params)
  end

  return "()" if signatures.empty?

  signatures.join(" | ")
end

.signature_part_mentions_name?(text, name) ⇒ Boolean

Checks whether a signature fragment already includes a parameter name.

Parameters:

  • text (String)

    Signature fragment.

  • name (String)

    Parameter name.

Returns:

  • (Boolean)

    True when the name appears as a standalone word.



171
172
173
# File 'lib/rdoc/generator/markdown/signatures.rb', line 171

def self.signature_part_mentions_name?(text, name)
  text.match?(/(?<!\w)#{name}(?!\w)/)
end

.split_signature_arguments_and_suffix(signature) ⇒ Array<String>?

Splits a parenthesized signature into arguments and suffix.

Parameters:

  • signature (String)

    Method signature.

Returns:

  • (Array<String>, nil)

    Argument text and suffix, or nil when not parenthesized.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/rdoc/generator/markdown/signatures.rb', line 101

def self.split_signature_arguments_and_suffix(signature)
  return unless signature.start_with?("(")

  depth = 0

  signature.each_char.with_index do |char, index|
    depth += 1 if char == "("

    next unless char == ")"

    depth -= 1
    return [signature[1...index], signature[(index + 1)..]] if depth.zero?
  end
end

.split_signature_list(list) ⇒ Array<String>

Splits a comma-separated signature list while preserving nested groups.

Parameters:

  • list (String)

    Signature argument list.

Returns:

  • (Array<String>)

    Signature parts.



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/rdoc/generator/markdown/signatures.rb', line 121

def self.split_signature_list(list)
  parts = []
  current = +""
  delimiters = []

  list.each_char do |char|
    if top_level_signature_separator?(char, delimiters)
      parts << current.strip
      current.clear
    else
      closing = DELIMITER_PAIRS[char]
      if closing
        delimiters << closing
      elsif char == delimiters.last
        delimiters.pop
      end
      current << char
    end
  end

  parts << current.strip unless current.empty?
  parts
end

.top_level_signature_separator?(char, delimiters) ⇒ Boolean

Checks whether a character separates top-level signature parts.

Parameters:

  • char (String)

    Current signature character.

  • delimiters (Array<String>)

    Expected closing delimiters.

Returns:

  • (Boolean)

    Whether the character is a top-level comma.



151
152
153
# File 'lib/rdoc/generator/markdown/signatures.rb', line 151

def self.top_level_signature_separator?(char, delimiters)
  char == "," && delimiters.empty?
end