solargraph 0.27.1 → 0.28.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +8 -17
  3. data/lib/solargraph/api_map.rb +4 -3
  4. data/lib/solargraph/api_map/cache.rb +5 -5
  5. data/lib/solargraph/api_map/source_to_yard.rb +3 -2
  6. data/lib/solargraph/api_map/store.rb +25 -13
  7. data/lib/solargraph/complex_type.rb +3 -0
  8. data/lib/solargraph/diagnostics/update_errors.rb +21 -1
  9. data/lib/solargraph/language_server/host.rb +4 -3
  10. data/lib/solargraph/language_server/message/text_document/rename.rb +1 -1
  11. data/lib/solargraph/library.rb +12 -4
  12. data/lib/solargraph/pin.rb +5 -1
  13. data/lib/solargraph/pin/base.rb +0 -10
  14. data/lib/solargraph/pin/conversions.rb +1 -1
  15. data/lib/solargraph/pin/documenting.rb +0 -9
  16. data/lib/solargraph/pin/duck_method.rb +1 -1
  17. data/lib/solargraph/pin/keyword.rb +0 -5
  18. data/lib/solargraph/pin/namespace.rb +3 -16
  19. data/lib/solargraph/pin/reference.rb +13 -26
  20. data/lib/solargraph/pin/reference/extend.rb +11 -0
  21. data/lib/solargraph/pin/reference/include.rb +11 -0
  22. data/lib/solargraph/pin/reference/require.rb +15 -0
  23. data/lib/solargraph/pin/reference/superclass.rb +11 -0
  24. data/lib/solargraph/pin/symbol.rb +0 -4
  25. data/lib/solargraph/pin/yard_pin/namespace.rb +14 -14
  26. data/lib/solargraph/pin/yard_pin/yard_mixin.rb +0 -4
  27. data/lib/solargraph/position.rb +7 -0
  28. data/lib/solargraph/source.rb +7 -28
  29. data/lib/solargraph/source/chain.rb +0 -1
  30. data/lib/solargraph/source/chain/call.rb +1 -1
  31. data/lib/solargraph/source/chain/constant.rb +13 -4
  32. data/lib/solargraph/source/change.rb +7 -1
  33. data/lib/solargraph/source/cursor.rb +1 -5
  34. data/lib/solargraph/source/node_chainer.rb +5 -6
  35. data/lib/solargraph/source/source_chainer.rb +14 -12
  36. data/lib/solargraph/source_map.rb +5 -6
  37. data/lib/solargraph/source_map/clip.rb +16 -29
  38. data/lib/solargraph/source_map/mapper.rb +26 -21
  39. data/lib/solargraph/version.rb +1 -1
  40. data/lib/solargraph/workspace.rb +1 -2
  41. data/lib/solargraph/workspace/config.rb +10 -5
  42. data/lib/solargraph/yard_map.rb +3 -1
  43. metadata +6 -3
  44. data/lib/solargraph/source/chain/definition.rb +0 -20
@@ -0,0 +1,11 @@
1
+ module Solargraph
2
+ module Pin
3
+ class Reference
4
+ class Extend < Reference
5
+ def kind
6
+ Pin::EXTEND_REFERENCE
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Solargraph
2
+ module Pin
3
+ class Reference
4
+ class Include < Reference
5
+ def kind
6
+ Pin::INCLUDE_REFERENCE
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Solargraph
2
+ module Pin
3
+ class Reference
4
+ class Require < Reference
5
+ def initialize location, name
6
+ super(location, '', name)
7
+ end
8
+
9
+ def kind
10
+ Pin::REQUIRE_REFERENCE
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Solargraph
2
+ module Pin
3
+ class Reference
4
+ class Superclass < Reference
5
+ def kind
6
+ Pin::SUPERCLASS_REFERENCE
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -20,10 +20,6 @@ module Solargraph
20
20
  ''
21
21
  end
22
22
 
23
- def identifier
24
- name
25
- end
26
-
27
23
  def completion_item_kind
28
24
  Solargraph::LanguageServer::CompletionItemKinds::KEYWORD
29
25
  end
@@ -9,20 +9,20 @@ module Solargraph
9
9
  # @todo This method of superclass detection is a bit of a hack. If
