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
@@ -1,7 +1,7 @@
|
|
1
1
|
module Solargraph
|
2
2
|
module Pin
|
3
3
|
class ProxyType < Base
|
4
|
-
# @param location [Solargraph::
|
4
|
+
# @param location [Solargraph::Location]
|
5
5
|
# @param namespace [String]
|
6
6
|
# @param name [String]
|
7
7
|
# @param return_type [ComplexType]
|
@@ -10,10 +10,6 @@ module Solargraph
|
|
10
10
|
@return_complex_type = return_type
|
11
11
|
end
|
12
12
|
|
13
|
-
def scope
|
14
|
-
return_complex_type.scope
|
15
|
-
end
|
16
|
-
|
17
13
|
def path
|
18
14
|
@path ||= begin
|
19
15
|
result = namespace.to_s
|
@@ -22,8 +18,8 @@ module Solargraph
|
|
22
18
|
end
|
23
19
|
end
|
24
20
|
|
25
|
-
def
|
26
|
-
|
21
|
+
def context
|
22
|
+
@return_complex_type
|
27
23
|
end
|
28
24
|
|
29
25
|
# @param return_type [ComplexType]
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Solargraph
|
2
2
|
module Pin
|
3
3
|
class Reference
|
4
|
-
# @return [
|
4
|
+
# @return [Location]
|
5
5
|
attr_reader :location
|
6
6
|
|
7
7
|
# @return [String]
|
@@ -10,7 +10,7 @@ module Solargraph
|
|
10
10
|
# @return [String]
|
11
11
|
attr_reader :name
|
12
12
|
|
13
|
-
# @param location [
|
13
|
+
# @param location [Location]
|
14
14
|
# @param namespace [String]
|
15
15
|
# @param name [String]
|
16
16
|
def initialize location, namespace, name
|
@@ -4,9 +4,9 @@ module Solargraph
|
|
4
4
|
class Method < Pin::Method
|
5
5
|
include YardMixin
|
6
6
|
|
7
|
-
def initialize code_object, location
|
7
|
+
def initialize code_object, location, name = nil, scope = nil, visibility = nil
|
8
8
|
comments = (code_object.docstring ? code_object.docstring.all : nil)
|
9
|
-
super(location, code_object.namespace.to_s, code_object.name.to_s, comments, code_object.scope, code_object.visibility, get_parameters(code_object))
|
9
|
+
super(location, code_object.namespace.to_s, name || code_object.name.to_s, comments, scope || code_object.scope, visibility || code_object.visibility, get_parameters(code_object))
|
10
10
|
end
|
11
11
|
|
12
12
|
def return_complex_type
|
@@ -6,14 +6,23 @@ module Solargraph
|
|
6
6
|
|
7
7
|
def initialize code_object, location
|
8
8
|
superclass = nil
|
9
|
-
|
9
|
+
# @todo This method of superclass detection is a bit of a hack. If
|
10
|
+
# the superclass is a Proxy, it is assumed to be undefined in its
|
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
|
10
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)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
17
26
|
end
|
18
27
|
|
19
28
|
private
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Solargraph
|
2
|
+
|
3
|
+
class Position
|
4
|
+
# @return [Integer]
|
5
|
+
attr_reader :line
|
6
|
+
|
7
|
+
# @return [Integer]
|
8
|
+
attr_reader :character
|
9
|
+
|
10
|
+
def initialize line, character
|
11
|
+
@line = line
|
12
|
+
@character = character
|
13
|
+
end
|
14
|
+
|
15
|
+
def column
|
16
|
+
character
|
17
|
+
end
|
18
|
+
|
19
|
+
# Get a hash of the position. This representation is suitable for use in
|
20
|
+
# the language server protocol.
|
21
|
+
#
|
22
|
+
# @return [Hash]
|
23
|
+
def to_hash
|
24
|
+
{
|
25
|
+
line: line,
|
26
|
+
character: character
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"<Position #{line}, #{character}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get a numeric offset for the specified text and position.
|
35
|
+
#
|
36
|
+
# @param text [String]
|
37
|
+
# @param position [Position]
|
38
|
+
# @return [Integer]
|
39
|
+
def self.to_offset text, position
|
40
|
+
result = 0
|
41
|
+
feed = 0
|
42
|
+
line = position.line
|
43
|
+
column = position.character
|
44
|
+
text.lines.each do |l|
|
45
|
+
line_length = l.length
|
46
|
+
char_length = l.chomp.length
|
47
|
+
if feed == line
|
48
|
+
result += column
|
49
|
+
break
|
50
|
+
end
|
51
|
+
result += line_length
|
52
|
+
feed += 1
|
53
|
+
end
|
54
|
+
result
|
55
|
+
end
|
56
|
+
|
57
|
+
# Get a numeric offset for the specified text and a position identified
|
58
|
+
# by its line and character.
|
59
|
+
#
|
60
|
+
# @param text [String]
|
61
|
+
# @param line [Integer]
|
62
|
+
# @param character [Integer]
|
63
|
+
# @return [Integer]
|
64
|
+
def self.line_char_to_offset text, line, character
|
65
|
+
to_offset(text, Position.new(line, character))
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get a position for the specified text and offset.
|
69
|
+
#
|
70
|
+
# @param text [String]
|
71
|
+
# @param offset [Integer]
|
72
|
+
# @return [Position]
|
73
|
+
def self.from_offset text, offset
|
74
|
+
cursor = 0
|
75
|
+
line = 0
|
76
|
+
character = nil
|
77
|
+
text.lines.each do |l|
|
78
|
+
line_length = l.length
|
79
|
+
char_length = l.chomp.length
|
80
|
+
if cursor + char_length >= offset
|
81
|
+
character = offset - cursor
|
82
|
+
break
|
83
|
+
end
|
84
|
+
cursor += line_length
|
85
|
+
line += 1
|
86
|
+
end
|
87
|
+
character = 0 if character.nil? and offset == cursor
|
88
|
+
raise InvalidOffsetError if character.nil?
|
89
|
+
Position.new(line, character)
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.normalize object
|
93
|
+
return object if object.is_a?(Position)
|
94
|
+
return Position.new(object[0], object[1]) if object.is_a?(Array)
|
95
|
+
raise ArgumentError, "Unable to convert #{object.class} to Position"
|
96
|
+
end
|
97
|
+
|
98
|
+
def == other
|
99
|
+
return false unless other.is_a?(Position)
|
100
|
+
line == other.line and character == other.character
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Solargraph
|
2
|
+
class Range
|
3
|
+
# @return [Position]
|
4
|
+
attr_reader :start
|
5
|
+
|
6
|
+
# @return [Position]
|
7
|
+
attr_reader :ending
|
8
|
+
|
9
|
+
# @param start [Position]
|
10
|
+
# @param ending [Position]
|
11
|
+
def initialize start, ending
|
12
|
+
@start = start
|
13
|
+
@ending = ending
|
14
|
+
end
|
15
|
+
|
16
|
+
# Get a hash of the range. This representation is suitable for use in
|
17
|
+
# the language server protocol.
|
18
|
+
#
|
19
|
+
# @return [Hash<Symbol, Position>]
|
20
|
+
def to_hash
|
21
|
+
{
|
22
|
+
start: start.to_hash,
|
23
|
+
end: ending.to_hash
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
# True if the specified position is inside the range.
|
28
|
+
#
|
29
|
+
# @param position [Solargraph::Position]
|
30
|
+
# @return [Boolean]
|
31
|
+
def contain? position
|
32
|
+
return false if position.line < start.line or position.line > ending.line
|
33
|
+
return false if position.line == start.line and position.character < start.character
|
34
|
+
return false if position.line == ending.line and position.character > ending.character
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
# True if the range contains the specified position and the position does not precede it.
|
39
|
+
#
|
40
|
+
# @param position [Position]
|
41
|
+
# @return [Boolean]
|
42
|
+
def include? position
|
43
|
+
contain?(position) and !(position.line == start.line and position.character == start.character)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a range from a pair of lines and characters.
|
47
|
+
#
|
48
|
+
# @param l1 [Integer] Starting line
|
49
|
+
# @param c1 [Integer] Starting character
|
50
|
+
# @param l2 [Integer] Ending line
|
51
|
+
# @param c2 [Integer] Ending character
|
52
|
+
# @return [Range]
|
53
|
+
def self.from_to l1, c1, l2, c2
|
54
|
+
Range.new(Position.new(l1, c1), Position.new(l2, c2))
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.from_node node
|
58
|
+
from_expr(node.loc.expression)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.from_expr expr
|
62
|
+
from_to(expr.line, expr.column, expr.last_line, expr.last_column)
|
63
|
+
end
|
64
|
+
|
65
|
+
def == other
|
66
|
+
return false unless other.is_a?(Range)
|
67
|
+
start == other.start and ending == other.ending
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/solargraph/source.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
1
|
require 'parser/current'
|
2
|
-
require 'time'
|
3
|
-
require 'yard'
|
4
2
|
|
5
3
|
module Solargraph
|
4
|
+
# A Ruby file that has been parsed into an AST.
|
5
|
+
#
|
6
6
|
class Source
|
7
7
|
autoload :FlawedBuilder, 'solargraph/source/flawed_builder'
|
8
|
-
autoload :Fragment, 'solargraph/source/fragment'
|
9
|
-
autoload :Position, 'solargraph/source/position'
|
10
|
-
autoload :Range, 'solargraph/source/range'
|
11
|
-
autoload :Location, 'solargraph/source/location'
|
12
8
|
autoload :Updater, 'solargraph/source/updater'
|
13
9
|
autoload :Change, 'solargraph/source/change'
|
14
10
|
autoload :Mapper, 'solargraph/source/mapper'
|
15
11
|
autoload :NodeMethods, 'solargraph/source/node_methods'
|
16
|
-
autoload :Chain, 'solargraph/source/chain'
|
17
12
|
autoload :EncodingFixes, 'solargraph/source/encoding_fixes'
|
18
|
-
autoload :
|
13
|
+
autoload :Cursor, 'solargraph/source/cursor'
|
14
|
+
autoload :Chain, 'solargraph/source/chain'
|
15
|
+
autoload :SourceChainer, 'solargraph/source/source_chainer'
|
19
16
|
autoload :NodeChainer, 'solargraph/source/node_chainer'
|
20
|
-
autoload :Completion, 'solargraph/source/completion'
|
21
17
|
|
22
18
|
include EncodingFixes
|
19
|
+
include NodeMethods
|
23
20
|
|
24
21
|
# @return [String]
|
25
22
|
attr_reader :code
|
@@ -27,173 +24,50 @@ module Solargraph
|
|
27
24
|
# @return [Parser::AST::Node]
|
28
25
|
attr_reader :node
|
29
26
|
|
30
|
-
# @return [Array]
|
31
27
|
attr_reader :comments
|
32
28
|
|
33
29
|
# @return [String]
|
34
30
|
attr_reader :filename
|
35
31
|
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# @return [Time]
|
39
|
-
attr_reader :mtime
|
40
|
-
|
41
|
-
attr_reader :directives
|
42
|
-
|
43
|
-
attr_reader :path_macros
|
44
|
-
|
32
|
+
# @todo Deprecate?
|
45
33
|
# @return [Integer]
|
46
|
-
|
47
|
-
|
48
|
-
# Get the time of the last synchronization.
|
49
|
-
#
|
50
|
-
# @return [Time]
|
51
|
-
attr_reader :stime
|
52
|
-
|
53
|
-
# @return [Array<Solargraph::Pin::Base>]
|
54
|
-
attr_reader :pins
|
55
|
-
|
56
|
-
# @return [Array<Solargraph::Pin::Reference>]
|
57
|
-
attr_reader :requires
|
58
|
-
|
59
|
-
attr_reader :domains
|
60
|
-
|
61
|
-
# @return [Array<Solargraph::Pin::Base>]
|
62
|
-
attr_reader :locals
|
63
|
-
|
64
|
-
include NodeMethods
|
34
|
+
attr_reader :version
|
65
35
|
|
66
36
|
# @param code [String]
|
67
37
|
# @param filename [String]
|
68
|
-
|
38
|
+
# @param version [Integer]
|
39
|
+
def initialize code, filename = nil, version = 0
|
40
|
+
@code = normalize(code)
|
41
|
+
@repaired = code
|
42
|
+
@filename = filename
|
43
|
+
@version = version
|
44
|
+
@domains = []
|
69
45
|
begin
|
70
|
-
@
|
71
|
-
@
|
72
|
-
@filename = filename
|
73
|
-
@version = 0
|
74
|
-
@domains = []
|
75
|
-
parse
|
46
|
+
@node, @comments = Source.parse_with_comments(@code, filename)
|
47
|
+
@parsed = true
|
76
48
|
rescue Parser::SyntaxError, EncodingError => e
|
77
|
-
|
49
|
+
@node, @comments = Source.parse_with_comments(@code.gsub(/[^s]/, ' '), filename)
|
50
|
+
@parsed = false
|
78
51
|
rescue Exception => e
|
79
52
|
STDERR.puts e.message
|
80
53
|
STDERR.puts e.backtrace
|
81
54
|
raise "Error parsing #{filename || '(source)'}: [#{e.class}] #{e.message}"
|
55
|
+
ensure
|
56
|
+
@code.freeze
|
82
57
|
end
|
83
58
|
end
|
84
59
|
|
85
|
-
# @param range [Solargraph::
|
60
|
+
# @param range [Solargraph::Range]
|
86
61
|
def at range
|
87
62
|
from_to range.start.line, range.start.character, range.ending.line, range.ending.character
|
88
63
|
end
|
89
64
|
|
90
65
|
def from_to l1, c1, l2, c2
|
91
|
-
b = Solargraph::
|
92
|
-
e = Solargraph::
|
66
|
+
b = Solargraph::Position.line_char_to_offset(@code, l1, c1)
|
67
|
+
e = Solargraph::Position.line_char_to_offset(@code, l2, c2)
|
93
68
|
@code[b..e-1]
|
94
69
|
end
|
95
70
|
|
96
|
-
def macro path
|
97
|
-
@path_macros[path]
|
98
|
-
end
|
99
|
-
|
100
|
-
# @todo Temporary blank
|
101
|
-
def path_macros
|
102
|
-
@path_macros ||= {}
|
103
|
-
end
|
104
|
-
|
105
|
-
# @todo Name problem
|
106
|
-
# @return [Array<Solargraph::Pin::Reference>]
|
107
|
-
def required
|
108
|
-
requires
|
109
|
-
end
|
110
|
-
|
111
|
-
# @return [Array<String>]
|
112
|
-
def namespaces
|
113
|
-
@namespaces ||= pins.select{|pin| pin.kind == Pin::NAMESPACE}.map(&:path)
|
114
|
-
end
|
115
|
-
|
116
|
-
# @param fqns [String] The namespace (nil for all)
|
117
|
-
# @return [Array<Solargraph::Pin::Namespace>]
|
118
|
-
def namespace_pins fqns = nil
|
119
|
-
pins.select{|pin| pin.kind == Pin::NAMESPACE}
|
120
|
-
end
|
121
|
-
|
122
|
-
# @param fqns [String] The namespace (nil for all)
|
123
|
-
# @return [Array<Solargraph::Pin::Method>]
|
124
|
-
def method_pins fqns = nil
|
125
|
-
pins.select{|pin| pin.kind == Solargraph::Pin::METHOD or pin.kind == Solargraph::Pin::ATTRIBUTE}
|
126
|
-
end
|
127
|
-
|
128
|
-
# @return [Array<Solargraph::Pin::Attribute>]
|
129
|
-
def attribute_pins
|
130
|
-
pins.select{|pin| pin.kind == Pin::ATTRIBUTE}
|
131
|
-
end
|
132
|
-
|
133
|
-
# @return [Array<Solargraph::Pin::InstanceVariable>]
|
134
|
-
def instance_variable_pins
|
135
|
-
pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE}
|
136
|
-
end
|
137
|
-
|
138
|
-
# @return [Array<Solargraph::Pin::ClassVariable>]
|
139
|
-
def class_variable_pins
|
140
|
-
pins.select{|pin| pin.kind == Pin::CLASS_VARIABLE}
|
141
|
-
end
|
142
|
-
|
143
|
-
# @return [Array<Solargraph::Pin::GlobalVariable>]
|
144
|
-
def global_variable_pins
|
145
|
-
pins.select{|pin| pin.kind == Pin::GLOBAL_VARIABLE}
|
146
|
-
end
|
147
|
-
|
148
|
-
# @return [Array<Solargraph::Pin::Constant>]
|
149
|
-
def constant_pins
|
150
|
-
pins.select{|pin| pin.kind == Pin::CONSTANT}
|
151
|
-
end
|
152
|
-
|
153
|
-
# @return [Array<Solargraph::Pin::Symbol>]
|
154
|
-
def symbol_pins
|
155
|
-
@symbols
|
156
|
-
end
|
157
|
-
|
158
|
-
# @return [Array<Solargraph::Pin::Symbol>]
|
159
|
-
def symbols
|
160
|
-
symbol_pins
|
161
|
-
end
|
162
|
-
|
163
|
-
# @param name [String]
|
164
|
-
# @return [Array<Source::Location>]
|
165
|
-
def references name
|
166
|
-
inner_node_references(name, node).map do |n|
|
167
|
-
offset = Position.to_offset(code, get_node_start_position(n))
|
168
|
-
soff = code.index(name, offset)
|
169
|
-
eoff = soff + name.length
|
170
|
-
Location.new(
|
171
|
-
filename,
|
172
|
-
Solargraph::Source::Range.new(
|
173
|
-
Position.from_offset(code, soff),
|
174
|
-
Position.from_offset(code, eoff)
|
175
|
-
)
|
176
|
-
)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def locate_named_path_pin line, character
|
181
|
-
_locate_pin line, character, Pin::NAMESPACE, Pin::METHOD
|
182
|
-
end
|
183
|
-
|
184
|
-
# Locate the namespace pin at the specified line and character.
|
185
|
-
#
|
186
|
-
# @param line [line]
|
187
|
-
# @param character [character]
|
188
|
-
# @return [Pin::Namespace]
|
189
|
-
def locate_namespace_pin line, character
|
190
|
-
_locate_pin line, character, Pin::NAMESPACE
|
191
|
-
end
|
192
|
-
|
193
|
-
def locate_block_pin line, character
|
194
|
-
_locate_pin line, character, Pin::NAMESPACE, Pin::METHOD, Pin::BLOCK
|
195
|
-
end
|
196
|
-
|
197
71
|
# Get the nearest node that contains the specified index.
|
198
72
|
#
|
199
73
|
# @param line [Integer]
|
@@ -203,38 +77,6 @@ module Solargraph
|
|
203
77
|
tree_at(line, column).first
|
204
78
|
end
|
205
79
|
|
206
|
-
# True if the specified location is inside a string.
|
207
|
-
#
|
208
|
-
# @param line [Integer]
|
209
|
-
# @param column [Integer]
|
210
|
-
# @return [Boolean]
|
211
|
-
def string_at?(line, column)
|
212
|
-
# node = node_at(line, column)
|
213
|
-
# # @todo raise InvalidOffset or InvalidRange or something?
|
214
|
-
# return false if node.nil?
|
215
|
-
# node.type == :str or node.type == :dstr
|
216
|
-
pos = Source::Position.new(line, column)
|
217
|
-
@strings.each do |str|
|
218
|
-
return true if str.contain?(pos)
|
219
|
-
break if str.start.line > pos.line
|
220
|
-
end
|
221
|
-
false
|
222
|
-
end
|
223
|
-
|
224
|
-
# True if the specified location is inside a comment.
|
225
|
-
#
|
226
|
-
# @param line [Integer]
|
227
|
-
# @param column [Integer]
|
228
|
-
# @return [Boolean]
|
229
|
-
def comment_at?(line, column)
|
230
|
-
pos = Source::Position.new(line, column)
|
231
|
-
@comment_ranges.each do |cmnt|
|
232
|
-
return true if cmnt.include?(pos)
|
233
|
-
break if cmnt.start.line > pos.line
|
234
|
-
end
|
235
|
-
false
|
236
|
-
end
|
237
|
-
|
238
80
|
# Get an array of nodes containing the specified index, starting with the
|
239
81
|
# nearest node and ending with the root.
|
240
82
|
#
|
@@ -251,82 +93,111 @@ module Solargraph
|
|
251
93
|
|
252
94
|
# @param updater [Source::Updater]
|
253
95
|
# @param reparse [Boolean]
|
254
|
-
# @return [
|
255
|
-
def synchronize updater
|
96
|
+
# @return [Source]
|
97
|
+
def synchronize updater
|
256
98
|
raise 'Invalid synchronization' unless updater.filename == filename
|
257
|
-
|
258
|
-
|
259
|
-
@code
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
begin
|
269
|
-
parse
|
270
|
-
rescue Parser::SyntaxError, EncodingError => e
|
271
|
-
hard_fix_node
|
99
|
+
real_code = updater.write(@code)
|
100
|
+
incr_code = updater.write(@code, true)
|
101
|
+
if real_code == @code
|
102
|
+
@version = updater.version
|
103
|
+
return self
|
104
|
+
end
|
105
|
+
synced = Source.new(incr_code, filename)
|
106
|
+
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))
|
272
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
|
273
117
|
end
|
118
|
+
synced.version = updater.version
|
119
|
+
synced
|
274
120
|
end
|
275
121
|
|
276
|
-
# @param
|
277
|
-
# @return [
|
278
|
-
def
|
279
|
-
|
280
|
-
down = query.downcase
|
281
|
-
all_symbols.select{|p| p.path.downcase.include?(down)}
|
122
|
+
# @param position [Position]
|
123
|
+
# @return [Source::Cursor]
|
124
|
+
def cursor_at position
|
125
|
+
Cursor.new(self, position)
|
282
126
|
end
|
283
127
|
|
284
|
-
# @return [
|
285
|
-
def
|
286
|
-
@
|
287
|
-
[Pin::ATTRIBUTE, Pin::CONSTANT, Pin::METHOD, Pin::NAMESPACE].include?(pin.kind) and !pin.name.empty?
|
288
|
-
}
|
128
|
+
# @return [Boolean]
|
129
|
+
def parsed?
|
130
|
+
@parsed
|
289
131
|
end
|
290
132
|
|
291
|
-
|
292
|
-
|
293
|
-
def locate_pin location
|
294
|
-
# return nil unless location.start_with?("#{filename}:")
|
295
|
-
pins.select{|pin| pin.location == location}
|
133
|
+
def repaired?
|
134
|
+
@is_repaired ||= (@code != @repaired)
|
296
135
|
end
|
297
136
|
|
298
|
-
# @param
|
299
|
-
# @
|
300
|
-
|
301
|
-
|
302
|
-
|
137
|
+
# @param position [Position]
|
138
|
+
# @return [Boolean]
|
139
|
+
def string_at? position
|
140
|
+
string_ranges.each do |range|
|
141
|
+
return true if range.include?(position)
|
142
|
+
break if range.ending.line > position.line
|
143
|
+
end
|
144
|
+
false
|
303
145
|
end
|
304
146
|
|
147
|
+
# @param position [Position]
|
305
148
|
# @return [Boolean]
|
306
|
-
def
|
307
|
-
|
149
|
+
def comment_at? position
|
150
|
+
comment_ranges.each do |range|
|
151
|
+
return true if range.include?(position)
|
152
|
+
break if range.ending.line > position.line
|
153
|
+
end
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
# @param name [String]
|
158
|
+
# @return [Array<Location>]
|
159
|
+
def references name
|
160
|
+
inner_node_references(name, node).map do |n|
|
161
|
+
offset = Position.to_offset(code, get_node_start_position(n))
|
162
|
+
soff = code.index(name, offset)
|
163
|
+
eoff = soff + name.length
|
164
|
+
Location.new(
|
165
|
+
filename,
|
166
|
+
Range.new(
|
167
|
+
Position.from_offset(code, soff),
|
168
|
+
Position.from_offset(code, eoff)
|
169
|
+
)
|
170
|
+
)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# @return [Array<Range>]
|
175
|
+
def error_ranges
|
176
|
+
@error_ranges ||= []
|
308
177
|
end
|
309
178
|
|
310
179
|
private
|
311
180
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
181
|
+
# @return [Array<Range>]
|
182
|
+
def string_ranges
|
183
|
+
@string_ranges ||= string_ranges_in(@node)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [Array<Range>]
|
187
|
+
def comment_ranges
|
188
|
+
@comment_ranges || @comments.map do |cmnt|
|
189
|
+
Range.from_expr(cmnt.loc.expression)
|
318
190
|
end
|
319
|
-
# @todo Assuming the root pin is always valid
|
320
|
-
found || pins.first
|
321
191
|
end
|
322
192
|
|
323
|
-
def
|
193
|
+
def string_ranges_in n
|
324
194
|
result = []
|
325
|
-
if
|
326
|
-
if
|
327
|
-
result.push
|
195
|
+
if n.is_a?(Parser::AST::Node)
|
196
|
+
if n.type == :str
|
197
|
+
result.push Range.from_node(n)
|
198
|
+
else
|
199
|
+
n.children.each{ |c| result.concat string_ranges_in(c) }
|
328
200
|
end
|
329
|
-
top.children.each { |c| result.concat inner_node_references(name, c) }
|
330
201
|
end
|
331
202
|
result
|
332
203
|
end
|
@@ -344,101 +215,46 @@ module Solargraph
|
|
344
215
|
end
|
345
216
|
end
|
346
217
|
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
218
|
+
def inner_node_references name, top
|
219
|
+
result = []
|
220
|
+
if top.kind_of?(AST::Node)
|
221
|
+
if top.children.any?{|c| c.to_s == name}
|
222
|
+
result.push top
|
223
|
+
end
|
224
|
+
top.children.each { |c| result.concat inner_node_references(name, c) }
|
225
|
+
end
|
226
|
+
result
|
354
227
|
end
|
355
228
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
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
|
363
243
|
end
|
364
244
|
|
365
|
-
|
366
|
-
parser = Parser::CurrentRuby.new(FlawedBuilder.new)
|
367
|
-
parser.diagnostics.all_errors_are_fatal = true
|
368
|
-
parser.diagnostics.ignore_warnings = true
|
369
|
-
buffer = Parser::Source::Buffer.new(filename, 0)
|
370
|
-
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
371
|
-
parser.parse_with_comments(buffer)
|
372
|
-
end
|
245
|
+
protected
|
373
246
|
|
374
|
-
|
375
|
-
|
376
|
-
synchronize_mapped *new_map_data
|
377
|
-
end
|
247
|
+
# @return [Integer]
|
248
|
+
attr_writer :version
|
378
249
|
|
379
|
-
|
380
|
-
|
381
|
-
@comment_ranges = new_comment_ranges
|
382
|
-
return if @requires == new_requires and @symbols == new_symbols and try_merge(new_pins, new_locals)
|
383
|
-
@pins = new_pins
|
384
|
-
@locals = new_locals
|
385
|
-
@requires = new_requires
|
386
|
-
@symbols = new_symbols
|
387
|
-
@all_symbols = nil # Reset for future queries
|
388
|
-
@domains = []
|
389
|
-
@path_macros = {}
|
390
|
-
dirpins = []
|
391
|
-
@pins.select(&:maybe_directives?).each do |pin|
|
392
|
-
dirpins.push pin unless pin.directives.empty?
|
393
|
-
end
|
394
|
-
process_directives dirpins
|
395
|
-
@stime = Time.now
|
396
|
-
end
|
250
|
+
# @return [String]
|
251
|
+
attr_writer :code
|
397
252
|
|
398
|
-
# @
|
399
|
-
|
400
|
-
# @return [Boolean]
|
401
|
-
def try_merge new_pins, new_locals
|
402
|
-
return false if @pins.nil? or @locals.nil? or new_pins.length != @pins.length or new_locals.length != @locals.length
|
403
|
-
new_pins.each_index do |i|
|
404
|
-
return false unless @pins[i].try_merge!(new_pins[i])
|
405
|
-
end
|
406
|
-
new_locals.each_index do |i|
|
407
|
-
return false unless @locals[i].try_merge!(new_locals[i])
|
408
|
-
end
|
409
|
-
true
|
410
|
-
end
|
253
|
+
# @return [String]
|
254
|
+
attr_accessor :repaired
|
411
255
|
|
412
|
-
# @return [
|
413
|
-
|
414
|
-
pins.each do |pin|
|
415
|
-
pin.directives.each do |d|
|
416
|
-
# ns = namespace_for(k.node)
|
417
|
-
ns = (pin.kind == Pin::NAMESPACE ? pin.path : pin.namespace)
|
418
|
-
docstring = YARD::Docstring.parser.parse(d.tag.text).to_docstring
|
419
|
-
if d.tag.tag_name == 'attribute'
|
420
|
-
t = (d.tag.types.nil? || d.tag.types.empty?) ? nil : d.tag.types.flatten.join('')
|
421
|
-
if t.nil? or t.include?('r')
|
422
|
-
# location, namespace, name, docstring, access
|
423
|
-
@pins.push Solargraph::Pin::Attribute.new(pin.location, pin.path, d.tag.name, docstring.all, :reader, :instance)
|
424
|
-
end
|
425
|
-
if t.nil? or t.include?('w')
|
426
|
-
@pins.push Solargraph::Pin::Attribute.new(pin.location, pin.path, "#{d.tag.name}=", docstring.all, :writer, :instance)
|
427
|
-
end
|
428
|
-
elsif d.tag.tag_name == 'method'
|
429
|
-
gen_src = Source.new("def #{d.tag.name};end", filename)
|
430
|
-
gen_pin = gen_src.pins.last # Method is last pin after root namespace
|
431
|
-
@pins.push Solargraph::Pin::Method.new(pin.location, pin.path, gen_pin.name, docstring.all, :instance, :public, [])
|
432
|
-
elsif d.tag.tag_name == 'macro'
|
433
|
-
@path_macros[pin.path] = d
|
434
|
-
elsif d.tag.tag_name == 'domain'
|
435
|
-
@domains.push d.tag.text
|
436
|
-
else
|
437
|
-
# STDERR.puts "Nothing to do for directive: #{d}"
|
438
|
-
end
|
439
|
-
end
|
440
|
-
end
|
441
|
-
end
|
256
|
+
# @return [Boolean]
|
257
|
+
attr_writer :parsed
|
442
258
|
|
443
259
|
class << self
|
444
260
|
# @param filename [String]
|
@@ -453,17 +269,32 @@ module Solargraph
|
|
453
269
|
# @param code [String]
|
454
270
|
# @param filename [String]
|
455
271
|
# @return [Solargraph::Source]
|
456
|
-
def load_string code, filename = nil
|
457
|
-
Source.new code, filename
|
272
|
+
def load_string code, filename = nil, version = 0
|
273
|
+
Source.new code, filename, version
|
458
274
|
end
|
459
275
|
|
460
|
-
def
|
276
|
+
def parse_with_comments code, filename = nil
|
277
|
+
buffer = Parser::Source::Buffer.new(filename, 0)
|
278
|
+
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
279
|
+
parser.parse_with_comments(buffer)
|
280
|
+
end
|
281
|
+
|
282
|
+
def parse code, filename = nil
|
283
|
+
buffer = Parser::Source::Buffer.new(filename, 0)
|
284
|
+
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
285
|
+
parser.parse(buffer)
|
286
|
+
end
|
287
|
+
|
288
|
+
private
|
289
|
+
|
290
|
+
# @return [Parser::Base]
|
291
|
+
def parser
|
292
|
+
# @todo Consider setting an instance variable. We might not need to
|
293
|
+
# recreate the parser every time we use it.
|
461
294
|
parser = Parser::CurrentRuby.new(FlawedBuilder.new)
|
462
295
|
parser.diagnostics.all_errors_are_fatal = true
|
463
296
|
parser.diagnostics.ignore_warnings = true
|
464
|
-
|
465
|
-
buffer.source = code.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '_')
|
466
|
-
parser.parse(buffer)
|
297
|
+
parser
|
467
298
|
end
|
468
299
|
end
|
469
300
|
end
|