decode 0.22.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/bake/decode/index.rb +16 -9
  4. data/context/coverage.md +325 -0
  5. data/context/getting-started.md +242 -0
  6. data/context/ruby-documentation.md +363 -0
  7. data/lib/decode/comment/attribute.rb +9 -3
  8. data/lib/decode/comment/node.rb +4 -2
  9. data/lib/decode/comment/option.rb +1 -1
  10. data/lib/decode/comment/parameter.rb +12 -6
  11. data/lib/decode/comment/pragma.rb +12 -1
  12. data/lib/decode/comment/raises.rb +1 -1
  13. data/lib/decode/comment/returns.rb +3 -4
  14. data/lib/decode/comment/tag.rb +13 -3
  15. data/lib/decode/comment/tags.rb +17 -2
  16. data/lib/decode/comment/text.rb +4 -1
  17. data/lib/decode/comment/throws.rb +1 -1
  18. data/lib/decode/comment/yields.rb +7 -1
  19. data/lib/decode/definition.rb +54 -42
  20. data/lib/decode/documentation.rb +12 -14
  21. data/lib/decode/index.rb +29 -14
  22. data/lib/decode/language/generic.rb +30 -14
  23. data/lib/decode/language/reference.rb +13 -4
  24. data/lib/decode/language/ruby/alias.rb +41 -0
  25. data/lib/decode/language/ruby/attribute.rb +7 -6
  26. data/lib/decode/language/ruby/block.rb +4 -1
  27. data/lib/decode/language/ruby/call.rb +16 -6
  28. data/lib/decode/language/ruby/class.rb +19 -36
  29. data/lib/decode/language/ruby/code.rb +27 -15
  30. data/lib/decode/language/ruby/constant.rb +9 -8
  31. data/lib/decode/language/ruby/definition.rb +27 -19
  32. data/lib/decode/language/ruby/function.rb +2 -1
  33. data/lib/decode/language/ruby/generic.rb +17 -7
  34. data/lib/decode/language/ruby/method.rb +47 -12
  35. data/lib/decode/language/ruby/module.rb +4 -11
  36. data/lib/decode/language/ruby/parser.rb +358 -207
  37. data/lib/decode/language/ruby/reference.rb +26 -17
  38. data/lib/decode/language/ruby/segment.rb +11 -4
  39. data/lib/decode/language/ruby.rb +4 -2
  40. data/lib/decode/language.rb +2 -2
  41. data/lib/decode/languages.rb +25 -6
  42. data/lib/decode/location.rb +2 -0
  43. data/lib/decode/scope.rb +1 -1
  44. data/lib/decode/segment.rb +6 -5
  45. data/lib/decode/source.rb +12 -4
  46. data/lib/decode/syntax/link.rb +9 -1
  47. data/lib/decode/syntax/match.rb +12 -0
  48. data/lib/decode/syntax/rewriter.rb +10 -0
  49. data/lib/decode/trie.rb +27 -22
  50. data/lib/decode/version.rb +1 -1
  51. data.tar.gz.sig +0 -0
  52. metadata +9 -10
  53. metadata.gz.sig +0 -0
@@ -3,7 +3,7 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'tag'
6
+ require_relative "tag"
7
7
 
8
8
  module Decode
9
9
  module Comment
@@ -15,6 +15,9 @@ module Decode
15
15
  class Yields < Tag
16
16
  PATTERN = /\A(?<block>{.*?})(\s+(?<details>.*?))?\Z/
17
17
 
18
+ # Build a yields tag from a directive and match.
19
+ # @parameter directive [String] The directive name.
20
+ # @parameter match [MatchData] The regex match data.
18
21
  def self.build(directive, match)
19
22
  node = self.new(directive, match[:block])
20
23
 
@@ -25,6 +28,9 @@ module Decode
25
28
  return node
26
29
  end
27
30
 
31
+ # Initialize a new yields tag.
32
+ # @parameter directive [String] The directive name.
33
+ # @parameter block [String] The block signature.
28
34
  def initialize(directive, block)
29
35
  super(directive)
30
36
 
@@ -3,76 +3,106 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'location'
6
+ require_relative "location"
7
7
 
8
8
  module Decode
