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,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServerSidecar
4
+ module PuppetParserHelper
5
+ def self.compile_node_graph(content)
6
+ result = PuppetLanguageServerSidecar::Protocol::PuppetNodeGraph.new
7
+
8
+ begin
9
+ node_graph = compile_to_pretty_relationship_graph(content)
10
+ if node_graph.vertices.count.zero?
11
+ result.set_error('There were no resources created in the node graph. Is there an include statement missing?')
12
+ return result
13
+ end
14
+
15
+ result.vertices = []
16
+ result.edges = []
17
+
18
+ node_graph.vertices.each do |vertex|
19
+ result.vertices << { label: vertex.to_s }
20
+ end
21
+ node_graph.edges.each do |edge|
22
+ result.edges << { source: edge.source.to_s, target: edge.target.to_s }
23
+ end
24
+ rescue StandardError => e
25
+ result.set_error("Error while parsing the file. #{e}")
26
+ rescue LoadError => e
27
+ result.set_error("Load error while parsing the file. #{e}")
28
+ end
29
+
30
+ result
31
+ end
32
+
33
+ # Reference - https://github.com/puppetlabs/puppet/blob/master/spec/lib/puppet_spec/compiler.rb
34
+ def self.compile_to_catalog(string, node = Puppet::Node.new('test'))
35
+ Puppet[:code] = string
36
+ # see lib/puppet/indirector/catalog/compiler.rb#filter
37
+ Puppet::Parser::Compiler.compile(node).filter(&:virtual?)
38
+ end
39
+
40
+ def self.compile_to_ral(manifest, node = Puppet::Node.new('test'))
41
+ # Add the node facts if they don't already exist
42
+ node.merge(Facter.to_hash) if node.facts.nil?
43
+
44
+ catalog = compile_to_catalog(manifest, node)
45
+ ral = catalog.to_ral
46
+ ral.finalize
47
+ ral
48
+ end
49
+
50
+ def self.compile_to_relationship_graph(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)
51
+ ral = compile_to_ral(manifest)
52
+ graph = Puppet::Graph::RelationshipGraph.new(prioritizer)
53
+ graph.populate_from(ral)
54
+ graph
55
+ end
56
+
57
+ def self.compile_to_pretty_relationship_graph(manifest, prioritizer = Puppet::Graph::SequentialPrioritizer.new)
58
+ graph = compile_to_relationship_graph(manifest, prioritizer)
59
+
60
+ # Remove vertexes which just clutter the graph
61
+
62
+ # Remove all of the Puppet::Type::Whit nodes. This is an internal only class
63
+ list = graph.vertices.select { |node| node.is_a?(Puppet::Type::Whit) }
64
+ list.each { |node| graph.remove_vertex!(node) }
65
+
66
+ # Remove all of the Puppet::Type::Schedule nodes
67
+ list = graph.vertices.select { |node| node.is_a?(Puppet::Type::Schedule) }
68
+ list.each { |node| graph.remove_vertex!(node) }
69
+
70
+ # Remove all of the Puppet::Type::Filebucket nodes
71
+ list = graph.vertices.select { |node| node.is_a?(Puppet::Type::Filebucket) }
72
+ list.each { |node| graph.remove_vertex!(node) }
73
+
74
+ graph
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,399 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServerSidecar
4
+ module PuppetStringsHelper
5
+ def self.instance
6
+ @instance ||= Helper.new
7
+ end
8
+
9
+ def self.file_documentation(path, puppet_path, cache = nil)
10
+ instance.file_documentation(path, puppet_path, cache)
11
+ end
12
+
13
+ def self.require_puppet_strings
14
+ return @puppet_strings_loaded unless @puppet_strings_loaded.nil?
15
+
16
+ begin
17
+ require 'puppet-strings'
18
+ require 'puppet-strings/yard'
19
+ require 'puppet-strings/json'
20
+
21
+ require File.expand_path(File.join(File.dirname(__FILE__), 'puppet_strings_monkey_patches'))
22
+ @puppet_strings_loaded = true
23
+ rescue LoadError => e
24
+ PuppetLanguageServerSidecar.log_message(:error, "[PuppetStringsHelper::require_puppet_strings] Unable to load puppet-strings gem: #{e}")
25
+ @puppet_strings_loaded = false
26
+ end
27
+ @puppet_strings_loaded
28
+ end
29
+
30
+ def self.setup_yard!
31
+ unless @yard_setup # rubocop:disable Style/GuardClause
32
+ ::PuppetStrings::Yard.setup!
33
+ @yard_setup = true
34
+ end
35
+ end
36
+
37
+ class Helper
38
+ # Returns a FileDocumentation object for a given path
39
+ #
40
+ # @param [String] path The absolute path to the file that will be documented
41
+ # @param [PuppetLanguageServerSidecar::Cache] cache A Sidecar cache which stores already parsed documents as serialised FileDocumentation objects
42
+ # @return [FileDocumentation, nil] Returns the documentation for the path, or nil if it cannot be extracted
43
+ def file_documentation(path, puppet_path, cache = nil)
44
+ return nil unless PuppetLanguageServerSidecar::PuppetStringsHelper.require_puppet_strings
45
+
46
+ @helper_cache = FileDocumentationCache.new if @helper_cache.nil?
47
+ return @helper_cache.document(path) if @helper_cache.path_exists?(path)
48
+
49
+ # Load from the permanent cache
50
+ @helper_cache.populate_from_sidecar_cache!(path, cache) unless cache.nil? || !cache.active?
51
+ return @helper_cache.document(path) if @helper_cache.path_exists?(path)
52
+
53
+ PuppetLanguageServerSidecar.log_message(:debug, "[PuppetStringsHelper::file_documentation] Fetching documentation for #{path}")
54
+
55
+ PuppetLanguageServerSidecar::PuppetStringsHelper.setup_yard!
56
+
57
+ # For now, assume a single file path
58
+ search_patterns = [path]
59
+
60
+ # Format the arguments to YARD
61
+ args = ['doc']
62
+ args << '--no-output'
63
+ args << '--quiet'
64
+ args << '--no-stats'
65
+ args << '--no-progress'
66
+ args << '--no-save'
67
+ args << '--api public'
68
+ args << '--api private'
69
+ args << '--no-api'
70
+ args += search_patterns
71
+
72
+ # Run YARD
73
+ ::YARD::CLI::Yardoc.run(*args)
74
+
75
+ # Populate the documentation cache from the YARD information
76
+ @helper_cache.populate_from_yard_registry!(puppet_path)
77
+
78
+ # Save to the permanent cache
79
+ @helper_cache.save_to_sidecar_cache(path, cache) unless cache.nil? || !cache.active?
80
+
81
+ # Return the documentation details
82
+ @helper_cache.document(path)
83
+ end
84
+ end
85
+ end
86
+
87
+ class FileDocumentationCache
88
+ def initialize
89
+ # Hash of <[String] path, FileDocumentation> objects
90
+ @cache = {}
91
+ end
92
+
93
+ def path_exists?(path)
94
+ @cache.key?(path)
95
+ end
96
+
97
+ def document(path)
98
+ @cache[path]
99
+ end
100
+
101
+ def populate_from_yard_registry!(puppet_path)
102
+ # Extract all of the information
103
+ # Ref - https://github.com/puppetlabs/puppet-strings/blob/87a8e10f45bfeb7b6b8e766324bfb126de59f791/lib/puppet-strings/json.rb#L10-L16
104
+ populate_classes_from_yard_registry!
105
+ populate_data_types_from_yard_registry!
106
+ populate_functions_from_yard_registry!
107
+ populate_types_from_yard_registry!(puppet_path)
108
+ end
109
+
110
+ def populate_from_sidecar_cache!(path, cache)
111
+ cached_result = cache.load(path, PuppetLanguageServerSidecar::Cache::PUPPETSTRINGS_SECTION)
112
+ unless cached_result.nil? # rubocop:disable Style/GuardClause Reads better this way
113
+ begin
114
+ obj = FileDocumentation.new.from_json!(cached_result)
115
+ @cache[path] = obj
116
+ rescue StandardError => e
117
+ PuppetLanguageServerSidecar.log_message(:warn, "[FileDocumentationCache::populate_from_sidecar_cache!] Error while deserializing #{path} from cache: #{e}")
118
+ end
119
+ end
120
+ end
121
+
122
+ def save_to_sidecar_cache(path, cache)
123
+ cache.save(path, PuppetLanguageServerSidecar::Cache::PUPPETSTRINGS_SECTION, document(path).to_json) if cache.active?
124
+ end
125
+
126
+ private
127
+
128
+ def populate_classes_from_yard_registry!
129
+ %I[puppet_class puppet_defined_type].each do |yard_type|
130
+ YARD::Registry.all(yard_type).map(&:to_hash).each do |item|
131
+ source_path = item[:file]
132
+ class_name = item[:name].to_s
133
+ @cache[source_path] = FileDocumentation.new(source_path) if @cache[source_path].nil?
134
+
135
+ obj = PuppetLanguageServer::Sidecar::Protocol::PuppetClass.new
136
+ obj.key = class_name
137
+ obj.source = item[:file]
138
+ obj.calling_source = obj.source
139
+ obj.line = item[:line]
140
+
141
+ obj.doc = item[:docstring][:text]
142
+ obj.parameters = {}
143
+ # Extract the class parameters
144
+ unless item[:docstring][:tags].nil?
145
+ item[:docstring][:tags].select { |tag| tag[:tag_name] == 'param' && tag.key?(:types) }.each do |tag|
146
+ param_name = tag[:name]
147
+ obj.parameters[param_name] = {
148
+ doc: tag[:text],
149
+ type: tag[:types]&.join(', ')
150
+ }
151
+ end
152
+ end
153
+
154
+ @cache[source_path].classes << obj
155
+ end
156
+ end
157
+ end
158
+
159
+ def populate_data_types_from_yard_registry!
160
+ ::YARD::Registry.all(:puppet_data_type).map(&:to_hash).each do |item|
161
+ source_path = item[:file]
162
+ type_name = item[:name].to_s
163
+ @cache[source_path] = FileDocumentation.new(source_path) if @cache[source_path].nil?
164
+
165
+ obj = PuppetLanguageServer::Sidecar::Protocol::PuppetDataType.new
166
+ obj.key = type_name
167
+ obj.source = item[:file]
168
+ obj.calling_source = obj.source
169
+ obj.line = item[:line]
170
+ obj.doc = item[:docstring][:text]
171
+ obj.is_type_alias = false
172
+ obj.alias_of = nil
173
+
174
+ defaults = item[:defaults] || {}
175
+ unless item[:docstring][:tags].nil?
176
+ item[:docstring][:tags].select { |tag| tag[:tag_name] == 'param' }.each do |tag|
177
+ obj.attributes << PuppetLanguageServer::Sidecar::Protocol::PuppetDataTypeAttribute.new.from_h!(
178
+ 'key' => tag[:name],
179
+ 'default_value' => defaults[tag[:name]],
180
+ 'doc' => tag[:text],
181
+ 'types' => tag[:types].nil? ? nil : tag[:types].join(', ')
182
+ )
183
+ end
184
+ end
185
+
186
+ @cache[source_path].datatypes << obj
187
+ end
188
+ ::YARD::Registry.all(:puppet_data_type_alias).map(&:to_hash).each do |item|
189
+ source_path = item[:file]
190
+ type_name = item[:name].to_s
191
+ @cache[source_path] = FileDocumentation.new(source_path) if @cache[source_path].nil?
192
+
193
+ obj = PuppetLanguageServer::Sidecar::Protocol::PuppetDataType.new
194
+ obj.key = type_name
195
+ obj.source = item[:file]
196
+ obj.calling_source = obj.source
197
+ obj.line = item[:line]
198
+ obj.doc = item[:docstring][:text]
199
+ obj.is_type_alias = true
200
+ obj.alias_of = item[:alias_of]
201
+
202
+ @cache[source_path].datatypes << obj
203
+ end
204
+ end
205
+
206
+ def populate_functions_from_yard_registry!
207
+ ::YARD::Registry.all(:puppet_function).map(&:to_hash).each do |item|
208
+ source_path = item[:file]
209
+ func_name = item[:name].to_s
210
+ @cache[source_path] = FileDocumentation.new(source_path) if @cache[source_path].nil?
211
+
212
+ obj = PuppetLanguageServer::Sidecar::Protocol::PuppetFunction.new
213
+ obj.key = func_name
214
+ obj.source = item[:file]
215
+ obj.calling_source = obj.source
216
+ obj.line = item[:line]
217
+ obj.doc = item[:docstring][:text]
218
+ # 'ruby3x' functions are version 3. 'ruby4x' and 'puppet' are version 4
219
+ obj.function_version = item[:type] == 'ruby3x' ? 3 : 4
220
+
221
+ # Try and determine the function call site from the source file
222
+ char = item[:source].index(":#{func_name}")
223
+ unless char.nil?
224
+ obj.char = char
225
+ obj.length = func_name.length + 1
226
+ end
227
+
228
+ # Note that puppet strings doesn't populate the method signatures for V3 functions
229
+ # Also, we don't have access to the arity of V3 functions so we can't reverse engineer the signature
230
+ item[:signatures].each do |signature|
231
+ sig = PuppetLanguageServer::Sidecar::Protocol::PuppetFunctionSignature.new
232
+
233
+ sig.key = signature[:signature]
234
+ sig.doc = signature[:docstring][:text]
235
+
236
+ unless signature[:docstring][:tags].nil?
237
+ signature[:docstring][:tags].each do |tag|
238
+ case tag[:tag_name]
239
+ when 'param'
240
+ sig.parameters << PuppetLanguageServer::Sidecar::Protocol::PuppetFunctionSignatureParameter.new.from_h!(
241
+ 'name' => tag[:name],
242
+ 'types' => tag[:types],
243
+ 'doc' => tag[:text]
244
+ )
245
+ when 'return'
246
+ sig.return_types = tag[:types]
247
+ end
248
+ end
249
+ end
250
+ calculate_signature_parameter_locations!(sig)
251
+ obj.signatures << sig
252
+ end
253
+
254
+ # Extract other common information
255
+ # TODO: Other common tags include `example`, `overload`
256
+ pre_docs = ''
257
+ pre_docs += "This uses the legacy Ruby function API\n" if item[:type] == 'ruby3x'
258
+ since_tag = item[:docstring][:tags].find { |tag| tag[:tag_name] == 'since' }
259
+ pre_docs += "Since #{since_tag[:text]}\n" unless since_tag.nil?
260
+ obj.doc = "#{pre_docs}\n#{obj.doc}" unless pre_docs.empty?
261
+
262
+ @cache[source_path].functions << obj
263
+ end
264
+ end
265
+
266
+ def populate_types_from_yard_registry!(puppet_path)
267
+ ::YARD::Registry.all(:puppet_type).map(&:to_hash).each do |item|
268
+ type_name = item[:name].to_s
269
+ source_path = item[:file]
270
+ @cache[source_path] = FileDocumentation.new(source_path) if @cache[source_path].nil?
271
+
272
+ obj = PuppetLanguageServer::Sidecar::Protocol::PuppetType.new
273
+ obj.key = type_name
274
+ obj.source = item[:file]
275
+ obj.calling_source = obj.source
276
+ obj.line = item[:line]
277
+ obj.doc = item[:docstring][:text]
278
+
279
+ obj.attributes = {}
280
+ unless item[:properties].nil?
281
+ item[:properties].each do |prop|
282
+ obj.attributes[prop[:name]] = {
283
+ type: :property,
284
+ doc: prop[:description]
285
+ }
286
+ end
287
+ end
288
+ unless item[:parameters].nil?
289
+ item[:parameters].each do |prop|
290
+ obj.attributes[prop[:name]] = {
291
+ type: :param,
292
+ doc: prop[:description],
293
+ isnamevar?: prop[:isnamevar]
294
+ }
295
+ end
296
+ end
297
+
298
+ if obj.key == 'file'
299
+ # Special case for file type
300
+ # we need to set the source and calling_source to the correct file definition
301
+ path = File.join(puppet_path, 'lib/puppet/type')
302
+ obj.source = "#{path}/#{obj.key}.rb"
303
+ obj.calling_source = obj.source
304
+ end
305
+ @cache[source_path].types << obj
306
+ end
307
+ end
308
+
309
+ def calculate_signature_parameter_locations!(sig)
310
+ # When Puppet Strings extracts the parameter name it differs from how it appears in the signature key
311
+ # This makes it hard for clients to determine where in the signature, the parameter actually is. So
312
+ # We need to calculate where in the signature key a parameter is
313
+
314
+ sig.parameters.each do |param|
315
+ name = param.name.dup # Don't want to modify the original object
316
+ # Munge the parameter name to what it appears in the signature key
317
+ # Ref - https://github.com/puppetlabs/puppet-strings/blob/2987558bb3170bc37e6077aab1b60efb17161eff/lib/puppet-strings/yard/handlers/ruby/function_handler.rb#L293-L317
318
+ if name.start_with?('*', '&')
319
+ name.insert(1, '$')
320
+ else
321
+ name = "$#{name}"
322
+ end
323
+
324
+ # We need to use terminating characters here due to substring matching e.g. $abc will incorrectly match in
325
+ # function([String] $abc123, [String] $abc)
326
+ idx = sig.key.index("#{name},")
327
+ idx = sig.key.index("#{name})") if idx.nil?
328
+
329
+ unless idx.nil?
330
+ param.signature_key_offset = idx
331
+ param.signature_key_length = name.length
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ class FileDocumentation
338
+ # The path to file that has been documented
339
+ attr_accessor :path
340
+
341
+ # PuppetLanguageServer::Sidecar::Protocol::PuppetClassList object holding all classes
342
+ attr_accessor :classes
343
+
344
+ # PuppetLanguageServer::Sidecar::Protocol::PuppetDataTypeList object holding all types
345
+ attr_accessor :datatypes
346
+
347
+ # PuppetLanguageServer::Sidecar::Protocol::PuppetFunctionList object holding all functions
348
+ attr_accessor :functions
349
+
350
+ # PuppetLanguageServer::Sidecar::Protocol::PuppetTypeList object holding all types
351
+ attr_accessor :types
352
+
353
+ def initialize(path = nil)
354
+ @path = path
355
+ @classes = PuppetLanguageServer::Sidecar::Protocol::PuppetClassList.new
356
+ @datatypes = PuppetLanguageServer::Sidecar::Protocol::PuppetDataTypeList.new
357
+ @functions = PuppetLanguageServer::Sidecar::Protocol::PuppetFunctionList.new
358
+ @types = PuppetLanguageServer::Sidecar::Protocol::PuppetTypeList.new
359
+ end
360
+
361
+ # Serialisation
362
+ def to_h
363
+ {
364
+ 'path' => path,
365
+ 'classes' => classes,
366
+ 'datatypes' => datatypes,
367
+ 'functions' => functions,
368
+ 'types' => types
369
+ }
370
+ end
371
+
372
+ def to_json(*options)
373
+ JSON.generate(to_h, options)
374
+ end
375
+
376
+ # Deserialisation
377
+ def from_json!(json_string)
378
+ obj = JSON.parse(json_string)
379
+
380
+ obj.keys.each do |key|
381
+ case key
382
+ when 'path'
383
+ # Simple deserialised object types
384
+ self.instance_variable_set(:"@#{key}", obj[key]) # rubocop:disable Style/RedundantSelf Reads better this way
385
+ else
386
+ # Sidecar protocol list object types
387
+ prop = self.instance_variable_get(:"@#{key}") # rubocop:disable Style/RedundantSelf Reads better this way
388
+
389
+ obj[key].each do |child_hash|
390
+ child = prop.child_type.new
391
+ # Let the sidecar deserialise for us
392
+ prop << child.from_h!(child_hash)
393
+ end
394
+ end
395
+ end
396
+ self
397
+ end
398
+ end
399
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yard/logging'
4
+ module YARD
5
+ class Logger < ::Logger
6
+ # Suppress ANY output
7
+ def self.instance(_pipe = $stdout)
8
+ @logger ||= new(nil)
9
+ end
10
+
11
+ # Suppress ANY progress indicators
12
+ def show_progress
13
+ false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppet-languageserver/sidecar_protocol'
4
+
5
+ module PuppetLanguageServerSidecar
6
+ module Protocol
7
+ class PuppetNodeGraph < PuppetLanguageServer::Sidecar::Protocol::PuppetNodeGraph
8
+ def set_error(message) # rubocop:disable Naming/AccessorMethodName
9
+ self.error_content = message
10
+ self.vertices = nil
11
+ self.edges = nil
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServerSidecar
4
+ module Workspace
5
+ @root_path = nil
6
+ @has_module_metadata = false
7
+ @has_environmentconf = false
8
+
9
+ def self.detect_workspace(path)
10
+ result = process_workspace(path)
11
+ @root_path = result[:root_path]
12
+ @has_module_metadata = result[:has_metadatajson]
13
+ @has_environmentconf = result[:has_environmentconf]
14
+ end
15
+
16
+ def self.root_path
17
+ @root_path
18
+ end
19
+
20
+ def self.has_module_metadata? # rubocop:disable Naming/PredicateName
21
+ @has_module_metadata
22
+ end
23
+
24
+ def self.has_environmentconf? # rubocop:disable Naming/PredicateName
25
+ @has_environmentconf
26
+ end
27
+
28
+ # Given a path, locate a metadata.json or environment.conf file to determine where the
29
+ # root of the module/control repo actually is
30
+ def self.find_root_path(path)
31
+ return nil if path.nil?
32
+
33
+ filepath = File.expand_path(path)
34
+
35
+ if dir_exist?(filepath)
36
+ directory = filepath
37
+ elsif file_exist?(filepath)
38
+ directory = File.dirname(filepath)
39
+ else
40
+ return nil
41
+ end
42
+
43
+ until directory.nil?
44
+ break if file_exist?(File.join(directory, 'metadata.json')) || file_exist?(File.join(directory, 'environment.conf'))
45
+
46
+ parent = File.dirname(directory)
47
+ # If the parent is the same as the original, then we've reached the end of the path chain
48
+ directory = if parent == directory
49
+ nil
50
+ else
51
+ parent
52
+ end
53
+ end
54
+
55
+ directory
56
+ end
57
+ private_class_method :find_root_path
58
+
59
+ def self.process_workspace(path)
60
+ result = {
61
+ root_path: nil,
62
+ has_environmentconf: false,
63
+ has_metadatajson: false
64
+ }
65
+ return result if path.nil?
66
+
67
+ root_path = find_root_path(path)
68
+ if root_path.nil?
69
+ result[:root_path] = path
70
+ else
71
+ result[:root_path] = root_path
72
+ result[:has_metadatajson] = file_exist?(File.join(root_path, 'metadata.json'))
73
+ result[:has_environmentconf] = file_exist?(File.join(root_path, 'environment.conf'))
74
+ end
75
+ result
76
+ end
77
+ private_class_method :process_workspace
78
+
79
+ def self.file_exist?(path)
80
+ File.exist?(path) && !File.directory?(path)
81
+ end
82
+ private_class_method :file_exist?
83
+
84
+ def self.dir_exist?(path)
85
+ Dir.exist?(path)
86
+ end
87
+ private_class_method :dir_exist?
88
+ end
89
+ end