lib/steep/services/hover_content.rb in steep-0.44.1 vs lib/steep/services/hover_content.rb in steep-0.45.0

- old
+ new

@@ -3,10 +3,14 @@ class HoverContent TypeContent = Struct.new(:node, :type, :location, keyword_init: true) VariableContent = Struct.new(:node, :name, :type, :location, keyword_init: true) MethodCallContent = Struct.new(:node, :method_name, :type, :definition, :location, keyword_init: true) DefinitionContent = Struct.new(:node, :method_name, :method_type, :definition, :location, keyword_init: true) do + TypeAliasContent = Struct.new(:location, :decl, keyword_init: true) + ClassContent = Struct.new(:location, :decl, keyword_init: true) + InterfaceContent = Struct.new(:location, :decl, keyword_init: true) + def comment_string if comments = definition&.comments comments.map {|c| c.string.chomp }.uniq.join("\n----\n") end end @@ -48,102 +52,150 @@ rescue nil end def content_for(path:, line:, column:) - target = project.target_for_source_path(path) + target_for_code, targets_for_sigs = project.targets_for_path(path) - if target - file = service.source_files[path] - typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return + case + when target = target_for_code + Steep.logger.info "target #{target}" - node, *parents = typing.source.find_nodes(line: line, column: column) + hover_for_source(column, line, path, target) - if node - case node.type - when :lvar - var_name = node.children[0] - context = typing.context_at(line: line, column: column) - var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil) + when target = targets_for_sigs[0] + service = self.service.signature_services[target.name] - VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name) - when :lvasgn - var_name, rhs = node.children - context = typing.context_at(line: line, column: column) - type = context.lvar_env[var_name] || typing.type_of(node: rhs) + _buffer, decls = service.latest_env.buffers_decls.find do |buffer, _| + Pathname(buffer.name) == path + end - VariableContent.new(node: node, name: var_name, type: type, location: node.location.name) - when :send - receiver, method_name, *_ = node.children + return if decls.nil? + locator = RBS::Locator.new(decls: decls) + hd, tail = locator.find2(line: line, column: column) - result_node = if parents[0]&.type == :block - parents[0] - else - node - end + case type = tail[0] + when RBS::Types::Alias + alias_decl = service.latest_env.alias_decls[type.name]&.decl or raise - context = typing.context_at(line: line, column: column) + location = tail[0].location + TypeAliasContent.new( + location: location, + decl: alias_decl + ) + when RBS::Types::ClassInstance, RBS::Types::ClassSingleton + if hd == :name + env = service.latest_env + class_decl = env.class_decls[type.name]&.decls[0]&.decl or raise + location = tail[0].location[:name] + ClassContent.new( + location: location, + decl: class_decl + ) + end + when RBS::Types::Interface + env = service.latest_env + interface_decl = env.interface_decls[type.name]&.decl or raise + location = type.location[:name] - receiver_type = if receiver - typing.type_of(node: receiver) - else - context.self_type - end + InterfaceContent.new( + location: location, + decl: interface_decl + ) + end + end + end - factory = context.type_env.subtyping.factory - method_name, definition = case receiver_type - when AST::Types::Name::Instance - method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name) - if method_definition&.defined_in - owner_name = method_definition.defined_in - [ - InstanceMethodName.new(owner_name, method_name), - method_definition - ] - end - when AST::Types::Name::Singleton - method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name) - if method_definition&.defined_in - owner_name = method_definition.defined_in - [ - SingletonMethodName.new(owner_name, method_name), - method_definition - ] - end - else - nil - end + def hover_for_source(column, line, path, target) + file = service.source_files[path] + typing = typecheck(target, path: path, content: file.content, line: line, column: column) or return + node, *parents = typing.source.find_nodes(line: line, column: column) - MethodCallContent.new( - node: node, - method_name: method_name, - type: typing.type_of(node: result_node), - definition: definition, - location: result_node.location.expression - ) - when :def, :defs - context = typing.context_at(line: line, column: column) - method_context = context.method_context + if node + case node.type + when :lvar + var_name = node.children[0] + context = typing.context_at(line: line, column: column) + var_type = context.lvar_env[var_name] || AST::Types::Any.new(location: nil) - if method_context && method_context.method - DefinitionContent.new( - node: node, - method_name: method_context.name, - method_type: method_context.method_type, - definition: method_context.method, - location: node.loc.expression - ) - end - else - type = typing.type_of(node: node) + VariableContent.new(node: node, name: var_name, type: var_type, location: node.location.name) + when :lvasgn + var_name, rhs = node.children + context = typing.context_at(line: line, column: column) + type = context.lvar_env[var_name] || typing.type_of(node: rhs) - TypeContent.new( + VariableContent.new(node: node, name: var_name, type: type, location: node.location.name) + when :send + receiver, method_name, *_ = node.children + + + result_node = if parents[0]&.type == :block + parents[0] + else + node + end + + context = typing.context_at(line: line, column: column) + + receiver_type = if receiver + typing.type_of(node: receiver) + else + context.self_type + end + + factory = context.type_env.subtyping.factory + method_name, definition = case receiver_type + when AST::Types::Name::Instance + method_definition = method_definition_for(factory, receiver_type.name, instance_method: method_name) + if method_definition&.defined_in + owner_name = method_definition.defined_in + [ + InstanceMethodName.new(owner_name, method_name), + method_definition + ] + end + when AST::Types::Name::Singleton + method_definition = method_definition_for(factory, receiver_type.name, singleton_method: method_name) + if method_definition&.defined_in + owner_name = method_definition.defined_in + [ + SingletonMethodName.new(owner_name, method_name), + method_definition + ] + end + else + nil + end + + MethodCallContent.new( + node: node, + method_name: method_name, + type: typing.type_of(node: result_node), + definition: definition, + location: result_node.location.expression + ) + when :def, :defs + context = typing.context_at(line: line, column: column) + method_context = context.method_context + + if method_context && method_context.method + DefinitionContent.new( node: node, - type: type, - location: node.location.expression + method_name: method_context.name, + method_type: method_context.method_type, + definition: method_context.method, + location: node.loc.expression ) end + else + type = typing.type_of(node: node) + + TypeContent.new( + node: node, + type: type, + location: node.location.expression + ) end end end end end