<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -165,7 +165,7 @@ def entities_to_hex(text, wrap):
     return re.sub(r'&amp;(#x?)?([0-9]+|[0-9a-fA-F]+);', wrap_hex, text)
 
 def trim(context, text, lines=True, sides='both', respect_indent=False,
-		 preserve_linebreaks=True):
+         preserve_linebreaks=True):
     '''
     Trims whitespace from the text
     
@@ -482,6 +482,47 @@ def select_from_zones(context, range=None, default=None, **syntaxes):
     # If we reach this point, there's no match
     return default
 
+def range_in_zone(context, range, selector):
+    '''
+    Tests the location of the range to see if it matches the provided
+    zone selector string
+    '''
+    target = SXSelectorGroup.selectorGroupWithString_(selector)
+    if context.string().length() == range.location:
+        zone = context.syntaxTree().rootZone()
+    else:
+        zone = context.syntaxTree().rootZone().zoneAtCharacterIndex_(
+            range.location
+        )
+    return target.matches_(zone)
+
+def cursor_in_zone(context, selector):
+    '''
+    Tests the location of the range to see if it matches the provided
+    zone selector string
+    '''
+    ranges = get_ranges(context)
+    return range_in_zone(context, ranges[0], selector)
+
+# ===============================================================
+# Itemizer methods
+# ===============================================================
+
+def get_item_for_range(context, range):
+    '''Returns the smallest item containing the given range'''
+    return context.itemizer().smallestItemContainingCharacterRange_()
+
+def get_item_parent_for_range(context, range):
+    '''Returns the parent of the item containing the given range'''
+    item = tea.get_item_from_range(context, range)
+    new_range = item.range()
+    # Select the parent if the range is the same
+    while(item.parent() and (new_range.location == range.location and \
+          new_range.length == range.length):
+        item = item.parent()
+        new_range = item.range()
+    return item
+
 # ===============================================================
 # Snippet methods
 # ===============================================================</diff>
      <filename>src/Contents/Resources/tea_actions.py</filename>
    </modified>
    <modified>
      <diff>@@ -1,44 +1,71 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+'''
+Attempts to locate the balanced delimiters around the cursor and select
+their contents.
+
+If direction == 'in', balance will attempt to move inward (select first
+balanced delimiters contained within the current delimiter) rather than
+outward.
+'''
+
 import tea_actions as tea
 
 from zencoding import html_matcher as html_matcher
 
 def act(context, direction='out'):
-	rng = tea.get_single_range(context)
-	cursor = rng.location + rng.length
-	range_start, range_end = rng.location, rng.location + rng.length
-	content = context.string()
-	
-	old_open_tag = html_matcher.last_match['opening_tag']
-	old_close_tag = html_matcher.last_match['closing_tag']
-	
-	if direction.lower() == 'in' and old_open_tag and range_start != range_end:
-		# user has previously selected tag and wants to move inward
-		if not old_close_tag:
-			# unary tag was selected, can't move inward
-			return False
-		elif old_open_tag.start == range_start:
-			if content[old_open_tag.end] == '&lt;':
-				# test if the first inward tag matches the entire parent tag's content
-				_start, _end = html_matcher.find(content, old_open_tag.end + 1)
-				if _start == old_open_tag.end and _end == old_close_tag.start:
-					start, end = html_matcher.match(content, old_open_tag.end + 1)
-				else:
-					start, end = old_open_tag.end, old_close_tag.start
-			else:
-				start, end = old_open_tag.end, old_close_tag.start
-		else:
-			new_cursor = content.find('&lt;', old_open_tag.end, old_close_tag.start)
-			search_pos = new_cursor != -1 and new_cursor + 1 or old_open_tag.end
-			start, end = html_matcher.match(content, search_pos)
-			
-	else:
-		start, end = html_matcher.match(content, cursor)
-	
-	if start is not None:
-		new_range = tea.new_range(start, end - start)
-		tea.set_selected_range(context, new_range)
-		return True
-	else:
-		return False
\ No newline at end of file
+    if tea.cursor_in_zone(context, &quot;html, html *, xml, xml *&quot;):
+        # HTML or XML, so use Zen-coding's excellent balancing commands
+        
+        # Using this method rather than tea.get_single_range() is better
+        # because it won't cause the action to fail if there's more than
+        # one selection
+        ranges = tea.get_ranges(context)
+        rng = ranges[0]
+        cursor = rng.location + rng.length
+        range_start, range_end = rng.location, rng.location + rng.length
+        content = context.string()
+        
+        old_open_tag = html_matcher.last_match['opening_tag']
+        old_close_tag = html_matcher.last_match['closing_tag']
+        
+        if direction.lower() == 'in' and old_open_tag and range_start != range_end:
+            # user has previously selected tag and wants to move inward
+            if not old_close_tag:
+                # unary tag was selected, can't move inward
+                return False
+            elif old_open_tag.start == range_start:
+                if content[old_open_tag.end] == '&lt;':
+                    # test if the first inward tag matches the entire parent tag's content
+                    _start, _end = html_matcher.find(content, old_open_tag.end + 1)
+                    if _start == old_open_tag.end and _end == old_close_tag.start:
+                        start, end = html_matcher.match(content, old_open_tag.end + 1)
+                    else:
+                        start, end = old_open_tag.end, old_close_tag.start
+                else:
+                    start, end = old_open_tag.end, old_close_tag.start
+            else:
+                new_cursor = content.find('&lt;', old_open_tag.end, old_close_tag.start)
+                search_pos = new_cursor != -1 and new_cursor + 1 or old_open_tag.end
+                start, end = html_matcher.match(content, search_pos)
+                
+        else:
+            start, end = html_matcher.match(content, cursor)
+        
+        if start is not None:
+            new_range = tea.new_range(start, end - start)
+            tea.set_selected_range(context, new_range)
+            return True
+        else:
+            return False
+    else:
+        # No HTML or XML, so we'll rely on itemizers
+        ranges = tea.get_ranges(context)
+        targets = []
+        for range in ranges:
+            if direction.lower() == 'in':
+                targets[] = tea.get_item_for_range(context, range).range()
+            else:
+                targets[] = tea.get_item_parent_for_range(context, range).range()
+        
+        # Set the selections, and return
+        context.setSelectedRanges_([NSValue.valueWithRange_(range) for range in targets])
+        return True</diff>
      <filename>src/Support/Scripts/balance.py</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ca8653417f407f598e9dd1f842cf13cbf5c18daa</id>
    </parent>
  </parents>
  <author>
    <name>Ian Beck</name>
    <email>ian@onecrayon.com</email>
  </author>
  <url>http://github.com/onecrayon/tea-for-espresso/commit/8a24b1c32ff74647da8b1a8b963dc8f1964ec6cb</url>
  <id>8a24b1c32ff74647da8b1a8b963dc8f1964ec6cb</id>
  <committed-date>2009-10-24T22:07:04-07:00</committed-date>
  <authored-date>2009-10-24T22:07:04-07:00</authored-date>
  <message>Added itemizer balancing to balance action as a fallback</message>
  <tree>ebd04c99b8e8e06ae6492158e3d36a101f1b44b1</tree>
  <committer>
    <name>Ian Beck</name>
    <email>ian@onecrayon.com</email>
  </committer>
</commit>
