MCPcopy Create free account
hub / github.com/astral-sh/ruff / format

Function format

crates/ruff_python_formatter/src/string/docstring.rs:113–215  ·  view source on GitHub ↗

Format a docstring by trimming whitespace and adjusting the indentation. Summary of changes we make: Normalize the string like all other strings Ignore docstring that have an escaped newline Trim all trailing whitespace, except for a chaperone space that avoids quotes or backslashes in the last line. Trim leading whitespace on the first line, again except for a chaperone space If there is only co

(normalized: &NormalizedString, f: &mut PyFormatter)

Source from the content-addressed store, hash-verified

111/// `indent-width * spaces` to tabs because doing so could break ASCII art and other docstrings
112/// that use spaces for alignment.
113pub(crate) fn format(normalized: &NormalizedString, f: &mut PyFormatter) -> FormatResult<()> {
114 let docstring = &normalized.text();
115
116 // Black doesn't change the indentation of docstrings that contain an escaped newline
117 if contains_unescaped_newline(docstring) {
118 return normalized.fmt(f);
119 }
120
121 // is_borrowed is unstable :/
122 let already_normalized = matches!(docstring, Cow::Borrowed(_));
123
124 // Use `split` instead of `lines` to preserve the closing quotes on their own line
125 // if they have no indentation (in which case the last line is `\n` which
126 // `lines` omit for the last element).
127 let mut lines = docstring.split('\n').peekable();
128
129 // Start the string
130 let kind = normalized.flags();
131 let quotes = StringQuotes::from(kind);
132 write!(f, [kind.prefix(), quotes])?;
133 // We track where in the source docstring we are (in source code byte offsets)
134 let mut offset = normalized.start();
135
136 // The first line directly after the opening quotes has different rules than the rest, mainly
137 // that we remove all leading whitespace as there's no indentation
138 let first = lines.next().unwrap_or_default();
139 // Black trims whitespace using [`str.strip()`](https://docs.python.org/3/library/stdtypes.html#str.strip)
140 // https://github.com/psf/black/blob/b4dca26c7d93f930bbd5a7b552807370b60d4298/src/black/strings.py#L77-L85
141 // So we use the unicode whitespace definition through `trim_{start,end}` instead of the python
142 // tokenizer whitespace definition in `trim_whitespace_{start,end}`.
143 let trim_end = first.trim_end();
144 let trim_both = trim_end.trim_start();
145
146 // Edge case: The first line is `""" "content`, so we need to insert chaperone space that keep
147 // inner quotes and closing quotes from getting to close to avoid `""""content`
148 if trim_both.starts_with(quotes.quote_char.as_char()) {
149 space().fmt(f)?;
150 }
151
152 if !trim_end.is_empty() {
153 // For the first line of the docstring we strip the leading and trailing whitespace, e.g.
154 // `""" content ` to `"""content`
155 let leading_whitespace = trim_end.text_len() - trim_both.text_len();
156 let trimmed_line_range =
157 TextRange::at(offset, trim_end.text_len()).add_start(leading_whitespace);
158 if already_normalized {
159 source_text_slice(trimmed_line_range).fmt(f)?;
160 } else {
161 text(trim_both).fmt(f)?;
162 }
163 }
164 offset += first.text_len();
165
166 // Check if we have a single line (or empty) docstring
167 if docstring[first.len()..].trim().is_empty() {
168 // For `"""\n"""` or other whitespace between the quotes, black keeps a single whitespace,
169 // but `""""""` doesn't get one inserted.
170 if needs_chaperone_space(normalized.flags(), trim_end)

Callers 3

formatMethod · 0.50
into_formatMethod · 0.50
fmt_fieldsMethod · 0.50

Calls 15

source_text_sliceFunction · 0.85
textFunction · 0.85
needs_chaperone_spaceFunction · 0.85
OkClass · 0.85
hard_line_breakFunction · 0.85
splitMethod · 0.80
trim_startMethod · 0.80
columnsMethod · 0.80
add_iterMethod · 0.80
spaceFunction · 0.50
defaultFunction · 0.50

Tested by

no test coverage detected