solargraph 0.26.1 → 0.27.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|