Class: RubyLsp::Listeners::CodeLens

Inherits:
Object
  • Object
show all
Includes:
Requests::Support::Common
Defined in:
lib/ruby_lsp/listeners/code_lens.rb

Constant Summary collapse

BASE_COMMAND =
begin
  Bundler.with_original_env { Bundler.default_lockfile }
  "bundle exec ruby"
rescue Bundler::GemfileNotFound
  "ruby"
end
ACCESS_MODIFIERS =

: String

[:public, :private, :protected]
SUPPORTED_TEST_LIBRARIES =

: Array

["minitest", "test-unit"]
DYNAMIC_REFERENCE_MARKER =

: String

"<dynamic_reference>"

Instance Method Summary collapse

Methods included from Requests::Support::Common

#categorized_markdown_from_index_entries, #constant_name, #create_code_lens, #each_constant_path_part, #kind_for_entry, #markdown_from_index_entries, #namespace_constant_name, #not_in_dependencies?, #range_from_location, #range_from_node, #self_receiver?

Constructor Details

#initialize(response_builder, global_state, uri, dispatcher) ⇒ CodeLens

: (ResponseBuilders::CollectionResponseBuilder response_builder, GlobalState global_state, URI::Generic uri, Prism::Dispatcher dispatcher) -> void



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

def initialize(response_builder, global_state, uri, dispatcher)
  @response_builder = response_builder
  @global_state = global_state
  @uri = uri #: URI::Generic
  @path = uri.to_standardized_path #: String?
  # visibility_stack is a stack of [current_visibility, previous_visibility]
  @visibility_stack = [[:public, :public]] #: Array[Array[Symbol?]]
  @group_stack = [] #: Array[String]
  @group_id = 1 #: Integer
  @group_id_stack = [] #: Array[Integer]
  # We want to avoid adding code lenses for nested definitions
  @def_depth = 0 #: Integer
  @spec_id = 0 #: Integer

  dispatcher.register(
    self,
    :on_class_node_enter,
    :on_class_node_leave,
    :on_module_node_enter,
    :on_module_node_leave,
    :on_def_node_enter,
    :on_def_node_leave,
    :on_call_node_enter,
    :on_call_node_leave,
  )
end

Instance Method Details

#on_call_node_enter(node) ⇒ Object

: (Prism::CallNode node) -> void



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 124

def on_call_node_enter(node)
  name = node.name
  arguments = node.arguments

  # If we found `private` by itself or `private def foo`
  if ACCESS_MODIFIERS.include?(name)
    if arguments.nil?
      @visibility_stack.pop
      @visibility_stack.push([name, name])
    elsif arguments.arguments.first.is_a?(Prism::DefNode)
      visibility, _ = @visibility_stack.pop
      @visibility_stack.push([name, visibility])
    end

    return
  end

  case name
  when :describe
    add_spec_code_lens(node, kind: :group)
    @group_id_stack.push(@group_id)
    @group_id += 1
  when :it, :specify # `specify` is an alias for `it`
    add_spec_code_lens(node, kind: :example)
  end
end

#on_call_node_leave(node) ⇒ Object

: (Prism::CallNode node) -> void



152
153
154
155
156
157
158
159
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 152

def on_call_node_leave(node)
  _, prev_visibility = @visibility_stack.pop
  @visibility_stack.push([prev_visibility, prev_visibility])
  if node.name == :describe
    @group_id_stack.pop
    @group_stack.pop
  end
end

#on_class_node_enter(node) ⇒ Object

: (Prism::ClassNode node) -> void



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 50

def on_class_node_enter(node)
  @visibility_stack.push([:public, :public])
  class_name = node.constant_path.slice
  @group_stack.push(class_name)

  if @path && class_name.end_with?("Test")
    add_test_code_lens(
      node,
      name: class_name,
      command: generate_test_command(group_stack: @group_stack),
      kind: :group,
      id: generate_fully_qualified_id(group_stack: @group_stack),
    )

    @group_id_stack.push(@group_id)
    @group_id += 1
  end
end

#on_class_node_leave(node) ⇒ Object

: (Prism::ClassNode node) -> void



70
71
72
73
74
75
76
77
78
79
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 70

def on_class_node_leave(node)
  @visibility_stack.pop
  @group_stack.pop

  class_name = node.constant_path.slice

  if @path && class_name.end_with?("Test")
    @group_id_stack.pop
  end
end

#on_def_node_enter(node) ⇒ Object

: (Prism::DefNode node) -> void



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

def on_def_node_enter(node)
  @def_depth += 1
  return if @def_depth > 1

  class_name = @group_stack.last
  return unless class_name&.end_with?("Test")

  visibility, _ = @visibility_stack.last
  if visibility == :public
    method_name = node.name.to_s
    if @path && method_name.start_with?("test_")
      add_test_code_lens(
        node,
        name: method_name,
        command: generate_test_command(method_name: method_name, group_stack: @group_stack),
        kind: :example,
        id: generate_fully_qualified_id(group_stack: @group_stack, method_name: method_name),
      )
    end
  end
end

#on_def_node_leave(node) ⇒ Object

: (Prism::DefNode node) -> void



105
106
107
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 105

def on_def_node_leave(node)
  @def_depth -= 1
end

#on_module_node_enter(node) ⇒ Object

: (Prism::ModuleNode node) -> void



110
111
112
113
114
115
116
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 110

def on_module_node_enter(node)
  if (path = namespace_constant_name(node))
    @group_stack.push(path)
  else
    @group_stack.push(DYNAMIC_REFERENCE_MARKER)
  end
end

#on_module_node_leave(node) ⇒ Object

: (Prism::ModuleNode node) -> void



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

def on_module_node_leave(node)
  @group_stack.pop
end