solargraph 0.30.2 → 0.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +7 -0
  3. data/lib/solargraph/api_map.rb +31 -38
  4. data/lib/solargraph/api_map/store.rb +7 -1
  5. data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
  6. data/lib/solargraph/language_server/host.rb +34 -83
  7. data/lib/solargraph/language_server/host/cataloger.rb +17 -7
  8. data/lib/solargraph/language_server/host/diagnoser.rb +19 -10
  9. data/lib/solargraph/language_server/host/dispatch.rb +110 -0
  10. data/lib/solargraph/language_server/host/sources.rb +100 -1
  11. data/lib/solargraph/language_server/message/base.rb +15 -11
  12. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
  13. data/lib/solargraph/language_server/message/initialize.rb +32 -27
  14. data/lib/solargraph/language_server/message/text_document/completion.rb +1 -8
  15. data/lib/solargraph/language_server/transport/adapter.rb +26 -15
  16. data/lib/solargraph/language_server/transport/data_reader.rb +2 -2
  17. data/lib/solargraph/library.rb +30 -58
  18. data/lib/solargraph/live_map.rb +1 -1
  19. data/lib/solargraph/pin.rb +1 -0
  20. data/lib/solargraph/pin/base.rb +1 -1
  21. data/lib/solargraph/pin/base_method.rb +1 -1
  22. data/lib/solargraph/pin/method.rb +1 -1
  23. data/lib/solargraph/pin/method_alias.rb +15 -4
  24. data/lib/solargraph/plugin/process.rb +1 -1
  25. data/lib/solargraph/position.rb +1 -2
  26. data/lib/solargraph/server_methods.rb +1 -0
  27. data/lib/solargraph/shell.rb +0 -28
  28. data/lib/solargraph/source.rb +116 -20
  29. data/lib/solargraph/source/encoding_fixes.rb +1 -1
  30. data/lib/solargraph/source/source_chainer.rb +16 -8
  31. data/lib/solargraph/source_map.rb +11 -2
  32. data/lib/solargraph/source_map/clip.rb +1 -1
  33. data/lib/solargraph/source_map/mapper.rb +8 -5
  34. data/lib/solargraph/version.rb +1 -1
  35. data/lib/solargraph/views/environment.erb +3 -0
  36. data/lib/solargraph/workspace.rb +17 -14
  37. data/lib/solargraph/workspace/config.rb +1 -1
  38. data/lib/solargraph/yard_map.rb +6 -5
  39. data/lib/solargraph/yard_map/core_docs.rb +68 -18
  40. data/lib/solargraph/yard_map/core_gen.rb +47 -0
  41. data/lib/yard-coregen.rb +16 -0
  42. data/lib/yard-solargraph.rb +10 -1
  43. metadata +21 -4
@@ -1,6 +1,8 @@
1
1
  module Solargraph
2
2
  module LanguageServer
3
3
  class Host
4
+ # An asynchronous library cataloging handler.
5
+ #
4
6
  class Cataloger
5
7
  def initialize host
6
8
  @host = host
@@ -11,10 +13,12 @@ module Solargraph
11
13
 
12
14
  # Notify the Cataloger that changes are pending.
13
15
  #
16
+ # @param lib [Library] The library that needs cataloging
14
17
  # @return [void]
15
18
  def ping lib
16
19
  mutex.synchronize { pings.push lib }
17
20
  end
21
+ alias schedule ping
18
22
 
19
23
  def synchronizing?
20
24
  !pings.empty?
@@ -42,17 +46,23 @@ module Solargraph
42
46
  @stopped = false
43
47
  Thread.new do
44
48
  until stopped?
45
- sleep 0.1
46
- next if pings.empty?
47
- mutex.synchronize do
48
- lib = pings.shift
49
- next if pings.include?(lib)
50
- host.catalog lib
51
- end
49
+ tick
50
+ sleep 0.01
52
51
  end
53
52
  end
54
53
  end
55
54
 
55
+ # Perform cataloging.
56
+ #
57
+ # @return [void]
58
+ def tick
59
+ return if pings.empty?
60
+ mutex.synchronize do
61
+ lib = pings.shift
62
+ lib.catalog unless pings.include?(lib)
63
+ end
64
+ end
65
+
56
66
  private
57
67
 
58
68
  # @return [Host]
@@ -1,7 +1,10 @@
1
1
  module Solargraph
2
2
  module LanguageServer
3
3
  class Host
4
+ # An asynchronous diagnosis reporter.
5
+ #
4
6
  class Diagnoser
7
+ # @param host [Host]
5
8
  def initialize host
6
9
  @host = host
7
10
  @mutex = Mutex.new
@@ -39,22 +42,28 @@ module Solargraph
39
42
  @stopped = false
40
43
  Thread.new do
41
44
  until stopped?
45
+ tick
42
46
  sleep 0.1
