solargraph 0.26.1 → 0.27.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.
- checksums.yaml +4 -4
- data/lib/solargraph.rb +5 -2
- data/lib/solargraph/api_map.rb +236 -234
- data/lib/solargraph/api_map/store.rb +18 -53
- data/lib/solargraph/bundle.rb +22 -0
- data/lib/solargraph/complex_type.rb +9 -5
- data/lib/solargraph/complex_type/type_methods.rb +113 -0
- data/lib/solargraph/complex_type/unique_type.rb +35 -0
- data/lib/solargraph/core_fills.rb +1 -0
- data/lib/solargraph/diagnostics.rb +6 -4
- data/lib/solargraph/diagnostics/base.rb +3 -0
- data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
- data/lib/solargraph/diagnostics/rubocop.rb +21 -6
- data/lib/solargraph/diagnostics/type_not_defined.rb +4 -3
- data/lib/solargraph/diagnostics/update_errors.rb +18 -0
- data/lib/solargraph/language_server/host.rb +90 -222
- data/lib/solargraph/language_server/host/cataloger.rb +68 -0
- data/lib/solargraph/language_server/host/diagnoser.rb +85 -0
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +35 -24
- data/lib/solargraph/language_server/message/text_document/completion.rb +6 -8
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +0 -1
- data/lib/solargraph/language_server/transport/socket.rb +4 -6
- data/lib/solargraph/language_server/transport/stdio.rb +4 -6
- data/lib/solargraph/library.rb +152 -99
- data/lib/solargraph/live_map.rb +1 -1
- data/lib/solargraph/location.rb +28 -0
- data/lib/solargraph/pin.rb +2 -0
- data/lib/solargraph/pin/attribute.rb +26 -12
- data/lib/solargraph/pin/base.rb +15 -35
- data/lib/solargraph/pin/base_variable.rb +7 -15
- data/lib/solargraph/pin/block.rb +5 -9
- data/lib/solargraph/pin/block_parameter.rb +9 -7
- data/lib/solargraph/pin/conversions.rb +5 -5
- data/lib/solargraph/pin/duck_method.rb +1 -1
- data/lib/solargraph/pin/instance_variable.rb +0 -4
- data/lib/solargraph/pin/keyword.rb +4 -0
- data/lib/solargraph/pin/localized.rb +5 -3
- data/lib/solargraph/pin/method.rb +11 -0
- data/lib/solargraph/pin/namespace.rb +7 -3
- data/lib/solargraph/pin/proxy_type.rb +3 -7
- data/lib/solargraph/pin/reference.rb +2 -2
- data/lib/solargraph/pin/symbol.rb +1 -1
- data/lib/solargraph/pin/yard_pin/method.rb +2 -2
- data/lib/solargraph/pin/yard_pin/namespace.rb +16 -7
- data/lib/solargraph/position.rb +103 -0
- data/lib/solargraph/range.rb +70 -0
- data/lib/solargraph/source.rb +159 -328
- data/lib/solargraph/source/chain.rb +38 -55
- data/lib/solargraph/source/chain/call.rb +47 -29
- data/lib/solargraph/source/chain/class_variable.rb +2 -2
- data/lib/solargraph/source/chain/constant.rb +3 -3
- data/lib/solargraph/source/chain/definition.rb +7 -3
- data/lib/solargraph/source/chain/global_variable.rb +1 -1
- data/lib/solargraph/source/chain/head.rb +22 -9
- data/lib/solargraph/source/chain/instance_variable.rb +2 -2
- data/lib/solargraph/source/chain/link.rb +4 -4
- data/lib/solargraph/source/chain/literal.rb +1 -1
- data/lib/solargraph/source/chain/variable.rb +2 -2
- data/lib/solargraph/source/change.rb +0 -6
- data/lib/solargraph/source/cursor.rb +161 -0
- data/lib/solargraph/source/encoding_fixes.rb +1 -1
- data/lib/solargraph/source/node_chainer.rb +28 -21
- data/lib/solargraph/source/node_methods.rb +1 -1
- data/lib/solargraph/source/source_chainer.rb +217 -0
- data/lib/solargraph/source_map.rb +138 -0
- data/lib/solargraph/source_map/clip.rb +123 -0
- data/lib/solargraph/{source → source_map}/completion.rb +3 -3
- data/lib/solargraph/{source → source_map}/mapper.rb +143 -41
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace.rb +13 -20
- data/lib/solargraph/yard_map.rb +77 -48
- metadata +17 -11
- data/lib/solargraph/basic_type.rb +0 -33
- data/lib/solargraph/basic_type_methods.rb +0 -111
- data/lib/solargraph/source/call_chainer.rb +0 -273
- data/lib/solargraph/source/fragment.rb +0 -342
- data/lib/solargraph/source/location.rb +0 -23
- data/lib/solargraph/source/position.rb +0 -95
- data/lib/solargraph/source/range.rb +0 -64
@@ -15,65 +15,55 @@ module Solargraph
|
|
15
15
|
autoload :Definition, 'solargraph/source/chain/definition'
|
16
16
|
autoload :Head, 'solargraph/source/chain/head'
|
17
17
|
|
18
|
-
UNDEFINED_CALL =
|
19
|
-
UNDEFINED_CONSTANT =
|
18
|
+
UNDEFINED_CALL = Chain::Call.new('<undefined>')
|
19
|
+
UNDEFINED_CONSTANT = Chain::Constant.new('<undefined>')
|
20
20
|
|
21
21
|
# @return [Array<Source::Chain::Link>]
|
22
22
|
attr_reader :links
|
23
23
|
|
24
|
-
# @param filename [String]
|
25
24
|
# @param links [Array<Chain::Link>]
|
26
|
-
def initialize
|
27
|
-
@filename = filename
|
25
|
+
def initialize links
|
28
26
|
@links = links
|
29
27
|
@links.push UNDEFINED_CALL if @links.empty?
|
30
28
|
end
|
31
29
|
|
32
|
-
# @return [
|
30
|
+
# @return [Chain]
|
33
31
|
def base
|
34
|
-
|
35
|
-
@base ||= links[0..-2]
|
36
|
-
end
|
37
|
-
|
38
|
-
# @return [Source::Chain::Link]
|
39
|
-
def tail
|
40
|
-
@tail ||= links.last
|
41
|
-
end
|
42
|
-
|
43
|
-
def literal?
|
44
|
-
tail.is_a?(Literal)
|
32
|
+
@base ||= Chain.new(links[0..-2])
|
45
33
|
end
|
46
34
|
|
47
35
|
# @param api_map [ApiMap]
|
48
|
-
# @param
|
36
|
+
# @param name_pin [Pin::Base]
|
49
37
|
# @param locals [Array<Pin::Base>]
|
50
38
|
# @return [Array<Pin::Base>]
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
def define api_map, name_pin, locals
|
40
|
+
return [] if undefined?
|
41
|
+
type = ComplexType::UNDEFINED
|
42
|
+
head = true
|
43
|
+
working_pin = name_pin
|
44
|
+
links[0..-2].each do |link|
|
45
|
+
pins = link.resolve(api_map, working_pin, head ? locals : [])
|
46
|
+
head = false
|
47
|
+
return [] if pins.empty?
|
48
|
+
type = ComplexType::UNDEFINED
|
49
|
+
pins.each do |pin|
|
50
|
+
type = pin.infer(api_map)
|
51
|
+
break unless type.undefined?
|
52
|
+
end
|
53
|
+
return [] if type.undefined?
|
54
|
+
working_pin = Pin::ProxyType.anonymous(type)
|
55
|
+
end
|
56
|
+
links.last.resolve(api_map, working_pin, head ? locals: [])
|
57
57
|
end
|
58
58
|
|
59
59
|
# @param api_map [ApiMap]
|
60
|
-
# @param
|
60
|
+
# @param name_pin [Pin::Base]
|
61
61
|
# @param locals [Array<Pin::Base>]
|
62
62
|
# @return [ComplexType]
|
63
|
-
def
|
64
|
-
|
65
|
-
inner_infer_type_with(links, api_map, context, locals)
|
66
|
-
end
|
67
|
-
|
68
|
-
def infer_base_type_with api_map, context, locals
|
69
|
-
inner_infer_type_with(links[0..-2], api_map, context, locals)
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
def inner_infer_type_with array, api_map, context, locals
|
63
|
+
def infer api_map, name_pin, locals
|
64
|
+
return ComplexType::UNDEFINED if undefined?
|
75
65
|
type = ComplexType::UNDEFINED
|
76
|
-
pins =
|
66
|
+
pins = define(api_map, name_pin, locals)
|
77
67
|
pins.each do |pin|
|
78
68
|
type = pin.infer(api_map)
|
79
69
|
break unless type.undefined?
|
@@ -81,23 +71,16 @@ module Solargraph
|
|
81
71
|
type
|
82
72
|
end
|
83
73
|
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
type = pin.infer(api_map)
|
95
|
-
break unless type.undefined?
|
96
|
-
end
|
97
|
-
return [] if type.undefined?
|
98
|
-
context = Pin::ProxyType.anonymous(type)
|
99
|
-
end
|
100
|
-
array.last.resolve_pins(api_map, context, head ? locals: [])
|
74
|
+
def literal?
|
75
|
+
links.last.is_a?(Chain::Literal)
|
76
|
+
end
|
77
|
+
|
78
|
+
def undefined?
|
79
|
+
links.any?(&:undefined?)
|
80
|
+
end
|
81
|
+
|
82
|
+
def constant?
|
83
|
+
links.last.is_a?(Chain::Constant)
|
101
84
|
end
|
102
85
|
end
|
103
86
|
end
|
@@ -13,59 +13,77 @@ module Solargraph
|
|
13
13
|
@arguments = arguments
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def resolve api_map, name_pin, locals
|
17
17
|
found = locals.select{|p| p.name == word}
|
18
|
-
return inferred_pins(found, api_map, context) unless found.empty?
|
19
|
-
pins = api_map.get_method_stack(
|
18
|
+
return inferred_pins(found, api_map, name_pin.context, locals) unless found.empty?
|
19
|
+
pins = api_map.get_method_stack(name_pin.context.namespace, word, scope: name_pin.context.scope)
|
20
20
|
return [] if pins.empty?
|
21
|
-
pins
|
22
|
-
inferred_pins(pins, api_map, context)
|
21
|
+
pins.unshift virtual_new_pin(pins.first, name_pin.context) if external_constructor?(pins.first, name_pin.context)
|
22
|
+
inferred_pins(pins, api_map, name_pin.context, locals)
|
23
23
|
end
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
# @param pin [Pin::Base]
|
28
|
-
# @return [String]
|
29
|
-
def namespace_from_context pin
|
30
|
-
return pin.namespace if pin.kind == Pin::ATTRIBUTE or pin.kind == Pin::METHOD
|
31
|
-
pin.return_complex_type.namespace
|
32
|
-
end
|
33
|
-
|
34
27
|
# Create a `new` pin to facilitate type inference. This is necessary for
|
35
28
|
# classes from YARD and classes in the namespace that do not have an
|
36
29
|
# `initialize` method.
|
37
30
|
#
|
38
31
|
# @param new_pin [Solargraph::Pin::Base]
|
39
|
-
# @param
|
32
|
+
# @param context [Solargraph::ComplexType]
|
40
33
|
# @return [Pin::Method]
|
41
|
-
def virtual_new_pin new_pin,
|
42
|
-
pin = Pin::Method.new(new_pin.location,
|
34
|
+
def virtual_new_pin new_pin, context
|
35
|
+
# pin = Pin::Method.new(new_pin.location, context.namespace, new_pin.name, '', :class, new_pin.visibility, new_pin.parameters)
|
43
36
|
# @todo Smelly instance variable access.
|
44
|
-
pin.instance_variable_set(:@return_complex_type, ComplexType.parse(
|
45
|
-
pin
|
37
|
+
# pin.instance_variable_set(:@return_complex_type, ComplexType.parse(context.namespace))
|
38
|
+
# pin
|
39
|
+
Pin::ProxyType.anonymous(ComplexType.parse(context.namespace))
|
46
40
|
end
|
47
41
|
|
48
|
-
def
|
49
|
-
|
50
|
-
# return api_map.get_path_suggestions(context.namespace)
|
51
|
-
context
|
52
|
-
end
|
53
|
-
|
54
|
-
def inferred_pins pins, api_map, context_pin
|
55
|
-
result = pins.uniq(&:location).map do |p|
|
42
|
+
def inferred_pins pins, api_map, context, locals
|
43
|
+
result = pins.map do |p|
|
56
44
|
if CoreFills::METHODS_RETURNING_SELF.include?(p.path)
|
57
|
-
next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{
|
45
|
+
next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.tag}]", p.scope, p.visibility, p.parameters)
|
58
46
|
end
|
59
|
-
if CoreFills::METHODS_RETURNING_SUBTYPES.include?(p.path)
|
60
|
-
next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{
|
47
|
+
if CoreFills::METHODS_RETURNING_SUBTYPES.include?(p.path) && !context.subtypes.empty?
|
48
|
+
next Solargraph::Pin::Method.new(p.location, p.namespace, p.name, "@return [#{context.subtypes.first.tag}]", p.scope, p.visibility, p.parameters)
|
61
49
|
end
|
62
|
-
|
50
|
+
# @todo Temporarily disabled macros
|
51
|
+
# if p.kind == Pin::METHOD && !p.macros.empty?
|
52
|
+
# result = process_macro(p, api_map, context, locals)
|
53
|
+
# next result unless result.return_type.undefined?
|
54
|
+
# end
|
55
|
+
next p if p.kind == Pin::METHOD || p.kind == Pin::ATTRIBUTE || p.kind == Pin::NAMESPACE
|
63
56
|
type = p.infer(api_map)
|
64
57
|
next p if p.return_complex_type == type
|
65
58
|
Pin::ProxyType.new(p.location, nil, p.name, type)
|
66
59
|
end
|
67
60
|
result
|
68
61
|
end
|
62
|
+
|
63
|
+
def external_constructor? pin, context
|
64
|
+
pin.path == 'Class#new' || (pin.name == 'new' && pin.scope == :class && pin.context != context)
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param pin [Pin::Method]
|
68
|
+
# @param api_map [ApiMap]
|
69
|
+
# @param context [ComplexType]
|
70
|
+
def process_macro pin, api_map, context, locals
|
71
|
+
pin.macros.each do |macro|
|
72
|
+
vals = arguments.map{ |c| Pin::ProxyType.anonymous(c.infer(api_map, pin, locals)) }
|
73
|
+
txt = macro.tag.text.clone
|
74
|
+
i = 1
|
75
|
+
vals.each do |v|
|
76
|
+
txt.gsub!(/\$#{i}/, v.context.namespace)
|
77
|
+
i += 1
|
78
|
+
end
|
79
|
+
docstring = YARD::Docstring.parser.parse(txt).to_docstring
|
80
|
+
tag = docstring.tag(:return)
|
81
|
+
unless tag.nil? || tag.types.nil?
|
82
|
+
return Pin::ProxyType.anonymous(ComplexType.parse(*tag.types))
|
83
|
+
end
|
84
|
+
end
|
85
|
+
return Pin::ProxyType.new(nil, nil, nil, ComplexType::UNDEFINED)
|
86
|
+
end
|
69
87
|
end
|
70
88
|
end
|
71
89
|
end
|
@@ -2,8 +2,8 @@ module Solargraph
|
|
2
2
|
class Source
|
3
3
|
class Chain
|
4
4
|
class ClassVariable < Link
|
5
|
-
def
|
6
|
-
api_map.get_class_variable_pins(context.namespace).select{|p| p.name == word}
|
5
|
+
def resolve api_map, name_pin, locals
|
6
|
+
api_map.get_class_variable_pins(name_pin.context.namespace).select{|p| p.name == word}
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -6,12 +6,12 @@ module Solargraph
|
|
6
6
|
@word = word
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
def resolve api_map, name_pin, locals
|
10
|
+
return [Pin::ROOT_PIN] if word.empty?
|
11
11
|
parts = word.split('::')
|
12
12
|
last = parts.pop
|
13
13
|
first = parts.join('::').to_s
|
14
|
-
api_map.get_constants(first, context.
|
14
|
+
api_map.get_constants(first, name_pin.context.namespace).select{|p| p.name == last}
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -2,13 +2,17 @@ module Solargraph
|
|
2
2
|
class Source
|
3
3
|
class Chain
|
4
4
|
class Definition < Link
|
5
|
-
# @param location [Solargraph::
|
5
|
+
# @param location [Solargraph::Location]
|
6
6
|
def initialize location
|
7
7
|
@location = location
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
10
|
+
# @param api_map [ApiMap]
|
11
|
+
def resolve api_map, name_pin, locals
|
12
|
+
result = api_map.locate_pin(@location)
|
13
|
+
# result = api_map.source_map(@location.filename).locate_named_path_pin(@location.range.start.line, @location.range.start.column)
|
14
|
+
return [] if result.nil?
|
15
|
+
[result]
|
12
16
|
end
|
13
17
|
end
|
14
18
|
end
|
@@ -4,15 +4,28 @@ module Solargraph
|
|
4
4
|
# Chain::Head is a link for ambiguous words, e.g.; `String` can refer to
|
5
5
|
# either a class (`String`) or a function (`Kernel#String`).
|
6
6
|
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
return
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
# @note Chain::Head is only intended to handle `self` and `super`.
|
8
|
+
class Head < Link
|
9
|
+
def resolve api_map, name_pin, locals
|
10
|
+
return [self_pin(name_pin.context)] if word == 'self'
|
11
|
+
return super_pins(api_map, name_pin) if word == 'super'
|
12
|
+
[]
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# @param context [ComplexType]
|
18
|
+
# @return [Pin::ProxyType]
|
19
|
+
def self_pin(context)
|
20
|
+
Pin::ProxyType.anonymous(context)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param api_map [ApiMap]
|
24
|
+
# @param name_pin [Pin::Base]
|
25
|
+
# @return [Array<Pin::Base>]
|
26
|
+
def super_pins api_map, name_pin
|
27
|
+
pins = api_map.get_method_stack(name_pin.namespace, name_pin.name, scope: name_pin.scope)
|
28
|
+
pins.reject{|p| p.path == name_pin.path}
|
16
29
|
end
|
17
30
|
end
|
18
31
|
end
|
@@ -2,8 +2,8 @@ module Solargraph
|
|
2
2
|
class Source
|
3
3
|
class Chain
|
4
4
|
class InstanceVariable < Link
|
5
|
-
def
|
6
|
-
api_map.get_instance_variable_pins(context.namespace, context.scope).select{|p| p.name == word}
|
5
|
+
def resolve api_map, name_pin, locals
|
6
|
+
api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select{|p| p.name == word}
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -17,10 +17,10 @@ module Solargraph
|
|
17
17
|
end
|
18
18
|
|
19
19
|
# @param api_map [ApiMap]
|
20
|
-
# @param
|
21
|
-
# @param locals [Array<
|
22
|
-
# @return [Array<
|
23
|
-
def
|
20
|
+
# @param name_pin [Pin::Base]
|
21
|
+
# @param locals [Array<Pin::Base>]
|
22
|
+
# @return [Array<Pin::Base>]
|
23
|
+
def resolve api_map, name_pin, locals
|
24
24
|
[]
|
25
25
|
end
|
26
26
|
|
@@ -2,8 +2,8 @@ module Solargraph
|
|
2
2
|
class Source
|
3
3
|
class Chain
|
4
4
|
class Variable < Link
|
5
|
-
def
|
6
|
-
api_map.get_instance_variable_pins(context.namespace, context.scope).select{|p| p.name == word}
|
5
|
+
def resolve api_map, name_pin, locals
|
6
|
+
api_map.get_instance_variable_pins(name_pin.context.namespace, name_pin.context.scope).select{|p| p.name == word}
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -38,12 +38,6 @@ module Solargraph
|
|
38
38
|
break
|
39
39
|
end
|
40
40
|
commit text, "#{new_text[0..-2]} "
|
41
|
-
elsif nullable and !range.nil? and new_text.empty?
|
42
|
-
offset = Position.to_offset(text, range.start)
|
43
|
-
if offset > 0 and text[offset - 1] == '.'
|
44
|
-
text = text[0..offset - 1] + ' ' + text[offset..-1]
|
45
|
-
end
|
46
|
-
commit text, new_text
|
47
41
|
elsif range.nil?
|
48
42
|
new_text
|
49
43
|
else
|
@@ -0,0 +1,161 @@
|
|
1
|
+
module Solargraph
|
2
|
+
class Source
|
3
|
+
# Information about a position in a source, including the word located
|
4
|
+
# there.
|
5
|
+
#
|
6
|
+
class Cursor
|
7
|
+
# @return [Position]
|
8
|
+
attr_reader :position
|
9
|
+
|
10
|
+
# @return [Source]
|
11
|
+
attr_reader :source
|
12
|
+
|
13
|
+
# @param source [Source]
|
14
|
+
# @param position [Position, Array(Integer, Integer)]
|
15
|
+
def initialize source, position
|
16
|
+
@source = source
|
17
|
+
@position = if position.is_a?(Array)
|
18
|
+
Position.new(position[0], position[1])
|
19
|
+
else
|
20
|
+
position
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [String]
|
25
|
+
def filename
|
26
|
+
source.filename
|
27
|
+
end
|
28
|
+
|
29
|
+
# The whole word at the current position. Given the text `foo.bar`, the
|
30
|
+
# word at position(0,6) is `bar`.
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
def word
|
34
|
+
@word ||= start_of_word + end_of_word
|
35
|
+
end
|
36
|
+
|
37
|
+
# The part of the word before the current position. Given the text
|
38
|
+
# `foo.bar`, the start_of_word at position(0, 6) is `ba`.
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
def start_of_word
|
42
|
+
@start_of_word ||= begin
|
43
|
+
match = source.code[0..offset-1].to_s.match(start_word_pattern)
|
44
|
+
result = (match ? match[0] : '')
|
45
|
+
# Including the preceding colon if the word appears to be a symbol
|
46
|
+
result = ":#{result}" if source.code[0..offset-result.length-1].end_with?(':') and !source.code[0..offset-result.length-1].end_with?('::')
|
47
|
+
result
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# The part of the word after the current position. Given the text
|
52
|
+
# `foo.bar`, the end_of_word at position (0,6) is `r`.
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
def end_of_word
|
56
|
+
@end_of_word ||= begin
|
57
|
+
match = source.code[offset..-1].to_s.match(end_word_pattern)
|
58
|
+
match ? match[0] : ''
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def start_of_constant?
|
63
|
+
source.code[offset-2, 2] == '::'
|
64
|
+
end
|
65
|
+
|
66
|
+
# The range of the word at the current position.
|
67
|
+
#
|
68
|
+
# @return [Range]
|
69
|
+
def range
|
70
|
+
@range ||= begin
|
71
|
+
s = Position.from_offset(source.code, offset - start_of_word.length)
|
72
|
+
e = Position.from_offset(source.code, offset + end_of_word.length)
|
73
|
+
Solargraph::Range.new(s, e)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Chain]
|
78
|
+
def chain
|
79
|
+
@chain ||= SourceChainer.chain(source, position)
|
80
|
+
end
|
81
|
+
|
82
|
+
# @return [Boolean]
|
83
|
+
def argument?
|
84
|
+
@argument ||= !signature_position.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Boolean]
|
88
|
+
def comment?
|
89
|
+
@comment ||= source.comment_at?(position)
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Boolean]
|
93
|
+
def string?
|
94
|
+
@string ||= source.string_at?(position)
|
95
|
+
end
|
96
|
+
|
97
|
+
# @return [Cursor, nil]
|
98
|
+
def recipient
|
99
|
+
return nil unless argument?
|
100
|
+
@recipient ||= Cursor.new(source, signature_position)
|
101
|
+
end
|
102
|
+
|
103
|
+
def node_position
|
104
|
+
@node_position ||= begin
|
105
|
+
if start_of_word.empty?
|
106
|
+
match = source.code[0, offset].match(/[\s]*(\.|:+)[\s]*$/)
|
107
|
+
if match
|
108
|
+
Position.from_offset(source.code, offset - match[0].length)
|
109
|
+
else
|
110
|
+
position
|
111
|
+
end
|
112
|
+
else
|
113
|
+
position
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# @return [Integer]
|
121
|
+
def offset
|
122
|
+
@offset ||= Position.to_offset(source.code, position)
|
123
|
+
end
|
124
|
+
|
125
|
+
# A regular expression to find the start of a word from an offset.
|
126
|
+
#
|
127
|
+
# @return [Regexp]
|
128
|
+
def start_word_pattern
|
129
|
+
/(@{1,2}|\$)?([a-z0-9_]|[^\u0000-\u007F])*\z/i
|
130
|
+
end
|
131
|
+
|
132
|
+
# A regular expression to find the end of a word from an offset.
|
133
|
+
#
|
134
|
+
# @return [Regexp]
|
135
|
+
def end_word_pattern
|
136
|
+
/^([a-z0-9_]|[^\u0000-\u007F])*[\?\!]?/i
|
137
|
+
end
|
138
|
+
|
139
|
+
def signature_position
|
140
|
+
if @signature_position.nil?
|
141
|
+
open_parens = 0
|
142
|
+
cursor = offset - 1
|
143
|
+
while cursor >= 0
|
144
|
+
break if cursor < 0
|
145
|
+
if source.code[cursor] == ')'
|
146
|
+
open_parens -= 1
|
147
|
+
elsif source.code[cursor] == '('
|
148
|
+
open_parens += 1
|
149
|
+
end
|
150
|
+
break if open_parens == 1
|
151
|
+
cursor -= 1
|
152
|
+
end
|
153
|
+
if cursor >= 0
|
154
|
+
@signature_position = Position.from_offset(source.code, cursor)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
@signature_position
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|