openvox-editor-services 4.0.0 → 4.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bdea88fc71917e7a0813d0ede0cd6baa82770a416a998a68ee5a6e6e781fe88d
4
- data.tar.gz: 6de1fb9e00fdfe2dc9824d13d40283c605ef2c79e62452df2b0a7614b4cba634
3
+ metadata.gz: 2482da72d66357f9710a3705a119f913b8b1f589cd8c1308fdf4267380516c14
4
+ data.tar.gz: 9166657dc9d5c62fd15ce34b97a89ab81db74cce077789c9d4801da61cfb7514
5
5
  SHA512:
6
- metadata.gz: cc271d47fb8bfbda55e0948dc08b3786818d14b5141b3fa85fe51fa76a55949ba4e964961296faee32c1276e8a97727765da76eb5888af5eda8faab9c8ae07b6
7
- data.tar.gz: ceff85dbada907b81d0ee9f527e5af58cff47373048c8d8c6de16233968255b02bf74f90d1bb75339497f280d53e89fe65aeadefe5488aa2fa6dd7bde1592a21
6
+ metadata.gz: 07a236de9536aa66d4311bb1632f0686a79bca0e0a2ab167c61ddeee1f0beafab2de63efc3d710b7023c45322f74bfea39b99bd1b1def1b9f15b34e1f9f3dee2
7
+ data.tar.gz: 1c3138263fc890e6c0aacce1c7806a538c73ab3fbd4d488d80ed07f8d21c6ddcd780cd01da4c12049698d502def58660df19be9fa66cbce148a70e288663edce
data/CHANGELOG.md CHANGED
@@ -1,8 +1,16 @@
1
1
  # Changelog
2
2
 
3
- ## [4.0.0](https://github.com/voxpupuli/openvox-editor-services/tree/4.0.0) (2026-11-06)
3
+ ## [4.0.1](https://github.com/voxpupuli/openvox-editor-services/tree/4.0.1) (2026-11-09)
4
4
 
