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,462 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'puppet_editor_services/handler/json_rpc'
|
|
4
|
+
require 'puppet_editor_services/protocol/json_rpc_messages'
|
|
5
|
+
require 'puppet-languageserver/server_capabilities'
|
|
6
|
+
require 'puppet-languageserver/client_session_state'
|
|
7
|
+
require 'puppet-languageserver/global_queues'
|
|
8
|
+
|
|
9
|
+
module PuppetLanguageServer
|
|
10
|
+
class MessageHandler < PuppetEditorServices::Handler::JsonRPC
|
|
11
|
+
def initialize(*_)
|
|
12
|
+
super
|
|
13
|
+
@session_state = ClientSessionState.new(self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def session_state # rubocop:disable Style/TrivialAccessors During the refactor, this is fine.
|
|
17
|
+
@session_state
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def language_client
|
|
21
|
+
session_state.language_client
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def documents
|
|
25
|
+
session_state.documents
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def request_initialize(_, json_rpc_message)
|
|
29
|
+
PuppetLanguageServer.log_message(:debug, 'Received initialize method')
|
|
30
|
+
|
|
31
|
+
language_client.parse_lsp_initialize!(json_rpc_message.params)
|
|
32
|
+
static_folding_provider = !language_client.client_capability('textDocument', 'foldingRange', 'dynamicRegistration') &&
|
|
33
|
+
PuppetLanguageServer::ServerCapabilites.folding_provider_supported?
|
|
34
|
+
# Setup static registrations if dynamic registration is not available
|
|
35
|
+
info = {
|
|
36
|
+
documentOnTypeFormattingProvider: !language_client.client_capability('textDocument', 'onTypeFormatting', 'dynamicRegistration'),
|
|
37
|
+
foldingRangeProvider: static_folding_provider
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Configure the document store
|
|
41
|
+
documents.initialize_store(
|
|
42
|
+
workspace: workspace_root_from_initialize_params(json_rpc_message.params)
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# Initiate loading the object_cache
|
|
46
|
+
session_state.load_default_data!
|
|
47
|
+
session_state.load_static_data!
|
|
48
|
+
|
|
49
|
+
# Initiate loading of the workspace if needed
|
|
50
|
+
session_state.load_workspace_data! if documents.store_has_module_metadata? || documents.store_has_environmentconf?
|
|
51
|
+
|
|
52
|
+
{ 'capabilities' => PuppetLanguageServer::ServerCapabilites.capabilities(info) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def request_shutdown(_, _json_rpc_message)
|
|
56
|
+
PuppetLanguageServer.log_message(:debug, 'Received shutdown method')
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def request_puppet_getversion(_, _json_rpc_message)
|
|
61
|
+
LSP::PuppetVersion.new(
|
|
62
|
+
'languageServerVersion' => PuppetEditorServices.version,
|
|
63
|
+
'puppetVersion' => Puppet.version,
|
|
64
|
+
'facterVersion' => Facter.version,
|
|
65
|
+
'factsLoaded' => session_state.facts_loaded?,
|
|
66
|
+
'functionsLoaded' => session_state.default_functions_loaded?,
|
|
67
|
+
'typesLoaded' => session_state.default_types_loaded?,
|
|
68
|
+
'classesLoaded' => session_state.default_classes_loaded?
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def request_puppet_getfacts(_, _json_rpc_message)
|
|
73
|
+
results = PuppetLanguageServer::FacterHelper.facts_to_hash(session_state)
|
|
74
|
+
LSP::PuppetFactResponse.new('facts' => results)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def request_puppet_getresource(_, json_rpc_message)
|
|
78
|
+
type_name = json_rpc_message.params['typename']
|
|
79
|
+
title = json_rpc_message.params['title']
|
|
80
|
+
return LSP::PuppetResourceResponse.new('error' => 'Missing Typename') if type_name.nil?
|
|
81
|
+
|
|
82
|
+
resource_list = PuppetLanguageServer::PuppetHelper.get_puppet_resource(session_state, type_name, title, documents.store_root_path)
|
|
83
|
+
return LSP::PuppetResourceResponse.new('data' => '') if resource_list.nil? || resource_list.length.zero?
|
|
84
|
+
|
|
85
|
+
content = "#{resource_list.map(&:manifest).join("\n\n")}\n"
|
|
86
|
+
LSP::PuppetResourceResponse.new('data' => content)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def request_puppet_compilenodegraph(_, json_rpc_message)
|
|
90
|
+
file_uri = json_rpc_message.params['external']
|
|
91
|
+
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
|
|
92
|
+
|
|
93
|
+
document = documents.document(file_uri)
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
node_graph = PuppetLanguageServer::PuppetHelper.get_node_graph(session_state, document.content, documents.store_root_path)
|
|
97
|
+
LSP::PuppetNodeGraphResponse.new('vertices' => node_graph.vertices,
|
|
98
|
+
'edges' => node_graph.edges,
|
|
99
|
+
'error' => node_graph.error_content)
|
|
100
|
+
rescue StandardError => e
|
|
101
|
+
PuppetLanguageServer.log_message(:error, "(puppet/compileNodeGraph) Error generating node graph. #{e}")
|
|
102
|
+
LSP::PuppetNodeGraphResponse.new('error' => 'An internal error occured while generating the the node graph. Please see the debug log files for more information.')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def request_puppetfile_getdependencies(_, json_rpc_message)
|
|
107
|
+
file_uri = json_rpc_message.params['uri']
|
|
108
|
+
return LSP::PuppetfileDependencyResponse.new('error' => 'Must be a puppetfile in order to find dependencies.') unless documents.document_type(file_uri) == :puppetfile
|
|
109
|
+
|
|
110
|
+
document = documents.document(file_uri)
|
|
111
|
+
|
|
112
|
+
result = []
|
|
113
|
+
begin
|
|
114
|
+
result = PuppetLanguageServer::Puppetfile::ValidationProvider.find_dependencies(document.content)
|
|
115
|
+
rescue StandardError => e
|
|
116
|
+
PuppetLanguageServer.log_message(:error, "(puppetfile/getdependencies) Error parsing puppetfile. #{e}")
|
|
117
|
+
return LSP::PuppetfileDependencyResponse.new('error' => 'An internal error occured while parsing the puppetfile. Please see the debug log files for more information.')
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
LSP::PuppetfileDependencyResponse.new('dependencies' => result)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def request_puppet_fixdiagnosticerrors(_, json_rpc_message)
|
|
124
|
+
formatted_request = LSP::PuppetFixDiagnosticErrorsRequest.new(json_rpc_message.params)
|
|
125
|
+
file_uri = formatted_request.documentUri
|
|
126
|
+
content = documents.document_content(file_uri)
|
|
127
|
+
|
|
128
|
+
case documents.document_type(file_uri)
|
|
129
|
+
when :manifest
|
|
130
|
+
changes, new_content = PuppetLanguageServer::Manifest::ValidationProvider.fix_validate_errors(session_state, content)
|
|
131
|
+
else
|
|
132
|
+
raise "Unable to fixDiagnosticErrors on #{file_uri}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
LSP::PuppetFixDiagnosticErrorsResponse.new(
|
|
136
|
+
'documentUri' => formatted_request.documentUri,
|
|
137
|
+
'fixesApplied' => changes,
|
|
138
|
+
'newContent' => changes > 0 || formatted_request.alwaysReturnContent ? new_content : nil
|
|
139
|
+
)
|
|
140
|
+
rescue StandardError => e
|
|
141
|
+
PuppetLanguageServer.log_message(:error, "(puppet/fixDiagnosticErrors) #{e}")
|
|
142
|
+
unless formatted_request.nil?
|
|
143
|
+
LSP::PuppetFixDiagnosticErrorsResponse.new(
|
|
144
|
+
'documentUri' => formatted_request.documentUri,
|
|
145
|
+
'fixesApplied' => 0,
|
|
146
|
+
'newContent' => formatted_request.alwaysReturnContent ? content : nil
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def request_textdocument_completion(_, json_rpc_message)
|
|
152
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
153
|
+
line_num = json_rpc_message.params['position']['line']
|
|
154
|
+
char_num = json_rpc_message.params['position']['character']
|
|
155
|
+
content = documents.document_content(file_uri)
|
|
156
|
+
context = json_rpc_message.params['context'].nil? ? nil : LSP::CompletionContext.new(json_rpc_message.params['context'])
|
|
157
|
+
|
|
158
|
+
case documents.document_type(file_uri)
|
|
159
|
+
when :manifest
|
|
160
|
+
PuppetLanguageServer::Manifest::CompletionProvider.complete(session_state, content, line_num, char_num, context: context, tasks_mode: documents.plan_file?(file_uri))
|
|
161
|
+
else
|
|
162
|
+
raise "Unable to provide completion on #{file_uri}"
|
|
163
|
+
end
|
|
164
|
+
rescue StandardError => e
|
|
165
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/completion) #{e}")
|
|
166
|
+
LSP::CompletionList.new('isIncomplete' => false, 'items' => [])
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def request_textdocument_foldingrange(_, json_rpc_message)
|
|
170
|
+
return nil unless language_client.folding_range
|
|
171
|
+
|
|
172
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
173
|
+
case documents.document_type(file_uri)
|
|
174
|
+
when :manifest
|
|
175
|
+
PuppetLanguageServer::Manifest::FoldingProvider.instance.folding_ranges(documents.document_tokens(file_uri))
|
|
176
|
+
else
|
|
177
|
+
raise "Unable to provide folding ranages on #{file_uri}"
|
|
178
|
+
end
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/foldingRange) #{e}")
|
|
181
|
+
nil
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def request_completionitem_resolve(_, json_rpc_message)
|
|
185
|
+
PuppetLanguageServer::Manifest::CompletionProvider.resolve(session_state, LSP::CompletionItem.new(json_rpc_message.params))
|
|
186
|
+
rescue StandardError => e
|
|
187
|
+
PuppetLanguageServer.log_message(:error, "(completionItem/resolve) #{e}")
|
|
188
|
+
# Spit back the same params if an error happens
|
|
189
|
+
json_rpc_message.params
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def request_textdocument_hover(_, json_rpc_message)
|
|
193
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
194
|
+
line_num = json_rpc_message.params['position']['line']
|
|
195
|
+
char_num = json_rpc_message.params['position']['character']
|
|
196
|
+
content = documents.document_content(file_uri)
|
|
197
|
+
case documents.document_type(file_uri)
|
|
198
|
+
when :manifest
|
|
199
|
+
PuppetLanguageServer::Manifest::HoverProvider.resolve(session_state, content, line_num, char_num, tasks_mode: documents.plan_file?(file_uri))
|
|
200
|
+
else
|
|
201
|
+
raise "Unable to provide hover on #{file_uri}"
|
|
202
|
+
end
|
|
203
|
+
rescue StandardError => e
|
|
204
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/hover) #{e}")
|
|
205
|
+
nil
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def request_textdocument_definition(_, json_rpc_message)
|
|
209
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
210
|
+
line_num = json_rpc_message.params['position']['line']
|
|
211
|
+
char_num = json_rpc_message.params['position']['character']
|
|
212
|
+
content = documents.document_content(file_uri)
|
|
213
|
+
|
|
214
|
+
case documents.document_type(file_uri)
|
|
215
|
+
when :manifest
|
|
216
|
+
PuppetLanguageServer::Manifest::DefinitionProvider.find_definition(session_state, content, line_num, char_num, tasks_mode: documents.plan_file?(file_uri))
|
|
217
|
+
else
|
|
218
|
+
raise "Unable to provide definition on #{file_uri}"
|
|
219
|
+
end
|
|
220
|
+
rescue StandardError => e
|
|
221
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/definition) #{e}")
|
|
222
|
+
nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def request_textdocument_documentsymbol(_, json_rpc_message)
|
|
226
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
227
|
+
content = documents.document_content(file_uri)
|
|
228
|
+
|
|
229
|
+
case documents.document_type(file_uri)
|
|
230
|
+
when :manifest
|
|
231
|
+
PuppetLanguageServer::Manifest::DocumentSymbolProvider.extract_document_symbols(content, tasks_mode: documents.plan_file?(file_uri))
|
|
232
|
+
else
|
|
233
|
+
raise "Unable to provide definition on #{file_uri}"
|
|
234
|
+
end
|
|
235
|
+
rescue StandardError => e
|
|
236
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/documentSymbol) #{e}")
|
|
237
|
+
nil
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def request_textdocument_ontypeformatting(_, json_rpc_message)
|
|
241
|
+
return nil unless language_client.format_on_type
|
|
242
|
+
|
|
243
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
244
|
+
line_num = json_rpc_message.params['position']['line']
|
|
245
|
+
char_num = json_rpc_message.params['position']['character']
|
|
246
|
+
content = documents.document_content(file_uri)
|
|
247
|
+
|
|
248
|
+
case documents.document_type(file_uri)
|
|
249
|
+
when :manifest
|
|
250
|
+
PuppetLanguageServer::Manifest::FormatOnTypeProvider.instance.format(
|
|
251
|
+
content,
|
|
252
|
+
line_num,
|
|
253
|
+
char_num,
|
|
254
|
+
json_rpc_message.params['ch'],
|
|
255
|
+
json_rpc_message.params['options'],
|
|
256
|
+
language_client.format_on_type_filesize_limit
|
|
257
|
+
)
|
|
258
|
+
else
|
|
259
|
+
raise "Unable to format on type on #{file_uri}"
|
|
260
|
+
end
|
|
261
|
+
rescue StandardError => e
|
|
262
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/onTypeFormatting) #{e}")
|
|
263
|
+
nil
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def request_textdocument_signaturehelp(_, json_rpc_message)
|
|
267
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
268
|
+
line_num = json_rpc_message.params['position']['line']
|
|
269
|
+
char_num = json_rpc_message.params['position']['character']
|
|
270
|
+
content = documents.document_content(file_uri)
|
|
271
|
+
|
|
272
|
+
case documents.document_type(file_uri)
|
|
273
|
+
when :manifest
|
|
274
|
+
PuppetLanguageServer::Manifest::SignatureProvider.signature_help(
|
|
275
|
+
session_state,
|
|
276
|
+
content,
|
|
277
|
+
line_num,
|
|
278
|
+
char_num,
|
|
279
|
+
tasks_mode: documents.plan_file?(file_uri)
|
|
280
|
+
)
|
|
281
|
+
else
|
|
282
|
+
raise "Unable to provide signatures on #{file_uri}"
|
|
283
|
+
end
|
|
284
|
+
rescue StandardError => e
|
|
285
|
+
PuppetLanguageServer.log_message(:error, "(textDocument/signatureHelp) #{e}")
|
|
286
|
+
nil
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def request_workspace_symbol(_, json_rpc_message)
|
|
290
|
+
result = []
|
|
291
|
+
result.concat(PuppetLanguageServer::Manifest::DocumentSymbolProvider.workspace_symbols(json_rpc_message.params['query'], session_state.object_cache))
|
|
292
|
+
result
|
|
293
|
+
rescue StandardError => e
|
|
294
|
+
PuppetLanguageServer.log_message(:error, "(workspace/symbol) #{e}")
|
|
295
|
+
[]
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def notification_initialized(_, _json_rpc_message)
|
|
299
|
+
PuppetLanguageServer.log_message(:info, 'Client has received initialization')
|
|
300
|
+
# Raise a warning if the Puppet version is mismatched
|
|
301
|
+
server_options = protocol.connection.server.server_options
|
|
302
|
+
unless server_options[:puppet_version].nil? || server_options[:puppet_version] == Puppet.version
|
|
303
|
+
protocol.encode_and_send(
|
|
304
|
+
::PuppetEditorServices::Protocol::JsonRPCMessages.new_notification(
|
|
305
|
+
'window/showMessage',
|
|
306
|
+
'type' => LSP::MessageType::WARNING,
|
|
307
|
+
'message' => "Unable to use Puppet version '#{server_options[:puppet_version]}' as it is not available. Using version '#{Puppet.version}' instead."
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# Register for workspace setting changes if it's supported
|
|
313
|
+
if language_client.client_capability('workspace', 'didChangeConfiguration', 'dynamicRegistration') == true
|
|
314
|
+
language_client.register_capability('workspace/didChangeConfiguration')
|
|
315
|
+
else
|
|
316
|
+
PuppetLanguageServer.log_message(:debug, 'Client does not support didChangeConfiguration dynamic registration. Using push method for configuration change detection.')
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def notification_exit(_, _json_rpc_message)
|
|
321
|
+
PuppetLanguageServer.log_message(:info, 'Received exit notification. Closing connection to client...')
|
|
322
|
+
protocol.connection.close
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def notification_textdocument_didopen(client_handler_id, json_rpc_message)
|
|
326
|
+
PuppetLanguageServer.log_message(:info, 'Received textDocument/didOpen notification.')
|
|
327
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
328
|
+
content = json_rpc_message.params['textDocument']['text']
|
|
329
|
+
doc_version = json_rpc_message.params['textDocument']['version']
|
|
330
|
+
documents.set_document(file_uri, content, doc_version)
|
|
331
|
+
enqueue_validation(file_uri, doc_version, client_handler_id)
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
def notification_textdocument_didclose(client_handler_id, json_rpc_message)
|
|
335
|
+
PuppetLanguageServer.log_message(:info, 'Received textDocument/didClose notification.')
|
|
336
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
337
|
+
documents.remove_document(file_uri)
|
|
338
|
+
enqueue_validation(file_uri, nil, client_handler_id)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def notification_textdocument_didchange(client_handler_id, json_rpc_message)
|
|
342
|
+
PuppetLanguageServer.log_message(:info, 'Received textDocument/didChange notification.')
|
|
343
|
+
file_uri = json_rpc_message.params['textDocument']['uri']
|
|
344
|
+
content = json_rpc_message.params['contentChanges'][0]['text'] # TODO: Bad hardcoding zero
|
|
345
|
+
doc_version = json_rpc_message.params['textDocument']['version']
|
|
346
|
+
documents.set_document(file_uri, content, doc_version)
|
|
347
|
+
enqueue_validation(file_uri, doc_version, client_handler_id)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def notification_textdocument_didsave(_, _json_rpc_message)
|
|
351
|
+
PuppetLanguageServer.log_message(:info, 'Received textDocument/didSave notification.')
|
|
352
|
+
# Expire the store cache so that the store information can re-evaluated
|
|
353
|
+
documents.expire_store_information
|
|
354
|
+
if documents.store_has_module_metadata? || documents.store_has_environmentconf?
|
|
355
|
+
# Load the workspace information
|
|
356
|
+
session_state.load_workspace_data!
|
|
357
|
+
else
|
|
358
|
+
# Purge the workspace information
|
|
359
|
+
session_state.purge_workspace_data!
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def notification_workspace_didchangeconfiguration(_, json_rpc_message)
|
|
364
|
+
if json_rpc_message.params.key?('settings') && json_rpc_message.params['settings'].nil?
|
|
365
|
+
# This is a notification from a dynamic registration. Need to send a workspace/configuration
|
|
366
|
+
# request to get the actual configuration
|
|
367
|
+
language_client.send_configuration_request
|
|
368
|
+
else
|
|
369
|
+
language_client.parse_lsp_configuration_settings!(json_rpc_message.params['settings'])
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def response_client_registercapability(_, json_rpc_message, original_request)
|
|
374
|
+
language_client.parse_register_capability_response!(json_rpc_message, original_request)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def response_client_unregistercapability(_, json_rpc_message, original_request)
|
|
378
|
+
language_client.parse_unregister_capability_response!(json_rpc_message, original_request)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def response_workspace_configuration(_, json_rpc_message, original_request)
|
|
382
|
+
return unless json_rpc_message.is_successful
|
|
383
|
+
|
|
384
|
+
original_request.params.items.each_with_index do |item, index|
|
|
385
|
+
# The response from the client strips the section name so we need to re-add it
|
|
386
|
+
language_client.parse_lsp_configuration_settings!(item.section => json_rpc_message.result[index])
|
|
387
|
+
end
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def unhandled_exception(error, options)
|
|
391
|
+
super
|
|
392
|
+
PuppetLanguageServer::CrashDump.write_crash_file(error, session_state, nil, options)
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
private
|
|
396
|
+
|
|
397
|
+
def enqueue_validation(file_uri, doc_version, client_handler_id, options = {})
|
|
398
|
+
if documents.document_type(file_uri) == :puppetfile
|
|
399
|
+
options[:resolve_puppetfile] = language_client.use_puppetfile_resolver
|
|
400
|
+
options[:puppet_version] = Puppet.version
|
|
401
|
+
options[:module_path] = PuppetLanguageServer::PuppetHelper.module_path
|
|
402
|
+
end
|
|
403
|
+
GlobalQueues.validate_queue.enqueue(file_uri, doc_version, client_handler_id, options)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
def workspace_root_from_initialize_params(params)
|
|
407
|
+
if params.key?('workspaceFolders')
|
|
408
|
+
return nil if params['workspaceFolders'].nil? || params['workspaceFolders'].empty?
|
|
409
|
+
|
|
410
|
+
# We don't support multiple workspace folders yet, so just select the first one
|
|
411
|
+
return UriHelper.uri_path(params['workspaceFolders'][0]['uri'])
|
|
412
|
+
end
|
|
413
|
+
return UriHelper.uri_path(params['rootUri']) if params.key?('rootUri') && !params['rootUri'].nil?
|
|
414
|
+
|
|
415
|
+
params['rootPath']
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
class DisabledMessageHandler < PuppetEditorServices::Handler::JsonRPC
|
|
420
|
+
def request_initialize(_, _json_rpc_message)
|
|
421
|
+
PuppetLanguageServer.log_message(:debug, 'Received initialize method')
|
|
422
|
+
# If the Language Server is not active then we can not respond to any capability
|
|
423
|
+
{ 'capabilities' => PuppetLanguageServer::ServerCapabilites.no_capabilities }
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
def request_shutdown(_, _json_rpc_message)
|
|
427
|
+
PuppetLanguageServer.log_message(:debug, 'Received shutdown method')
|
|
428
|
+
nil
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def request_puppet_getversion(_, _json_rpc_message)
|
|
432
|
+
# Clients may use the getVersion request to figure out when the server has "finished" loading. In this
|
|
433
|
+
# case just fake the response that we are fully loaded with unknown gem versions
|
|
434
|
+
LSP::PuppetVersion.new(
|
|
435
|
+
'languageServerVersion' => PuppetEditorServices.version,
|
|
436
|
+
'puppetVersion' => 'Unknown',
|
|
437
|
+
'facterVersion' => 'Unknown',
|
|
438
|
+
'factsLoaded' => true,
|
|
439
|
+
'functionsLoaded' => true,
|
|
440
|
+
'typesLoaded' => true,
|
|
441
|
+
'classesLoaded' => true
|
|
442
|
+
)
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def notification_initialized(_, _json_rpc_message)
|
|
446
|
+
PuppetLanguageServer.log_message(:info, 'Client has received initialization')
|
|
447
|
+
|
|
448
|
+
protocol.encode_and_send(
|
|
449
|
+
::PuppetEditorServices::Protocol::JsonRPCMessages.new_notification(
|
|
450
|
+
'window/showMessage',
|
|
451
|
+
'type' => LSP::MessageType::WARNING,
|
|
452
|
+
'message' => 'An error occured while starting the Language Server. The server has been disabled.'
|
|
453
|
+
)
|
|
454
|
+
)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def notification_exit(_, _json_rpc_message)
|
|
458
|
+
PuppetLanguageServer.log_message(:info, 'Received exit notification. Closing connection to client...')
|
|
459
|
+
protocol.close_connection
|
|
460
|
+
end
|
|
461
|
+
end
|
|
462
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
%w[
|
|
4
|
+
epp/validation_provider
|
|
5
|
+
manifest/completion_provider
|
|
6
|
+
manifest/definition_provider
|
|
7
|
+
manifest/document_symbol_provider
|
|
8
|
+
manifest/folding_provider
|
|
9
|
+
manifest/format_on_type_provider
|
|
10
|
+
manifest/signature_provider
|
|
11
|
+
manifest/validation_provider
|
|
12
|
+
manifest/hover_provider
|
|
13
|
+
puppetfile/validation_provider
|
|
14
|
+
].each do |lib|
|
|
15
|
+
require "puppet-languageserver/#{lib}"
|
|
16
|
+
rescue LoadError
|
|
17
|
+
require File.expand_path(File.join(File.dirname(__FILE__), lib))
|
|
18
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
require 'puppet-languageserver/session_state/object_cache'
|
|
6
|
+
require 'puppet-languageserver/global_queues'
|
|
7
|
+
|
|
8
|
+
module PuppetLanguageServer
|
|
9
|
+
module PuppetHelper
|
|
10
|
+
def self.module_path
|
|
11
|
+
return @module_path unless @module_path.nil?
|
|
12
|
+
|
|
13
|
+
# TODO: It would be nice if this wasn't using the whole puppet environment to calculate the modulepath directoties
|
|
14
|
+
# In the meantime memoize it. Currently you can't change the modulepath mid-process.
|
|
15
|
+
begin
|
|
16
|
+
env = Puppet.lookup(:environments).get!(Puppet.settings[:environment])
|
|
17
|
+
rescue Puppet::Environments::EnvironmentNotFound, StandardError
|
|
18
|
+
env = Puppet.lookup(:current_environment)
|
|
19
|
+
end
|
|
20
|
+
return [] if env.nil?
|
|
21
|
+
|
|
22
|
+
@module_path = env.modulepath
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Node Graph
|
|
26
|
+
def self.get_node_graph(session_state, content, local_workspace)
|
|
27
|
+
with_temporary_file(content) do |filepath|
|
|
28
|
+
ap = PuppetLanguageServer::Sidecar::Protocol::ActionParams.new
|
|
29
|
+
ap['source'] = filepath
|
|
30
|
+
|
|
31
|
+
args = ["--action-parameters=#{ap.to_json}"]
|
|
32
|
+
args << "--local-workspace=#{local_workspace}" unless local_workspace.nil?
|
|
33
|
+
|
|
34
|
+
sidecar_queue.execute('node_graph', args, false, session_state.connection_id)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.get_puppet_resource(session_state, typename, title, local_workspace)
|
|
39
|
+
ap = PuppetLanguageServer::Sidecar::Protocol::ActionParams.new
|
|
40
|
+
ap['typename'] = typename
|
|
41
|
+
ap['title'] = title unless title.nil?
|
|
42
|
+
|
|
43
|
+
args = ["--action-parameters=#{ap.to_json}"]
|
|
44
|
+
args << "--local-workspace=#{local_workspace}" unless local_workspace.nil?
|
|
45
|
+
|
|
46
|
+
sidecar_queue.execute('resource_list', args, false, session_state.connection_id)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.get_type(session_state, name)
|
|
50
|
+
session_state.object_cache.object_by_name(:type, name)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.type_names(session_state)
|
|
54
|
+
session_state.object_cache.object_names_by_section(:type).map(&:to_s)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.function(session_state, name, tasks_mode = false)
|
|
58
|
+
exclude_origins = tasks_mode ? [] : [:bolt]
|
|
59
|
+
session_state.object_cache.object_by_name(
|
|
60
|
+
:function,
|
|
61
|
+
name,
|
|
62
|
+
fuzzy_match: true,
|
|
63
|
+
exclude_origins: exclude_origins
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.function_names(session_state, tasks_mode = false)
|
|
68
|
+
exclude_origins = tasks_mode ? [] : [:bolt]
|
|
69
|
+
session_state.object_cache.object_names_by_section(:function, exclude_origins: exclude_origins).map(&:to_s)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.get_class(session_state, name)
|
|
73
|
+
session_state.object_cache.object_by_name(:class, name)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def self.class_names(session_state)
|
|
77
|
+
session_state.object_cache.object_names_by_section(:class).map(&:to_s)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def self.datatype(session_state, name, tasks_mode = false)
|
|
81
|
+
exclude_origins = tasks_mode ? [] : [:bolt]
|
|
82
|
+
session_state.object_cache.object_by_name(
|
|
83
|
+
:datatype,
|
|
84
|
+
name,
|
|
85
|
+
fuzzy_match: true,
|
|
86
|
+
exclude_origins: exclude_origins
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.sidecar_queue
|
|
91
|
+
PuppetLanguageServer::GlobalQueues.sidecar_queue
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.with_temporary_file(content)
|
|
95
|
+
tempfile = Tempfile.new('langserver-sidecar')
|
|
96
|
+
tempfile.open
|
|
97
|
+
|
|
98
|
+
tempfile.write(content)
|
|
99
|
+
|
|
100
|
+
tempfile.close
|
|
101
|
+
|
|
102
|
+
yield tempfile.path
|
|
103
|
+
ensure
|
|
104
|
+
tempfile.delete if tempfile
|
|
105
|
+
end
|
|
106
|
+
private_class_method :with_temporary_file
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puppet
|
|
4
|
+
module Pops
|
|
5
|
+
module Parser
|
|
6
|
+
# This Lexer adds code to create comment tokens.
|
|
7
|
+
# The default lexer just throws them out
|
|
8
|
+
# Ref - https://github.com/puppetlabs/puppet-specifications/blob/master/language/lexical_structure.md#comments
|
|
9
|
+
class Lexer2WithComments < Puppet::Pops::Parser::Lexer2
|
|
10
|
+
# The PATTERN_COMMENT in lexer2 also consumes the trailing \r in the token and
|
|
11
|
+
# we don't want that.
|
|
12
|
+
PATTERN_COMMENT_NO_WS = /#[^\r\n]*/.freeze
|
|
13
|
+
|
|
14
|
+
TOKEN_COMMENT = [:COMMENT, '#', 1].freeze
|
|
15
|
+
TOKEN_MLCOMMENT = [:MLCOMMENT, nil, 0].freeze
|
|
16
|
+
|
|
17
|
+
def initialize
|
|
18
|
+
super
|
|
19
|
+
|
|
20
|
+
# Remove the selector for line comments so we can add our own
|
|
21
|
+
@new_selector = @selector.reject { |k, _v| k == '#' }
|
|
22
|
+
|
|
23
|
+
# Add code to scan line comments
|
|
24
|
+
@new_selector['#'] = lambda {
|
|
25
|
+
scn = @scanner
|
|
26
|
+
before = scn.pos
|
|
27
|
+
value = scn.scan(PATTERN_COMMENT_NO_WS)
|
|
28
|
+
|
|
29
|
+
if value
|
|
30
|
+
emit_completed([:TOKEN_COMMENT, value[1..-1].freeze, scn.pos - before], before)
|
|
31
|
+
else
|
|
32
|
+
# It's probably not possible to EVER get here ... but just incase
|
|
33
|
+
emit(TOKEN_COMMENT, before)
|
|
34
|
+
end
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Add code to scan multi-line comments
|
|
38
|
+
old_lambda = @new_selector['/']
|
|
39
|
+
@new_selector['/'] = lambda {
|
|
40
|
+
scn = @scanner
|
|
41
|
+
la = scn.peek(2)
|
|
42
|
+
if la[1] == '*'
|
|
43
|
+
before = scn.pos
|
|
44
|
+
value = scn.scan(PATTERN_MLCOMMENT)
|
|
45
|
+
return emit_completed([:TOKEN_MLCOMMENT, value[2..-3].freeze, scn.pos - before], before) if value
|
|
46
|
+
end
|
|
47
|
+
old_lambda.call
|
|
48
|
+
}.freeze
|
|
49
|
+
@new_selector.freeze
|
|
50
|
+
@selector = @new_selector
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Monkey Patch the Puppet language parser so we can globally lock any changes to the
|
|
4
|
+
# global setting Puppet[:tasks]. We need to manage this so we can switch between
|
|
5
|
+
# parsing modes. Unfortunately we can't do this as method parameter, only via the
|
|
6
|
+
# global Puppet settings which is not thread safe
|
|
7
|
+
$PuppetParserMutex = Mutex.new # rubocop:disable Style/GlobalVars
|
|
8
|
+
module Puppet
|
|
9
|
+
module Pops
|
|
10
|
+
module Parser
|
|
11
|
+
class Parser
|
|
12
|
+
def singleton_parse_string(code, task_mode = false, path = nil)
|
|
13
|
+
$PuppetParserMutex.synchronize do # rubocop:disable Style/GlobalVars
|
|
14
|
+
original_taskmode = Puppet[:tasks] if Puppet.tasks_supported?
|
|
15
|
+
Puppet[:tasks] = task_mode if Puppet.tasks_supported?
|
|
16
|
+
return parse_string(code, path)
|
|
17
|
+
ensure
|
|
18
|
+
Puppet[:tasks] = original_taskmode if Puppet.tasks_supported?
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Puppet
|
|
27
|
+
# Tasks first appeared in Puppet 5.4.0
|
|
28
|
+
def self.tasks_supported?
|
|
29
|
+
Gem::Version.new(Puppet.version) >= Gem::Version.new('5.4.0')
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# MUST BE LAST!!!!!!
|
|
34
|
+
# Suppress any warning messages to STDOUT. It can pollute stdout when running in STDIO mode
|
|
35
|
+
Puppet::Util::Log.newdesttype :null_logger do
|
|
36
|
+
def handle(msg)
|
|
37
|
+
PuppetLanguageServer.log_message(:debug, "[PUPPET LOG] [#{msg.level}] #{msg.message}")
|
|
38
|
+
end
|
|
39
|
+
end
|