solargraph 0.59.2 → 0.60.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +4 -1
  3. data/.rubocop.yml +1 -0
  4. data/.rubocop_todo.yml +1 -1
  5. data/CHANGELOG.md +11 -0
  6. data/Gemfile +3 -0
  7. data/lib/solargraph/api_map/index.rb +13 -2
  8. data/lib/solargraph/api_map/store.rb +21 -6
  9. data/lib/solargraph/api_map.rb +34 -2
  10. data/lib/solargraph/complex_type/unique_type.rb +4 -0
  11. data/lib/solargraph/complex_type.rb +4 -0
  12. data/lib/solargraph/doc_map.rb +1 -0
  13. data/lib/solargraph/parser/parser_gem/node_methods.rb +42 -0
  14. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -5
  15. data/lib/solargraph/pin/base.rb +31 -3
  16. data/lib/solargraph/pin/callable.rb +2 -2
  17. data/lib/solargraph/pin/common.rb +12 -0
  18. data/lib/solargraph/pin/method.rb +56 -16
  19. data/lib/solargraph/rbs_map/conversions.rb +96 -145
  20. data/lib/solargraph/rbs_translator.rb +206 -0
  21. data/lib/solargraph/shell.rb +130 -63
  22. data/lib/solargraph/source/chain/call.rb +8 -76
  23. data/lib/solargraph/source_map/mapper.rb +5 -135
  24. data/lib/solargraph/source_map.rb +14 -0
  25. data/lib/solargraph/version.rb +19 -1
  26. data/lib/solargraph/yard_map/directives/attribute_directive.rb +65 -0
  27. data/lib/solargraph/yard_map/directives/domain_directive.rb +30 -0
  28. data/lib/solargraph/yard_map/directives/method_directive.rb +51 -0
  29. data/lib/solargraph/yard_map/directives/override_directive.rb +30 -0
  30. data/lib/solargraph/yard_map/directives/parse_directive.rb +53 -0
  31. data/lib/solargraph/yard_map/directives/visibility_directive.rb +70 -0
  32. data/lib/solargraph/yard_map/directives.rb +35 -0
  33. data/lib/solargraph/yard_map/macro.rb +113 -0
  34. data/lib/solargraph/yard_map/mapper.rb +19 -1
  35. data/lib/solargraph/yard_map.rb +2 -0
  36. data/lib/solargraph.rb +1 -0
  37. data/solargraph.gemspec +1 -0
  38. metadata +24 -1
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module MethodDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Method>]
15
+ def process_directive source, pins, source_position, comment_position, directive
16
+ namespace = closure_at(pins, source_position) || pins.first
17
+
18
+ namespace = closure_at(pins, comment_position) if namespace.location&.range&.start&.line&.< comment_position.line # rubocop:disable Style/SafeNavigationChainLength
19
+ begin
20
+ src = Solargraph::Source.load_string("def #{directive.tag.name};end", source.filename)
21
+ region = Parser::Region.new(source: src, closure: namespace)
22
+ method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
23
+ gen_pin = method_gen_pins.last
24
+ return [] if gen_pin.nil?
25
+ # Move the location to the end of the line so it gets recognized
26
+ # as originating from a comment
27
+ shifted = Solargraph::Position.new(comment_position.line,
28
+ source.code.lines[comment_position.line].to_s.chomp.length)
29
+ comments = Solargraph::Source.parse_docstring(directive.tag.text.to_s).to_docstring.all.to_s
30
+ # @todo: Smelly instance variable access
31
+ gen_pin.instance_variable_set(:@comments, comments)
32
+ gen_pin.instance_variable_set(:@location,
33
+ Solargraph::Location.new(source.filename, Range.new(shifted, shifted)))
34
+ gen_pin.instance_variable_set(:@explicit, false)
35
+ [gen_pin]
36
+ rescue Parser::SyntaxError
37
+ # @todo Handle error in directive
38
+ []
39
+ end
40
+ end
41
+
42
+ # @param [Array<Pin::Base>] pins
43
+ # @param [Position] position
44
+ # @return [Pin::Closure]
45
+ def closure_at pins, position
46
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module OverrideDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param _pins [Array<Solargraph::Pin::Base>]
11
+ # @param _source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Pin::Base>]
15
+ def process_directive source, _pins, _source_position, comment_position, directive
16
+ docstring = Solargraph::Source.parse_docstring(directive.tag.text.to_s).to_docstring
17
+ location = Location.new(source.filename, Range.new(comment_position, comment_position))
18
+ [Pin::Reference::Override.new(location, directive.tag.name.to_s, docstring.tags, source: :yard_map)]
19
+ end
20
+
21
+ # @param [Array<Pin::Base>] pins
22
+ # @param [Position] position
23
+ # @return [Pin::Closure]
24
+ def closure_at pins, position
25
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module ParseDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Base>]
15
+ def process_directive source, pins, source_position, comment_position, directive
16
+ ns = closure_at(pins, source_position)
17
+ pins_copy = pins.dup
18
+ src = Solargraph::Source.load_string(directive.tag.text.to_s, source.filename)
19
+ region = Parser::Region.new(source: src, closure: ns)
20
+ # @todo These pins may need to be marked not explicit
21
+ old_pins_index = pins.length
22
+ loff = if source.code.lines[comment_position.line].strip.end_with?('@!parse')
23
+ comment_position.line + 1
24
+ else
25
+ comment_position.line
26
+ end
27
+ Parser.process_node(src.node, region, pins_copy)
28
+ new_pins = pins_copy[old_pins_index..] || []
29
+ new_pins.each do |p|
30
+ # @todo Smelly instance variable access
31
+ next if p.location.nil?
32
+ # @sg-ignore Unresolved call to range on Solargraph::Location, nil - does not account for next clause above.
33
+ p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
34
+ # @sg-ignore Unresolved call to range on Solargraph::Location, nil
35
+ p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
36
+ end
37
+
38
+ new_pins
39
+ rescue Parser::SyntaxError
40
+ # @todo Handle parser errors in !parse directives
41
+ []
42
+ end
43
+
44
+ # @param [Array<Pin::Base>] pins
45
+ # @param [Position] position
46
+ # @return [Pin::Closure]
47
+ def closure_at pins, position
48
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module VisibilityDirective
7
+ module_function
8
+
9
+ VALID_VISIBILITIES = %i[public protected private].freeze
10
+
11
+ # @param source [Solargraph::Source]
12
+ # @param pins [Array<Solargraph::Pin::Base>]
13
+ # @param source_position [Position]
14
+ # @param comment_position [Position]
15
+ # @param directive [YARD::Tags::Directive]
16
+ # @return [Array<Solargraph::Pin::Base>]
17
+ def process_directive source, pins, source_position, comment_position, directive
18
+ kind = directive.tag.text&.to_sym
19
+
20
+ # @sg-ignore include? only expects Symbol, but receives Symbol or nil
21
+ return [] unless VALID_VISIBILITIES.include?(kind.to_sym)
22
+
23
+ name = directive.tag.name
24
+ closure = closure_at(pins, source_position) || pins.first
25
+ closure = closure_at(pins, comment_position) if closure.location&.range&.start&.line&.< comment_position.line # rubocop:disable Style/SafeNavigationChainLength
26
+ if closure.is_a?(Pin::Method) && no_empty_lines?(source.code, comment_position.line, source_position.line)
27
+ # @todo Smelly instance variable access
28
+ closure.instance_variable_set(:@visibility, kind)
29
+ else
30
+ namespace = closure_at(pins, source_position)
31
+ matches = pins.select do |pin|
32
+ if pin.is_a?(Pin::Method) &&
33
+ pin.name == name &&
34
+ pin.namespace == namespace &&
35
+ pin.context.scope == namespace.is_a?(Pin::Singleton)
36
+ :class
37
+ else
38
+ :instance
39
+ end
40
+ end
41
+
42
+ matches.each do |pin|
43
+ # @todo Smelly instance variable access
44
+ pin.instance_variable_set(:@visibility, kind)
45
+ end
46
+ end
47
+
48
+ []
49
+ end
50
+
51
+ # @param [String] code
52
+ # @param [Integer] line1
53
+ # @param [Integer] line2
54
+ # @return [Boolean]
55
+ # @sg-ignore return type could not be inferred
56
+ def no_empty_lines? code, line1, line2
57
+ # @sg-ignore unresolved call none? on the array.
58
+ code.lines[line1..line2].none? { |line| line.strip.empty? }
59
+ end
60
+
61
+ # @param [Array<Pin::Base>] pins
62
+ # @param [Position] position
63
+ # @return [Pin::Closure]
64
+ def closure_at pins, position
65
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ autoload :AttributeDirective, 'solargraph/yard_map/directives/attribute_directive'
7
+ autoload :MethodDirective, 'solargraph/yard_map/directives/method_directive'
8
+ autoload :DomainDirective, 'solargraph/yard_map/directives/domain_directive'
9
+ autoload :OverrideDirective, 'solargraph/yard_map/directives/override_directive'
10
+ autoload :ParseDirective, 'solargraph/yard_map/directives/parse_directive'
11
+ autoload :VisibilityDirective, 'solargraph/yard_map/directives/visibility_directive'
12
+
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Class<AttributeDirective>, Class<MethodDirective>, Class<DomainDirective>, Class<OverrideDirective>, Class<ParseDirective>, Class<VisibilityDirective>, nil]
15
+ def self.for directive
16
+ case directive.tag.tag_name
17
+ when 'attribute'
18
+ AttributeDirective
19
+ when 'method'
20
+ MethodDirective
21
+ when 'domain'
22
+ DomainDirective
23
+ when 'override'
24
+ OverrideDirective
25
+ when 'parse'
26
+ ParseDirective
27
+ when 'visibility'
28
+ VisibilityDirective
29
+ else # rubocop:disable Style/EmptyElse
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ class Macro
6
+ PROCESSABLE_DIRECTIVES = %w[method attribute parse].freeze
7
+
8
+ class << self
9
+ # @param directive [YARD::Tags::Directive]
10
+ # @param method_pin [Pin::Method]
11
+ # @return [Macro]
12
+ def from_directive directive, method_pin
13
+ macro_name = directive.tag.name.empty? ? method_pin.path.downcase : directive.tag.name
14
+ method_object = method_object_from_pin(method_pin)
15
+ code = directive.tag.text.to_s.gsub(/\n(?!@!|\s)/, "\n ")
16
+ macro_object = YARD::CodeObjects::MacroObject.create(macro_name.to_s, code, method_object)
17
+ new(macro_object, method_pin, directive)
18
+ end
19
+
20
+ private
21
+
22
+ # @param method_pin [Pin::Method]
23
+ # @return [YARD::CodeObjects::MethodObject]
24
+ def method_object_from_pin method_pin
25
+ namespace_object = nil
26
+ method_pin.each_closure do |namespace_pin|
27
+ next if namespace_pin.name.empty?
28
+
29
+ namespace_object = YARD::CodeObjects::NamespaceObject.new(
30
+ namespace_object,
31
+ namespace_pin.name.to_sym
32
+ )
33
+ end
34
+ # @sg-ignore Wrong argument type for YARD::CodeObjects::MethodObject.new: namespace
35
+ # expected YARD::CodeObjects::NamespaceObject, got nil.
36
+ # False positive because namespace_object is set in the loop above.
37
+ YARD::CodeObjects::MethodObject.new(namespace_object, method_pin.name)
38
+ end
39
+ end
40
+
41
+ # @return [YARD::Tags::MacroDirective]
42
+ attr_reader :directive
43
+ # @return [YARD::CodeObjects::MacroObject]
44
+ attr_reader :macro_object
45
+
46
+ # @param macro_object [YARD::CodeObjects::MacroObject]
47
+ # @param method_pin [Pin::Method]
48
+ # @param directive [YARD::Tags::Directive]
49
+ def initialize macro_object, method_pin, directive
50
+ @macro_object = macro_object
51
+ @method_pin = method_pin
52
+ @directive = directive
53
+ end
54
+
55
+ # @return [String]
56
+ def name
57
+ @directive.tag.name.to_s
58
+ end
59
+
60
+ # @return [String]
61
+ def text
62
+ @directive.tag.text.to_s
63
+ end
64
+
65
+ # @return [YARD::Tags::Tag]
66
+ def tag
67
+ @directive.tag
68
+ end
69
+
70
+ # @param chain [Source::Chain]
71
+ # @param pin [Pin::Closure]
72
+ # @param source_map [SourceMap]
73
+ # @return [Array<Pin::Base>]
74
+ def generate_pins_from chain, pin, source_map
75
+ call_location = Solargraph::Location.from_node(chain.node)
76
+ # @param generated_pins [Array<Pin::Base>]
77
+ generate_yardoc_from(chain, source_map).reduce([]) do |generated_pins, directive|
78
+ directive_processor = YardMap::Directives.for(directive)
79
+ next generated_pins unless directive_processor && call_location
80
+ generated_pins + directive_processor.process_directive(
81
+ source_map.source, source_map.pins, call_location.range.start, call_location.range.start, directive
82
+ )
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # @param chain [Solargraph::Source::Chain]
89
+ # @param [SourceMap] source_map
90
+ # @return [Array<YARD::Tags::Directive>]
91
+ def generate_yardoc_from chain, source_map
92
+ name = chain.links.last.word
93
+ # @sg-ignore chain.links.last is assumed to be a Chain::Call
94
+ values = chain.links.last.arguments.map(&:node).map { |arg| Solargraph::Parser::ParserGem::NodeMethods.simple_convert(arg).to_s }
95
+ # @sg-ignore chain.node is assumed to exist
96
+ code = source_map.source.code_for(chain.node)
97
+ expanded_comment = macro_object.expand([name, *values], code)
98
+ .gsub(/\n(?!@!|\s)/, "\n ")
99
+ directives = Solargraph::Source.parse_docstring(expanded_comment).directives.select do |directive|
100
+ PROCESSABLE_DIRECTIVES.include?(directive.tag.tag_name)
101
+ end
102
+ directives.each do |directive|
103
+ # @sg-ignore chain.node is assumed to exist
104
+ comments = source_map.source.comments_for(chain.node)
105
+ if comments&.length&.positive? && directive.tag.tag_name != 'parse'
106
+ directive.tag.text += "\n#{comments}"
107
+ end
108
+ end
109
+ directives
110
+ end
111
+ end
112
+ end
113
+ end
@@ -11,6 +11,7 @@ module Solargraph
11
11
  # @param spec [Gem::Specification, nil]
