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.



11
12
13
14
# File 'lib/puppet-languageserver/message_handler.rb', line 11

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

Instance Method Details

#documentsObject



24
25
26
# File 'lib/puppet-languageserver/message_handler.rb', line 24

def documents
  session_state.documents
end

#language_clientObject



20
21
22
# File 'lib/puppet-languageserver/message_handler.rb', line 20

def language_client
  session_state.language_client
end

#notification_exit(_, _json_rpc_message) ⇒ Object



320
321
322
323
# File 'lib/puppet-languageserver/message_handler.rb', line 320

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



298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/puppet-languageserver/message_handler.rb', line 298

def notification_initialized(_, _json_rpc_message)
  PuppetLanguageServer.log_message(:info, 'Client has received initialization')
  # Raise a warning if the Puppet 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 Puppet 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



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

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



334
335
336
337
338
339
# File 'lib/puppet-languageserver/message_handler.rb', line 334

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



325
326
327
328
329
330
331
332
# File 'lib/puppet-languageserver/message_handler.rb', line 325

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



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

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



363
364
365
366
367
368
369
370
371
# File 'lib/puppet-languageserver/message_handler.rb', line 363

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



184
185
186
187
188
189
190
# File 'lib/puppet-languageserver/message_handler.rb', line 184

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



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

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



89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/puppet-languageserver/message_handler.rb', line 89

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



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/puppet-languageserver/message_handler.rb', line 123

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



72
73
74
75
# File 'lib/puppet-languageserver/message_handler.rb', line 72

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



77
78
79
80
81
82
83
84
85
86
87
# File 'lib/puppet-languageserver/message_handler.rb', line 77

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



60
61
62
63
64
65
66
67
68
69
70
# File 'lib/puppet-languageserver/message_handler.rb', line 60

def request_puppet_getversion(_, _json_rpc_message)
  LSP::PuppetVersion.new(
    'languageServerVersion' => PuppetEditorServices.version,
    'puppetVersion' => Puppet.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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/puppet-languageserver/message_handler.rb', line 106

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



55
56
57
58
# File 'lib/puppet-languageserver/message_handler.rb', line 55

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

#request_textdocument_completion(_, json_rpc_message) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/puppet-languageserver/message_handler.rb', line 151

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



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/puppet-languageserver/message_handler.rb', line 208

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



225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/puppet-languageserver/message_handler.rb', line 225

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



169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/puppet-languageserver/message_handler.rb', line 169

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_hover(_, json_rpc_message) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/puppet-languageserver/message_handler.rb', line 192

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



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

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



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

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



289
290
291
292
293
294
295
296
# File 'lib/puppet-languageserver/message_handler.rb', line 289

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



373
374
375
# File 'lib/puppet-languageserver/message_handler.rb', line 373

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



377
378
379
# File 'lib/puppet-languageserver/message_handler.rb', line 377

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



381
382
383
384
385
386
387
388
# File 'lib/puppet-languageserver/message_handler.rb', line 381

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.



16
17
18
# File 'lib/puppet-languageserver/message_handler.rb', line 16

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

#unhandled_exception(error, options) ⇒ Object



390
391
392
393
# File 'lib/puppet-languageserver/message_handler.rb', line 390

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