solargraph 0.22.0 → 0.23.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph/api_map.rb +17 -2
  3. data/lib/solargraph/api_map/probe.rb +3 -0
  4. data/lib/solargraph/api_map/store.rb +21 -1
  5. data/lib/solargraph/diagnostics/require_not_found.rb +0 -2
  6. data/lib/solargraph/diagnostics/rubocop.rb +23 -11
  7. data/lib/solargraph/language_server/host.rb +79 -14
  8. data/lib/solargraph/language_server/message/initialize.rb +49 -44
  9. data/lib/solargraph/language_server/message/initialized.rb +11 -7
  10. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +5 -3
  11. data/lib/solargraph/language_server/message/text_document/hover.rb +2 -0
  12. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +16 -16
  13. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +3 -2
  14. data/lib/solargraph/language_server/transport.rb +2 -1
  15. data/lib/solargraph/language_server/transport/socket.rb +0 -3
  16. data/lib/solargraph/language_server/transport/stdio.rb +66 -0
  17. data/lib/solargraph/pin/attribute.rb +1 -1
  18. data/lib/solargraph/pin/base.rb +5 -0
  19. data/lib/solargraph/pin/base_variable.rb +5 -0
  20. data/lib/solargraph/pin/constant.rb +5 -0
  21. data/lib/solargraph/pin/conversions.rb +26 -33
  22. data/lib/solargraph/pin/localized.rb +1 -1
  23. data/lib/solargraph/pin/method.rb +5 -0
  24. data/lib/solargraph/pin/namespace.rb +5 -0
  25. data/lib/solargraph/shell.rb +14 -0
  26. data/lib/solargraph/source/fragment.rb +12 -0
  27. data/lib/solargraph/version.rb +1 -1
  28. data/lib/solargraph/workspace/config.rb +2 -4
  29. data/lib/solargraph/yard_map.rb +4 -0
  30. data/lib/solargraph/yard_map/cache.rb +8 -0
  31. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c647321bb57cb5ba859e1762032855324aa967e78a562c0e259508e5c24c22ee
4
- data.tar.gz: 057c1a4298f9c4574c0773425cce496216ab60e22165871487d6d77e13733745
3
+ metadata.gz: 6e4fd079cd97cdd7650d3040d3a9182451f280e8e89c1de91f82d2092dbef840
4
+ data.tar.gz: '0516619d1947a312eebb8b1caa535c4194bed4a308b4c68467af7d55c705119a'
5
5
  SHA512:
6
- metadata.gz: ae8cb69f92e2f3ad78959bd8767b78dda5a7d4baa861fa59680be191b7c92944f0df15ff12ccad2ed4a93231afcc55db53bd40cf9b42473387491f4d168f1cef
7
- data.tar.gz: 8d02f2a10b1a71579a0b48e9dae4268689cc0ec642c137b1adedf6fc99749ae5a1b04f46b4d9b30448d3316a71b65dfdf79b5066c0af499e2181fcc5a107201f
6
+ metadata.gz: 4b290e1ed7ecf9b59933abef586de329903c6c9f06d61b233dc71b817b75f7a4e4cae1f246803965779eac8b9eed1107e27263e63a039a282e2ede3da2f2260d
7
+ data.tar.gz: 7b29c6d72ce773d6ea9a55a5ae9d23398fbe1d4ef9ace42bab1b29723e8c61171bbae1c12900fd4e64f2e1b17193519b4eab202e39ab8428e3a24d691c8c17af
@@ -46,10 +46,12 @@ module Solargraph
46
46
  self.new(Solargraph::Workspace.new(directory))
47
47
  end
48
48
 
49
+ # @return [Array<Solargraph::Pin::Base>]
49
50
  def pins
50
51
  store.pins
51
52
  end
52
53
 
54
+ # @return [Array<String>]
53
55
  def domains
54
56
  @domains ||= []
55
57
  end
@@ -244,6 +246,13 @@ module Solargraph
244
246
  globals
245
247
  end
246
248
 
249
+ # Get an array of methods available in a particular context.
250
+ #
251
+ # @param fqns [String] The fully qualified namespace to search for methods
252
+ # @param scope [Symbol] :class or :instance
253
+ # @param visibility [Array<Symbol>] :public, :protected, and/or :private
254
+ # @param deep [Boolean] True to include superclasses, mixins, etc.
255
+ # @return [Array<Solargraph::Pin::Base>]
247
256
  def get_methods fqns, scope: :instance, visibility: [:public], deep: true
248
257
  result = []
249
258
  skip = []
@@ -266,6 +275,10 @@ module Solargraph
266
275
  result
267
276
  end
268
277
 
278
+ # Get a set of available completions for the specified fragment. The
279
+ # resulting Completion object contains an array of pins and the range of
280
+ # text to replace in the source.
281
+ #
269
282
  # @param fragment [Solargraph::Source::Fragment]
270
283
  # @return [ApiMap::Completion]
271
284
  def complete fragment
@@ -318,10 +331,12 @@ module Solargraph
318
331
  Completion.new(filtered, fragment.whole_word_range)
319
332
  end
320
333
 
334
+ # Get an array of pins that describe the symbol at the specified fragment.
335
+ #
321
336
  # @param fragment [Solargraph::Source::Fragment]
322
337
  # @return [Array<Solargraph::Pin::Base>]
323
338
  def define fragment
