solargraph 0.30.1 → 0.30.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/solargraph/api_map.rb +1 -7
- data/lib/solargraph/api_map/cache.rb +0 -11
- data/lib/solargraph/diagnostics.rb +1 -0
- data/lib/solargraph/diagnostics/rubocop.rb +44 -60
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +46 -0
- data/lib/solargraph/language_server/host.rb +114 -50
- data/lib/solargraph/language_server/host/sources.rb +39 -0
- data/lib/solargraph/language_server/message/initialize.rb +2 -8
- data/lib/solargraph/language_server/uri_helpers.rb +1 -1
- data/lib/solargraph/library.rb +69 -41
- data/lib/solargraph/pin/conversions.rb +2 -2
- data/lib/solargraph/source_map/node_processor/send_node.rb +14 -2
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/yard_map/core_docs.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 61453319a056f4779bf21c852bb34b5f797207c19ff9266d422d25c724934bc1
|
4
|
+
data.tar.gz: 405c13271fce7df56c5daae95b95430f5933a66728d3e2f6fff11762d4f392d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5dcd832eff27b926dd8d244feff2edc37398f0004fa62dd002f07981e325d6f5a88f7c93bc1ad91d1f0ce34954790afc0410ed9581936d5ad57a465f99cc2372
|
7
|
+
data.tar.gz: 246cb5bbb21a7134532512c0321e86633d21adcf4eebd24de3d33427ab3da39663dafba488b8e0a304abd252c874408513ced6370cdc8e88cde18187f1242b79
|
data/lib/solargraph/api_map.rb
CHANGED
@@ -354,13 +354,7 @@ module Solargraph
|
|
354
354
|
# @param scope [Symbol] :instance or :class
|
355
355
|
# @return [Array<Solargraph::Pin::Base>]
|
356
356
|
def get_method_stack fqns, name, scope: :instance
|
357
|
-
|
358
|
-
# Solargraph on Solargraph itself.
|
359
|
-
# cached = cache.get_method_stack(fqns, name, scope)
|
360
|
-
# return cached unless cached.nil?
|
361
|
-
result = get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select{|p| p.name == name}
|
362
|
-
# cache.set_method_stack(fqns, name, scope, result)
|
363
|
-
# result
|
357
|
+
get_methods(fqns, scope: scope, visibility: [:private, :protected, :public]).select{|p| p.name == name}
|
364
358
|
end
|
365
359
|
|
366
360
|
# Get an array of all suggestions that match the specified path.
|
@@ -3,7 +3,6 @@ module Solargraph
|
|
3
3
|
class Cache
|
4
4
|
def initialize
|
5
5
|
@methods = {}
|
6
|
-
@method_stacks = {}
|
7
6
|
@constants = {}
|
8
7
|
@qualified_namespaces = {}
|
9
8
|
end
|
@@ -16,14 +15,6 @@ module Solargraph
|
|
16
15
|
@methods[[fqns, scope, visibility.sort, deep]] = value
|
17
16
|
end
|
18
17
|
|
19
|
-
def get_method_stack fqns, name, scope
|
20
|
-
@method_stacks[[fqns, name, scope]]
|
21
|
-
end
|
22
|
-
|
23
|
-
def set_method_stack fqns, name, scope, value
|
24
|
-
@method_stacks[[fqns, name, scope]] = value
|
25
|
-
end
|
26
|
-
|
27
18
|
def get_constants namespace, context
|
28
19
|
@constants[[namespace, context]]
|
29
20
|
end
|
@@ -43,7 +34,6 @@ module Solargraph
|
|
43
34
|
# @return [void]
|
44
35
|
def clear
|
45
36
|
@methods.clear
|
46
|
-
@method_stacks.clear
|
47
37
|
@constants.clear
|
48
38
|
@qualified_namespaces.clear
|
49
39
|
end
|
@@ -51,7 +41,6 @@ module Solargraph
|
|
51
41
|
# @return [Boolean]
|
52
42
|
def empty?
|
53
43
|
@methods.empty? &&
|
54
|
-
@method_stacks.empty? &&
|
55
44
|
@constants.empty? &&
|
56
45
|
@qualified_namespaces.empty?
|
57
46
|
end
|
@@ -6,6 +6,7 @@ module Solargraph
|
|
6
6
|
autoload :Base, 'solargraph/diagnostics/base'
|
7
7
|
autoload :Severities, 'solargraph/diagnostics/severities'
|
8
8
|
autoload :Rubocop, 'solargraph/diagnostics/rubocop'
|
9
|
+
autoload :RubocopHelpers, 'solargraph/diagnostics/rubocop_helpers'
|
9
10
|
autoload :RequireNotFound, 'solargraph/diagnostics/require_not_found'
|
10
11
|
autoload :TypeNotDefined, 'solargraph/diagnostics/type_not_defined'
|
11
12
|
autoload :UpdateErrors, 'solargraph/diagnostics/update_errors'
|
@@ -6,6 +6,8 @@ module Solargraph
|
|
6
6
|
# This reporter provides linting through RuboCop.
|
7
7
|
#
|
8
8
|
class Rubocop < Base
|
9
|
+
include RubocopHelpers
|
10
|
+
|
9
11
|
# Conversion of RuboCop severity names to LSP constants
|
10
12
|
SEVERITIES = {
|
11
13
|
'refactor' => Severities::HINT,
|
@@ -16,46 +18,23 @@ module Solargraph
|
|
16
18
|
}
|
17
19
|
|
18
20
|
# @param source [Solargraph::Source]
|
19
|
-
# @param
|
21
|
+
# @param _api_map [Solargraph::ApiMap]
|
20
22
|
# @return [Array<Hash>]
|
21
|
-
def diagnose source,
|
23
|
+
def diagnose source, _api_map
|
22
24
|
options, paths = generate_options(source.filename, source.code)
|
23
25
|
runner = RuboCop::Runner.new(options, RuboCop::ConfigStore.new)
|
24
26
|
result = redirect_stdout{ runner.run(paths) }
|
25
27
|
make_array JSON.parse(result)
|
28
|
+
rescue RuboCop::ValidationError, RuboCop::ConfigNotFoundError => e
|
29
|
+
raise DiagnosticsError, "Error in RuboCop configuration: #{e.message}"
|
26
30
|
rescue JSON::ParserError
|
27
31
|
raise DiagnosticsError, 'RuboCop returned invalid data'
|
28
32
|
end
|
29
33
|
|
30
34
|
private
|
31
35
|
|
32
|
-
# @
|
33
|
-
#
|
34
|
-
# @return [Array]
|
35
|
-
def generate_options filename, code
|
36
|
-
args = ['-f', 'j']
|
37
|
-
rubocop_file = find_rubocop_file(filename)
|
38
|
-
args.push('-c', fix_drive_letter(rubocop_file)) unless rubocop_file.nil?
|
39
|
-
args.push filename
|
40
|
-
options, paths = RuboCop::Options.new.parse(args)
|
41
|
-
options[:stdin] = code
|
42
|
-
[options, paths]
|
43
|
-
end
|
44
|
-
|
45
|
-
# @param filename [String]
|
46
|
-
# @return [String, nil]
|
47
|
-
def find_rubocop_file filename
|
48
|
-
dir = File.dirname(filename)
|
49
|
-
until File.dirname(dir) == dir
|
50
|
-
here = File.join(dir, '.rubocop.yml')
|
51
|
-
return here if File.exist?(here)
|
52
|
-
dir = File.dirname(dir)
|
53
|
-
end
|
54
|
-
nil
|
55
|
-
end
|
56
|
-
|
57
|
-
# @todo This is a smelly way to redirect output, but the RuboCop specs do the
|
58
|
-
# same thing.
|
36
|
+
# @todo This is a smelly way to redirect output, but the RuboCop specs do
|
37
|
+
# the same thing.
|
59
38
|
# @return [String]
|
60
39
|
def redirect_stdout
|
61
40
|
redir = StringIO.new
|
@@ -71,43 +50,48 @@ module Solargraph
|
|
71
50
|
diagnostics = []
|
72
51
|
resp['files'].each do |file|
|
73
52
|
file['offenses'].each do |off|
|
74
|
-
|
75
|
-
last_line = off['location']['start_line']
|
76
|
-
last_column = 0
|
77
|
-
else
|
78
|
-
last_line = off['location']['last_line'] - 1
|
79
|
-
last_column = off['location']['last_column']
|
80
|
-
end
|
81
|
-
diag = {
|
82
|
-
range: {
|
83
|
-
start: {
|
84
|
-
line: off['location']['start_line'] - 1,
|
85
|
-
character: off['location']['start_column'] - 1
|
86
|
-
},
|
87
|
-
end: {
|
88
|
-
line: last_line,
|
89
|
-
character: last_column
|
90
|
-
}
|
91
|
-
},
|
92
|
-
# 1 = Error, 2 = Warning, 3 = Information, 4 = Hint
|
93
|
-
severity: SEVERITIES[off['severity']],
|
94
|
-
source: off['cop_name'],
|
95
|
-
message: off['message'].gsub(/^#{off['cop_name']}\:/, '')
|
96
|
-
}
|
97
|
-
diagnostics.push diag
|
53
|
+
diagnostics.push offense_to_diagnostic(off)
|
98
54
|
end
|
99
55
|
end
|
100
56
|
diagnostics
|
101
57
|
end
|
102
58
|
|
103
|
-
#
|
104
|
-
# so we need to convert the paths provided to the command.
|
59
|
+
# Convert a RuboCop offense to an LSP diagnostic
|
105
60
|
#
|
106
|
-
# @param
|
107
|
-
# @return [
|
108
|
-
def
|
109
|
-
|
110
|
-
|
61
|
+
# @param off [Hash] Offense received from Rubocop
|
62
|
+
# @return [Hash] LSP diagnostic
|
63
|
+
def offense_to_diagnostic off
|
64
|
+
{
|
65
|
+
range: offense_range(off).to_hash,
|
66
|
+
# 1 = Error, 2 = Warning, 3 = Information, 4 = Hint
|
67
|
+
severity: SEVERITIES[off['severity']],
|
68
|
+
source: off['cop_name'],
|
69
|
+
message: off['message'].gsub(/^#{off['cop_name']}\:/, '')
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param off [Hash]
|
74
|
+
# @return [Range]
|
75
|
+
def offense_range off
|
76
|
+
Range.new(offense_start_position(off), offense_ending_position(off))
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param off [Hash]
|
80
|
+
# @return [Position]
|
81
|
+
def offense_start_position off
|
82
|
+
Position.new(off['location']['start_line'] - 1, off['location']['start_column'] - 1)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @param off [Hash]
|
86
|
+
# @return [Position]
|
87
|
+
def offense_ending_position off
|
88
|
+
if off['location']['start_line'] != off['location']['last_line']
|
89
|
+
Position.new(off['location']['start_line'], 0)
|
90
|
+
else
|
91
|
+
Position.new(
|
92
|
+
off['location']['start_line'] - 1, off['location']['last_column']
|
93
|
+
)
|
94
|
+
end
|
111
95
|
end
|
112
96
|
end
|
113
97
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Solargraph
|
2
|
+
module Diagnostics
|
3
|
+
module RubocopHelpers
|
4
|
+
module_function
|
5
|
+
|
6
|
+
# Generate command-line options for the specified filename and code.
|
7
|
+
#
|
8
|
+
# @param filename [String]
|
9
|
+
# @param code [String]
|
10
|
+
# @return [Array(Array<String>, Array<String>)]
|
11
|
+
def generate_options filename, code
|
12
|
+
args = ['-f', 'j']
|
13
|
+
rubocop_file = find_rubocop_file(filename)
|
14
|
+
args.push('-c', fix_drive_letter(rubocop_file)) unless rubocop_file.nil?
|
15
|
+
args.push filename
|
16
|
+
options, paths = RuboCop::Options.new.parse(args)
|
17
|
+
options[:stdin] = code
|
18
|
+
[options, paths]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Find a RuboCop configuration file in a file's directory tree.
|
22
|
+
#
|
23
|
+
# @param filename [String]
|
24
|
+
# @return [String, nil]
|
25
|
+
def find_rubocop_file filename
|
26
|
+
dir = File.dirname(filename)
|
27
|
+
until File.dirname(dir) == dir
|
28
|
+
here = File.join(dir, '.rubocop.yml')
|
29
|
+
return here if File.exist?(here)
|
30
|
+
dir = File.dirname(dir)
|
31
|
+
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# RuboCop internally uses capitalized drive letters for Windows paths,
|
36
|
+
# so we need to convert the paths provided to the command.
|
37
|
+
#
|
38
|
+
# @param path [String]
|
39
|
+
# @return [String]
|
40
|
+
def fix_drive_letter path
|
41
|
+
return path unless path.match(/^[a-z]:/)
|
42
|
+
path[0].upcase + path[1..-1]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -10,6 +10,7 @@ module Solargraph
|
|
10
10
|
class Host
|
11
11
|
autoload :Diagnoser, 'solargraph/language_server/host/diagnoser'
|
12
12
|
autoload :Cataloger, 'solargraph/language_server/host/cataloger'
|
13
|
+
autoload :Sources, 'solargraph/language_server/host/sources'
|
13
14
|
|
14
15
|
include Solargraph::LanguageServer::UriHelpers
|
15
16
|
include Logging
|
@@ -93,14 +94,17 @@ module Solargraph
|
|
93
94
|
end
|
94
95
|
|
95
96
|
# Respond to a notification that a file was created in the workspace.
|
96
|
-
# The
|
97
|
-
#
|
97
|
+
# The libraries will determine whether the file should be merged; see
|
98
|
+
# Solargraph::Library#create_from_disk.
|
98
99
|
#
|
99
100
|
# @param uri [String] The file uri.
|
101
|
+
# @return [Boolean] True if a library accepted the file.
|
100
102
|
def create uri
|
101
|
-
library = library_for(uri)
|
102
103
|
filename = uri_to_file(uri)
|
103
|
-
result =
|
104
|
+
result = false
|
105
|
+
libraries.each do |lib|
|
106
|
+
result = true if lib.create_from_disk filename
|
107
|
+
end
|
104
108
|
diagnoser.schedule uri if open?(uri)
|
105
109
|
result
|
106
110
|
end
|
@@ -109,9 +113,11 @@ module Solargraph
|
|
109
113
|
#
|
110
114
|
# @param uri [String] The file uri.
|
111
115
|
def delete uri
|
112
|
-
|
116
|
+
sources.close uri
|
113
117
|
filename = uri_to_file(uri)
|
114
|
-
|
118
|
+
libraries.each do |lib|
|
119
|
+
lib.delete filename
|
120
|
+
end
|
115
121
|
send_notification "textDocument/publishDiagnostics", {
|
116
122
|
uri: uri,
|
117
123
|
diagnostics: []
|
@@ -124,8 +130,10 @@ module Solargraph
|
|
124
130
|
# @param text [String] The contents of the file.
|
125
131
|
# @param version [Integer] A version number.
|
126
132
|
def open uri, text, version
|
127
|
-
|
128
|
-
|
133
|
+
src = sources.open(uri, text, version)
|
134
|
+
libraries.each do |lib|
|
135
|
+
lib.merge src
|
136
|
+
end
|
129
137
|
diagnoser.schedule uri
|
130
138
|
end
|
131
139
|
|
@@ -140,45 +148,58 @@ module Solargraph
|
|
140
148
|
# @param uri [String]
|
141
149
|
# @return [Boolean]
|
142
150
|
def open? uri
|
143
|
-
|
151
|
+
sources.include? uri
|
144
152
|
end
|
145
153
|
|
146
154
|
# Close the file specified by the URI.
|
147
155
|
#
|
148
156
|
# @param uri [String]
|
157
|
+
# @return [void]
|
149
158
|
def close uri
|
150
|
-
|
151
|
-
|
159
|
+
logger.info "Closing #{uri}"
|
160
|
+
sources.close uri
|
152
161
|
diagnoser.schedule uri
|
153
162
|
end
|
154
163
|
|
155
164
|
# @param uri [String]
|
165
|
+
# @return [void]
|
156
166
|
def diagnose uri
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
167
|
+
if sources.include?(uri)
|
168
|
+
logger.info "Diagnosing #{uri}"
|
169
|
+
library = library_for(uri)
|
170
|
+
begin
|
171
|
+
results = library.diagnose uri_to_file(uri)
|
172
|
+
send_notification "textDocument/publishDiagnostics", {
|
173
|
+
uri: uri,
|
174
|
+
diagnostics: results
|
175
|
+
}
|
176
|
+
rescue DiagnosticsError => e
|
177
|
+
logger.warn "Error in diagnostics: #{e.message}"
|
178
|
+
options['diagnostics'] = false
|
179
|
+
send_notification 'window/showMessage', {
|
180
|
+
type: LanguageServer::MessageTypes::ERROR,
|
181
|
+
message: "Error in diagnostics: #{e.message}"
|
182
|
+
}
|
183
|
+
end
|
184
|
+
else
|
185
|
+
send_notification 'textDocument/publishDiagnostics', {
|
162
186
|
uri: uri,
|
163
|
-
diagnostics:
|
164
|
-
}
|
165
|
-
rescue DiagnosticsError => e
|
166
|
-
STDERR.puts "Error in diagnostics: #{e.message}"
|
167
|
-
options['diagnostics'] = false
|
168
|
-
send_notification 'window/showMessage', {
|
169
|
-
type: LanguageServer::MessageTypes::ERROR,
|
170
|
-
message: "Error in diagnostics: #{e.message}"
|
187
|
+
diagnostics: []
|
171
188
|
}
|
172
189
|
end
|
173
190
|
end
|
174
191
|
|
192
|
+
# Update a document from the parameters of a textDocument/didChange
|
193
|
+
# method.
|
194
|
+
#
|
195
|
+
# @param params [Hash]
|
196
|
+
# @return [void]
|
175
197
|
def change params
|
176
|
-
library = library_for(params['textDocument']['uri'])
|
177
198
|
updater = generate_updater(params)
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
199
|
+
src = sources.update(params['textDocument']['uri'], updater)
|
200
|
+
libraries.each do |lib|
|
201
|
+
lib.merge src
|
202
|
+
end
|
182
203
|
diagnoser.schedule params['textDocument']['uri']
|
183
204
|
end
|
184
205
|
|
@@ -206,8 +227,12 @@ module Solargraph
|
|
206
227
|
# Prepare a library for the specified directory.
|
207
228
|
#
|
208
229
|
# @param directory [String]
|
209
|
-
# @param name [String]
|
230
|
+
# @param name [String, nil]
|
231
|
+
# @return [void]
|
210
232
|
def prepare directory, name = nil
|
233
|
+
# No need to create a library without a directory. The generic library
|
234
|
+
# will handle it.
|
235
|
+
return if directory.nil?
|
211
236
|
logger.info "Preparing library for #{directory}"
|
212
237
|
path = ''
|
213
238
|
path = normalize_separators(directory) unless directory.nil?
|
@@ -225,20 +250,26 @@ module Solargraph
|
|
225
250
|
cataloger.start
|
226
251
|
end
|
227
252
|
|
253
|
+
# Prepare multiple folders.
|
254
|
+
#
|
255
|
+
# @param array [Array<Hash{String => String}>]
|
256
|
+
# @return [void]
|
228
257
|
def prepare_folders array
|
258
|
+
return if array.nil?
|
229
259
|
array.each do |folder|
|
230
260
|
prepare uri_to_file(folder['uri']), folder['name']
|
231
261
|
end
|
232
262
|
end
|
233
263
|
|
264
|
+
# Remove a directory.
|
265
|
+
#
|
266
|
+
# @param directory [String]
|
267
|
+
# @return [void]
|
234
268
|
def remove directory
|
235
269
|
logger.info "Removing library for #{directory}"
|
236
270
|
# @param lib [Library]
|
237
271
|
libraries.delete_if do |lib|
|
238
272
|
next false if lib.workspace.directory != directory
|
239
|
-
lib.open_sources.each do |src|
|
240
|
-
orphan_library.open(src.filename, src.code, src.version)
|
241
|
-
end
|
242
273
|
true
|
243
274
|
end
|
244
275
|
end
|
@@ -299,6 +330,7 @@ module Solargraph
|
|
299
330
|
# that were not flagged for dynamic registration by the client.
|
300
331
|
#
|
301
332
|
# @param methods [Array<String>] The methods to register
|
333
|
+
# @return [void]
|
302
334
|
def register_capabilities methods
|
303
335
|
logger.debug "Registering capabilities: #{methods}"
|
304
336
|
@register_semaphore.synchronize do
|
@@ -320,6 +352,7 @@ module Solargraph
|
|
320
352
|
# that were not flagged for dynamic registration by the client.
|
321
353
|
#
|
322
354
|
# @param methods [Array<String>] The methods to unregister
|
355
|
+
# @return [void]
|
323
356
|
def unregister_capabilities methods
|
324
357
|
logger.debug "Unregistering capabilities: #{methods}"
|
325
358
|
@register_semaphore.synchronize do
|
@@ -338,6 +371,7 @@ module Solargraph
|
|
338
371
|
# Flag a method as available for dynamic registration.
|
339
372
|
#
|
340
373
|
# @param method [String] The method name, e.g., 'textDocument/completion'
|
374
|
+
# @return [void]
|
341
375
|
def allow_registration method
|
342
376
|
@register_semaphore.synchronize do
|
343
377
|
@dynamic_capabilities.add method
|
@@ -364,6 +398,7 @@ module Solargraph
|
|
364
398
|
cataloger.synchronizing?
|
365
399
|
end
|
366
400
|
|
401
|
+
# @return [void]
|
367
402
|
def stop
|
368
403
|
@stopped = true
|
369
404
|
cataloger.stop
|
@@ -454,15 +489,15 @@ module Solargraph
|
|
454
489
|
# @return [Array<Solargraph::Range>]
|
455
490
|
def references_from filename, line, column, strip: true
|
456
491
|
library = library_for(file_to_uri(filename))
|
457
|
-
|
492
|
+
library.references_from(filename, line, column, strip: strip)
|
458
493
|
end
|
459
494
|
|
460
495
|
# @param query [String]
|
461
496
|
# @return [Array<Solargraph::Pin::Base>]
|
462
497
|
def query_symbols query
|
463
498
|
result = []
|
464
|
-
libraries.each { |lib| result.concat lib.query_symbols(query) }
|
465
|
-
result
|
499
|
+
(libraries + [generic_library]).each { |lib| result.concat lib.query_symbols(query) }
|
500
|
+
result.uniq
|
466
501
|
end
|
467
502
|
|
468
503
|
# @param query [String]
|
@@ -492,6 +527,7 @@ module Solargraph
|
|
492
527
|
#
|
493
528
|
# @param text [String]
|
494
529
|
# @param type [Integer] A MessageType constant
|
530
|
+
# @return [void]
|
495
531
|
def show_message text, type = LanguageServer::MessageTypes::INFO
|
496
532
|
send_notification 'window/showMessage', {
|
497
533
|
type: type,
|
@@ -506,6 +542,7 @@ module Solargraph
|
|
506
542
|
# @param actions [Array<String>] Response options for the client
|
507
543
|
# @param &block The block that processes the response
|
508
544
|
# @yieldparam [String] The action received from the client
|
545
|
+
# @return [void]
|
509
546
|
def show_message_request text, type, actions, &block
|
510
547
|
send_request 'window/showMessageRequest', {
|
511
548
|
type: type,
|
@@ -546,6 +583,8 @@ module Solargraph
|
|
546
583
|
lib.catalog
|
547
584
|
end
|
548
585
|
|
586
|
+
# @param uri [String]
|
587
|
+
# @return [Array<Range>]
|
549
588
|
def folding_ranges uri
|
550
589
|
library = library_for(uri)
|
551
590
|
file = uri_to_file(uri)
|
@@ -559,26 +598,56 @@ module Solargraph
|
|
559
598
|
@libraries ||= []
|
560
599
|
end
|
561
600
|
|
601
|
+
# @return [Sources]
|
602
|
+
def sources
|
603
|
+
@sources ||= Sources.new
|
604
|
+
end
|
605
|
+
|
562
606
|
# @param uri [String]
|
563
607
|
# @return [Library]
|
564
608
|
def library_for uri
|
565
|
-
|
609
|
+
explicit_library_for(uri) ||
|
610
|
+
implicit_library_for(uri) ||
|
611
|
+
generic_library_for(uri)
|
612
|
+
end
|
613
|
+
|
614
|
+
# @param uri [String]
|
615
|
+
# @return [Library, nil]
|
616
|
+
def explicit_library_for uri
|
566
617
|
filename = uri_to_file(uri)
|
567
|
-
# Find a library with an explicit reference to the file
|
568
618
|
libraries.each do |lib|
|
569
|
-
|
619
|
+
if lib.contain?(filename) #|| lib.open?(filename)
|
620
|
+
lib.attach sources.find(uri) if sources.include?(uri)
|
621
|
+
return lib
|
622
|
+
end
|
570
623
|
end
|
571
|
-
|
624
|
+
nil
|
625
|
+
end
|
626
|
+
|
627
|
+
# @param uri [String]
|
628
|
+
# @return [Library, nil]
|
629
|
+
def implicit_library_for uri
|
630
|
+
filename = uri_to_file(uri)
|
572
631
|
libraries.each do |lib|
|
573
|
-
return lib if filename.start_with?(lib.workspace.directory)
|
632
|
+
# return lib if filename.start_with?(lib.workspace.directory)
|
633
|
+
if lib.open?(filename) || filename.start_with?(lib.workspace.directory)
|
634
|
+
lib.attach sources.find(uri)
|
635
|
+
return lib
|
636
|
+
end
|
574
637
|
end
|
575
|
-
|
576
|
-
|
638
|
+
nil
|
639
|
+
end
|
640
|
+
|
641
|
+
# @param uri [String]
|
642
|
+
# @return [Library]
|
643
|
+
def generic_library_for uri
|
644
|
+
generic_library.attach sources.find(uri)
|
645
|
+
generic_library
|
577
646
|
end
|
578
647
|
|
579
648
|
# @return [Library]
|
580
|
-
def
|
581
|
-
@
|
649
|
+
def generic_library
|
650
|
+
@generic_library ||= Solargraph::Library.new
|
582
651
|
end
|
583
652
|
|
584
653
|
# @return [Diagnoser]
|
@@ -591,11 +660,6 @@ module Solargraph
|
|
591
660
|
@cataloger ||= Cataloger.new(self)
|
592
661
|
end
|
593
662
|
|
594
|
-
def unsafe_open? uri
|
595
|
-
library = library_for(uri)
|
596
|
-
library.open?(uri_to_file(uri))
|
597
|
-
end
|
598
|
-
|
599
663
|
def requests
|
600
664
|
@requests ||= {}
|
601
665
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Solargraph
|
2
|
+
module LanguageServer
|
3
|
+
class Host
|
4
|
+
class Sources
|
5
|
+
include UriHelpers
|
6
|
+
|
7
|
+
def open uri, text, version
|
8
|
+
filename = uri_to_file(uri)
|
9
|
+
source = Solargraph::Source.new(text, filename, version)
|
10
|
+
open_source_hash[uri] = source
|
11
|
+
end
|
12
|
+
|
13
|
+
def update uri, updater
|
14
|
+
src = find(uri)
|
15
|
+
open_source_hash[uri] = src.synchronize(updater)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Source]
|
19
|
+
def find uri
|
20
|
+
open_source_hash[uri] || raise(Solargraph::FileNotFoundError, "Host could not find #{uri}")
|
21
|
+
end
|
22
|
+
|
23
|
+
def close uri
|
24
|
+
open_source_hash.delete uri
|
25
|
+
end
|
26
|
+
|
27
|
+
def include? uri
|
28
|
+
open_source_hash.key? uri
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def open_source_hash
|
34
|
+
@open_source_hash ||= {}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -5,7 +5,6 @@ module Solargraph
|
|
5
5
|
def process
|
6
6
|
host.configure params['initializationOptions']
|
7
7
|
if support_workspace_folders?
|
8
|
-
# @todo Prepare multiple folders
|
9
8
|
host.prepare_folders params['workspaceFolders']
|
10
9
|
elsif params['rootUri']
|
11
10
|
host.prepare UriHelpers.uri_to_file(params['rootUri'])
|
@@ -42,7 +41,8 @@ module Solargraph
|
|
42
41
|
def support_workspace_folders?
|
43
42
|
params['capabilities'] &&
|
44
43
|
params['capabilities']['workspace'] &&
|
45
|
-
params['capabilities']['workspace']['workspaceFolders']
|
44
|
+
params['capabilities']['workspace']['workspaceFolders'] &&
|
45
|
+
params['workspaceFolders']
|
46
46
|
end
|
47
47
|
|
48
48
|
def static_completion
|
@@ -113,12 +113,6 @@ module Solargraph
|
|
113
113
|
}
|
114
114
|
end
|
115
115
|
|
116
|
-
def static_references
|
117
|
-
{
|
118
|
-
referencesProvider: true
|
119
|
-
}
|
120
|
-
end
|
121
|
-
|
122
116
|
def static_folding_range
|
123
117
|
{
|
124
118
|
foldingRangeProvider: true
|
data/lib/solargraph/library.rb
CHANGED
@@ -11,6 +11,7 @@ module Solargraph
|
|
11
11
|
attr_reader :name
|
12
12
|
|
13
13
|
# @param workspace [Solargraph::Workspace]
|
14
|
+
# @param name [String, nil]
|
14
15
|
def initialize workspace = Solargraph::Workspace.new, name = nil
|
15
16
|
@workspace = workspace
|
16
17
|
@name = name
|
@@ -30,37 +31,52 @@ module Solargraph
|
|
30
31
|
# Open a file in the library. Opening a file will make it available for
|
31
32
|
# checkout and merge it into the workspace if applicable.
|
32
33
|
#
|
34
|
+
# @deprecated The library should not be responsible for this. Instead, it
|
35
|
+
# should accept a source and determine whether or not to merge it.
|
36
|
+
#
|
33
37
|
# @param filename [String]
|
34
38
|
# @param text [String]
|
35
39
|
# @param version [Integer]
|
36
40
|
# @return [void]
|
37
41
|
def open filename, text, version
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
open_file_hash[filename] = source
|
43
|
-
checkout filename
|
44
|
-
end
|
42
|
+
logger.warn "Library#open is deprecated"
|
43
|
+
source = Solargraph::Source.load_string(text, filename, version)
|
44
|
+
merge source
|
45
|
+
attach source
|
45
46
|
end
|
46
47
|
|
48
|
+
# Open a file from disk and try to merge it into the workspace.
|
49
|
+
#
|
50
|
+
# @param filename [String]
|
51
|
+
# @return [Boolean] True if the file was merged into the source.
|
47
52
|
def open_from_disk filename
|
53
|
+
source = Solargraph::Source.load(filename)
|
54
|
+
merge source
|
55
|
+
end
|
56
|
+
|
57
|
+
# Attach a source to the library.
|
58
|
+
#
|
59
|
+
# The attached source does not need to be a part of the workspace. The
|
60
|
+
# library will include it in the ApiMap while it's attached. Only one
|
61
|
+
# source can be attached to the library at a time.
|
62
|
+
#
|
63
|
+
# @param source [Source]
|
64
|
+
# @return [void]
|
65
|
+
def attach source
|
48
66
|
mutex.synchronize do
|
49
|
-
@synchronized =
|
50
|
-
|
51
|
-
workspace.merge source
|
52
|
-
open_file_hash[filename] = source
|
53
|
-
checkout filename
|
67
|
+
@synchronized = (@current == source) if synchronized?
|
68
|
+
@current = source
|
54
69
|
end
|
55
70
|
end
|
56
71
|
|
57
|
-
# True if the specified file is currently
|
72
|
+
# True if the specified file is currently attached.
|
58
73
|
#
|
59
74
|
# @param filename [String]
|
60
75
|
# @return [Boolean]
|
61
|
-
def
|
62
|
-
|
76
|
+
def attached? filename
|
77
|
+
!@current.nil? && @current.filename == filename
|
63
78
|
end
|
79
|
+
alias open? attached?
|
64
80
|
|
65
81
|
# True if the specified file is included in the workspace (but not
|
66
82
|
# necessarily open).
|
@@ -84,7 +100,6 @@ module Solargraph
|
|
84
100
|
@synchronized = false
|
85
101
|
source = Solargraph::Source.load_string(text, filename)
|
86
102
|
workspace.merge(source)
|
87
|
-
catalog
|
88
103
|
result = true
|
89
104
|
end
|
90
105
|
result
|
@@ -103,9 +118,6 @@ module Solargraph
|
|
103
118
|
@synchronized = false
|
104
119
|
source = Solargraph::Source.load_string(File.read(filename), filename)
|
105
120
|
workspace.merge(source)
|
106
|
-
open_file_hash[filename] = source if open_file_hash.key?(filename)
|
107
|
-
@current = source if @current && @current.filename == source.filename
|
108
|
-
catalog
|
109
121
|
result = true
|
110
122
|
end
|
111
123
|
result
|
@@ -120,9 +132,9 @@ module Solargraph
|
|
120
132
|
def delete filename
|
121
133
|
mutex.synchronize do
|
122
134
|
@synchronized = false
|
123
|
-
|
135
|
+
@current = nil if @current && @current.filename == filename
|
124
136
|
workspace.remove filename
|
125
|
-
catalog
|
137
|
+
# catalog
|
126
138
|
end
|
127
139
|
end
|
128
140
|
|
@@ -134,7 +146,7 @@ module Solargraph
|
|
134
146
|
def close filename
|
135
147
|
mutex.synchronize do
|
136
148
|
@synchronized = false
|
137
|
-
|
149
|
+
@current = nil if @current && @current.filename == filename
|
138
150
|
catalog
|
139
151
|
end
|
140
152
|
end
|
@@ -194,13 +206,13 @@ module Solargraph
|
|
194
206
|
return [] if pins.empty?
|
195
207
|
result = []
|
196
208
|
pins.uniq.each do |pin|
|
197
|
-
(workspace.sources +
|
209
|
+
(workspace.sources + (@current ? [@current] : [])).uniq.each do |source|
|
198
210
|
found = source.references(pin.name)
|
199
211
|
found.select! do |loc|
|
200
212
|
referenced = definitions_at(loc.filename, loc.range.ending.line, loc.range.ending.character)
|
201
213
|
referenced.any?{|r| r == pin}
|
202
214
|
end
|
203
|
-
# HACK for language clients that exclude special characters from the start of variable names
|
215
|
+
# HACK: for language clients that exclude special characters from the start of variable names
|
204
216
|
if strip && match = cursor.word.match(/^[^a-z0-9_]+/i)
|
205
217
|
found.map! do |loc|
|
206
218
|
Solargraph::Location.new(loc.filename, Solargraph::Range.from_to(loc.range.start.line, loc.range.start.column + match[0].length, loc.range.ending.line, loc.range.ending.column))
|
@@ -242,7 +254,7 @@ module Solargraph
|
|
242
254
|
# @return [Source]
|
243
255
|
def checkout filename
|
244
256
|
checked = read(filename)
|
245
|
-
@synchronized = (checked
|
257
|
+
@synchronized = (checked == @current) if synchronized?
|
246
258
|
@current = checked
|
247
259
|
catalog
|
248
260
|
@current
|
@@ -281,7 +293,6 @@ module Solargraph
|
|
281
293
|
# @return [Array<Solargraph::Pin::Base>]
|
282
294
|
def document_symbols filename
|
283
295
|
checkout filename
|
284
|
-
return [] unless open_file_hash.key?(filename)
|
285
296
|
api_map.document_symbols(filename)
|
286
297
|
end
|
287
298
|
|
@@ -297,20 +308,21 @@ module Solargraph
|
|
297
308
|
# @note This method will not update the library's ApiMap. See
|
298
309
|
# Library#synchronized? and Library#catalog for more information.
|
299
310
|
#
|
311
|
+
# @deprecated The library should not be responsible for this. Instead, it
|
312
|
+
# should accept a source and determine whether or not to merge it.
|
300
313
|
#
|
301
314
|
# @raise [FileNotFoundError] if the updater's file is not available.
|
302
315
|
# @param updater [Solargraph::Source::Updater]
|
303
316
|
# @return [void]
|
304
317
|
def update updater
|
318
|
+
logger.warn 'Library#update is deprecated'
|
305
319
|
mutex.synchronize do
|
306
320
|
if workspace.has_file?(updater.filename)
|
307
321
|
workspace.synchronize!(updater)
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
open_file_hash[updater.filename] = open_file_hash[updater.filename].synchronize(updater)
|
322
|
+
@current = workspace.source(updater.filename) if @current && @current.filename == updater.filename
|
323
|
+
elsif @current && @current.filename == updater.filename
|
324
|
+
@current = @current.synchronize(updater)
|
312
325
|
end
|
313
|
-
@current = open_file_hash[updater.filename] if @current && @current.filename == updater.filename
|
314
326
|
@synchronized = false
|
315
327
|
end
|
316
328
|
end
|
@@ -332,7 +344,9 @@ module Solargraph
|
|
332
344
|
# @todo Only open files get diagnosed. Determine whether anything or
|
333
345
|
# everything in the workspace should get diagnosed, or if there should
|
334
346
|
# be an option to do so.
|
347
|
+
#
|
335
348
|
return [] unless open?(filename)
|
349
|
+
catalog
|
336
350
|
result = []
|
337
351
|
source = read(filename)
|
338
352
|
workspace.config.reporters.each do |name|
|
@@ -355,23 +369,45 @@ module Solargraph
|
|
355
369
|
end
|
356
370
|
end
|
357
371
|
|
372
|
+
# Get an array of foldable ranges for the specified file.
|
373
|
+
#
|
374
|
+
# @param filename [String]
|
375
|
+
# @return [Array<Range>]
|
358
376
|
def folding_ranges filename
|
359
377
|
read(filename).folding_ranges
|
360
378
|
end
|
361
379
|
|
380
|
+
# @deprecated Libraries are no longer responsible for tracking open files.
|
381
|
+
#
|
362
382
|
# @return [Array<Source>]
|
363
383
|
def open_sources
|
364
|
-
|
384
|
+
logger.warn 'Library#open_sources is deprecated'
|
385
|
+
@current ? [@current] : []
|
365
386
|
end
|
366
387
|
|
367
388
|
# Create a library from a directory.
|
368
389
|
#
|
369
390
|
# @param directory [String] The path to be used for the workspace
|
391
|
+
# @param name [String, nil]
|
370
392
|
# @return [Solargraph::Library]
|
371
393
|
def self.load directory = '', name = nil
|
372
394
|
Solargraph::Library.new(Solargraph::Workspace.new(directory), name)
|
373
395
|
end
|
374
396
|
|
397
|
+
# Try to merge a source into the library's workspace. If the workspace is
|
398
|
+
# not configured to include the source, it gets ignored.
|
399
|
+
#
|
400
|
+
# @param source [Source]
|
401
|
+
# @return [Boolean] True if the source was merged into the workspace.
|
402
|
+
def merge source
|
403
|
+
result = nil
|
404
|
+
mutex.synchronize do
|
405
|
+
result = workspace.merge(source)
|
406
|
+
@synchronized = result if synchronized?
|
407
|
+
end
|
408
|
+
result
|
409
|
+
end
|
410
|
+
|
375
411
|
private
|
376
412
|
|
377
413
|
# @return [Mutex]
|
@@ -392,14 +428,6 @@ module Solargraph
|
|
392
428
|
)
|
393
429
|
end
|
394
430
|
|
395
|
-
# A collection of files that are currently open in the library. Open
|
396
|
-
# files do not need to be in the workspace.
|
397
|
-
#
|
398
|
-
# @return [Hash{String => Source}]
|
399
|
-
def open_file_hash
|
400
|
-
@open_file_hash ||= {}
|
401
|
-
end
|
402
|
-
|
403
431
|
# Get the source for an open file or create a new source if the file
|
404
432
|
# exists on disk. Sources created from disk are not added to the open
|
405
433
|
# workspace files, i.e., the version on disk remains the authoritative
|
@@ -409,7 +437,7 @@ module Solargraph
|
|
409
437
|
# @param filename [String]
|
410
438
|
# @return [Solargraph::Source]
|
411
439
|
def read filename
|
412
|
-
return
|
440
|
+
return @current if @current && @current.filename == filename
|
413
441
|
raise FileNotFoundError, "File not found: #{filename}" unless workspace.has_file?(filename)
|
414
442
|
workspace.source(filename)
|
415
443
|
end
|
@@ -71,8 +71,8 @@ module Solargraph
|
|
71
71
|
def generate_link
|
72
72
|
this_path = path || return_type.tag
|
73
73
|
return nil if this_path.nil? || this_path == 'undefined'
|
74
|
-
return this_path if comments.empty?
|
75
|
-
"[#{this_path.gsub('_', '\\\\_')}](solargraph:/document?query=#{URI.
|
74
|
+
# return this_path if comments.empty?
|
75
|
+
"[#{this_path.gsub('_', '\\\\_')}](solargraph:/document?query=#{URI.escape(this_path)})"
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
@@ -5,8 +5,20 @@ module Solargraph
|
|
5
5
|
def process
|
6
6
|
if node.children[0].nil?
|
7
7
|
if [:private, :public, :protected].include?(node.children[1])
|
8
|
-
|
9
|
-
|
8
|
+
if (node.children.length > 2)
|
9
|
+
node.children[2..-1].each do |child|
|
10
|
+
next unless child.is_a?(AST::Node) && (child.type == :sym || child.type == :str)
|
11
|
+
name = child.children[0].to_s
|
12
|
+
matches = pins.select{ |pin| [Pin::METHOD, Pin::ATTRIBUTE].include?(pin.kind) && pin.name == name && pin.namespace == region.namespace && pin.context.scope == region.scope }
|
13
|
+
matches.each do |pin|
|
14
|
+
# @todo Smelly instance variable access
|
15
|
+
pin.instance_variable_set(:@visibility, node.children[1])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
else
|
19
|
+
# @todo Smelly instance variable access
|
20
|
+
region.instance_variable_set(:@visibility, node.children[1])
|
21
|
+
end
|
10
22
|
elsif node.children[1] == :module_function
|
11
23
|
process_module_function
|
12
24
|
elsif [:attr_reader, :attr_writer, :attr_accessor].include?(node.children[1])
|
data/lib/solargraph/version.rb
CHANGED
@@ -12,7 +12,7 @@ module Solargraph
|
|
12
12
|
|
13
13
|
class << self
|
14
14
|
def cache_dir
|
15
|
-
@cache_dir ||= File.join(Dir.home, '.solargraph', 'cache')
|
15
|
+
@cache_dir ||= ENV["SOLARGRAPH_CACHE"] || File.join(Dir.home, '.solargraph', 'cache')
|
16
16
|
end
|
17
17
|
|
18
18
|
# Ensure installation of minimum documentation.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: solargraph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.30.
|
4
|
+
version: 0.30.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Fred Snyder
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: backport
|
@@ -227,6 +227,7 @@ files:
|
|
227
227
|
- lib/solargraph/diagnostics/base.rb
|
228
228
|
- lib/solargraph/diagnostics/require_not_found.rb
|
229
229
|
- lib/solargraph/diagnostics/rubocop.rb
|
230
|
+
- lib/solargraph/diagnostics/rubocop_helpers.rb
|
230
231
|
- lib/solargraph/diagnostics/severities.rb
|
231
232
|
- lib/solargraph/diagnostics/type_not_defined.rb
|
232
233
|
- lib/solargraph/diagnostics/update_errors.rb
|
@@ -236,6 +237,7 @@ files:
|
|
236
237
|
- lib/solargraph/language_server/host.rb
|
237
238
|
- lib/solargraph/language_server/host/cataloger.rb
|
238
239
|
- lib/solargraph/language_server/host/diagnoser.rb
|
240
|
+
- lib/solargraph/language_server/host/sources.rb
|
239
241
|
- lib/solargraph/language_server/message.rb
|
240
242
|
- lib/solargraph/language_server/message/base.rb
|
241
243
|
- lib/solargraph/language_server/message/cancel_request.rb
|