diff --git a/reviewboard/diffviewer/diffutils.py b/reviewboard/diffviewer/diffutils.py
index 3e00c09855ded20857ec7e9025b60b36a84d1fde..bd705fdf064d1a39a010a4a97b3f9232845ba15f 100644
--- a/reviewboard/diffviewer/diffutils.py
+++ b/reviewboard/diffviewer/diffutils.py
@@ -428,7 +428,6 @@ def compute_chunk_last_header(lines, numlines, meta, last_header=None):
             last_header[i] = {
                 'line': header[0],
                 'text': header[1].strip(),
-                'expand_offset': linenum + numlines - header[0],
             }
 
     return last_header
diff --git a/reviewboard/diffviewer/templatetags/difftags.py b/reviewboard/diffviewer/templatetags/difftags.py
index f90f18a438137ba823cacf07ce29c45a82d80066..45e1522696c32d843ead99e0dd425a5b384983ad 100644
--- a/reviewboard/diffviewer/templatetags/difftags.py
+++ b/reviewboard/diffviewer/templatetags/difftags.py
@@ -1,6 +1,10 @@
 import re
 
 from django import template
+from django.conf import settings
+from django.template.loader import render_to_string
+from django.utils.translation import ugettext as _
+from djblets.util.decorators import basictag
 
 register = template.Library()
 
@@ -128,3 +132,85 @@ def showextrawhitespace(value):
     return value.replace("\t", '<span class="tb">\t</span>')
 
 showextrawhitespace.is_safe = True
+
+
+def _diff_expand_link(context, expandable, text, tooltip,
+                      expand_pos, image, image_alt='[+]',
+                      image_width=14, image_height=14):
+    """Utility function to render a diff expansion link.
+
+    This is used internally by other template tags to provide a diff
+    expansion link. It assumes nothing about the content and serves only
+    to render the data from a template.
+    """
+    return render_to_string('diffviewer/expand_link.html', {
+        'tooltip': tooltip,
+        'text': text,
+        'base_url': context['base_url'],
+        'chunk': context['chunk'],
+        'file': context['file'],
+        'expand_pos': expand_pos,
+        'image': image,
+        'image_width': image_width,
+        'image_height': image_height,
+        'image_alt': image_alt,
+        'expandable': expandable,
+        'MEDIA_URL': settings.MEDIA_URL,
+        'MEDIA_SERIAL': settings.MEDIA_SERIAL,
+    })
+
+@register.tag
+@basictag(takes_context=True)
+def diff_expand_link(context, expanding, tooltip,
+                     expand_pos_1=None, expand_pos_2=None, text=None):
+    """Renders a diff expansion link.
+
+    This link will expand the diff entirely, or incrementally in one
+    or more directions.
+
+    'expanding' is expected to be one of 'all', 'above', or 'below'.
+    """
+    if expanding == 'all':
+        image = 'rb/images/diff-expand-all.png'
+        expand_pos = None
+        image_alt = '[20]'
+        image_width = 14
+    else:
+        lines_of_context = context['lines_of_context']
+        expand_pos = (lines_of_context[0] + expand_pos_1,
+                      lines_of_context[1] + expand_pos_2)
+        image = 'rb/images/diff-expand-%s.png' % expanding
+        image_width = 28
+        image_alt = '[+20]'
+
+
+    return _diff_expand_link(context, True, text, tooltip, expand_pos,
+                             image, image_alt, image_width)
+
+@register.tag
+@basictag(takes_context=True)
+def diff_chunk_header(context, header):
+    """Renders a diff header as HTML.
+
+    This diff header may be expandable, depending on whether or not the
+    function/class referenced in the header is contained within the collapsed
+    region.
+    """
+    lines_of_context = context['lines_of_context']
+    chunk = context['chunk']
+
+    line = chunk['lines'][0]
+
+    if header['line'] >= line[1]:
+        expand_offset = line[1] + chunk['numlines'] - header['line']
+        expandable = True
+    else:
+        expand_offset = 0
+        expandable = False
+
+    return _diff_expand_link(context, expandable,
+                             '<code>%s</code>' % header['text'],
+                             _('Expand to header'),
+                             (lines_of_context[0],
+                              expand_offset + lines_of_context[1]),
+                             'rb/images/diff-expand-header.png')
diff --git a/reviewboard/templates/diffviewer/diff_file_fragment.html b/reviewboard/templates/diffviewer/diff_file_fragment.html
index 4297553dbf3b4f3a7818b8308bb4c561388c719b..3d3be9d5a0ad1f84b4913a3a77cf55edc5bab9ea 100644
--- a/reviewboard/templates/diffviewer/diff_file_fragment.html
+++ b/reviewboard/templates/diffviewer/diff_file_fragment.html
@@ -97,31 +97,23 @@
   <tr>
    <th>
 {% ifnotequal chunk.index 0 %}
