Class: Markbridge::Parsers::BBCode::ClosingStrategies::TagReconciler

Inherits:
Object
  • Object
show all
Defined in:
lib/markbridge/parsers/bbcode/closing_strategies/tag_reconciler.rb

Overview

Encapsulates logic for reconciling mismatched closing tags

Constant Summary collapse

MAX_AUTO_CLOSE_DEPTH =
5

Instance Method Summary collapse

Constructor Details

#initialize(registry:) ⇒ TagReconciler

Returns a new instance of TagReconciler.



11
12
13
# File 'lib/markbridge/parsers/bbcode/closing_strategies/tag_reconciler.rb', line 11

def initialize(registry:)
  @registry = registry
end

Instance Method Details

#try_auto_close(handler:, context:) ⇒ Boolean

Attempt to auto-close tags to match a closing tag

Parameters:

  • handler (BaseHandler)

    the handler for the closing tag

  • context (ParserState)

Returns:

  • (Boolean)

    true if successful, false if auto-close not possible



20
21
22
23
24
25
26
27
28
# File 'lib/markbridge/parsers/bbcode/closing_strategies/tag_reconciler.rb', line 20

def try_auto_close(handler:, context:)
  count = auto_close_count(handler, context)
  return false if count.nil?

  count.times { context.pop }
  context.auto_close!(count)

  true
end

#try_reopen(handler:, context:, tokens:) ⇒ Boolean

Attempt to close the target tag and reopen any intervening auto-closeable tags so subsequent content continues in the same formatting context. Used when closing tags are not adjacent (e.g. “[b][i]x more”).

Reopening only makes sense when there is upcoming content that would benefit from the reopened context. If the next token is a closing tag (or nothing), plain auto-close is correct.

Parameters:

  • handler (BaseHandler)

    the handler for the closing tag

  • context (ParserState)
  • tokens (Object, nil)

    the token stream (used to check that content follows)

Returns:

  • (Boolean)

    true if successful, false otherwise



43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/markbridge/parsers/bbcode/closing_strategies/tag_reconciler.rb', line 43

def try_reopen(handler:, context:, tokens:)
  case tokens&.peek
  when TextToken, TagStartToken
    nil # content follows -> reopening is justified
  else
    return false
  end

  match_depth = find_matching_handler_depth(handler, context)
  return false if match_depth.nil? || match_depth.zero?
  return false unless all_auto_closeable?(context, match_depth)

  intervening = context.elements_from_current(match_depth - 1).map(&:class)

  count = match_depth + 1
  count.times { context.pop }
  context.auto_close!(count)

  intervening.reverse_each { |klass| context.push(klass.new) }

  true
end

#try_reorder(handler:, tokens:, context:) ⇒ Boolean

Attempt to reorder closing tags

Parameters:

  • handler (BaseHandler)

    the handler for the closing tag

  • tokens (Object)

    the token stream

  • context (ParserState)

Returns:

  • (Boolean)

    true if successful, false otherwise



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/markbridge/parsers/bbcode/closing_strategies/tag_reconciler.rb', line 72

def try_reorder(handler:, tokens:, context:)
  match_depth = find_matching_handler_depth(handler, context)
  opening_handlers = collect_auto_closeable_handlers(context, match_depth)

  closing_handlers = [handler, *peek_closing_handlers(tokens, opening_handlers.size - 1)]
  unless opening_handlers.sort_by(&:object_id) == closing_handlers.sort_by(&:object_id)
    return false
  end

  # Consume the extra closing tags. We've already verified via
  # peek_closing_handlers that the next opening_handlers.size - 1
  # tokens are TagEndTokens with handlers we accept.
  (opening_handlers.size - 1).times { tokens.next }

  opening_handlers.each { context.pop }
  context.auto_close!(opening_handlers.size)

  true
end