12
12
  def initialize code_objects, spec = nil
13
13
  @code_objects = code_objects
14
+ @macro_code_objects = code_objects.select { |co| co.is_a?(YARD::CodeObjects::MacroObject) }
14
15
  @spec = spec
15
16
  # @type [Array<Solargraph::Pin::Base>]
16
17
  @pins = []
@@ -24,7 +25,7 @@ module Solargraph
24
25
  end
25
26
  # Some yardocs contain documentation for dependencies that can be
26
27
  # ignored here. The YardMap will load dependencies separately.
27
- # @sg-ignore Need to add nil check here
28
+ # @sg-ignore does not consider `pin.location.nil? || ` condition
28
29
  @pins.keep_if { |pin| pin.location.nil? || File.file?(pin.location.filename) } if @spec
29
30
  @pins
30
31
  end
@@ -65,6 +66,7 @@ module Solargraph
65
66
  end
66
67
  when YARD::CodeObjects::MethodObject
67
68
  closure = @namespace_pins[code_object.namespace.to_s]
69
+ macros_for_method_object(code_object)
68
70
  # @sg-ignore flow sensitive typing ought to be able to handle 'when ClassName'
69
71
  if code_object.name == :initialize && code_object.scope == :instance
70
72
  # @todo Check the visibility of <Class>.new