9
9
  # A symbol with attached documentation.
10
10
  class Definition
11
11
  # Initialize the symbol.
12
- # @parameter name [Symbol] The name of the definition.
12
+ # @parameter path [Symbol | Array(Symbol)] The path of the definition relatve to the parent.
13
13
  # @parameter parent [Symbol] The parent lexical scope.
14
14
  # @parameter language [Language] The language in which the symbol is defined in.
15
15
  # @parameter comments [Array(String)] The comments associated with the definition.
16
- def initialize(name, parent: nil, language: parent.language, comments: nil)
17
- @name = name
16
+ # @parameter source [Source] The source file containing this definition.
17
+ def initialize(path, parent: nil, language: parent&.language, comments: nil, visibility: :public, source: parent&.source)
18
+ @path = Array(path).map(&:to_sym)
18
19
 
19
20
  @parent = parent
20
21
  @language = language
22
+ @source = source
21
23
 
22
24
  @comments = comments
25
+ @visibility = visibility
23
26
 
24
- @path = nil
27
+ @full_path = nil
25
28
  @qualified_name = nil
29
+ @nested_name = nil
26
30
  end
27
31
 
32
+ # Generate a debug representation of the definition.
28
33
  def inspect
29
34
  "\#<#{self.class} #{qualified_name}>"
30
35
  end
31
36
 
37
+ # Generate a string representation of the definition.
32
38
  alias to_s inspect
33
-
39
+
34
40
  # The symbol name.
35
41
  # e.g. `:Decode`.
36
42
  # @attribute [Symbol]
37
- attr :name
43
+ def name
44
+ @path.last
45
+ end
46
+
47
+ # @attribute [Array(Symbol)] The path to the definition, relative to the parent.
48
+ attr :path
49
+
50
+ # The full path to the definition.
51
+ def full_path
52
+ @full_path ||= begin
53
+ if @parent
54
+ @parent.full_path + @path
55
+ else
56
+ @path
57
+ end
58
+ end
59
+ end
60
+
61
+ # The lexical path to the definition (full path including all namespaces).
62
+ # @returns [Array(Symbol)] The complete path from root to this definition.
63
+ alias lexical_path full_path
38
64
 
39
- # The parent definition, defining lexical scope.
40
- # @attribute [Definition | Nil]
65
+ # @attribute [Definition | Nil] The parent definition, defining lexical scope.
41
66
  attr :parent
42
67
 
43
- # The language the symbol is defined within.
44
- # @attribute [Language::Generic]
68
+ # @attribute [Language::Generic] The language the symbol is defined within.
45
69
  attr :language
46
70
 
47
- # The comment lines which directly preceeded the definition.
48
- # @attribute [Array(String)]
71
+ # @attribute [Source | Nil] The source file containing this definition.
72
+ attr :source
73
+
74
+ # @attribute [Array(String)] The comment lines which directly preceeded the definition.
49
75
  attr :comments
50
76
 
51
77
  # Whether the definition is considered part of the public interface.
52
- #
53
78
  # This is used to determine whether the definition should be documented for coverage purposes.
54
- #
55
- # @returns [Boolean]
79
+ # @returns [Boolean] True if the definition is public.
56
80
  def public?
57
81
  true
58
82
  end
59
83
 
84
+ # Whether the definition has documentation.
85
+ # @returns [Boolean] True if the definition has non-empty comments.
86
+ def documented?
87
+ @comments&.any?
88
+ end
89
+
60
90
  # The qualified name is an absolute name which includes any and all namespacing.
61
91
  # @returns [String]
62
92
  def qualified_name
63
93
  @qualified_name ||= begin
64
94
  if @parent
65
- @parent.qualified_name + self.nested_name
95
+ [@parent.qualified_name, self.nested_name].join("::")
66
96
  else
67
- @name.to_s
97
+ self.nested_name
68
98
  end
69
99
  end
70
100
  end
71
101
 
72
- # The name of this definition plus the nesting prefix.
102
+ # The name relative to the parent.
73
103
  # @returns [String]
74
104
  def nested_name
75
- "::#{@name}"
105
+ @nested_name ||= "#{@path.join("::")}"
76
106
  end
77
107
 
78
108
  # Does the definition name match the specified prefix?