324
- return [] if fragment.string? or fragment.comment?
339
+ return [] if fragment.string? or fragment.comment? or fragment.literal?
325
340
  if fragment.base_literal?
326
341
  probe.infer_signature_pins fragment.whole_signature, Pin::ProxyMethod.new(fragment.base_literal), fragment.locals
327
342
  else
@@ -421,11 +436,11 @@ module Solargraph
421
436
  result
422
437
  end
423
438
 
439
+ # @return [Solargraph::Pin::Base]
424
440
  def locate_pin location
425
441
  @sources.each do |source|
426
442
  pin = source.locate_pin(location)
427
443
  unless pin.nil?
428
- # pin.resolve self
429
444
  return pin
430
445
  end
431
446
  end
@@ -148,6 +148,9 @@ module Solargraph
148
148
  pin
149
149
  end
150
150
 
151
+ # @param pin [Solargraph::Pin::Base]
152
+ # @param locals [Array<Solargraph::Pin::Base>]
153
+ # @return [String]
151
154
  def resolve_pin_type pin, locals
152
155
  pin.return_type
153
156
  return pin.return_type unless pin.return_type.nil?
@@ -3,12 +3,13 @@ require 'set'
3
3
  module Solargraph
4
4
  class ApiMap
5
5
  class Store
6
- # @param sources [Solargraph::Source]
6
+ # @param sources [Array<Solargraph::Source>]
7
7
  def initialize sources
8
8
  update *sources
9
9
  index
10
10
  end
11
11
 
12
+ # @return [Array<Solargraph::Pin::Base>]
12
13
  def pins
13
14
  @pins ||= []
14
15
  end
@@ -31,6 +32,7 @@ module Solargraph
31
32
  index
32
33
  end
33
34
 
35
+ # @return [Array<Solargraph::Pin::Base>]
34
36
  def get_constants fqns, visibility = [:public]
35
37
  namespace_pins(fqns).select { |pin|
36
38
  !pin.name.empty? and (pin.kind == Pin::NAMESPACE or pin.kind == Pin::CONSTANT) and visibility.include?(pin.visibility)
@@ -43,10 +45,13 @@ module Solargraph
43
45
  }
44
46
  end
45
47
 
48
+ # @return [Array<Solargraph::Pin::Base>]
46
49
  def get_attrs fqns, scope
47
50
  namespace_pins(fqns).select{ |pin| pin.kind == Pin::ATTRIBUTE and pin.scope == scope }
48
51
  end
49
52
 
53
+ # @param fqns [String]
54
+ # @return [String]
50
55
  def get_superclass fqns
51
56
  fqns_pins(fqns).each do |pin|
52
57
  return pin.superclass_reference.name unless pin.superclass_reference.nil?
@@ -54,6 +59,8 @@ module Solargraph
54
59
  nil
55
60
  end
56
61
 
62
+ # @param fqns [String]
63
+ # @return [Array<String>]
57
64
  def get_includes fqns
58
65
  result = []
59
66
  fqns_pins(fqns).each do |pin|
@@ -62,6 +69,8 @@ module Solargraph
62
69
  result
63
70
  end
64
71
 
72
+ # @param fqns [String]
73
+ # @return [Array<String>]
65
74
  def get_extends fqns
66
75
  result = []
67
76
  fqns_pins(fqns).each do |pin|
@@ -70,28 +79,39 @@ module Solargraph
70
79
  result
71
80
  end
72
81
 
82
+ # @param path [String]
83
+ # @return [Array<Solargraph::Pin::Base>]
73
84
  def get_path_pins path
