mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:35:58 +00:00
Rename ruff
crate to ruff_linter
(#7529)
This commit is contained in:
parent
dcbd8eacd8
commit
5849a75223
4397 changed files with 93921 additions and 93915 deletions
75
crates/ruff_linter/src/docstrings/extraction.rs
Normal file
75
crates/ruff_linter/src/docstrings/extraction.rs
Normal file
|
@ -0,0 +1,75 @@
|
|||
//! Extract docstrings from an AST.
|
||||
|
||||
use ruff_python_ast::{self as ast, Constant, Expr, Stmt};
|
||||
use ruff_python_semantic::{Definition, DefinitionId, Definitions, Member, MemberKind};
|
||||
|
||||
/// Extract a docstring from a function or class body.
|
||||
pub(crate) fn docstring_from(suite: &[Stmt]) -> Option<&Expr> {
|
||||
let stmt = suite.first()?;
|
||||
// Require the docstring to be a standalone expression.
|
||||
let Stmt::Expr(ast::StmtExpr { value, range: _ }) = stmt else {
|
||||
return None;
|
||||
};
|
||||
// Only match strings.
|
||||
if !matches!(
|
||||
value.as_ref(),
|
||||
Expr::Constant(ast::ExprConstant {
|
||||
value: Constant::Str(_),
|
||||
..
|
||||
})
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
Some(value)
|
||||
}
|
||||
|
||||
/// Extract a docstring from a `Definition`.
|
||||
pub(crate) fn extract_docstring<'a>(definition: &'a Definition<'a>) -> Option<&'a Expr> {
|
||||
match definition {
|
||||
Definition::Module(module) => docstring_from(module.python_ast),
|
||||
Definition::Member(member) => docstring_from(member.body()),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum ExtractionTarget<'a> {
|
||||
Class(&'a ast::StmtClassDef),
|
||||
Function(&'a ast::StmtFunctionDef),
|
||||
}
|
||||
|
||||
/// Extract a `Definition` from the AST node defined by a `Stmt`.
|
||||
pub(crate) fn extract_definition<'a>(
|
||||
target: ExtractionTarget<'a>,
|
||||
parent: DefinitionId,
|
||||
definitions: &Definitions<'a>,
|
||||
) -> Member<'a> {
|
||||
match target {
|
||||
ExtractionTarget::Function(function) => match &definitions[parent] {
|
||||
Definition::Module(..) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Function(function),
|
||||
},
|
||||
Definition::Member(Member {
|
||||
kind: MemberKind::Class(_) | MemberKind::NestedClass(_),
|
||||
..
|
||||
}) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Method(function),
|
||||
},
|
||||
Definition::Member(_) => Member {
|
||||
parent,
|
||||
kind: MemberKind::NestedFunction(function),
|
||||
},
|
||||
},
|
||||
ExtractionTarget::Class(class) => match &definitions[parent] {
|
||||
Definition::Module(_) => Member {
|
||||
parent,
|
||||
kind: MemberKind::Class(class),
|
||||
},
|
||||
Definition::Member(_) => Member {
|
||||
parent,
|
||||
kind: MemberKind::NestedClass(class),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
38
crates/ruff_linter/src/docstrings/google.rs
Normal file
38
crates/ruff_linter/src/docstrings/google.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
//! Abstractions for Google-style docstrings.
|
||||
|
||||
use crate::docstrings::sections::SectionKind;
|
||||
|
||||
pub(crate) static GOOGLE_SECTIONS: &[SectionKind] = &[
|
||||
SectionKind::Attributes,
|
||||
SectionKind::Examples,
|
||||
SectionKind::Methods,
|
||||
SectionKind::Notes,
|
||||
SectionKind::Raises,
|
||||
SectionKind::References,
|
||||
SectionKind::Returns,
|
||||
SectionKind::SeeAlso,
|
||||
SectionKind::Yields,
|
||||
// Google-only
|
||||
SectionKind::Args,
|
||||
SectionKind::Arguments,
|
||||
SectionKind::Attention,
|
||||
SectionKind::Caution,
|
||||
SectionKind::Danger,
|
||||
SectionKind::Error,
|
||||
SectionKind::Example,
|
||||
SectionKind::Hint,
|
||||
SectionKind::Important,
|
||||
SectionKind::KeywordArgs,
|
||||
SectionKind::KeywordArguments,
|
||||
SectionKind::Note,
|
||||
SectionKind::Notes,
|
||||
SectionKind::OtherArgs,
|
||||
SectionKind::OtherArguments,
|
||||
SectionKind::Return,
|
||||
SectionKind::Tip,
|
||||
SectionKind::Todo,
|
||||
SectionKind::Warning,
|
||||
SectionKind::Warnings,
|
||||
SectionKind::Warns,
|
||||
SectionKind::Yield,
|
||||
];
|
78
crates/ruff_linter/src/docstrings/mod.rs
Normal file
78
crates/ruff_linter/src/docstrings/mod.rs
Normal file
|
@ -0,0 +1,78 @@
|
|||
use std::fmt::{Debug, Formatter};
|
||||
use std::ops::Deref;
|
||||
|
||||
use ruff_python_ast::Expr;
|
||||
use ruff_python_semantic::Definition;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
pub(crate) mod extraction;
|
||||
pub(crate) mod google;
|
||||
pub(crate) mod numpy;
|
||||
pub(crate) mod sections;
|
||||
pub(crate) mod styles;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Docstring<'a> {
|
||||
pub(crate) definition: &'a Definition<'a>,
|
||||
pub(crate) expr: &'a Expr,
|
||||
/// The content of the docstring, including the leading and trailing quotes.
|
||||
pub(crate) contents: &'a str,
|
||||
/// The range of the docstring body (without the quotes). The range is relative to [`Self::contents`].
|
||||
pub(crate) body_range: TextRange,
|
||||
pub(crate) indentation: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> Docstring<'a> {
|
||||
pub(crate) fn body(&self) -> DocstringBody {
|
||||
DocstringBody { docstring: self }
|
||||
}
|
||||
|
||||
pub(crate) fn leading_quote(&self) -> &'a str {
|
||||
&self.contents[TextRange::up_to(self.body_range.start())]
|
||||
}
|
||||
|
||||
pub(crate) fn triple_quoted(&self) -> bool {
|
||||
let leading_quote = self.leading_quote();
|
||||
leading_quote.ends_with("\"\"\"") || leading_quote.ends_with("'''")
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for Docstring<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
self.expr.range()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct DocstringBody<'a> {
|
||||
docstring: &'a Docstring<'a>,
|
||||
}
|
||||
|
||||
impl<'a> DocstringBody<'a> {
|
||||
pub(crate) fn as_str(self) -> &'a str {
|
||||
&self.docstring.contents[self.docstring.body_range]
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for DocstringBody<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
self.docstring.body_range + self.docstring.start()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DocstringBody<'_> {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for DocstringBody<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("DocstringBody")
|
||||
.field("text", &self.as_str())
|
||||
.field("range", &self.range())
|
||||
.finish()
|
||||
}
|
||||
}
|
21
crates/ruff_linter/src/docstrings/numpy.rs
Normal file
21
crates/ruff_linter/src/docstrings/numpy.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
//! Abstractions for NumPy-style docstrings.
|
||||
|
||||
use crate::docstrings::sections::SectionKind;
|
||||
|
||||
pub(crate) static NUMPY_SECTIONS: &[SectionKind] = &[
|
||||
SectionKind::Attributes,
|
||||
SectionKind::Examples,
|
||||
SectionKind::Methods,
|
||||
SectionKind::Notes,
|
||||
SectionKind::Raises,
|
||||
SectionKind::References,
|
||||
SectionKind::Returns,
|
||||
SectionKind::SeeAlso,
|
||||
SectionKind::Yields,
|
||||
// NumPy-only
|
||||
SectionKind::ExtendedSummary,
|
||||
SectionKind::OtherParams,
|
||||
SectionKind::OtherParameters,
|
||||
SectionKind::Parameters,
|
||||
SectionKind::ShortSummary,
|
||||
];
|
443
crates/ruff_linter/src/docstrings/sections.rs
Normal file
443
crates/ruff_linter/src/docstrings/sections.rs
Normal file
|
@ -0,0 +1,443 @@
|
|||
use std::fmt::{Debug, Formatter};
|
||||
use std::iter::FusedIterator;
|
||||
|
||||
use ruff_python_ast::docstrings::{leading_space, leading_words};
|
||||
use ruff_text_size::{Ranged, TextLen, TextRange, TextSize};
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
use ruff_source_file::{Line, UniversalNewlineIterator, UniversalNewlines};
|
||||
|
||||
use crate::docstrings::styles::SectionStyle;
|
||||
use crate::docstrings::{Docstring, DocstringBody};
|
||||
|
||||
#[derive(EnumIter, PartialEq, Eq, Debug, Clone, Copy)]
|
||||
pub(crate) enum SectionKind {
|
||||
Args,
|
||||
Arguments,
|
||||
Attention,
|
||||
Attributes,
|
||||
Caution,
|
||||
Danger,
|
||||
Error,
|
||||
Example,
|
||||
Examples,
|
||||
ExtendedSummary,
|
||||
Hint,
|
||||
Important,
|
||||
KeywordArgs,
|
||||
KeywordArguments,
|
||||
Methods,
|
||||
Note,
|
||||
Notes,
|
||||
OtherArgs,
|
||||
OtherArguments,
|
||||
OtherParams,
|
||||
OtherParameters,
|
||||
Parameters,
|
||||
Raises,
|
||||
References,
|
||||
Return,
|
||||
Returns,
|
||||
SeeAlso,
|
||||
ShortSummary,
|
||||
Tip,
|
||||
Todo,
|
||||
Warning,
|
||||
Warnings,
|
||||
Warns,
|
||||
Yield,
|
||||
Yields,
|
||||
}
|
||||
|
||||
impl SectionKind {
|
||||
pub(crate) fn from_str(s: &str) -> Option<Self> {
|
||||
match s.to_ascii_lowercase().as_str() {
|
||||
"args" => Some(Self::Args),
|
||||
"arguments" => Some(Self::Arguments),
|
||||
"attention" => Some(Self::Attention),
|
||||
"attributes" => Some(Self::Attributes),
|
||||
"caution" => Some(Self::Caution),
|
||||
"danger" => Some(Self::Danger),
|
||||
"error" => Some(Self::Error),
|
||||
"example" => Some(Self::Example),
|
||||
"examples" => Some(Self::Examples),
|
||||
"extended summary" => Some(Self::ExtendedSummary),
|
||||
"hint" => Some(Self::Hint),
|
||||
"important" => Some(Self::Important),
|
||||
"keyword args" => Some(Self::KeywordArgs),
|
||||
"keyword arguments" => Some(Self::KeywordArguments),
|
||||
"methods" => Some(Self::Methods),
|
||||
"note" => Some(Self::Note),
|
||||
"notes" => Some(Self::Notes),
|
||||
"other args" => Some(Self::OtherArgs),
|
||||
"other arguments" => Some(Self::OtherArguments),
|
||||
"other params" => Some(Self::OtherParams),
|
||||
"other parameters" => Some(Self::OtherParameters),
|
||||
"parameters" => Some(Self::Parameters),
|
||||
"raises" => Some(Self::Raises),
|
||||
"references" => Some(Self::References),
|
||||
"return" => Some(Self::Return),
|
||||
"returns" => Some(Self::Returns),
|
||||
"see also" => Some(Self::SeeAlso),
|
||||
"short summary" => Some(Self::ShortSummary),
|
||||
"tip" => Some(Self::Tip),
|
||||
"todo" => Some(Self::Todo),
|
||||
"warning" => Some(Self::Warning),
|
||||
"warnings" => Some(Self::Warnings),
|
||||
"warns" => Some(Self::Warns),
|
||||
"yield" => Some(Self::Yield),
|
||||
"yields" => Some(Self::Yields),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Args => "Args",
|
||||
Self::Arguments => "Arguments",
|
||||
Self::Attention => "Attention",
|
||||
Self::Attributes => "Attributes",
|
||||
Self::Caution => "Caution",
|
||||
Self::Danger => "Danger",
|
||||
Self::Error => "Error",
|
||||
Self::Example => "Example",
|
||||
Self::Examples => "Examples",
|
||||
Self::ExtendedSummary => "Extended Summary",
|
||||
Self::Hint => "Hint",
|
||||
Self::Important => "Important",
|
||||
Self::KeywordArgs => "Keyword Args",
|
||||
Self::KeywordArguments => "Keyword Arguments",
|
||||
Self::Methods => "Methods",
|
||||
Self::Note => "Note",
|
||||
Self::Notes => "Notes",
|
||||
Self::OtherArgs => "Other Args",
|
||||
Self::OtherArguments => "Other Arguments",
|
||||
Self::OtherParams => "Other Params",
|
||||
Self::OtherParameters => "Other Parameters",
|
||||
Self::Parameters => "Parameters",
|
||||
Self::Raises => "Raises",
|
||||
Self::References => "References",
|
||||
Self::Return => "Return",
|
||||
Self::Returns => "Returns",
|
||||
Self::SeeAlso => "See Also",
|
||||
Self::ShortSummary => "Short Summary",
|
||||
Self::Tip => "Tip",
|
||||
Self::Todo => "Todo",
|
||||
Self::Warning => "Warning",
|
||||
Self::Warnings => "Warnings",
|
||||
Self::Warns => "Warns",
|
||||
Self::Yield => "Yield",
|
||||
Self::Yields => "Yields",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SectionContexts<'a> {
|
||||
contexts: Vec<SectionContextData>,
|
||||
docstring: &'a Docstring<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SectionContexts<'a> {
|
||||
/// Extract all `SectionContext` values from a docstring.
|
||||
pub(crate) fn from_docstring(docstring: &'a Docstring<'a>, style: SectionStyle) -> Self {
|
||||
let contents = docstring.body();
|
||||
|
||||
let mut contexts = Vec::new();
|
||||
let mut last: Option<SectionContextData> = None;
|
||||
|
||||
let mut lines = contents.universal_newlines().peekable();
|
||||
|
||||
// Skip the first line, which is the summary.
|
||||
let mut previous_line = lines.next();
|
||||
|
||||
while let Some(line) = lines.next() {
|
||||
if let Some(section_kind) = suspected_as_section(&line, style) {
|
||||
let indent = leading_space(&line);
|
||||
let section_name = leading_words(&line);
|
||||
|
||||
let section_name_range = TextRange::at(indent.text_len(), section_name.text_len());
|
||||
|
||||
if is_docstring_section(
|
||||
&line,
|
||||
section_name_range,
|
||||
previous_line.as_ref(),
|
||||
lines.peek(),
|
||||
) {
|
||||
if let Some(mut last) = last.take() {
|
||||
last.range = TextRange::new(last.start(), line.start());
|
||||
contexts.push(last);
|
||||
}
|
||||
|
||||
last = Some(SectionContextData {
|
||||
kind: section_kind,
|
||||
name_range: section_name_range + line.start(),
|
||||
range: TextRange::empty(line.start()),
|
||||
summary_full_end: line.full_end(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
previous_line = Some(line);
|
||||
}
|
||||
|
||||
if let Some(mut last) = last.take() {
|
||||
last.range = TextRange::new(last.start(), contents.text_len());
|
||||
contexts.push(last);
|
||||
}
|
||||
|
||||
Self {
|
||||
contexts,
|
||||
docstring,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn len(&self) -> usize {
|
||||
self.contexts.len()
|
||||
}
|
||||
|
||||
pub(crate) fn iter(&self) -> SectionContextsIter {
|
||||
SectionContextsIter {
|
||||
docstring_body: self.docstring.body(),
|
||||
inner: self.contexts.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a SectionContexts<'a> {
|
||||
type IntoIter = SectionContextsIter<'a>;
|
||||
type Item = SectionContext<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SectionContexts<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_list().entries(self.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SectionContextsIter<'a> {
|
||||
docstring_body: DocstringBody<'a>,
|
||||
inner: std::slice::Iter<'a, SectionContextData>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for SectionContextsIter<'a> {
|
||||
type Item = SectionContext<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.inner.next()?;
|
||||
|
||||
Some(SectionContext {
|
||||
data: next,
|
||||
docstring_body: self.docstring_body,
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.inner.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DoubleEndedIterator for SectionContextsIter<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
let back = self.inner.next_back()?;
|
||||
Some(SectionContext {
|
||||
data: back,
|
||||
docstring_body: self.docstring_body,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FusedIterator for SectionContextsIter<'_> {}
|
||||
impl ExactSizeIterator for SectionContextsIter<'_> {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SectionContextData {
|
||||
kind: SectionKind,
|
||||
|
||||
/// Range of the section name, relative to the [`Docstring::body`]
|
||||
name_range: TextRange,
|
||||
|
||||
/// Range from the start to the end of the section, relative to the [`Docstring::body`]
|
||||
range: TextRange,
|
||||
|
||||
/// End of the summary, relative to the [`Docstring::body`]
|
||||
summary_full_end: TextSize,
|
||||
}
|
||||
|
||||
impl Ranged for SectionContextData {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SectionContext<'a> {
|
||||
data: &'a SectionContextData,
|
||||
docstring_body: DocstringBody<'a>,
|
||||
}
|
||||
|
||||
impl<'a> SectionContext<'a> {
|
||||
/// The `kind` of the section, e.g. [`SectionKind::Args`] or [`SectionKind::Returns`].
|
||||
pub(crate) const fn kind(&self) -> SectionKind {
|
||||
self.data.kind
|
||||
}
|
||||
|
||||
/// The name of the section as it appears in the docstring, e.g. "Args" or "Returns".
|
||||
pub(crate) fn section_name(&self) -> &'a str {
|
||||
&self.docstring_body.as_str()[self.data.name_range]
|
||||
}
|
||||
|
||||
/// Returns the rest of the summary line after the section name.
|
||||
pub(crate) fn summary_after_section_name(&self) -> &'a str {
|
||||
&self.summary_line()[usize::from(self.data.name_range.end() - self.data.range.start())..]
|
||||
}
|
||||
|
||||
fn offset(&self) -> TextSize {
|
||||
self.docstring_body.start()
|
||||
}
|
||||
|
||||
/// The absolute range of the section name
|
||||
pub(crate) fn section_name_range(&self) -> TextRange {
|
||||
self.data.name_range + self.offset()
|
||||
}
|
||||
|
||||
/// The absolute range of the summary line, excluding any trailing newline character.
|
||||
pub(crate) fn summary_range(&self) -> TextRange {
|
||||
TextRange::at(self.range().start(), self.summary_line().text_len())
|
||||
}
|
||||
|
||||
/// Range of the summary line relative to [`Docstring::body`], including the trailing newline character.
|
||||
fn summary_full_range_relative(&self) -> TextRange {
|
||||
TextRange::new(self.range_relative().start(), self.data.summary_full_end)
|
||||
}
|
||||
|
||||
/// Returns the range of this section relative to [`Docstring::body`]
|
||||
const fn range_relative(&self) -> TextRange {
|
||||
self.data.range
|
||||
}
|
||||
|
||||
/// The absolute range of the full-section.
|
||||
pub(crate) fn range(&self) -> TextRange {
|
||||
self.range_relative() + self.offset()
|
||||
}
|
||||
|
||||
/// Summary line without the trailing newline characters
|
||||
pub(crate) fn summary_line(&self) -> &'a str {
|
||||
let full_summary = &self.docstring_body.as_str()[self.summary_full_range_relative()];
|
||||
|
||||
let mut bytes = full_summary.bytes().rev();
|
||||
|
||||
let newline_width = match bytes.next() {
|
||||
Some(b'\n') => {
|
||||
if bytes.next() == Some(b'\r') {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
Some(b'\r') => 1,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
&full_summary[..full_summary.len() - newline_width]
|
||||
}
|
||||
|
||||
/// Returns the text of the last line of the previous section or an empty string if it is the first section.
|
||||
pub(crate) fn previous_line(&self) -> Option<&'a str> {
|
||||
let previous =
|
||||
&self.docstring_body.as_str()[TextRange::up_to(self.range_relative().start())];
|
||||
previous.universal_newlines().last().map(|l| l.as_str())
|
||||
}
|
||||
|
||||
/// Returns the lines belonging to this section after the summary line.
|
||||
pub(crate) fn following_lines(&self) -> UniversalNewlineIterator<'a> {
|
||||
let lines = self.following_lines_str();
|
||||
UniversalNewlineIterator::with_offset(lines, self.offset() + self.data.summary_full_end)
|
||||
}
|
||||
|
||||
fn following_lines_str(&self) -> &'a str {
|
||||
&self.docstring_body.as_str()[self.following_range_relative()]
|
||||
}
|
||||
|
||||
/// Returns the range to the following lines relative to [`Docstring::body`].
|
||||
const fn following_range_relative(&self) -> TextRange {
|
||||
TextRange::new(self.data.summary_full_end, self.range_relative().end())
|
||||
}
|
||||
|
||||
/// Returns the absolute range of the following lines.
|
||||
pub(crate) fn following_range(&self) -> TextRange {
|
||||
self.following_range_relative() + self.offset()
|
||||
}
|
||||
}
|
||||
|
||||
impl Ranged for SectionContext<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range()
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SectionContext<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("SectionContext")
|
||||
.field("kind", &self.kind())
|
||||
.field("section_name", &self.section_name())
|
||||
.field("summary_line", &self.summary_line())
|
||||
.field("following_lines", &&self.following_lines_str())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn suspected_as_section(line: &str, style: SectionStyle) -> Option<SectionKind> {
|
||||
if let Some(kind) = SectionKind::from_str(leading_words(line)) {
|
||||
if style.sections().contains(&kind) {
|
||||
return Some(kind);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Check if the suspected context is really a section header.
|
||||
fn is_docstring_section(
|
||||
line: &Line,
|
||||
section_name_range: TextRange,
|
||||
previous_line: Option<&Line>,
|
||||
next_line: Option<&Line>,
|
||||
) -> bool {
|
||||
// Determine whether the current line looks like a section header, e.g., "Args:".
|
||||
let section_name_suffix = line[usize::from(section_name_range.end())..].trim();
|
||||
let this_looks_like_a_section_name =
|
||||
section_name_suffix == ":" || section_name_suffix.is_empty();
|
||||
if !this_looks_like_a_section_name {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Determine whether the next line is an underline, e.g., "-----".
|
||||
let next_line_is_underline = next_line.is_some_and(|next_line| {
|
||||
let next_line = next_line.trim();
|
||||
if next_line.is_empty() {
|
||||
false
|
||||
} else {
|
||||
let next_line_is_underline = next_line.chars().all(|char| matches!(char, '-' | '='));
|
||||
next_line_is_underline
|
||||
}
|
||||
});
|
||||
if next_line_is_underline {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Determine whether the previous line looks like the end of a paragraph.
|
||||
let previous_line_looks_like_end_of_paragraph = previous_line.map_or(true, |previous_line| {
|
||||
let previous_line = previous_line.trim();
|
||||
let previous_line_ends_with_punctuation = [',', ';', '.', '-', '\\', '/', ']', '}', ')']
|
||||
.into_iter()
|
||||
.any(|char| previous_line.ends_with(char));
|
||||
previous_line_ends_with_punctuation || previous_line.is_empty()
|
||||
});
|
||||
if !previous_line_looks_like_end_of_paragraph {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
18
crates/ruff_linter/src/docstrings/styles.rs
Normal file
18
crates/ruff_linter/src/docstrings/styles.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use crate::docstrings::google::GOOGLE_SECTIONS;
|
||||
use crate::docstrings::numpy::NUMPY_SECTIONS;
|
||||
use crate::docstrings::sections::SectionKind;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum SectionStyle {
|
||||
Numpy,
|
||||
Google,
|
||||
}
|
||||
|
||||
impl SectionStyle {
|
||||
pub(crate) fn sections(&self) -> &[SectionKind] {
|
||||
match self {
|
||||
SectionStyle::Numpy => NUMPY_SECTIONS,
|
||||
SectionStyle::Google => GOOGLE_SECTIONS,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue