puppet-editor-services 2.0.4
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 +7 -0
- data/CHANGELOG.md +510 -0
- data/CODEOWNERS +2 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +54 -0
- data/Gemfile +53 -0
- data/LICENSE +201 -0
- data/README.md +308 -0
- data/Rakefile +185 -0
- data/bin/puppet-debugserver +8 -0
- data/bin/puppet-languageserver +7 -0
- data/bin/puppet-languageserver-sidecar +7 -0
- data/lib/dsp/dsp.rb +7 -0
- data/lib/dsp/dsp_base.rb +62 -0
- data/lib/dsp/dsp_protocol.rb +4619 -0
- data/lib/lsp/lsp.rb +10 -0
- data/lib/lsp/lsp_base.rb +63 -0
- data/lib/lsp/lsp_custom.rb +170 -0
- data/lib/lsp/lsp_enums.rb +143 -0
- data/lib/lsp/lsp_protocol.rb +2785 -0
- data/lib/lsp/lsp_protocol_callhierarchy.proposed.rb +239 -0
- data/lib/lsp/lsp_protocol_colorprovider.rb +100 -0
- data/lib/lsp/lsp_protocol_configuration.rb +82 -0
- data/lib/lsp/lsp_protocol_declaration.rb +73 -0
- data/lib/lsp/lsp_protocol_foldingrange.rb +129 -0
- data/lib/lsp/lsp_protocol_implementation.rb +75 -0
- data/lib/lsp/lsp_protocol_progress.rb +200 -0
- data/lib/lsp/lsp_protocol_selectionrange.rb +79 -0
- data/lib/lsp/lsp_protocol_sematictokens.proposed.rb +340 -0
- data/lib/lsp/lsp_protocol_typedefinition.rb +75 -0
- data/lib/lsp/lsp_protocol_workspacefolders.rb +174 -0
- data/lib/lsp/lsp_types.rb +1534 -0
- data/lib/puppet-debugserver/debug_session/break_points.rb +137 -0
- data/lib/puppet-debugserver/debug_session/flow_control.rb +161 -0
- data/lib/puppet-debugserver/debug_session/hook_handlers.rb +295 -0
- data/lib/puppet-debugserver/debug_session/puppet_session_run_mode.rb +66 -0
- data/lib/puppet-debugserver/debug_session/puppet_session_state.rb +122 -0
- data/lib/puppet-debugserver/hooks.rb +132 -0
- data/lib/puppet-debugserver/message_handler.rb +277 -0
- data/lib/puppet-debugserver/puppet_debug_session.rb +541 -0
- data/lib/puppet-debugserver/puppet_monkey_patches.rb +118 -0
- data/lib/puppet-languageserver/client_session_state.rb +119 -0
- data/lib/puppet-languageserver/crash_dump.rb +50 -0
- data/lib/puppet-languageserver/epp/validation_provider.rb +34 -0
- data/lib/puppet-languageserver/facter_helper.rb +25 -0
- data/lib/puppet-languageserver/global_queues/sidecar_queue.rb +205 -0
- data/lib/puppet-languageserver/global_queues/single_instance_queue.rb +126 -0
- data/lib/puppet-languageserver/global_queues/validation_queue.rb +102 -0
- data/lib/puppet-languageserver/global_queues.rb +16 -0
- data/lib/puppet-languageserver/manifest/completion_provider.rb +331 -0
- data/lib/puppet-languageserver/manifest/definition_provider.rb +99 -0
- data/lib/puppet-languageserver/manifest/document_symbol_provider.rb +228 -0
- data/lib/puppet-languageserver/manifest/folding_provider.rb +226 -0
- data/lib/puppet-languageserver/manifest/format_on_type_provider.rb +143 -0
- data/lib/puppet-languageserver/manifest/hover_provider.rb +221 -0
- data/lib/puppet-languageserver/manifest/signature_provider.rb +169 -0
- data/lib/puppet-languageserver/manifest/validation_provider.rb +127 -0
- data/lib/puppet-languageserver/message_handler.rb +462 -0
- data/lib/puppet-languageserver/providers.rb +18 -0
- data/lib/puppet-languageserver/puppet_helper.rb +108 -0
- data/lib/puppet-languageserver/puppet_lexer_helper.rb +55 -0
- data/lib/puppet-languageserver/puppet_monkey_patches.rb +39 -0
- data/lib/puppet-languageserver/puppet_parser_helper.rb +212 -0
- data/lib/puppet-languageserver/puppetfile/validation_provider.rb +185 -0
- data/lib/puppet-languageserver/server_capabilities.rb +48 -0
- data/lib/puppet-languageserver/session_state/document_store.rb +272 -0
- data/lib/puppet-languageserver/session_state/language_client.rb +239 -0
- data/lib/puppet-languageserver/session_state/object_cache.rb +162 -0
- data/lib/puppet-languageserver/sidecar_protocol.rb +532 -0
- data/lib/puppet-languageserver/uri_helper.rb +46 -0
- data/lib/puppet-languageserver-sidecar/cache/base.rb +36 -0
- data/lib/puppet-languageserver-sidecar/cache/filesystem.rb +111 -0
- data/lib/puppet-languageserver-sidecar/cache/null.rb +27 -0
- data/lib/puppet-languageserver-sidecar/facter_helper.rb +41 -0
- data/lib/puppet-languageserver-sidecar/puppet_environment_monkey_patches.rb +52 -0
- data/lib/puppet-languageserver-sidecar/puppet_helper.rb +281 -0
- data/lib/puppet-languageserver-sidecar/puppet_modulepath_monkey_patches.rb +146 -0
- data/lib/puppet-languageserver-sidecar/puppet_monkey_patches.rb +9 -0
- data/lib/puppet-languageserver-sidecar/puppet_parser_helper.rb +77 -0
- data/lib/puppet-languageserver-sidecar/puppet_strings_helper.rb +399 -0
- data/lib/puppet-languageserver-sidecar/puppet_strings_monkey_patches.rb +16 -0
- data/lib/puppet-languageserver-sidecar/sidecar_protocol_extensions.rb +16 -0
- data/lib/puppet-languageserver-sidecar/workspace.rb +89 -0
- data/lib/puppet_debugserver.rb +164 -0
- data/lib/puppet_editor_services/connection/base.rb +62 -0
- data/lib/puppet_editor_services/connection/stdio.rb +25 -0
- data/lib/puppet_editor_services/connection/tcp.rb +34 -0
- data/lib/puppet_editor_services/handler/base.rb +16 -0
- data/lib/puppet_editor_services/handler/debug_adapter.rb +63 -0
- data/lib/puppet_editor_services/handler/json_rpc.rb +133 -0
- data/lib/puppet_editor_services/logging.rb +45 -0
- data/lib/puppet_editor_services/protocol/base.rb +27 -0
- data/lib/puppet_editor_services/protocol/debug_adapter.rb +135 -0
- data/lib/puppet_editor_services/protocol/debug_adapter_messages.rb +171 -0
- data/lib/puppet_editor_services/protocol/json_rpc.rb +241 -0
- data/lib/puppet_editor_services/protocol/json_rpc_messages.rb +200 -0
- data/lib/puppet_editor_services/server/base.rb +42 -0
- data/lib/puppet_editor_services/server/stdio.rb +85 -0
- data/lib/puppet_editor_services/server/tcp.rb +349 -0
- data/lib/puppet_editor_services/server.rb +15 -0
- data/lib/puppet_editor_services/version.rb +36 -0
- data/lib/puppet_editor_services.rb +8 -0
- data/lib/puppet_languageserver.rb +263 -0
- data/lib/puppet_languageserver_sidecar.rb +361 -0
- data/puppet-debugserver +11 -0
- data/puppet-editor-services.gemspec +29 -0
- data/puppet-languageserver +15 -0
- data/puppet-languageserver-sidecar +14 -0
- metadata +240 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dsp/dsp'
|
|
4
|
+
require 'puppet_editor_services'
|
|
5
|
+
|
|
6
|
+
require 'optparse'
|
|
7
|
+
require 'logger'
|
|
8
|
+
|
|
9
|
+
module PuppetDebugServer
|
|
10
|
+
def self.version
|
|
11
|
+
PuppetEditorServices.version
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.require_gems(options)
|
|
15
|
+
original_verbose = $VERBOSE
|
|
16
|
+
$VERBOSE = nil
|
|
17
|
+
|
|
18
|
+
# Use specific Puppet Gem version if possible
|
|
19
|
+
# Note that puppet is required implicitly in the monkey patches
|
|
20
|
+
# so we don't need to explicity require it here
|
|
21
|
+
unless options[:puppet_version].nil?
|
|
22
|
+
available_puppet_gems = Gem::Specification
|
|
23
|
+
.select { |item| item.name.casecmp('puppet').zero? }
|
|
24
|
+
.map { |item| item.version.to_s }
|
|
25
|
+
if available_puppet_gems.include?(options[:puppet_version])
|
|
26
|
+
gem 'puppet', options[:puppet_version]
|
|
27
|
+
else
|
|
28
|
+
log_message(:warn, "Unable to use puppet version #{options[:puppet_version]}, as only the following versions are available [#{available_puppet_gems.join(', ')}]")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
%w[
|
|
33
|
+
message_handler
|
|
34
|
+
hooks
|
|
35
|
+
puppet_debug_session
|
|
36
|
+
debug_session/break_points
|
|
37
|
+
debug_session/hook_handlers
|
|
38
|
+
debug_session/flow_control
|
|
39
|
+
debug_session/puppet_session_run_mode
|
|
40
|
+
debug_session/puppet_session_state
|
|
41
|
+
puppet_monkey_patches
|
|
42
|
+
].each do |lib|
|
|
43
|
+
require "puppet-debugserver/#{lib}"
|
|
44
|
+
rescue LoadError
|
|
45
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'puppet-debugserver', lib))
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
$VERBOSE = original_verbose
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
class CommandLineParser
|
|
52
|
+
def self.parse(options)
|
|
53
|
+
# Set defaults here
|
|
54
|
+
args = {
|
|
55
|
+
port: nil,
|
|
56
|
+
ipaddress: 'localhost',
|
|
57
|
+
stop_on_client_exit: true,
|
|
58
|
+
connection_timeout: 10,
|
|
59
|
+
debug: nil,
|
|
60
|
+
puppet_version: nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
opt_parser = OptionParser.new do |opts|
|
|
64
|
+
opts.banner = 'Usage: puppet-debugserver.rb [options]'
|
|
65
|
+
|
|
66
|
+
opts.on('-pPORT', '--port=PORT', 'TCP Port to listen on. Default is random port') do |port|
|
|
67
|
+
args[:port] = port.to_i
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
opts.on('-ipADDRESS', '--ip=ADDRESS', "IP Address to listen on (0.0.0.0 for all interfaces). Default is #{args[:ipaddress]}") do |ipaddress|
|
|
71
|
+
args[:ipaddress] = ipaddress
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
opts.on('-tTIMEOUT', '--timeout=TIMEOUT', "Stop the Debug Server if a client does not connection within TIMEOUT seconds. A value of zero will not timeout. Default is #{args[:connection_timeout]} seconds") do |timeout|
|
|
75
|
+
args[:connection_timeout] = timeout.to_i
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
opts.on('--debug=DEBUG', "Output debug information. Either specify a filename or 'STDOUT'. Default is no debug output") do |debug|
|
|
79
|
+
args[:debug] = debug
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
opts.on('--puppet-version=TEXT', String, 'The version of the Puppet Gem to use (defaults to latest version if not specified or the version does not exist) e.g. --puppet-version=5.4.0') do |text|
|
|
83
|
+
args[:puppet_version] = text
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
opts.on('-h', '--help', 'Prints this help') do
|
|
87
|
+
puts opts
|
|
88
|
+
exit
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
opts.on('-v', '--version', 'Prints the Debug Server version') do
|
|
92
|
+
puts PuppetEditorServices.version
|
|
93
|
+
exit
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
opt_parser.parse!(options.dup)
|
|
98
|
+
args
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.log_message(severity, message)
|
|
103
|
+
PuppetEditorServices.log_message(severity, message)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.init_puppet(options)
|
|
107
|
+
PuppetEditorServices.init_logging(options)
|
|
108
|
+
log_message(:info, "Debug Server is v#{PuppetDebugServer.version}")
|
|
109
|
+
log_message(:debug, 'Loading gems...')
|
|
110
|
+
require_gems(options)
|
|
111
|
+
require 'puppet'
|
|
112
|
+
log_message(:info, "Using Puppet v#{::Puppet.version}")
|
|
113
|
+
|
|
114
|
+
raise("Detected Puppet #{Puppet.version} however the Debug Server requires Puppet 5.0 and above") if Gem::Version.new(Puppet.version) < Gem::Version.new('5.0.0')
|
|
115
|
+
|
|
116
|
+
true
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def self.rpc_server_async(options)
|
|
120
|
+
log_message(:info, 'Starting RPC Server (Async)...')
|
|
121
|
+
|
|
122
|
+
Thread.new do
|
|
123
|
+
Thread.current.abort_on_exception = true
|
|
124
|
+
|
|
125
|
+
require 'puppet_editor_services/protocol/debug_adapter'
|
|
126
|
+
require 'puppet_editor_services/server/tcp'
|
|
127
|
+
|
|
128
|
+
server_options = options.dup
|
|
129
|
+
protocol_options = { class: PuppetEditorServices::Protocol::DebugAdapter }.merge(options)
|
|
130
|
+
handler_options = { class: PuppetDebugServer::MessageHandler }.merge(options)
|
|
131
|
+
# TODO: Add max threads?
|
|
132
|
+
server_options[:servicename] = 'DEBUG SERVER'
|
|
133
|
+
|
|
134
|
+
log_message(:debug, 'Using TCP Server')
|
|
135
|
+
server = ::PuppetEditorServices::Server::Tcp.new(server_options, protocol_options, handler_options)
|
|
136
|
+
trap('INT') do
|
|
137
|
+
server.stop_services(true)
|
|
138
|
+
PuppetDebugServer::PuppetDebugSession.instance.flow_control.assert_flag(:terminate)
|
|
139
|
+
end
|
|
140
|
+
server.start
|
|
141
|
+
|
|
142
|
+
log_message(:info, 'Debug Server exited.')
|
|
143
|
+
# Forcibly kill the Debug Session
|
|
144
|
+
log_message(:info, 'Signalling Debug Session to terminate with extreme prejudice')
|
|
145
|
+
PuppetDebugServer::PuppetDebugSession.instance.force_terminate
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def self.execute(rpc_thread)
|
|
150
|
+
debug_session = PuppetDebugServer::PuppetDebugSession.instance
|
|
151
|
+
debug_session.initialize_session
|
|
152
|
+
|
|
153
|
+
# TODO: Can I use a real mutex here? might be hard with the rpc_thread.alive? call
|
|
154
|
+
sleep(0.5) while !debug_session.flow_control.flag?(:start_puppet) && rpc_thread.alive? && !debug_session.flow_control.terminate?
|
|
155
|
+
return unless rpc_thread.alive? || debug_session.flow_control.terminate?
|
|
156
|
+
|
|
157
|
+
debug_session.run_puppet
|
|
158
|
+
|
|
159
|
+
return unless rpc_thread.alive?
|
|
160
|
+
|
|
161
|
+
debug_session.close
|
|
162
|
+
rpc_thread.join
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetEditorServices
|
|
4
|
+
module Connection
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :server, :protocol
|
|
7
|
+
|
|
8
|
+
def initialize(server)
|
|
9
|
+
@server = server
|
|
10
|
+
@protocol = server.protocol_options[:class].new(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Override this method
|
|
14
|
+
# @api public
|
|
15
|
+
def error?
|
|
16
|
+
false
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Override this method
|
|
20
|
+
# @api public
|
|
21
|
+
def send_data(_data)
|
|
22
|
+
false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Shouldn't need to override this method
|
|
26
|
+
# @api public
|
|
27
|
+
def receive_data(data)
|
|
28
|
+
@protocol.receive_data(data)
|
|
29
|
+
rescue StandardError => e
|
|
30
|
+
server.log("Protocol #{@protocol.class} raised error #{e}: #{e.backtrace}")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Override this method
|
|
34
|
+
# @api public
|
|
35
|
+
def close_after_writing
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Override this method
|
|
40
|
+
# @api public
|
|
41
|
+
def close
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Override this method if needed
|
|
46
|
+
# @api public
|
|
47
|
+
def post_init
|
|
48
|
+
server.log("Client #{id} has connected to the server")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Override this method if needed
|
|
52
|
+
# @api public
|
|
53
|
+
def unbind
|
|
54
|
+
server.log("Client #{id} has disconnected from the server")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def id
|
|
58
|
+
object_id.to_s
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'puppet_editor_services/connection/base'
|
|
4
|
+
|
|
5
|
+
module PuppetEditorServices
|
|
6
|
+
module Connection
|
|
7
|
+
class Stdio < ::PuppetEditorServices::Connection::Base
|
|
8
|
+
def send_data(data)
|
|
9
|
+
$editor_services_stdout.write(data) # rubocop:disable Style/GlobalVars We need this global var
|
|
10
|
+
true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def close_after_writing
|
|
14
|
+
$editor_services_stdout.flush # rubocop:disable Style/GlobalVars We need this global var
|
|
15
|
+
server.close_connection
|
|
16
|
+
true
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def close
|
|
20
|
+
server.close_connection
|
|
21
|
+
true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'puppet_editor_services/connection/base'
|
|
4
|
+
|
|
5
|
+
module PuppetEditorServices
|
|
6
|
+
module Connection
|
|
7
|
+
class Tcp < ::PuppetEditorServices::Connection::Base
|
|
8
|
+
attr_accessor :socket
|
|
9
|
+
|
|
10
|
+
def initialize(server, socket)
|
|
11
|
+
super(server)
|
|
12
|
+
@socket = socket
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def send_data(data)
|
|
16
|
+
return false if socket.nil?
|
|
17
|
+
|
|
18
|
+
socket.write(data)
|
|
19
|
+
true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def close_after_writing
|
|
23
|
+
socket.flush unless socket.nil?
|
|
24
|
+
server.remove_connection_async(socket)
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def close
|
|
29
|
+
server.remove_connection_async(socket)
|
|
30
|
+
true
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetEditorServices
|
|
4
|
+
module Handler
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :protocol
|
|
7
|
+
|
|
8
|
+
def initialize(protocol)
|
|
9
|
+
@protocol = protocol
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @abstract
|
|
13
|
+
def handle(_message, _context = {}); end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'puppet_editor_services/handler/base'
|
|
4
|
+
require 'puppet_editor_services/protocol/debug_adapter_messages'
|
|
5
|
+
|
|
6
|
+
module PuppetEditorServices
|
|
7
|
+
module Handler
|
|
8
|
+
class DebugAdapter < ::PuppetEditorServices::Handler::Base
|
|
9
|
+
# options
|
|
10
|
+
# source :request, :notification etc.
|
|
11
|
+
# message JSON Message that caused the error
|
|
12
|
+
# error Actual ruby error
|
|
13
|
+
# @abstract
|
|
14
|
+
def unhandled_exception(error, options)
|
|
15
|
+
PuppetEditorServices.log_message(:error, "Unhandled exception from #{options[:source]}. JSON Message #{options[:message]}: #{error.inspect}\n#{error.backtrace}")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# context
|
|
19
|
+
def handle(json_rpc_message, context = {})
|
|
20
|
+
unless json_rpc_message.is_a?(PuppetEditorServices::Protocol::DebugAdapterMessages::Request)
|
|
21
|
+
PuppetEditorServices.log_message(:error, "Unknown JSON RPC message type #{json_rpc_message.class}")
|
|
22
|
+
return false
|
|
23
|
+
end
|
|
24
|
+
handle_request(json_rpc_message, context)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Example Request
|
|
28
|
+
#
|
|
29
|
+
# For a textDocument/completion request
|
|
30
|
+
# def request_textdocument_completion(connection_id, json_rpc_message)
|
|
31
|
+
# ...
|
|
32
|
+
# end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def handle_request(request_message, _context)
|
|
37
|
+
method_name = rpc_name_to_ruby_method_name('request', request_message.command)
|
|
38
|
+
if respond_to?(method_name.intern)
|
|
39
|
+
begin
|
|
40
|
+
result = send(method_name, protocol.connection.id, request_message)
|
|
41
|
+
protocol.encode_and_send(result) unless result.nil?
|
|
42
|
+
rescue NoMethodError, LoadError => e
|
|
43
|
+
unhandled_exception(e, source: :request, message: request_message)
|
|
44
|
+
raise
|
|
45
|
+
rescue StandardError => e
|
|
46
|
+
unhandled_exception(e, source: :request, message: request_message)
|
|
47
|
+
end
|
|
48
|
+
return true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Default processing
|
|
52
|
+
protocol.encode_and_send(::PuppetEditorServices::Protocol::DebugAdapterMessages.reply_error(request_message, "This feature is not supported - Request #{request_message.command}"))
|
|
53
|
+
PuppetEditorServices.log_message(:error, "Unknown request command #{request_message.command}")
|
|
54
|
+
|
|
55
|
+
false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def rpc_name_to_ruby_method_name(prefix, rpc_name)
|
|
59
|
+
"#{prefix}_#{rpc_name.tr('/', '_').tr('$', 'dollar').downcase}"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'puppet_editor_services/handler/base'
|
|
4
|
+
require 'puppet_editor_services/protocol/json_rpc_messages'
|
|
5
|
+
|
|
6
|
+
module PuppetEditorServices
|
|
7
|
+
module Handler
|
|
8
|
+
class JsonRPC < ::PuppetEditorServices::Handler::Base
|
|
9
|
+
# options
|
|
10
|
+
# source :request, :notification etc.
|
|
11
|
+
# message JSON Message that caused the error
|
|
12
|
+
# error Actual ruby error
|
|
13
|
+
# @abstract
|
|
14
|
+
def unhandled_exception(error, options)
|
|
15
|
+
PuppetEditorServices.log_message(:error, "Unhandled exception from #{options[:source]}. JSON Message #{options[:message]}: #{error.inspect}\n#{error.backtrace}")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# context
|
|
19
|
+
# request => original request
|
|
20
|
+
def handle(json_rpc_message, context = {})
|
|
21
|
+
case json_rpc_message
|
|
22
|
+
|
|
23
|
+
when ::PuppetEditorServices::Protocol::JsonRPCMessages::RequestMessage
|
|
24
|
+
return handle_request(json_rpc_message, context)
|
|
25
|
+
|
|
26
|
+
when ::PuppetEditorServices::Protocol::JsonRPCMessages::NotificationMessage
|
|
27
|
+
return handle_notification(json_rpc_message, context)
|
|
28
|
+
|
|
29
|
+
when ::PuppetEditorServices::Protocol::JsonRPCMessages::ResponseMessage
|
|
30
|
+
return handle_response(json_rpc_message, context)
|
|
31
|
+
|
|
32
|
+
else
|
|
33
|
+
PuppetEditorServices.log_message(:error, "Unknown JSON RPC message type #{json_rpc_message.class}")
|
|
34
|
+
end
|
|
35
|
+
false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Example Request
|
|
39
|
+
#
|
|
40
|
+
# For a textDocument/completion request
|
|
41
|
+
# def request_textdocument_completion(connection_id, json_rpc_message)
|
|
42
|
+
# ...
|
|
43
|
+
# end
|
|
44
|
+
|
|
45
|
+
# Example Notification
|
|
46
|
+
# For a workspace/didChangeNotification notification
|
|
47
|
+
# def notification_workspace_didchangeconfiguration(connection_id, json_rpc_message)
|
|
48
|
+
# ...
|
|
49
|
+
# end
|
|
50
|
+
|
|
51
|
+
# Example Response
|
|
52
|
+
# A response to a client/registerCapability request
|
|
53
|
+
# def notification_workspace_didchangeconfiguration(connection_id, json_rpc_message, original_request)
|
|
54
|
+
# ...
|
|
55
|
+
# end
|
|
56
|
+
|
|
57
|
+
private
|
|
58
|
+
|
|
59
|
+
def handle_request(request_message, _context)
|
|
60
|
+
method_name = rpc_name_to_ruby_method_name('request', request_message.rpc_method)
|
|
61
|
+
if respond_to?(method_name.intern)
|
|
62
|
+
begin
|
|
63
|
+
protocol.encode_and_send(
|
|
64
|
+
::PuppetEditorServices::Protocol::JsonRPCMessages.reply_result(
|
|
65
|
+
request_message, send(method_name, protocol.connection.id, request_message)
|
|
66
|
+
)
|
|
67
|
+
)
|
|
68
|
+
rescue StandardError => e
|
|
69
|
+
unhandled_exception(e, source: :request, message: request_message)
|
|
70
|
+
end
|
|
71
|
+
return true
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Default processing
|
|
75
|
+
protocol.encode_and_send(::PuppetEditorServices::Protocol::JsonRPCMessages.reply_method_not_found(request_message))
|
|
76
|
+
if request_message.rpc_method.start_with?('$/')
|
|
77
|
+
PuppetEditorServices.log_message(:debug, "Ignoring RPC request #{request_message.rpc_method}")
|
|
78
|
+
else
|
|
79
|
+
PuppetEditorServices.log_message(:error, "Unknown RPC method #{request_message.rpc_method}")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def handle_notification(notification_message, _context)
|
|
86
|
+
method_name = rpc_name_to_ruby_method_name('notification', notification_message.rpc_method)
|
|
87
|
+
if respond_to?(method_name.intern)
|
|
88
|
+
begin
|
|
89
|
+
send(method_name, protocol.connection.id, notification_message)
|
|
90
|
+
rescue StandardError => e
|
|
91
|
+
unhandled_exception(e, source: :notification, message: notification_message)
|
|
92
|
+
end
|
|
93
|
+
return true
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Default processing
|
|
97
|
+
if notification_message.rpc_method.start_with?('$/')
|
|
98
|
+
PuppetEditorServices.log_message(:debug, "Ignoring RPC notification #{notification_message.rpc_method}")
|
|
99
|
+
else
|
|
100
|
+
PuppetEditorServices.log_message(:error, "Unknown RPC notification #{notification_message.rpc_method}")
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
false
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def handle_response(response_message, context)
|
|
107
|
+
original_request = context[:request]
|
|
108
|
+
return false if original_request.nil?
|
|
109
|
+
|
|
110
|
+
unless response_message.is_successful # rubocop:disable Style/IfUnlessModifier Line is too long otherwise
|
|
111
|
+
PuppetEditorServices.log_message(:error, "Response for method '#{original_request.rpc_method}' with id '#{original_request.id}' failed with #{response_message.error}")
|
|
112
|
+
end
|
|
113
|
+
method_name = rpc_name_to_ruby_method_name('response', original_request.rpc_method)
|
|
114
|
+
if respond_to?(method_name.intern)
|
|
115
|
+
begin
|
|
116
|
+
send(method_name, protocol.connection.id, response_message, original_request)
|
|
117
|
+
rescue StandardError => e
|
|
118
|
+
unhandled_exception(e, source: :response, message: response_message)
|
|
119
|
+
end
|
|
120
|
+
return true
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Default processing
|
|
124
|
+
PuppetEditorServices.log_message(:error, "Unknown RPC response for method #{original_request.rpc_method}")
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def rpc_name_to_ruby_method_name(prefix, rpc_name)
|
|
129
|
+
"#{prefix}_#{rpc_name.tr('/', '_').tr('$', 'dollar').downcase}"
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetEditorServices
|
|
4
|
+
def self.log_message(severity, message)
|
|
5
|
+
return if @logger.nil?
|
|
6
|
+
|
|
7
|
+
case severity
|
|
8
|
+
when :debug
|
|
9
|
+
@logger.debug(message)
|
|
10
|
+
when :info
|
|
11
|
+
@logger.info(message)
|
|
12
|
+
when :warn
|
|
13
|
+
@logger.warn(message)
|
|
14
|
+
when :error
|
|
15
|
+
@logger.error(message)
|
|
16
|
+
when :fatal
|
|
17
|
+
@logger.fatal(message)
|
|
18
|
+
else
|
|
19
|
+
@logger.unknown(message)
|
|
20
|
+
end
|
|
21
|
+
@log_file.fsync unless @log_file.nil?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.init_logging(options)
|
|
25
|
+
@log_file = nil
|
|
26
|
+
if options[:debug].nil?
|
|
27
|
+
@logger = nil
|
|
28
|
+
elsif (options[:debug].casecmp 'stdout').zero?
|
|
29
|
+
@logger = Logger.new($stdout)
|
|
30
|
+
elsif !options[:debug].to_s.empty?
|
|
31
|
+
# Log to file
|
|
32
|
+
begin
|
|
33
|
+
@log_file = File.open(options[:debug], 'w')
|
|
34
|
+
rescue Errno::ENOENT => e
|
|
35
|
+
# We can't open the log file and we can't log to STDOUT if we're in STDIO mode
|
|
36
|
+
# So log the error to STDERR and disable logging
|
|
37
|
+
$stderr.puts "Error opening log file #{options[:debug]} : #{e}" # rubocop:disable Style/StderrPuts
|
|
38
|
+
@log_file = nil
|
|
39
|
+
return
|
|
40
|
+
end
|
|
41
|
+
@log_file.sync = true
|
|
42
|
+
@logger = Logger.new(@log_file)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetEditorServices
|
|
4
|
+
module Protocol
|
|
5
|
+
class Base
|
|
6
|
+
attr_reader :connection, :handler
|
|
7
|
+
|
|
8
|
+
def initialize(connection)
|
|
9
|
+
@connection = connection
|
|
10
|
+
@handler = connection.server.handler_options[:class].new(self)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @abstract
|
|
14
|
+
def receive_data(data); end
|
|
15
|
+
|
|
16
|
+
def close_connection
|
|
17
|
+
connection.close unless connection.nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def connection_error?
|
|
21
|
+
return false if connection.nil?
|
|
22
|
+
|
|
23
|
+
connection.error?
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|