 class InnerAngleBracketAllowForwarding extends AngleBracket
   @extend()
 
-# -------------------------
-tagPattern = /(<(\/?))([^\s>]+)[^>]*>/g
+# Tag
+# -------------------------
+tagPattern = /<(\/?)([^\s>]+)[^>]*>/g
+
+findTagRange = (which, direction, options, fn) ->
+  {editor, from, filters, allowNextLine} = options
+  switch direction
+    when 'forward'
+      scanRange = new Range(from, editor.buffer.getEndPosition())
+      scanFunctionName = 'scanInBufferRange'
+    when 'backward'
+      scanRange = new Range([0, 0], from)
+      scanFunctionName = 'backwardsScanInBufferRange'
+
+  stack = []
+  range = null
+
+  findingState = which
+  oppositeState = switch findingState
+    when 'open' then 'close'
+    when 'close' then 'open'
+
+  editor[scanFunctionName] tagPattern, scanRange, (event) ->
+    if not allowNextLine and (from.row isnt event.range.start.row)
+      event.stop()
+      return
+
+    if filters? and filters.some((filter) -> filter(event))
+      return
+
+    tagState = getTagState(event)
+    if tagState.state is oppositeState
+      stack.push(tagState)
+    else
+      if oppositeTagState = findTagState(stack, oppositeState, tagState.name)
+        stack = stack[0...stack.indexOf(oppositeTagState)]
+
+      if fn(stack, oppositeTagState)
+        range = event.range
+        event.stop()
+
+  return range
+
+getTagState = (event) ->
+  backslash = event.match[1]
+  tagName = event.match[2]
+  {
+    state: if (backslash is '') then 'open' else 'close'
+    name: tagName
+    event: event
+  }
+
+findTagState = (stack, state, name) ->
+  for tagState in stack by -1 when (tagState.state is state) and (tagState.name is name)
+    return tagState
+
+findCloseTagRangeForward = (options) ->
+  {from, allowForwarding} = options
+  delete options.allowForwarding
+
+  findTagRange 'close', 'forward', options, (stack, openTagState) ->
+    unless openTagState?
+      # I'm very torelant for orphan tag like 'br', 'hr', or unclosed tag.
+      return true
+
+    if stack.length is 0
+      {start} = openTagState.event.range
+      start.isEqual(from) or (allowForwarding and start.row is from.row)
+
+findOpenTagRangeBackward = (options) ->
+  findTagRange 'open', 'backward', options, (stack, closeTagState) ->
+    stack.length is 0
+
 class Tag extends Pair
   @extend(false)
   allowNextLine: true
   allowForwarding: true
   adjustInnerRange: false
-  getPattern: ->
-    tagPattern
-
-  getPairState: ({match, matchText}) ->
-    [__, __, slash, tagName] = match
-    if slash is ''
-      ['open', tagName]
-    else
-      ['close', tagName]
 
   getTagStartPoint: (from) ->
     tagRange = null

  delete options.allowForwarding

  findPairRange 'close', 'forward', options, (stack, openEvent) ->
    if stack.length is 0
      if not openEvent?
        true
      else
        {start} = openEvent.range
        start.isEqual(from) or (allowForwarding and start.row is from.row)

findOpenPairRangeBackward = (options, fn) ->
  findPairRange 'open', 'backward', options, (stack, openEvent) ->

        stop()
    tagRange?.start ? from

  getPairInfo: (from) ->
    from = @getTagStartPoint(from)
    filters = @getFilters()
    options = {@editor, from, filters, @allowNextLine, @allowForwarding}

    closeRange = findCloseTagRangeForward(options)
    if closeRange?
      options.from = closeRange.end
      openRange = findOpenTagRangeBackward(options)

    unless (openRange? and closeRange?)
      return null

    aRange = new Range(openRange.start, closeRange.end)
    innerRange = new Range(openRange.end, closeRange.start)
    targetRange = if @isInner() then innerRange else aRange
    {openRange, closeRange, aRange, innerRange, targetRange}

class ATag extends Tag
  @extend()