@@ -79,6 +81,22 @@ module Solargraph
79
81
  end
80
82
  result
81
83
  end
84
+
85
+ # @return [Array<YARD::CodeObjects::MacroObject>]
86
+ def attached_macros
87
+ @attached_macros ||= @macro_code_objects.select(&:attached?)
88
+ end
89
+
90
+ # @return [Hash{YARD::CodeObjects::MethodObject => Array<YARD::CodeObjects::MacroObject>}]
91
+ def attached_macros_by_method_object
92
+ @attached_macros_by_method_object ||= attached_macros.group_by(&:method_object)
93
+ end
94
+
95
+ # @param method_object [YARD::CodeObjects::MethodObject]
96
+ # @return [Array<YARD::CodeObjects::MacroObject>]
97
+ def macros_for_method_object method_object
98
+ attached_macros_by_method_object[method_object]
99
+ end
82
100
  end
83
101
  end
84
102
  end
@@ -13,5 +13,7 @@ module Solargraph
13
13
  autoload :Cache, 'solargraph/yard_map/cache'
14
14
  autoload :Mapper, 'solargraph/yard_map/mapper'
15
15
  autoload :Helpers, 'solargraph/yard_map/helpers'
16
+ autoload :Macro, 'solargraph/yard_map/macro'
17
+ autoload :Directives, 'solargraph/yard_map/directives'
16
18
  end
