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.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +510 -0
  3. data/CODEOWNERS +2 -0
  4. data/CODE_OF_CONDUCT.md +46 -0
  5. data/CONTRIBUTING.md +54 -0
  6. data/Gemfile +53 -0
  7. data/LICENSE +201 -0
  8. data/README.md +308 -0
  9. data/Rakefile +185 -0
  10. data/bin/puppet-debugserver +8 -0
  11. data/bin/puppet-languageserver +7 -0
  12. data/bin/puppet-languageserver-sidecar +7 -0
  13. data/lib/dsp/dsp.rb +7 -0
  14. data/lib/dsp/dsp_base.rb +62 -0
  15. data/lib/dsp/dsp_protocol.rb +4619 -0
  16. data/lib/lsp/lsp.rb +10 -0
  17. data/lib/lsp/lsp_base.rb +63 -0
  18. data/lib/lsp/lsp_custom.rb +170 -0
  19. data/lib/lsp/lsp_enums.rb +143 -0
  20. data/lib/lsp/lsp_protocol.rb +2785 -0
  21. data/lib/lsp/lsp_protocol_callhierarchy.proposed.rb +239 -0
  22. data/lib/lsp/lsp_protocol_colorprovider.rb +100 -0
  23. data/lib/lsp/lsp_protocol_configuration.rb +82 -0
  24. data/lib/lsp/lsp_protocol_declaration.rb +73 -0
  25. data/lib/lsp/lsp_protocol_foldingrange.rb +129 -0
  26. data/lib/lsp/lsp_protocol_implementation.rb +75 -0
  27. data/lib/lsp/lsp_protocol_progress.rb +200 -0
  28. data/lib/lsp/lsp_protocol_selectionrange.rb +79 -0
  29. data/lib/lsp/lsp_protocol_sematictokens.proposed.rb +340 -0
  30. data/lib/lsp/lsp_protocol_typedefinition.rb +75 -0
  31. data/lib/lsp/lsp_protocol_workspacefolders.rb +174 -0
  32. data/lib/lsp/lsp_types.rb +1534 -0
  33. data/lib/puppet-debugserver/debug_session/break_points.rb +137 -0
  34. data/lib/puppet-debugserver/debug_session/flow_control.rb +161 -0
  35. data/lib/puppet-debugserver/debug_session/hook_handlers.rb +295 -0
  36. data/lib/puppet-debugserver/debug_session/puppet_session_run_mode.rb +66 -0
  37. data/lib/puppet-debugserver/debug_session/puppet_session_state.rb +122 -0
  38. data/lib/puppet-debugserver/hooks.rb +132 -0
  39. data/lib/puppet-debugserver/message_handler.rb +277 -0
  40. data/lib/puppet-debugserver/puppet_debug_session.rb +541 -0
  41. data/lib/puppet-debugserver/puppet_monkey_patches.rb +118 -0
  42. data/lib/puppet-languageserver/client_session_state.rb +119 -0
  43. data/lib/puppet-languageserver/crash_dump.rb +50 -0
  44. data/lib/puppet-languageserver/epp/validation_provider.rb +34 -0
  45. data/lib/puppet-languageserver/facter_helper.rb +25 -0
  46. data/lib/puppet-languageserver/global_queues/sidecar_queue.rb +205 -0
  47. data/lib/puppet-languageserver/global_queues/single_instance_queue.rb +126 -0
  48. data/lib/puppet-languageserver/global_queues/validation_queue.rb +102 -0
  49. data/lib/puppet-languageserver/global_queues.rb +16 -0
  50. data/lib/puppet-languageserver/manifest/completion_provider.rb +331 -0
  51. data/lib/puppet-languageserver/manifest/definition_provider.rb +99 -0
  52. data/lib/puppet-languageserver/manifest/document_symbol_provider.rb +228 -0
  53. data/lib/puppet-languageserver/manifest/folding_provider.rb +226 -0
  54. data/lib/puppet-languageserver/manifest/format_on_type_provider.rb +143 -0
  55. data/lib/puppet-languageserver/manifest/hover_provider.rb +221 -0
  56. data/lib/puppet-languageserver/manifest/signature_provider.rb +169 -0
  57. data/lib/puppet-languageserver/manifest/validation_provider.rb +127 -0
  58. data/lib/puppet-languageserver/message_handler.rb +462 -0
  59. data/lib/puppet-languageserver/providers.rb +18 -0
  60. data/lib/puppet-languageserver/puppet_helper.rb +108 -0
  61. data/lib/puppet-languageserver/puppet_lexer_helper.rb +55 -0
  62. data/lib/puppet-languageserver/puppet_monkey_patches.rb +39 -0
  63. data/lib/puppet-languageserver/puppet_parser_helper.rb +212 -0
  64. data/lib/puppet-languageserver/puppetfile/validation_provider.rb +185 -0
  65. data/lib/puppet-languageserver/server_capabilities.rb +48 -0
  66. data/lib/puppet-languageserver/session_state/document_store.rb +272 -0
  67. data/lib/puppet-languageserver/session_state/language_client.rb +239 -0
  68. data/lib/puppet-languageserver/session_state/object_cache.rb +162 -0
  69. data/lib/puppet-languageserver/sidecar_protocol.rb +532 -0
  70. data/lib/puppet-languageserver/uri_helper.rb +46 -0
  71. data/lib/puppet-languageserver-sidecar/cache/base.rb +36 -0
  72. data/lib/puppet-languageserver-sidecar/cache/filesystem.rb +111 -0
  73. data/lib/puppet-languageserver-sidecar/cache/null.rb +27 -0
  74. data/lib/puppet-languageserver-sidecar/facter_helper.rb +41 -0
  75. data/lib/puppet-languageserver-sidecar/puppet_environment_monkey_patches.rb +52 -0
  76. data/lib/puppet-languageserver-sidecar/puppet_helper.rb +281 -0
  77. data/lib/puppet-languageserver-sidecar/puppet_modulepath_monkey_patches.rb +146 -0
  78. data/lib/puppet-languageserver-sidecar/puppet_monkey_patches.rb +9 -0
  79. data/lib/puppet-languageserver-sidecar/puppet_parser_helper.rb +77 -0
  80. data/lib/puppet-languageserver-sidecar/puppet_strings_helper.rb +399 -0
  81. data/lib/puppet-languageserver-sidecar/puppet_strings_monkey_patches.rb +16 -0
  82. data/lib/puppet-languageserver-sidecar/sidecar_protocol_extensions.rb +16 -0
  83. data/lib/puppet-languageserver-sidecar/workspace.rb +89 -0
  84. data/lib/puppet_debugserver.rb +164 -0
  85. data/lib/puppet_editor_services/connection/base.rb +62 -0
  86. data/lib/puppet_editor_services/connection/stdio.rb +25 -0
  87. data/lib/puppet_editor_services/connection/tcp.rb +34 -0
  88. data/lib/puppet_editor_services/handler/base.rb +16 -0
  89. data/lib/puppet_editor_services/handler/debug_adapter.rb +63 -0
  90. data/lib/puppet_editor_services/handler/json_rpc.rb +133 -0
  91. data/lib/puppet_editor_services/logging.rb +45 -0
  92. data/lib/puppet_editor_services/protocol/base.rb +27 -0
  93. data/lib/puppet_editor_services/protocol/debug_adapter.rb +135 -0
  94. data/lib/puppet_editor_services/protocol/debug_adapter_messages.rb +171 -0
  95. data/lib/puppet_editor_services/protocol/json_rpc.rb +241 -0
  96. data/lib/puppet_editor_services/protocol/json_rpc_messages.rb +200 -0
  97. data/lib/puppet_editor_services/server/base.rb +42 -0
  98. data/lib/puppet_editor_services/server/stdio.rb +85 -0
  99. data/lib/puppet_editor_services/server/tcp.rb +349 -0
  100. data/lib/puppet_editor_services/server.rb +15 -0
  101. data/lib/puppet_editor_services/version.rb +36 -0
  102. data/lib/puppet_editor_services.rb +8 -0
  103. data/lib/puppet_languageserver.rb +263 -0
  104. data/lib/puppet_languageserver_sidecar.rb +361 -0
  105. data/puppet-debugserver +11 -0
  106. data/puppet-editor-services.gemspec +29 -0
  107. data/puppet-languageserver +15 -0
  108. data/puppet-languageserver-sidecar +14 -0
  109. 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