|
51 | 51 | function format_docstring(style::AbstractStyle, state::State, text::AbstractString) |
52 | 52 | state_indent = state.indent |
53 | 53 | start_boundary = findfirst(!=('"'), text) |
| 54 | + is_triple_quoted = let |
| 55 | + # Assuming well-formedness, the string is triple-quoted iif |
| 56 | + # the chars after/before (o)pening/(c)losing quotes are also quotes. |
| 57 | + o, c = map(f -> f(==('"'), text), (findfirst, findlast)) |
| 58 | + text[o+1] == text[c-1] == '"' |
| 59 | + end |
54 | 60 | # if the docstring is non-empty |
55 | 61 | if !isnothing(start_boundary) |
56 | 62 | _end_boundary = findlast(!=('"'), text) |
@@ -119,21 +125,24 @@ function format_docstring(style::AbstractStyle, state::State, text::AbstractStri |
119 | 125 | # the docstring is empty |
120 | 126 | formatted = "" |
121 | 127 | end |
122 | | - # Indent all non-first lines to match the current parser indent |
123 | | - buf = IOBuffer() |
124 | | - indent = " "^state_indent |
125 | | - # This is the first line, so the rest have to be indented. A newline for it will be added below |
126 | | - write(buf, "\"\"\"") |
127 | | - for line in split(formatted, '\n') |
128 | | - # The last line will be empty and will turn into an indent, so no need to indent the last line below |
129 | | - write(buf, '\n') |
130 | | - # don't write empty lines #667 |
131 | | - if !all(isspace, line) |
132 | | - write(buf, indent) |
133 | | - write(buf, line) |
134 | | - end |
| 128 | + # Render into text lines, taking care of original indentation, |
| 129 | + quot = is_triple_quoted ? "\"\"\"" : '"' |
| 130 | + indentation = " "^state_indent |
| 131 | + indent(line) = indentation * line |
| 132 | + clean(line) = all(isspace, line) ? "" : line # don't write empty lines #667 |
| 133 | + lines = split(formatted, '\n') # Always contains at least an empty last line. |
| 134 | + if is_triple_quoted |
| 135 | + prep = line -> clean(indent(line)) # All lines are prepared the same way. |
| 136 | + lines = Iterators.map(prep, lines) |
| 137 | + quot * '\n' * join(lines, '\n') * indent(quot) |
| 138 | + else |
| 139 | + # The first line needs no indentation. |
| 140 | + lines = Iterators.Stateful(lines[1:end-1]) # (drop last empty line) |
| 141 | + first = popfirst!(lines) |> clean |
| 142 | + isempty(lines) && return quot * first * quot |
| 143 | + # Upcoming ones do. |
| 144 | + prep = line -> '\n' * indent(line) |
| 145 | + lines = Iterators.map(prep, lines) |
| 146 | + quot * first * join(lines) * quot |
135 | 147 | end |
136 | | - write(buf, indent) |
137 | | - write(buf, "\"\"\"") |
138 | | - String(take!(buf)) |
139 | 148 | end |
0 commit comments