solargraph 0.18.2 → 0.18.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/solargraph.rb +33 -28
- data/lib/solargraph/api_map.rb +997 -1044
- data/lib/solargraph/api_map/source_to_yard.rb +4 -3
- data/lib/solargraph/diagnostics/rubocop.rb +4 -3
- data/lib/solargraph/language_server/host.rb +140 -70
- data/lib/solargraph/language_server/message/base.rb +1 -0
- data/lib/solargraph/language_server/message/client.rb +6 -2
- data/lib/solargraph/language_server/message/text_document/completion.rb +34 -39
- data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/did_close.rb +1 -0
- data/lib/solargraph/language_server/message/text_document/did_save.rb +1 -3
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
- data/lib/solargraph/language_server/message/text_document/hover.rb +25 -30
- data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +1 -1
- data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +8 -7
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +1 -1
- data/lib/solargraph/language_server/transport/socket.rb +15 -17
- data/lib/solargraph/library.rb +34 -16
- data/lib/solargraph/node_methods.rb +96 -96
- data/lib/solargraph/pin.rb +1 -0
- data/lib/solargraph/pin/base.rb +2 -1
- data/lib/solargraph/pin/base_variable.rb +45 -5
- data/lib/solargraph/pin/block_parameter.rb +5 -2
- data/lib/solargraph/pin/method.rb +22 -0
- data/lib/solargraph/pin/namespace.rb +32 -2
- data/lib/solargraph/pin/reference.rb +21 -0
- data/lib/solargraph/pin/yard_object.rb +9 -0
- data/lib/solargraph/shell.rb +136 -136
- data/lib/solargraph/source.rb +134 -188
- data/lib/solargraph/source/change.rb +70 -0
- data/lib/solargraph/source/fragment.rb +120 -66
- data/lib/solargraph/source/position.rb +41 -0
- data/lib/solargraph/source/updater.rb +48 -0
- data/lib/solargraph/version.rb +3 -3
- data/lib/solargraph/workspace/config.rb +4 -9
- data/lib/solargraph/yard_map/core_docs.rb +0 -1
- metadata +5 -2
@@ -18,6 +18,7 @@ module Solargraph
|
|
18
18
|
code_object_map.clear
|
19
19
|
sources.each do |s|
|
20
20
|
s.namespace_pins.each do |pin|
|
21
|
+
next if pin.path.empty?
|
21
22
|
if pin.kind == Solargraph::Suggestion::CLASS
|
22
23
|
code_object_map[pin.path] ||= YARD::CodeObjects::ClassObject.new(root_code_object, pin.path)
|
23
24
|
else
|
@@ -26,9 +27,9 @@ module Solargraph
|
|
26
27
|
code_object_map[pin.path].docstring = pin.docstring unless pin.docstring.nil?
|
27
28
|
code_object_map[pin.path].files.push pin.source.filename
|
28
29
|
end
|
29
|
-
s.
|
30
|
-
|
31
|
-
code_object_map[
|
30
|
+
s.namespace_pins.each do |pin|
|
31
|
+
pin.include_references.each do |ref|
|
32
|
+
code_object_map[pin.path].instance_mixins.push code_object_map[ref.name] unless code_object_map[ref.name].nil? or code_object_map[pin.path].nil?
|
32
33
|
end
|
33
34
|
end
|
34
35
|
s.attribute_pins.each do |pin|
|
@@ -2,6 +2,7 @@ require 'open3'
|
|
2
2
|
require 'shellwords'
|
3
3
|
|
4
4
|
module Solargraph
|
5
|
+
|
5
6
|
module Diagnostics
|
6
7
|
class Rubocop
|
7
8
|
def initialize
|
@@ -13,10 +14,10 @@ module Solargraph
|
|
13
14
|
cmd = "rubocop -f j -s #{Shellwords.escape(filename)}"
|
14
15
|
o, e, s = Open3.capture3(cmd, stdin_data: text)
|
15
16
|
make_array text, JSON.parse(o)
|
17
|
+
rescue JSON::ParserError
|
18
|
+
raise DiagnosticsError, 'RuboCop returned invalid data'
|
16
19
|
rescue Exception => e
|
17
|
-
|
18
|
-
STDERR.puts "#{e.backtrace}"
|
19
|
-
nil
|
20
|
+
raise DiagnosticsError, 'An internal error occurred while running diagnostics'
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# require 'rubocop'
|
2
1
|
require 'thread'
|
3
2
|
require 'set'
|
4
3
|
|
@@ -10,18 +9,15 @@ module Solargraph
|
|
10
9
|
class Host
|
11
10
|
include Solargraph::LanguageServer::UriHelpers
|
12
11
|
|
13
|
-
# @return [Solargraph::Library]
|
14
|
-
attr_reader :library
|
15
|
-
|
16
12
|
def initialize
|
17
13
|
@change_semaphore = Mutex.new
|
14
|
+
@cancel_semaphore = Mutex.new
|
18
15
|
@buffer_semaphore = Mutex.new
|
19
16
|
@change_queue = []
|
20
17
|
@diagnostics_queue = []
|
21
18
|
@cancel = []
|
22
19
|
@buffer = ''
|
23
20
|
@stopped = false
|
24
|
-
@library = nil # @todo How to initialize the library
|
25
21
|
start_change_thread
|
26
22
|
start_diagnostics_thread
|
27
23
|
end
|
@@ -37,15 +33,17 @@ module Solargraph
|
|
37
33
|
end
|
38
34
|
|
39
35
|
def cancel id
|
40
|
-
@cancel.push id
|
36
|
+
@cancel_semaphore.synchronize { @cancel.push id }
|
41
37
|
end
|
42
38
|
|
43
39
|
def cancel? id
|
44
|
-
|
40
|
+
result = false
|
41
|
+
@cancel_semaphore.synchronize { result = @cancel.include? id }
|
42
|
+
result
|
45
43
|
end
|
46
44
|
|
47
45
|
def clear id
|
48
|
-
@cancel.delete id
|
46
|
+
@cancel_semaphore.synchronize { @cancel.delete id }
|
49
47
|
end
|
50
48
|
|
51
49
|
def start request
|
@@ -53,37 +51,69 @@ module Solargraph
|
|
53
51
|
begin
|
54
52
|
message.process
|
55
53
|
rescue Exception => e
|
56
|
-
|
57
|
-
STDERR.puts e.backtrace
|
58
|
-
message.set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, e.message
|
54
|
+
message.set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, "[#{e.class}] #{e.message}"
|
59
55
|
end
|
60
56
|
message
|
61
57
|
end
|
62
58
|
|
63
59
|
def create uri
|
64
|
-
|
65
|
-
|
60
|
+
@change_semaphore.synchronize do
|
61
|
+
filename = uri_to_file(uri)
|
62
|
+
library.create filename, File.read(filename)
|
63
|
+
end
|
66
64
|
end
|
67
65
|
|
68
66
|
def delete uri
|
69
|
-
|
70
|
-
|
67
|
+
@change_semaphore.synchronize do
|
68
|
+
filename = uri_to_file(uri)
|
69
|
+
library.delete filename
|
70
|
+
end
|
71
71
|
end
|
72
72
|
|
73
73
|
def open uri, text, version
|
74
|
-
|
75
|
-
|
74
|
+
@change_semaphore.synchronize do
|
75
|
+
library.open uri_to_file(uri), text, version
|
76
|
+
@diagnostics_queue.push uri
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def open? uri
|
81
|
+
result = nil
|
82
|
+
@change_semaphore.synchronize do
|
83
|
+
result = library.open?(uri_to_file(uri))
|
84
|
+
end
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
def close uri
|
89
|
+
@change_semaphore.synchronize do
|
90
|
+
library.close uri_to_file(uri)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def save params
|
95
|
+
@change_semaphore.synchronize do
|
96
|
+
uri = params['textDocument']['uri']
|
97
|
+
filename = uri_to_file(uri)
|
98
|
+
version = params['textDocument']['version']
|
99
|
+
@change_queue.delete_if do |change|
|
100
|
+
return true if change['textDocument']['uri'] == uri and change['textDocument']['version'] <= version
|
101
|
+
false
|
102
|
+
end
|
103
|
+
library.overwrite filename, version
|
104
|
+
end
|
76
105
|
end
|
77
106
|
|
78
107
|
def change params
|
79
108
|
@change_semaphore.synchronize do
|
80
|
-
if
|
109
|
+
if unsafe_changing? params['textDocument']['uri']
|
81
110
|
@change_queue.push params
|
82
111
|
else
|
83
112
|
source = library.checkout(uri_to_file(params['textDocument']['uri']))
|
84
113
|
@change_queue.push params
|
85
114
|
if params['textDocument']['version'] == source.version + params['contentChanges'].length
|
86
|
-
|
115
|
+
updater = generate_updater(params)
|
116
|
+
library.synchronize updater
|
87
117
|
library.refresh
|
88
118
|
@change_queue.pop
|
89
119
|
@diagnostics_queue.push params['textDocument']['uri']
|
@@ -128,7 +158,11 @@ module Solargraph
|
|
128
158
|
end
|
129
159
|
|
130
160
|
def changing? file_uri
|
131
|
-
|
161
|
+
result = false
|
162
|
+
@change_semaphore.synchronize do
|
163
|
+
result = unsafe_changing?(file_uri)
|
164
|
+
end
|
165
|
+
result
|
132
166
|
end
|
133
167
|
|
134
168
|
def stop
|
@@ -139,12 +173,6 @@ module Solargraph
|
|
139
173
|
@stopped
|
140
174
|
end
|
141
175
|
|
142
|
-
def synchronize &block
|
143
|
-
@change_semaphore.synchronize do
|
144
|
-
block.call
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
176
|
def locate_pin params
|
149
177
|
pin = nil
|
150
178
|
@change_semaphore.synchronize do
|
@@ -159,7 +187,11 @@ module Solargraph
|
|
159
187
|
|
160
188
|
def read_text uri
|
161
189
|
filename = uri_to_file(uri)
|
162
|
-
|
190
|
+
text = nil
|
191
|
+
@change_semaphore.synchronize do
|
192
|
+
text = library.read_text(filename)
|
193
|
+
end
|
194
|
+
text
|
163
195
|
end
|
164
196
|
|
165
197
|
def completions_at filename, line, column
|
@@ -172,7 +204,7 @@ module Solargraph
|
|
172
204
|
|
173
205
|
# @return [Array<Solargraph::Pin::Base>]
|
174
206
|
def definitions_at filename, line, column
|
175
|
-
results =
|
207
|
+
results = []
|
176
208
|
@change_semaphore.synchronize do
|
177
209
|
results = library.definitions_at(filename, line, column)
|
178
210
|
end
|
@@ -187,45 +219,69 @@ module Solargraph
|
|
187
219
|
results
|
188
220
|
end
|
189
221
|
|
222
|
+
def query_symbols query
|
223
|
+
results = nil
|
224
|
+
@change_semaphore.synchronize { results = library.query_symbols(query) }
|
225
|
+
results
|
226
|
+
end
|
227
|
+
|
228
|
+
def file_symbols uri
|
229
|
+
library.file_symbols(uri_to_file(uri))
|
230
|
+
end
|
231
|
+
|
190
232
|
private
|
191
233
|
|
234
|
+
# @return [Solargraph::Library]
|
235
|
+
def library
|
236
|
+
@library
|
237
|
+
end
|
238
|
+
|
239
|
+
def unsafe_changing? file_uri
|
240
|
+
@change_queue.any?{|change| change['textDocument']['uri'] == file_uri}
|
241
|
+
end
|
242
|
+
|
192
243
|
def start_change_thread
|
193
244
|
Thread.new do
|
194
245
|
until stopped?
|
195
246
|
@change_semaphore.synchronize do
|
196
247
|
begin
|
197
248
|
changed = false
|
249
|
+
@change_queue.sort!{|a, b| a['textDocument']['version'] <=> b['textDocument']['version']}
|
198
250
|
@change_queue.delete_if do |change|
|
199
251
|
filename = uri_to_file(change['textDocument']['uri'])
|
200
252
|
source = library.checkout(filename)
|
201
253
|
if change['textDocument']['version'] == source.version + change['contentChanges'].length
|
202
|
-
|
254
|
+
updater = generate_updater(change)
|
255
|
+
library.synchronize updater
|
203
256
|
@diagnostics_queue.push change['textDocument']['uri']
|
204
257
|
changed = true
|
205
|
-
true
|
258
|
+
next true
|
206
259
|
elsif change['textDocument']['version'] == source.version + 1 #and change['contentChanges'].length == 0
|
207
260
|
# HACK: This condition fixes the fact that formatting
|
208
261
|
# increments the version by one regardless of the number
|
209
262
|
# of changes
|
210
|
-
|
263
|
+
STDERR.puts "Warning: change applied to #{uri_to_file(change['textDocument']['uri'])} is possibly out of sync"
|
264
|
+
updater = generate_updater(change)
|
265
|
+
library.synchronize updater
|
211
266
|
@diagnostics_queue.push change['textDocument']['uri']
|
212
|
-
true
|
267
|
+
changed = true
|
268
|
+
next true
|
213
269
|
elsif change['textDocument']['version'] <= source.version
|
214
270
|
# @todo Is deleting outdated changes correct behavior?
|
215
|
-
STDERR.puts "
|
271
|
+
STDERR.puts "Warning: outdated to change to #{change['textDocument']['uri']} was ignored"
|
216
272
|
@diagnostics_queue.push change['textDocument']['uri']
|
217
|
-
|
218
|
-
true
|
273
|
+
next true
|
219
274
|
else
|
220
275
|
# @todo Change is out of order. Save it for later
|
221
|
-
|
222
|
-
false
|
276
|
+
next false
|
223
277
|
end
|
224
278
|
end
|
225
|
-
|
226
|
-
library.refresh if
|
279
|
+
refreshable = changed and @change_queue.empty?
|
280
|
+
library.refresh if refreshable
|
227
281
|
rescue Exception => e
|
228
|
-
|
282
|
+
# Trying to get anything out of the error except its class
|
283
|
+
# hangs the thread for some reason
|
284
|
+
STDERR.puts "An error occurred in the change thread: #{e.class}"
|
229
285
|
end
|
230
286
|
end
|
231
287
|
sleep 0.1
|
@@ -237,43 +293,39 @@ module Solargraph
|
|
237
293
|
Thread.new do
|
238
294
|
diagnoser = Diagnostics::Rubocop.new
|
239
295
|
until stopped?
|
296
|
+
sleep 1
|
240
297
|
if options['diagnostics'] != 'rubocop'
|
241
298
|
@change_semaphore.synchronize { @diagnostics_queue.clear }
|
242
|
-
sleep 1
|
243
299
|
next
|
244
300
|
end
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
}
|
269
|
-
end
|
270
|
-
end
|
301
|
+
begin
|
302
|
+
# Diagnosis is broken into two parts to reduce the amount of times it runs while
|
303
|
+
# a document is changing
|
304
|
+
current = nil
|
305
|
+
already_changing = nil
|
306
|
+
@change_semaphore.synchronize do
|
307
|
+
current = @diagnostics_queue.shift
|
308
|
+
break if current.nil?
|
309
|
+
already_changing = unsafe_changing?(current)
|
310
|
+
@diagnostics_queue.delete current unless already_changing
|
311
|
+
end
|
312
|
+
next if current.nil? or already_changing
|
313
|
+
filename = uri_to_file(current)
|
314
|
+
text = library.read_text(filename)
|
315
|
+
results = diagnoser.diagnose text, filename
|
316
|
+
@change_semaphore.synchronize do
|
317
|
+
already_changing = (unsafe_changing?(current) or @diagnostics_queue.include?(current))
|
318
|
+
# publish_diagnostics current, resp unless already_changing
|
319
|
+
unless already_changing
|
320
|
+
send_notification "textDocument/publishDiagnostics", {
|
321
|
+
uri: current,
|
322
|
+
diagnostics: results
|
323
|
+
}
|
271
324
|
end
|
272
|
-
rescue Exception => e
|
273
|
-
STDERR.puts e.message
|
274
325
|
end
|
326
|
+
rescue Exception => e
|
327
|
+
STDERR.puts "Error in diagnostics: #{e.class}"
|
275
328
|
end
|
276
|
-
sleep 0.1
|
277
329
|
end
|
278
330
|
end
|
279
331
|
end
|
@@ -282,6 +334,24 @@ module Solargraph
|
|
282
334
|
return path if File::ALT_SEPARATOR.nil?
|
283
335
|
path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
|
284
336
|
end
|
337
|
+
|
338
|
+
def generate_updater params
|
339
|
+
changes = []
|
340
|
+
params['contentChanges'].each do |chng|
|
341
|
+
changes.push Solargraph::Source::Change.new(
|
342
|
+
(chng['range'].nil? ?
|
343
|
+
nil :
|
344
|
+
Solargraph::Source::Range.from_to(chng['range']['start']['line'], chng['range']['start']['character'], chng['range']['end']['line'], chng['range']['end']['character'])
|
345
|
+
),
|
346
|
+
chng['text']
|
347
|
+
)
|
348
|
+
end
|
349
|
+
Solargraph::Source::Updater.new(
|
350
|
+
uri_to_file(params['textDocument']['uri']),
|
351
|
+
params['textDocument']['version'],
|
352
|
+
changes
|
353
|
+
)
|
354
|
+
end
|
285
355
|
end
|
286
356
|
end
|
287
357
|
end
|
@@ -52,6 +52,7 @@ module Solargraph
|
|
52
52
|
}
|
53
53
|
response[:result] = result unless result.nil?
|
54
54
|
response[:error] = error unless error.nil?
|
55
|
+
response[:result] = nil if result.nil? and error.nil?
|
55
56
|
json = response.to_json
|
56
57
|
envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
|
57
58
|
host.queue envelope
|
@@ -1,5 +1,9 @@
|
|
1
1
|
module Solargraph
|
2
|
-
module
|
3
|
-
|
2
|
+
module LanguageServer
|
3
|
+
module Message
|
4
|
+
module Client
|
5
|
+
autoload :RegisterCapability, 'solargraph/language_server/message/client/register_capability'
|
6
|
+
end
|
7
|
+
end
|
4
8
|
end
|
5
9
|
end
|
@@ -6,33 +6,20 @@ module Solargraph
|
|
6
6
|
module TextDocument
|
7
7
|
class Completion < Base
|
8
8
|
def process
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
processed = true
|
17
|
-
end
|
18
|
-
else
|
19
|
-
inner_process
|
9
|
+
start = Time.now
|
10
|
+
processed = false
|
11
|
+
until processed
|
12
|
+
if host.changing?(params['textDocument']['uri'])
|
13
|
+
if Time.now - start > 1
|
14
|
+
# set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, 'Completion request timed out'
|
15
|
+
set_result empty_result
|
20
16
|
processed = true
|
21
17
|
end
|
22
|
-
sleep 0.1 unless processed
|
23
|
-
end
|
24
|
-
rescue Exception => e
|
25
|
-
STDERR.puts e.message
|
26
|
-
STDERR.puts e.backtrace
|
27
|
-
# Ignore 'Invalid offset' errors, since they usually just mean
|
28
|
-
# that the document is in the process of changing.
|
29
|
-
if e.message.include?('Invalid offset')
|
30
|
-
# @todo Should this result be marked as incomplete? It might
|
31
|
-
# be possible to resolve it after changes are finished.
|
32
|
-
set_result empty_result
|
33
18
|
else
|
34
|
-
|
19
|
+
inner_process
|
20
|
+
processed = true
|
35
21
|
end
|
22
|
+
sleep 0.1 unless processed
|
36
23
|
end
|
37
24
|
end
|
38
25
|
|
@@ -42,23 +29,31 @@ module Solargraph
|
|
42
29
|
filename = uri_to_file(params['textDocument']['uri'])
|
43
30
|
line = params['position']['line']
|
44
31
|
col = params['position']['character']
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
32
|
+
begin
|
33
|
+
completion = host.completions_at(filename, line, col)
|
34
|
+
if host.cancel?(id)
|
35
|
+
return set_result(empty_result) if host.cancel?(id)
|
36
|
+
end
|
37
|
+
items = []
|
38
|
+
idx = 0
|
39
|
+
completion.pins.each do |pin|
|
40
|
+
items.push pin.completion_item.merge({
|
41
|
+
textEdit: {
|
42
|
+
range: completion.range.to_hash,
|
43
|
+
newText: pin.name
|
44
|
+
},
|
45
|
+
sortText: "#{pin.name}#{idx.to_s.rjust(4, '0')}"
|
46
|
+
})
|
47
|
+
idx += 1
|
48
|
+
end
|
49
|
+
set_result(
|
50
|
+
isIncomplete: false,
|
51
|
+
items: items
|
52
|
+
)
|
53
|
+
rescue InvalidOffsetError => e
|
54
|
+
STDERR.puts "Skipping invalid offset: #{filename}, line #{line}, character #{col}"
|
55
|
+
set_result empty_result
|
57
56
|
end
|
58
|
-
set_result(
|
59
|
-
isIncomplete: false,
|
60
|
-
items: items
|
61
|
-
)
|
62
57
|
end
|
63
58
|
|
64
59
|
def empty_result
|