solargraph 0.17.4 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +16 -12
  3. data/lib/solargraph/api_map.rb +516 -588
  4. data/lib/solargraph/api_map/completion.rb +16 -0
  5. data/lib/solargraph/api_map/source_to_yard.rb +2 -2
  6. data/lib/solargraph/language_server.rb +12 -0
  7. data/lib/solargraph/language_server/completion_item_kinds.rb +31 -0
  8. data/lib/solargraph/language_server/error_codes.rb +16 -0
  9. data/lib/solargraph/language_server/host.rb +305 -0
  10. data/lib/solargraph/language_server/message.rb +70 -0
  11. data/lib/solargraph/language_server/message/base.rb +64 -0
  12. data/lib/solargraph/language_server/message/cancel_request.rb +11 -0
  13. data/lib/solargraph/language_server/message/client.rb +5 -0
  14. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -0
  15. data/lib/solargraph/language_server/message/completion_item.rb +9 -0
  16. data/lib/solargraph/language_server/message/completion_item/resolve.rb +23 -0
  17. data/lib/solargraph/language_server/message/exit_notification.rb +12 -0
  18. data/lib/solargraph/language_server/message/extended.rb +15 -0
  19. data/lib/solargraph/language_server/message/extended/document.rb +18 -0
  20. data/lib/solargraph/language_server/message/extended/search.rb +18 -0
  21. data/lib/solargraph/language_server/message/initialize.rb +39 -0
  22. data/lib/solargraph/language_server/message/initialized.rb +10 -0
  23. data/lib/solargraph/language_server/message/method_not_found.rb +14 -0
  24. data/lib/solargraph/language_server/message/method_not_implemented.rb +12 -0
  25. data/lib/solargraph/language_server/message/shutdown.rb +11 -0
  26. data/lib/solargraph/language_server/message/text_document.rb +21 -0
  27. data/lib/solargraph/language_server/message/text_document/base.rb +17 -0
  28. data/lib/solargraph/language_server/message/text_document/completion.rb +69 -0
  29. data/lib/solargraph/language_server/message/text_document/definition.rb +38 -0
  30. data/lib/solargraph/language_server/message/text_document/did_change.rb +15 -0
  31. data/lib/solargraph/language_server/message/text_document/did_close.rb +12 -0
  32. data/lib/solargraph/language_server/message/text_document/did_open.rb +13 -0
  33. data/lib/solargraph/language_server/message/text_document/did_save.rb +15 -0
  34. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +31 -0
  35. data/lib/solargraph/language_server/message/text_document/formatting.rb +36 -0
  36. data/lib/solargraph/language_server/message/text_document/hover.rb +19 -0
  37. data/lib/solargraph/language_server/message/text_document/on_type_formatting.rb +29 -0
  38. data/lib/solargraph/language_server/message/text_document/signature_help.rb +23 -0
  39. data/lib/solargraph/language_server/message/workspace.rb +11 -0
  40. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +9 -0
  41. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +30 -0
  42. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +31 -0
  43. data/lib/solargraph/language_server/symbol_kinds.rb +32 -0
  44. data/lib/solargraph/language_server/transport.rb +7 -0
  45. data/lib/solargraph/language_server/transport/socket.rb +66 -0
  46. data/lib/solargraph/language_server/uri_helpers.rb +21 -0
  47. data/lib/solargraph/library.rb +225 -0
  48. data/lib/solargraph/live_map.rb +1 -1
  49. data/lib/solargraph/page.rb +61 -0
  50. data/lib/solargraph/pin.rb +7 -0
  51. data/lib/solargraph/pin/attribute.rb +9 -0
  52. data/lib/solargraph/pin/base.rb +76 -6
  53. data/lib/solargraph/pin/base_variable.rb +29 -7
  54. data/lib/solargraph/pin/block_parameter.rb +53 -0
  55. data/lib/solargraph/pin/constant.rb +6 -2
  56. data/lib/solargraph/pin/conversions.rb +65 -0
  57. data/lib/solargraph/pin/directed/attribute.rb +4 -0
  58. data/lib/solargraph/pin/directed/method.rb +6 -1
  59. data/lib/solargraph/pin/helper.rb +35 -0
  60. data/lib/solargraph/pin/keyword.rb +22 -0
  61. data/lib/solargraph/pin/local_variable.rb +0 -1
  62. data/lib/solargraph/pin/method.rb +55 -2
  63. data/lib/solargraph/pin/method_parameter.rb +19 -0
  64. data/lib/solargraph/pin/namespace.rb +7 -2
  65. data/lib/solargraph/pin/parameter.rb +23 -0
  66. data/lib/solargraph/pin/plugin/method.rb +3 -2
  67. data/lib/solargraph/pin/yard_object.rb +101 -0
  68. data/lib/solargraph/server.rb +82 -135
  69. data/lib/solargraph/shell.rb +20 -1
  70. data/lib/solargraph/source.rb +709 -0
  71. data/lib/solargraph/source/flawed_builder.rb +10 -0
  72. data/lib/solargraph/source/fragment.rb +319 -0
  73. data/lib/solargraph/source/position.rb +26 -0
  74. data/lib/solargraph/source/range.rb +39 -0
  75. data/lib/solargraph/suggestion.rb +29 -4
  76. data/lib/solargraph/version.rb +1 -1
  77. data/lib/solargraph/workspace.rb +105 -0
  78. data/lib/solargraph/{api_map → workspace}/config.rb +1 -1
  79. data/lib/solargraph/yard_map.rb +59 -37
  80. metadata +168 -5
  81. data/lib/solargraph/api_map/source.rb +0 -470
  82. data/lib/solargraph/code_map.rb +0 -868
