Module: Hecks::Adapters::Prism

Defined in:
lib/hecks/adapters/driven/prism.rb

Constant Summary collapse

TREES =
{}

Class Method Summary collapse

Class Method Details

.block_node_at(file, line) ⇒ Object



26
27
28
29
30
31
32
33
34
35
# File 'lib/hecks/adapters/driven/prism.rb', line 26

def block_node_at(file, line)
  found = nil
  walk(tree_for(file)) do |node|
    next unless node.is_a?(::Prism::BlockNode)
    next unless node.location.start_line == line

    found ||= node
  end
  found
end

.body_source(block) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/hecks/adapters/driven/prism.rb', line 18

def body_source(block)
  file, line = block.source_location
  return nil unless file && File.readable?(file)

  node = block_node_at(file, line)
  node&.body&.slice
end

.canonical(block) ⇒ Object



13
14
15
16
# File 'lib/hecks/adapters/driven/prism.rb', line 13

def canonical(block)
  body = body_source(block)
  body && canonicalise(body)
end

.canonicalise(source) ⇒ Object



75
76
77
# File 'lib/hecks/adapters/driven/prism.rb', line 75

def canonicalise(source)
  Bluebook::Expression::CanonicalForm.apply(source)
end

.forget(file) ⇒ Object

TREES caches for the life of the PROCESS, keyed by path, with no staleness check — correct for every ordinary caller (a file loads once per process: one bin/ir run, one rspec worker, never edited out from under it), but WRONG for anything that legitimately reloads an edited file in-process: a stale cached tree reports a given/ensures block at its OLD line number, which no longer matches the freshly re-executed file's own block.source_location — surfacing as "did not survive extraction" on a perfectly valid file. Found for real building Hecks::Codemod (lib/hecks/codemod.rb), which used to reach into TREES.clear directly — a private implementation detail poked from outside. forget/forget_all are the real API so nothing else that reloads an edited file in-process has to know TREES exists at all.



64
# File 'lib/hecks/adapters/driven/prism.rb', line 64

def forget(file) = TREES.delete(file)

.forget_allObject



66
# File 'lib/hecks/adapters/driven/prism.rb', line 66

def forget_all = TREES.clear

.tree_for(file) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/hecks/adapters/driven/prism.rb', line 37

def tree_for(file)
  # ::Prism.parse_file(file) reads the file itself, at the C
  # extension level — bypassing Ruby's own File/IO layer entirely.
  # That's invisible to anything that virtualizes the filesystem at
  # the Ruby level instead of the OS level (e.g. tebako's memfs,
  # used to press a hecks-based app into a single executable —
  # see domain/README.md's "Deploying" section in lifeadelics for
  # why that matters). ::Prism.parse(File.read(file)) parses the
  # exact same bytes, just read through Ruby's File.read first,
  # which those tools do intercept.
  TREES[file] ||= ::Prism.parse(File.read(file)).value
end

.walk(node, &visit) ⇒ Object



68
69
70
71
72
73
# File 'lib/hecks/adapters/driven/prism.rb', line 68

def walk(node, &visit)
  return unless node.is_a?(::Prism::Node)

  visit.call(node)
  node.compact_child_nodes.each { |child| walk(child, &visit) }
end