Class: PuppetLanguageServer::MessageHandler

Inherits:
PuppetEditorServices::Handler::JsonRPC show all
Defined in:
lib/puppet-languageserver/message_handler.rb

Instance Attribute Summary

Attributes inherited from PuppetEditorServices::Handler::Base

#protocol

Instance Method Summary collapse

Methods inherited from PuppetEditorServices::Handler::JsonRPC

#handle

Methods inherited from PuppetEditorServices::Handler::Base

#handle

Constructor Details

#initialize(*_) ⇒ MessageHandler

Returns a new instance of MessageHandler.



13
14
15
16
# File 'lib/puppet-languageserver/message_handler.rb', line 13

def initialize(*_)
  super
  @session_state = ClientSessionState.new(self)
end

Instance Method Details

#documentsObject



26
27
28
# File 'lib/puppet-languageserver/message_handler.rb', line 26

def documents
  session_state.documents
end

#language_clientObject



22
23
24
# File 'lib/puppet-languageserver/message_handler.rb', line 22

def language_client
  session_state.language_client
end

#notification_exit(_, _json_rpc_message) ⇒ Object



345
346
347
348
# File 'lib/puppet-languageserver/message_handler.rb', line 345

def notification_exit(_, _json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Received exit notification.  Closing connection to client...')
  protocol.connection.close
end

#notification_initialized(_, _json_rpc_message) ⇒ Object



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
# File 'lib/puppet-languageserver/message_handler.rb', line 323

def notification_initialized(_, _json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Client has received initialization')
  # Raise a warning if the OpenVox version is mismatched
  server_options = protocol.connection.server.server_options
  unless server_options[:puppet_version].nil? || server_options[:puppet_version] == Puppet.version
    protocol.encode_and_send(
      ::PuppetEditorServices::Protocol::JsonRPCMessages.new_notification(
        'window/showMessage',
        'type' => LSP::MessageType::WARNING,
        'message' => "Unable to use OpenVox version '#{server_options[:puppet_version]}' as it is not available. Using version '#{Puppet.version}' instead."
      )
    )
  end

  # Register for workspace setting changes if it's supported
  if language_client.client_capability('workspace', 'didChangeConfiguration', 'dynamicRegistration') == true
    language_client.register_capability('workspace/didChangeConfiguration')
  else
    PuppetLanguageServer.log_message(:debug, 'Client does not support didChangeConfiguration dynamic registration. Using push method for configuration change detection.')
  end
end

#notification_textdocument_didchange(client_handler_id, json_rpc_message) ⇒ Object



366
367
368
369
370
371
372
373
# File 'lib/puppet-languageserver/message_handler.rb', line 366

def notification_textdocument_didchange(client_handler_id, json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Received textDocument/didChange notification.')
  file_uri = json_rpc_message.params['textDocument']['uri']
  content = json_rpc_message.params['contentChanges'][0]['text'] # TODO: Bad hardcoding zero
  doc_version = json_rpc_message.params['textDocument']['version']
  documents.set_document(file_uri, content, doc_version)
  enqueue_validation(file_uri, doc_version, client_handler_id)
end

#notification_textdocument_didclose(client_handler_id, json_rpc_message) ⇒ Object



359
360
361
362
363
364
# File 'lib/puppet-languageserver/message_handler.rb', line 359

def notification_textdocument_didclose(client_handler_id, json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Received textDocument/didClose notification.')
  file_uri = json_rpc_message.params['textDocument']['uri']
  documents.remove_document(file_uri)
  enqueue_validation(file_uri, nil, client_handler_id)
end

#notification_textdocument_didopen(client_handler_id, json_rpc_message) ⇒ Object



350
351
352
353
354
355
356
357
# File 'lib/puppet-languageserver/message_handler.rb', line 350

def notification_textdocument_didopen(client_handler_id, json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Received textDocument/didOpen notification.')
  file_uri = json_rpc_message.params['textDocument']['uri']
  content = json_rpc_message.params['textDocument']['text']
  doc_version = json_rpc_message.params['textDocument']['version']
  documents.set_document(file_uri, content, doc_version)
  enqueue_validation(file_uri, doc_version, client_handler_id)
end

#notification_textdocument_didsave(_, _json_rpc_message) ⇒ Object



375
376
377
378
379
380
381
382
383
384
385
386
# File 'lib/puppet-languageserver/message_handler.rb', line 375

def notification_textdocument_didsave(_, _json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Received textDocument/didSave notification.')
  # Expire the store cache so that the store information can re-evaluated
  documents.expire_store_information
  if documents.store_has_module_metadata? || documents.store_has_environmentconf?
    # Load the workspace information
    session_state.load_workspace_data!
  else
    # Purge the workspace information
    session_state.purge_workspace_data!
  end
end

#notification_workspace_didchangeconfiguration(_, json_rpc_message) ⇒ Object



388
389
390
391
392
393
394
395
396
# File 'lib/puppet-languageserver/message_handler.rb', line 388

def notification_workspace_didchangeconfiguration(_, json_rpc_message)
  if json_rpc_message.params.key?('settings') && json_rpc_message.params['settings'].nil?
    # This is a notification from a dynamic registration. Need to send a workspace/configuration
    # request to get the actual configuration
    language_client.send_configuration_request
  else
    language_client.parse_lsp_configuration_settings!(json_rpc_message.params['settings'])
  end
end

#request_completionitem_resolve(_, json_rpc_message) ⇒ Object



190
191
192
193
194
195
196
# File 'lib/puppet-languageserver/message_handler.rb', line 190

def request_completionitem_resolve(_, json_rpc_message)
  PuppetLanguageServer::Manifest::CompletionProvider.resolve(session_state, LSP::CompletionItem.new(json_rpc_message.params))
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(completionItem/resolve) #{e}")
  # Spit back the same params if an error happens
  json_rpc_message.params
end

#request_initialize(_, json_rpc_message) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/puppet-languageserver/message_handler.rb', line 30

def request_initialize(_, json_rpc_message)
  PuppetLanguageServer.log_message(:debug, 'Received initialize method')

  language_client.parse_lsp_initialize!(json_rpc_message.params)
  static_folding_provider = !language_client.client_capability('textDocument', 'foldingRange', 'dynamicRegistration') &&
                            PuppetLanguageServer::ServerCapabilites.folding_provider_supported?
  # Setup static registrations if dynamic registration is not available
  info = {
    documentOnTypeFormattingProvider: !language_client.client_capability('textDocument', 'onTypeFormatting', 'dynamicRegistration'),
    foldingRangeProvider: static_folding_provider
  }

  # Configure the document store
  documents.initialize_store(
    workspace: workspace_root_from_initialize_params(json_rpc_message.params)
  )

  # Initiate loading the object_cache
  session_state.load_default_data!
  session_state.load_static_data!

  # Initiate loading of the workspace if needed
  session_state.load_workspace_data! if documents.store_has_module_metadata? || documents.store_has_environmentconf?

  { 'capabilities' => PuppetLanguageServer::ServerCapabilites.capabilities(info) }
end

#request_puppet_compilenodegraph(_, json_rpc_message) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/puppet-languageserver/message_handler.rb', line 95

def request_puppet_compilenodegraph(_, json_rpc_message)
  file_uri = json_rpc_message.params['external']
  return LSP::PuppetNodeGraphResponse.new('error' => 'Files of this type can not be used to create a node graph.') unless documents.document_type(file_uri) == :manifest

  document = documents.document(file_uri)

  begin
    node_graph = PuppetLanguageServer::PuppetHelper.get_node_graph(session_state, document.content, documents.store_root_path)
    LSP::PuppetNodeGraphResponse.new('vertices' => node_graph.vertices,
                                     'edges' => node_graph.edges,
                                     'error' => node_graph.error_content)
  rescue StandardError => e
    PuppetLanguageServer.log_message(:error, "(puppet/compileNodeGraph) Error generating node graph. #{e}")
    LSP::PuppetNodeGraphResponse.new('error' => 'An internal error occured while generating the the node graph. Please see the debug log files for more information.')
  end
end

#request_puppet_fixdiagnosticerrors(_, json_rpc_message) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/puppet-languageserver/message_handler.rb', line 129

def request_puppet_fixdiagnosticerrors(_, json_rpc_message)
  formatted_request = LSP::PuppetFixDiagnosticErrorsRequest.new(json_rpc_message.params)
  file_uri = formatted_request.documentUri
  content = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    changes, new_content = PuppetLanguageServer::Manifest::ValidationProvider.fix_validate_errors(session_state, content)
  else
    raise "Unable to fixDiagnosticErrors on #{file_uri}"
  end

  LSP::PuppetFixDiagnosticErrorsResponse.new(
    'documentUri' => formatted_request.documentUri,
    'fixesApplied' => changes,
    'newContent' => changes > 0 || formatted_request.alwaysReturnContent ? new_content : nil
  )
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(puppet/fixDiagnosticErrors) #{e}")
  unless formatted_request.nil?
    LSP::PuppetFixDiagnosticErrorsResponse.new(
      'documentUri' => formatted_request.documentUri,
      'fixesApplied' => 0,
      'newContent' => formatted_request.alwaysReturnContent ? content : nil
    )
  end
end

#request_puppet_getfacts(_, _json_rpc_message) ⇒ Object



78
79
80
81
# File 'lib/puppet-languageserver/message_handler.rb', line 78

def request_puppet_getfacts(_, _json_rpc_message)
  results = PuppetLanguageServer::FacterHelper.facts_to_hash(session_state)
  LSP::PuppetFactResponse.new('facts' => results)
end

#request_puppet_getresource(_, json_rpc_message) ⇒ Object



83
84
85
86
87
88
89
90
91
92
93
# File 'lib/puppet-languageserver/message_handler.rb', line 83

def request_puppet_getresource(_, json_rpc_message)
  type_name = json_rpc_message.params['typename']
  title = json_rpc_message.params['title']
  return LSP::PuppetResourceResponse.new('error' => 'Missing Typename') if type_name.nil?

  resource_list = PuppetLanguageServer::PuppetHelper.get_puppet_resource(session_state, type_name, title, documents.store_root_path)
  return LSP::PuppetResourceResponse.new('data' => '') if resource_list.nil? || resource_list.length.zero?

  content = "#{resource_list.map(&:manifest).join("\n\n")}\n"
  LSP::PuppetResourceResponse.new('data' => content)
end

#request_puppet_getversion(_, _json_rpc_message) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/puppet-languageserver/message_handler.rb', line 62

def request_puppet_getversion(_, _json_rpc_message)
  LSP::PuppetVersion.new(
    'languageServerVersion' => PuppetEditorServices.version,
    'runtimeName' => OpenVoxRuntime.gem_name,
    'runtimeGemVersion' => OpenVoxRuntime.gem_version,
    'puppetVersion' => Puppet.version,
    'factRuntimeName' => OpenFactRuntime.gem_name,
    'factRuntimeGemVersion' => OpenFactRuntime.gem_version,
    'facterVersion' => Facter.version,
    'factsLoaded' => session_state.facts_loaded?,
    'functionsLoaded' => session_state.default_functions_loaded?,
    'typesLoaded' => session_state.default_types_loaded?,
    'classesLoaded' => session_state.default_classes_loaded?
  )
end

#request_puppetfile_getdependencies(_, json_rpc_message) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/puppet-languageserver/message_handler.rb', line 112

def request_puppetfile_getdependencies(_, json_rpc_message)
  file_uri = json_rpc_message.params['uri']
  return LSP::PuppetfileDependencyResponse.new('error' => 'Must be a puppetfile in order to find dependencies.') unless documents.document_type(file_uri) == :puppetfile

  document = documents.document(file_uri)

  result = []
  begin
    result = PuppetLanguageServer::Puppetfile::ValidationProvider.find_dependencies(document.content)
  rescue StandardError => e
    PuppetLanguageServer.log_message(:error, "(puppetfile/getdependencies) Error parsing puppetfile. #{e}")
    return LSP::PuppetfileDependencyResponse.new('error' => 'An internal error occured while parsing the puppetfile. Please see the debug log files for more information.')
  end

  LSP::PuppetfileDependencyResponse.new('dependencies' => result)
end

#request_shutdown(_, _json_rpc_message) ⇒ Object



57
58
59
60
# File 'lib/puppet-languageserver/message_handler.rb', line 57

def request_shutdown(_, _json_rpc_message)
  PuppetLanguageServer.log_message(:debug, 'Received shutdown method')
  nil
end

#request_textdocument_completion(_, json_rpc_message) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/puppet-languageserver/message_handler.rb', line 157

def request_textdocument_completion(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  line_num = json_rpc_message.params['position']['line']
  char_num = json_rpc_message.params['position']['character']
  content = documents.document_content(file_uri)
  context = json_rpc_message.params['context'].nil? ? nil : LSP::CompletionContext.new(json_rpc_message.params['context'])

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::CompletionProvider.complete(session_state, content, line_num, char_num, context:, tasks_mode: documents.plan_file?(file_uri))
  else
    raise "Unable to provide completion on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/completion) #{e}")
  LSP::CompletionList.new('isIncomplete' => false, 'items' => [])
end

#request_textdocument_definition(_, json_rpc_message) ⇒ Object



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/puppet-languageserver/message_handler.rb', line 214

def request_textdocument_definition(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  line_num = json_rpc_message.params['position']['line']
  char_num = json_rpc_message.params['position']['character']
  content = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::DefinitionProvider.find_definition(session_state, content, line_num, char_num, tasks_mode: documents.plan_file?(file_uri))
  else
    raise "Unable to provide definition on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/definition) #{e}")
  nil
end

#request_textdocument_documentsymbol(_, json_rpc_message) ⇒ Object



231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/puppet-languageserver/message_handler.rb', line 231

def request_textdocument_documentsymbol(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  content  = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::DocumentSymbolProvider.extract_document_symbols(content, tasks_mode: documents.plan_file?(file_uri))
  else
    raise "Unable to provide definition on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/documentSymbol) #{e}")
  nil
end

#request_textdocument_foldingrange(_, json_rpc_message) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/puppet-languageserver/message_handler.rb', line 175

def request_textdocument_foldingrange(_, json_rpc_message)
  return nil unless language_client.folding_range

  file_uri = json_rpc_message.params['textDocument']['uri']
  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::FoldingProvider.instance.folding_ranges(documents.document_tokens(file_uri))
  else
    raise "Unable to provide folding ranages on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/foldingRange) #{e}")
  nil
end

#request_textdocument_formatting(_, json_rpc_message) ⇒ Object



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/puppet-languageserver/message_handler.rb', line 246

def request_textdocument_formatting(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  content  = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::DocumentFormattingProvider.instance.format(
      content,
      json_rpc_message.params['options'],
      language_client.format_on_type_filesize_limit
    )
  else
    raise "Unable to format document on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/formatting) #{e}")
  nil
end

#request_textdocument_hover(_, json_rpc_message) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/puppet-languageserver/message_handler.rb', line 198

def request_textdocument_hover(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  line_num = json_rpc_message.params['position']['line']
  char_num = json_rpc_message.params['position']['character']
  content = documents.document_content(file_uri)
  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::HoverProvider.resolve(session_state, content, line_num, char_num, tasks_mode: documents.plan_file?(file_uri))
  else
    raise "Unable to provide hover on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/hover) #{e}")
  nil
end

#request_textdocument_ontypeformatting(_, json_rpc_message) ⇒ Object



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/puppet-languageserver/message_handler.rb', line 265

def request_textdocument_ontypeformatting(_, json_rpc_message)
  return nil unless language_client.format_on_type

  file_uri = json_rpc_message.params['textDocument']['uri']
  line_num = json_rpc_message.params['position']['line']
  char_num = json_rpc_message.params['position']['character']
  content  = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::FormatOnTypeProvider.instance.format(
      content,
      line_num,
      char_num,
      json_rpc_message.params['ch'],
      json_rpc_message.params['options'],
      language_client.format_on_type_filesize_limit
    )
  else
    raise "Unable to format on type on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/onTypeFormatting) #{e}")
  nil
end

#request_textdocument_signaturehelp(_, json_rpc_message) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/puppet-languageserver/message_handler.rb', line 291

def request_textdocument_signaturehelp(_, json_rpc_message)
  file_uri = json_rpc_message.params['textDocument']['uri']
  line_num = json_rpc_message.params['position']['line']
  char_num = json_rpc_message.params['position']['character']
  content  = documents.document_content(file_uri)

  case documents.document_type(file_uri)
  when :manifest
    PuppetLanguageServer::Manifest::SignatureProvider.signature_help(
      session_state,
      content,
      line_num,
      char_num,
      tasks_mode: documents.plan_file?(file_uri)
    )
  else
    raise "Unable to provide signatures on #{file_uri}"
  end
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(textDocument/signatureHelp) #{e}")
  nil
end

#request_workspace_symbol(_, json_rpc_message) ⇒ Object



314
315
316
317
318
319
320
321
# File 'lib/puppet-languageserver/message_handler.rb', line 314

def request_workspace_symbol(_, json_rpc_message)
  result = []
  result.concat(PuppetLanguageServer::Manifest::DocumentSymbolProvider.workspace_symbols(json_rpc_message.params['query'], session_state.object_cache))
  result
rescue StandardError => e
  PuppetLanguageServer.log_message(:error, "(workspace/symbol) #{e}")
  []
end

#response_client_registercapability(_, json_rpc_message, original_request) ⇒ Object



398
399
400
# File 'lib/puppet-languageserver/message_handler.rb', line 398

def response_client_registercapability(_, json_rpc_message, original_request)
  language_client.parse_register_capability_response!(json_rpc_message, original_request)
end

#response_client_unregistercapability(_, json_rpc_message, original_request) ⇒ Object



402
403
404
# File 'lib/puppet-languageserver/message_handler.rb', line 402

def response_client_unregistercapability(_, json_rpc_message, original_request)
  language_client.parse_unregister_capability_response!(json_rpc_message, original_request)
end

#response_workspace_configuration(_, json_rpc_message, original_request) ⇒ Object



406
407
408
409
410
411
412
413
# File 'lib/puppet-languageserver/message_handler.rb', line 406

def response_workspace_configuration(_, json_rpc_message, original_request)
  return unless json_rpc_message.is_successful

  original_request.params.items.each_with_index do |item, index|
    # The response from the client strips the section name so we need to re-add it
    language_client.parse_lsp_configuration_settings!(item.section => json_rpc_message.result[index])
  end
end

#session_stateObject

rubocop:disable Style/TrivialAccessors -- During the refactor, this is fine.



18
19
20
# File 'lib/puppet-languageserver/message_handler.rb', line 18

def session_state # rubocop:disable Style/TrivialAccessors -- During the refactor, this is fine.
  @session_state
end

#unhandled_exception(error, options) ⇒ Object



415
416
417
418
# File 'lib/puppet-languageserver/message_handler.rb', line 415

def unhandled_exception(error, options)
  super
  PuppetLanguageServer::CrashDump.write_crash_file(error, session_state, nil, options)
end