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,212 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PuppetLanguageServer
4
+ module PuppetParserHelper
5
+ def self.remove_chars_starting_at(content, line_offsets, line_num, char_num, num_chars_to_remove)
6
+ line_offset = line_offsets[line_num]
7
+ raise if line_offset.nil?
8
+
9
+ # Remove the offending character
10
+ content.slice(0, line_offset + char_num - num_chars_to_remove) + content.slice(line_offset + char_num, content.length - num_chars_to_remove)
11
+ end
12
+
13
+ def self.remove_char_at(content, line_offsets, line_num, char_num)
14
+ remove_chars_starting_at(content, line_offsets, line_num, char_num, 1)
15
+ end
16
+
17
+ def self.get_char_at(content, line_offsets, line_num, char_num)
18
+ line_offset = line_offsets[line_num]
19
+ raise if line_offset.nil?
20
+
21
+ absolute_offset = line_offset + (char_num - 1)
22
+
23
+ content[absolute_offset]
24
+ end
25
+
26
+ def self.insert_text_at(content, line_offsets, line_num, char_num, text)
27
+ # Insert text after where the cursor is
28
+ # This helps due to syntax errors like `$facts[]` or `ensure =>`
29
+ line_offset = line_offsets[line_num]
30
+ raise if line_offset.nil?
31
+
32
+ # Insert the text
33
+ content.slice(0, line_offset + char_num) + text + content.slice(line_offset + char_num, content.length - 1)
34
+ end
35
+
36
+ def self.line_offsets(content)
37
+ # Calculate all of the offsets of \n in the file
38
+ line_offsets = [0]
39
+ line_offset = -1
40
+ loop do
41
+ line_offset = content.index("\n", line_offset + 1)
42
+ break if line_offset.nil?
43
+
44
+ line_offsets << (line_offset + 1)
45
+ end
46
+ line_offsets
47
+ end
48
+
49
+ def self.get_line_at(content, line_offsets, line_num)
50
+ # Get the text of the designated line
51
+ start_index = line_offsets[line_num]
52
+ if line_offsets[line_num + 1].nil?
53
+ content.slice(start_index, content.length - start_index)
54
+ else
55
+ content.slice(start_index, line_offsets[line_num + 1] - start_index - 1)
56
+ end
57
+ end
58
+
59
+ def self.object_under_cursor(content, line_num, char_num, options)
60
+ options = {
61
+ multiple_attempts: false,
62
+ disallowed_classes: [],
63
+ tasks_mode: false,
64
+ remove_trigger_char: true
65
+ }.merge(options)
66
+
67
+ # Use Puppet to generate the AST
68
+ parser = Puppet::Pops::Parser::Parser.new
69
+
70
+ # Calculating the line offsets can be expensive and is only required
71
+ # if we're doing mulitple passes of parsing
72
+ line_offsets = line_offsets(content) if options[:multiple_attempts]
73
+
74
+ result = nil
75
+ move_offset = 0
76
+ %i[noop remove_word try_quotes try_quotes_and_comma remove_char].each do |method|
77
+ new_content = nil
78
+ case method
79
+ when :noop
80
+ new_content = content
81
+ when :remove_char
82
+ next if line_num.zero? && char_num.zero?
83
+
84
+ new_content = remove_char_at(content, line_offsets, line_num, char_num)
85
+ move_offset = -1
86
+ when :remove_word
87
+ next if line_num.zero? && char_num.zero?
88
+
89
+ next_char = get_char_at(content, line_offsets, line_num, char_num)
90
+
91
+ while /[[:word:]]/ =~ next_char
92
+ move_offset -= 1
93
+ next_char = get_char_at(content, line_offsets, line_num, char_num + move_offset)
94
+
95
+ break if char_num + move_offset < 0
96
+ end
97
+
98
+ new_content = remove_chars_starting_at(content, line_offsets, line_num, char_num, -move_offset)
99
+ when :try_quotes
100
+ # Perhaps try inserting double quotes. Useful in empty arrays or during variable assignment
101
+ # Grab the line up to the cursor character + 1
102
+ line = get_line_at(content, line_offsets, line_num).slice!(0, char_num + 1)
103
+ if line.strip.end_with?('=') || line.end_with?('[]') # rubocop:disable Style/IfUnlessModifier Nicer to read like this
104
+ new_content = insert_text_at(content, line_offsets, line_num, char_num, "''")
105
+ end
106
+ when :try_quotes_and_comma
107
+ # Perhaps try inserting double quotes with a comma. Useful resource properties and parameter assignments
108
+ # Grab the line up to the cursor character + 1
109
+ line = get_line_at(content, line_offsets, line_num).slice!(0, char_num + 1)
110
+ if line.strip.end_with?('=>') # rubocop:disable Style/IfUnlessModifier Nicer to read like this
111
+ new_content = insert_text_at(content, line_offsets, line_num, char_num, "'',")
112
+ end
113
+ else
114
+ raise("Unknown parsing method #{method}")
115
+ end
116
+ # if we have no content to parse, try the next method.
117
+ next if new_content.nil?
118
+
119
+ begin
120
+ result = parser.singleton_parse_string(new_content, options[:tasks_mode], '')
121
+ break
122
+ rescue Puppet::ParseErrorWithIssue
123
+ next if options[:multiple_attempts]
124
+
125
+ raise
126
+ end
127
+ end
128
+ raise('Unable to parse content') if result.nil?
129
+
130
+ # Convert line and char nums (base 0) to an absolute offset
131
+ # result.line_offsets contains an array of the offsets on a per line basis e.g.
132
+ # [0, 14, 34, 36] means line number 2 starts at absolute offset 34
133
+ # Once we know the line offset, we can simply add on the char_num to get the absolute offset
134
+ # If during paring we modified the source we may need to change the cursor location
135
+ line_offset = if result.respond_to?(:line_offsets)
136
+ result.line_offsets[line_num]
137
+ else
138
+ result['locator'].line_index[line_num]
139
+ end
140
+ abs_offset = line_offset + char_num + move_offset
141
+ # Typically we're completing after something was typed, so go back one char by default
142
+ abs_offset -= 1 if options[:remove_trigger_char]
143
+
144
+ # Enumerate the AST looking for items that span the line/char we want.
145
+ # Once we have all valid items, sort them by the smallest span. Typically the smallest span
146
+ # is the most specific object in the AST
147
+ #
148
+ # TODO: Should probably walk the AST and only look for the deepest child, but integer sorting
149
+ # is so much easier and faster.
150
+ model_path_locator_struct = Struct.new(:model, :path, :locator)
151
+
152
+ valid_models = []
153
+ if result.model.respond_to? :eAllContents
154
+ valid_models = result.model.eAllContents.select do |item|
155
+ check_for_valid_item(item, abs_offset, options[:disallowed_classes])
156
+ end
157
+
158
+ valid_models.sort! { |a, b| a.length - b.length }
159
+ else
160
+ path = []
161
+ result.model._pcore_all_contents(path) do |item|
162
+ if check_for_valid_item(item, abs_offset, options[:disallowed_classes]) # rubocop:disable Style/IfUnlessModifier Nicer to read like this
163
+ valid_models.push(model_path_locator_struct.new(item, path.dup))
164
+ end
165
+ end
166
+
167
+ valid_models.sort! { |a, b| a[:model].length - b[:model].length }
168
+ end
169
+ # nil means the root of the document
170
+ return nil if valid_models.empty?
171
+
172
+ response = valid_models[0]
173
+
174
+ if response.respond_to? :eAllContents # rubocop:disable Style/IfUnlessModifier Nicer to read like this
175
+ response = model_path_locator_struct.new(response, construct_path(response))
176
+ end
177
+
178
+ response.locator = result.model.locator
179
+ response
180
+ end
181
+
182
+ def self.construct_path(item)
183
+ path = []
184
+ item = item.eContainer
185
+ while item.class != Puppet::Pops::Model::Program
186
+ path.unshift item
187
+ item = item.eContainer
188
+ end
189
+
190
+ path
191
+ end
192
+
193
+ def self.check_for_valid_item(item, abs_offset, disallowed_classes)
194
+ item.respond_to?(:offset) && !item.offset.nil? && !item.length.nil? && abs_offset >= item.offset && abs_offset <= item.offset + item.length && !disallowed_classes.include?(item.class)
195
+ end
196
+
197
+ # This method is only required during development or debugging. Visualising the AST tree can be difficult
198
+ # so this method just prints it to the console.
199
+ # def self.recurse_showast(item, abs_offset, disallowed_classes, depth = 0)
200
+ # output = " " * depth
201
+ # output += check_for_valid_item(item, abs_offset, disallowed_classes) ? 'X ' : ' '
202
+ # output += "#{item.class.to_s} (#{item.object_id})"
203
+ # if item.respond_to?(:offset)
204
+ # output += " (Off-#{item.offset}:#{item.offset + item.length} Pos-#{item.line}:#{item.pos} Len-#{item.length}) ~#{item.locator.extract_text(item.offset, item.length)}~"
205
+ # end
206
+ # puts output
207
+ # item._pcore_contents do |child|
208
+ # recurse_showast(child, abs_offset, disallowed_classes, depth + 1)
209
+ # end
210
+ # end
211
+ end
212
+ end
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lsp/lsp'
4
+
5
+ module PuppetLanguageServer
6
+ module Puppetfile
7
+ module ValidationProvider
8
+ def self.max_line_length
9
+ # TODO: ... need to figure out the actual line length
10
+ 1000
11
+ end
12
+
13
+ def self.validate(content, options = {})
14
+ options = {
15
+ max_problems: 100,
16
+ resolve_puppetfile: true,
17
+ module_path: [],
18
+ document_uri: '???'
19
+ }.merge(options)
20
+
21
+ result = []
22
+ # TODO: Need to implement max_problems
23
+ _problems = 0
24
+
25
+ require 'puppetfile-resolver'
26
+ require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
27
+ parser = PuppetfileResolver::Puppetfile::Parser::R10KEval
28
+
29
+ # Attempt to parse the file
30
+ puppetfile = nil
31
+ begin
32
+ puppetfile = parser.parse(content)
33
+ rescue PuppetfileResolver::Puppetfile::Parser::ParserError => e
34
+ result << LSP::Diagnostic.new(
35
+ 'severity' => LSP::DiagnosticSeverity::ERROR,
36
+ 'range' => document_location_to_lsp_range(e.location),
37
+ 'source' => 'Puppet',
38
+ 'message' => e.to_s
39
+ )
40
+ puppetfile = nil
41
+ end
42
+ return result if puppetfile.nil?
43
+
44
+ puppetfile.validation_errors.each do |validation_error|
45
+ related_information = nil
46
+
47
+ if validation_error.is_a?(PuppetfileResolver::Puppetfile::DocumentDuplicateModuleError)
48
+ related_information = validation_error.duplicates.map do |dup_mod|
49
+ {
50
+ 'location' => {
51
+ 'uri' => options[:document_uri],
52
+ 'range' => document_location_to_lsp_range(dup_mod.location)
53
+ },
54
+ 'message' => validation_error.message
55
+ }
56
+ end
57
+ end
58
+
59
+ result << LSP::Diagnostic.new(
60
+ 'severity' => LSP::DiagnosticSeverity::ERROR,
61
+ 'range' => document_location_to_lsp_range(validation_error.puppet_module.location),
62
+ 'source' => 'Puppet',
63
+ 'message' => validation_error.message,
64
+ 'relatedInformation' => related_information
65
+ )
66
+ end
67
+
68
+ return result unless options[:resolve_puppetfile] && puppetfile.valid?
69
+
70
+ result + validate_resolution(puppetfile, options[:document_uri], resolver_cache, options[:module_path], options[:puppet_version])
71
+ end
72
+
73
+ def self.validate_resolution(puppetfile_document, document_uri, cache, module_path, puppet_version)
74
+ ui = nil
75
+ resolver = PuppetfileResolver::Resolver.new(puppetfile_document, puppet_version)
76
+ opts = { cache: cache, ui: ui, module_paths: module_path, allow_missing_modules: true }
77
+ begin
78
+ resolution = resolver.resolve(opts)
79
+ rescue PuppetfileResolver::Puppetfile::DocumentVersionConflictError,
80
+ PuppetfileResolver::Puppetfile::DocumentCircularDependencyError => e
81
+ return [document_error_to_diagnostic(document_uri, e)]
82
+ rescue PuppetfileResolver::Puppetfile::DocumentResolveError => e
83
+ return [LSP::Diagnostic.new(
84
+ 'severity' => LSP::DiagnosticSeverity::ERROR,
85
+ 'range' => LSP.create_range(0, 0, 0, max_line_length),
86
+ 'source' => 'Puppet',
87
+ 'message' => e.message
88
+ )]
89
+ end
90
+
91
+ resolution.validation_errors.map do |error|
92
+ severity = case error
93
+ when PuppetfileResolver::Puppetfile::DocumentLatestVersionError
94
+ LSP::DiagnosticSeverity::INFORMATION
95
+ when PuppetfileResolver::Puppetfile::DocumentMissingModuleError
96
+ LSP::DiagnosticSeverity::HINT
97
+ else
98
+ LSP::DiagnosticSeverity::ERROR
99
+ end
100
+ LSP::Diagnostic.new(
101
+ 'severity' => severity,
102
+ 'range' => document_location_to_lsp_range(error.puppet_module.location),
103
+ 'source' => 'Puppet',
104
+ 'message' => error.message
105
+ )
106
+ end
107
+ end
108
+
109
+ def self.find_dependencies(content)
110
+ require 'puppetfile-resolver'
111
+ require 'puppetfile-resolver/puppetfile/parser/r10k_eval'
112
+ parser = PuppetfileResolver::Puppetfile::Parser::R10KEval
113
+
114
+ result = []
115
+ puppetfile = parser.parse(content)
116
+
117
+ return result if puppetfile.nil?
118
+
119
+ raise 'Puppetfile is not valid' unless puppetfile.valid?
120
+
121
+ puppetfile.modules.select { |d| d.module_type == :forge }.each do |dep|
122
+ result << {
123
+ name: dep.name,
124
+ title: dep.title,
125
+ owner: dep.owner,
126
+ version: dep.version.to_s,
127
+ start_line: dep.location.start_line,
128
+ end_line: dep.location.end_line
129
+ }
130
+ end
131
+
132
+ result
133
+ end
134
+
135
+ def self.resolver_cache
136
+ return @resolver_cache unless @resolver_cache.nil?
137
+
138
+ require 'puppetfile-resolver/cache/base'
139
+ # TODO: The cache should probably not cache local module information though
140
+ # Share a cache between resolution calls to speed-up lookups
141
+ @resolver_cache = PuppetfileResolver::Cache::Base.new(nil)
142
+ end
143
+ private_class_method :resolver_cache
144
+
145
+ def self.document_error_to_diagnostic(document_uri, error)
146
+ if error.puppetfile_modules.count.zero?
147
+ return LSP::Diagnostic.new(
148
+ 'severity' => LSP::DiagnosticSeverity::ERROR,
149
+ 'range' => LSP.create_range(0, 0, 0, max_line_length),
150
+ 'source' => 'Puppet',
151
+ 'message' => error.message
152
+ )
153
+ end
154
+
155
+ related_information = error.puppetfile_modules.slice(1..-1).map do |dup_mod|
156
+ {
157
+ 'location' => {
158
+ 'uri' => document_uri,
159
+ 'range' => document_location_to_lsp_range(dup_mod.location)
160
+ },
161
+ 'message' => "Module definition for #{dup_mod.name}"
162
+ }
163
+ end
164
+
165
+ LSP::Diagnostic.new(
166
+ 'severity' => LSP::DiagnosticSeverity::ERROR,
167
+ 'range' => document_location_to_lsp_range(error.puppetfile_modules[0].location),
168
+ 'source' => 'Puppet',
169
+ 'message' => error.message,
170
+ 'relatedInformation' => related_information.empty? ? nil : related_information
171
+ )
172
+ end
173
+ private_class_method :document_error_to_diagnostic
174
+
175
+ def self.document_location_to_lsp_range(location)
176
+ start_line = location.start_line
177
+ start_char = location.start_char.nil? ? 0 : location.start_char
178
+ end_line = location.end_line
179
+ end_char = location.end_char.nil? ? max_line_length : location.end_char
180
+ LSP.create_range(start_line, start_char, end_line, end_char)
181
+ end
182
+ private_class_method :document_location_to_lsp_range
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lsp/lsp'
4
+
5
+ module PuppetLanguageServer
6
+ module ServerCapabilites
7
+ def self.folding_provider_supported?
8
+ @folding_provider ||= PuppetLanguageServer::Manifest::FoldingProvider.supported?
9
+ end
10
+
11
+ def self.capabilities(options = {})
12
+ # https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md#initialize-request
13
+
14
+ value = {
15
+ 'textDocumentSync' => LSP::TextDocumentSyncKind::FULL,
16
+ 'hoverProvider' => true,
17
+ 'completionProvider' => {
18
+ 'resolveProvider' => true,
19
+ 'triggerCharacters' => ['>', '$', '[', '=']
20
+ },
21
+ 'definitionProvider' => true,
22
+ 'documentSymbolProvider' => true,
23
+ 'workspaceSymbolProvider' => true,
24
+ 'signatureHelpProvider' => {
25
+ 'triggerCharacters' => ['(', ',']
26
+ }
27
+ }
28
+ value['documentOnTypeFormattingProvider'] = document_on_type_formatting_options if options[:documentOnTypeFormattingProvider]
29
+ value['foldingRangeProvider'] = folding_range_provider_options if options[:foldingRangeProvider]
30
+ value
31
+ end
32
+
33
+ def self.document_on_type_formatting_options
34
+ {
35
+ 'firstTriggerCharacter' => '>'
36
+ }
37
+ end
38
+
39
+ def self.folding_range_provider_options
40
+ true
41
+ end
42
+
43
+ def self.no_capabilities
44
+ # Any empty hash denotes no capabilities at all
45
+ {}
46
+ end
47
+ end
48
+ end