Class: Rigor::LanguageServer::HoverProvider

Inherits:
Object
  • Object
show all
Defined in:
lib/rigor/language_server/hover_provider.rb

Overview

Answers ‘textDocument/hover` requests by running the same NodeLocator + ScopeIndexer + `Scope#type_of` chain that `rigor type-of` already drives. The LSP wraps the result in a `Hover` payload with markdown contents.

Per LSP spec § “Position”:

  • ‘line` and `character` are 0-based.

  • ‘character` counts UTF-16 code units; v1 emits byte counts for ASCII source (UTF-16 conversion is queued, see design doc § “Open questions”).

Instance Method Summary collapse

Constructor Details

#initialize(buffer_table:, project_context:, renderer: HoverRenderer.new) ⇒ HoverProvider

Returns a new instance of HoverProvider.



25
26
27
28
29
# File 'lib/rigor/language_server/hover_provider.rb', line 25

def initialize(buffer_table:, project_context:, renderer: HoverRenderer.new)
  @buffer_table = buffer_table
  @project_context = project_context
  @renderer = renderer
end

Instance Method Details

#provide(uri:, line:, character:) ⇒ Hash?

Returns an LSP ‘Hover` payload or nil when no expression sits at the queried position. Returning nil maps to `result: null` per the LSP spec — clients suppress the hover popup in that case.

Returns:

  • (Hash, nil)

    an LSP ‘Hover` payload or nil when no expression sits at the queried position. Returning nil maps to `result: null` per the LSP spec — clients suppress the hover popup in that case.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/rigor/language_server/hover_provider.rb', line 35

def provide(uri:, line:, character:)
  path = Uri.to_path(uri)
  return nil if path.nil?

  entry = @buffer_table[uri]
  return nil if entry.nil?

  parse_result = Prism.parse(entry.bytes, filepath: path, version: @project_context.configuration.target_ruby)
  return nil unless parse_result.errors.empty?

  # Rigor's NodeLocator uses 1-based line / column; LSP uses
  # 0-based. Translate at the boundary.
  node = locate_node(source: entry.bytes, root: parse_result.value, line: line + 1, character: character + 1)
  return nil if node.nil?

  scope = base_scope(path)
  index = Inference::ScopeIndexer.index(parse_result.value, default_scope: scope)
  node_scope = index[node]
  type = node_scope.type_of(node)

  @renderer.render(node: node, type: type, node_scope_lookup: index)
end