17
19
  end
data/lib/solargraph.rb CHANGED
@@ -49,6 +49,7 @@ module Solargraph
49
49
  autoload :RbsMap, 'solargraph/rbs_map'
50
50
  autoload :GemPins, 'solargraph/gem_pins'
51
51
  autoload :PinCache, 'solargraph/pin_cache'
52
+ autoload :RbsTranslator, 'solargraph/rbs_translator'
52
53
 
53
54
  dir = File.dirname(__FILE__)
54
55
  VIEWS_PATH = File.join(dir, 'solargraph', 'views')
data/solargraph.gemspec CHANGED
@@ -47,6 +47,7 @@ Gem::Specification.new do |s|
47
47
  s.add_dependency 'rbs', '>= 3.10.0'
48
48
  s.add_dependency 'reverse_markdown', '~> 3.0'
49
49
  s.add_dependency 'rubocop', '~> 1.76'
50
+ s.add_dependency 'sord', '~> 7.0'
50
51
  s.add_dependency 'thor', '~> 1.0'
51
52
  s.add_dependency 'tilt', '~> 2.0'
52
53
  s.add_dependency 'yard', '~> 0.9', '>= 0.9.24'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.59.2
4
+ version: 0.60.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
@@ -253,6 +253,20 @@ dependencies:
253
253
  - - "~>"