43
- next if queue.empty? || host.synchronizing?
44
- if !host.options['diagnostics']
45
- mutex.synchronize { queue.clear }
46
- next
47
- end
48
- current = nil
49
- mutex.synchronize { current = queue.shift }
50
- next if queue.include?(current)
51
- host.diagnose current
52
- sleep 0.5
53
47
  end
54
48
  end
55
49
  self
56
50
  end
57
51
 
52
+ # Perform diagnoses.
53
+ #
54
+ # @return [void]
55
+ def tick
56
+ return if queue.empty? || host.synchronizing?
57
+ if !host.options['diagnostics']
58
+ mutex.synchronize { queue.clear }
59
+ return
60
+ end
61
+ current = nil
62
+ mutex.synchronize { current = queue.shift }
63
+ return if queue.include?(current)
64
+ host.diagnose current
65
+ end
66
+
58
67
  private
59
68
 
60
69
  # @return [Host]
@@ -0,0 +1,110 @@
1
+ module Solargraph
2
+ module LanguageServer
3
+ class Host
4
+ # Methods for associating sources with libraries via URIs.
5
+ #
6
+ module Dispatch
7
+ # @return [Sources]
8
+ def sources
9
+ @sources ||= begin
10
+ src = Sources.new
11
+ src.add_observer self, :update_libraries
12
+ src
13
+ end
14
+ end
15
+
16
+ # @return [Array<Library>]
17
+ def libraries
18
+ @libraries ||= []
19
+ end
20
+
21
+ # The Sources observer callback that merges a source into the host's
22
+ # libraries when it gets updated.
23
+ #
24
+ # @param src [Source]
25
+ # @return [void]
26
+ def update_libraries src
27
+ # @todo This module should not call cataloger and diagnoser
28
+ libraries.each do |lib|
29
+ lib.merge src
30
+ cataloger.ping(lib) if lib.contain?(src.filename) || lib.open?(src.filename)
31
+ end
32
+ diagnoser.schedule file_to_uri(src.filename) if src.synchronized?
33
+ end
34
+
35
+ # Find the best libary match for the given URI.
36
+ #
37
+ # @param uri [String]
38
+ # @return [Library]
39
+ def library_for uri
40
+ explicit_library_for(uri) ||
41
+ implicit_library_for(uri) ||
42
+ generic_library_for(uri)
43
+ end
44
+
45
+ # Find an explicit library match for the given URI. An explicit match
46
+ # means the libary's workspace includes the file.
47
+ #
48
+ # If a matching library is found, the source corresponding to the URI
49
+ # gets attached to it.
50
+ #
51
+ # @raise [FileNotFoundError] if the source could not be attached.
52
+ #
53
+ # @param uri [String]
54
+ # @return [Library, nil]
55
+ def explicit_library_for uri
56
+ filename = UriHelpers.uri_to_file(uri)
57
+ libraries.each do |lib|
58
+ if lib.contain?(filename)
59
+ lib.attach sources.find(uri) if sources.include?(uri)
60
+ return lib
61
+ end
62
+ end
63
+ nil
64
+ end
65
+
66
+ # Find an implicit library match for the given URI. An implicit match
67
+ # means the file is located inside the library's workspace directory,
68
+ # regardless of whether the workspace is configured to include it.
69
+ #
70
+ # If a matching library is found, the source corresponding to the URI
71
+ # gets attached to it.
72
+ #
73
+ # @raise [FileNotFoundError] if the source could not be attached.
74
+ #
75
+ # @param uri [String]
76
+ # @return [Library, nil]
77
+ def implicit_library_for uri
78
+ filename = UriHelpers.uri_to_file(uri)
79
+ libraries.each do |lib|
80
+ # @todo We probably shouldn't depend on attachments to select
81
+ # a library.
82
+ # return lib if lib.open?(filename)
83
+ if filename.start_with?(lib.workspace.directory)
84
+ lib.attach sources.find(uri)
85
+ return lib
86
+ end
87
+ end
88
+ nil
89
+ end
90
+
91
+ # Get a generic library for the given URI and attach the corresponding
92
+ # source.
93
+ #
94
+ # @raise [FileNotFoundError] if the source could not be attached.
95
+ #
96
+ # @param uri [String]
97
+ # @return [Library]
98
+ def generic_library_for uri
99
+ generic_library.attach sources.find(uri)
100
+ generic_library
101
+ end
102
+
103
+ # @return [Library]
104
+ def generic_library
105
+ @generic_library ||= Solargraph::Library.new
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,38 +1,137 @@
1
+ require 'observer'
2
+
1
3
  module Solargraph
2
4
  module LanguageServer
3
5
  class Host
6
+ # A Host class for managing sources.
7
+ #
4
8
  class Sources
9
+ include Observable
5
10
  include UriHelpers
