Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/puppet-languageserver/json_rpc_handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def reply_result(result)
end

def reply_internal_error(message = nil)
return nil if @json_rpc_handler.error?
return nil if @json_rpc_handler.connection_error?
@json_rpc_handler.reply_error(@id, CODE_INTERNAL_ERROR, message || MSG_INTERNAL_ERROR)
end

Expand Down
64 changes: 64 additions & 0 deletions lib/puppet-languageserver/message_router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,68 @@ def receive_notification(method, params)
raise
end
end

class DisabledMessageRouter
attr_accessor :json_rpc_handler

def initialize(_options)
end

def receive_request(request)
case request.rpc_method
when 'initialize'
PuppetLanguageServer.log_message(:debug, 'Received initialize method')
# If the Language Server is not active then we can not respond to any capability. We also
# send a warning to the user telling them this
request.reply_result('capabilities' => PuppetLanguageServer::ServerCapabilites.no_capabilities)
# Add a minor delay before sending the notification to give the client some processing time
sleep(0.5)
@json_rpc_handler.send_show_message_notification(
LSP::MessageType::WARNING,
'An error occured while the Language Server was starting. The server has been disabled.'
)

when 'shutdown'
PuppetLanguageServer.log_message(:debug, 'Received shutdown method')
request.reply_result(nil)

when 'puppet/getVersion'
# Clients may use the getVersion request to figure out when the server has "finished" loading. In this
# case just fake the response that we are fully loaded with unknown gem versions
request.reply_result(LSP::PuppetVersion.new(
'puppetVersion' => 'Unknown',
'facterVersion' => 'Unknown',
'factsLoaded' => true,
'functionsLoaded' => true,
'typesLoaded' => true,
'classesLoaded' => true
))

else
# For any request return an internal error.
request.reply_internal_error('Puppet Language Server is not active')
PuppetLanguageServer.log_message(:error, "Unknown RPC method #{request.rpc_method}")
end
rescue StandardError => e
PuppetLanguageServer::CrashDump.write_crash_file(e, nil, 'request' => request.rpc_method, 'params' => request.params)
raise
end

def receive_notification(method, params)
case method
when 'initialized'
PuppetLanguageServer.log_message(:info, 'Client has received initialization')

when 'exit'
PuppetLanguageServer.log_message(:info, 'Received exit notification. Closing connection to client...')
@json_rpc_handler.close_connection

else
PuppetLanguageServer.log_message(:error, "Unknown RPC notification #{method}")
end
rescue StandardError => e
PuppetLanguageServer::CrashDump.write_crash_file(e, nil, 'notification' => method, 'params' => params)
raise
end
end
end
6 changes: 6 additions & 0 deletions lib/puppet-languageserver/server_capabilities.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,11 @@ def self.capabilities
'workspaceSymbolProvider' => true
}
end

def self.no_capabilities
# Any empty hash denotes no capabilities at all
{
}
end
end
end
41 changes: 37 additions & 4 deletions lib/puppet_languageserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ def self.version
PuppetEditorServices.version
end

# Whether the language server is actually in a state that can be used.
# Typically this is false when a catastrophic error occurs during startup e.g. Puppet is missing.
#
# @return [Bool] Whether the language server is actually in a state that can be used
def self.active?
@server_is_active
end

def self.require_gems(options)
original_verbose = $VERBOSE
$VERBOSE = nil
@server_is_active = false

# Use specific Puppet Gem version if possible
unless options[:puppet_version].nil?
Expand All @@ -32,16 +41,33 @@ def self.require_gems(options)
end
end

require 'lsp/lsp'
require 'puppet'

# These libraries do not require the puppet gem and required for the
# server to respond to clients.
%w[
json_rpc_handler
document_store
crash_dump
message_router
validation_queue
server_capabilities
].each do |lib|
begin
require "puppet-languageserver/#{lib}"
rescue LoadError
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
end
end

begin
require 'lsp/lsp'
require 'puppet'
rescue LoadError => e
log_message(:error, "Error while loading a critical gem: #{e} #{e.backtrace}")
return
end

# These libraries require the puppet and LSP gems.
%w[
validation_queue
sidecar_protocol
sidecar_queue
puppet_parser_helper
Expand All @@ -57,6 +83,7 @@ def self.require_gems(options)
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-languageserver', lib))
end
end
@server_is_active = true
ensure
$VERBOSE = original_verbose
end
Expand Down Expand Up @@ -165,6 +192,7 @@ def self.init_puppet(options)
log_message(:info, "Language Server is v#{PuppetEditorServices.version}")
log_message(:debug, 'Loading gems...')
require_gems(options)
return unless active?
log_message(:info, "Using Puppet v#{Puppet.version}")

log_message(:debug, "Detected additional puppet settings #{options[:puppet_settings]}")
Expand Down Expand Up @@ -220,6 +248,11 @@ def self.rpc_server(options)
log_message(:info, 'Starting RPC Server...')
options[:servicename] = 'LANGUAGE SERVER'

unless active?
options[:message_router] = @message_router = PuppetLanguageServer::DisabledMessageRouter.new(options)
log_message(:info, 'Configured the Language Server to use the Disabled Message Router')
end

if options[:stdio]
log_message(:debug, 'Using STDIO')
server = PuppetEditorServices::SimpleSTDIOServer.new
Expand Down