Module: PuppetLanguageServer::Manifest::SignatureProvider

Defined in:
lib/puppet-languageserver/manifest/signature_provider.rb

Class Method Summary collapse

Class Method Details

.signature_help(session_state, content, line_num, char_num, options = {}) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/puppet-languageserver/manifest/signature_provider.rb', line 6

def self.signature_help(session_state, content, line_num, char_num, options = {})
  options = {
    tasks_mode: false
  }.merge(options)

  result = PuppetLanguageServer::PuppetParserHelper.object_under_cursor(content, line_num, char_num,
                                                                        multiple_attempts: false,
                                                                        tasks_mode: options[:tasks_mode],
                                                                        remove_trigger_char: false)
  response = LSP::SignatureHelp.new.from_h!('signatures' => [], 'activeSignature' => nil, 'activeParameter' => nil)
  # We are in the root of the document so no signatures here.
  return response if result.nil?

  item    = result[:model]
  path    = result[:path]
  locator = result[:locator]

  function_ast_object = nil
  # Try and find the acutal function object within the AST
  if item.instance_of?(::Puppet::Pops::Model::CallNamedFunctionExpression)
    function_ast_object = item
  else
    # Try and find the function with the AST tree
    distance_up_ast = -1
    function_ast_object = path[distance_up_ast]
    while !function_ast_object.nil? && function_ast_object.class.to_s != 'Puppet::Pops::Model::CallNamedFunctionExpression'
      distance_up_ast -= 1
      function_ast_object = path[distance_up_ast]
    end
    raise "Unable to find suitable parent object for object of type #{item.class}" if function_ast_object.nil?
  end

  function_name = function_ast_object.functor_expr.value
  raise 'Could not determine the function name' if function_name.nil?

  # Convert line and char nums (base 0) to an absolute offset within the document
  #   result.line_offsets contains an array of the offsets on a per line basis e.g.
  #     [0, 14, 34, 36]  means line number 2 starts at absolute offset 34
  #   Once we know the line offset, we can simply add on the char_num to get the absolute offset
  line_offset = if function_ast_object.respond_to?(:locator)
                  function_ast_object.locator.line_index[line_num]
                else
                  locator.line_index[line_num]
                end

  abs_offset = line_offset + char_num
  # We need to use offsets here in case functions span lines
  param_number = param_number_from_ast(abs_offset, function_ast_object, locator)
  raise 'Cursor is not within the function expression' if param_number.nil?

  func_info = PuppetLanguageServer::PuppetHelper.function(session_state, function_name)
  raise "Function #{function_name} does not exist" if func_info.nil?

  func_info.signatures.each do |sig|
    lsp_sig = LSP::SignatureInformation.new.from_h!(
      'label' => sig.key,
      'documentation' => sig.doc,
      'parameters' => []
    )

    sig.parameters.each do |param|
      lsp_sig.parameters << LSP::ParameterInformation.new.from_h!(
        '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],
        'documentation' => param.doc
      )
    end

    response.signatures << lsp_sig
  end

  # Now figure out the first signature which could have the same or more than the number of arguments in the function call
  func_arg_count = function_ast_object.arguments.count
  signature_number = func_info.signatures.find_index { |sig| sig.parameters.count >= func_arg_count }

  # If we still don't know the signature number then assume it's the first one
  signature_number = 0 if signature_number.nil? && func_info.signatures.count > 0

  response.activeSignature = signature_number unless signature_number.nil?
  response.activeParameter = param_number

  response
end