6
11
 
12
+ def initialize
13
+ @stopped = true
14
+ end
15
+
16
+ def stopped?
17
+ @stopped
18
+ end
19
+
20
+ # @return [void]
21
+ def start
22
+ return unless @stopped
23
+ @stopped = false
24
+ Thread.new do
25
+ until stopped?
26
+ tick
27
+ sleep 0.25 if queue.empty?
28
+ end
29
+ end
30
+ end
31
+
32
+ # @return [void]
33
+ def tick
34
+ return if queue.empty?
35
+ uri = mutex.synchronize { queue.shift }
36
+ return if queue.include?(uri)
37
+ mutex.synchronize do
38
+ nxt = open_source_hash[uri].finish_synchronize
39
+ open_source_hash[uri] = nxt
40
+ changed
41
+ notify_observers open_source_hash[uri]
42
+ end
43
+ end
44
+
45
+ # @return [void]
46
+ def stop
47
+ @stopped = true
48
+ end
49
+
50
+ # Open a source.
51
+ #
52
+ # @param uri [String]
53
+ # @param text [String]
54
+ # @param version [Integer]
55
+ # @return [Source]
7
56
  def open uri, text, version
8
57
  filename = uri_to_file(uri)
9
58
  source = Solargraph::Source.new(text, filename, version)
10
59
  open_source_hash[uri] = source
11
60
  end
12
61
 
62
+ # Update an existing source.
63
+ #
64
+ # @raise [FileNotFoundError] if the URI does not match an open source.
65
+ #
66
+ # @param uri [String]
67
+ # @param updater [Source::Updater]
68
+ # @return [Source]
13
69
  def update uri, updater
14
70
  src = find(uri)
15
- open_source_hash[uri] = src.synchronize(updater)
71
+ mutex.synchronize { open_source_hash[uri] = src.synchronize(updater) }
72
+ changed
73
+ notify_observers open_source_hash[uri]
74
+ end
75
+
76
+ # @param uri [String]
77
+ # @param updater [Source::Updater]
78
+ # @return [Thread]
79
+ def async_update uri, updater
80
+ src = find(uri)
81
+ mutex.synchronize { open_source_hash[uri] = src.start_synchronize(updater) }
82
+ mutex.synchronize { queue.push uri }
83
+ changed
84
+ notify_observers open_source_hash[uri]
16
85
  end
17
86
 
87
+ # Find the source with the given URI.
88
+ #
89
+ # @raise [FileNotFoundError] if the URI does not match an open source.
90
+ #
91
+ # @param uri [String]
18
92
  # @return [Source]
19
93
  def find uri
20
94
  open_source_hash[uri] || raise(Solargraph::FileNotFoundError, "Host could not find #{uri}")
21
95
  end
22
96
 
97
+ # Close the source with the given URI.
98
+ #
99
+ # @param uri [String]
100
+ # @return [void]
23
101
  def close uri
24
102
  open_source_hash.delete uri
25
103
  end
26
104
 
105
+ # True if a source with given URI is currently open.
106
+ # @param uri [String]
107
+ # @return [Boolean]
27
108
  def include? uri
28
109
  open_source_hash.key? uri
29
110
  end
30
111
 
112
+ # @return [void]
113
+ def clear
114
+ open_source_hash.clear
115
+ end
116
+
31
117
  private
32
118
 
119
+ # @return [Array<Source>]
33
120
  def open_source_hash
34
121
  @open_source_hash ||= {}
35
122
  end
123
+
124
+ # @return [Mutex]
125
+ def mutex
126
+ @mutex ||= Mutex.new
127
+ end
128
+
129
+ # An array of source URIs that are waiting to finish synchronizing.
130
+ #
131
+ # @return [Array<String>]
132
+ def queue
133
+ @queue ||= []
134
+ end
36
135
  end
37
136
  end
38
137
  end
@@ -58,18 +58,22 @@ module Solargraph
58
58
 
59
59
  # @return [void]
60
60
  def send_response
61
- unless id.nil? or host.cancel?(id)
62
- response = {
63
- jsonrpc: "2.0",
64
- id: id,
65
- }
66
- response[:result] = result unless result.nil?
67
- response[:error] = error unless error.nil?
68
- response[:result] = nil if result.nil? and error.nil?
69
- json = response.to_json
70
- envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
71
- host.queue envelope
61
+ return if id.nil?
62
+ if host.cancel?(id)
63
+ Solargraph::Logging.logger.info "Cancelled response to #{method}"
64
+ return
72
65
  end
66
+ Solargraph::Logging.logger.info "Sending response to #{method}"
67
+ response = {
68
+ jsonrpc: "2.0",
69
+ id: id,
70
+ }
71
+ response[:result] = result unless result.nil?
72
+ response[:error] = error unless error.nil?
73
+ response[:result] = nil if result.nil? and error.nil?
74
+ json = response.to_json
75
+ envelope = "Content-Length: #{json.bytesize}\r\n\r\n#{json}"
76
+ host.queue envelope
73
77
  host.clear id
