ruff/crates/ruff_linter/src/docstrings/mod.rs
2025-02-16 15:23:52 +00:00

125 lines
3.6 KiB
Rust

use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use ruff_python_ast::{self as ast, StringFlags};
use ruff_python_semantic::Definition;
use ruff_source_file::LineRanges;
use ruff_text_size::{Ranged, TextRange, TextSize};
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>,
/// The literal AST node representing the docstring.
pub(crate) expr: &'a ast::StringLiteral,
/// The source file the docstring was defined in.
pub(crate) source: &'a str,
}
impl<'a> Docstring<'a> {
fn flags(&self) -> ast::StringLiteralFlags {
self.expr.flags
}
/// The contents of the docstring, including the opening and closing quotes.
pub(crate) fn contents(&self) -> &'a str {
&self.source[self.range()]
}
/// The contents of the docstring, excluding the opening and closing quotes.
pub(crate) fn body(&self) -> DocstringBody {
DocstringBody { docstring: self }
}
/// Compute the start position of the docstring's opening line
pub(crate) fn line_start(&self) -> TextSize {
self.source.line_start(self.start())
}
/// Return the slice of source code that represents the indentation of the docstring's opening quotes.
pub(crate) fn compute_indentation(&self) -> &'a str {
&self.source[TextRange::new(self.line_start(), self.start())]
}
pub(crate) fn quote_style(&self) -> ast::str::Quote {
self.flags().quote_style()
}
pub(crate) fn is_raw_string(&self) -> bool {
self.flags().prefix().is_raw()
}
pub(crate) fn is_u_string(&self) -> bool {
self.flags().prefix().is_unicode()
}
pub(crate) fn is_triple_quoted(&self) -> bool {
self.flags().is_triple_quoted()
}
/// The docstring's prefixes as they exist in the original source code.
pub(crate) fn prefix_str(&self) -> &'a str {
// N.B. This will normally be exactly the same as what you might get from
// `self.flags().prefix().as_str()`, but doing it this way has a few small advantages.
// For example, the casing of the `u` prefix will be preserved if it's a u-string.
&self.source[TextRange::new(
self.start(),
self.start() + self.flags().prefix().text_len(),
)]
}
/// The docstring's "opener" (the string's prefix, if any, and its opening quotes).
pub(crate) fn opener(&self) -> &'a str {
&self.source[TextRange::new(self.start(), self.start() + self.flags().opener_len())]
}
/// The docstring's closing quotes.
pub(crate) fn closer(&self) -> &'a str {
&self.source[TextRange::new(self.end() - self.flags().closer_len(), self.end())]
}
}
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.source[self.range()]
}
}
impl Ranged for DocstringBody<'_> {
fn range(&self) -> TextRange {
self.docstring.expr.content_range()
}
}
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()
}
}