10
10
  # the superclass is a Proxy, it is assumed to be undefined in its
11
11
  # yardoc and converted to a fully qualified namespace.
12
- if code_object.is_a?(YARD::CodeObjects::ClassObject) && code_object.superclass
13
- if code_object.superclass.is_a?(YARD::CodeObjects::Proxy)
14
- superclass = "::#{code_object.superclass}"
15
- else
16
- superclass = code_object.superclass.to_s
17
- end
18
- end
19
- super(location, code_object.namespace.to_s, code_object.name.to_s, comments_from(code_object), namespace_type(code_object), code_object.visibility, superclass)
20
- code_object.class_mixins.each do |m|
21
- extend_references.push Pin::Reference.new(location, path, m.path)
22
- end
23
- code_object.instance_mixins.each do |m|
24
- include_references.push Pin::Reference.new(location, path, m.path)
25
- end
12
+ # if code_object.is_a?(YARD::CodeObjects::ClassObject) && code_object.superclass
13
+ # if code_object.superclass.is_a?(YARD::CodeObjects::Proxy)
14
+ # superclass = "::#{code_object.superclass}"
15
+ # else
16
+ # superclass = code_object.superclass.to_s
17
+ # end
18
+ # end
19
+ super(location, code_object.namespace.to_s, code_object.name.to_s, comments_from(code_object), namespace_type(code_object), code_object.visibility)
20
+ # code_object.class_mixins.each do |m|
21
+ # extend_references.push Pin::Reference.new(location, path, m.path)
22
+ # end
23
+ # code_object.instance_mixins.each do |m|
24
+ # include_references.push Pin::Reference.new(location, path, m.path)
25
+ # end
26
26
  end
27
27
 
28
28
  private
@@ -2,10 +2,6 @@ module Solargraph
2
2
  module Pin
3
3
  module YardPin
4
4
  module YardMixin
5
- def yard_pin?
6
- true
7
- end
8
-
9
5
  private
10
6
 
11
7
  def comments_from code_object
@@ -89,6 +89,13 @@ module Solargraph
89
89
  Position.new(line, character)
90
90
  end
91
91
 
92
+ # A helper method for generating positions from arrays of integers. The
93
+ # original parameter is returned if it is already a position.
94
+ #
95
+ # @raise [ArgumentError] if the object cannot be converted to a position.
96
+ #
97
+ # @param object [Position, Array(Integer, Integer)]
98
+ # @return [Position]
92
99
  def self.normalize object
93
100
  return object if object.is_a?(Position)
94
101
  return Position.new(object[0], object[1]) if object.is_a?(Array)
@@ -97,24 +97,19 @@ module Solargraph
97
97
  def synchronize updater
98
98
  raise 'Invalid synchronization' unless updater.filename == filename
99
99
  real_code = updater.write(@code)
100
- incr_code = updater.write(@code, true)
101
100
  if real_code == @code
102
101
  @version = updater.version
103
102
  return self
104
103
  end
105
- synced = Source.new(incr_code, filename)
104
+ synced = Source.new(real_code, filename)
106
105
  if synced.parsed?
107
- synced.code = real_code
108
- if synced.repaired?
109
- synced.error_ranges.concat combine_errors(error_ranges + updater.changes.map(&:range))
110
- end
111
- else
112
- new_repair = updater.repair(@repaired)
113
- synced = Source.new(new_repair, filename)
114
- synced.error_ranges.concat combine_errors(error_ranges + updater.changes.map(&:range))
115
- synced.parsed = false
116
- synced.code = real_code
106
+ synced.version = updater.version
107
+ return synced
117
108
  end
109
+ incr_code = updater.repair(@repaired)
110
+ synced = Source.new(incr_code, filename)
111
+ synced.error_ranges.concat (error_ranges + updater.changes.map(&:range))
112
+ synced.code = real_code
118
113
  synced.version = updater.version
119
114
  synced
120
115
  end
@@ -226,22 +221,6 @@ module Solargraph
226
221
  result
227
222
  end
228
223
 
