Class: RubyLsp::Listeners::CodeLens

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

Constant Summary collapse

BASE_COMMAND =
T.let(
  begin
    Bundler.with_original_env { Bundler.default_lockfile }
    "bundle exec ruby"
  rescue Bundler::GemfileNotFound
    "ruby"
  end + " -Itest ",
  String,
)
ACCESS_MODIFIERS =
T.let([:public, :private, :protected], T::Array[Symbol])
SUPPORTED_TEST_LIBRARIES =
T.let(["minitest", "test-unit"], T::Array[String])
DESCRIBE_KEYWORD =
T.let(:describe, Symbol)
IT_KEYWORD =
T.let(:it, Symbol)
DYNAMIC_REFERENCE_MARKER =
T.let("<dynamic_reference>", String)

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?, #sorbet_level_true_or_higher?, #visible?

Constructor Details

#initialize(response_builder, global_state, uri, dispatcher) ⇒ 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
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 35

def initialize(response_builder, global_state, uri, dispatcher)
  @response_builder = response_builder
  @global_state = global_state
  @uri = T.let(uri, URI::Generic)
  @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(Symbol)]])
  @group_stack = T.let([], T::Array[String])
  @group_id = T.let(1, Integer)
  @group_id_stack = T.let([], T::Array[Integer])
  # We want to avoid adding code lenses for nested definitions
  @def_depth = T.let(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



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 134

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

  if [DESCRIBE_KEYWORD, IT_KEYWORD].include?(name)
    case name
    when DESCRIBE_KEYWORD
      add_spec_code_lens(node, kind: :group)
      @group_id_stack.push(@group_id)
      @group_id += 1
    when IT_KEYWORD
      add_spec_code_lens(node, kind: :example)
    end
  end
end

#on_call_node_leave(node) ⇒ Object



164
165
166
167
168
169
170
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 164

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

#on_class_node_enter(node) ⇒ Object



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

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,
    )

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

#on_class_node_leave(node) ⇒ Object



81
82
83
84
85
86
87
88
89
90
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 81

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



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 93

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,
      )
    end
  end
end

#on_def_node_leave(node) ⇒ Object



115
116
117
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 115

def on_def_node_leave(node)
  @def_depth -= 1
end

#on_module_node_enter(node) ⇒ Object



120
121
122
123
124
125
126
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 120

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



129
130
131
# File 'lib/ruby_lsp/listeners/code_lens.rb', line 129

def on_module_node_leave(node)
  @group_stack.pop
end