Class: Markbridge::Renderers::Discourse::RenderContext

Inherits:
Object
  • Object
show all
Defined in:
lib/markbridge/renderers/discourse/render_context.rb

Overview

Immutable context for rendering, implemented as a linked parent chain: each context holds the nearest parent element plus a link to the enclosing context. #with_parent runs once per rendered element — the chain form makes it a single fixed-size allocation instead of copying (and freezing) a parents array per element. Provides query methods to ask about parent elements without the renderer knowing about specific element types.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(parents = [], html_mode: false, parent: nil, element: nil) ⇒ RenderContext

Returns a new instance of RenderContext.

Parameters:

  • parents (Array<AST::Element>) (defaults to: [])

    parent elements in document order (outermost first); convenience form for building a context from scratch. Ignored when element: is given.

  • html_mode (Boolean) (defaults to: false)
  • parent (RenderContext, nil) (defaults to: nil)

    enclosing context (chain form)

  • element (AST::Element, nil) (defaults to: nil)

    nearest parent element (chain form)



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 29

def initialize(parents = [], html_mode: false, parent: nil, element: nil)
  @html_mode = html_mode
  if element
    @parent_context = parent
    @element = element
    @depth = (parent ? parent.depth : 0) + 1
  elsif parents.empty?
    # Root context: @element and @parent_context stay unset and
    # read as nil.
    @depth = 0
  else
    @parent_context = self.class.new(parents[0, parents.size - 1], html_mode:)
    @element = parents.last
    @depth = parents.size
  end
end

Instance Attribute Details

#depthInteger (readonly)

Returns number of parent elements in the chain.

Returns:

  • (Integer)

    number of parent elements in the chain



15
16
17
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 15

def depth
  @depth
end

#elementAST::Element? (readonly)

Returns the nearest parent element (nil at root).

Returns:

  • (AST::Element, nil)

    the nearest parent element (nil at root)



18
19
20
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 18

def element
  @element
end

#parent_contextRenderContext? (readonly)

Returns the enclosing context (nil at root).

Returns:



21
22
23
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 21

def parent_context
  @parent_context
end

Instance Method Details

#count_parents(klass) ⇒ Integer

Count parents that are is_a? klass (handles subclasses).

Parameters:

  • klass (Class)

Returns:

  • (Integer)


99
100
101
102
103
104
105
106
107
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 99

def count_parents(klass)
  count = 0
  context = self
  while context && (parent = context.element)
    count += 1 if parent.is_a?(klass)
    context = context.parent_context
  end
  count
end

#find_parent(klass) ⇒ AST::Element?

Find closest parent that is_a? klass (handles subclasses). The chain walks are inlined in each query (instead of a shared yielding helper) — these run several times per rendered text node, and a plain while loop avoids the block invocation.

Parameters:

  • klass (Class)

Returns:

  • (AST::Element, nil)

    nil when no parent matches (implicit from the exhausted while loop)



88
89
90
91
92
93
94
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 88

def find_parent(klass)
  context = self
  while context && (parent = context.element)
    return parent if parent.is_a?(klass)
    context = context.parent_context
  end
end

#has_parent?(klass) ⇒ Boolean

Check if any parent is_a? klass (handles subclasses).

Parameters:

  • klass (Class)

Returns:

  • (Boolean)


112
113
114
115
116
117
118
119
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 112

def has_parent?(klass)
  context = self
  while context && (parent = context.element)
    return true if parent.is_a?(klass)
    context = context.parent_context
  end
  false
end

#html_mode?Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 77

def html_mode?
  @html_mode
end

#parentsArray<AST::Element>

Parent elements in document order (outermost first), materialized from the chain into a frozen Array. Allocates on every call — prefer #find_parent / #has_parent? / #count_parents on hot paths.

Returns:



51
52
53
54
55
56
57
58
59
60
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 51

def parents
  result = []
  context = self
  while context && (parent = context.element)
    result << parent
    context = context.parent_context
  end
  result.reverse!
  result.freeze
end

#root?Boolean

Check if we're at the root (no parents).

Returns:

  • (Boolean)


123
124
125
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 123

def root?
  @depth.zero?
end

#with_html_mode(value) ⇒ RenderContext

Create new context with html_mode toggled.

Parameters:

  • value (Boolean)

Returns:



72
73
74
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 72

def with_html_mode(value)
  self.class.new(html_mode: value, parent: @parent_context, element: @element)
end

#with_parent(element) ⇒ RenderContext

Create new context with element added to parent chain.

Parameters:

Returns:



65
66
67
# File 'lib/markbridge/renderers/discourse/render_context.rb', line 65

def with_parent(element)
  self.class.new(html_mode: @html_mode, parent: self, element:)
end