Skip to content

Commit

Permalink
Add a --use-tabs argument to use tabs instead of spaces
Browse files Browse the repository at this point in the history
  • Loading branch information
jleclanche authored and sciyoshi committed Aug 10, 2023
1 parent 800ce65 commit d472e28
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 7 deletions.
9 changes: 9 additions & 0 deletions src/black/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,13 @@ def validate_regex(
" functionality in the next major release."
),
)
@click.option(
"--use-tabs",
is_flag=True,
help=(
"Use tabs instead of spaces for indentation. Tabs are always equal to 4 spaces."
),
)
@click.option(
"--check",
is_flag=True,
Expand Down Expand Up @@ -457,6 +464,7 @@ def main( # noqa: C901
skip_magic_trailing_comma: bool,
experimental_string_processing: bool,
preview: bool,
use_tabs: bool,
quiet: bool,
verbose: bool,
required_version: Optional[str],
Expand Down Expand Up @@ -546,6 +554,7 @@ def main( # noqa: C901
experimental_string_processing=experimental_string_processing,
preview=preview,
python_cell_magics=set(python_cell_magics),
use_tabs=use_tabs,
)

if code is not None:
Expand Down
5 changes: 3 additions & 2 deletions src/black/linegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,11 @@ def visit_STRING(self, leaf: Leaf) -> Iterator[Line]:
quote_len = 1 if docstring[1] != quote_char else 3
docstring = docstring[quote_len:-quote_len]
docstring_started_empty = not docstring
indent = " " * 4 * self.current_line.depth
indent_style = " " * 4 if not self.mode.use_tabs else "\t"
indent = indent_style * self.current_line.depth

if is_multiline_string(leaf):
docstring = fix_docstring(docstring, indent)
docstring = fix_docstring(docstring, indent, not self.mode.use_tabs)
else:
docstring = docstring.strip()

Expand Down
12 changes: 9 additions & 3 deletions src/black/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,11 +457,15 @@ def clone(self) -> "Line":
)

def __str__(self) -> str:
return self.render()

def render(self, force_spaces: bool = False) -> str:
"""Render the line."""
if not self:
return "\n"

indent = " " * self.depth
indent_style = " " if force_spaces or not self.mode.use_tabs else "\t"
indent = indent_style * self.depth
leaves = iter(self.leaves)
first = next(leaves)
res = f"{first.prefix}{indent}{first.value}"
Expand Down Expand Up @@ -762,6 +766,8 @@ def is_line_short_enough( # noqa: C901
"""
if not line_str:
line_str = line_to_string(line)
else:
line_str = line_str.expandtabs(tabsize=4)

width = str_width if mode.preview else len

Expand Down Expand Up @@ -1005,9 +1011,9 @@ def _can_omit_closing_paren(line: Line, *, last: Leaf, line_length: int) -> bool
return False


def line_to_string(line: Line) -> str:
def line_to_string(line: Line, force_spaces: bool = False) -> str:
"""Returns the string representation of @line.
WARNING: This is known to be computationally expensive.
"""
return str(line).strip("\n")
return line.render(force_spaces=force_spaces).strip("\n")
2 changes: 2 additions & 0 deletions src/black/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class Mode:
experimental_string_processing: bool = False
python_cell_magics: Set[str] = field(default_factory=set)
preview: bool = False
use_tabs: bool = False

def __post_init__(self) -> None:
if self.experimental_string_processing:
Expand Down Expand Up @@ -239,5 +240,6 @@ def get_cache_key(self) -> str:
str(int(self.experimental_string_processing)),
str(int(self.preview)),
sha256((",".join(sorted(self.python_cell_magics))).encode()).hexdigest(),
str(int(self.use_tabs)),
]
return ".".join(parts)
7 changes: 5 additions & 2 deletions src/black/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,14 @@ def lines_with_leading_tabs_expanded(s: str) -> List[str]:
return lines


def fix_docstring(docstring: str, prefix: str) -> str:
def fix_docstring(docstring: str, prefix: str, expand_leading_tabs: bool) -> str:
# https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation
if not docstring:
return ""
lines = lines_with_leading_tabs_expanded(docstring)
if expand_leading_tabs:
lines = lines_with_leading_tabs_expanded(docstring)
else:
lines = docstring.splitlines()
# Determine minimum indentation (first line doesn't count):
indent = sys.maxsize
for line in lines[1:]:
Expand Down
276 changes: 276 additions & 0 deletions tests/data/tabs/docstring_tabs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
class MyClass:
""" Multiline
class docstring
"""

def method(self):
"""Multiline
method docstring
"""
pass


def foo():
"""This is a docstring with
some lines of text here
"""
return


def bar():
'''This is another docstring
with more lines of text
'''
return


def baz():
'''"This" is a string with some
embedded "quotes"'''
return


def troz():
'''Indentation with tabs
is just as OK
'''
return


def zort():
"""Another
multiline
docstring
"""
pass

def poit():
"""
Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
- dolore magna aliqua
- enim ad minim veniam
- quis nostrud exercitation ullamco laboris nisi
- aliquip ex ea commodo consequat
"""
pass


def under_indent():
"""
These lines are indented in a way that does not
make sense.
"""
pass


def over_indent():
"""
This has a shallow indent
- But some lines are deeper
- And the closing quote is too deep
"""
pass


def single_line():
"""But with a newline after it!
"""
pass


def this():
r"""
'hey ho'
"""


def that():
""" "hey yah" """


def and_that():
"""
"hey yah" """


def and_this():
'''
"hey yah"'''


def believe_it_or_not_this_is_in_the_py_stdlib(): '''
"hey yah"'''


def ignored_docstring():
"""a => \
b"""


def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""


def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass


# output

class MyClass:
"""Multiline
class docstring
"""

def method(self):
"""Multiline
method docstring
"""
pass


def foo():
"""This is a docstring with
some lines of text here
"""
return


def bar():
"""This is another docstring
with more lines of text
"""
return


def baz():
'''"This" is a string with some
embedded "quotes"'''
return


def troz():
"""Indentation with tabs
is just as OK
"""
return


def zort():
"""Another
multiline
docstring
"""
pass


def poit():
"""
Lorem ipsum dolor sit amet.
Consectetur adipiscing elit:
- sed do eiusmod tempor incididunt ut labore
- dolore magna aliqua
- enim ad minim veniam
- quis nostrud exercitation ullamco laboris nisi
- aliquip ex ea commodo consequat
"""
pass


def under_indent():
"""
These lines are indented in a way that does not
make sense.
"""
pass


def over_indent():
"""
This has a shallow indent
- But some lines are deeper
- And the closing quote is too deep
"""
pass


def single_line():
"""But with a newline after it!"""
pass


def this():
r"""
'hey ho'
"""


def that():
""" "hey yah" """


def and_that():
"""
"hey yah" """


def and_this():
'''
"hey yah"'''


def believe_it_or_not_this_is_in_the_py_stdlib():
'''
"hey yah"'''


def ignored_docstring():
"""a => \
b"""


def docstring_with_inline_tabs_and_space_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""


def docstring_with_inline_tabs_and_tab_indentation():
"""hey
tab separated value
tab at start of line and then a tab separated value
multiple tabs at the beginning and inline
mixed tabs and spaces at beginning. next line has mixed tabs and spaces only.
line ends with some tabs
"""
pass
Loading

0 comments on commit d472e28

Please sign in to comment.