mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 15:48:22 +00:00
Format docstrings (#6452)
**Summary** Implement docstring formatting **Test Plan** Matches black's `docstring.py` fixture exactly, added some new cases for what is hard to debug with black and with what black doesn't cover. similarity index: main: zulip: 0.99702 django: 0.99784 warehouse: 0.99585 build: 0.75623 transformers: 0.99469 cpython: 0.75989 typeshed: 0.74853 this branch: zulip: 0.99702 django: 0.99784 warehouse: 0.99585 build: 0.75623 transformers: 0.99464 cpython: 0.75517 typeshed: 0.74853 The regression in transformers is actually an improvement in a file they don't format with black (they run `black examples tests src utils setup.py conftest.py`, the difference is in hubconf.py). cpython doesn't use black. Closes #6196
This commit is contained in:
parent
910dbbd9b6
commit
01eceaf0dc
9 changed files with 1064 additions and 1245 deletions
|
@ -135,7 +135,7 @@ def multiline_backslash_3():
|
|||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,73 +1,75 @@
|
||||
@@ -1,24 +1,24 @@
|
||||
class ALonelyClass:
|
||||
- '''
|
||||
+ """
|
||||
|
@ -167,96 +167,7 @@ def multiline_backslash_3():
|
|||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
- """This is a docstring with
|
||||
- some lines of text here
|
||||
- """
|
||||
+ """This is a docstring with
|
||||
+ some lines of text here
|
||||
+ """
|
||||
return
|
||||
|
||||
|
||||
def baz():
|
||||
'''"This" is a string with some
|
||||
- embedded "quotes"'''
|
||||
+ embedded "quotes"'''
|
||||
return
|
||||
|
||||
|
||||
def poit():
|
||||
"""
|
||||
- Lorem ipsum dolor sit amet.
|
||||
+ 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
|
||||
- """
|
||||
+ 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.
|
||||
- """
|
||||
+ 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
|
||||
+ 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!"""
|
||||
+ """But with a newline after it!
|
||||
+
|
||||
+ """
|
||||
pass
|
||||
|
||||
|
||||
@@ -83,41 +85,41 @@
|
||||
|
||||
def and_that():
|
||||
"""
|
||||
- "hey yah" """
|
||||
+ "hey yah" """
|
||||
|
||||
|
||||
def and_this():
|
||||
- '''
|
||||
- "hey yah"'''
|
||||
+ '''
|
||||
+ "hey yah"'''
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib():
|
||||
- '''
|
||||
- "hey yah"'''
|
||||
+ '''
|
||||
+"hey yah"'''
|
||||
@@ -97,27 +97,27 @@
|
||||
|
||||
|
||||
def shockingly_the_quotes_are_normalized_v2():
|
||||
|
@ -285,14 +196,14 @@ def multiline_backslash_3():
|
|||
- '''
|
||||
- hey there \ '''
|
||||
+ """
|
||||
+ hey there \ """
|
||||
+ hey there \ """
|
||||
|
||||
|
||||
def multiline_backslash_3():
|
||||
- '''
|
||||
- already escaped \\'''
|
||||
+ """
|
||||
+ already escaped \\ """
|
||||
+ already escaped \\"""
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
@ -323,53 +234,51 @@ def shockingly_the_quotes_are_normalized():
|
|||
|
||||
|
||||
def foo():
|
||||
"""This is a docstring with
|
||||
some lines of text here
|
||||
"""
|
||||
"""This is a docstring with
|
||||
some lines of text here
|
||||
"""
|
||||
return
|
||||
|
||||
|
||||
def baz():
|
||||
'''"This" is a string with some
|
||||
embedded "quotes"'''
|
||||
embedded "quotes"'''
|
||||
return
|
||||
|
||||
|
||||
def poit():
|
||||
"""
|
||||
Lorem ipsum dolor sit amet.
|
||||
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
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
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
|
||||
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!
|
||||
|
||||
"""
|
||||
"""But with a newline after it!"""
|
||||
pass
|
||||
|
||||
|
||||
|
@ -385,17 +294,17 @@ def that():
|
|||
|
||||
def and_that():
|
||||
"""
|
||||
"hey yah" """
|
||||
"hey yah" """
|
||||
|
||||
|
||||
def and_this():
|
||||
'''
|
||||
"hey yah"'''
|
||||
'''
|
||||
"hey yah"'''
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib():
|
||||
'''
|
||||
"hey yah"'''
|
||||
'''
|
||||
"hey yah"'''
|
||||
|
||||
|
||||
def shockingly_the_quotes_are_normalized_v2():
|
||||
|
@ -417,12 +326,12 @@ def multiline_backslash_1():
|
|||
|
||||
def multiline_backslash_2():
|
||||
"""
|
||||
hey there \ """
|
||||
hey there \ """
|
||||
|
||||
|
||||
def multiline_backslash_3():
|
||||
"""
|
||||
already escaped \\ """
|
||||
already escaped \\"""
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/comments_non_breaking_space.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```py
|
||||
from .config import ( ConfigTypeAttributes, Int, Path, # String,
|
||||
# DEFAULT_TYPE_ATTRIBUTES,
|
||||
)
|
||||
|
||||
result = 1 # A simple comment
|
||||
result = ( 1, ) # Another one
|
||||
|
||||
result = 1 # type: ignore
|
||||
result = 1# This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
|
||||
def function(a:int=42):
|
||||
""" This docstring is already formatted
|
||||
a
|
||||
b
|
||||
"""
|
||||
# There's a NBSP + 3 spaces before
|
||||
# And 4 spaces on the next line
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
|
||||
def function(a: int = 42):
|
||||
- """This docstring is already formatted
|
||||
- a
|
||||
- b
|
||||
+ """ This docstring is already formatted
|
||||
+ a
|
||||
+ b
|
||||
"""
|
||||
# There's a NBSP + 3 spaces before
|
||||
# And 4 spaces on the next line
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
from .config import (
|
||||
ConfigTypeAttributes,
|
||||
Int,
|
||||
Path, # String,
|
||||
# DEFAULT_TYPE_ATTRIBUTES,
|
||||
)
|
||||
|
||||
result = 1 # A simple comment
|
||||
result = (1,) # Another one
|
||||
|
||||
result = 1 # type: ignore
|
||||
result = 1 # This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
|
||||
|
||||
def function(a: int = 42):
|
||||
""" This docstring is already formatted
|
||||
a
|
||||
b
|
||||
"""
|
||||
# There's a NBSP + 3 spaces before
|
||||
# And 4 spaces on the next line
|
||||
pass
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```py
|
||||
from .config import (
|
||||
ConfigTypeAttributes,
|
||||
Int,
|
||||
Path, # String,
|
||||
# DEFAULT_TYPE_ATTRIBUTES,
|
||||
)
|
||||
|
||||
result = 1 # A simple comment
|
||||
result = (1,) # Another one
|
||||
|
||||
result = 1 # type: ignore
|
||||
result = 1 # This comment is talking about type: ignore
|
||||
square = Square(4) # type: Optional[Square]
|
||||
|
||||
|
||||
def function(a: int = 42):
|
||||
"""This docstring is already formatted
|
||||
a
|
||||
b
|
||||
"""
|
||||
# There's a NBSP + 3 spaces before
|
||||
# And 4 spaces on the next line
|
||||
pass
|
||||
```
|
||||
|
||||
|
|
@ -1,924 +0,0 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/simple_cases/docstring.py
|
||||
---
|
||||
## Input
|
||||
|
||||
```py
|
||||
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 multiline_whitespace():
|
||||
'''
|
||||
|
||||
|
||||
|
||||
|
||||
'''
|
||||
|
||||
|
||||
def oneline_whitespace():
|
||||
''' '''
|
||||
|
||||
|
||||
def empty():
|
||||
""""""
|
||||
|
||||
|
||||
def single_quotes():
|
||||
'testing'
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib(): '''
|
||||
"hey yah"'''
|
||||
|
||||
|
||||
def ignored_docstring():
|
||||
"""a => \
|
||||
b"""
|
||||
|
||||
def single_line_docstring_with_whitespace():
|
||||
""" This should be stripped """
|
||||
|
||||
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
|
||||
|
||||
|
||||
def backslash_space():
|
||||
"""\ """
|
||||
|
||||
|
||||
def multiline_backslash_1():
|
||||
'''
|
||||
hey\there\
|
||||
\ '''
|
||||
|
||||
|
||||
def multiline_backslash_2():
|
||||
'''
|
||||
hey there \ '''
|
||||
|
||||
# Regression test for #3425
|
||||
def multiline_backslash_really_long_dont_crash():
|
||||
"""
|
||||
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
|
||||
|
||||
|
||||
def multiline_backslash_3():
|
||||
'''
|
||||
already escaped \\ '''
|
||||
|
||||
|
||||
def my_god_its_full_of_stars_1():
|
||||
"I'm sorry Dave\u2001"
|
||||
|
||||
|
||||
# the space below is actually a \u2001, removed in output
|
||||
def my_god_its_full_of_stars_2():
|
||||
"I'm sorry Dave "
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit():
|
||||
"""long docstring................................................................."""
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit2():
|
||||
"""long docstring.................................................................
|
||||
|
||||
..................................................................................
|
||||
"""
|
||||
|
||||
|
||||
def docstring_at_line_limit():
|
||||
"""long docstring................................................................"""
|
||||
|
||||
|
||||
def multiline_docstring_at_line_limit():
|
||||
"""first line-----------------------------------------------------------------------
|
||||
|
||||
second line----------------------------------------------------------------------"""
|
||||
|
||||
|
||||
def stable_quote_normalization_with_immediate_inner_single_quote(self):
|
||||
''''<text here>
|
||||
|
||||
<text here, since without another non-empty line black is stable>
|
||||
'''
|
||||
```
|
||||
|
||||
## Black Differences
|
||||
|
||||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,83 +1,85 @@
|
||||
class MyClass:
|
||||
- """Multiline
|
||||
- class docstring
|
||||
- """
|
||||
+ """ Multiline
|
||||
+ class docstring
|
||||
+ """
|
||||
|
||||
def method(self):
|
||||
"""Multiline
|
||||
- method docstring
|
||||
- """
|
||||
+ method docstring
|
||||
+ """
|
||||
pass
|
||||
|
||||
|
||||
def foo():
|
||||
- """This is a docstring with
|
||||
- some lines of text here
|
||||
- """
|
||||
+ """This is a docstring with
|
||||
+ some lines of text here
|
||||
+ """
|
||||
return
|
||||
|
||||
|
||||
def bar():
|
||||
"""This is another docstring
|
||||
- with more lines of text
|
||||
- """
|
||||
+ with more lines of text
|
||||
+ """
|
||||
return
|
||||
|
||||
|
||||
def baz():
|
||||
'''"This" is a string with some
|
||||
- embedded "quotes"'''
|
||||
+ embedded "quotes"'''
|
||||
return
|
||||
|
||||
|
||||
def troz():
|
||||
"""Indentation with tabs
|
||||
- is just as OK
|
||||
- """
|
||||
+ is just as OK
|
||||
+ """
|
||||
return
|
||||
|
||||
|
||||
def zort():
|
||||
"""Another
|
||||
- multiline
|
||||
- docstring
|
||||
- """
|
||||
+ multiline
|
||||
+ docstring
|
||||
+ """
|
||||
pass
|
||||
|
||||
|
||||
def poit():
|
||||
"""
|
||||
- Lorem ipsum dolor sit amet.
|
||||
+ 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
|
||||
- """
|
||||
+ 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.
|
||||
- """
|
||||
+ 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
|
||||
+ 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!"""
|
||||
+ """But with a newline after it!
|
||||
+
|
||||
+ """
|
||||
pass
|
||||
|
||||
|
||||
@@ -93,20 +95,25 @@
|
||||
|
||||
def and_that():
|
||||
"""
|
||||
- "hey yah" """
|
||||
+ "hey yah" """
|
||||
|
||||
|
||||
def and_this():
|
||||
- '''
|
||||
- "hey yah"'''
|
||||
+ '''
|
||||
+ "hey yah"'''
|
||||
|
||||
|
||||
def multiline_whitespace():
|
||||
- """ """
|
||||
+ """
|
||||
+
|
||||
+
|
||||
+
|
||||
+
|
||||
+ """
|
||||
|
||||
|
||||
def oneline_whitespace():
|
||||
- """ """
|
||||
+ """ """
|
||||
|
||||
|
||||
def empty():
|
||||
@@ -118,8 +125,8 @@
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib():
|
||||
- '''
|
||||
- "hey yah"'''
|
||||
+ '''
|
||||
+"hey yah"'''
|
||||
|
||||
|
||||
def ignored_docstring():
|
||||
@@ -128,31 +135,31 @@
|
||||
|
||||
|
||||
def single_line_docstring_with_whitespace():
|
||||
- """This should be stripped"""
|
||||
+ """ This should be stripped """
|
||||
|
||||
|
||||
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
|
||||
+ 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
|
||||
- """
|
||||
+ 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
|
||||
|
||||
|
||||
@@ -168,7 +175,7 @@
|
||||
|
||||
def multiline_backslash_2():
|
||||
"""
|
||||
- hey there \ """
|
||||
+ hey there \ """
|
||||
|
||||
|
||||
# Regression test for #3425
|
||||
@@ -179,7 +186,7 @@
|
||||
|
||||
def multiline_backslash_3():
|
||||
"""
|
||||
- already escaped \\"""
|
||||
+ already escaped \\ """
|
||||
|
||||
|
||||
def my_god_its_full_of_stars_1():
|
||||
@@ -188,7 +195,7 @@
|
||||
|
||||
# the space below is actually a \u2001, removed in output
|
||||
def my_god_its_full_of_stars_2():
|
||||
- "I'm sorry Dave"
|
||||
+ "I'm sorry Dave "
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit():
|
||||
```
|
||||
|
||||
## Ruff Output
|
||||
|
||||
```py
|
||||
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 multiline_whitespace():
|
||||
"""
|
||||
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def oneline_whitespace():
|
||||
""" """
|
||||
|
||||
|
||||
def empty():
|
||||
""""""
|
||||
|
||||
|
||||
def single_quotes():
|
||||
"testing"
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib():
|
||||
'''
|
||||
"hey yah"'''
|
||||
|
||||
|
||||
def ignored_docstring():
|
||||
"""a => \
|
||||
b"""
|
||||
|
||||
|
||||
def single_line_docstring_with_whitespace():
|
||||
""" This should be stripped """
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def backslash_space():
|
||||
"""\ """
|
||||
|
||||
|
||||
def multiline_backslash_1():
|
||||
"""
|
||||
hey\there\
|
||||
\ """
|
||||
|
||||
|
||||
def multiline_backslash_2():
|
||||
"""
|
||||
hey there \ """
|
||||
|
||||
|
||||
# Regression test for #3425
|
||||
def multiline_backslash_really_long_dont_crash():
|
||||
"""
|
||||
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
|
||||
|
||||
|
||||
def multiline_backslash_3():
|
||||
"""
|
||||
already escaped \\ """
|
||||
|
||||
|
||||
def my_god_its_full_of_stars_1():
|
||||
"I'm sorry Dave\u2001"
|
||||
|
||||
|
||||
# the space below is actually a \u2001, removed in output
|
||||
def my_god_its_full_of_stars_2():
|
||||
"I'm sorry Dave "
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit():
|
||||
"""long docstring................................................................."""
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit2():
|
||||
"""long docstring.................................................................
|
||||
|
||||
..................................................................................
|
||||
"""
|
||||
|
||||
|
||||
def docstring_at_line_limit():
|
||||
"""long docstring................................................................"""
|
||||
|
||||
|
||||
def multiline_docstring_at_line_limit():
|
||||
"""first line-----------------------------------------------------------------------
|
||||
|
||||
second line----------------------------------------------------------------------"""
|
||||
|
||||
|
||||
def stable_quote_normalization_with_immediate_inner_single_quote(self):
|
||||
"""'<text here>
|
||||
|
||||
<text here, since without another non-empty line black is stable>
|
||||
"""
|
||||
```
|
||||
|
||||
## Black Output
|
||||
|
||||
```py
|
||||
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 multiline_whitespace():
|
||||
""" """
|
||||
|
||||
|
||||
def oneline_whitespace():
|
||||
""" """
|
||||
|
||||
|
||||
def empty():
|
||||
""""""
|
||||
|
||||
|
||||
def single_quotes():
|
||||
"testing"
|
||||
|
||||
|
||||
def believe_it_or_not_this_is_in_the_py_stdlib():
|
||||
'''
|
||||
"hey yah"'''
|
||||
|
||||
|
||||
def ignored_docstring():
|
||||
"""a => \
|
||||
b"""
|
||||
|
||||
|
||||
def single_line_docstring_with_whitespace():
|
||||
"""This should be stripped"""
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
def backslash_space():
|
||||
"""\ """
|
||||
|
||||
|
||||
def multiline_backslash_1():
|
||||
"""
|
||||
hey\there\
|
||||
\ """
|
||||
|
||||
|
||||
def multiline_backslash_2():
|
||||
"""
|
||||
hey there \ """
|
||||
|
||||
|
||||
# Regression test for #3425
|
||||
def multiline_backslash_really_long_dont_crash():
|
||||
"""
|
||||
hey there hello guten tag hi hoow are you ola zdravstvuyte ciao como estas ca va \ """
|
||||
|
||||
|
||||
def multiline_backslash_3():
|
||||
"""
|
||||
already escaped \\"""
|
||||
|
||||
|
||||
def my_god_its_full_of_stars_1():
|
||||
"I'm sorry Dave\u2001"
|
||||
|
||||
|
||||
# the space below is actually a \u2001, removed in output
|
||||
def my_god_its_full_of_stars_2():
|
||||
"I'm sorry Dave"
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit():
|
||||
"""long docstring................................................................."""
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit2():
|
||||
"""long docstring.................................................................
|
||||
|
||||
..................................................................................
|
||||
"""
|
||||
|
||||
|
||||
def docstring_at_line_limit():
|
||||
"""long docstring................................................................"""
|
||||
|
||||
|
||||
def multiline_docstring_at_line_limit():
|
||||
"""first line-----------------------------------------------------------------------
|
||||
|
||||
second line----------------------------------------------------------------------"""
|
||||
|
||||
|
||||
def stable_quote_normalization_with_immediate_inner_single_quote(self):
|
||||
"""'<text here>
|
||||
|
||||
<text here, since without another non-empty line black is stable>
|
||||
"""
|
||||
```
|
||||
|
||||
|
|
@ -62,11 +62,7 @@ def single_quote_docstring_over_line_limit2():
|
|||
```diff
|
||||
--- Black
|
||||
+++ Ruff
|
||||
@@ -1,9 +1,11 @@
|
||||
def docstring_almost_at_line_limit():
|
||||
- """long docstring................................................................."""
|
||||
+ """long docstring.................................................................
|
||||
+ """
|
||||
@@ -3,7 +3,8 @@
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit_with_prefix():
|
||||
|
@ -82,8 +78,7 @@ def single_quote_docstring_over_line_limit2():
|
|||
|
||||
```py
|
||||
def docstring_almost_at_line_limit():
|
||||
"""long docstring.................................................................
|
||||
"""
|
||||
"""long docstring................................................................."""
|
||||
|
||||
|
||||
def docstring_almost_at_line_limit_with_prefix():
|
||||
|
|
|
@ -0,0 +1,448 @@
|
|||
---
|
||||
source: crates/ruff_python_formatter/tests/fixtures.rs
|
||||
input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/docstring.py
|
||||
---
|
||||
## Input
|
||||
```py
|
||||
def single_line_backslashes1():
|
||||
""" content\ """
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes2():
|
||||
""" content\\ """
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes3():
|
||||
""" content\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes1():
|
||||
"""This is a docstring with
|
||||
some lines of text\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes2():
|
||||
"""This is a docstring with
|
||||
some lines of text\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes3():
|
||||
"""This is a docstring with
|
||||
some lines of text\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiple_negatively_indented_docstring_lines():
|
||||
"""a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
"""
|
||||
|
||||
|
||||
def overindentend_docstring():
|
||||
"""a
|
||||
over-indented
|
||||
"""
|
||||
|
||||
|
||||
def comment_before_docstring():
|
||||
# don't lose this function comment ...
|
||||
"""Does nothing.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this function comment
|
||||
|
||||
|
||||
class CommentBeforeDocstring():
|
||||
# don't lose this class comment ...
|
||||
"""Empty class.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this class comment
|
||||
|
||||
|
||||
class IndentMeSome:
|
||||
def doc_string_without_linebreak_after_colon(self): """ This is somewhat strange
|
||||
a
|
||||
b
|
||||
We format this a is the docstring had started properly indented on the next
|
||||
line if the target indentation. This may we incorrect since source and target
|
||||
indentation can be incorrect, but this is also an edge case.
|
||||
"""
|
||||
|
||||
|
||||
class IgnoreImplicitlyConcatenatedStrings:
|
||||
"""""" ""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break1():
|
||||
"""
|
||||
he said "the news of my death have been greatly exaggerated"
|
||||
"""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break2():
|
||||
"""he said "the news of my death have been greatly exaggerated"
|
||||
"""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break3():
|
||||
"""he said "the news of my death have been greatly exaggerated"
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
^^^^^^^^^^
|
||||
Normal indented line
|
||||
- autor
|
||||
"""
|
||||
```
|
||||
|
||||
## Outputs
|
||||
### Output 1
|
||||
```
|
||||
indent-style = Spaces, size: 4
|
||||
line-width = 88
|
||||
quote-style = Double
|
||||
magic-trailing-comma = Respect
|
||||
```
|
||||
|
||||
```py
|
||||
def single_line_backslashes1():
|
||||
"""content\ """
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes2():
|
||||
"""content\\"""
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes3():
|
||||
"""content\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes1():
|
||||
"""This is a docstring with
|
||||
some lines of text\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes2():
|
||||
"""This is a docstring with
|
||||
some lines of text\\"""
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes3():
|
||||
"""This is a docstring with
|
||||
some lines of text\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiple_negatively_indented_docstring_lines():
|
||||
"""a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
"""
|
||||
|
||||
|
||||
def overindentend_docstring():
|
||||
"""a
|
||||
over-indented
|
||||
"""
|
||||
|
||||
|
||||
def comment_before_docstring():
|
||||
# don't lose this function comment ...
|
||||
"""Does nothing.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this function comment
|
||||
|
||||
|
||||
class CommentBeforeDocstring:
|
||||
# don't lose this class comment ...
|
||||
"""Empty class.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this class comment
|
||||
|
||||
|
||||
class IndentMeSome:
|
||||
def doc_string_without_linebreak_after_colon(self):
|
||||
"""This is somewhat strange
|
||||
a
|
||||
b
|
||||
We format this a is the docstring had started properly indented on the next
|
||||
line if the target indentation. This may we incorrect since source and target
|
||||
indentation can be incorrect, but this is also an edge case.
|
||||
"""
|
||||
|
||||
|
||||
class IgnoreImplicitlyConcatenatedStrings:
|
||||
"""""" ""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break1():
|
||||
"""
|
||||
he said "the news of my death have been greatly exaggerated"
|
||||
"""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break2():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break3():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
^^^^^^^^^^
|
||||
Normal indented line
|
||||
- autor
|
||||
"""
|
||||
```
|
||||
|
||||
|
||||
### Output 2
|
||||
```
|
||||
indent-style = Spaces, size: 2
|
||||
line-width = 88
|
||||
quote-style = Double
|
||||
magic-trailing-comma = Respect
|
||||
```
|
||||
|
||||
```py
|
||||
def single_line_backslashes1():
|
||||
"""content\ """
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes2():
|
||||
"""content\\"""
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes3():
|
||||
"""content\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes1():
|
||||
"""This is a docstring with
|
||||
some lines of text\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes2():
|
||||
"""This is a docstring with
|
||||
some lines of text\\"""
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes3():
|
||||
"""This is a docstring with
|
||||
some lines of text\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiple_negatively_indented_docstring_lines():
|
||||
"""a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
"""
|
||||
|
||||
|
||||
def overindentend_docstring():
|
||||
"""a
|
||||
over-indented
|
||||
"""
|
||||
|
||||
|
||||
def comment_before_docstring():
|
||||
# don't lose this function comment ...
|
||||
"""Does nothing.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this function comment
|
||||
|
||||
|
||||
class CommentBeforeDocstring:
|
||||
# don't lose this class comment ...
|
||||
"""Empty class.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this class comment
|
||||
|
||||
|
||||
class IndentMeSome:
|
||||
def doc_string_without_linebreak_after_colon(self):
|
||||
"""This is somewhat strange
|
||||
a
|
||||
b
|
||||
We format this a is the docstring had started properly indented on the next
|
||||
line if the target indentation. This may we incorrect since source and target
|
||||
indentation can be incorrect, but this is also an edge case.
|
||||
"""
|
||||
|
||||
|
||||
class IgnoreImplicitlyConcatenatedStrings:
|
||||
"""""" ""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break1():
|
||||
"""
|
||||
he said "the news of my death have been greatly exaggerated"
|
||||
"""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break2():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break3():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
^^^^^^^^^^
|
||||
Normal indented line
|
||||
- autor
|
||||
"""
|
||||
```
|
||||
|
||||
|
||||
### Output 3
|
||||
```
|
||||
indent-style = Tab
|
||||
line-width = 88
|
||||
quote-style = Double
|
||||
magic-trailing-comma = Respect
|
||||
```
|
||||
|
||||
```py
|
||||
def single_line_backslashes1():
|
||||
"""content\ """
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes2():
|
||||
"""content\\"""
|
||||
return
|
||||
|
||||
|
||||
def single_line_backslashes3():
|
||||
"""content\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes1():
|
||||
"""This is a docstring with
|
||||
some lines of text\ """
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes2():
|
||||
"""This is a docstring with
|
||||
some lines of text\\"""
|
||||
return
|
||||
|
||||
|
||||
def multiline_backslashes3():
|
||||
"""This is a docstring with
|
||||
some lines of text\\\ """
|
||||
return
|
||||
|
||||
|
||||
def multiple_negatively_indented_docstring_lines():
|
||||
"""a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
"""
|
||||
|
||||
|
||||
def overindentend_docstring():
|
||||
"""a
|
||||
over-indented
|
||||
"""
|
||||
|
||||
|
||||
def comment_before_docstring():
|
||||
# don't lose this function comment ...
|
||||
"""Does nothing.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this function comment
|
||||
|
||||
|
||||
class CommentBeforeDocstring:
|
||||
# don't lose this class comment ...
|
||||
"""Empty class.
|
||||
|
||||
But it has comments
|
||||
""" # ... neither lose this class comment
|
||||
|
||||
|
||||
class IndentMeSome:
|
||||
def doc_string_without_linebreak_after_colon(self):
|
||||
"""This is somewhat strange
|
||||
a
|
||||
b
|
||||
We format this a is the docstring had started properly indented on the next
|
||||
line if the target indentation. This may we incorrect since source and target
|
||||
indentation can be incorrect, but this is also an edge case.
|
||||
"""
|
||||
|
||||
|
||||
class IgnoreImplicitlyConcatenatedStrings:
|
||||
"""""" ""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break1():
|
||||
"""
|
||||
he said "the news of my death have been greatly exaggerated"
|
||||
"""
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break2():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
def docstring_that_ends_with_quote_and_a_line_break3():
|
||||
"""he said "the news of my death have been greatly exaggerated" """
|
||||
|
||||
|
||||
class TabbedIndent:
|
||||
def tabbed_indent(self):
|
||||
"""check for correct tabbed formatting
|
||||
^^^^^^^^^^
|
||||
Normal indented line
|
||||
- autor
|
||||
"""
|
||||
```
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue