diff --git a/lib/ruby_lsp/executor.rb b/lib/ruby_lsp/executor.rb index 2bd12b9f4..04277d4d5 100644 --- a/lib/ruby_lsp/executor.rb +++ b/lib/ruby_lsp/executor.rb @@ -143,7 +143,11 @@ def run(request) nil end when "textDocument/documentHighlight" - document_highlight(uri, request.dig(:params, :position)) + dispatcher = Prism::Dispatcher.new + document = @store.get(uri) + request = Requests::DocumentHighlight.new(document, request.dig(:params, :position), dispatcher) + dispatcher.dispatch(document.tree) + request.response when "textDocument/onTypeFormatting" on_type_formatting(uri, request.dig(:params, :position), request.dig(:params, :ch)) when "textDocument/hover" @@ -348,22 +352,6 @@ def on_type_formatting(uri, position, character) Requests::OnTypeFormatting.new(@store.get(uri), position, character).run end - sig do - params( - uri: URI::Generic, - position: T::Hash[Symbol, T.untyped], - ).returns(T.nilable(T::Array[Interface::DocumentHighlight])) - end - def document_highlight(uri, position) - document = @store.get(uri) - - target, parent = document.locate_node(position) - dispatcher = Prism::Dispatcher.new - listener = Requests::DocumentHighlight.new(target, parent, dispatcher) - dispatcher.visit(document.tree) - listener.response - end - sig do params( uri: URI::Generic, diff --git a/lib/ruby_lsp/listeners/document_highlight.rb b/lib/ruby_lsp/listeners/document_highlight.rb new file mode 100644 index 000000000..91bf723af --- /dev/null +++ b/lib/ruby_lsp/listeners/document_highlight.rb @@ -0,0 +1,556 @@ +# typed: strict +# frozen_string_literal: true + +module RubyLsp + module Listeners + class DocumentHighlight < Listener + extend T::Sig + + ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } } + + GLOBAL_VARIABLE_NODES = T.let( + [ + Prism::GlobalVariableAndWriteNode, + Prism::GlobalVariableOperatorWriteNode, + Prism::GlobalVariableOrWriteNode, + Prism::GlobalVariableReadNode, + Prism::GlobalVariableTargetNode, + Prism::GlobalVariableWriteNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + INSTANCE_VARIABLE_NODES = T.let( + [ + Prism::InstanceVariableAndWriteNode, + Prism::InstanceVariableOperatorWriteNode, + Prism::InstanceVariableOrWriteNode, + Prism::InstanceVariableReadNode, + Prism::InstanceVariableTargetNode, + Prism::InstanceVariableWriteNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + CONSTANT_NODES = T.let( + [ + Prism::ConstantAndWriteNode, + Prism::ConstantOperatorWriteNode, + Prism::ConstantOrWriteNode, + Prism::ConstantReadNode, + Prism::ConstantTargetNode, + Prism::ConstantWriteNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + CONSTANT_PATH_NODES = T.let( + [ + Prism::ConstantPathAndWriteNode, + Prism::ConstantPathNode, + Prism::ConstantPathOperatorWriteNode, + Prism::ConstantPathOrWriteNode, + Prism::ConstantPathTargetNode, + Prism::ConstantPathWriteNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + CLASS_VARIABLE_NODES = T.let( + [ + Prism::ClassVariableAndWriteNode, + Prism::ClassVariableOperatorWriteNode, + Prism::ClassVariableOrWriteNode, + Prism::ClassVariableReadNode, + Prism::ClassVariableTargetNode, + Prism::ClassVariableWriteNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + LOCAL_NODES = T.let( + [ + Prism::LocalVariableAndWriteNode, + Prism::LocalVariableOperatorWriteNode, + Prism::LocalVariableOrWriteNode, + Prism::LocalVariableReadNode, + Prism::LocalVariableTargetNode, + Prism::LocalVariableWriteNode, + Prism::BlockParameterNode, + Prism::RequiredParameterNode, + Prism::RequiredKeywordParameterNode, + Prism::OptionalKeywordParameterNode, + Prism::RestParameterNode, + Prism::OptionalParameterNode, + Prism::KeywordRestParameterNode, + ], + T::Array[T.class_of(Prism::Node)], + ) + + sig { override.returns(ResponseType) } + attr_reader :_response + + sig do + params( + target: T.nilable(Prism::Node), + parent: T.nilable(Prism::Node), + dispatcher: Prism::Dispatcher, + ).void + end + def initialize(target, parent, dispatcher) + super(dispatcher) + + @_response = T.let([], T::Array[Interface::DocumentHighlight]) + + return unless target && parent + + highlight_target = + case target + when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode, + Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode, + Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode, + Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode, + Prism::InstanceVariableWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, + Prism::ConstantOrWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathNode, + Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathTargetNode, + Prism::ConstantPathWriteNode, Prism::ConstantReadNode, Prism::ConstantTargetNode, Prism::ConstantWriteNode, + Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode, Prism::ClassVariableOrWriteNode, + Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode, + Prism::LocalVariableAndWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode, + Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode, + Prism::CallNode, Prism::BlockParameterNode, Prism::RequiredKeywordParameterNode, + Prism::RequiredKeywordParameterNode, Prism::KeywordRestParameterNode, Prism::OptionalParameterNode, + Prism::RequiredParameterNode, Prism::RestParameterNode + target + end + + @target = T.let(highlight_target, T.nilable(Prism::Node)) + @target_value = T.let(node_value(highlight_target), T.nilable(String)) + + if @target && @target_value + dispatcher.register( + self, + :on_call_node_enter, + :on_def_node_enter, + :on_global_variable_target_node_enter, + :on_instance_variable_target_node_enter, + :on_constant_path_target_node_enter, + :on_constant_target_node_enter, + :on_class_variable_target_node_enter, + :on_local_variable_target_node_enter, + :on_block_parameter_node_enter, + :on_required_parameter_node_enter, + :on_class_node_enter, + :on_module_node_enter, + :on_local_variable_read_node_enter, + :on_constant_path_node_enter, + :on_constant_read_node_enter, + :on_instance_variable_read_node_enter, + :on_class_variable_read_node_enter, + :on_global_variable_read_node_enter, + :on_constant_path_write_node_enter, + :on_constant_path_or_write_node_enter, + :on_constant_path_and_write_node_enter, + :on_constant_path_operator_write_node_enter, + :on_local_variable_write_node_enter, + :on_required_keyword_parameter_node_enter, + :on_optional_keyword_parameter_node_enter, + :on_rest_parameter_node_enter, + :on_optional_parameter_node_enter, + :on_keyword_rest_parameter_node_enter, + :on_local_variable_and_write_node_enter, + :on_local_variable_operator_write_node_enter, + :on_local_variable_or_write_node_enter, + :on_class_variable_write_node_enter, + :on_class_variable_or_write_node_enter, + :on_class_variable_operator_write_node_enter, + :on_class_variable_and_write_node_enter, + :on_constant_write_node_enter, + :on_constant_or_write_node_enter, + :on_constant_operator_write_node_enter, + :on_instance_variable_write_node_enter, + :on_constant_and_write_node_enter, + :on_instance_variable_or_write_node_enter, + :on_instance_variable_and_write_node_enter, + :on_instance_variable_operator_write_node_enter, + :on_global_variable_write_node_enter, + :on_global_variable_or_write_node_enter, + :on_global_variable_and_write_node_enter, + :on_global_variable_operator_write_node_enter, + ) + end + end + + sig { params(node: Prism::CallNode).void } + def on_call_node_enter(node) + return unless matches?(node, [Prism::CallNode, Prism::DefNode]) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::DefNode).void } + def on_def_node_enter(node) + return unless matches?(node, [Prism::CallNode, Prism::DefNode]) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::GlobalVariableTargetNode).void } + def on_global_variable_target_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::InstanceVariableTargetNode).void } + def on_instance_variable_target_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::ConstantPathTargetNode).void } + def on_constant_path_target_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::ConstantTargetNode).void } + def on_constant_target_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::ClassVariableTargetNode).void } + def on_class_variable_target_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::LocalVariableTargetNode).void } + def on_local_variable_target_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::BlockParameterNode).void } + def on_block_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::RequiredParameterNode).void } + def on_required_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) + end + + sig { params(node: Prism::ClassNode).void } + def on_class_node_enter(node) + return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ClassNode]) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location) + end + + sig { params(node: Prism::ModuleNode).void } + def on_module_node_enter(node) + return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ModuleNode]) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location) + end + + sig { params(node: Prism::LocalVariableReadNode).void } + def on_local_variable_read_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::ConstantPathNode).void } + def on_constant_path_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::ConstantReadNode).void } + def on_constant_read_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::InstanceVariableReadNode).void } + def on_instance_variable_read_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::ClassVariableReadNode).void } + def on_class_variable_read_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::GlobalVariableReadNode).void } + def on_global_variable_read_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::READ, node.location) + end + + sig { params(node: Prism::ConstantPathWriteNode).void } + def on_constant_path_write_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) + end + + sig { params(node: Prism::ConstantPathOrWriteNode).void } + def on_constant_path_or_write_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) + end + + sig { params(node: Prism::ConstantPathAndWriteNode).void } + def on_constant_path_and_write_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) + end + + sig { params(node: Prism::ConstantPathOperatorWriteNode).void } + def on_constant_path_operator_write_node_enter(node) + return unless matches?(node, CONSTANT_PATH_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) + end + + sig { params(node: Prism::LocalVariableWriteNode).void } + def on_local_variable_write_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::RequiredKeywordParameterNode).void } + def on_required_keyword_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::OptionalKeywordParameterNode).void } + def on_optional_keyword_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::RestParameterNode).void } + def on_rest_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + name_loc = node.name_loc + add_highlight(Constant::DocumentHighlightKind::WRITE, name_loc) if name_loc + end + + sig { params(node: Prism::OptionalParameterNode).void } + def on_optional_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::KeywordRestParameterNode).void } + def on_keyword_rest_parameter_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + name_loc = node.name_loc + add_highlight(Constant::DocumentHighlightKind::WRITE, name_loc) if name_loc + end + + sig { params(node: Prism::LocalVariableAndWriteNode).void } + def on_local_variable_and_write_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::LocalVariableOperatorWriteNode).void } + def on_local_variable_operator_write_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::LocalVariableOrWriteNode).void } + def on_local_variable_or_write_node_enter(node) + return unless matches?(node, LOCAL_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ClassVariableWriteNode).void } + def on_class_variable_write_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ClassVariableOrWriteNode).void } + def on_class_variable_or_write_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ClassVariableOperatorWriteNode).void } + def on_class_variable_operator_write_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ClassVariableAndWriteNode).void } + def on_class_variable_and_write_node_enter(node) + return unless matches?(node, CLASS_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ConstantWriteNode).void } + def on_constant_write_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ConstantOrWriteNode).void } + def on_constant_or_write_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ConstantOperatorWriteNode).void } + def on_constant_operator_write_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::InstanceVariableWriteNode).void } + def on_instance_variable_write_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::InstanceVariableOrWriteNode).void } + def on_instance_variable_or_write_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::InstanceVariableAndWriteNode).void } + def on_instance_variable_and_write_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::InstanceVariableOperatorWriteNode).void } + def on_instance_variable_operator_write_node_enter(node) + return unless matches?(node, INSTANCE_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::ConstantAndWriteNode).void } + def on_constant_and_write_node_enter(node) + return unless matches?(node, CONSTANT_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::GlobalVariableWriteNode).void } + def on_global_variable_write_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::GlobalVariableOrWriteNode).void } + def on_global_variable_or_write_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::GlobalVariableAndWriteNode).void } + def on_global_variable_and_write_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + sig { params(node: Prism::GlobalVariableOperatorWriteNode).void } + def on_global_variable_operator_write_node_enter(node) + return unless matches?(node, GLOBAL_VARIABLE_NODES) + + add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + end + + private + + sig { params(node: Prism::Node, classes: T::Array[T.class_of(Prism::Node)]).returns(T.nilable(T::Boolean)) } + def matches?(node, classes) + classes.any? { |klass| @target.is_a?(klass) } && @target_value == node_value(node) + end + + sig { params(kind: Integer, location: Prism::Location).void } + def add_highlight(kind, location) + @_response << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind) + end + + sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(String)) } + def node_value(node) + case node + when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::BlockArgumentNode, Prism::ConstantTargetNode, + Prism::ConstantPathWriteNode, Prism::ConstantPathTargetNode, Prism::ConstantPathOrWriteNode, + Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathAndWriteNode + node.slice + when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode, + Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode, + Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode, + Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode, + Prism::InstanceVariableWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, + Prism::ConstantOrWriteNode, Prism::ConstantWriteNode, Prism::ClassVariableAndWriteNode, + Prism::ClassVariableOperatorWriteNode, Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, + Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode, Prism::LocalVariableAndWriteNode, + Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode, Prism::LocalVariableReadNode, + Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode, Prism::DefNode, Prism::BlockParameterNode, + Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode, Prism::KeywordRestParameterNode, + Prism::OptionalParameterNode, Prism::RequiredParameterNode, Prism::RestParameterNode + + node.name.to_s + when Prism::CallNode + node.message + when Prism::ClassNode, Prism::ModuleNode + node.constant_path.slice + end + end + end + end +end diff --git a/lib/ruby_lsp/requests/document_highlight.rb b/lib/ruby_lsp/requests/document_highlight.rb index 880251108..9ee2c86b8 100644 --- a/lib/ruby_lsp/requests/document_highlight.rb +++ b/lib/ruby_lsp/requests/document_highlight.rb @@ -1,6 +1,8 @@ # typed: strict # frozen_string_literal: true +require_relative "../listeners/document_highlight" + module RubyLsp module Requests # ![Document highlight demo](../../document_highlight.gif) @@ -22,553 +24,27 @@ module Requests # FOO # should be highlighted as "read" # end # ``` - class DocumentHighlight < Listener + class DocumentHighlight < ListenerBasedRequest extend T::Sig ResponseType = type_member { { fixed: T::Array[Interface::DocumentHighlight] } } - GLOBAL_VARIABLE_NODES = T.let( - [ - Prism::GlobalVariableAndWriteNode, - Prism::GlobalVariableOperatorWriteNode, - Prism::GlobalVariableOrWriteNode, - Prism::GlobalVariableReadNode, - Prism::GlobalVariableTargetNode, - Prism::GlobalVariableWriteNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - INSTANCE_VARIABLE_NODES = T.let( - [ - Prism::InstanceVariableAndWriteNode, - Prism::InstanceVariableOperatorWriteNode, - Prism::InstanceVariableOrWriteNode, - Prism::InstanceVariableReadNode, - Prism::InstanceVariableTargetNode, - Prism::InstanceVariableWriteNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - CONSTANT_NODES = T.let( - [ - Prism::ConstantAndWriteNode, - Prism::ConstantOperatorWriteNode, - Prism::ConstantOrWriteNode, - Prism::ConstantReadNode, - Prism::ConstantTargetNode, - Prism::ConstantWriteNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - CONSTANT_PATH_NODES = T.let( - [ - Prism::ConstantPathAndWriteNode, - Prism::ConstantPathNode, - Prism::ConstantPathOperatorWriteNode, - Prism::ConstantPathOrWriteNode, - Prism::ConstantPathTargetNode, - Prism::ConstantPathWriteNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - CLASS_VARIABLE_NODES = T.let( - [ - Prism::ClassVariableAndWriteNode, - Prism::ClassVariableOperatorWriteNode, - Prism::ClassVariableOrWriteNode, - Prism::ClassVariableReadNode, - Prism::ClassVariableTargetNode, - Prism::ClassVariableWriteNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - LOCAL_NODES = T.let( - [ - Prism::LocalVariableAndWriteNode, - Prism::LocalVariableOperatorWriteNode, - Prism::LocalVariableOrWriteNode, - Prism::LocalVariableReadNode, - Prism::LocalVariableTargetNode, - Prism::LocalVariableWriteNode, - Prism::BlockParameterNode, - Prism::RequiredParameterNode, - Prism::RequiredKeywordParameterNode, - Prism::OptionalKeywordParameterNode, - Prism::RestParameterNode, - Prism::OptionalParameterNode, - Prism::KeywordRestParameterNode, - ], - T::Array[T.class_of(Prism::Node)], - ) - - sig { override.returns(ResponseType) } - attr_reader :_response - sig do params( - target: T.nilable(Prism::Node), - parent: T.nilable(Prism::Node), + document: Document, + position: T::Hash[Symbol, T.untyped], dispatcher: Prism::Dispatcher, ).void end - def initialize(target, parent, dispatcher) - super(dispatcher) - - @_response = T.let([], T::Array[Interface::DocumentHighlight]) - - return unless target && parent - - highlight_target = - case target - when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode, - Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode, - Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode, - Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode, - Prism::InstanceVariableWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, - Prism::ConstantOrWriteNode, Prism::ConstantPathAndWriteNode, Prism::ConstantPathNode, - Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathOrWriteNode, Prism::ConstantPathTargetNode, - Prism::ConstantPathWriteNode, Prism::ConstantReadNode, Prism::ConstantTargetNode, Prism::ConstantWriteNode, - Prism::ClassVariableAndWriteNode, Prism::ClassVariableOperatorWriteNode, Prism::ClassVariableOrWriteNode, - Prism::ClassVariableReadNode, Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode, - Prism::LocalVariableAndWriteNode, Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode, - Prism::LocalVariableReadNode, Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode, - Prism::CallNode, Prism::BlockParameterNode, Prism::RequiredKeywordParameterNode, - Prism::RequiredKeywordParameterNode, Prism::KeywordRestParameterNode, Prism::OptionalParameterNode, - Prism::RequiredParameterNode, Prism::RestParameterNode - target - end - - @target = T.let(highlight_target, T.nilable(Prism::Node)) - @target_value = T.let(node_value(highlight_target), T.nilable(String)) - - if @target && @target_value - dispatcher.register( - self, - :on_call_node_enter, - :on_def_node_enter, - :on_global_variable_target_node_enter, - :on_instance_variable_target_node_enter, - :on_constant_path_target_node_enter, - :on_constant_target_node_enter, - :on_class_variable_target_node_enter, - :on_local_variable_target_node_enter, - :on_block_parameter_node_enter, - :on_required_parameter_node_enter, - :on_class_node_enter, - :on_module_node_enter, - :on_local_variable_read_node_enter, - :on_constant_path_node_enter, - :on_constant_read_node_enter, - :on_instance_variable_read_node_enter, - :on_class_variable_read_node_enter, - :on_global_variable_read_node_enter, - :on_constant_path_write_node_enter, - :on_constant_path_or_write_node_enter, - :on_constant_path_and_write_node_enter, - :on_constant_path_operator_write_node_enter, - :on_local_variable_write_node_enter, - :on_required_keyword_parameter_node_enter, - :on_optional_keyword_parameter_node_enter, - :on_rest_parameter_node_enter, - :on_optional_parameter_node_enter, - :on_keyword_rest_parameter_node_enter, - :on_local_variable_and_write_node_enter, - :on_local_variable_operator_write_node_enter, - :on_local_variable_or_write_node_enter, - :on_class_variable_write_node_enter, - :on_class_variable_or_write_node_enter, - :on_class_variable_operator_write_node_enter, - :on_class_variable_and_write_node_enter, - :on_constant_write_node_enter, - :on_constant_or_write_node_enter, - :on_constant_operator_write_node_enter, - :on_instance_variable_write_node_enter, - :on_constant_and_write_node_enter, - :on_instance_variable_or_write_node_enter, - :on_instance_variable_and_write_node_enter, - :on_instance_variable_operator_write_node_enter, - :on_global_variable_write_node_enter, - :on_global_variable_or_write_node_enter, - :on_global_variable_and_write_node_enter, - :on_global_variable_operator_write_node_enter, - ) - end - end - - sig { params(node: Prism::CallNode).void } - def on_call_node_enter(node) - return unless matches?(node, [Prism::CallNode, Prism::DefNode]) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::DefNode).void } - def on_def_node_enter(node) - return unless matches?(node, [Prism::CallNode, Prism::DefNode]) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::GlobalVariableTargetNode).void } - def on_global_variable_target_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::InstanceVariableTargetNode).void } - def on_instance_variable_target_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::ConstantPathTargetNode).void } - def on_constant_path_target_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::ConstantTargetNode).void } - def on_constant_target_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::ClassVariableTargetNode).void } - def on_class_variable_target_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::LocalVariableTargetNode).void } - def on_local_variable_target_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::BlockParameterNode).void } - def on_block_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::RequiredParameterNode).void } - def on_required_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.location) - end - - sig { params(node: Prism::ClassNode).void } - def on_class_node_enter(node) - return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ClassNode]) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location) - end - - sig { params(node: Prism::ModuleNode).void } - def on_module_node_enter(node) - return unless matches?(node, CONSTANT_NODES + CONSTANT_PATH_NODES + [Prism::ModuleNode]) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.constant_path.location) - end - - sig { params(node: Prism::LocalVariableReadNode).void } - def on_local_variable_read_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::ConstantPathNode).void } - def on_constant_path_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::ConstantReadNode).void } - def on_constant_read_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::InstanceVariableReadNode).void } - def on_instance_variable_read_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::ClassVariableReadNode).void } - def on_class_variable_read_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::GlobalVariableReadNode).void } - def on_global_variable_read_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::READ, node.location) - end - - sig { params(node: Prism::ConstantPathWriteNode).void } - def on_constant_path_write_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) - end - - sig { params(node: Prism::ConstantPathOrWriteNode).void } - def on_constant_path_or_write_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) - end - - sig { params(node: Prism::ConstantPathAndWriteNode).void } - def on_constant_path_and_write_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) - end - - sig { params(node: Prism::ConstantPathOperatorWriteNode).void } - def on_constant_path_operator_write_node_enter(node) - return unless matches?(node, CONSTANT_PATH_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.target.location) - end - - sig { params(node: Prism::LocalVariableWriteNode).void } - def on_local_variable_write_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::RequiredKeywordParameterNode).void } - def on_required_keyword_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::OptionalKeywordParameterNode).void } - def on_optional_keyword_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::RestParameterNode).void } - def on_rest_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - name_loc = node.name_loc - add_highlight(Constant::DocumentHighlightKind::WRITE, name_loc) if name_loc - end - - sig { params(node: Prism::OptionalParameterNode).void } - def on_optional_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::KeywordRestParameterNode).void } - def on_keyword_rest_parameter_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - name_loc = node.name_loc - add_highlight(Constant::DocumentHighlightKind::WRITE, name_loc) if name_loc - end - - sig { params(node: Prism::LocalVariableAndWriteNode).void } - def on_local_variable_and_write_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::LocalVariableOperatorWriteNode).void } - def on_local_variable_operator_write_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::LocalVariableOrWriteNode).void } - def on_local_variable_or_write_node_enter(node) - return unless matches?(node, LOCAL_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ClassVariableWriteNode).void } - def on_class_variable_write_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ClassVariableOrWriteNode).void } - def on_class_variable_or_write_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ClassVariableOperatorWriteNode).void } - def on_class_variable_operator_write_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ClassVariableAndWriteNode).void } - def on_class_variable_and_write_node_enter(node) - return unless matches?(node, CLASS_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ConstantWriteNode).void } - def on_constant_write_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) + def initialize(document, position, dispatcher) + target, parent = document.locate_node(position) + listener = Listeners::DocumentHighlight.new(target, parent, dispatcher) + super([listener]) end - sig { params(node: Prism::ConstantOrWriteNode).void } - def on_constant_or_write_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ConstantOperatorWriteNode).void } - def on_constant_operator_write_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::InstanceVariableWriteNode).void } - def on_instance_variable_write_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::InstanceVariableOrWriteNode).void } - def on_instance_variable_or_write_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::InstanceVariableAndWriteNode).void } - def on_instance_variable_and_write_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::InstanceVariableOperatorWriteNode).void } - def on_instance_variable_operator_write_node_enter(node) - return unless matches?(node, INSTANCE_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::ConstantAndWriteNode).void } - def on_constant_and_write_node_enter(node) - return unless matches?(node, CONSTANT_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::GlobalVariableWriteNode).void } - def on_global_variable_write_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::GlobalVariableOrWriteNode).void } - def on_global_variable_or_write_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::GlobalVariableAndWriteNode).void } - def on_global_variable_and_write_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - sig { params(node: Prism::GlobalVariableOperatorWriteNode).void } - def on_global_variable_operator_write_node_enter(node) - return unless matches?(node, GLOBAL_VARIABLE_NODES) - - add_highlight(Constant::DocumentHighlightKind::WRITE, node.name_loc) - end - - private - - sig { params(node: Prism::Node, classes: T::Array[T.class_of(Prism::Node)]).returns(T.nilable(T::Boolean)) } - def matches?(node, classes) - classes.any? { |klass| @target.is_a?(klass) } && @target_value == node_value(node) - end - - sig { params(kind: Integer, location: Prism::Location).void } - def add_highlight(kind, location) - @_response << Interface::DocumentHighlight.new(range: range_from_location(location), kind: kind) - end - - sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(String)) } - def node_value(node) - case node - when Prism::ConstantReadNode, Prism::ConstantPathNode, Prism::BlockArgumentNode, Prism::ConstantTargetNode, - Prism::ConstantPathWriteNode, Prism::ConstantPathTargetNode, Prism::ConstantPathOrWriteNode, - Prism::ConstantPathOperatorWriteNode, Prism::ConstantPathAndWriteNode - node.slice - when Prism::GlobalVariableReadNode, Prism::GlobalVariableAndWriteNode, Prism::GlobalVariableOperatorWriteNode, - Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableTargetNode, Prism::GlobalVariableWriteNode, - Prism::InstanceVariableAndWriteNode, Prism::InstanceVariableOperatorWriteNode, - Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableReadNode, Prism::InstanceVariableTargetNode, - Prism::InstanceVariableWriteNode, Prism::ConstantAndWriteNode, Prism::ConstantOperatorWriteNode, - Prism::ConstantOrWriteNode, Prism::ConstantWriteNode, Prism::ClassVariableAndWriteNode, - Prism::ClassVariableOperatorWriteNode, Prism::ClassVariableOrWriteNode, Prism::ClassVariableReadNode, - Prism::ClassVariableTargetNode, Prism::ClassVariableWriteNode, Prism::LocalVariableAndWriteNode, - Prism::LocalVariableOperatorWriteNode, Prism::LocalVariableOrWriteNode, Prism::LocalVariableReadNode, - Prism::LocalVariableTargetNode, Prism::LocalVariableWriteNode, Prism::DefNode, Prism::BlockParameterNode, - Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode, Prism::KeywordRestParameterNode, - Prism::OptionalParameterNode, Prism::RequiredParameterNode, Prism::RestParameterNode - - node.name.to_s - when Prism::CallNode - node.message - when Prism::ClassNode, Prism::ModuleNode - node.constant_path.slice - end + sig { override.returns(ResponseType) } + def response + @listeners.flat_map(&:response) end end end diff --git a/test/requests/document_highlight_expectations_test.rb b/test/requests/document_highlight_expectations_test.rb index a31795d91..abf8f927c 100644 --- a/test/requests/document_highlight_expectations_test.rb +++ b/test/requests/document_highlight_expectations_test.rb @@ -11,11 +11,9 @@ def run_expectations(source) uri = URI("file://#{@_path}") params = @__params&.any? ? @__params : default_args document = RubyLsp::RubyDocument.new(source: source, version: 1, uri: uri) - target, parent = document.locate_node(params.first) dispatcher = Prism::Dispatcher.new - - listener = RubyLsp::Requests::DocumentHighlight.new(target, parent, dispatcher) + listener = RubyLsp::Requests::DocumentHighlight.new(document, params.first, dispatcher) dispatcher.dispatch(document.tree) listener.response end