@@ -86,28 +116,6 @@ module Decode
86
116
  raise ArgumentError, "Unable to convert #{self} into #{kind}!"
87
117
  end
88
118
 
89
- # The lexical scope as an array of names.
90
- # e.g. `[:Decode, :Definition]`
91
- # @returns [Array]
92
- def path
93
- if @path
94
- # Cached version:
95
- @path
96
- elsif @parent
97
- # Merge with parent:
98
- @path = [*@parent.path, *path_name].freeze
99
- else
100
- # At top:
101
- @path = path_name.freeze
102
- end
103
- end
104
-
105
- def path_name
106
- [@name]
107
- end
108
-
109
- alias lexical_path path
110
-
111
119
  # A short form of the definition.
112
120
  # e.g. `def short_form`.
113
121
  #
@@ -173,5 +181,9 @@ module Decode
173
181
  def location
174
182
  nil
175
183
  end
184
+
185
+ # The visibility of the definition.
186
+ # @attribute [Symbol] :public, :private, :protected
187
+ attr_accessor :visibility
176
188
  end
177
189
  end
@@ -3,17 +3,17 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'comment/node'
6
+ require_relative "comment/node"
7
7
 
8
- require_relative 'comment/tags'
9
- require_relative 'comment/attribute'
10
- require_relative 'comment/parameter'
11
- require_relative 'comment/option'
12
- require_relative 'comment/pragma'
13
- require_relative 'comment/raises'
14
- require_relative 'comment/returns'
15
- require_relative 'comment/throws'
16
- require_relative 'comment/yields'
8
+ require_relative "comment/tags"
9
+ require_relative "comment/attribute"
10
+ require_relative "comment/parameter"
11
+ require_relative "comment/option"
12
+ require_relative "comment/pragma"
13
+ require_relative "comment/raises"
14
+ require_relative "comment/returns"
15
+ require_relative "comment/throws"
16
+ require_relative "comment/yields"
17
17
 
18
18
  module Decode
19
19
  # Structured access to a set of comment lines.
@@ -31,12 +31,10 @@ module Decode
31
31
  end
32
32
  end
33
33
 
34
- # The underlying comments from which the documentation is extracted.
35
- # @attribute [Array(String)]
34
+ # @attribute [Array(String)] The underlying comments from which the documentation is extracted.
36
35
  attr :comments
37
36
 
38
- # The language in which the documentation was extracted from.
39
- # @attribute [Language::Generic]
37
+ # @attribute [Language::Generic] The language in which the documentation was extracted from.
40
38
  attr :language
41
39
  end
42
40
  end
data/lib/decode/index.rb CHANGED
@@ -3,90 +3,105 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'source'
7
- require_relative 'trie'
6
+ require_relative "source"
7
+ require_relative "trie"
8
8
 
9
- require_relative 'languages'
9
+ require_relative "languages"
10
10
 
11
11
  module Decode
12
- # A list of definitions organised for quick lookup and lexical enumeration.
12
+ # Represents a list of definitions organised for quick lookup and lexical enumeration.
13
13
  class Index
14
14
  # Initialize an empty index.
15
+ # @parameter languages [Languages] The languages to support in this index.
15
16
  def initialize(languages = Languages.all)
17
+ # Initialize with supported languages:
16
18
  @languages = languages
17
19
 
20
+ # Initialize storage for sources and definitions:
18
21
  @sources = {}
19
22
  @definitions = {}
20
23
 
21
- # This is essentially a prefix tree:
24
+ # Create a prefix tree for efficient lookups:
22
25
  @trie = Trie.new
23
26
  end
24
27
 
28
+ # Generate a string representation of this index.
29
+ # @returns [String] A formatted string showing the number of definitions.
25
30
  def inspect
26
31
  "#<#{self.class} #{@definitions.size} definition(s)>"
27
32
  end
28
33
 
34
+ # Generate a string representation of the index.
29
35
  alias to_s inspect
30
36
 
31
37
  # All supported languages for this index.
32
- # @attribute [Languages]
38
+ # @attribute [Languages] The languages this index can parse.
33
39
  attr :languages
34
40
 
35
41
  # All source files that have been parsed.