74
78
  end
75
79
  end
@@ -36,7 +36,7 @@ module Solargraph
36
36
  host.show_message "The Solargraph gem is up to date (version #{Solargraph::VERSION})."
37
37
  end
38
38
  elsif fetched?
39
- STDERR.puts error
39
+ Solargraph::Logging.logger.warn error
40
40
  host.show_message(error, MessageTypes::ERROR) if params['verbose']
41
41
  end
42
42
  set_result({
@@ -1,39 +1,44 @@
1
+ require 'benchmark'
2
+
1
3
  module Solargraph
2
4
  module LanguageServer
3
5
  module Message
4
6
  class Initialize < Base
5
7
  def process
6
- host.configure params['initializationOptions']
7
- if support_workspace_folders?
8
- host.prepare_folders params['workspaceFolders']
9
- elsif params['rootUri']
10
- host.prepare UriHelpers.uri_to_file(params['rootUri'])
11
- else
12
- host.prepare params['rootPath']
13
- end
14
- result = {
15
- capabilities: {
16
- textDocumentSync: 2, # @todo What should this be?
17
- workspace: {
18
- workspaceFolders: {
19
- supported: true,
20
- changeNotifications: true
8
+ bm = Benchmark.measure {
9
+ host.configure params['initializationOptions']
10
+ if support_workspace_folders?
11
+ host.prepare_folders params['workspaceFolders']
12
+ elsif params['rootUri']
13
+ host.prepare UriHelpers.uri_to_file(params['rootUri'])
14
+ else
15
+ host.prepare params['rootPath']
16
+ end
17
+ result = {
18
+ capabilities: {
19
+ textDocumentSync: 2, # @todo What should this be?
20
+ workspace: {
21
+ workspaceFolders: {
22
+ supported: true,
23
+ changeNotifications: true
24
+ }
21
25
  }
22
26
  }
23
27
  }
28
+ result[:capabilities].merge! static_completion unless dynamic_registration_for?('textDocument', 'completion')
29
+ result[:capabilities].merge! static_signature_help unless dynamic_registration_for?('textDocument', 'signatureHelp')
30
+ # result[:capabilities].merge! static_on_type_formatting unless dynamic_registration_for?('textDocument', 'onTypeFormatting')
31
+ result[:capabilities].merge! static_hover unless dynamic_registration_for?('textDocument', 'hover')
32
+ result[:capabilities].merge! static_document_formatting unless dynamic_registration_for?('textDocument', 'formatting')
33
+ result[:capabilities].merge! static_document_symbols unless dynamic_registration_for?('textDocument', 'documentSymbol')
34
+ result[:capabilities].merge! static_definitions unless dynamic_registration_for?('textDocument', 'definition')
35
+ result[:capabilities].merge! static_rename unless dynamic_registration_for?('textDocument', 'rename')
36
+ result[:capabilities].merge! static_references unless dynamic_registration_for?('textDocument', 'references')
37
+ result[:capabilities].merge! static_workspace_symbols unless dynamic_registration_for?('workspace', 'symbol')
38
+ result[:capabilities].merge! static_folding_range unless dynamic_registration_for?('textDocument', 'foldingRange')
39
+ set_result result
24
40
  }
25
- result[:capabilities].merge! static_completion unless dynamic_registration_for?('textDocument', 'completion')
26
- result[:capabilities].merge! static_signature_help unless dynamic_registration_for?('textDocument', 'signatureHelp')
27
- # result[:capabilities].merge! static_on_type_formatting unless dynamic_registration_for?('textDocument', 'onTypeFormatting')
28
- result[:capabilities].merge! static_hover unless dynamic_registration_for?('textDocument', 'hover')
29
- result[:capabilities].merge! static_document_formatting unless dynamic_registration_for?('textDocument', 'formatting')
30
- result[:capabilities].merge! static_document_symbols unless dynamic_registration_for?('textDocument', 'documentSymbol')
31
- result[:capabilities].merge! static_definitions unless dynamic_registration_for?('textDocument', 'definition')
32
- result[:capabilities].merge! static_rename unless dynamic_registration_for?('textDocument', 'rename')
33
- result[:capabilities].merge! static_references unless dynamic_registration_for?('textDocument', 'references')
34
- result[:capabilities].merge! static_workspace_symbols unless dynamic_registration_for?('workspace', 'symbol')
35
- result[:capabilities].merge! static_folding_range unless dynamic_registration_for?('textDocument', 'foldingRange')
36
- set_result result
41
+ Solargraph.logger.unknown "Solargraph initialized (#{bm.real} seconds)"
37
42
  end
38
43
 
39
44
  private