229
- # @param ranges [Array<Range>]
230
- # @return [Array<Range>]
231
- def combine_errors ranges
232
- result = []
233
- lines = []
234
- ranges.sort{|a, b| a.start.line <=> b.start.line}.each do |rng|
235
- next if rng.nil? || lines.include?(rng.start.line)
236
- lines.push rng.start.line
237
- next if comment_at?(rng.start) || rng.start.line >= code.lines.length
238
- fcol = code.lines[rng.start.line].index(/[^\s]/) || 0
239
- ecol = code.lines[rng.start.line].length
240
- result.push Range.from_to(rng.start.line, fcol, rng.start.line, ecol)
241
- end
242
- result
243
- end
244
-
245
224
  protected
246
225
 
247
226
  # @return [Integer]
@@ -12,7 +12,6 @@ module Solargraph
12
12
  autoload :InstanceVariable, 'solargraph/source/chain/instance_variable'
13
13
  autoload :GlobalVariable, 'solargraph/source/chain/global_variable'
14
14
  autoload :Literal, 'solargraph/source/chain/literal'
15
- autoload :Definition, 'solargraph/source/chain/definition'
16
15
  autoload :Head, 'solargraph/source/chain/head'
17
16
 
18
17
  UNDEFINED_CALL = Chain::Call.new('<undefined>')
@@ -61,7 +61,7 @@ module Solargraph
61
61
  end
62
62
 
63
63
  def external_constructor? pin, context
64
- pin.path == 'Class#new' || (pin.name == 'new' && pin.scope == :class && pin.context != context)
64
+ pin.path == 'Class#new' || (pin.name == 'new' && pin.scope == :class && pin.return_type != context)
65
65
  end
66
66
 
67
67
  # @param pin [Pin::Method]
@@ -8,10 +8,19 @@ module Solargraph
8
8
 
9
9
  def resolve api_map, name_pin, locals
10
10
  return [Pin::ROOT_PIN] if word.empty?
11
- parts = word.split('::')
12
- last = parts.pop
13
- first = parts.join('::').to_s
14
- api_map.get_constants(first, name_pin.context.namespace).select{|p| p.name == last}
11
+ if word.start_with?('::')
12
+ context = ''
13
+ bottom = word[2..-1]
14
+ else
15
+ context = name_pin.context.namespace
16
+ bottom = word
17
+ end
18
+ if bottom.include?('::')
19
+ ns = bottom.split('::')[0..-2].join('::')
20
+ else
21
+ ns = ''
22
+ end
23
+ api_map.get_constants(ns, context).select{|p| p.path.end_with?(bottom)}
15
24
  end
16
25
  end
17
26
  end
@@ -55,7 +55,13 @@ module Solargraph
55
55
  if range.nil?
56
56
  fixed
57
57
  else
