lib/ruby_lsp/ruby-lsp-cell/code_lens.rb
# typed: false # frozen_string_literal: true module RubyLsp module Cell class CodeLens include ::RubyLsp::Requests::Support::Common #: ( #| RubyLsp::ResponseBuilders::CollectionResponseBuilder[untyped] response_builder, #| URI::Generic uri, #| Prism::Dispatcher dispatcher, #| RubyLsp::GlobalState global_state, #| enabled: bool, #| default_view_filename: String #| ) -> void def initialize(response_builder, uri, dispatcher, global_state, enabled:, default_view_filename:) return unless enabled @response_builder = response_builder @global_state = global_state @path = uri.to_standardized_path #: String @class_name = "" @pattern = "_cell" @default_view_filename = default_view_filename @in_cell_class = false @nesting = [] #: Array[String] dispatcher.register( self, :on_module_node_enter, :on_module_node_leave, :on_class_node_enter, :on_class_node_leave, :on_def_node_enter, ) end #: (Prism::ModuleNode node) -> void def on_module_node_enter(node) @nesting.push(node.constant_path.slice) end #: (Prism::ModuleNode node) -> void def on_module_node_leave(node) @nesting.pop end #: (Prism::ClassNode node) -> void def on_class_node_enter(node) @nesting.push(node.constant_path.slice) class_name = @nesting.join("::") return unless class_name.end_with?("Cell") return unless @global_state.index.linearized_ancestors_of(class_name).include?("Cell::ViewModel") @in_cell_class = true add_default_goto_code_lens(node) end #: (Prism::ClassNode node) -> void def on_class_node_leave(node) @nesting.pop @in_cell_class = false end #: (Prism::DefNode node) -> void def on_def_node_enter(node) return unless @in_cell_class return unless contains_render_call?(node.body) add_function_goto_code_lens(node, node.name.to_s) end private #: (Prism::Node node) -> void def add_default_goto_code_lens(node) erb_filename = remove_last_pattern_in_string @default_view_filename, ".erb" uri = compute_erb_view_path @default_view_filename create_go_to_file_code_lens(node, erb_filename, uri) end #: (Prism::Node? node) -> bool def contains_render_call?(node) return false if node.nil? if node.is_a?(Prism::CallNode) return true if node.receiver.nil? && node.name == :render end node.child_nodes.any? { |child| contains_render_call?(child) } end #: (Prism::Node node, String name) -> void def add_function_goto_code_lens(node, name) uri = compute_erb_view_path("#{name}.erb") create_go_to_file_code_lens(node, name, uri) end #: (String string, String pattern) -> String def remove_last_pattern_in_string(string, pattern) string.sub(/#{pattern}$/, "") end #: (String name) -> String def compute_erb_view_path(name) escaped_pattern = Regexp.escape(@pattern) base_path = @path.sub(/#{escaped_pattern}\.rb$/, "") folder = File.basename(base_path) path = File.join(File.dirname(base_path), folder, name) uri = URI::File.from_path(path: path).to_s uri end #: (Prism::Node node, String name, String uri) -> void def create_go_to_file_code_lens(node, name, uri) @response_builder << create_code_lens( node, title: "Go to #{name}", command_name: "rubyLsp.openFile", arguments: [[uri]], data: { type: "file" }, ) end end end end