36
- # @attribute [Array(Source)]
42
+ # @attribute [Hash(String, Source)] A mapping of file paths to source objects.
37
43
  attr :sources
38
44
 
39
45
  # All definitions which have been parsed.
40
- # @attribute [Hash(String, Definition)]
46
+ # @attribute [Hash(String, Definition)] A mapping of qualified names to definitions.
41
47
  attr :definitions
42
48
 
43
49
  # A (prefix) trie of lexically scoped definitions.
44
- # @attribute [Trie]
50
+ # @attribute [Trie] The trie structure for efficient lookups.
45
51
  attr :trie
46
52
 
47
53
  # Updates the index by parsing the specified files.
48
54
  # All extracted definitions are merged into the existing index.
49
- #
50
- # @parameter paths [Array(String)] The source file paths.
55
+ # @parameter paths [Array(String)] The source file paths to parse and index.
51
56
  def update(paths)
52
57
  paths.each do |path|
53
58
  if source = @languages.source_for(path)
59
+ # Store the source file:
54
60
  @sources[path] = source
55
61
 
62
+ # Extract and index all definitions:
56
63
  source.definitions do |symbol|
57
64
  # $stderr.puts "Adding #{symbol.qualified_name} to #{symbol.lexical_path.join(' -> ')}"
58
65
 
66
+ # Add to definitions lookup:
59
67
  @definitions[symbol.qualified_name] = symbol
60
- @trie.insert(symbol.path, symbol)
68
+
69
+ # Add to trie for hierarchical lookup:
70
+ @trie.insert(symbol.full_path, symbol)
61
71
  end
62
72
  end
63
73
  end
64
74
  end
65
75
 
66
76
  # Lookup the specified reference and return matching definitions.
67
- #
68
77
  # @parameter reference [Language::Reference] The reference to match.
69
78
  # @parameter relative_to [Definition] Lookup the reference relative to the scope of this definition.
79
+ # @returns [Definition | Nil] The best matching definition, or nil if not found.
70
80
  def lookup(reference, relative_to: nil)
71
81
  if reference.absolute? || relative_to.nil?
82
+ # Start from root scope:
72
83
  lexical_path = []
73
84
  else
74
- lexical_path = relative_to.path.dup
85
+ # Start from the given definition's scope:
86
+ lexical_path = relative_to.full_path.dup
75
87
  end
76
88
 
77
89
  path = reference.path
78
90
 
79
91
  while true
92
+ # Get the current scope node:
80
93
  node = @trie.lookup(lexical_path)
81
94
 
82
95
  if node.children[path.first]
83
96
  if target = node.lookup(path)
97
+ # Return the best matching definition:
84
98
  return reference.best(target.values)
85
99
  else
86
100
  return nil
87
101
  end
88
102
  end
89
103
 
104
+ # Move up one scope level:
90
105
  break if lexical_path.empty?
91
106
  lexical_path.pop
92
107
  end
@@ -3,53 +3,69 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'reference'
7
- require_relative '../documentation'
6
+ require_relative "reference"
7
+ require_relative "../documentation"
8
8
 
9
9
  module Decode
10
10
  module Language
11
+ # Represents a generic language implementation that can be extended for specific languages.
11
12
  class Generic
12
13
  EXTENSIONS = []
13
14
 
14
15
  TAGS = Comment::Tags.build do |tags|
15
- tags['attribute'] = Comment::Attribute
16
- tags['parameter'] = Comment::Parameter
17
- tags['option'] = Comment::Option
18
- tags['yields'] = Comment::Yields
19
- tags['returns'] = Comment::Returns
20
- tags['raises'] = Comment::Raises
21
- tags['throws'] = Comment::Throws
16
+ tags["attribute"] = Comment::Attribute
17
+ tags["parameter"] = Comment::Parameter
18
+ tags["option"] = Comment::Option
19
+ tags["yields"] = Comment::Yields
20
+ tags["returns"] = Comment::Returns
21
+ tags["raises"] = Comment::Raises
22
+ tags["throws"] = Comment::Throws
22
23
 
23
- tags['deprecated'] = Comment::Pragma
24
+ tags["deprecated"] = Comment::Pragma
24
25
 