58
- commit text, fixed
58
+ result = commit text, fixed
59
+ off = Position.to_offset(text, range.start)
60
+ match = result[0..off].match(/[\.:]+\z/)
61
+ if match
62
+ result = result[0..off].sub(/#{match[0]}\z/, ' ' * match[0].length) + result[off..-1]
63
+ end
64
+ result
59
65
  end
60
66
  end
61
67
 
@@ -14,11 +14,7 @@ module Solargraph
14
14
  # @param position [Position, Array(Integer, Integer)]
15
15
  def initialize source, position
16
16
  @source = source
17
- @position = if position.is_a?(Array)
18
- Position.new(position[0], position[1])
19
- else
20
- position
21
- end
17
+ @position = Position.normalize(position)
22
18
  end
23
19
 
24
20
  # @return [String]
@@ -73,10 +73,11 @@ module Solargraph
73
73
  elsif n.type == :const
74
74
  # result.push Chain::Constant.new(unpack_name(n))
75
75
  const = unpack_name(n)
76
- parts = const.split('::')
77
- last = parts.pop
78
- result.push Chain::Constant.new(parts.join('::')) unless parts.empty?
79
- result.push Chain::Constant.new(last)
76
+ # parts = const.split('::')
77
+ # last = parts.pop
78
+ # result.push Chain::Constant.new(parts.join('::')) unless parts.empty?
79
+ # result.push Chain::Constant.new(last)
80
+ result.push Chain::Constant.new(const)
80
81
  elsif [:lvar, :lvasgn].include?(n.type)
81
82
  result.push Chain::Call.new(n.children[0].to_s)
82
83
  elsif [:ivar, :ivasgn].include?(n.type)
@@ -86,8 +87,6 @@ module Solargraph
86
87
  elsif [:gvar, :gvasgn].include?(n.type)
87
88
  result.push Chain::GlobalVariable.new(n.children[0].to_s)
88
89
  elsif [:class, :module, :def, :defs].include?(n.type)
89
- # location = Solargraph::Location.new(@filename, Range.from_to(n.loc.expression.line, n.loc.expression.column, n.loc.expression.last_line, n.loc.expression.last_column))
90
- # result.push Chain::Definition.new(location)
91
90
  # @todo Undefined or what?
92
91
  result.push Chain::UNDEFINED_CALL
93
92
  else
@@ -35,13 +35,15 @@ module Solargraph
35
35
  # @return [Source::Chain]
36
36
  def chain
37
37
  return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
38
+ # return Chain.new([Chain::UNDEFINED_CALL]) unless infer_literal_node_type(source.node_at(position.line, position.column)).nil?
38
39
  begin
39
- if source.repaired? && source.parsed?
40
- node = source.node_at(fixed_position.line, fixed_position.column)
41
- elsif source.parsed?
40
+ return Chain.new([]) if phrase.end_with?('..')
41
+ if !source.repaired? && source.parsed?
42
42
  node = source.node_at(position.line, position.column)
43
43
  else
44
- node = Source.parse(fixed_phrase)
44
+ node = nil
45
+ node = source.node_at(fixed_position.line, fixed_position.column) unless source.error_ranges.any?{|r| r.include?(fixed_position)}
46
+ node = Source.parse(fixed_phrase) if node.nil?
45
47
  end
46
48
  rescue Parser::SyntaxError
47
49
  return Chain.new([Chain::UNDEFINED_CALL])
@@ -54,15 +56,13 @@ module Solargraph
54
56
  elsif end_of_phrase.strip == '::'
55
57
  chain.links.push Chain::UNDEFINED_CONSTANT
56
58
  end
57
- elsif end_of_phrase.strip == '::'
58
- chain.links.pop
59
- chain.links.push Chain::UNDEFINED_CONSTANT
60
59
  end
61
60
  chain
62
61
  end
63
62
 
64
63
  private
65
64
 
65
+ # @return [Position]
66
66
  attr_reader :position
67
67
 
68
68
  # The zero-based line number of the fragment's location.
@@ -78,18 +78,22 @@ module Solargraph
78
78
  # @return [Solargraph::Source]
79
79
  attr_reader :source
80
80
 
81
+ # @return [String]
81
82
  def phrase
82
83
  @phrase ||= source.code[signature_data[0]..offset-1]
83
84
  end
84
85
 
86
+ # @return [String]
85
87
  def fixed_phrase
86
88
  @fixed_phrase ||= phrase[0..-(end_of_phrase.length+1)]
87
89
  end
88
90
 
91
+ # @return [Position]
89
92
  def fixed_position
90
93
  @fixed_position ||= Position.from_offset(source.code, offset - end_of_phrase.length)
91
94
  end
92
95
 
96
+ # @return [String]
93
97
  def end_of_phrase
94
98
  @end_of_phrase ||= begin
95
99
  match = phrase.match(/[\s]*(\.{1}|::)[\s]*$/)
@@ -108,11 +112,6 @@ module Solargraph
108
112
  @column
109
113
  end
110
114
 
111
- # @return [Position]
112
- def position
113
- @position ||= Position.new(line, column)
114
- end
115
-
116
115
  # True if the current offset is inside a string.
117
116
  #
118
117
  # @return [Boolean]
@@ -126,6 +125,9 @@ module Solargraph
126
125
  @offset ||= get_offset(line, column)
127
126
  end
128
127
 
128
+ # @param line [Integer]
129
+ # @param column [Integer]
130
+ # @return [Integer]
129
131
  def get_offset line, column
130
132
  Position.line_char_to_offset(@source.code, line, column)
131
133
  end
@@ -15,16 +15,11 @@ module Solargraph
15
15
  # @return [Array<Pin::Base>]
16
16
  attr_reader :locals
17
17
 
18
- # @return [Array<Pin::Reference>]
19
- attr_reader :requires
20
-
21
- def initialize source, pins, locals, requires, symbols
18
+ def initialize source, pins, locals
22
19
  # HACK: Keep the library from changing this
23
20
  @source = source.dup
24
21
  @pins = pins
25
22
  @locals = locals
26
- @requires = requires
27
- @pins.concat symbols
28
23
  end
29
24
 
30
25
  def filename
@@ -35,6 +30,10 @@ module Solargraph
35
30
  source.code
36
31
  end
37
32
 
33
+ def requires
34
+ @requires ||= pins.select{|p| p.kind == Pin::REQUIRE_REFERENCE}
35
+ end
36
+
38
37
  # @param position [Position]
39
38
  # @return [Boolean]
40
39
  def string_at? position
@@ -1,11 +1,12 @@
1
1
  module Solargraph
2
2
  class SourceMap
3
+ # A static analysis tool for obtaining definitions, completions,
4
+ # signatures, and type inferences from a cursor.
5
+ #
3
6
  class Clip
4
7
  # @param api_map [ApiMap]
5
8
  # @param cursor [Source::Cursor]
6
9
  def initialize api_map, cursor
7
- # @todo Just some temporary stuff while I make sure this works
8
- raise "Not a cursor: #{cursor.class}" unless cursor.is_a?(Source::Cursor)
9
10
  @api_map = api_map
10
11
  @cursor = cursor
11
12
  end
@@ -36,7 +37,7 @@ module Solargraph
36
37
  elsif cursor.word.start_with?('$')
37
38
  return package_completions(api_map.get_global_variable_pins)
38
39
  end
39
- result.concat prefer_non_nil_variables(locals)
40
+ result.concat locals
40
41
  result.concat api_map.get_constants('', context_pin.context.namespace)
41
42
  result.concat api_map.get_methods(context_pin.context.namespace, scope: context_pin.context.scope, visibility: [:public, :private, :protected])
42
43
  result.concat api_map.get_methods('Kernel')
@@ -53,17 +54,11 @@ module Solargraph
53
54
  clip.define.select{|pin| pin.kind == Pin::METHOD}
54
55
  end
55
56
 
57
+ # @return [ComplexType]
56
58
  def infer
57
59
  cursor.chain.infer(api_map, context_pin, locals)
58
60
  end
59
61
 
60
- # The context at the current position.
61
- #
62
- # @return [Pin::Base]
63
- def context_pin
64
- @context ||= source_map.locate_named_path_pin(cursor.node_position.line, cursor.node_position.character)
65
- end
66
-
67
62
  # Get an array of all the locals that are visible from the cursors's
68
63
  # position. Locals can be local variables, method parameters, or block
69
64
  # parameters. The array starts with the nearest local pin.
@@ -93,31 +88,23 @@ module Solargraph
93
88
  @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character)
