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,331 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetLanguageServer
|
|
4
|
+
module Manifest
|
|
5
|
+
module CompletionProvider
|
|
6
|
+
def self.complete(session_state, content, line_num, char_num, options = {})
|
|
7
|
+
options = {
|
|
8
|
+
tasks_mode: false,
|
|
9
|
+
context: nil # LSP::CompletionContext object
|
|
10
|
+
}.merge(options)
|
|
11
|
+
items = []
|
|
12
|
+
incomplete = false
|
|
13
|
+
is_trigger_char = !options[:context].nil? && options[:context].triggerKind == LSP::CompletionTriggerKind::TRIGGERCHARACTER
|
|
14
|
+
|
|
15
|
+
result = PuppetLanguageServer::PuppetParserHelper.object_under_cursor(content, line_num, char_num,
|
|
16
|
+
multiple_attempts: true,
|
|
17
|
+
disallowed_classes: [Puppet::Pops::Model::QualifiedName, Puppet::Pops::Model::BlockExpression],
|
|
18
|
+
tasks_mode: options[:tasks_mode],
|
|
19
|
+
remove_trigger_char: is_trigger_char)
|
|
20
|
+
if result.nil?
|
|
21
|
+
# We are in the root of the document.
|
|
22
|
+
|
|
23
|
+
# Add keywords
|
|
24
|
+
keywords(%w[class define node application site]) { |x| items << x }
|
|
25
|
+
keywords(%w[plan]) { |x| items << x } if options[:tasks_mode]
|
|
26
|
+
|
|
27
|
+
# Add resources
|
|
28
|
+
all_resources(session_state) { |x| items << x }
|
|
29
|
+
|
|
30
|
+
all_functions(session_state, options[:tasks_mode]) { |x| items << x }
|
|
31
|
+
|
|
32
|
+
response = LSP::CompletionList.new
|
|
33
|
+
response.items = items
|
|
34
|
+
response.isIncomplete = incomplete
|
|
35
|
+
return response
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
item = result[:model]
|
|
39
|
+
|
|
40
|
+
case item.class.to_s
|
|
41
|
+
when 'Puppet::Pops::Model::VariableExpression'
|
|
42
|
+
expr = item.expr.value
|
|
43
|
+
|
|
44
|
+
# Complete for `$facts[...`
|
|
45
|
+
all_facts(session_state) { |x| items << x } if expr == 'facts'
|
|
46
|
+
|
|
47
|
+
when 'Puppet::Pops::Model::HostClassDefinition', 'Puppet::Pops::Model::ResourceTypeDefinition'
|
|
48
|
+
# We are in the root of a `class` or `define` statement
|
|
49
|
+
|
|
50
|
+
# Add keywords
|
|
51
|
+
keywords(%w[require contain]) { |x| items << x }
|
|
52
|
+
|
|
53
|
+
# Add resources
|
|
54
|
+
all_resources(session_state) { |x| items << x }
|
|
55
|
+
|
|
56
|
+
when 'Puppet::Pops::Model::PlanDefinition'
|
|
57
|
+
# We are in the root of a `plan` statement
|
|
58
|
+
|
|
59
|
+
# Add resources
|
|
60
|
+
all_resources(session_state) { |x| items << x }
|
|
61
|
+
|
|
62
|
+
all_functions(session_state, options[:tasks_mode]) { |x| items << x }
|
|
63
|
+
|
|
64
|
+
when 'Puppet::Pops::Model::ResourceExpression'
|
|
65
|
+
# We are inside a resource definition. Should display all available
|
|
66
|
+
# properities and parameters.
|
|
67
|
+
|
|
68
|
+
# Try Types first
|
|
69
|
+
# The `class` pseudo resource type is actually used to set properties/params for the puppet type
|
|
70
|
+
# specified in the resource title.
|
|
71
|
+
# Ref: https://puppet.com/docs/puppet/5.3/lang_classes.html#using-resource-like-declarations
|
|
72
|
+
item_value = item.type_name.value == 'class' && item.bodies.length == 1 ? item.bodies[0].title.value : item.type_name.value
|
|
73
|
+
item_object = PuppetLanguageServer::PuppetHelper.get_type(session_state, item_value)
|
|
74
|
+
unless item_object.nil?
|
|
75
|
+
# Add Parameters
|
|
76
|
+
item_object.attributes.select { |_name, data| data[:type] == :param }.each_key do |name|
|
|
77
|
+
items << LSP::CompletionItem.new(
|
|
78
|
+
'label' => name.to_s,
|
|
79
|
+
'kind' => LSP::CompletionItemKind::PROPERTY,
|
|
80
|
+
'detail' => 'Parameter',
|
|
81
|
+
'data' => {
|
|
82
|
+
'type' => 'resource_parameter',
|
|
83
|
+
'param' => name.to_s,
|
|
84
|
+
'resource_type' => item_value
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
# Add Properties
|
|
89
|
+
item_object.attributes.select { |_name, data| data[:type] == :property }.each_key do |name|
|
|
90
|
+
items << LSP::CompletionItem.new(
|
|
91
|
+
'label' => name.to_s,
|
|
92
|
+
'kind' => LSP::CompletionItemKind::PROPERTY,
|
|
93
|
+
'detail' => 'Property',
|
|
94
|
+
'data' => {
|
|
95
|
+
'type' => 'resource_property',
|
|
96
|
+
'prop' => name.to_s,
|
|
97
|
+
'resource_type' => item_value
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
# TODO: What about meta parameters?
|
|
102
|
+
end
|
|
103
|
+
if item_object.nil?
|
|
104
|
+
# Try Classes/Defined Types
|
|
105
|
+
item_object = PuppetLanguageServer::PuppetHelper.get_class(session_state, item_value)
|
|
106
|
+
unless item_object.nil?
|
|
107
|
+
# Add Parameters
|
|
108
|
+
item_object.parameters.each_key do |name|
|
|
109
|
+
items << LSP::CompletionItem.new(
|
|
110
|
+
'label' => name.to_s,
|
|
111
|
+
'kind' => LSP::CompletionItemKind::PROPERTY,
|
|
112
|
+
'detail' => 'Parameter',
|
|
113
|
+
'data' => {
|
|
114
|
+
'type' => 'resource_class_parameter',
|
|
115
|
+
'param' => name.to_s,
|
|
116
|
+
'resource_type' => item_value
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
response = LSP::CompletionList.new
|
|
125
|
+
response.items = items
|
|
126
|
+
response.isIncomplete = incomplete
|
|
127
|
+
response
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# BEGIN CompletionItem Helpers
|
|
131
|
+
def self.keywords(keywords = [], &block)
|
|
132
|
+
keywords.each do |keyword|
|
|
133
|
+
item = LSP::CompletionItem.new(
|
|
134
|
+
'label' => keyword,
|
|
135
|
+
'kind' => LSP::CompletionItemKind::KEYWORD,
|
|
136
|
+
'detail' => 'Keyword',
|
|
137
|
+
'data' => {
|
|
138
|
+
'type' => 'keyword',
|
|
139
|
+
'name' => keyword
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
yield(item) if block
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def self.all_facts(session_state, &block)
|
|
147
|
+
PuppetLanguageServer::FacterHelper.fact_names(session_state).each do |name|
|
|
148
|
+
item = LSP::CompletionItem.new(
|
|
149
|
+
'label' => name.to_s,
|
|
150
|
+
'insertText' => "'#{name}'",
|
|
151
|
+
'kind' => LSP::CompletionItemKind::VARIABLE,
|
|
152
|
+
'detail' => 'Fact',
|
|
153
|
+
'data' => {
|
|
154
|
+
'type' => 'variable_expr_fact',
|
|
155
|
+
'expr' => name
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
yield(item) if block
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def self.all_resources(session_state, &block)
|
|
163
|
+
# Find Puppet Types
|
|
164
|
+
PuppetLanguageServer::PuppetHelper.type_names(session_state).each do |pup_type|
|
|
165
|
+
item = LSP::CompletionItem.new(
|
|
166
|
+
'label' => pup_type,
|
|
167
|
+
'kind' => LSP::CompletionItemKind::MODULE,
|
|
168
|
+
'detail' => 'Resource',
|
|
169
|
+
'data' => {
|
|
170
|
+
'type' => 'resource_type',
|
|
171
|
+
'name' => pup_type
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
yield(item) if block
|
|
175
|
+
end
|
|
176
|
+
# Find Puppet Classes/Defined Types
|
|
177
|
+
PuppetLanguageServer::PuppetHelper.class_names(session_state).each do |pup_class|
|
|
178
|
+
item = LSP::CompletionItem.new('label' => pup_class,
|
|
179
|
+
'kind' => LSP::CompletionItemKind::MODULE,
|
|
180
|
+
'detail' => 'Resource',
|
|
181
|
+
'data' => { 'type' => 'resource_class',
|
|
182
|
+
'name' => pup_class })
|
|
183
|
+
yield(item) if block
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def self.all_functions(session_state, tasks_mode, &block)
|
|
188
|
+
PuppetLanguageServer::PuppetHelper.function_names(session_state, tasks_mode).each do |name|
|
|
189
|
+
item = LSP::CompletionItem.new(
|
|
190
|
+
'label' => name.to_s,
|
|
191
|
+
'kind' => LSP::CompletionItemKind::FUNCTION,
|
|
192
|
+
'detail' => 'Function',
|
|
193
|
+
'data' => {
|
|
194
|
+
'type' => 'function',
|
|
195
|
+
'name' => name.to_s
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
yield(item) if block
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
# END Helpers
|
|
202
|
+
|
|
203
|
+
# completion_item is an instance of LSP::CompletionItem
|
|
204
|
+
def self.resolve(session_state, completion_item)
|
|
205
|
+
result = completion_item.clone
|
|
206
|
+
data = result.data
|
|
207
|
+
case data['type']
|
|
208
|
+
when 'variable_expr_fact'
|
|
209
|
+
value = PuppetLanguageServer::FacterHelper.fact_value(session_state, data['expr'])
|
|
210
|
+
# TODO: More things?
|
|
211
|
+
result.documentation = value.to_s
|
|
212
|
+
|
|
213
|
+
when 'keyword'
|
|
214
|
+
case data['name']
|
|
215
|
+
when 'class'
|
|
216
|
+
result.documentation = 'Classes are named blocks of Puppet code that are stored in modules for later use and ' \
|
|
217
|
+
'are not applied until they are invoked by name. They can be added to a node’s catalog ' \
|
|
218
|
+
'by either declaring them in your manifests or assigning them from an ENC.'
|
|
219
|
+
result.insertText = "# Class: $1\n#\n#\nclass ${1:name} {\n\t${2:# resources}\n}$0"
|
|
220
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
221
|
+
when 'define'
|
|
222
|
+
result.documentation = 'Defined resource types (also called defined types or defines) are blocks of Puppet code ' \
|
|
223
|
+
'that can be evaluated multiple times with different parameters. Once defined, they act ' \
|
|
224
|
+
'like a new resource type: you can cause the block to be evaluated by declaring a resource ' \
|
|
225
|
+
'of that new resource type.'
|
|
226
|
+
result.insertText = "define ${1:name} () {\n\t${2:# resources}\n}$0"
|
|
227
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
228
|
+
when 'application'
|
|
229
|
+
result.detail = 'Orchestrator'
|
|
230
|
+
result.documentation = 'Application definitions are a lot like a defined resource type except that instead of defining ' \
|
|
231
|
+
'a chunk of reusable configuration that applies to a single node, the application definition ' \
|
|
232
|
+
'operates at a higher level. The components you declare inside an application can be individually ' \
|
|
233
|
+
'assigned to separate nodes you manage with Puppet.'
|
|
234
|
+
result.insertText = "application ${1:name} () {\n\t${2:# resources}\n}$0"
|
|
235
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
236
|
+
when 'site'
|
|
237
|
+
result.detail = 'Orchestrator'
|
|
238
|
+
result.documentation = 'Within the site block, applications are declared like defined types. They can be declared any ' \
|
|
239
|
+
'number of times, but their type and title combination must be unique within an environment.'
|
|
240
|
+
result.insertText = "site ${1:name} () {\n\t${2:# applications}\n}$0"
|
|
241
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
when 'function'
|
|
245
|
+
# We don't know if this resolution is coming from a plan or not, so just assume it is
|
|
246
|
+
item_type = PuppetLanguageServer::PuppetHelper.function(session_state, data['name'], true)
|
|
247
|
+
return result if item_type.nil?
|
|
248
|
+
|
|
249
|
+
result.documentation = item_type.doc unless item_type.doc.nil?
|
|
250
|
+
unless item_type.nil? || item_type.signatures.count.zero?
|
|
251
|
+
result.detail = item_type.signatures.map(&:key).join("\n\n")
|
|
252
|
+
# The signature provider should handle suggestions after this, so just place the cursor ready for an opening bracket
|
|
253
|
+
result.insertText = data['name'].to_s
|
|
254
|
+
result.insertTextFormat = LSP::InsertTextFormat::PLAINTEXT
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
when 'resource_type'
|
|
258
|
+
item_type = PuppetLanguageServer::PuppetHelper.get_type(session_state, data['name'])
|
|
259
|
+
return result if item_type.nil?
|
|
260
|
+
|
|
261
|
+
attr_names = []
|
|
262
|
+
# Add required attributes. Ignore namevars as they come from the resource title
|
|
263
|
+
item_type.attributes.each { |name, item| attr_names.push(name.to_s) if item[:required?] && item[:isnamevar?] != true }
|
|
264
|
+
# Remove the'ensure' param/property for now, and we'll re-add later
|
|
265
|
+
attr_names.reject! { |item| item == 'ensure' }
|
|
266
|
+
# The param/property list should initially sorted alphabetically
|
|
267
|
+
attr_names.sort!
|
|
268
|
+
# Add the 'ensure' param/property at the top if the resource supports it
|
|
269
|
+
attr_names.insert(0, 'ensure') unless item_type.attributes.keys.find_index(:ensure).nil?
|
|
270
|
+
# Get the longest string length for later hash-rocket padding
|
|
271
|
+
max_length = -1
|
|
272
|
+
attr_names.each { |name| max_length = name.length if name.length > max_length }
|
|
273
|
+
|
|
274
|
+
# Generate the text snippet
|
|
275
|
+
snippet = "#{data['name']} { '${1:title}':\n"
|
|
276
|
+
attr_names.each_index do |index|
|
|
277
|
+
name = attr_names[index]
|
|
278
|
+
value_text = (name == 'ensure') ? 'present' : 'value' # rubocop:disable Style/TernaryParentheses In this case it's easier to read.
|
|
279
|
+
snippet += "\t#{name.ljust(max_length, ' ')} => '${#{index + 2}:#{value_text}}'\n"
|
|
280
|
+
end
|
|
281
|
+
snippet += '}'
|
|
282
|
+
|
|
283
|
+
result.documentation = item_type.doc unless item_type.doc.nil?
|
|
284
|
+
result.insertText = snippet
|
|
285
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
286
|
+
when 'resource_parameter'
|
|
287
|
+
item_type = PuppetLanguageServer::PuppetHelper.get_type(session_state, data['resource_type'])
|
|
288
|
+
return result if item_type.nil?
|
|
289
|
+
|
|
290
|
+
param_type = item_type.attributes[data['param'].intern]
|
|
291
|
+
unless param_type.nil?
|
|
292
|
+
# TODO: More things?
|
|
293
|
+
result.documentation = param_type[:doc] unless param_type[:doc].nil?
|
|
294
|
+
result.insertText = "#{data['param']} => ,"
|
|
295
|
+
end
|
|
296
|
+
when 'resource_property'
|
|
297
|
+
item_type = PuppetLanguageServer::PuppetHelper.get_type(session_state, data['resource_type'])
|
|
298
|
+
return result if item_type.nil?
|
|
299
|
+
|
|
300
|
+
prop_type = item_type.attributes[data['prop'].intern]
|
|
301
|
+
unless prop_type.nil?
|
|
302
|
+
# TODO: More things?
|
|
303
|
+
result.documentation = prop_type[:doc] unless prop_type[:doc].nil?
|
|
304
|
+
result.insertText = "#{data['prop']} => ,"
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
when 'resource_class'
|
|
308
|
+
item_class = PuppetLanguageServer::PuppetHelper.get_class(session_state, data['name'])
|
|
309
|
+
return result if item_class.nil?
|
|
310
|
+
|
|
311
|
+
result.insertText = "#{data['name']} { '${1:title}':\n\t$0\n}"
|
|
312
|
+
result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
|
|
313
|
+
when 'resource_class_parameter'
|
|
314
|
+
item_class = PuppetLanguageServer::PuppetHelper.get_class(session_state, data['resource_type'])
|
|
315
|
+
return result if item_class.nil?
|
|
316
|
+
|
|
317
|
+
param_type = item_class.parameters[data['param']]
|
|
318
|
+
unless param_type.nil?
|
|
319
|
+
doc = ''
|
|
320
|
+
doc += param_type[:type] unless param_type[:type].nil?
|
|
321
|
+
doc += "---\n#{param_type[:doc]}" unless param_type[:doc].nil?
|
|
322
|
+
result.documentation = doc
|
|
323
|
+
result.insertText = "#{data['param']} => ,"
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
result
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetLanguageServer
|
|
4
|
+
module Manifest
|
|
5
|
+
module DefinitionProvider
|
|
6
|
+
def self.find_definition(session_state, content, line_num, char_num, options = {})
|
|
7
|
+
options = {
|
|
8
|
+
tasks_mode: false
|
|
9
|
+
}.merge(options)
|
|
10
|
+
result = PuppetLanguageServer::PuppetParserHelper.object_under_cursor(content, line_num, char_num,
|
|
11
|
+
disallowed_classes: [Puppet::Pops::Model::BlockExpression],
|
|
12
|
+
tasks_mode: options[:tasks_mode])
|
|
13
|
+
return nil if result.nil?
|
|
14
|
+
|
|
15
|
+
path = result[:path]
|
|
16
|
+
item = result[:model]
|
|
17
|
+
|
|
18
|
+
response = []
|
|
19
|
+
case item.class.to_s
|
|
20
|
+
when 'Puppet::Pops::Model::CallNamedFunctionExpression'
|
|
21
|
+
func_name = item.functor_expr.value
|
|
22
|
+
response << function_name(session_state, func_name)
|
|
23
|
+
|
|
24
|
+
when 'Puppet::Pops::Model::LiteralString'
|
|
25
|
+
# LiteralString could be anything. Context is the key here
|
|
26
|
+
parent = path.last
|
|
27
|
+
|
|
28
|
+
# What if it's a resource name. Then the Literal String must be the same as the Resource Title
|
|
29
|
+
# e.g.
|
|
30
|
+
# class { 'testclass': <--- testclass would be the LiteralString inside a ResourceBody
|
|
31
|
+
# }
|
|
32
|
+
if !parent.nil? &&
|
|
33
|
+
parent.instance_of?(::Puppet::Pops::Model::ResourceBody) &&
|
|
34
|
+
parent.title.value == item.value
|
|
35
|
+
resource_name = item.value
|
|
36
|
+
response << type_or_class(session_state, resource_name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
when 'Puppet::Pops::Model::QualifiedName'
|
|
40
|
+
# Qualified names could be anything. Context is the key here
|
|
41
|
+
parent = path.last
|
|
42
|
+
|
|
43
|
+
# What if it's a function name. Then the Qualified name must be the same as the function name
|
|
44
|
+
if !parent.nil? &&
|
|
45
|
+
parent.instance_of?(::Puppet::Pops::Model::CallNamedFunctionExpression) &&
|
|
46
|
+
parent.functor_expr.value == item.value
|
|
47
|
+
func_name = item.value
|
|
48
|
+
response << function_name(session_state, func_name)
|
|
49
|
+
end
|
|
50
|
+
# What if it's an "include <class>" call
|
|
51
|
+
if !parent.nil? && parent.instance_of?(::Puppet::Pops::Model::CallNamedFunctionExpression) && parent.functor_expr.value == 'include'
|
|
52
|
+
resource_name = item.value
|
|
53
|
+
response << type_or_class(session_state, resource_name)
|
|
54
|
+
end
|
|
55
|
+
# What if it's the name of a resource type or class
|
|
56
|
+
if !parent.nil? && parent.instance_of?(::Puppet::Pops::Model::ResourceExpression)
|
|
57
|
+
resource_name = item.value
|
|
58
|
+
response << type_or_class(session_state, resource_name)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
when 'Puppet::Pops::Model::ResourceExpression'
|
|
62
|
+
resource_name = item.type_name.value
|
|
63
|
+
response << type_or_class(session_state, resource_name)
|
|
64
|
+
|
|
65
|
+
else
|
|
66
|
+
raise "Unable to generate Defintion information for object of type #{item.class}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
response.compact
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def self.type_or_class(session_state, resource_name)
|
|
73
|
+
# Strip the leading double-colons for root resource names
|
|
74
|
+
resource_name = resource_name.slice(2, resource_name.length - 2) if resource_name.start_with?('::')
|
|
75
|
+
item = PuppetLanguageServer::PuppetHelper.get_type(session_state, resource_name)
|
|
76
|
+
item = PuppetLanguageServer::PuppetHelper.get_class(session_state, resource_name) if item.nil?
|
|
77
|
+
unless item.nil?
|
|
78
|
+
return LSP::Location.new(
|
|
79
|
+
'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source),
|
|
80
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
81
|
+
)
|
|
82
|
+
end
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
private_class_method :type_or_class
|
|
86
|
+
|
|
87
|
+
def self.function_name(session_state, func_name)
|
|
88
|
+
item = PuppetLanguageServer::PuppetHelper.function(session_state, func_name)
|
|
89
|
+
return nil if item.nil? || item.source.nil? || item.line.nil?
|
|
90
|
+
|
|
91
|
+
LSP::Location.new(
|
|
92
|
+
'uri' => "file:///#{item.source}",
|
|
93
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
94
|
+
)
|
|
95
|
+
end
|
|
96
|
+
private_class_method :function_name
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PuppetLanguageServer
|
|
4
|
+
module Manifest
|
|
5
|
+
module DocumentSymbolProvider
|
|
6
|
+
def self.workspace_symbols(query, object_cache)
|
|
7
|
+
query = '' if query.nil?
|
|
8
|
+
result = []
|
|
9
|
+
object_cache.all_objects do |key, item|
|
|
10
|
+
key_string = key.to_s
|
|
11
|
+
next unless query.empty? || key_string.include?(query)
|
|
12
|
+
|
|
13
|
+
case item
|
|
14
|
+
when PuppetLanguageServer::Sidecar::Protocol::PuppetType
|
|
15
|
+
result << LSP::SymbolInformation.new(
|
|
16
|
+
'name' => key_string,
|
|
17
|
+
'kind' => LSP::SymbolKind::METHOD,
|
|
18
|
+
'location' => {
|
|
19
|
+
'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source),
|
|
20
|
+
# Don't have char pos for types so just pick extreme values
|
|
21
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
22
|
+
}
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
when PuppetLanguageServer::Sidecar::Protocol::PuppetFunction
|
|
26
|
+
result << LSP::SymbolInformation.new(
|
|
27
|
+
'name' => key_string,
|
|
28
|
+
'kind' => LSP::SymbolKind::FUNCTION,
|
|
29
|
+
'location' => {
|
|
30
|
+
'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source),
|
|
31
|
+
# Don't have char pos for functions so just pick extreme values
|
|
32
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
when PuppetLanguageServer::Sidecar::Protocol::PuppetClass
|
|
37
|
+
result << LSP::SymbolInformation.new(
|
|
38
|
+
'name' => key_string,
|
|
39
|
+
'kind' => LSP::SymbolKind::CLASS,
|
|
40
|
+
'location' => {
|
|
41
|
+
'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source),
|
|
42
|
+
# Don't have char pos for classes so just pick extreme values
|
|
43
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
when PuppetLanguageServer::Sidecar::Protocol::PuppetDataType
|
|
48
|
+
result << LSP::SymbolInformation.new(
|
|
49
|
+
'name' => key_string,
|
|
50
|
+
'kind' => LSP::SymbolKind::NAMESPACE,
|
|
51
|
+
'location' => {
|
|
52
|
+
'uri' => PuppetLanguageServer::UriHelper.build_file_uri(item.source),
|
|
53
|
+
# Don't have char pos for data types so just pick extreme values
|
|
54
|
+
'range' => LSP.create_range(item.line, 0, item.line, 1024)
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
when PuppetLanguageServer::Sidecar::Protocol::Fact
|
|
59
|
+
# Do nothing
|
|
60
|
+
|
|
61
|
+
else
|
|
62
|
+
PuppetLanguageServer.log_message(:warn, "[Manifest::DocumentSymbolProvider] Unknown object type #{item.class}")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
result
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def self.extract_document_symbols(content, options = {})
|
|
69
|
+
options = {
|
|
70
|
+
tasks_mode: false
|
|
71
|
+
}.merge(options)
|
|
72
|
+
parser = Puppet::Pops::Parser::Parser.new
|
|
73
|
+
result = parser.singleton_parse_string(content, options[:tasks_mode], '')
|
|
74
|
+
|
|
75
|
+
if result.model.respond_to? :eAllContents
|
|
76
|
+
# We are unable to build a document symbol tree for Puppet 4 AST
|
|
77
|
+
return []
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
symbols = []
|
|
81
|
+
recurse_document_symbols(result.model, '', nil, symbols) # []
|
|
82
|
+
|
|
83
|
+
symbols
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.create_range(offset, length, locator)
|
|
87
|
+
start_line = locator.line_for_offset(offset) - 1
|
|
88
|
+
start_char = locator.pos_on_line(offset) - 1
|
|
89
|
+
end_line = locator.line_for_offset(offset + length) - 1
|
|
90
|
+
end_char = locator.pos_on_line(offset + length) - 1
|
|
91
|
+
|
|
92
|
+
LSP.create_range(start_line, start_char, end_line, end_char)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.locator_text(offset, length, locator)
|
|
96
|
+
locator.string.slice(offset, length)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.recurse_document_symbols(object, path, parentsymbol, symbollist)
|
|
100
|
+
# POPS Object Model
|
|
101
|
+
# https://github.com/puppetlabs/puppet/blob/master/lib/puppet/pops/model/ast.pp
|
|
102
|
+
|
|
103
|
+
# Path is just an internal path for debugging
|
|
104
|
+
# path = path + '/' + object.class.to_s[object.class.to_s.rindex('::')..-1]
|
|
105
|
+
|
|
106
|
+
this_symbol = nil
|
|
107
|
+
|
|
108
|
+
case object.class.to_s
|
|
109
|
+
# Puppet Resources
|
|
110
|
+
when 'Puppet::Pops::Model::ResourceExpression'
|
|
111
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
112
|
+
'name' => object.type_name.value,
|
|
113
|
+
'kind' => LSP::SymbolKind::METHOD,
|
|
114
|
+
'detail' => object.type_name.value,
|
|
115
|
+
'range' => create_range(object.offset, object.length, object.locator),
|
|
116
|
+
'selectionRange' => create_range(object.offset, object.length, object.locator),
|
|
117
|
+
'children' => []
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
when 'Puppet::Pops::Model::ResourceBody'
|
|
121
|
+
# We modify the parent symbol with the resource information,
|
|
122
|
+
# mainly we care about the resource title.
|
|
123
|
+
parentsymbol.name = "#{parentsymbol.name}: #{locator_text(object.title.offset, object.title.length, object.title.locator)}"
|
|
124
|
+
parentsymbol.detail = parentsymbol.name
|
|
125
|
+
parentsymbol.selectionRange = create_range(object.title.offset, object.title.length, object.locator)
|
|
126
|
+
|
|
127
|
+
when 'Puppet::Pops::Model::AttributeOperation'
|
|
128
|
+
attr_name = object.attribute_name
|
|
129
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
130
|
+
'name' => attr_name,
|
|
131
|
+
'kind' => LSP::SymbolKind::VARIABLE,
|
|
132
|
+
'detail' => attr_name,
|
|
133
|
+
'range' => create_range(object.offset, object.length, object.locator),
|
|
134
|
+
'selectionRange' => create_range(object.offset, attr_name.length, object.locator),
|
|
135
|
+
'children' => []
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# Puppet Class
|
|
139
|
+
when 'Puppet::Pops::Model::HostClassDefinition'
|
|
140
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
141
|
+
'name' => object.name,
|
|
142
|
+
'kind' => LSP::SymbolKind::CLASS,
|
|
143
|
+
'detail' => object.name,
|
|
144
|
+
'range' => create_range(object.offset, object.length, object.locator),
|
|
145
|
+
'selectionRange' => create_range(object.offset, object.length, object.locator),
|
|
146
|
+
'children' => []
|
|
147
|
+
)
|
|
148
|
+
# Load in the class parameters
|
|
149
|
+
object.parameters.each do |param|
|
|
150
|
+
param_symbol = LSP::DocumentSymbol.new(
|
|
151
|
+
'name' => "$#{param.name}",
|
|
152
|
+
'kind' => LSP::SymbolKind::PROPERTY,
|
|
153
|
+
'detail' => "$#{param.name}",
|
|
154
|
+
'range' => create_range(param.offset, param.length, param.locator),
|
|
155
|
+
'selectionRange' => create_range(param.offset, param.length, param.locator),
|
|
156
|
+
'children' => []
|
|
157
|
+
)
|
|
158
|
+
this_symbol.children.push(param_symbol)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Puppet Defined Type
|
|
162
|
+
when 'Puppet::Pops::Model::ResourceTypeDefinition'
|
|
163
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
164
|
+
'name' => object.name,
|
|
165
|
+
'kind' => LSP::SymbolKind::CLASS,
|
|
166
|
+
'detail' => object.name,
|
|
167
|
+
'range' => create_range(object.offset, object.length, object.locator),
|
|
168
|
+
'selectionRange' => create_range(object.offset, object.length, object.locator),
|
|
169
|
+
'children' => []
|
|
170
|
+
)
|
|
171
|
+
# Load in the class parameters
|
|
172
|
+
object.parameters.each do |param|
|
|
173
|
+
param_symbol = LSP::DocumentSymbol.new(
|
|
174
|
+
'name' => "$#{param.name}",
|
|
175
|
+
'kind' => LSP::SymbolKind::FIELD,
|
|
176
|
+
'detail' => "$#{param.name}",
|
|
177
|
+
'range' => create_range(param.offset, param.length, param.locator),
|
|
178
|
+
'selectionRange' => create_range(param.offset, param.length, param.locator),
|
|
179
|
+
'children' => []
|
|
180
|
+
)
|
|
181
|
+
this_symbol.children.push(param_symbol)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
when 'Puppet::Pops::Model::AssignmentExpression'
|
|
185
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
186
|
+
'name' => "$#{object.left_expr.expr.value}",
|
|
187
|
+
'kind' => LSP::SymbolKind::VARIABLE,
|
|
188
|
+
'detail' => "$#{object.left_expr.expr.value}",
|
|
189
|
+
'range' => create_range(object.left_expr.offset, object.left_expr.length, object.left_expr.locator),
|
|
190
|
+
'selectionRange' => create_range(object.left_expr.offset, object.left_expr.length, object.left_expr.locator),
|
|
191
|
+
'children' => []
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
# Puppet Plan
|
|
195
|
+
when 'Puppet::Pops::Model::PlanDefinition'
|
|
196
|
+
this_symbol = LSP::DocumentSymbol.new(
|
|
197
|
+
'name' => object.name,
|
|
198
|
+
'kind' => LSP::SymbolKind::CLASS,
|
|
199
|
+
'detail' => object.name,
|
|
200
|
+
'range' => create_range(object.offset, object.length, object.locator),
|
|
201
|
+
'selectionRange' => create_range(object.offset, object.length, object.locator),
|
|
202
|
+
'children' => []
|
|
203
|
+
)
|
|
204
|
+
# Load in the class parameters
|
|
205
|
+
object.parameters.each do |param|
|
|
206
|
+
param_symbol = LSP::DocumentSymbol.new(
|
|
207
|
+
'name' => "$#{param.name}",
|
|
208
|
+
'kind' => LSP::SymbolKind::CLASS,
|
|
209
|
+
'detail' => "$#{param.name}",
|
|
210
|
+
'range' => create_range(param.offset, param.length, param.locator),
|
|
211
|
+
'selectionRange' => create_range(param.offset, param.length, param.locator),
|
|
212
|
+
'children' => []
|
|
213
|
+
)
|
|
214
|
+
this_symbol.children.push(param_symbol)
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
object._pcore_contents do |item|
|
|
219
|
+
recurse_document_symbols(item, path, this_symbol.nil? ? parentsymbol : this_symbol, symbollist)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
return if this_symbol.nil?
|
|
223
|
+
|
|
224
|
+
parentsymbol.nil? ? symbollist.push(this_symbol) : parentsymbol.children.push(this_symbol)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
end
|