@@ -0,0 +1,16 @@
1
+ module Solargraph
2
+ class ApiMap
3
+ class Completion
4
+ # @return [Array<Solargraph::Pin::Base>]
5
+ attr_reader :pins
6
+
7
+ # @return [Solargraph::Source::Range]
8
+ attr_reader :range
9
+
10
+ def initialize pins, range
11
+ @pins = pins
12
+ @range = range
13
+ end
14
+ end
15
+ end
16
+ end
@@ -13,7 +13,7 @@ module Solargraph
13
13
  code_object_map.keys
14
14
  end
15
15
 
16
- # @param sources [Array<Solargraph::ApiMap::Source>] Sources for code objects
16
+ # @param sources [Array<Solargraph::Source>] Sources for code objects
17
17
  def rake_yard sources
18
18
  code_object_map.clear
19
19
  sources.each do |s|
@@ -28,7 +28,7 @@ module Solargraph
28
28
  end
29
29
  s.namespace_includes.each_pair do |n, i|
30
30
  i.each do |inc|
31
- code_object_map[n].instance_mixins.push code_object_map[inc] unless code_object_map[inc].nil?
31
+ code_object_map[n].instance_mixins.push code_object_map[inc] unless code_object_map[inc].nil? or code_object_map[n].nil?
32
32
  end
33
33
  end
34
34
  s.attribute_pins.each do |pin|