74
85
  base = path.sub(/(#|\.|::)[a-z0-9_]*(\?|\!)?$/i, '')
75
86
  base = '' if base == path
76
87
  namespace_pins(base).select{ |pin| pin.path == path }
77
88
  end
78
89
 
90
+ # @param fqns [String]
91
+ # @param scope [Symbol] :class or :instance
92
+ # @return [Array<Solargraph::Pin::Base>]
79
93
  def get_instance_variables(fqns, scope = :instance)
80
94
  namespace_pins(fqns).select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.scope == scope}
81
95
  end
82
96
 
97
+ # @param fqns [String]
98
+ # @return [Array<Solargraph::Pin::Base>]
83
99
  def get_class_variables(fqns)
84
100
  namespace_pins(fqns).select{|pin| pin.kind == Pin::CLASS_VARIABLE}
85
101
  end
86
102
 
103
+ # @return [Array<Solargraph::Pin::Base>]
87
104
  def get_symbols
88
105
  symbols.uniq(&:name)
89
106
  end
90
107
 
108
+ # @param fqns [String]
109
+ # @return [Boolean]
91
110
  def namespace_exists?(fqns)
92
111
  fqns_pins(fqns).any?
93
112
  end
94
113
 
114
+ # @return [Set<String>]
95
115
  def namespaces
96
116
  @namespaces ||= Set.new
97
117
  end
@@ -3,8 +3,6 @@ module Solargraph
3
3
  # RequireNotFound reports required paths that could not be resolved to
4
4
  # either a file in the workspace or a gem.
5
5
  #
6
- # @todo Some stdlib paths can result in false positives.
7
- #
8
6
  class RequireNotFound < Base
9
7
  def diagnose source, api_map
10
8
  result = []
@@ -6,6 +6,15 @@ module Solargraph
6
6
  # This reporter provides linting through RuboCop.
7
7
  #
8
8
  class Rubocop < Base
9
+ # Conversion of RuboCop severity names to LSP constants
10
+ SEVERITIES = {
11
+ 'refactor' => 4,
12
+ 'convention' => 3,
13
+ 'warning' => 2,
14
+ 'error' => 1,
15
+ 'fatal' => 1
16
+ }
17
+
9
18
  # The rubocop command
10
19
  #
11
20
  # @return [String]
@@ -23,11 +32,12 @@ module Solargraph
23
32
  text = source.code
24
33
  filename = source.filename
25
34
  raise DiagnosticsError, 'No command specified' if command.nil? or command.empty?
26
- cmd = "#{Shellwords.escape(command)} -f j -s #{Shellwords.escape(filename)}"
35
+ cmd = "#{Shellwords.escape(command)} -f j"
27
36
  unless api_map.workspace.nil? or api_map.workspace.directory.nil?
28
37
  rc = File.join(api_map.workspace.directory, '.rubocop.yml')
29
- cmd += " -c #{Shellwords.escape(rc)}" if File.file?(rc)
38
+ cmd += " -c #{Shellwords.escape(fix_drive_letter(rc))}" if File.file?(rc)
30
39
  end
40
+ cmd += " -s #{Shellwords.escape(fix_drive_letter(filename))}"
31
41
  o, e, s = Open3.capture3(cmd, stdin_data: text)
32
42
  STDERR.puts e unless e.empty?
33
43
  raise DiagnosticsError, "Command '#{command}' is not available (gem exception)" if e.include?('Gem::Exception')
@@ -43,14 +53,6 @@ module Solargraph
43
53
  private
44
54
 
45
55
  def make_array resp
46
- # Conversion of RuboCop severity names to LSP constants
47
- severities = {
48
- 'refactor' => 4,
49
- 'convention' => 3,
50
- 'warning' => 2,
51
- 'error' => 1,
52
- 'fatal' => 1
53
- }
54
56
  diagnostics = []
55
57
  resp['files'].each do |file|
56
58
  file['offenses'].each do |off|
@@ -73,7 +75,7 @@ module Solargraph
73
75
  }
74
76
  },
75
77
  # 1 = Error, 2 = Warning, 3 = Information, 4 = Hint
76
- severity: severities[off['severity']],
78
+ severity: SEVERITIES[off['severity']],
77
79
  source: off['cop_name'],
78
80
  message: off['message'].gsub(/^#{off['cop_name']}\:/, '')
79
81
  }
@@ -82,6 +84,16 @@ module Solargraph
82
84
  end
83
85
  diagnostics
84
86
  end
87
+
88
+ # RuboCop internally uses capitalized drive letters for Windows paths,
89
+ # so we need to convert the paths provided to the command.
90
+ #
91
+ # @param path [String]
92
+ # @return [String]
93
+ def fix_drive_letter path
94
+ return path unless path.match(/^[a-z]:/)
95
+ path[0].upcase + path[1..-1]
96
+ end
85
97
  end
86
98
  end
87
99
  end
@@ -14,12 +14,15 @@ module Solargraph
14
14
  @change_semaphore = Mutex.new
15
15
  @cancel_semaphore = Mutex.new
16
16
  @buffer_semaphore = Mutex.new
17
+ @register_semaphore = Mutex.new
17
18
  @change_queue = []
18
19
  @diagnostics_queue = []
19
20
  @cancel = []
20
21
  @buffer = ''
21
22
  @stopped = false
22
23
  @next_request_id = 0
24
+ @dynamic_capabilities = Set.new
25
+ @registered_capabilities = []
23
26
  start_change_thread
24
27
  start_diagnostics_thread
25
28
  end
@@ -28,7 +31,8 @@ module Solargraph
28
31
  #
29
32
  # @param update [Hash]
30
33
  def configure update
31
- options.merge! update unless update.nil?
34
+ return if update.nil?
35
+ options.merge! update
32
36
  end
33
37
 
34
38
  # @return [Hash]
@@ -94,6 +98,11 @@ module Solargraph
94
98
  @change_semaphore.synchronize do
95
99
  filename = uri_to_file(uri)
96
100
  library.delete filename
101
+ # Remove diagnostics for deleted files
102
+ send_notification "textDocument/publishDiagnostics", {
103
+ uri: uri,
104
+ diagnostics: []
105
+ }
97
106
  end
98
107
  end
99
108
 
@@ -235,27 +244,64 @@ module Solargraph
235
244
  @next_request_id += 1
236
245
  end
237
246
 
247
+ # Register the methods as capabilities with the client.
248
+ # This method will avoid duplicating registrations and ignore methods
249
+ # that were not flagged for dynamic registration by the client.
250
+ #
251
+ # @param methods [Array<String>] The methods to register
238
252
  def register_capabilities methods
