22
33module PuppetLanguageServer
44 class LanguageClient
5- def initialize
5+ attr_reader :message_router
6+
7+ def initialize ( message_router )
8+ @message_router = message_router
69 @client_capabilites = { }
10+
11+ # Internal registry of dynamic registrations and their current state
12+ # @registrations[ <[String] method_name>] = [
13+ # {
14+ # :id => [String] Request ID. Used for de-registration
15+ # :registered => [Boolean] true | false
16+ # :state => [Enum] :pending | :complete
17+ # }
18+ # ]
19+ @registrations = { }
720 end
821
922 def client_capability ( *names )
1023 safe_hash_traverse ( @client_capabilites , *names )
1124 end
1225
13- def send_configuration_request ( message_router )
26+ def send_configuration_request
1427 params = LSP ::ConfigurationParams . new . from_h! ( 'items' => [ ] )
1528 params . items << LSP ::ConfigurationItem . new . from_h! ( 'section' => 'puppet' )
1629
@@ -30,32 +43,115 @@ def parse_lsp_configuration_settings!(settings = [{}])
3043 # end
3144 end
3245
33- def register_capability ( message_router , method , options = { } )
34- id = SecureRandom . uuid
46+ def capability_registrations ( method )
47+ return [ { :registered => false , :state => :complete } ] if @registrations [ method ] . nil? || @registrations [ method ] . empty?
48+ @registrations [ method ] . dup
49+ end
50+
51+ def register_capability ( method , options = { } )
52+ id = new_request_id
3553
3654 PuppetLanguageServer . log_message ( :info , "Attempting to dynamically register the #{ method } method with id #{ id } " )
3755
56+ if @registrations [ method ] && @registrations [ method ] . select { |i | i [ :state ] == :pending } . count > 0
57+ # The protocol doesn't specify whether this is allowed and is probably per client specific. For the moment we will allow
58+ # the registration to be sent but log a message that something may be wrong.
59+ PuppetLanguageServer . log_message ( :warn , "A dynamic registration/deregistration for the #{ method } method is already in progress" )
60+ end
61+
3862 params = LSP ::RegistrationParams . new . from_h! ( 'registrations' => [ ] )
3963 params . registrations << LSP ::Registration . new . from_h! ( 'id' => id , 'method' => method , 'registerOptions' => options )
64+ # Note - Don't put more than one method per request even though you can. It makes decoding errors much harder!
65+
66+ @registrations [ method ] = [ ] if @registrations [ method ] . nil?
67+ @registrations [ method ] << { :registered => false , :state => :pending , :id => id }
4068
4169 message_router . json_rpc_handler . send_client_request ( 'client/registerCapability' , params )
4270 true
4371 end
4472
45- def parse_register_capability_response! ( message_router , _response , original_request )
73+ def unregister_capability ( method )
74+ if @registrations [ method ] . nil?
75+ PuppetLanguageServer . log_message ( :debug , "No registrations to deregister for the #{ method } " )
76+ return true
77+ end
78+
79+ params = LSP ::UnregistrationParams . new . from_h! ( 'unregisterations' => [ ] )
80+ @registrations [ method ] . each do |reg |
81+ next if reg [ :id ] . nil?
82+ PuppetLanguageServer . log_message ( :warn , "A dynamic registration/deregistration for the #{ method } method, with id #{ reg [ :id ] } is already in progress" ) if reg [ :state ] == :pending
83+ # Ignore registrations that don't need to be unregistered
84+ next if reg [ :state ] == :complete && !reg [ :registered ]
85+ params . unregisterations << LSP ::Unregistration . new . from_h! ( 'id' => reg [ :id ] , 'method' => method )
86+ reg [ :state ] = :pending
87+ end
88+
89+ if params . unregisterations . count . zero?
90+ PuppetLanguageServer . log_message ( :debug , "Nothing to deregister for the #{ method } method" )
91+ return true
92+ end
93+
94+ message_router . json_rpc_handler . send_client_request ( 'client/unregisterCapability' , params )
95+ true
96+ end
97+
98+ def parse_register_capability_response! ( response , original_request )
4699 raise 'Response is not from client/registerCapability request' unless original_request [ 'method' ] == 'client/registerCapability'
100+
101+ unless response . key? ( 'result' )
102+ original_request [ 'params' ] . registrations . each do |reg |
103+ # Mark the registration as completed and failed
104+ @registrations [ reg . method__lsp ] = [ ] if @registrations [ reg . method__lsp ] . nil?
105+ @registrations [ reg . method__lsp ] . select { |i | i [ :id ] == reg . id } . each { |i | i [ :registered ] = false ; i [ :state ] = :complete } # rubocop:disable Style/Semicolon This is fine
106+ end
107+ return true
108+ end
109+
47110 original_request [ 'params' ] . registrations . each do |reg |
48111 PuppetLanguageServer . log_message ( :info , "Succesfully dynamically registered the #{ reg . method__lsp } method" )
112+
113+ # Mark the registration as completed and succesful
114+ @registrations [ reg . method__lsp ] = [ ] if @registrations [ reg . method__lsp ] . nil?
115+ @registrations [ reg . method__lsp ] . select { |i | i [ :id ] == reg . id } . each { |i | i [ :registered ] = true ; i [ :state ] = :complete } # rubocop:disable Style/Semicolon This is fine
116+
49117 # If we just registered the workspace/didChangeConfiguration method then
50118 # also trigger a configuration request to get the initial state
51- send_configuration_request ( message_router ) if reg . method__lsp == 'workspace/didChangeConfiguration'
119+ send_configuration_request if reg . method__lsp == 'workspace/didChangeConfiguration'
120+ end
121+
122+ true
123+ end
124+
125+ def parse_unregister_capability_response! ( response , original_request )
126+ raise 'Response is not from client/unregisterCapability request' unless original_request [ 'method' ] == 'client/unregisterCapability'
127+
128+ unless response . key? ( 'result' )
129+ original_request [ 'params' ] . unregisterations . each do |reg |
130+ # Mark the registration as completed and failed
131+ @registrations [ reg . method__lsp ] = [ ] if @registrations [ reg . method__lsp ] . nil?
132+ @registrations [ reg . method__lsp ] . select { |i | i [ :id ] == reg . id && i [ :registered ] } . each { |i | i [ :state ] = :complete }
133+ @registrations [ reg . method__lsp ] . delete_if { |i | i [ :id ] == reg . id && !i [ :registered ] }
134+ end
135+ return true
136+ end
137+
138+ original_request [ 'params' ] . unregisterations . each do |reg |
139+ PuppetLanguageServer . log_message ( :info , "Succesfully dynamically unregistered the #{ reg . method__lsp } method" )
140+
141+ # Remove registrations
142+ @registrations [ reg . method__lsp ] = [ ] if @registrations [ reg . method__lsp ] . nil?
143+ @registrations [ reg . method__lsp ] . delete_if { |i | i [ :id ] == reg . id }
52144 end
53145
54146 true
55147 end
56148
57149 private
58150
151+ def new_request_id
152+ SecureRandom . uuid
153+ end
154+
59155 def safe_hash_traverse ( hash , *names )
60156 return nil if names . empty?
61157 item = nil
0 commit comments