254
254
  - !ruby/object:Gem::Version
255
255
  version: '1.76'
256
+ - !ruby/object:Gem::Dependency
257
+ name: sord
258
+ requirement: !ruby/object:Gem::Requirement
259
+ requirements:
260
+ - - "~>"
261
+ - !ruby/object:Gem::Version
262
+ version: '7.0'
263
+ type: :runtime
264
+ prerelease: false
265
+ version_requirements: !ruby/object:Gem::Requirement
266
+ requirements:
267
+ - - "~>"
268
+ - !ruby/object:Gem::Version
269
+ version: '7.0'
256
270
  - !ruby/object:Gem::Dependency
257
271
  name: thor
258
272
  requirement: !ruby/object:Gem::Requirement
@@ -742,6 +756,7 @@ files:
742
756
  - lib/solargraph/rbs_map/core_fills.rb
743
757
  - lib/solargraph/rbs_map/core_map.rb
744
758
  - lib/solargraph/rbs_map/stdlib_map.rb
759
+ - lib/solargraph/rbs_translator.rb
745
760
  - lib/solargraph/server_methods.rb
746
761
  - lib/solargraph/shell.rb
747
762
  - lib/solargraph/source.rb
@@ -790,7 +805,15 @@ files:
790
805
  - lib/solargraph/workspace/require_paths.rb
791
806
  - lib/solargraph/yard_map.rb
792
807
  - lib/solargraph/yard_map/cache.rb
808
+ - lib/solargraph/yard_map/directives.rb
809
+ - lib/solargraph/yard_map/directives/attribute_directive.rb
810
+ - lib/solargraph/yard_map/directives/domain_directive.rb
811
+ - lib/solargraph/yard_map/directives/method_directive.rb
812
+ - lib/solargraph/yard_map/directives/override_directive.rb
813
+ - lib/solargraph/yard_map/directives/parse_directive.rb
814
+ - lib/solargraph/yard_map/directives/visibility_directive.rb
793
815
  - lib/solargraph/yard_map/helpers.rb
816
+ - lib/solargraph/yard_map/macro.rb
794
817
  - lib/solargraph/yard_map/mapper.rb
795
818
  - lib/solargraph/yard_map/mapper/to_constant.rb
796
819
  - lib/solargraph/yard_map/mapper/to_method.rb