-    <a href="#" title="{% trans "Expand 20 lines above" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', '{{lines_of_context.0|add:20}},{{lines_of_context.1}}', this); return false;">
-     <img src="{{MEDIA_URL}}rb/images/diff-expand-above.png?{{MEDIA_SERIAL}}" width="28" height="14" alt="[+20]" />
-    </a>
+    {% diff_expand_link 'above' _('Show 20 more lines above') 20 0 %}
 {% endifnotequal %}
    </th>
    <td colspan="3">
-    <a href="#" title="{% trans "Expand all lines" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', null, this); return false;">
-     <img src="{{MEDIA_URL}}rb/images/diff-expand-all.png?{{MEDIA_SERIAL}}" width="14" height="14" alt="[+]" />
-     {{chunk.numlines}} line{{chunk.numlines|pluralize}}
-    </a>
+    {% definevar 'expand_text' %}{{chunk.numlines}} line{{chunk.numlines|pluralize}}{% enddefinevar %}
+    {% diff_expand_link 'all' _('Show all lines') 0 0 expand_text %}
    </td>
   </tr>
 {% ifnotequal chunk.index|add:1 file.num_chunks %}
   <tr>
-   <th>
-    <a href="#" title="{% trans "Expand 20 lines below" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', '{{lines_of_context.0}},{{lines_of_context.1|add:20}}', this); return false;">
-     <img src="{{MEDIA_URL}}rb/images/diff-expand-below.png?{{MEDIA_SERIAL}}" width="28" height="14" alt="[+20]" />
-    </a>
-   </th>
+   <th>{% diff_expand_link 'below' _('Show 20 more lines below') 0 20 %}</th>
 {%   if chunk.meta.headers and chunk.meta.headers.0 %}
 {%    ifequal chunk.meta.headers.0.text chunk.meta.headers.1.text %}
-   <td colspan="3"><a href="#" title="{% trans "Expand to header" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', '{{lines_of_context.0}},{{chunk.meta.headers.0.expand_offset|add:lines_of_context.1}}', this); return false;"><img src="{{MEDIA_URL}}rb/images/diff-expand-header.png?{{MEDIA_SERIAL}}" width="14" height="14" alt="[+]" /> <code>{{chunk.meta.headers.0.text}}</code></a></td>
+   <td colspan="3">{% diff_chunk_header chunk.meta.headers.0 %}</td>
 {%    else %}
-   <td><a href="#" title="{% trans "Expand to header" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', '{{lines_of_context.0}},{{chunk.meta.headers.1.expand_offset|add:lines_of_context.1}}', this); return false;"><img src="{{MEDIA_URL}}rb/images/diff-expand-header.png?{{MEDIA_SERIAL}}" width="14" height="14" alt="[+]" /> <code>{{chunk.meta.headers.0.text}}</code></a></td>
-   <td colspan="2"><a href="#" title="{% trans "Expand to header" %}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', '{{lines_of_context.0}},{{chunk.meta.headers.1.expand_offset|add:lines_of_context.1}}', this); return false;"><img src="{{MEDIA_URL}}rb/images/diff-expand-header.png?{{MEDIA_SERIAL}}" width="14" height="14" alt="[+]" /> <code>{{chunk.meta.headers.1.text}}</code></a></td>
+   <td>{% diff_chunk_header chunk.meta.headers.0 %}</td>
+   <td colspan="2">{% diff_chunk_header chunk.meta.headers.1 %}</td>
 {%    endifequal %}
 {%   else %}
    <td colspan="3"></td>
diff --git a/reviewboard/templates/diffviewer/expand_link.html b/reviewboard/templates/diffviewer/expand_link.html
new file mode 100644
index 0000000000000000000000000000000000000000..8f555877d06103204bdf314495f95cc7676e9370
--- /dev/null
+++ b/reviewboard/templates/diffviewer/expand_link.html
@@ -0,0 +1 @@
+{% if expandable %}<a href="#" title="{{tooltip}}" onclick="javascript:expandChunk('{{base_url}}', 'file{{file.index}}', '{{file.filediff.id}}', '{{file.filediff.diffset.revision}}', {% if file.interfilediff %}'{{file.interfilediff.diffset.revision}}'{% else %}null{% endif %}, '{{file.index}}', '{{chunk.index}}', {% if expand_pos %}'{{expand_pos.0}},{{expand_pos.1}}'{% else %}null{% endif %}, this); return false;"><img src="{{MEDIA_URL}}{{image}}?{{MEDIA_SERIAL}}" width="{{image_width}}" height="{{image_height}}" alt="{{image_alt}}" /> {% endif %}{% if text %}{{text|safe}}{% endif %}{% if expandable %}</a>{% endif %}
