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,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServer
4
+ module Manifest
5
+ module SignatureProvider
6
+ def self.signature_help(session_state, content, line_num, char_num, options = {})
7
+ options = {
8
+ tasks_mode: false
9
+ }.merge(options)
10
+
11
+ result = PuppetLanguageServer::PuppetParserHelper.object_under_cursor(content, line_num, char_num,
12
+ multiple_attempts: false,
13
+ tasks_mode: options[:tasks_mode],
14
+ remove_trigger_char: false)
15
+ response = LSP::SignatureHelp.new.from_h!('signatures' => [], 'activeSignature' => nil, 'activeParameter' => nil)
16
+ # We are in the root of the document so no signatures here.
17
+ return response if result.nil?
18
+
19
+ item = result[:model]
20
+ path = result[:path]
21
+ locator = result[:locator]
22
+
23
+ function_ast_object = nil
24
+ # Try and find the acutal function object within the AST
25
+ if item.instance_of?(::Puppet::Pops::Model::CallNamedFunctionExpression)
26
+ function_ast_object = item
27
+ else
28
+ # Try and find the function with the AST tree
29
+ distance_up_ast = -1
30
+ function_ast_object = path[distance_up_ast]
31
+ while !function_ast_object.nil? && function_ast_object.class.to_s != 'Puppet::Pops::Model::CallNamedFunctionExpression'
32
+ distance_up_ast -= 1
33
+ function_ast_object = path[distance_up_ast]
34
+ end
35
+ raise "Unable to find suitable parent object for object of type #{item.class}" if function_ast_object.nil?
36
+ end
37
+
38
+ function_name = function_ast_object.functor_expr.value
39
+ raise 'Could not determine the function name' if function_name.nil?
40
+
41
+ # Convert line and char nums (base 0) to an absolute offset within the document
42
+ # result.line_offsets contains an array of the offsets on a per line basis e.g.
43
+ # [0, 14, 34, 36] means line number 2 starts at absolute offset 34
44
+ # Once we know the line offset, we can simply add on the char_num to get the absolute offset
45
+ line_offset = if function_ast_object.respond_to?(:locator)
46
+ function_ast_object.locator.line_index[line_num]
47
+ else
48
+ locator.line_index[line_num]
49
+ end
50
+
51
+ abs_offset = line_offset + char_num
52
+ # We need to use offsets here in case functions span lines
53
+ param_number = param_number_from_ast(abs_offset, function_ast_object, locator)
54
+ raise 'Cursor is not within the function expression' if param_number.nil?
55
+
56
+ func_info = PuppetLanguageServer::PuppetHelper.function(session_state, function_name)
57
+ raise "Function #{function_name} does not exist" if func_info.nil?
58
+
59
+ func_info.signatures.each do |sig|
60
+ lsp_sig = LSP::SignatureInformation.new.from_h!(
61
+ 'label' => sig.key,
62
+ 'documentation' => sig.doc,
63
+ 'parameters' => []
64
+ )
65
+
66
+ sig.parameters.each do |param|
67
+ lsp_sig.parameters << LSP::ParameterInformation.new.from_h!(
68
+ 'label' => param.signature_key_offset.nil? || param.signature_key_length.nil? ? param.name : [param.signature_key_offset, param.signature_key_offset + param.signature_key_length],
69
+ 'documentation' => param.doc
70
+ )
71
+ end
72
+
73
+ response.signatures << lsp_sig
74
+ end
75
+
76
+ # Now figure out the first signature which could have the same or more than the number of arguments in the function call
77
+ func_arg_count = function_ast_object.arguments.count
78
+ signature_number = func_info.signatures.find_index { |sig| sig.parameters.count >= func_arg_count }
79
+
80
+ # If we still don't know the signature number then assume it's the first one
81
+ signature_number = 0 if signature_number.nil? && func_info.signatures.count > 0
82
+
83
+ response.activeSignature = signature_number unless signature_number.nil?
84
+ response.activeParameter = param_number
85
+
86
+ response
87
+ end
88
+
89
+ def self.param_number_from_ast(char_offset, function_ast_object, locator)
90
+ # Figuring out which parameter the cursor is in is a little tricky. For example:
91
+ #
92
+ # func_two_param( $param1 , $param2 )
93
+ # |------------------------------------| Locator for the entire function
94
+ # |-------------| Locator for the function expression
95
+ # |-----| Locator for function.arguments[0] child (contains it's children)
96
+ # |-----| Locator for function.arguments[1] child (contains it's children)
97
+ #
98
+ # Importantly, whitespace isn't included in the AST
99
+
100
+ function_offset = function_ast_object.offset
101
+ function_length = function_ast_object.length
102
+ functor_expr_offset = function_ast_object.functor_expr.offset
103
+ functor_expr_length = function_ast_object.functor_expr.length
104
+
105
+ # Shouldn't happen but, safety first!
106
+ return nil if function_offset.nil? || function_length.nil? || functor_expr_offset.nil? || functor_expr_length.nil?
107
+
108
+ # Is the cursor on the function name or the opening bracket? then we are not in any parameters
109
+ return nil if char_offset <= functor_expr_offset + functor_expr_length
110
+ # Is the cursor on or beyond the closing bracket? then we are not in any parameters
111
+ return nil if char_offset >= function_offset + function_length
112
+ # Does the function even have arguments? then the cursor HAS to be in the first parameter
113
+ return 0 if function_ast_object.arguments.count.zero?
114
+
115
+ # Is the cursor within any of the function argument locations? if so, return the parameter number we're in
116
+ param_number = function_ast_object.arguments.find_index { |arg| char_offset >= arg.offset && char_offset <= arg.offset + arg.length }
117
+ return param_number unless param_number.nil?
118
+
119
+ # So now we know the char_offset exists outside any of the locators. Check the extremities
120
+ # Is it before the first argument? if so then the parameter number has to be zero
121
+ return 0 if char_offset < function_ast_object.arguments[0].offset
122
+
123
+ last_arg_index = function_ast_object.arguments.count - 1
124
+ last_offset = function_ast_object.arguments[last_arg_index].offset + function_ast_object.arguments[last_arg_index].length
125
+ if char_offset > last_offset
126
+ # We know that the cursor is after the last argument, but before the closing bracket.
127
+ # Therefore the before_index is the last argument in the list, and the after_index is one more than that
128
+ before_index = last_arg_index
129
+ after_index = last_arg_index + 1
130
+ before_offset = function_ast_object.arguments[before_index].offset
131
+ before_length = function_ast_object.arguments[before_index].length
132
+ # But we need to get the text after the last argument, to the closing bracket
133
+ locator_args = [
134
+ last_offset,
135
+ function_offset + function_length - last_offset - 1 # Find the difference from the entire function length and the last offset and subtract 1 for the closing bracket as we don't need it
136
+ ]
137
+ else
138
+ # Now we now the char_offset exists between two existing locators. Determine the location by finding which argument is AFTER the cursor
139
+ after_index = function_ast_object.arguments.find_index { |arg| char_offset < arg.offset }
140
+ return nil if after_index.nil? || after_index.zero? # This should never happen but, you never know.
141
+
142
+ before_index = after_index - 1
143
+
144
+ # Now we know between which arguments (before_index and after_index) the char_offset lies
145
+ before_length = function_ast_object.arguments[before_index].length
146
+ before_offset = function_ast_object.arguments[before_index].offset
147
+ after_offset = function_ast_object.arguments[after_index].offset
148
+
149
+ # Determine the text between the two arguments
150
+ locator_args = [
151
+ before_offset + before_length, # From the end the begin_index
152
+ after_offset - before_offset - before_length # to the start of the after_index
153
+ ]
154
+ end
155
+ between_text = function_ast_object.respond_to?(:locator) ? function_ast_object.locator.extract_text(*locator_args) : locator.extract_text(*locator_args)
156
+
157
+ # Now we have the text between the two arguments, determine where the comma is
158
+ comma_index = between_text.index(',')
159
+ # If there is no comma then it has to be the before_index i.e.
160
+ # ..., $param ) <--- the space before the closing bracket would have a between_text of ' '
161
+ return before_index if comma_index.nil?
162
+
163
+ # If the char_offset is after the comma then choose the after_index otherwise choose the before_index
164
+ char_offset > before_offset + before_length + comma_index ? after_index : before_index
165
+ end
166
+ private_class_method :param_number_from_ast
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puppet-lint'
4
+ module PuppetLanguageServer
5
+ module Manifest
6
+ module ValidationProvider
7
+ # Similar to 'validate' this will run puppet-lint and returns
8
+ # the manifest with any fixes applied
9
+ #
10
+ # Returns:
11
+ # [ <Int> Number of problems fixed,
12
+ # <String> New Content
13
+ # ]
14
+ def self.fix_validate_errors(session_state, content)
15
+ init_puppet_lint(session_state.documents.store_root_path, ['--fix'])
16
+
17
+ linter = PuppetLint::Checks.new
18
+ linter.load_data(nil, content)
19
+
20
+ problems = linter.run(nil, content)
21
+ problems_fixed = problems.nil? ? 0 : problems.count { |item| item[:kind] == :fixed }
22
+
23
+ [problems_fixed, linter.manifest]
24
+ end
25
+
26
+ def self.validate(session_state, content, options = {})
27
+ options = {
28
+ max_problems: 100,
29
+ tasks_mode: false
30
+ }.merge(options)
31
+
32
+ result = []
33
+ # TODO: Need to implement max_problems
34
+ problems = 0
35
+
36
+ init_puppet_lint(session_state.documents.store_root_path, [])
37
+
38
+ begin
39
+ linter = PuppetLint::Checks.new
40
+ linter.load_data(nil, content)
41
+
42
+ problems = linter.run(nil, content)
43
+ unless problems.nil?
44
+ problems.each do |problem|
45
+ # Syntax errors are better handled by the puppet parser, not puppet lint
46
+ next if problem[:kind] == :error && problem[:check] == :syntax
47
+ # Ignore linting errors what were ignored by puppet-lint
48
+ next if problem[:kind] == :ignored
49
+
50
+ severity = case problem[:kind]
51
+ when :error
52
+ LSP::DiagnosticSeverity::ERROR
53
+ when :warning
54
+ LSP::DiagnosticSeverity::WARNING
55
+ else
56
+ LSP::DiagnosticSeverity::HINT
57
+ end
58
+
59
+ endpos = problem[:column] - 1
60
+ endpos = problem[:column] - 1 + problem[:token].to_manifest.length unless problem[:token].nil? || problem[:token].value.nil?
61
+
62
+ result << LSP::Diagnostic.new('severity' => severity,
63
+ 'code' => problem[:check].to_s,
64
+ 'range' => LSP.create_range(problem[:line] - 1, problem[:column] - 1, problem[:line] - 1, endpos),
65
+ 'source' => 'Puppet',
66
+ 'message' => problem[:message])
67
+ end
68
+ end
69
+ rescue StandardError
70
+ # If anything catastrophic happens we resort to puppet parsing anyway
71
+ end
72
+ # TODO: Should I wrap this thing in a big rescue block?
73
+ Puppet[:code] = content
74
+ env = Puppet.lookup(:current_environment)
75
+ loaders = Puppet::Pops::Loaders.new(env)
76
+ Puppet.override({ loaders: loaders }, 'For puppet parser validate') do
77
+ validation_environment = env
78
+ $PuppetParserMutex.synchronize do # rubocop:disable Style/GlobalVars
79
+ original_taskmode = Puppet[:tasks] if Puppet.tasks_supported?
80
+ Puppet[:tasks] = options[:tasks_mode] if Puppet.tasks_supported?
81
+ validation_environment.check_for_reparse
82
+ validation_environment.known_resource_types.clear
83
+ ensure
84
+ Puppet[:tasks] = original_taskmode if Puppet.tasks_supported?
85
+ end
86
+ rescue StandardError => e
87
+ # Sometimes the error is in the cause not the root object itself
88
+ e = e.cause if !e.respond_to?(:line) && e.respond_to?(:cause)
89
+ ex_line = e.respond_to?(:line) && !e.line.nil? ? e.line - 1 : nil # Line numbers from puppet exceptions are base 1
90
+ ex_pos = e.respond_to?(:pos) && !e.pos.nil? ? e.pos : nil # Pos numbers from puppet are base 1
91
+
92
+ message = e.respond_to?(:message) ? e.message : nil
93
+ message = e.basic_message if message.nil? && e.respond_to?(:basic_message)
94
+
95
+ unless ex_line.nil? || ex_pos.nil? || message.nil?
96
+ result << LSP::Diagnostic.new('severity' => LSP::DiagnosticSeverity::ERROR,
97
+ 'range' => LSP.create_range(ex_line, ex_pos, ex_line, ex_pos + 1),
98
+ 'source' => 'Puppet',
99
+ 'message' => message)
100
+ end
101
+ end
102
+
103
+ result
104
+ end
105
+
106
+ def self.init_puppet_lint(root_dir, lint_options = [])
107
+ linter_options = nil
108
+ if root_dir.nil?
109
+ linter_options = PuppetLint::OptParser.build
110
+ else
111
+ begin
112
+ $PuppetParserMutex.synchronize do # rubocop:disable Style/GlobalVars
113
+ Dir.chdir(root_dir.to_s) { linter_options = PuppetLint::OptParser.build }
114
+ end
115
+ rescue OptionParser::InvalidOption => e
116
+ PuppetLanguageServer.log_message(:error, "(#{name}) Error reading Puppet Lint configuration. Using default: #{e}")
117
+ linter_options = PuppetLint::OptParser.build
118
+ end
119
+ end
120
+ # Reset the fix flag
121
+ PuppetLint.configuration.fix = false
122
+ linter_options.parse!(lint_options)
123
+ end
124
+ private_class_method :init_puppet_lint
125
+ end
126
+ end
127
+ end