239
- send_request 'client/registerCapability', {
240
- registrations: methods.map { |m|
241
- {
242
- id: m,
243
- method: m,
244
- registerOptions: dynamic_capability_options[m]
253
+ @register_semaphore.synchronize do
254
+ send_request 'client/registerCapability', {
255
+ registrations: methods.reject{|m| @dynamic_capabilities.include?(m) and @registered_capabilities.include?(m)}.map { |m|
256
+ @registered_capabilities.push m
257
+ {
258
+ id: m,
259
+ method: m,
260
+ registerOptions: dynamic_capability_options[m]
261
+ }
245
262
  }
246
263
  }
247
- }
264
+ end
248
265
  end
249
266
 
267
+ # Unregister the methods with the client.
268
+ # This method will avoid duplicating unregistrations and ignore methods
269
+ # that were not flagged for dynamic registration by the client.
270
+ #
271
+ # @param methods [Array<String>] The methods to unregister
250
272
  def unregister_capabilities methods
251
- send_request 'client/unregisterCapability', {
252
- unregisterations: methods.map { |m|
253
- {
254
- id: m,
255
- method: m
273
+ @register_semaphore.synchronize do
274
+ send_request 'client/unregisterCapability', {
275
+ unregisterations: methods.select{|m| @registered_capabilities.include?(m)}.map{ |m|
276
+ @registered_capabilities.delete m
277
+ {
278
+ id: m,
279
+ method: m
280
+ }
256
281
  }
257
282
  }
258
- }
283
+ end
284
+ end
285
+
286
+ # Flag a method as available for dynamic registration.
287
+ #
288
+ # @param method [String] The method name, e.g., 'textDocument/completion'
289
+ def allow_registration method
290
+ @register_semaphore.synchronize do
291
+ @dynamic_capabilities.add method
292
+ end
293
+ end
294
+
295
+ # True if the specified method has been registered.
296
+ #
297
+ # @param method [String] The method name, e.g., 'textDocument/completion'
298
+ # @return [Boolean]
299
+ def registered? method
300
+ result = nil
301
+ @register_semaphore.synchronize do
302
+ result = @registered_capabilities.include?(method)
303
+ end
304
+ result
259
305
  end
260
306
 
261
307
  # True if the specified file is in the process of changing.
@@ -380,6 +426,10 @@ module Solargraph
380
426
  {
381
427
  'completion' => true,
382
428
  'hover' => true,
429
+ 'symbols' => true,
430
+ 'definitions' => true,
431
+ 'rename' => true,
432
+ 'references' => true,
383
433
  'autoformat' => false,
384
434
  'diagnostics' => false,
385
435
  'formatting' => false
@@ -545,6 +595,21 @@ module Solargraph
545
595
  # changeNotifications: true
546
596
  # }
547
597
  # }
598
+ 'textDocument/definition' => {
599
+ definitionProvider: true
600
+ },
601
+ 'textDocument/references' => {
602
+ referencesProvider: true
603
+ },
604
+ 'textDocument/rename' => {
605
+ renameProvider: true
606
+ },
607
+ 'textDocument/documentSymbol' => {
608
+ documentSymbolProvider: true
609
+ },
610
+ 'workspace/workspaceSymbol' => {
611
+ workspaceSymbolProvider: true
612
+ }
548
613
  }
549
614
  end
550
615
  end
@@ -8,24 +8,24 @@ module Solargraph
8
8
  result = {
9
9
  capabilities: {
10
10
  textDocumentSync: 2, # @todo What should this be?
11
- definitionProvider: true,
12
- documentSymbolProvider: true,
13
- workspaceSymbolProvider: true,
14
- renameProvider: true,
15
11
  workspace: {
16
12
  workspaceFolders: {
17
13
  supported: true,
18
14
  changeNotifications: true
19
15
  }
20
- },
21
- referencesProvider: true
16
+ }
22
17
  }
23
18
  }
24
- result[:capabilities].merge! static_completion unless dynamic_completion?
25
- result[:capabilities].merge! static_signature_help unless dynamic_signature_help?
26
- result[:capabilities].merge! static_on_type_formatting unless dynamic_on_type_formatting?
27
- result[:capabilities].merge! static_hover unless dynamic_hover?
28
- result[:capabilities].merge! static_document_formatting unless dynamic_document_formatting?
19
+ result[:capabilities].merge! static_completion unless dynamic_registration_for?('textDocument', 'completion')
20
+ result[:capabilities].merge! static_signature_help unless dynamic_registration_for?('textDocument', 'signatureHelp')
21
+ # result[:capabilities].merge! static_on_type_formatting unless dynamic_registration_for?('textDocument', 'onTypeFormatting')
22
+ result[:capabilities].merge! static_hover unless dynamic_registration_for?('textDocument', 'hover')
23
+ result[:capabilities].merge! static_document_formatting unless dynamic_registration_for?('textDocument', 'formatting')
24
+ result[:capabilities].merge! static_document_symbols unless dynamic_registration_for?('textDocument', 'documentSymbol')
25
+ result[:capabilities].merge! static_definitions unless dynamic_registration_for?('textDocument', 'definition')
26
+ result[:capabilities].merge! static_rename unless dynamic_registration_for?('textDocument', 'rename')
27
+ result[:capabilities].merge! static_references unless dynamic_registration_for?('textDocument', 'references')
28
+ result[:capabilities].merge! static_workspace_symbols unless dynamic_registration_for?('workspace', 'symbol')
29
29
  set_result result
30
30
  end
31
31
 
@@ -40,13 +40,6 @@ module Solargraph
40
40
  }
41
41
  end
42
42
 
43
- def dynamic_completion?
44
- params['capabilities'] and
45
- params['capabilities']['textDocument'] and
46
- params['capabilities']['textDocument']['completion'] and
47
- params['capabilities']['textDocument']['completion']['dynamicRegistration']
48
- end
49
-
50
43
  def static_signature_help
51
44
  {
52
45
  signatureHelpProvider: {
@@ -55,13 +48,6 @@ module Solargraph
55
48
  }
56
49
  end
57
50
 
58
- def dynamic_signature_help?
59
- params['capabilities'] and
60
- params['capabilities']['textDocument'] and
61
- params['capabilities']['textDocument']['signatureHelp'] and
62
- params['capabilities']['textDocument']['signatureHelp']['dynamicRegistration']
63
- end
64
-
65
51
  def static_on_type_formatting
66
52
  {
67
53
  documentOnTypeFormattingProvider: {
@@ -71,37 +57,56 @@ module Solargraph
71
57
  }
72
58
  end
73
59
 
74
- def dynamic_on_type_formatting?
75
- params['capabilities'] and
76
- params['capabilities']['textDocument'] and
77
- params['capabilities']['textDocument']['onTypeFormatting'] and
78
- params['capabilities']['textDocument']['onTypeFormatting']['dynamicRegistration']
79
- end
80
-
81
60
  def static_hover
82
61
  {
83
62
  hoverProvider: true
84
63
  }
85
64
  end
86
65
 
87
- def dynamic_hover?
88
- params['capabilities'] and
89
- params['capabilities']['textDocument'] and
90
- params['capabilities']['textDocument']['hover'] and
91
- params['capabilities']['textDocument']['hover']['dynamicRegistration']
92
- end
93
-
94
66
  def static_document_formatting
95
67
  {
96
68
  documentFormattingProvider: true
97
69
  }
98
70
  end
99
71
 
100
- def dynamic_document_formatting?
101
- params['capabilities'] and
102
- params['capabilities']['textDocument'] and
103
- params['capabilities']['textDocument']['hover'] and
104
- params['capabilities']['textDocument']['hover']['dynamicRegistration']
72
+ def static_document_symbols
73
+ {
74
+ documentSymbolProvider: true
75
+ }
76
+ end
77
+
78
+ def static_workspace_symbols
79
+ {
80
+ workspaceSymbolProvider: true
81
+ }
82
+ end
83
+
84
+ def static_definitions
85
+ {
86
+ definitionProvider: true
87
+ }
88
+ end
89
+
90
+ def static_rename
91
+ {
92
+ renameProvider: true
93
+ }
94
+ end
95
+
96
+ def static_references
97
+ {
98
+ referencesProvider: true
99
+ }
100
+ end
101
+
102
+ # @return [Boolean]
103
+ def dynamic_registration_for? section, capability
104
+ result = params['capabilities'] and
105
+ params['capabilities'][section] and
106
+ params['capabilities'][section][capability] and
107
+ params['capabilities'][section][capability]['dynamicRegistration']
108
+ host.allow_registration "#{section}/#{capability}" if result
109
+ result
105
110
  end
106
111
  end
107
112
  end
@@ -3,13 +3,17 @@ module Solargraph
3
3
  module Message
4
4
  class Initialized < Base
5
5
  def process
6
- meths = []
7
- meths.push 'textDocument/completion' if host.options['completion']
8
- meths.push 'textDocument/hover' if host.options['hover']
9
- meths.push 'textDocument/signatureHelp' if host.options['hover']
10
- meths.push 'textDocument/onTypeFormatting' if host.options['autoformat']
11
- meths.push 'textDocument/formatting' if host.options['formatting']
12
- host.register_capabilities meths unless meths.empty?
6
+ host.register_capabilities %w[
7
+ textDocument/completion
8
+ textDocument/hover
9
+ textDocument/signatureHelp
10
+ textDocument/formatting
11
+ textDocument/documentSymbol
12
+ textDocument/definition
13
+ textDocument/references
14
+ textDocument/rename
15
+ workspace/workspaceSymbol
16
+ ]
13
17
  end
14
18
  end
15
19
  end
@@ -4,14 +4,16 @@ class Solargraph::LanguageServer::Message::TextDocument::DocumentSymbol < Solarg
4
4
  def process
5
5
  pins = host.file_symbols params['textDocument']['uri']
6
6
  info = pins.map do |pin|
7
- {
8
- name: pin.path,
9
- kind: Solargraph::LanguageServer::SymbolKinds::NAMESPACE,
7
+ result = {
8
+ name: pin.name,
9
+ containerName: pin.namespace,
10
+ kind: pin.symbol_kind,
10
11
  location: {
11
12
  uri: file_to_uri(pin.location.filename),
12
13
  range: pin.location.range.to_hash
13
14
  }
14
15
  }
16
+ result
15
17
  end
16
18
  set_result info
17
19
  end
@@ -1,4 +1,5 @@
1
1
  require 'uri'
2
+ require 'htmlentities'
2
3
 
3
4
  module Solargraph::LanguageServer::Message::TextDocument
4
5
  class Hover < Base
@@ -20,6 +21,7 @@ module Solargraph::LanguageServer::Message::TextDocument
20
21
  if !this_path.nil? and this_path != last_path
21
22
  parts.push link_documentation(this_path)
22
23
  end
24
+ parts.push HTMLEntities.new.encode(pin.detail) unless pin.kind == Solargraph::Pin::NAMESPACE or pin.detail.nil?
23
25
  parts.push pin.documentation unless pin.documentation.nil? or pin.documentation.empty?
24
26
  contents.push parts.join("\n\n") unless parts.empty?
25
27
  last_path = this_path unless this_path.nil?
@@ -4,24 +4,24 @@ module Solargraph::LanguageServer::Message::Workspace
4
4
  class DidChangeConfiguration < Solargraph::LanguageServer::Message::Base
5
5
  def process
6
6
  update = params['settings']['solargraph']
7
+ host.configure update
8
+ register_from_options
9
+ end
7
10
 
8
- meths = []
9
- meths.push 'textDocument/completion' if update.has_key?('completion') and update['completion'] and !host.options['completion']
10
- meths.push 'textDocument/hover' if update.has_key?('hover') and update['hover'] and !host.options['hover']
11
- meths.push 'textDocument/signatureHelp' if update.has_key?('hover') and update['hover'] and !host.options['hover']
12
- meths.push 'textDocument/onTypeFormatting' if update.has_key?('autoformat') and update['autoformat'] and !host.options['autoformat']
13
- meths.push "textDocument/formatting" if update.has_key?('formatting') and update['formatting'] and !host.options['formatting']
14
- host.register_capabilities meths unless meths.empty?
15
-
16
- meths = []
17
- meths.push 'textDocument/completion' if update.has_key?('completion') and !update['completion'] and host.options['completion']
18
- meths.push 'textDocument/hover' if update.has_key?('hover') and !update['hover'] and host.options['hover']
19
- meths.push 'textDocument/signatureHelp' if update.has_key?('hover') and !update['hover'] and host.options['hover']
20
- meths.push 'textDocument/onTypeFormatting' if update.has_key?('autoformat') and !update['autoformat'] and host.options['autoformat']
21
- meths.push "textDocument/formatting" if update.has_key?('formatting') and !update['formatting'] and host.options['formatting']
22
- host.unregister_capabilities meths unless meths.empty?
11
+ private
23
12
 
24
- host.configure update
13
+ def register_from_options
14
+ y = []
15
+ n = []
16
+ (host.options['completion'] ? y : n).push('textDocument/completion')
17
+ (host.options['hover'] ? y : n).push('textDocument/hover', 'textDocument/signatureHelp')
18
+ (host.options['autoformat'] ? y : n).push('textDocument/onTypeFormatting')
19
+ (host.options['formatting'] ? y : n).push('textDocument/formatting')
20
+ (host.options['symbols'] ? y : n).push('textDocument/documentSymbol', 'workspace/workspaceSymbol')
21
+ (host.options['definitions'] ? y : n).push('textDocument/definition')
22
+ (host.options['references'] ? y : n).push('textDocument/references')
23
+ host.register_capabilities y
24
+ host.unregister_capabilities n
25
25
  end
26
26
  end
27
27
  end
@@ -6,8 +6,9 @@ class Solargraph::LanguageServer::Message::Workspace::WorkspaceSymbol < Solargra
6
6
  info = pins.map do |pin|
7
7
  uri = file_to_uri(pin.location.filename)
8
8
  {
9
- name: pin.path,
10
- kind: Solargraph::LanguageServer::SymbolKinds::NAMESPACE,
9
+ name: pin.name,
10
+ containerName: pin.namespace,
11
+ kind: pin.symbol_kind,
11
12
  location: {
12
13
  uri: uri,
13
14
  range: pin.location.range.to_hash
@@ -2,7 +2,8 @@ module Solargraph
2
2
  module LanguageServer
3
3
  module Transport
4
4
  autoload :DataReader, 'solargraph/language_server/transport/data_reader'
5
- autoload :Socket, 'solargraph/language_server/transport/socket'
5
+ autoload :Socket, 'solargraph/language_server/transport/socket'
6
+ autoload :Stdio, 'solargraph/language_server/transport/stdio'
6
7
  end
7
8
  end
8
9
  end
@@ -7,9 +7,6 @@ module Solargraph
7
7
  #
8
8
  module Socket
9
9
  def post_init
10
- @in_header = true
11
- @content_length = 0
12
- @buffer = ''
13
10
  @host = Solargraph::LanguageServer::Host.new
14
11
  @data_reader = Solargraph::LanguageServer::Transport::DataReader.new
15
12
  @data_reader.set_message_handler do |message|
@@ -0,0 +1,66 @@
1
+ require 'thread'
2
+
3
+ module Solargraph
4
+ module LanguageServer
5
+ module Transport
6
+ class Stdio
7
+ def initialize
8
+ # binmode is necessary to avoid EOL conversions
9
+ STDOUT.binmode
10
+ @host = Solargraph::LanguageServer::Host.new
11
+ @data_reader = Solargraph::LanguageServer::Transport::DataReader.new
12
+ @data_reader.set_message_handler do |message|
13
+ process message
14
+ end
15
+ end
16
+
17
+ def run
18
+ start_reader
19
+ start_timers
20
+ end
21
+
22
+ def self.run
23
+ std = Stdio.new
24
+ std.run
25
+ std
26
+ end
27
+
28
+ private
29
+
30
+ def start_reader
31
+ Thread.new do
32
+ until @host.stopped?
33
+ char = STDIN.sysread(1)
34
+ break if char.nil?
35
+ @data_reader.receive char
36
+ STDIN.flush
37
+ end
38
+ end
39
+ end
40
+
41
+ def send_data message
42
+ STDOUT.write message
43
+ STDOUT.flush
44
+ end
45
+
46
+ def process request
47
+ Thread.new do
48
+ message = @host.start(request)
49
+ message.send_response
50
+ tmp = @host.flush
51
+ send_data tmp unless tmp.empty?
52
+ GC.start unless request['method'] == 'textDocument/didChange'
53
+ end
54
+ end
55
+
56
+ def start_timers
57
+ EventMachine.add_periodic_timer 0.1 do
58
+ tmp = @host.flush
59
+ send_data tmp unless tmp.empty?
60
+ EventMachine.stop if @host.stopped?
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -23,7 +23,7 @@ module Solargraph
23
23
  end
24
24
 
25
25
  def path
26
- @path ||= namespace + '#' + name
26
+ @path ||= namespace + (scope == :instance ? '#' : '.') + name
27
27
  end
28
28
 
29
29
  def return_type
@@ -44,6 +44,11 @@ module Solargraph
44
44
  LanguageServer::CompletionItemKinds::KEYWORD
45
45
  end
46
46
 
47
+ # @return [Integer]
48
+ def symbol_kind
49
+ nil
50
+ end
51
+
47
52
  def to_s
48
53
  name.to_s
49
54
  end
@@ -20,6 +20,11 @@ module Solargraph
20
20
  Solargraph::LanguageServer::CompletionItemKinds::VARIABLE
21
21
  end
22
22
 
23
+ # @return [Integer]
24
+ def symbol_kind
25
+ Solargraph::LanguageServer::SymbolKinds::VARIABLE
26
+ end
27
+
23
28
  def return_type
24
29
  if @return_type.nil?
25
30
  if !docstring.nil?
@@ -16,6 +16,11 @@ module Solargraph
16
16
  Solargraph::LanguageServer::CompletionItemKinds::CONSTANT
17
17
  end
18
18
 
19
+ # @return [Integer]
20
+ def symbol_kind
21
+ LanguageServer::SymbolKinds::CONSTANT
22
+ end
23
+
19
24
  def path
20
25
  "#{namespace}::#{name}"
21
26
  end
@@ -4,10 +4,10 @@ module Solargraph
4
4
  module Conversions
5
5
  # @return [Hash]
6
6
  def completion_item
7
- {
7
+ @completion_item ||= {
8
8
  label: name,
9
9
  kind: completion_item_kind,
10
- detail: completion_item_detail,
10
+ detail: detail,
11
11
  data: {
12
12
  path: path,
13
13
  return_type: return_type,
@@ -18,50 +18,43 @@ module Solargraph
18
18
 
19
19
  # @return [Hash]
20
20
  def resolve_completion_item
21
- extra = {}
22
- alldoc = ''
23
- alldoc += link_documentation(path) unless path.nil?
24
- alldoc += "\n\n" unless alldoc.empty?
25
- alldoc += documentation unless documentation.nil?
26
- extra[:documentation] = alldoc unless alldoc.empty?
27
- completion_item.merge(extra)
28
- end
29
-
30
- # @todo Candidate for deprecation
31
- # @param api_map [Solargraph::ApiMap]
32
- def hover
33
- info = ''
34
- if self.kind_of?(Solargraph::Pin::BaseVariable)
35
- info.concat link_documentation(return_type) unless return_type.nil?
36
- else
37
- info.concat link_documentation(path) unless path.nil?
21
+ if @resolve_completion_item.nil?
22
+ extra = {}
23
+ alldoc = ''
24
+ alldoc += link_documentation(path) unless path.nil?
25
+ alldoc += "\n\n" unless alldoc.empty?
26
+ alldoc += documentation unless documentation.nil?
27
+ extra[:documentation] = alldoc unless alldoc.empty?
28
+ @resolve_completion_item = completion_item.merge(extra)
38
29
  end
39
- info.concat "\n\n#{documentation}"
40
- info
30
+ @resolve_completion_item
41
31
  end
42
32
 
43
33
  # @return [Hash]
44
34
  def signature_help
45
- {
35
+ @signature_help ||= {
46
36
  label: name + '(' + parameters.join(', ') + ')',
47
37
  documentation: documentation
48
38
  }
49
39
  end
50
40
 
51
- private
52
-
53
- def completion_item_detail
54
- detail = ''
55
- detail += "(#{parameters.join(', ')}) " unless kind != Pin::METHOD or parameters.empty?
56
- detail += "=> #{return_type}" unless return_type.nil?
57
- return nil if detail.empty?
58
- detail
41
+ # @return [String]
42
+ def detail
43
+ if @detail.nil?
44
+ @detail = ''
45
+ @detail += "(#{parameters.join(', ')}) " unless kind != Pin::METHOD or parameters.empty?
46
+ @detail += "=> #{return_type}" unless return_type.nil?
47
+ @detail.strip!
48
+ end
49
+ return nil if @detail.empty?
50
+ @detail
59
51
  end
60
52
 
53
+ private
54
+
61
55
  def link_documentation path
62
- uri = "solargraph:/document?query=" + URI.encode(path)
63
- "[#{path}](#{uri})"
64
- end
56
+ @link_documentation ||= "[#{path}](solargraph:/document?query=#{URI.encode(path)})"
57
+ end
65
58
  end
66
59
  end
67
60
  end
@@ -6,7 +6,7 @@ module Solargraph
6
6
  # @return [Source::Range]
7
7
  attr_reader :presence
8
8
 
9
- # @param other [Pin::Block] The caller's block
9
+ # @param other [Pin::Base] The caller's block
10
10
  # @param position [Source::Position] The caller's position
11
11
  # @return [Boolean]
12
12
  def visible_from?(other, position)
@@ -24,6 +24,11 @@ module Solargraph
24
24
  Solargraph::LanguageServer::CompletionItemKinds::METHOD
25
25
  end
26
26
 
27
+ # @return [Integer]
28
+ def symbol_kind
29
+ LanguageServer::SymbolKinds::METHOD
30
+ end
31
+
27
32
  def return_type
28
33
  if @return_type.nil? and !docstring.nil?
29
34
  tag = docstring.tag(:return)
@@ -42,6 +42,11 @@ module Solargraph
42
42
  (type == :class ? LanguageServer::CompletionItemKinds::CLASS : LanguageServer::CompletionItemKinds::MODULE)
43
43
  end
44
44
 
45
+ # @return [Integer]
46
+ def symbol_kind
47
+ (type == :class ? LanguageServer::SymbolKinds::CLASS : LanguageServer::SymbolKinds::MODULE)
48
+ end
49
+
45
50
  def path
46
51
  @path ||= (namespace.empty? ? '' : "#{namespace}::") + name
47
52
  end
@@ -35,6 +35,20 @@ module Solargraph
35
35
  end
36
36
  end
37
37
 
38
+ desc 'stdio', 'Run a Solargraph stdio server'
39
+ def stdio
40
+ EventMachine.run do
41
+ Signal.trap("INT") do
42
+ EventMachine.stop
43
+ end
44
+ Signal.trap("TERM") do
45
+ EventMachine.stop
46
+ end
47
+ Solargraph::LanguageServer::Transport::Stdio.run
48
+ STDERR.puts "Solargraph is listening on stdio PID=#{Process.pid}"
49
+ end
50
+ end
51
+
38
52
  desc 'suggest', 'Get code suggestions for the provided input'
39
53
  long_desc <<-LONGDESC
40
54
  Analyze a Ruby file and output a list of code suggestions in JSON format.
@@ -227,6 +227,18 @@ module Solargraph
227
227
  @base_literal
228
228
  end
229
229
 
230
+ def literal?
231
+ !literal.nil?
232
+ end
233
+
234
+ def literal
235
+ if @literal.nil? and !@calculated_actual_literal
236
+ @calculated_actual_literal = true
237
+ pn = @source.node_at(line, column)
238
+ @literal = infer_literal_node_type(pn)
239
+ end
240
+ end
241
+
230
242
  private
231
243
 
232
244
  # @return [Integer]
@@ -1,3 +1,3 @@
1
1
  module Solargraph
2
- VERSION = '0.22.0'
2
+ VERSION = '0.23.0'
3
3
  end
@@ -88,9 +88,7 @@ module Solargraph
88
88
  def process_globs globs
89
89
  result = []
90
90
  globs.each do |glob|
91
- Dir[File.join workspace, glob].each do |f|
92
- result.push File.realdirpath(f).gsub(/\\/, '/')
93
- end
91
+ result.concat Dir[File.join workspace, glob].map{ |f| f.gsub(/\\/, '/') }
94
92
  end
95
93
  result
96
94
  end
@@ -98,7 +96,7 @@ module Solargraph
98
96
  def process_exclusions globs
99
97
  remainder = globs.select do |glob|
100
98
  if glob_is_directory?(glob)
101
- exdir = File.realdirpath(File.join(workspace, glob_to_directory(glob)))
99
+ exdir = File.join(workspace, glob_to_directory(glob))
102
100
  included.delete_if { |file| file.start_with?(exdir) }
103
101
  false
104
102
  else
@@ -238,6 +238,8 @@ module Solargraph
238
238
  end
239
239
 
240
240
  def objects path, space = ''
241
+ cached = cache.get_objects(path, space)
242
+ return cached unless cached.nil?
241
243
  result = []
242
244
  yardocs.each { |y|
243
245
  yard = load_yardoc(y)
@@ -251,6 +253,7 @@ module Solargraph
251
253
  @stdlib_namespaces.each do |ns|
252
254
  result.push Pin::YardObject.new(ns, object_location(ns)) if ns.path == path
253
255
  end
256
+ cache.set_objects(path, space, result)
254
257
  result
255
258
  end
256
259
 
@@ -272,6 +275,7 @@ module Solargraph
272
275
 
273
276
  private
274
277
 
278
+ # @return [Solargraph::YardMap::Cache]
275
279
  def cache
276
280
  @cache ||= Cache.new
277
281
  end
@@ -4,6 +4,7 @@ module Solargraph
4
4
  @constants = {}
5
5
  @methods = {}
6
6
  @instance_methods = {}
7
+ @objects = {}
7
8
  end
8
9
 
9
10
  def set_constants namespace, scope, suggestions
@@ -30,5 +31,12 @@ module Solargraph
30
31
  @instance_methods[[namespace, scope, visibility]]
31
32
  end
32
33
 
34
+ def set_objects path, space, pins
35
+ @objects[[path, space]] = pins
36
+ end
37
+
38
+ def get_objects path, space
39
+ @objects[[path, space]]
40
+ end
33
41
  end
34
42
  end
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.22.0
4
+ version: 0.23.0
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-05-29 00:00:00.000000000 Z
11
+ date: 2018-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parser
@@ -295,6 +295,7 @@ files:
295
295
  - lib/solargraph/language_server/transport.rb
296
296
  - lib/solargraph/language_server/transport/data_reader.rb
297
297
  - lib/solargraph/language_server/transport/socket.rb
298
+ - lib/solargraph/language_server/transport/stdio.rb
298
299
  - lib/solargraph/language_server/uri_helpers.rb
299
300
  - lib/solargraph/library.rb
300
301
  - lib/solargraph/live_map.rb