@@ -0,0 +1,12 @@
1
+ require 'solargraph/language_server/error_codes'
2
+ require 'solargraph/language_server/completion_item_kinds'
3
+ require 'solargraph/language_server/symbol_kinds'
4
+
5
+ module Solargraph
6
+ module LanguageServer
7
+ autoload :Host, 'solargraph/language_server/host'
8
+ autoload :Message, 'solargraph/language_server/message'
9
+ autoload :Transport, 'solargraph/language_server/transport'
10
+ autoload :UriHelpers, 'solargraph/language_server/uri_helpers'
11
+ end
12
+ end
@@ -0,0 +1,31 @@
1
+ module Solargraph
2
+ module LanguageServer
3
+ module CompletionItemKinds
4
+ TEXT = 1
5
+ METHOD = 2
6
+ FUNCTION = 3
7
+ CONSTRUCTOR = 4
8
+ FIELD = 5
9
+ VARIABLE = 6
10
+ CLASS = 7
11
+ INTERFACE = 8
12
+ MODULE = 9
13
+ PROPERTY = 10
14
+ UNIT = 11
15
+ VALUE = 12
16
+ ENUM = 13
17
+ KEYWORD = 14
18
+ SNIPPET = 15
19
+ COLOR = 16
20
+ FILE = 17
21
+ REFERENCE = 18
22
+ FOLDER = 19
23
+ ENUM_MEMBER = 20
24
+ CONSTANT = 21
25
+ STRUCT = 22
26
+ EVENT = 23
27
+ OPERATOR = 24
28
+ TYPE_PARAMETER = 25
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,16 @@
1
+ module Solargraph
2
+ module LanguageServer
3
+ module ErrorCodes
4
+ PARSE_ERROR = -32700
5
+ INVALID_REQUEST = -32600
6
+ METHOD_NOT_FOUND = -32601
7
+ INVALID_PARAMS = -32602
8
+ INTERNAL_ERROR = -32603
9
+ SERVER_ERROR_START = -32099
10
+ SERVER_ERROR_END = -32000
11
+ SERVER_NOT_INITIALIZED = -32002
12
+ UNKNOWN_ERROR_CODE = -32001
13
+ REQUEST_CANCELLED = -32800
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,305 @@
1
+ require 'open3'
2
+ require 'shellwords'
3
+ # require 'rubocop'
4
+ require 'thread'
5
+ require 'set'
6
+
7
+ module Solargraph
8
+ module LanguageServer
9
+ # The language server protocol's data provider. Hosts are responsible for
10
+ # querying the library and processing messages.
11
+ #
12
+ class Host
13
+ include Solargraph::LanguageServer::UriHelpers
14
+
15
+ # @return [Solargraph::Library]
16
+ attr_reader :library
17
+
18
+ def initialize
19
+ @change_semaphore = Mutex.new
20
+ @buffer_semaphore = Mutex.new
21
+ @change_queue = []
22
+ @diagnostics_queue = []
23
+ @cancel = []
24
+ @buffer = ''
25
+ @stopped = false
26
+ @library = nil # @todo How to initialize the library
27
+ start_change_thread
28
+ start_diagnostics_thread
29
+ end
30
+
31
+ # @param update [Hash]
32
+ def configure update
33
+ options.merge! update
34
+ end
35
+
36
+ # @return [Hash]
37
+ def options
38
+ @options ||= {}
39
+ end
40
+
41
+ def cancel id
42
+ @cancel.push id
43
+ end
44
+
45
+ def cancel? id
46
+ @cancel.include? id
47
+ end
48
+
49
+ def clear id
50
+ @cancel.delete id
51
+ end
52
+
53
+ def start request
54
+ message = Message.select(request['method']).new(self, request)
55
+ begin
56
+ message.process
57
+ rescue Exception => e
58
+ STDERR.puts e.message
59
+ STDERR.puts e.backtrace
60
+ message.set_error Solargraph::LanguageServer::ErrorCodes::INTERNAL_ERROR, e.message
61
+ end
62
+ message
63
+ end
64
+
65
+ def create uri
66
+ filename = uri_to_file(uri)
67
+ library.create filename, File.read(filename)
68
+ end
69
+
70
+ def delete uri
71
+ filename = uri_to_file(uri)
72
+ library.delete filename
73
+ end
74
+
75
+ def open uri, text, version
76
+ library.open uri_to_file(uri), text, version
77
+ @change_semaphore.synchronize { @diagnostics_queue.push uri }
78
+ end
79
+
80
+ def change params
81
+ @change_semaphore.synchronize do
82
+ if changing? params['textDocument']['uri']
83
+ @change_queue.push params
84
+ else
85
+ source = library.checkout(uri_to_file(params['textDocument']['uri']))
86
+ @change_queue.push params
87
+ if params['textDocument']['version'] == source.version + params['contentChanges'].length
88
+ source.synchronize(params['contentChanges'], params['textDocument']['version'])
89
+ library.refresh
90
+ @change_queue.pop
91
+ @diagnostics_queue.push params['textDocument']['uri']
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ def queue message
98
+ @buffer_semaphore.synchronize do
99
+ @buffer += message
100
+ end
101
+ end
102
+
103
+ def flush
104
+ tmp = nil
105
+ @buffer_semaphore.synchronize do
106
+ tmp = @buffer.clone
107
+ @buffer.clear
108
+ end
109
+ tmp
110
+ end
111
+
112
+ # @param directory [String]
113
+ def prepare directory
114
+ path = nil
115
+ path = normalize_separators(directory) unless directory.nil?
116
+ @change_semaphore.synchronize do
117
+ @library = Solargraph::Library.load(path)
118
+ end
119
+ end
120
+
121
+ def send_notification method, params
122
+ response = {
123
+ jsonrpc: "2.0",
124
+ method: method,
125
+ params: params
126
+ }
127
+ json = response.to_json
128
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
129
+ queue envelope
130
+ end
131
+
132
+ def changing? file_uri
133
+ @change_queue.any?{|change| change['textDocument']['uri'] == file_uri}
134
+ end
135
+
136
+ def stop
137
+ @stopped = true
138
+ end
139
+
140
+ def stopped?
141
+ @stopped
142
+ end
143
+
144
+ def synchronize &block
145
+ @change_semaphore.synchronize do
146
+ block.call
147
+ end
148
+ end
149
+
150
+ def locate_pin params
151
+ pin = nil
152
+ @change_semaphore.synchronize do
153
+ pin = library.locate_pin(params['data']['location']) unless params['data']['location'].nil?
154
+ # @todo Improve pin location
155
+ if pin.nil? or pin.path != params['data']['path']
156
+ pin = library.path_pins(params['data']['path']).first
157
+ end
158
+ end
159
+ pin
160
+ end
161
+
162
+ def read_text uri
163
+ filename = uri_to_file(uri)
164
+ library.read_text(filename)
165
+ end
166
+
167
+ private
168
+
169
+ def start_change_thread
170
+ Thread.new do
171
+ until stopped?
172
+ @change_semaphore.synchronize do
173
+ begin
174
+ changed = false
175
+ @change_queue.delete_if do |change|
176
+ filename = uri_to_file(change['textDocument']['uri'])
177
+ source = library.checkout(filename)
178
+ if change['textDocument']['version'] == source.version + change['contentChanges'].length
179
+ source.synchronize(change['contentChanges'], change['textDocument']['version'])
180
+ @diagnostics_queue.push change['textDocument']['uri']
181
+ changed = true
182
+ true
183
+ elsif change['textDocument']['version'] == source.version + 1 #and change['contentChanges'].length == 0
184
+ # HACK: This condition fixes the fact that formatting
185
+ # increments the version by one regardless of the number
186
+ # of changes
187
+ source.synchronize(change['contentChanges'], change['textDocument']['version'])
188
+ @diagnostics_queue.push change['textDocument']['uri']
189
+ true
190
+ elsif change['textDocument']['version'] <= source.version
191
+ # @todo Is deleting outdated changes correct behavior?
192
+ STDERR.puts "Deleting stale change"
193
+ @diagnostics_queue.push change['textDocument']['uri']
194
+ changed = true
195
+ true
196
+ else
197
+ # @todo Change is out of order. Save it for later
198
+ STDERR.puts "Kept in queue: #{change['textDocument']['uri']} from #{source.version} to #{change['textDocument']['version']}"
199
+ false
200
+ end
201
+ end
202
+ STDERR.puts "#{@change_queue.length} pending" unless @change_queue.empty?
203
+ library.refresh if changed
204
+ rescue Exception => e
205
+ STDERR.puts e.message
206
+ end
207
+ end
208
+ sleep 0.1
209
+ end
210
+ end
211
+ end
212
+
213
+ def start_diagnostics_thread
214
+ Thread.new do
215
+ until stopped?
216
+ if options['diagnostics'] != 'rubocop'
217
+ @change_semaphore.synchronize { @diagnostics_queue.clear }
218
+ sleep 1
219
+ next
220
+ end
221
+ unless @change_semaphore.locked?
222
+ begin
223
+ current = nil
224
+ @change_semaphore.synchronize do
225
+ current = @diagnostics_queue.shift
226
+ end
227
+ unless current.nil?
228
+ already_changing = false
229
+ @change_semaphore.synchronize { already_changing = (changing?(current) or @diagnostics_queue.include?(current)) }
230
+ unless already_changing
231
+ resp = read_diagnostics(current)
232
+ @change_semaphore.synchronize { already_changing = (changing?(current) or @diagnostics_queue.include?(current)) }
233
+ publish_diagnostics current, resp unless already_changing
234
+ end
235
+ end
236
+ rescue Exception => e
237
+ STDERR.puts e.message
238
+ end
239
+ end
240
+ sleep 0.1
241
+ end
242
+ end
243
+ end
244
+
245
+ def normalize_separators path
246
+ return path if File::ALT_SEPARATOR.nil?
247
+ path.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
248
+ end
249
+
250
+ def read_diagnostics uri
251
+ begin
252
+ filename = nil
253
+ text = nil
254
+ @change_semaphore.synchronize do
255
+ filename = uri_to_file(uri)
256
+ text = library.read_text(filename)
257
+ end
258
+ cmd = "rubocop -f j -s #{Shellwords.escape(filename)}"
259
+ o, e, s = Open3.capture3(cmd, stdin_data: text)
260
+ JSON.parse(o)
261
+ rescue Exception => e
262
+ STDERR.puts "#{e}"
263
+ STDERR.puts "#{e.backtrace}"
264
+ nil
265
+ end
266
+ end
267
+
268
+ def publish_diagnostics uri, resp
269
+ severities = {
270
+ 'refactor' => 4,
271
+ 'convention' => 3,
272
+ 'warning' => 2,
273
+ 'error' => 1,
274
+ 'fatal' => 1
275
+ }
276
+ diagnostics = []
277
+ resp['files'].each do |file|
278
+ file['offenses'].each do |off|
279
+ diag = {
280
+ range: {
281
+ start: {
282
+ line: off['location']['start_line'] - 1,
283
+ character: off['location']['start_column'] - 1
284
+ },
285
+ end: {
286
+ line: off['location']['last_line'] - 1,
287
+ character: off['location']['last_column']
288
+ }
289
+ },
290
+ # 1 = Error, 2 = Warning, 3 = Information, 4 = Hint
291
+ severity: severities[off['severity']],
292
+ source: off['cop_name'],
293
+ message: off['message'].gsub(/^#{off['cop_name']}\:/, '')
294
+ }
295
+ diagnostics.push diag
296
+ end
297
+ end
298
+ send_notification "textDocument/publishDiagnostics", {
299
+ uri: uri,
300
+ diagnostics: diagnostics
301
+ }
302
+ end
303
+ end
304
+ end
305
+ end
@@ -0,0 +1,70 @@
1
+ require 'solargraph'
2
+ require 'uri'
3
+ require 'thread'
4
+
5
+ module Solargraph
6
+ module LanguageServer
7
+ module Message
8
+ autoload :Base, 'solargraph/language_server/message/base'
9
+ autoload :Initialize, 'solargraph/language_server/message/initialize'
10
+ autoload :Initialized, 'solargraph/language_server/message/initialized'
11
+ autoload :TextDocument, 'solargraph/language_server/message/text_document'
12
+ autoload :CompletionItem, 'solargraph/language_server/message/completion_item'
13
+ autoload :CancelRequest, 'solargraph/language_server/message/cancel_request'
14
+ autoload :MethodNotFound, 'solargraph/language_server/message/method_not_found'
15
+ autoload :MethodNotImplemented, 'solargraph/language_server/message/method_not_implemented'
16
+ autoload :Extended, 'solargraph/language_server/message/extended'
17
+ autoload :Shutdown, 'solargraph/language_server/message/shutdown'
18
+ autoload :ExitNotification, 'solargraph/language_server/message/exit_notification'
19
+ autoload :Workspace, 'solargraph/language_server/message/workspace'
20
+
21
+ class << self
22
+ def register path, message_class
23
+ method_map[path] = message_class
24
+ end
25
+
26
+ # @param path [String]
27
+ # @return [Solargraph::LanguageServer::Message::Base]
28
+ def select path
29
+ if method_map.has_key?(path)
30
+ method_map[path]
31
+ elsif path.start_with?('$/')
32
+ MethodNotImplemented
33
+ else
34
+ STDERR.puts "Method not found: #{path}"
35
+ MethodNotFound
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def method_map
42
+ @method_map ||= {}
43
+ end
44
+ end
45
+
46
+ register 'initialize', Initialize
47
+ register 'initialized', Initialized
48
+ register 'textDocument/completion', TextDocument::Completion
49
+ register 'completionItem/resolve', CompletionItem::Resolve
50
+ register 'textDocument/signatureHelp', TextDocument::SignatureHelp
51
+ register 'textDocument/didOpen', TextDocument::DidOpen
52
+ register 'textDocument/didChange', TextDocument::DidChange
53
+ register 'textDocument/didSave', TextDocument::DidSave
54
+ register 'textDocument/didClose', TextDocument::DidClose
55
+ register 'textDocument/hover', TextDocument::Hover
56
+ register 'textDocument/definition', TextDocument::Definition
57
+ register 'textDocument/formatting', TextDocument::Formatting
58
+ register 'textDocument/onTypeFormatting', TextDocument::OnTypeFormatting
59
+ register 'textDocument/documentSymbol', TextDocument::DocumentSymbol
60
+ register 'workspace/didChangeWatchedFiles', Workspace::DidChangeWatchedFiles
61
+ register 'workspace/didChangeConfiguration', Workspace::DidChangeConfiguration
62
+ register 'workspace/symbol', Workspace::WorkspaceSymbol
63
+ register '$/cancelRequest', CancelRequest
64
+ register '$/solargraph/document', Extended::Document
65
+ register '$/solargraph/search', Extended::Search
66
+ register 'shutdown', Shutdown
67
+ register 'exit', ExitNotification
68
+ end
69
+ end
70
+ end