Class: RubyLsp::Requests::CodeLens

Inherits:
Listener
  • Object
show all
Extended by:
T::Generic, T::Sig
Defined in:
lib/ruby_lsp/requests/code_lens.rb

Overview

![Code lens demo](../../code_lens.gif)

The [code lens](microsoft.github.io/language-server-protocol/specification#textDocument_codeLens) request informs the editor of runnable commands such as tests

# Example

“‘ruby # Run class Test < Minitest::Test end “`

Constant Summary collapse

ResponseType =
type_member { { fixed: T::Array[Interface::CodeLens] } }
BASE_COMMAND =
T.let((File.exist?("Gemfile.lock") ? "bundle exec ruby" : "ruby") + " -Itest ", String)
ACCESS_MODIFIERS =
T.let(["public", "private", "protected"], T::Array[String])
SUPPORTED_TEST_LIBRARIES =
T.let(["minitest", "test-unit"], T::Array[String])

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Listener

#merge_external_listeners_responses!

Methods included from Support::Common

#create_code_lens, #full_constant_name, #range_from_syntax_tree_node, #visible?

Constructor Details

#initialize(uri, emitter, message_queue, test_library) ⇒ CodeLens

Returns a new instance of CodeLens.



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
# File 'lib/ruby_lsp/requests/code_lens.rb', line 35

def initialize(uri, emitter, message_queue, test_library)
  super(emitter, message_queue)

  @uri = T.let(uri, URI::Generic)
  @external_listeners.concat(
    Extension.extensions.filter_map { |ext| ext.create_code_lens_listener(uri, emitter, message_queue) },
  )
  @test_library = T.let(test_library, String)
  @response = T.let([], ResponseType)
  @path = T.let(uri.to_standardized_path, T.nilable(String))
  # visibility_stack is a stack of [current_visibility, previous_visibility]
  @visibility_stack = T.let([["public", "public"]], T::Array[T::Array[T.nilable(String)]])
  @class_stack = T.let([], T::Array[String])

  emitter.register(
    self,
    :on_class,
    :after_class,
    :on_def,
    :on_command,
    :after_command,
    :on_call,
    :after_call,
    :on_vcall,
  )
end

Instance Attribute Details

#responseObject (readonly)

Returns the value of attribute response.



32
33
34
# File 'lib/ruby_lsp/requests/code_lens.rb', line 32

def response
  @response
end

Instance Method Details

#after_call(node) ⇒ Object



137
138
139
140
# File 'lib/ruby_lsp/requests/code_lens.rb', line 137

def after_call(node)
  _, prev_visibility = @visibility_stack.pop
  @visibility_stack.push([prev_visibility, prev_visibility])
end

#after_class(node) ⇒ Object



79
80
81
82
# File 'lib/ruby_lsp/requests/code_lens.rb', line 79

def after_class(node)
  @visibility_stack.pop
  @class_stack.pop
end

#after_command(node) ⇒ Object



118
119
120
121
# File 'lib/ruby_lsp/requests/code_lens.rb', line 118

def after_command(node)
  _, prev_visibility = @visibility_stack.pop
  @visibility_stack.push([prev_visibility, prev_visibility])
end

#merge_response!(other) ⇒ Object



153
154
155
156
# File 'lib/ruby_lsp/requests/code_lens.rb', line 153

def merge_response!(other)
  @response.concat(other.response)
  self
end

#on_call(node) ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/ruby_lsp/requests/code_lens.rb', line 124

def on_call(node)
  ident = node.message if node.message.is_a?(SyntaxTree::Ident)

  if ident
    ident_value = T.cast(ident, SyntaxTree::Ident).value
    if ACCESS_MODIFIERS.include?(ident_value)
      visibility, _ = @visibility_stack.pop
      @visibility_stack.push([ident_value, visibility])
    end
  end
end

#on_class(node) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ruby_lsp/requests/code_lens.rb', line 63

def on_class(node)
  @visibility_stack.push(["public", "public"])
  class_name = node.constant.constant.value
  @class_stack.push(class_name)

  if class_name.end_with?("Test")
    add_test_code_lens(
      node,
      name: class_name,
      command: generate_test_command(class_name: class_name),
      kind: :group,
    )
  end
end

#on_command(node) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/ruby_lsp/requests/code_lens.rb', line 104

def on_command(node)
  node_message = node.message.value
  if ACCESS_MODIFIERS.include?(node_message) && node.arguments.parts.any?
    visibility, _ = @visibility_stack.pop
    @visibility_stack.push([node_message, visibility])
  elsif @path&.include?("Gemfile") && node_message.include?("gem") && node.arguments.parts.any?
    remote = resolve_gem_remote(node)
    return unless remote

    add_open_gem_remote_code_lens(node, remote)
  end
end

#on_def(node) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/ruby_lsp/requests/code_lens.rb', line 85

def on_def(node)
  class_name = @class_stack.last
  return unless class_name&.end_with?("Test")

  visibility, _ = @visibility_stack.last
  if visibility == "public"
    method_name = node.name.value
    if method_name.start_with?("test_")
      add_test_code_lens(
        node,
        name: method_name,
        command: generate_test_command(method_name: method_name, class_name: class_name),
        kind: :example,
      )
    end
  end
end

#on_vcall(node) ⇒ Object



143
144
145
146
147
148
149
150
# File 'lib/ruby_lsp/requests/code_lens.rb', line 143

def on_vcall(node)
  vcall_value = node.value.value

  if ACCESS_MODIFIERS.include?(vcall_value)
    @visibility_stack.pop
    @visibility_stack.push([vcall_value, vcall_value])
  end
end