Class: Ibex::Runtime::CST::SyntaxEditor

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/runtime/cst/editor.rb,
sig/ibex/runtime/cst/editor.rbs

Overview

Applies multiple occurrence-addressed replacements in one path-copying pass.

Instance Method Summary collapse

Constructor Details

#initialize(root) ⇒ SyntaxEditor

Returns a new instance of SyntaxEditor.

RBS:

  • (SyntaxNode root) -> void

Parameters:



21
22
23
24
# File 'lib/ibex/runtime/cst/editor.rb', line 21

def initialize(root)
  @root = root
  @edits = []
end

Instance Method Details

#ancestor?(possible_ancestor, element) ⇒ Boolean

RBS:

  • (element possible_ancestor, element element) -> bool

Parameters:

  • possible_ancestor (element)
  • element (element)

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
# File 'lib/ibex/runtime/cst/editor.rb', line 77

def ancestor?(possible_ancestor, element)
  parent = element.parent
  while parent
    return true if same_occurrence?(parent, possible_ancestor)

    parent = parent.parent
  end
  false
end

#applySyntaxNode

RBS:

  • () -> SyntaxNode

Returns:



46
47
48
49
50
51
52
# File 'lib/ibex/runtime/cst/editor.rb', line 46

def apply
  green = apply_element(@root)
  return @root if green.equal?(@root.green)
  raise TypeError, "a syntax root must remain a GreenNode" unless green.is_a?(GreenNode)

  Editing.red_root(@root, green)
end

#apply_element(element) ⇒ green

RBS:

  • (element element) -> green

Parameters:

  • element (element)

Returns:

  • (green)


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/ibex/runtime/cst/editor.rb', line 57

def apply_element(element)
  replacement = @edits.find { |target, _green| same_occurrence?(target, element) }&.fetch(1)
  return replacement if replacement
  return element.green if element.is_a?(SyntaxToken)

  changed = false
  children = element.children.map do |child|
    green = apply_element(child)
    changed ||= !green.equal?(child.green)
    green
  end
  return element.green unless changed

  GreenNode.new(
    kind: element.green.kind, children: children,
    flags: element.green.intrinsic_flags, annotations: element.green.annotations
  )
end

#replace(target, replacement) ⇒ self

RBS:

  • (element target, green | element replacement) -> self

Parameters:

  • target (element)
  • replacement (green, element)

Returns:

  • (self)


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/ibex/runtime/cst/editor.rb', line 27

def replace(target, replacement)
  raise ArgumentError, "edit target belongs to a different root" unless target.root.equal?(@root)

  green = Editing.green_element(replacement)
  duplicate = @edits.find { |existing, _value| same_occurrence?(existing, target) }
  if duplicate
    raise EditConflictError, "the same syntax occurrence has conflicting replacements" unless
      duplicate.fetch(1).equal?(green)

    return self
  end
  return self if @edits.any? { |existing, _value| ancestor?(existing, target) }

  @edits.reject! { |existing, _value| ancestor?(target, existing) }
  @edits << [target, green]
  self
end

#same_occurrence?(left, right) ⇒ Boolean

RBS:

  • (element left, element right) -> bool

Parameters:

  • left (element)
  • right (element)

Returns:

  • (Boolean)


88
89
90
# File 'lib/ibex/runtime/cst/editor.rb', line 88

def same_occurrence?(left, right)
  left.root.equal?(right.root) && left.green.equal?(right.green) && left.offset == right.offset
end