solargraph 0.30.2 → 0.31.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.
- checksums.yaml +4 -4
- data/lib/solargraph.rb +7 -0
- data/lib/solargraph/api_map.rb +31 -38
- data/lib/solargraph/api_map/store.rb +7 -1
- data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
- data/lib/solargraph/language_server/host.rb +34 -83
- data/lib/solargraph/language_server/host/cataloger.rb +17 -7
- data/lib/solargraph/language_server/host/diagnoser.rb +19 -10
- data/lib/solargraph/language_server/host/dispatch.rb +110 -0
- data/lib/solargraph/language_server/host/sources.rb +100 -1
- data/lib/solargraph/language_server/message/base.rb +15 -11
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -1
- data/lib/solargraph/language_server/message/initialize.rb +32 -27
- data/lib/solargraph/language_server/message/text_document/completion.rb +1 -8
- data/lib/solargraph/language_server/transport/adapter.rb +26 -15
- data/lib/solargraph/language_server/transport/data_reader.rb +2 -2
- data/lib/solargraph/library.rb +30 -58
- data/lib/solargraph/live_map.rb +1 -1
- data/lib/solargraph/pin.rb +1 -0
- data/lib/solargraph/pin/base.rb +1 -1
- data/lib/solargraph/pin/base_method.rb +1 -1
- data/lib/solargraph/pin/method.rb +1 -1
- data/lib/solargraph/pin/method_alias.rb +15 -4
- data/lib/solargraph/plugin/process.rb +1 -1
- data/lib/solargraph/position.rb +1 -2
- data/lib/solargraph/server_methods.rb +1 -0
- data/lib/solargraph/shell.rb +0 -28
- data/lib/solargraph/source.rb +116 -20
- data/lib/solargraph/source/encoding_fixes.rb +1 -1
- data/lib/solargraph/source/source_chainer.rb +16 -8
- data/lib/solargraph/source_map.rb +11 -2
- data/lib/solargraph/source_map/clip.rb +1 -1
- data/lib/solargraph/source_map/mapper.rb +8 -5
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/views/environment.erb +3 -0
- data/lib/solargraph/workspace.rb +17 -14
- data/lib/solargraph/workspace/config.rb +1 -1
- data/lib/solargraph/yard_map.rb +6 -5
- data/lib/solargraph/yard_map/core_docs.rb +68 -18
- data/lib/solargraph/yard_map/core_gen.rb +47 -0
- data/lib/yard-coregen.rb +16 -0
- data/lib/yard-solargraph.rb +10 -1
- 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
|
-
|
46
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
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
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|