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
@@ -0,0 +1,68 @@
|
|
1
|
+
module Solargraph
|
2
|
+
module LanguageServer
|
3
|
+
class Host
|
4
|
+
class Cataloger
|
5
|
+
def initialize host
|
6
|
+
@host = host
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@stopped = true
|
9
|
+
@pings = []
|
10
|
+
end
|
11
|
+
|
12
|
+
# Notify the Cataloger that changes are pending.
|
13
|
+
#
|
14
|
+
# @return [void]
|
15
|
+
def ping
|
16
|
+
mutex.synchronize { pings.push nil }
|
17
|
+
end
|
18
|
+
|
19
|
+
def synchronizing?
|
20
|
+
!pings.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Stop the catalog thread.
|
24
|
+
#
|
25
|
+
# @return [void]
|
26
|
+
def stop
|
27
|
+
@stopped = true
|
28
|
+
end
|
29
|
+
|
30
|
+
# True if the cataloger is stopped.
|
31
|
+
#
|
32
|
+
# @return [Boolean]
|
33
|
+
def stopped?
|
34
|
+
@stopped
|
35
|
+
end
|
36
|
+
|
37
|
+
# Start the catalog thread.
|
38
|
+
#
|
39
|
+
# @return [void]
|
40
|
+
def start
|
41
|
+
return unless stopped?
|
42
|
+
@stopped = false
|
43
|
+
Thread.new do
|
44
|
+
until stopped?
|
45
|
+
sleep 0.1
|
46
|
+
next if pings.empty?
|
47
|
+
mutex.synchronize do
|
48
|
+
host.catalog
|
49
|
+
pings.clear
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# @return [Host]
|
58
|
+
attr_reader :host
|
59
|
+
|
60
|
+
# @return [Mutex]
|
61
|
+
attr_reader :mutex
|
62
|
+
|
63
|
+
# @return [Array]
|
64
|
+
attr_reader :pings
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Solargraph
|
2
|
+
module LanguageServer
|
3
|
+
class Host
|
4
|
+
class Diagnoser
|
5
|
+
def initialize host
|
6
|
+
@host = host
|
7
|
+
@mutex = Mutex.new
|
8
|
+
@queue = []
|
9
|
+
@stopped = true
|
10
|
+
end
|
11
|
+
|
12
|
+
# Schedule a file to be diagnosed.
|
13
|
+
#
|
14
|
+
# @param uri [String]
|
15
|
+
# @return [void]
|
16
|
+
def schedule uri
|
17
|
+
mutex.synchronize { queue.push uri }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Stop the diagnosis thread.
|
21
|
+
#
|
22
|
+
# @return [void]
|
23
|
+
def stop
|
24
|
+
@stopped = true
|
25
|
+
end
|
26
|
+
|
27
|
+
# True is the diagnoser is stopped.
|
28
|
+
#
|
29
|
+
# @return [Boolean]
|
30
|
+
def stopped?
|
31
|
+
@stopped
|
32
|
+
end
|
33
|
+
|
34
|
+
# Start the diagnosis thread.
|
35
|
+
#
|
36
|
+
# @return [self]
|
37
|
+
def start
|
38
|
+
return unless @stopped
|
39
|
+
@stopped = false
|
40
|
+
Thread.new do
|
41
|
+
until stopped?
|
42
|
+
sleep 0.1
|
43
|
+
next if queue.empty? || host.synchronizing?
|
44
|
+
if !host.options['diagnostics']
|
45
|
+
mutex.synchronize { queue.clear }
|
46
|
+
next
|
47
|
+
end
|
48
|
+
begin
|
49
|
+
current = nil
|
50
|
+
mutex.synchronize { current = queue.shift }
|
51
|
+
next if queue.include?(current)
|
52
|
+
results = []
|
53
|
+
results.concat host.diagnose(current) if host.open?(current)
|
54
|
+
host.send_notification "textDocument/publishDiagnostics", {
|
55
|
+
uri: current,
|
56
|
+
diagnostics: results
|
57
|
+
}
|
58
|
+
sleep 0.5
|
59
|
+
rescue DiagnosticsError => e
|
60
|
+
STDERR.puts "Error in diagnostics: #{e.message}"
|
61
|
+
options['diagnostics'] = false
|
62
|
+
host.send_notification 'window/showMessage', {
|
63
|
+
type: LanguageServer::MessageTypes::ERROR,
|
64
|
+
message: "Error in diagnostics: #{e.message}"
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# @return [Host]
|
75
|
+
attr_reader :host
|
76
|
+
|
77
|
+
# @return [Mutex]
|
78
|
+
attr_reader :mutex
|
79
|
+
|
80
|
+
# @return [Array]
|
81
|
+
attr_reader :queue
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'rubygems'
|
2
2
|
|
3
3
|
module Solargraph
|
4
4
|
module LanguageServer
|
@@ -10,30 +10,41 @@ module Solargraph
|
|
10
10
|
#
|
11
11
|
class CheckGemVersion < Base
|
12
12
|
def process
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
13
|
+
begin
|
14
|
+
fetcher = Gem::SpecFetcher.new
|
15
|
+
tuple = fetcher.search_for_dependency(Gem::Dependency.new('solargraph')).flatten.first
|
16
|
+
if tuple.nil?
|
17
|
+
msg = "An error occurred checking the Solargraph gem version."
|
18
|
+
STDERR.puts msg
|
19
|
+
host.show_message(msg, MessageTypes::ERROR) if params['verbose']
|
20
|
+
else
|
21
|
+
available = Gem::Version.new(tuple.version)
|
22
|
+
current = Gem::Version.new(Solargraph::VERSION)
|
23
|
+
if available > current
|
24
|
+
host.show_message_request "Solagraph gem version #{available} is available.",
|
25
|
+
LanguageServer::MessageTypes::INFO,
|
26
|
+
['Update now'] do |result|
|
27
|
+
break unless result == 'Update now'
|
28
|
+
o, s = Open3.capture2("gem update solargraph")
|
29
|
+
if s == 0
|
30
|
+
host.show_message 'Successfully updated the Solargraph gem.', LanguageServer::MessageTypes::INFO
|
31
|
+
else
|
32
|
+
host.show_message 'An error occurred while updating the gem.', LanguageServer::MessageTypes::ERROR
|
33
|
+
end
|
34
|
+
end
|
35
|
+
elsif params['verbose']
|
36
|
+
host.show_message "The Solargraph gem is up to date (version #{Solargraph::VERSION})."
|
37
|
+
end
|
38
|
+
set_result({
|
39
|
+
installed: current,
|
40
|
+
available: available
|
41
|
+
})
|
42
|
+
end
|
43
|
+
rescue Errno::EADDRNOTAVAIL => e
|
44
|
+
msg = "Unable to connect to gem source: #{e.message}"
|
45
|
+
STDERR.puts msg
|
46
|
+
host.show_message(msg, MessageTypes::ERROR) if params['verbose']
|
32
47
|
end
|
33
|
-
set_result({
|
34
|
-
installed: current,
|
35
|
-
available: available
|
36
|
-
})
|
37
48
|
end
|
38
49
|
end
|
39
50
|
end
|
@@ -6,11 +6,7 @@ module Solargraph
|
|
6
6
|
module TextDocument
|
7
7
|
class Completion < Base
|
8
8
|
def process
|
9
|
-
|
10
|
-
set_result empty_result(true)
|
11
|
-
else
|
12
|
-
inner_process
|
13
|
-
end
|
9
|
+
inner_process
|
14
10
|
end
|
15
11
|
|
16
12
|
private
|
@@ -26,16 +22,18 @@ module Solargraph
|
|
26
22
|
return set_result(empty_result) if host.cancel?(id)
|
27
23
|
end
|
28
24
|
items = []
|
29
|
-
|
25
|
+
last_context = nil
|
26
|
+
idx = -1
|
30
27
|
completion.pins.each do |pin|
|
28
|
+
idx += 1 if last_context != pin.context
|
31
29
|
items.push pin.completion_item.merge({
|
32
30
|
textEdit: {
|
33
31
|
range: completion.range.to_hash,
|
34
32
|
newText: pin.name.sub(/=$/, ' = ')
|
35
33
|
},
|
36
|
-
sortText: "#{
|
34
|
+
sortText: "#{idx.to_s.rjust(4, '0')}#{pin.name}"
|
37
35
|
})
|
38
|
-
|
36
|
+
last_context = pin.context
|
39
37
|
end
|
40
38
|
set_result(
|
41
39
|
isIncomplete: false,
|
@@ -2,7 +2,7 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
|
|
2
2
|
include Solargraph::LanguageServer::UriHelpers
|
3
3
|
|
4
4
|
def process
|
5
|
-
pins = host.
|
5
|
+
pins = host.document_symbols params['textDocument']['uri']
|
6
6
|
info = pins.map do |pin|
|
7
7
|
result = {
|
8
8
|
name: pin.name,
|
@@ -22,7 +22,6 @@ module Solargraph::LanguageServer::Message::Workspace
|
|
22
22
|
elsif change['type'] == DELETED
|
23
23
|
host.delete change['uri']
|
24
24
|
else
|
25
|
-
# @todo Handle error
|
26
25
|
set_error Solargraph::LanguageServer::ErrorCodes::INVALID_PARAMS, "Unknown change type ##{change['type']} for #{uri_to_file(change['uri'])}"
|
27
26
|
end
|
28
27
|
end
|
@@ -16,12 +16,10 @@ module Solargraph
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def process request
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
send_data tmp unless tmp.empty?
|
24
|
-
end
|
19
|
+
message = @host.start(request)
|
20
|
+
message.send_response
|
21
|
+
tmp = @host.flush
|
22
|
+
send_data tmp unless tmp.empty?
|
25
23
|
end
|
26
24
|
|
27
25
|
# @param data [String]
|
@@ -44,12 +44,10 @@ module Solargraph
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def process request
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
send_data tmp unless tmp.empty?
|
52
|
-
end
|
47
|
+
message = @host.start(request)
|
48
|
+
message.send_response
|
49
|
+
tmp = @host.flush
|
50
|
+
send_data tmp unless tmp.empty?
|
53
51
|
end
|
54
52
|
|
55
53
|
def start_timers
|
data/lib/solargraph/library.rb
CHANGED
@@ -1,11 +1,23 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
1
3
|
module Solargraph
|
2
4
|
# A library handles coordination between a Workspace and an ApiMap.
|
3
5
|
#
|
4
6
|
class Library
|
5
7
|
# @param workspace [Solargraph::Workspace]
|
6
8
|
def initialize workspace = Solargraph::Workspace.new(nil)
|
9
|
+
@mutex = Mutex.new
|
7
10
|
@workspace = workspace
|
8
|
-
api_map
|
11
|
+
api_map.catalog bundle
|
12
|
+
@synchronized = true
|
13
|
+
end
|
14
|
+
|
15
|
+
# True if the ApiMap is up to date with the library's workspace and open
|
16
|
+
# files.
|
17
|
+
#
|
18
|
+
# @return [Boolean]
|
19
|
+
def synchronized?
|
20
|
+
@synchronized
|
9
21
|
end
|
10
22
|
|
11
23
|
# Open a file in the library. Opening a file will make it available for
|
@@ -14,20 +26,22 @@ module Solargraph
|
|
14
26
|
# @param filename [String]
|
15
27
|
# @param text [String]
|
16
28
|
# @param version [Integer]
|
29
|
+
# @return [void]
|
17
30
|
def open filename, text, version
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
31
|
+
mutex.synchronize do
|
32
|
+
source = Solargraph::Source.load_string(text, filename, version)
|
33
|
+
workspace.merge source
|
34
|
+
open_file_hash[filename] = source
|
35
|
+
catalog #unless api_map.try_merge!(source)
|
36
|
+
end
|
23
37
|
end
|
24
38
|
|
25
|
-
# True if the specified file is currently open
|
39
|
+
# True if the specified file is currently open.
|
26
40
|
#
|
27
41
|
# @param filename [String]
|
28
42
|
# @return [Boolean]
|
29
43
|
def open? filename
|
30
|
-
|
44
|
+
open_file_hash.has_key? filename
|
31
45
|
end
|
32
46
|
|
33
47
|
# True if the specified file is included in the workspace (but not
|
@@ -39,18 +53,22 @@ module Solargraph
|
|
39
53
|
workspace.has_file?(filename)
|
40
54
|
end
|
41
55
|
|
42
|
-
# Create a
|
43
|
-
#
|
56
|
+
# Create a source to be added to the workspace. The file is ignored if the
|
57
|
+
# workspace is not configured to include the file.
|
44
58
|
#
|
45
59
|
# @param filename [String]
|
46
60
|
# @param text [String] The contents of the file
|
47
61
|
# @return [Boolean] True if the file was added to the workspace.
|
48
62
|
def create filename, text
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
63
|
+
result = false
|
64
|
+
mutex.synchronize do
|
65
|
+
next unless workspace.would_merge?(filename)
|
66
|
+
source = Solargraph::Source.load_string(text, filename)
|
67
|
+
workspace.merge(source)
|
68
|
+
catalog #unless api_map.try_merge!(source)
|
69
|
+
result = true
|
70
|
+
end
|
71
|
+
result
|
54
72
|
end
|
55
73
|
|
56
74
|
# Create a file source from a file on disk. The file is ignored if the
|
@@ -59,12 +77,16 @@ module Solargraph
|
|
59
77
|
# @param filename [String]
|
60
78
|
# @return [Boolean] True if the file was added to the workspace.
|
61
79
|
def create_from_disk filename
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
80
|
+
result = false
|
81
|
+
mutex.synchronize do
|
82
|
+
next if File.directory?(filename) or !File.exist?(filename)
|
83
|
+
next unless workspace.would_merge?(filename)
|
84
|
+
source = Solargraph::Source.load_string(File.read(filename), filename)
|
85
|
+
workspace.merge(source)
|
86
|
+
catalog #unless api_map.try_merge!(source)
|
87
|
+
result = true
|
88
|
+
end
|
89
|
+
result
|
68
90
|
end
|
69
91
|
|
70
92
|
# Delete a file from the library. Deleting a file will make it unavailable
|
@@ -72,35 +94,40 @@ module Solargraph
|
|
72
94
|
# workspace configuration determines that it should still exist.
|
73
95
|
#
|
74
96
|
# @param filename [String]
|
97
|
+
# @return [void]
|
75
98
|
def delete filename
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
99
|
+
mutex.synchronize do
|
100
|
+
open_file_hash.delete filename
|
101
|
+
workspace.remove filename
|
102
|
+
catalog
|
103
|
+
end
|
81
104
|
end
|
82
105
|
|
83
106
|
# Close a file in the library. Closing a file will make it unavailable for
|
84
107
|
# checkout although it may still exist in the workspace.
|
85
108
|
#
|
86
109
|
# @param filename [String]
|
110
|
+
# @return [void]
|
87
111
|
def close filename
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
workspace.merge source
|
112
|
+
mutex.synchronize do
|
113
|
+
open_file_hash.delete filename
|
114
|
+
catalog
|
92
115
|
end
|
93
116
|
end
|
94
117
|
|
95
118
|
# @param filename [String]
|
96
119
|
# @param version [Integer]
|
120
|
+
# @return [void]
|
97
121
|
def overwrite filename, version
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
122
|
+
mutex.synchronize do
|
123
|
+
source = source_hash[filename]
|
124
|
+
return if source.nil?
|
125
|
+
if source.version > version
|
126
|
+
STDERR.puts "Save out of sync for #{filename} (current #{source.version}, overwrite #{version})" if source.version > version
|
127
|
+
else
|
128
|
+
open filename, File.read(filename), version
|
129
|
+
end
|
130
|
+
catalog
|
104
131
|
end
|
105
132
|
end
|
106
133
|
|
@@ -109,12 +136,12 @@ module Solargraph
|
|
109
136
|
# @param filename [String] The file to analyze
|
110
137
|
# @param line [Integer] The zero-based line number
|
111
138
|
# @param column [Integer] The zero-based column number
|
112
|
-
# @return [
|
139
|
+
# @return [SourceMap::Completion]
|
140
|
+
# @todo Take a Location instead of filename/line/column
|
113
141
|
def completions_at filename, line, column
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
fragment.complete(api_map)
|
142
|
+
position = Position.new(line, column)
|
143
|
+
cursor = Source::Cursor.new(checkout(filename), position)
|
144
|
+
api_map.clip(cursor).complete
|
118
145
|
end
|
119
146
|
|
120
147
|
# Get definition suggestions for the expression at the specified file and
|
@@ -124,11 +151,11 @@ module Solargraph
|
|
124
151
|
# @param line [Integer] The zero-based line number
|
125
152
|
# @param column [Integer] The zero-based column number
|
126
153
|
# @return [Array<Solargraph::Pin::Base>]
|
154
|
+
# @todo Take filename/position instead of filename/line/column
|
127
155
|
def definitions_at filename, line, column
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
fragment.define(api_map)
|
156
|
+
position = Position.new(line, column)
|
157
|
+
cursor = Source::Cursor.new(checkout(filename), position)
|
158
|
+
api_map.clip(cursor).define
|
132
159
|
end
|
133
160
|
|
134
161
|
# Get signature suggestions for the method at the specified file and
|
@@ -138,37 +165,33 @@ module Solargraph
|
|
138
165
|
# @param line [Integer] The zero-based line number
|
139
166
|
# @param column [Integer] The zero-based column number
|
140
167
|
# @return [Array<Solargraph::Pin::Base>]
|
168
|
+
# @todo Take filename/position instead of filename/line/column
|
141
169
|
def signatures_at filename, line, column
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
fragment.signify(api_map)
|
170
|
+
position = Position.new(line, column)
|
171
|
+
cursor = Source::Cursor.new(checkout(filename), position)
|
172
|
+
api_map.clip(cursor).signify
|
146
173
|
end
|
147
174
|
|
148
175
|
# @param filename [String]
|
149
176
|
# @param line [Integer]
|
150
177
|
# @param column [Integer]
|
151
|
-
# @return [Array<Solargraph::
|
178
|
+
# @return [Array<Solargraph::Range>]
|
179
|
+
# @todo Take a Location instead of filename/line/column
|
152
180
|
def references_from filename, line, column
|
153
|
-
|
154
|
-
|
155
|
-
fragment = source.fragment_at(line, column)
|
156
|
-
pins = fragment.define(api_map)
|
181
|
+
clip = api_map.clip_at(filename, Position.new(line, column))
|
182
|
+
pins = clip.define
|
157
183
|
return [] if pins.empty?
|
158
184
|
result = []
|
159
|
-
# @param pin [Solargraph::Pin::Base]
|
160
185
|
pins.uniq.each do |pin|
|
161
|
-
|
162
|
-
mn_loc = get_symbol_name_location(pin)
|
163
|
-
result.push mn_loc unless mn_loc.nil?
|
164
|
-
end
|
165
|
-
(workspace.sources + source_hash.values).uniq(&:filename).each do |source|
|
186
|
+
(workspace.sources + open_file_hash.values).uniq.each do |source|
|
166
187
|
found = source.references(pin.name)
|
167
188
|
found.select do |loc|
|
168
189
|
referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character)
|
169
190
|
referenced.any?{|r| r.path == pin.path}
|
170
191
|
end
|
171
|
-
result.concat
|
192
|
+
result.concat(found.sort{ |a, b|
|
193
|
+
a.range.start.line <=> b.range.start.line
|
194
|
+
})
|
172
195
|
end
|
173
196
|
end
|
174
197
|
result
|
@@ -176,6 +199,7 @@ module Solargraph
|
|
176
199
|
|
177
200
|
# Get the pin at the specified location or nil if the pin does not exist.
|
178
201
|
#
|
202
|
+
# @param location [Location]
|
179
203
|
# @return [Solargraph::Pin::Base]
|
180
204
|
def locate_pin location
|
181
205
|
api_map.locate_pin location
|
@@ -200,16 +224,7 @@ module Solargraph
|
|
200
224
|
# @param filename [String]
|
201
225
|
# @return [Source]
|
202
226
|
def checkout filename
|
203
|
-
|
204
|
-
api_map.virtualize nil
|
205
|
-
nil
|
206
|
-
else
|
207
|
-
read filename
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
def refresh force = false
|
212
|
-
api_map.refresh force
|
227
|
+
read filename
|
213
228
|
end
|
214
229
|
|
215
230
|
# @param query [String]
|
@@ -232,10 +247,17 @@ module Solargraph
|
|
232
247
|
api_map.query_symbols query
|
233
248
|
end
|
234
249
|
|
250
|
+
# Get an array of document symbols.
|
251
|
+
#
|
252
|
+
# Document symbols are composed of namespace, method, and constant pins.
|
253
|
+
# The results of this query are appropriate for building the response to a
|
254
|
+
# textDocument/documentSymbol message in the language server protocol.
|
255
|
+
#
|
235
256
|
# @param filename [String]
|
236
257
|
# @return [Array<Solargraph::Pin::Base>]
|
237
|
-
def
|
238
|
-
|
258
|
+
def document_symbols filename
|
259
|
+
return [] unless open_file_hash.has_key?(filename)
|
260
|
+
api_map.document_symbols(filename)
|
239
261
|
end
|
240
262
|
|
241
263
|
# @param path [String]
|
@@ -244,10 +266,26 @@ module Solargraph
|
|
244
266
|
api_map.get_path_suggestions(path)
|
245
267
|
end
|
246
268
|
|
269
|
+
# Update a source in the library from the provided updater.
|
270
|
+
#
|
271
|
+
# @note This method will not update the library's ApiMap. See
|
272
|
+
# Library#ynchronized? and Library#catalog for more information.
|
273
|
+
#
|
274
|
+
#
|
275
|
+
# @raise [FileNotFoundError] if the updater's file is not available.
|
247
276
|
# @param updater [Solargraph::Source::Updater]
|
248
|
-
|
249
|
-
|
250
|
-
|
277
|
+
# @return [void]
|
278
|
+
def update updater
|
279
|
+
mutex.synchronize do
|
280
|
+
if workspace.has_file?(updater.filename)
|
281
|
+
workspace.synchronize!(updater)
|
282
|
+
open_file_hash[updater.filename] = workspace.source(updater.filename) if open?(updater.filename)
|
283
|
+
else
|
284
|
+
raise FileNotFoundError, "Unable to update #{updater.filename}" unless open?(updater.filename)
|
285
|
+
open_file_hash[updater.filename] = open_file_hash[updater.filename].synchronize(updater)
|
286
|
+
end
|
287
|
+
@synchronized = false
|
288
|
+
end
|
251
289
|
end
|
252
290
|
|
253
291
|
# Get the current text of a file in the library.
|
@@ -278,6 +316,14 @@ module Solargraph
|
|
278
316
|
result
|
279
317
|
end
|
280
318
|
|
319
|
+
# Update the ApiMap from the library's workspace and open files.
|
320
|
+
#
|
321
|
+
# @return [void]
|
322
|
+
def catalog
|
323
|
+
api_map.catalog bundle
|
324
|
+
@synchronized = true
|
325
|
+
end
|
326
|
+
|
281
327
|
# Create a library from a directory.
|
282
328
|
#
|
283
329
|
# @param directory [String] The path to be used for the workspace
|
@@ -288,14 +334,27 @@ module Solargraph
|
|
288
334
|
|
289
335
|
private
|
290
336
|
|
291
|
-
# @return [
|
292
|
-
|
293
|
-
@source_hash ||= {}
|
294
|
-
end
|
337
|
+
# @return [Mutex]
|
338
|
+
attr_reader :mutex
|
295
339
|
|
296
|
-
# @return [
|
340
|
+
# @return [ApiMap]
|
297
341
|
def api_map
|
298
|
-
@api_map ||= Solargraph::ApiMap.new
|
342
|
+
@api_map ||= Solargraph::ApiMap.new
|
343
|
+
end
|
344
|
+
|
345
|
+
# @return [YardMap]
|
346
|
+
def yard_map
|
347
|
+
@yard_map ||= Solargraph::YardMap.new
|
348
|
+
end
|
349
|
+
|
350
|
+
# @return [Bundle]
|
351
|
+
def bundle
|
352
|
+
Bundle.new(
|
353
|
+
sources: (workspace.sources + open_file_hash.values).uniq(&:filename),
|
354
|
+
required: workspace.config.required,
|
355
|
+
load_paths: workspace.require_paths,
|
356
|
+
yard_map: yard_map
|
357
|
+
)
|
299
358
|
end
|
300
359
|
|
301
360
|
# @return [Solargraph::Workspace]
|
@@ -303,6 +362,14 @@ module Solargraph
|
|
303
362
|
@workspace
|
304
363
|
end
|
305
364
|
|
365
|
+
# A collection of files that are currently open in the library. Open
|
366
|
+
# files do not need to be in the workspace.
|
367
|
+
#
|
368
|
+
# @return [Hash{String => Source}]
|
369
|
+
def open_file_hash
|
370
|
+
@open_file_hash ||= {}
|
371
|
+
end
|
372
|
+
|
306
373
|
# Get the source for an open file or create a new source if the file
|
307
374
|
# exists on disk. Sources created from disk are not added to the open
|
308
375
|
# workspace files, i.e., the version on disk remains the authoritative
|
@@ -312,23 +379,9 @@ module Solargraph
|
|
312
379
|
# @param filename [String]
|
313
380
|
# @return [Solargraph::Source]
|
314
381
|
def read filename
|
315
|
-
return
|
316
|
-
|
317
|
-
|
318
|
-
Solargraph::Source.load(filename)
|
319
|
-
end
|
320
|
-
|
321
|
-
def get_symbol_name_location pin
|
322
|
-
decsrc = read(pin.location.filename)
|
323
|
-
offset = Solargraph::Source::Position.to_offset(decsrc.code, pin.location.range.start)
|
324
|
-
soff = decsrc.code.index(pin.name, offset)
|
325
|
-
eoff = soff + pin.name.length
|
326
|
-
Solargraph::Source::Location.new(
|
327
|
-
pin.location.filename, Solargraph::Source::Range.new(
|
328
|
-
Solargraph::Source::Position.from_offset(decsrc.code, soff),
|
329
|
-
Solargraph::Source::Position.from_offset(decsrc.code, eoff)
|
330
|
-
)
|
331
|
-
)
|
382
|
+
return open_file_hash[filename] if open_file_hash.has_key?(filename)
|
383
|
+
raise FileNotFoundError, "File not found: #{filename}" unless workspace.has_file?(filename)
|
384
|
+
workspace.source(filename)
|
332
385
|
end
|
333
386
|
end
|
334
387
|
end
|