25
- tags['asynchronous'] = Comment::Pragma
26
+ tags["asynchronous"] = Comment::Pragma
26
27
 
27
- tags['public'] = Comment::Pragma
28
- tags['private'] = Comment::Pragma
28
+ tags["public"] = Comment::Pragma
29
+ tags["private"] = Comment::Pragma
29
30
  end
30
31
 
32
+ # Initialize a new generic language.
33
+ # @parameter name [String] The name of the language.
34
+ # @parameter extensions [Array(String)] File extensions for this language.
35
+ # @parameter tags [Comment::Tags] The comment tags to recognize.
31
36
  def initialize(name, extensions: self.class::EXTENSIONS, tags: self.class::TAGS)
32
37
  @name = name
33
38
  @extensions = extensions
34
39
  @tags = tags
35
40
  end
36
41
 
42
+ # The name of this language.
43
+ # @attribute [String] The language name.
37
44
  attr :name
38
45
 
46
+ # Get all names for this language.
47
+ # @returns [Array(String)] An array containing the language name.
39
48
  def names
40
49
  [@name]
41
50
  end
42
51
 
52
+ # The file extensions this language supports.
53
+ # @attribute [Array(String)] The supported file extensions.
43
54
  attr :extensions
44
55
 
56
+ # The comment tags this language recognizes.
57
+ # @attribute [Comment::Tags] The tag definitions.
45
58
  attr :tags
46
59
 
47
60
  # Generate a language-specific reference.
48
- # @parameter identifier [String] A valid identifier.
61
+ # @parameter identifier [String] A valid identifier for this language.
62
+ # @returns [Reference] A reference object for the given identifier.
49
63
  def reference_for(identifier)
50
64
  Reference.new(identifier, self)
51
65
  end
52
66
 
67
+ # Get the parser for this language.
68
+ # @returns [Parser | Nil] The parser instance, or nil if not available.
53
69
  def parser
54
70
  nil
55
71
  end
@@ -17,20 +17,20 @@ module Decode
17
17
  @path = nil
18
18
  end
19
19
 
20
+ # Generate a string representation of the reference.
20
21
  def to_s
21
22
  "{#{self.language} #{self.identifier}}"
22
23
  end
23
24
 
25
+ # Generate a debug representation of the reference.
24
26
  def inspect
25
27
  "\#<#{self.class} {#{self.identifier}}>"
26
28
  end
27
29
 
28
- # The identifier part of the reference.
29
- # @attribute [String]
30
+ # @attribute [String] The identifier part of the reference.
30
31
  attr :identifier
31
32
 
32
- # The language associated with this reference.
33
- # @attribute [Language::Generic]
33
+ # @attribute [Language::Generic] The language associated with this reference.
34
34
  attr :language
35
35
 
36
36
  # Whether the reference starts at the base of the lexical tree.
@@ -38,20 +38,27 @@ module Decode
38
38
  !self.relative?
39
39
  end
40
40
 
41
+ # Check if this is a relative reference.
41
42
  def relative?
42
43
  prefix, name = self.lexical_path.first
43
44
 
44
45
  return prefix.nil?
45
46
  end
46
47
 
48
+ # Split an identifier into prefix and name components.
49
+ # @parameter identifier [String] The identifier to split.
47
50
  def split(identifier)
48
51
  identifier.scan(/(\W+)?(\w+)/)
49
52
  end
50
53
 
54
+ # Get the lexical path of this reference.
51
55
  def lexical_path
52
56
  @lexical_path ||= self.split(@identifier)
53
57
  end
54
58
 
59
+ # Calculate the priority of a definition for matching.
60
+ # @parameter definition [String] The definition to check.
61
+ # @parameter prefix [String] The prefix to match against.
55
62
  def priority(definition, prefix)
56
63
  if prefix.nil?
57
64
  return 1
@@ -62,6 +69,8 @@ module Decode
62
69
  end
63
70
  end
64
71
 
72
+ # Find the best matching definition from a list.
73
+ # @parameter definitions [Array(String)] The definitions to choose from.
65
74
  def best(definitions)
66
75
  prefix, name = lexical_path.last
