solargraph 0.22.0 → 0.23.0

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.
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