diff --git a/crates/ruff_dev/src/generate_ty_options.rs b/crates/ruff_dev/src/generate_ty_options.rs index 6322f65981..98ea28b936 100644 --- a/crates/ruff_dev/src/generate_ty_options.rs +++ b/crates/ruff_dev/src/generate_ty_options.rs @@ -1,11 +1,13 @@ //! Generate a Markdown-compatible listing of configuration options for `pyproject.toml`. +use std::borrow::Cow; +use std::{fmt::Write, path::PathBuf}; + use anyhow::bail; use itertools::Itertools; use pretty_assertions::StrComparison; -use std::{fmt::Write, path::PathBuf}; - use ruff_options_metadata::{OptionField, OptionSet, OptionsMetadata, Visit}; +use ruff_python_trivia::textwrap; use ty_project::metadata::Options; use crate::{ @@ -165,62 +167,69 @@ fn emit_field(output: &mut String, name: &str, field: &OptionField, parents: &[S let _ = writeln!(output, "**Default value**: `{}`", field.default); output.push('\n'); let _ = writeln!(output, "**Type**: `{}`", field.value_type); + output.push('\n'); output.push_str("**Example usage**:\n\n"); - output.push_str(&format_example( - "pyproject.toml", - &format_header( - field.scope, - field.example, - parents, - ConfigurationFile::PyprojectToml, - ), - field.example, - )); - output.push('\n'); -} -fn format_example(title: &str, header: &str, content: &str) -> String { - if header.is_empty() { - format!("```toml title=\"{title}\"\n{content}\n```\n",) - } else { - format!("```toml title=\"{title}\"\n{header}\n{content}\n```\n",) + for configuration_file in [ConfigurationFile::PyprojectToml, ConfigurationFile::TyToml] { + let (header, example) = + format_snippet(field.scope, field.example, parents, configuration_file); + output.push_str(&format_tab(configuration_file.name(), &header, &example)); + + output.push('\n'); } } +fn format_tab(tab_name: &str, header: &str, content: &str) -> String { + let header = if header.is_empty() { + String::new() + } else { + format!("\n {header}") + }; + format!( + "=== \"{}\"\n\n ```toml{}\n{}\n ```\n", + tab_name, + header, + textwrap::indent(content, " ") + ) +} /// Format the TOML header for the example usage for a given option. /// -/// For example: `[tool.ruff.format]` or `[tool.ruff.lint.isort]`. -fn format_header( +/// For example: `[tool.ty.rules]`. +fn format_snippet<'a>( scope: Option<&str>, - example: &str, + example: &'a str, parents: &[Set], configuration: ConfigurationFile, -) -> String { - let tool_parent = match configuration { - ConfigurationFile::PyprojectToml => Some("tool.ty"), - ConfigurationFile::TyToml => None, - }; +) -> (String, Cow<'a, str>) { + let mut example = Cow::Borrowed(example); - let header = tool_parent + let header = configuration + .parent_table() .into_iter() .chain(parents.iter().filter_map(|parent| parent.name())) .chain(scope) .join("."); + // Rewrite examples starting with `[tool.ty]` or `[[tool.ty]]` to their `ty.toml` equivalent. + if matches!(configuration, ConfigurationFile::TyToml) { + example = example.replace("[tool.ty.", "[").into(); + } + // Ex) `[[tool.ty.xx]]` if example.starts_with(&format!("[[{header}")) { - return String::new(); + return (String::new(), example); } + // Ex) `[tool.ty.rules]` if example.starts_with(&format!("[{header}")) { - return String::new(); + return (String::new(), example); } if header.is_empty() { - String::new() + (String::new(), example) } else { - format!("[{header}]") + (format!("[{header}]"), example) } } @@ -243,10 +252,25 @@ impl Visit for CollectOptionsVisitor { #[derive(Debug, Copy, Clone)] enum ConfigurationFile { PyprojectToml, - #[expect(dead_code)] TyToml, } +impl ConfigurationFile { + const fn name(self) -> &'static str { + match self { + Self::PyprojectToml => "pyproject.toml", + Self::TyToml => "ty.toml", + } + } + + const fn parent_table(self) -> Option<&'static str> { + match self { + Self::PyprojectToml => Some("tool.ty"), + Self::TyToml => None, + } + } +} + #[cfg(test)] mod tests { use anyhow::Result; diff --git a/crates/ty/docs/configuration.md b/crates/ty/docs/configuration.md index b7842d94ad..da093919b1 100644 --- a/crates/ty/docs/configuration.md +++ b/crates/ty/docs/configuration.md @@ -20,11 +20,21 @@ Valid severities are: **Example usage**: -```toml title="pyproject.toml" -[tool.ty.rules] -possibly-unresolved-reference = "warn" -division-by-zero = "ignore" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.rules] + possibly-unresolved-reference = "warn" + division-by-zero = "ignore" + ``` + +=== "ty.toml" + + ```toml + [rules] + possibly-unresolved-reference = "warn" + division-by-zero = "ignore" + ``` --- @@ -48,11 +58,21 @@ Defaults to `true`. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.analysis] -# Disable support for `type: ignore` comments -respect-type-ignore-comments = false -``` +=== "pyproject.toml" + + ```toml + [tool.ty.analysis] + # Disable support for `type: ignore` comments + respect-type-ignore-comments = false + ``` + +=== "ty.toml" + + ```toml + [analysis] + # Disable support for `type: ignore` comments + respect-type-ignore-comments = false + ``` --- @@ -75,10 +95,19 @@ configuration setting. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -extra-paths = ["./shared/my-search-path"] -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + extra-paths = ["./shared/my-search-path"] + ``` + +=== "ty.toml" + + ```toml + [environment] + extra-paths = ["./shared/my-search-path"] + ``` --- @@ -106,10 +135,19 @@ This option can be used to point to virtual or system Python environments. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -python = "./custom-venv-location/.venv" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + python = "./custom-venv-location/.venv" + ``` + +=== "ty.toml" + + ```toml + [environment] + python = "./custom-venv-location/.venv" + ``` --- @@ -133,11 +171,21 @@ If no platform is specified, ty will use the current platform: **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -# Tailor type stubs and conditionalized type definitions to windows. -python-platform = "win32" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + # Tailor type stubs and conditionalized type definitions to windows. + python-platform = "win32" + ``` + +=== "ty.toml" + + ```toml + [environment] + # Tailor type stubs and conditionalized type definitions to windows. + python-platform = "win32" + ``` --- @@ -167,10 +215,19 @@ to reflect the differing contents of the standard library across Python versions **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -python-version = "3.12" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + python-version = "3.12" + ``` + +=== "ty.toml" + + ```toml + [environment] + python-version = "3.12" + ``` --- @@ -195,11 +252,21 @@ it will also be included in the first party search path. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -# Multiple directories (priority order) -root = ["./src", "./lib", "./vendor"] -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + # Multiple directories (priority order) + root = ["./src", "./lib", "./vendor"] + ``` + +=== "ty.toml" + + ```toml + [environment] + # Multiple directories (priority order) + root = ["./src", "./lib", "./vendor"] + ``` --- @@ -215,10 +282,19 @@ bundled as a zip file in the binary **Example usage**: -```toml title="pyproject.toml" -[tool.ty.environment] -typeshed = "/path/to/custom/typeshed" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.environment] + typeshed = "/path/to/custom/typeshed" + ``` + +=== "ty.toml" + + ```toml + [environment] + typeshed = "/path/to/custom/typeshed" + ``` --- @@ -268,15 +344,29 @@ If not specified, defaults to `[]` (excludes no files). **Example usage**: -```toml title="pyproject.toml" -[[tool.ty.overrides]] -exclude = [ - "generated", - "*.proto", - "tests/fixtures/**", - "!tests/fixtures/important.py" # Include this one file -] -``` +=== "pyproject.toml" + + ```toml + [[tool.ty.overrides]] + exclude = [ + "generated", + "*.proto", + "tests/fixtures/**", + "!tests/fixtures/important.py" # Include this one file + ] + ``` + +=== "ty.toml" + + ```toml + [[overrides]] + exclude = [ + "generated", + "*.proto", + "tests/fixtures/**", + "!tests/fixtures/important.py" # Include this one file + ] + ``` --- @@ -296,13 +386,25 @@ If not specified, defaults to `["**"]` (matches all files). **Example usage**: -```toml title="pyproject.toml" -[[tool.ty.overrides]] -include = [ - "src", - "tests", -] -``` +=== "pyproject.toml" + + ```toml + [[tool.ty.overrides]] + include = [ + "src", + "tests", + ] + ``` + +=== "ty.toml" + + ```toml + [[overrides]] + include = [ + "src", + "tests", + ] + ``` --- @@ -320,13 +422,25 @@ severity levels or disable them entirely. **Example usage**: -```toml title="pyproject.toml" -[[tool.ty.overrides]] -include = ["src"] +=== "pyproject.toml" -[tool.ty.overrides.rules] -possibly-unresolved-reference = "ignore" -``` + ```toml + [[tool.ty.overrides]] + include = ["src"] + + [tool.ty.overrides.rules] + possibly-unresolved-reference = "ignore" + ``` + +=== "ty.toml" + + ```toml + [[overrides]] + include = ["src"] + + [overrides.rules] + possibly-unresolved-reference = "ignore" + ``` --- @@ -386,15 +500,29 @@ to re-include `dist` use `exclude = ["!dist"]` **Example usage**: -```toml title="pyproject.toml" -[tool.ty.src] -exclude = [ - "generated", - "*.proto", - "tests/fixtures/**", - "!tests/fixtures/important.py" # Include this one file -] -``` +=== "pyproject.toml" + + ```toml + [tool.ty.src] + exclude = [ + "generated", + "*.proto", + "tests/fixtures/**", + "!tests/fixtures/important.py" # Include this one file + ] + ``` + +=== "ty.toml" + + ```toml + [src] + exclude = [ + "generated", + "*.proto", + "tests/fixtures/**", + "!tests/fixtures/important.py" # Include this one file + ] + ``` --- @@ -427,13 +555,25 @@ matches `/src` and not `/test/src`). **Example usage**: -```toml title="pyproject.toml" -[tool.ty.src] -include = [ - "src", - "tests", -] -``` +=== "pyproject.toml" + + ```toml + [tool.ty.src] + include = [ + "src", + "tests", + ] + ``` + +=== "ty.toml" + + ```toml + [src] + include = [ + "src", + "tests", + ] + ``` --- @@ -449,10 +589,19 @@ Enabled by default. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.src] -respect-ignore-files = false -``` +=== "pyproject.toml" + + ```toml + [tool.ty.src] + respect-ignore-files = false + ``` + +=== "ty.toml" + + ```toml + [src] + respect-ignore-files = false + ``` --- @@ -478,10 +627,19 @@ it will also be included in the first party search path. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.src] -root = "./app" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.src] + root = "./app" + ``` + +=== "ty.toml" + + ```toml + [src] + root = "./app" + ``` --- @@ -499,11 +657,21 @@ Defaults to `false`. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.terminal] -# Error if ty emits any warning-level diagnostics. -error-on-warning = true -``` +=== "pyproject.toml" + + ```toml + [tool.ty.terminal] + # Error if ty emits any warning-level diagnostics. + error-on-warning = true + ``` + +=== "ty.toml" + + ```toml + [terminal] + # Error if ty emits any warning-level diagnostics. + error-on-warning = true + ``` --- @@ -519,10 +687,19 @@ Defaults to `full`. **Example usage**: -```toml title="pyproject.toml" -[tool.ty.terminal] -output-format = "concise" -``` +=== "pyproject.toml" + + ```toml + [tool.ty.terminal] + output-format = "concise" + ``` + +=== "ty.toml" + + ```toml + [terminal] + output-format = "concise" + ``` ---