Repository: encode/django-rest-framework

Issue:
Browsable API markdown description broken if tabs are used in code indentation.
get_view_description(html=True) with markdown installed is broken if the project uses tabs instead of spaces. Everything after the first line is rendered as a `<pre>` block.

The problem is in the [utils.formatting.dedent()](https://github.com/tomchristie/django-rest-framework/blob/4e5da16/rest_framework/utils/formatting.py#L25) function which only dedents spaces, not tabs.
## Checklist
- [x] I have verified that that issue exists against the `master` branch of Django REST framework.
- [x] I have searched for similar issues in both open and closed tickets and cannot find a duplicate.
- [x] This is not a usage question. (Those should be directed to the [discussion group](https://groups.google.com/forum/#!forum/django-rest-framework) instead.)
- [ ] This cannot be dealt with as a third party library. (We prefer new functionality to be [in the form of third party libraries](http://www.django-rest-framework.org/topics/third-party-resources/#about-third-party-packages) where possible.)
- [x] I have reduced the issue to the simplest possible case.
- [ ] I have included a failing test as a pull request. (If you are unable to do so we can still accept the issue.)
## Steps to reproduce

``` python
from rest_framework.utils.formatting import dedent
dedent("\tfirst string\n\n\tsecond string")
```
## Expected behavior

It returns:

```
u'first string\n\n\tsecond string'
```
## Actual behavior

It should return:

```
u'first string\n\nsecond string'
```



Discussion:

Resolving patch:
diff --git a/rest_framework/utils/formatting.py b/rest_framework/utils/formatting.py
--- a/rest_framework/utils/formatting.py
+++ b/rest_framework/utils/formatting.py
@@ -32,13 +32,22 @@ def dedent(content):
     unindented text on the initial line.
     """
     content = force_text(content)
-    whitespace_counts = [len(line) - len(line.lstrip(' '))
-                         for line in content.splitlines()[1:] if line.lstrip()]
+    whitespace_counts = [
+        len(line) - len(line.lstrip(' '))
+        for line in content.splitlines()[1:] if line.lstrip()
+    ]
+    tab_counts = [
+        len(line) - len(line.lstrip('\t'))
+        for line in content.splitlines()[1:] if line.lstrip()
+    ]
 
     # unindent the content if needed
     if whitespace_counts:
         whitespace_pattern = '^' + (' ' * min(whitespace_counts))
         content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
+    elif tab_counts:
+        whitespace_pattern = '^' + ('\t' * min(whitespace_counts))
+        content = re.sub(re.compile(whitespace_pattern, re.MULTILINE), '', content)
 
     return content.strip()
 


Test patch:
diff --git a/tests/test_description.py b/tests/test_description.py
--- a/tests/test_description.py
+++ b/tests/test_description.py
@@ -6,6 +6,7 @@
 from django.utils.encoding import python_2_unicode_compatible
 
 from rest_framework.compat import apply_markdown
+from rest_framework.utils.formatting import dedent
 from rest_framework.views import APIView
 
 
@@ -120,3 +121,7 @@ def test_markdown(self):
             gte_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_gte_21
             lt_21_match = apply_markdown(DESCRIPTION) == MARKED_DOWN_lt_21
             self.assertTrue(gte_21_match or lt_21_match)
+
+
+def test_dedent_tabs():
+    assert dedent("\tfirst string\n\n\tsecond string") == 'first string\n\n\tsecond string'