67
76
 
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2020-2024, by Samuel Williams.
5
+
6
+ require_relative "definition"
7
+
8
+ module Decode
9
+ module Language
10
+ module Ruby
11
+ # Represents an alias statement, e.g., `alias new_name old_name` or `alias_method :new_name, :old_name`
12
+ class Alias < Definition
13
+ # Initialize a new alias definition.
14
+ # @parameter new_name [String] The new name for the alias.
15
+ # @parameter old_name [String] The original name being aliased.
16
+ # @parameter options [Hash] Additional options for the definition.
17
+ def initialize(new_name, old_name, **options)
18
+ super(new_name, **options)
19
+ @old_name = old_name
20
+ end
21
+
22
+ attr :old_name
23
+
24
+ # Generate a short form representation of the alias.
25
+ def short_form
26
+ "alias #{self.name} #{@old_name}"
27
+ end
28
+
29
+ # Generate a long form representation of the alias.
30
+ def long_form
31
+ "alias #{self.name} #{@old_name}"
32
+ end
33
+
34
+ # Generate a string representation of the alias.
35
+ def to_s
36
+ "#{self.class.name} #{self.name} -> #{@old_name}"
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -3,7 +3,7 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'definition'
6
+ require_relative "definition"
7
7
 
8
8
  module Decode
9
9
  module Language
@@ -13,17 +13,18 @@ module Decode
13
13
  # The short form of the attribute.
14
14
  # e.g. `attr :value`.
15
15
  def short_form
16
- case @node.type
17
- when :block
16
+ case @node&.type
17
+ when :block_node
18
18
  "#{@name} { ... }"
19
19
  else
20
- @node.location.expression.source
20
+ @node&.location&.slice || @name
21
21
  end
22
22
  end
23
23
 
24
+ # Generate a long form representation of the attribute.
24
25
  def long_form
25
- if @node.location.line == @node.location.last_line
26
- @node.location.expression.source
26
+ if @node&.location&.start_line == @node&.location&.end_line
27
+ @node.location.slice
27
28
  else
28
29
  short_form
29
30
  end
@@ -3,7 +3,7 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'definition'
6
+ require_relative "definition"
7
7
 
8
8
  module Decode
9
9
  module Language
@@ -15,6 +15,7 @@ module Decode
15
15
  true
16
16
  end
17
17
 
18
+ # Generate a nested name for the block.
18
19
  def nested_name
19
20
  ".#{name}"
20
21
  end
@@ -41,6 +42,8 @@ module Decode
41
42
  self.qualified_name
42
43
  end
43
44
 
45
+ # Convert the block to a different kind of definition.
46
+ # @parameter kind [Symbol] The kind to convert to.
44
47
  def convert(kind)
45
48
  case kind
46
49
  when :attribute
@@ -3,7 +3,7 @@
3
3
  # Released under the MIT License.
4
4
  # Copyright, 2020-2024, by Samuel Williams.
5
5
 
6
- require_relative 'definition'
6
+ require_relative "definition"
7
7
 
8
8
  module Decode
9
9
  module Language
@@ -12,22 +12,32 @@ module Decode
12
12
  class Call < Definition
13
13
  # A block can sometimes be a container for other definitions.
14
14
  def container?
15
- false
15
+ @node&.block && @node.block.opening == "do"
16
16
  end
17
17
 
18
18
  # The short form of the class.
19
19
  # e.g. `foo`.
20
20
  def short_form
21
- @name.to_s
21
+ if @node&.block && @node.block.opening == "{"
22
+ "#{name} { ... }"
23
+ else
24
+ name.to_s
25
+ end
22
26
  end
23
27
 
24
28
  # The long form of the class.
25
29
  # e.g. `foo(:bar)`.
26
30
  def long_form
27
- if @node.location.line == @node.location.last_line
28
- @node.location.expression.source
31
+ if @node.location.start_line == @node.location.end_line
32
+ @node.location.slice
29
33
  else
30
- self.short_form
34
+ # For multiline calls, use the actual call name with arguments
35
+ if @node.arguments && @node.arguments.arguments.any?
36
+ arg_text = @node.arguments.arguments.map { |arg| arg.location.slice }.join(", ")
37
+ "#{@node.name}(#{arg_text})"
38
+ else
39
+ @node.name.to_s
40
+ end
31
41
  end
32
42
  end
33
43