5
- [Full Changelog](https://github.com/voxpupuli/openvox-editor-services/compare/v3.1.2...4.0.0)
5
+ [Full Changelog](https://github.com/voxpupuli/openvox-editor-services/compare/v4.0.0...4.0.1)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - fix: repair/extend attributes completion [\#88](https://github.com/voxpupuli/openvox-editor-services/pull/88) ([jay7x](https://github.com/jay7x))
10
+
11
+ ## [v4.0.0](https://github.com/voxpupuli/openvox-editor-services/tree/v4.0.0) (2026-11-06)
12
+
13
+ [Full Changelog](https://github.com/voxpupuli/openvox-editor-services/compare/v3.1.2...v4.0.0)
6
14
 
7
15
  **Breaking changes:**
8
16
 
data/README.md CHANGED
@@ -11,6 +11,7 @@ The internal `Puppet` Ruby namespace and `puppet/*` protocol methods are retaine
11
11
  - [How to run the Language Server for Development](#how-to-run-the-language-server-for-development)
12
12
  - [How to run the Language Server in Production](#how-to-run-the-language-server-in-production)
13
13
  - [Command line arguments](#command-line-arguments)
14
+ - [Resource attribute completion](#resource-attribute-completion)
14
15
  - [Language Server Sidecar](#language-server-sidecar)
15
16
  - [How to run the Language Server Sidecar for Development](#how-to-run-the-language-server-sidecar-for-development)
16
17
  - [Example usage](#example-usage)
@@ -146,6 +147,13 @@ For the complete option list, run:
146
147
  bundle exec ruby ./openvox-languageserver --help
147
148
  ```
148
149
 
150
+ ### Resource attribute completion
151
+
152
+ Resource attribute completion aligns hashrockets to the longest attribute name in the resource.
153
+ The alignment padding is capped at 999 characters (`MAX_ATTRIBUTE_PADDING`) because OpenVox/Puppet does not limit identifier length.
154
+ This bounds memory and CPU use for pathological manifests and for client-supplied completion data.
155
+ Attributes longer than the cap are inserted without extra alignment padding.
156
+
149
157
  ## Language Server Sidecar
150
158
 
151
159
  ### How to run the Language Server Sidecar for Development
@@ -3,6 +3,7 @@
3
3
  require 'puppet-languageserver/global_queues/single_instance_queue'
4
4
  require 'puppet_editor_services/server'
5
5
  require 'open3'
6
+ require 'rbconfig'
6
7
 
7
8
  module PuppetLanguageServer
8
9
  module GlobalQueues
@@ -37,7 +38,10 @@ module PuppetLanguageServer
37
38
 
38
39
  sidecar_path = File.expand_path(File.join(__dir__, '..', '..', '..', 'openvox-languageserver-sidecar'))
39
40
  args = ['--action', job_object.action].concat(job_object.additional_args).concat(sidecar_args_from_connection(connection))
40
- cmd = ['ruby', sidecar_path].concat(args)
41
+ # Use the same Ruby interpreter as the language server. Relying on a bare
42
+ # 'ruby' from PATH can pick up a different runtime which cannot see the
43
+ # Puppet agent's gems and types.
44
+ cmd = [RbConfig.ruby, sidecar_path].concat(args)
41
45
  PuppetLanguageServer.log_message(:debug, "SidecarQueue Thread: Running sidecar #{cmd}")
42
46
  stdout, stderr, status = run_sidecar(cmd)
43
47
  PuppetLanguageServer.log_message(:warning, "SidecarQueue Thread: Calling sidecar with #{args.join(' ')} returned exitcode #{status.exitstatus}, #{stderr}")
@@ -162,8 +166,25 @@ module PuppetLanguageServer
162
166
  handler.respond_to?(:session_state) ? handler.session_state : nil
163
167
  end
164
168
 
169
+ # The language server removes GEM_HOME/GEM_PATH from its own environment
170
+ # while activating the OpenVox agent runtime. The sidecar is a child
171
+ # process and still needs the bundled extension gems (openvox-strings,
172
+ # yard, etc.), so restore them for the sidecar invocation.
173
+ def sidecar_gem_environment
174
+ gem_home = File.expand_path(File.join(__dir__, '..', '..', '..', 'gems'))
175
+ return {} unless Dir.exist?(gem_home)
176
+
177
+ # Preserve any GEM_PATH entries inherited from the environment (for
178
+ # example a user's own gems) instead of replacing them. Keep the
179
+ # bundled gems first so they win when the same gem is available twice.
180
+ existing_paths = ENV['GEM_PATH'].to_s.split(File::PATH_SEPARATOR).reject(&:empty?)
181
+ gem_path = ([gem_home] + existing_paths).uniq.join(File::PATH_SEPARATOR)
182
+
183
+ { 'GEM_HOME' => gem_home, 'GEM_PATH' => gem_path }
184
+ end
185
+
165
186
  def run_sidecar(cmd)
166
- Open3.capture3(*cmd)
187
+ Open3.capture3(sidecar_gem_environment, *cmd)
167
188
 
168
189
  # The following code should ONLY be uncommented when debugging acceptance tests failures.
169
190
  # It caches the sidecar responses so tests sidecar calls will be (VERY) quick, which speeds
@@ -182,7 +203,7 @@ module PuppetLanguageServer
182
203
  # ]
183
204
  # end
184
205
  # # Rubocop failure is here on purpose
185
- # stdout,stderr,status = Open3.capture3(*cmd)
206
+ # stdout,stderr,status = Open3.capture3(sidecar_gem_environment, *cmd)
186
207
  # File.write(cache_file, stdout, mode: 'wb', encoding: 'utf-8')
187
208
  # [stdout, stderr, status]
188
209
  end
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'puppet-languageserver/manifest/completion_provider_attributes'
4
+
3
5
  module PuppetLanguageServer
4
6
  module Manifest
5
7
  module CompletionProvider
8
+ extend CompletionProviderAttributes
9
+
6
10
  def self.complete(session_state, content, line_num, char_num, options = {})
7
11
  options = {
8
12
  tasks_mode: false,
9
- context: nil # LSP::CompletionContext object
13
+ context: nil, # LSP::CompletionContext object
14
+ file_uri: nil
10
15
  }.merge(options)
11
16
  items = []
12
17
  incomplete = false
@@ -62,14 +67,33 @@ module PuppetLanguageServer
62
67
  all_functions(session_state, options[:tasks_mode]) { |x| items << x }
63
68
 
64
69
  when 'Puppet::Pops::Model::ResourceExpression'
65
- # We are inside a resource definition. Should display all available
70
+ # We are inside a resource definition. Should display all available
66
71
  # properities and parameters.
67
72
 
73
+ # The attribute list is computed for the current prefix, and the
74
+ # client must ask again as the user keeps typing. Otherwise it reuses
75
+ # completion items whose document version and replacement range have
76
+ # gone stale.
77
+ incomplete = true
78
+
68
79
  # Try Types first
69
80
  # The `class` pseudo resource type is actually used to set properties/params for the puppet type
70
81
  # specified in the resource title.
71
82
  # Ref: https://puppet.com/docs/puppet/5.3/lang_classes.html#using-resource-like-declarations
72
83
  item_value = item.type_name.value == 'class' && item.bodies.length == 1 ? item.bodies[0].title.value : item.type_name.value
84
+
85
+ # Align hashrockets with the longest existing attribute name. A zero
86
+ # padding means there are no existing attributes. A value above the
87
+ # cap is pathological and disables alignment entirely.
88
+ operations = item.bodies.flat_map { |body| body.operations || [] }
89
+ file_uri = options[:file_uri]
90
+ document_version = file_uri.nil? ? nil : session_state.documents.document_version(file_uri)
91
+ resource_data = attribute_resource_data(
92
+ content, line_num, char_num,
93
+ { file_uri: file_uri, document_version: document_version, tasks_mode: options[:tasks_mode] },
94
+ item_value, operations
95
+ )
96
+
73
97
  item_object = PuppetLanguageServer::PuppetHelper.get_type(session_state, item_value)
74
98
  unless item_object.nil?
75
99
  # Add Parameters
@@ -78,11 +102,7 @@ module PuppetLanguageServer
78
102
  'label' => name.to_s,
79
103
  'kind' => LSP::CompletionItemKind::PROPERTY,
80
104
  'detail' => 'Parameter',
81
- 'data' => {
82
- 'type' => 'resource_parameter',
83
- 'param' => name.to_s,
84
- 'resource_type' => item_value
85
- }
105
+ 'data' => attribute_data(resource_data, 'resource_parameter', 'param', name)
86
106
  )
87
107
  end
88
108
  # Add Properties
@@ -91,15 +111,12 @@ module PuppetLanguageServer
91
111
  'label' => name.to_s,
92
112
  'kind' => LSP::CompletionItemKind::PROPERTY,
93
113
  'detail' => 'Property',
94
- 'data' => {
95
- 'type' => 'resource_property',
96
- 'prop' => name.to_s,
97
- 'resource_type' => item_value
98
- }
114
+ 'data' => attribute_data(resource_data, 'resource_property', 'prop', name)
99
115
  )
100
116
  end
101
117
  # TODO: What about meta parameters?
102
118
  end
119
+
103
120
  if item_object.nil?
104
121
  # Try Classes/Defined Types
105
122
  item_object = PuppetLanguageServer::PuppetHelper.get_class(session_state, item_value)
@@ -110,11 +127,7 @@ module PuppetLanguageServer
110
127
  'label' => name.to_s,
111
128
  'kind' => LSP::CompletionItemKind::PROPERTY,
112
129
  'detail' => 'Parameter',
113
- 'data' => {
114
- 'type' => 'resource_class_parameter',
115
- 'param' => name.to_s,
116
- 'resource_type' => item_value
117
- }
130
+ 'data' => attribute_data(resource_data, 'resource_class_parameter', 'param', name)
118
131
  )
119
132
  end
120
133
  end
@@ -128,6 +141,7 @@ module PuppetLanguageServer
128
141
  end
129
142
 
130
143
  # BEGIN CompletionItem Helpers
144
+
131
145
  def self.keywords(keywords = [], &block)
132
146
  keywords.each do |keyword|
133
147
  item = LSP::CompletionItem.new(
@@ -247,7 +261,7 @@ module PuppetLanguageServer
247
261
  return result if item_type.nil?
248
262
 
249
263
  result.documentation = item_type.doc unless item_type.doc.nil?
250
- unless item_type.nil? || item_type.signatures.none?
264
+ unless item_type.signatures.none?
251
265
  result.detail = item_type.signatures.map(&:key).join("\n\n")
252
266
  # The signature provider should handle suggestions after this, so just place the cursor ready for an opening bracket
253
267
  result.insertText = data['name'].to_s
@@ -282,48 +296,47 @@ module PuppetLanguageServer
282
296
 
283
297
  result.documentation = item_type.doc unless item_type.doc.nil?
284
298
  result.insertText = snippet
285
- result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
286
299
  when 'resource_parameter'
287
300
  item_type = PuppetLanguageServer::PuppetHelper.get_type(session_state, data['resource_type'])
288
301
  return result if item_type.nil?
289
302
 
290
303
  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
304
+ return result if param_type.nil?
305
+
306
+ # TODO: More things?
307
+ result.documentation = param_type[:doc] unless param_type[:doc].nil?
308
+ attribute_insert_text(session_state, result, data, data['param'])
296
309
  when 'resource_property'
297
310
  item_type = PuppetLanguageServer::PuppetHelper.get_type(session_state, data['resource_type'])
298
311
  return result if item_type.nil?
299
312
 
300
313
  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
314
+ return result if prop_type.nil?
315
+
316
+ # TODO: More things?
317
+ result.documentation = prop_type[:doc] unless prop_type[:doc].nil?
318
+ attribute_insert_text(session_state, result, data, data['prop'])
306
319
 
307
320
  when 'resource_class'
308
321
  item_class = PuppetLanguageServer::PuppetHelper.get_class(session_state, data['name'])
309
322
  return result if item_class.nil?
310
323
 
311
324
  result.insertText = "#{data['name']} { '${1:title}':\n\t$0\n}"
312
- result.insertTextFormat = LSP::InsertTextFormat::SNIPPET
313
325
  when 'resource_class_parameter'
314
326
  item_class = PuppetLanguageServer::PuppetHelper.get_class(session_state, data['resource_type'])
315
327
  return result if item_class.nil?
316
328
 
317
329
  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
330
+ return result if param_type.nil?
331
+
332
+ doc = ''
333
+ doc += param_type[:type] unless param_type[:type].nil?
334
+ doc += "---\n#{param_type[:doc]}" unless param_type[:doc].nil?
335
+ result.documentation = doc
336
+ attribute_insert_text(session_state, result, data, data['param'])
325
337
  end
326
338
 
339
+ result.insertTextFormat = LSP::InsertTextFormat::SNIPPET if data['type'].to_s.start_with?('resource_')
327
340
  result
328
341
  end
329
342
  end
@@ -0,0 +1,211 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServer
4
+ module Manifest
5
+ # Helpers for completing resource attributes.
6
+ module CompletionProviderAttributes
7
+ # OpenVox/Puppet does not limit attribute name length. The longest
8
+ # built-in attribute is 23 characters (selinux_ignore_defaults), and the
9
+ # style guide recommends lines no longer than 140 characters. This cap is
10
+ # purely defensive: it bounds alignment padding for pathological manifests
11
+ # and for client-supplied completion data.
12
+ MAX_ATTRIBUTE_PADDING = 999
13
+
14
+ def attribute_data(resource_data, type, name_key, name)
15
+ name_length = name.to_s.length
16
+ padding = [resource_data['padding'].to_i, name_length].max
17
+ alignment = resource_data['alignment'] == true && padding.positive? && padding <= MAX_ATTRIBUTE_PADDING
18
+ padding = name_length if padding > MAX_ATTRIBUTE_PADDING
19
+
20
+ resource_data.merge(
21
+ 'type' => type,
22
+ name_key => name.to_s,
23
+ 'padding' => padding,
24
+ 'alignment' => alignment
25
+ )
26
+ end
27
+
28
+ # Build the shared data payload for every completion item generated from
29
+ # one resource expression.
30
+ def attribute_resource_data(content, line_num, char_num, options, resource_type, operations)
31
+ slice = content.split("\n", -1)[line_num].to_s[0, char_num].to_s
32
+ indent = slice[/\A[ \t]*/] || ''
33
+ # An unanchored search can retry at every offset, so use rindex instead
34
+ # of a trailing-anchored regex on long runs of attribute-name characters.
35
+ boundary = slice.rindex(/[^\w-]/)
36
+ prefix = boundary.nil? ? slice : slice[(boundary + 1)..].to_s
37
+
38
+ padding = operations.map { |operation| operation.attribute_name.to_s.length }.max || 0
39
+ alignment = padding.positive? && padding <= MAX_ATTRIBUTE_PADDING
40
+
41
+ {
42
+ 'resource_type' => resource_type,
43
+ 'padding' => alignment ? padding : 0,
44
+ 'alignment' => alignment,
45
+ 'replace_range' => LSP.create_range(line_num, char_num - prefix.length, line_num, char_num),
46
+ 'uri' => options[:file_uri],
47
+ 'document_version' => options[:document_version],
48
+ 'cursor_line' => line_num,
49
+ 'cursor_char' => char_num,
50
+ 'indent_width' => indent.length,
51
+ 'tasks_mode' => options[:tasks_mode] == true
52
+ }
53
+ end
54
+
55
+ # Build hashrocket alignment edits for resource attributes.
56
+ #
57
+ # operations - Array of Puppet::Pops::Model::AttributeOperation
58
+ # cursor_line - 0-based line number of the completion, which is skipped
59
+ # indent_width - number of leading spaces on the completion line
60
+ # width - desired attribute-name column width
61
+ #
62
+ # Returns an Array of LSP::TextEdit. Returns an empty Array when
63
+ # alignment is not possible or not safe.
64
+ def alignment_edits_from_operations(operations, cursor_line, indent_width, width)
65
+ return [] if width.nil? || width <= 0 || width > MAX_ATTRIBUTE_PADDING
66
+ return [] if operations.nil? || operations.empty?
67
+
68
+ infos = []
69
+ operations.each do |operation|
70
+ name = operation.attribute_name.to_s
71
+ next if name.empty?
72
+ return [] if name.length > MAX_ATTRIBUTE_PADDING
73
+
74
+ locator = operation.respond_to?(:locator) ? operation.locator : nil
75
+ next if locator.nil?
76
+
77
+ source = locator.string
78
+ next if source.nil?
79
+
80
+ name_start = operation.offset
81
+ name_end = name_start + name.length
82
+ value_expr = operation.value_expr
83
+ next if value_expr.nil? || !value_expr.respond_to?(:offset)
84
+
85
+ gap = source[name_end...value_expr.offset]
86
+ next if gap.nil?
87
+
88
+ arrow_offset = gap.index('=>')
89
+ next if arrow_offset.nil?
90
+
91
+ arrow_start = name_end + arrow_offset
92
+
93
+ infos << {
94
+ line: locator.line_for_offset(name_start) - 1,
95
+ name_end_col: locator.pos_on_line(name_start) - 1 + name.length,
96
+ arrow_col: locator.pos_on_line(arrow_start) - 1
97
+ }
98
+ end
99
+
100
+ return [] if infos.empty?
101
+
102
+ # Match format-on-type: if two hashrockets share a line we cannot
103
+ # align the body safely.
104
+ lines = infos.map { |info| info[:line] }
105
+ return [] if lines.uniq.length != lines.length
106
+
107
+ target_col = infos.map { |info| info[:name_end_col] }.max + 1
108
+ target_col = [target_col, indent_width.to_i + width + 1].max
109
+
110
+ infos.each_with_object([]) do |info, edits|
111
+ # The completion snippet handles the line being completed.
112
+ next if info[:line] == cursor_line
113
+
114
+ whitespace = target_col - info[:name_end_col]
115
+ next if info[:arrow_col] - info[:name_end_col] == whitespace
116
+
117
+ edits << LSP::TextEdit.new.from_h!(
118
+ 'newText' => ' ' * whitespace,
119
+ 'range' => LSP.create_range(info[:line], info[:name_end_col], info[:line], info[:arrow_col])
120
+ )
121
+ end
122
+ end
123
+
124
+ def attribute_insert_text(session_state, item, data, name)
125
+ raw_padding = data['padding'].to_i
126
+ padding = if raw_padding.positive? && raw_padding <= MAX_ATTRIBUTE_PADDING
127
+ [raw_padding, name.to_s.length].max
128
+ else
129
+ name.to_s.length
130
+ end
131
+
132
+ content, char_num, replace_range = attribute_current_context(session_state, data)
133
+ item.insertText = "#{name.to_s.ljust(padding)} => ${1},$0"
134
+ item.textEdit = LSP::TextEdit.new.from_h!('newText' => item.insertText, 'range' => replace_range)
135
+ item.additionalTextEdits = nil
136
+
137
+ return if content.nil?
138
+
139
+ edits = attribute_alignment_edits(content, data, char_num, padding)
140
+ item.additionalTextEdits = edits unless edits.empty?
141
+ end
142
+
143
+ private
144
+
145
+ # Completion lists are reused while the user keeps typing, so the
146
+ # document version stored in the item data can lag behind the open
147
+ # document. When that happens, fall back to the current document and
148
+ # parse at the end of the completion line.
149
+ #
150
+ # Returns [content, cursor_char, replace_range].
151
+ def attribute_current_context(session_state, data)
152
+ uri = data['uri']
153
+ return [nil, nil, data['replace_range']] if uri.nil?
154
+
155
+ line_num = data['cursor_line'].to_i
156
+ content = session_state.documents.document_content(uri, data['document_version'])
157
+ return [content, data['cursor_char'].to_i, data['replace_range']] unless content.nil?
158
+
159
+ content = session_state.documents.document_content(uri)
160
+ return [nil, nil, data['replace_range']] if content.nil?
161
+
162
+ line_text = content.split("\n", -1)[line_num].to_s.rstrip
163
+ prefix = line_text[/[A-Za-z0-9_-]+\z/]
164
+ replace_range = if prefix.nil?
165
+ data['replace_range']
166
+ else
167
+ LSP.create_range(line_num, line_text.length - prefix.length, line_num, line_text.length)
168
+ end
169
+ [content, line_text.length, replace_range]
170
+ end
171
+
172
+ def attribute_alignment_edits(content, data, char_num, padding)
173
+ return [] unless data['alignment'] == true
174
+
175
+ raw_padding = data['padding'].to_i
176
+ return [] unless raw_padding.positive? && raw_padding <= MAX_ATTRIBUTE_PADDING
177
+
178
+ operations = attribute_operations(
179
+ content,
180
+ data['cursor_line'].to_i,
181
+ char_num.to_i,
182
+ data['tasks_mode'] == true
183
+ )
184
+ alignment_edits_from_operations(
185
+ operations,
186
+ data['cursor_line'].to_i,
187
+ data['indent_width'].to_i,
188
+ padding
189
+ )
190
+ rescue StandardError
191
+ []
192
+ end
193
+
194
+ def attribute_operations(content, line_num, char_num, tasks_mode)
195
+ result = PuppetLanguageServer::PuppetParserHelper.object_under_cursor(
196
+ content, line_num, char_num,
197
+ multiple_attempts: true,
198
+ disallowed_classes: [Puppet::Pops::Model::QualifiedName, Puppet::Pops::Model::BlockExpression],
199
+ tasks_mode: tasks_mode,
200
+ remove_trigger_char: false
201
+ )
202
+ return [] if result.nil?
203
+
204
+ model = result[:model]
205
+ return [] unless model.instance_of?(::Puppet::Pops::Model::ResourceExpression)
206
+
207
+ model.bodies.flat_map { |body| body.operations || [] }
208
+ end
209
+ end
210
+ end
211
+ end
@@ -163,7 +163,7 @@ module PuppetLanguageServer
163
163
 
164
164
  case documents.document_type(file_uri)
165
165
  when :manifest
166
- PuppetLanguageServer::Manifest::CompletionProvider.complete(session_state, content, line_num, char_num, context:, tasks_mode: documents.plan_file?(file_uri))
166
+ PuppetLanguageServer::Manifest::CompletionProvider.complete(session_state, content, line_num, char_num, context: context, tasks_mode: documents.plan_file?(file_uri), file_uri: file_uri)
167
167
  else
168
168
  raise "Unable to provide completion on #{file_uri}"
169
169
  end
@@ -262,16 +262,20 @@ module PuppetLanguageServerSidecar
262
262
  { relative_dir: 'manifests', glob: '/**/*.pp' } # Pretty much everything in most modules
263
263
  ],
264
264
  datatype: [
265
- { relative_dir: 'lib/puppet/datatypes', glob: '/**/*.rb' }, # Custom Data Types
266
- { relative_dir: 'types', glob: '/**/*.pp' } # Data Type aliases
265
+ { relative_dir: 'lib/puppet/datatypes', glob: '/**/*.rb' }, # Custom Data Types
266
+ { relative_dir: 'vendor_ruby/puppet/datatypes', glob: '/**/*.rb' }, # As above, in a Puppet agent layout
267
+ { relative_dir: 'types', glob: '/**/*.pp' } # Data Type aliases
267
268
  ],
268
269
  function: [
269
- { relative_dir: 'functions', glob: '/**/*.pp' }, # Contains custom functions written in the Puppet language.
270
- { relative_dir: 'lib/puppet/functions', glob: '/**/*.rb' }, # Contains functions written in Ruby for the modern Puppet::Functions API
271
- { relative_dir: 'lib/puppet/parser/functions', glob: '/**/*.rb' } # Contains functions written in Ruby for the legacy Puppet::Parser::Functions API
270
+ { relative_dir: 'functions', glob: '/**/*.pp' }, # Contains custom functions written in the Puppet language.
271
+ { relative_dir: 'lib/puppet/functions', glob: '/**/*.rb' }, # Contains functions written in Ruby for the modern Puppet::Functions API
272
+ { relative_dir: 'vendor_ruby/puppet/functions', glob: '/**/*.rb' }, # As above, in a Puppet agent layout
273
+ { relative_dir: 'lib/puppet/parser/functions', glob: '/**/*.rb' }, # Contains functions written in Ruby for the legacy Puppet::Parser::Functions API
274
+ { relative_dir: 'vendor_ruby/puppet/parser/functions', glob: '/**/*.rb' } # As above, in a Puppet agent layout
272
275
  ],
273
276
  type: [
274
- { relative_dir: 'lib/puppet/type', glob: '/{,file/}*.rb' } # Contains Puppet resource types. Resource types like `file` can live in subdirs, hence the glob
277
+ { relative_dir: 'lib/puppet/type', glob: '/{,file/}*.rb' }, # Contains Puppet resource types. Resource types like `file` can live in subdirs, hence the glob
278
+ { relative_dir: 'vendor_ruby/puppet/type', glob: '/{,file/}*.rb' } # As above, in a Puppet agent layout
275
279
  ]
276
280
  }
277
281
  end
@@ -294,6 +294,16 @@ module PuppetLanguageServerSidecar
294
294
  }
295
295
  end
296
296
  end
297
+ # Checks are parameters too. Types such as exec use `newcheck` for
298
+ # attributes like `creates`, `unless`, `onlyif` and `refreshonly`.
299
+ unless item[:checks].nil?
300
+ item[:checks].each do |check|
301
+ obj.attributes[check[:name]] = {
302
+ type: :param,
303
+ doc: check[:description]
304
+ }
305
+ end
306
+ end
297
307
 
298
308
  if obj.key == 'file'
299
309
  # Special case for file type
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'yard/logging'
4
4
  module YARD
5
- class Logger < ::Logger
5
+ class Logger
6
6
  # Suppress ANY output
7
7
  def self.instance(_pipe = $stdout)
8
8
  @logger ||= new(nil)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PuppetEditorServices
4
- PUPPETEDITORSERVICESVERSION = '4.0.0' unless defined? PUPPETEDITORSERVICESVERSION
4
+ PUPPETEDITORSERVICESVERSION = '4.0.1' unless defined? PUPPETEDITORSERVICESVERSION
5
5
 
6
6
  # @api public
7
7
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: openvox-editor-services
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - OpenVox Project contributors
@@ -192,6 +192,7 @@ files:
192
192
  - lib/puppet-languageserver/global_queues/single_instance_queue.rb
193
193
  - lib/puppet-languageserver/global_queues/validation_queue.rb
194
194
  - lib/puppet-languageserver/manifest/completion_provider.rb
195
+ - lib/puppet-languageserver/manifest/completion_provider_attributes.rb
195
196
  - lib/puppet-languageserver/manifest/definition_provider.rb
196
197
  - lib/puppet-languageserver/manifest/document_formatting_provider.rb
197
198
  - lib/puppet-languageserver/manifest/document_symbol_provider.rb