94
89
  end
95
90
 
96
- # @param cursor [cursor]
91
+ # The context at the current position.
92
+ #
93
+ # @return [Pin::Base]
94
+ def context_pin
95
+ @context_pin ||= source_map.locate_named_path_pin(cursor.node_position.line, cursor.node_position.character)
96
+ end
97
+
97
98
  # @param result [Array<Pin::Base>]
98
99
  # @return [Completion]
99
100
  def package_completions result
100
101
  frag_start = cursor.start_of_word.to_s.downcase
101
- filtered = result.uniq(&:name).select{|s| s.name.downcase.start_with?(frag_start) and (s.kind != Pin::METHOD or s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))}
102
+ filtered = result.uniq(&:name).select { |s|
103
+ s.name.downcase.start_with?(frag_start) &&
104
+ (s.kind != Pin::METHOD || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
105
+ }
102
106
  Completion.new(filtered, cursor.range)
103
107
  end
104
-
105
- # Sort an array of pins to put nil or undefined variables last.
106
- #
107
- # @param pins [Array<Pin::Base>]
108
- # @return [Array<Pin::Base>]
109
- def prefer_non_nil_variables pins
110
- result = []
111
- nil_pins = []
112
- pins.each do |pin|
113
- if pin.variable? and pin.nil_assignment?
114
- nil_pins.push pin
115
- else
116
- result.push pin
117
- end
118
- end
119
- result + nil_pins
120
- end
121
108
  end
122
109
  end
123
110
  end