--- trac/mimeview/api-t4336.py	2006-12-09 10:21:26.000000000 -0600
+++ trac/mimeview/api.py	2006-12-09 11:06:17.000000000 -0600
@@ -59,7 +59,7 @@
 import re
 from StringIO import StringIO
 
-from genshi import escape, Markup, Stream
+from genshi import escape, Markup, Stream, Attrs
 from genshi.core import TEXT, START, END, START_NS, END_NS
 from genshi.builder import Fragment, tag
 from genshi.input import HTMLParser
@@ -624,7 +624,7 @@
         raise RequestDone
 
 
-def _group_lines(stream):
+def _group_lines_core(stream):
     space_re = re.compile('(?P<spaces> (?: +))|^(?P<tag><\w+.*?>)?( )')
     def pad_spaces(match):
         m = match.group('spaces')
@@ -671,15 +671,57 @@
     buf = []
     for kind, data, pos in _generate():
         if kind is TEXT and data == '\n':
-            yield Stream(buf[:])
+            yield buf
             del buf[:]
         else:
             if kind is TEXT:
                 data = space_re.sub(pad_spaces, data)
             buf.append((kind, data, pos))
     if buf:
-        yield Stream(buf[:])
+        yield buf
+
+
+def _group_lines(stream):
+    stack = []
+    for buf in _group_lines_core(stream):
+        # Remove empty text events
+        for i in range(len(buf)-1, -1, -1):
+            if buf[i][0] is TEXT and not buf[i][1]:
+                del buf[i]
+        # Remove silly open-and-immediately-close spans from the
+        # end of each line.
+        while len(buf) >= 2 and \
+                buf[-2][0] is START and buf[-1][0] is END and \
+                buf[-2][1][0] == buf[-1][1]:
+            del buf[-2:]
+        # Same for the start of the line.
+        while len(buf) >= 2 and \
+                buf[0][0] is START and buf[1][0] is END and \
+                buf[0][1][0] == buf[1][1]:
+            del buf[:2]
+        # Now go through and get rid of <span class="">
+        # Make sure the stack is empty in case of invalid input
+        del stack[:]
+        noclass = Attrs([('class', '')])
+        
+        def stripclass():
+            for e in buf:
+                if e[0] is START:
+                    if e[1][1] == noclass:
+                        stack.append(None)
+                    else:
+                        stack.append(e)
+                        yield e
+                elif e[0] is END:
+                    o = stack.pop()
+                    if o:
+                        yield e
+                else:
+                    yield e
+
+        nbuf = list(stripclass())
 
+        yield Stream(nbuf)
 
 # -- Default annotators
 
--- trac/mimeview/tests/api-t4336.py	2006-12-09 10:30:53.000000000 -0600
+++ trac/mimeview/tests/api.py	2006-12-09 11:06:17.000000000 -0600
@@ -126,7 +126,7 @@
         lines = list(_group_lines(input))
         self.assertEquals(len(lines), 1)
         self.assertTrue(isinstance(lines[0], Stream))
-        self.assertEquals(lines[0].events, input)
+        self.assertEquals(lines[0].events, [])
 
     def test_empty_text_in_span(self):
         """
@@ -138,8 +138,8 @@
                  (END, ns.span, (None, -1, -1)),
                 ]
         lines = list(_group_lines(input))
-        self.assertEqual(len(lines), 1)
-        self.assertEqual(lines[0].render('html'), "<span></span>")
+        self.assertEquals(len(lines), 1)
+        self.assertEquals(lines[0].render('html'), "")
                  
     def test_newline(self):
         """
@@ -160,9 +160,9 @@
         ditto.
         """
         input = HTMLParser(StringIO('<span class="c">\n\n\na</span>'))
-        expected = ['<span class="c"></span>',
-                    '<span class="c"></span>',
-                    '<span class="c"></span>',
+        expected = ['',
+                    '',
+                    '',
                     '<span class="c">a</span>',
                    ]
         lines = list(_group_lines(input))
@@ -170,6 +170,70 @@
         for a, b in zip(lines, expected):
             self.assertEquals(a.render('xml'), b)
 
+    def test_strip_empty_text(self):
+        """
+        One part of
+        http://trac.edgewall.org/ticket/4339
+        """
+        ns = Namespace('http://www.w3.org/1999/xhtml')
+        input = [(START, (ns.span, Attrs([('class', 5)])), (None, -1, -1)),
+                 (TEXT, "", (None, -1, -1)),
+                 (TEXT, "a", (None, -1, -1)),
+                 (END, ns.span, (None, -1, -1)),
+                ]
+        expected = [(START, (ns.span, Attrs([('class', 5)])), (None, -1, -1)),
+                    (TEXT, "a", (None, -1, -1)),
+                    (END, ns.span, (None, -1, -1)),
+                   ]
+        lines = list(_group_lines(input))
+        self.assertEquals(len(lines), 1)
+        self.assertEquals(lines[0].render('html'),
+                          '<span class="5">a</span>')
+        for a, b in zip(lines[0], expected):
+            self.assertEquals(a, b)
+
+    def test_strip_noclass_span(self):
+        """
+        One part of
+        http://trac.edgewall.org/ticket/4339
+        """
+        ns = Namespace('http://www.w3.org/1999/xhtml')
+        input = [(START, (ns.span, Attrs([('class', '')])), (None, -1, -1)),
+                 (TEXT, "abc", (None, -1, -1)),
+                 (END, ns.span, (None, -1, -1)),
+                ]
+        expected = [(TEXT, "abc", (None, -1, -1))]
+        
+        lines = list(_group_lines(input))
+        self.assertEquals(len(lines), 1)
+        self.assertEquals(lines[0].render('html'), 'abc')
+        for a, b in zip(lines[0], expected):
+            self.assertEquals(a, b)
+
+    def test_strip_nested_noclass_span(self):
+        """
+        One part of
+        http://trac.edgewall.org/ticket/4339
+        """
+        ns = Namespace('http://www.w3.org/1999/xhtml')
+        input = [(START, (ns.em, Attrs([])), (None, -1, -1)),
+                 (START, (ns.span, Attrs([('class', '')])), (None, -1, -1)),
+                 (TEXT, "abc", (None, -1, -1)),
+                 (END, ns.span, (None, -1, -1)),
+                 (END, ns.em, (None, -1, -1)),
+                ]
+        expected = [(START, (ns.em, Attrs([])), (None, -1, -1)),
+                    (TEXT, "abc", (None, -1, -1)),
+                    (END, ns.em, (None, -1, -1)),
+                   ]
+        
+        lines = list(_group_lines(input))
+        self.assertEquals(len(lines), 1)
+        self.assertEquals(lines[0].render('html'), '<em>abc</em>')
+        for a, b in zip(lines[0], expected):
+            self.assertEquals(a, b)
+        
+
 
 def suite():
     suite = unittest.TestSuite()

