feat(python): support Ruff formatting and config directories (#1573)

Ruff is now an external formatter for Python next to the Ruff language server: Settings › Languages & Frameworks › Ruff has the executable path, a configuration directory (or file), the provider preference, enabled languages, run-on-save, and Reformat with Ruff. Formatting runs on the unsaved document through stdin (with Ruff's --range for selections, in 1-based code-point columns) and never writes the file first. The configuration directory is shared with Ruff LSP, which can then start without a root marker. Tools that can't format a language are rejected even when requested.

Co-authored-by: muutot <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Цей коміт міститься в:
authored and GitHub committed 2026-11-05 23:41:44 +08:00
1 джерело f35026f31c
коміт a275239138
24 files changed
+643 -69

No files matched your search

+13 -3
Переглянути файл
@@ -25,9 +25,10 @@ const DEFAULT_GO_BUILD_TAGS: GoBuildTags = {
experiments: "",
};
/** An external formatter (Prettier, oxfmt) and the languages it formats. */
/** An external formatter (Prettier, oxfmt, Ruff) and the languages it formats. */
interface ExternalFormatterValid {
path: string;
configurationDirectory: string;
languages: string[];
prefer: boolean;
runOnSave: boolean;
@@ -35,6 +36,7 @@ interface ExternalFormatterValid {
const DEFAULT_FORMATTER: ExternalFormatterValid = {
path: "",
configurationDirectory: "",
languages: [
"markdown",
"yaml",
@@ -55,6 +57,11 @@ const DEFAULT_FORMATTER: ExternalFormatterValid = {
runOnSave: false,
};
const DEFAULT_RUFF_FORMATTER: ExternalFormatterValid = {
...DEFAULT_FORMATTER,
languages: ["python"],
};
/** `languages.php.languageLevel` values; `""` = Auto (from `composer.json`). */
export const PHP_LANGUAGE_LEVELS = [
"",
@@ -99,6 +106,7 @@ export interface LanguagesValid {
typescript: { version: string };
prettier: ExternalFormatterValid;
oxfmt: ExternalFormatterValid;
ruff: ExternalFormatterValid;
java: {
jdk: string;
jdtlsJdk: string;
@@ -159,6 +167,7 @@ export const DEFAULT_LANGUAGES: LanguagesValid = {
typescript: { version: "bundled" },
prettier: DEFAULT_FORMATTER,
oxfmt: DEFAULT_FORMATTER,
ruff: DEFAULT_RUFF_FORMATTER,
java: {
jdk: "",
jdtlsJdk: "",
@@ -228,11 +237,11 @@ const folders = (v: unknown): string[] =>
const folderKey = (stored: string) => (stored === "." ? "" : stored);
const storedFolder = (rel: string) => normalizeFolder(rel) || ".";
function validateFormatter(raw: unknown): ExternalFormatterValid {
const d = DEFAULT_FORMATTER;
function validateFormatter(raw: unknown, d = DEFAULT_FORMATTER): ExternalFormatterValid {
const f = obj(raw);
return {
path: text(f.path, d.path),
configurationDirectory: text(f.configurationDirectory, d.configurationDirectory),
languages: Array.isArray(f.languages)
? [...new Set(f.languages.filter((v): v is string => typeof v === "string"))]
: d.languages,
@@ -299,6 +308,7 @@ export function validateLanguages(raw: unknown): LanguagesValid {
typescript: { version: text(ts.version, d.typescript.version) || d.typescript.version },
prettier: validateFormatter(l.prettier),
oxfmt: validateFormatter(l.oxfmt),
ruff: validateFormatter(l.ruff, DEFAULT_RUFF_FORMATTER),
java: {
jdk: text(java.jdk, d.java.jdk),
jdtlsJdk: text(java.jdtlsJdk, d.java.jdtlsJdk),
+13
Переглянути файл
@@ -13,6 +13,19 @@
"phpstan.neon, phpstan.neon.dist or phpstan.dist.neon": "phpstan.neon, phpstan.neon.dist or phpstan.dist.neon",
"The configuration vendor/bin/phpstan checks saved files with. Empty uses phpstan.neon, phpstan.neon.dist or phpstan.dist.neon in the project root. Absolute, or relative to the project.": "The configuration vendor/bin/phpstan checks saved files with. Empty uses phpstan.neon, phpstan.neon.dist or phpstan.dist.neon in the project root. Absolute, or relative to the project.",
"Select PHPStan Configuration File": "Select PHPStan Configuration File",
"Reformat with Ruff": "Reformat with Ruff",
"Ruff path": "Ruff path",
"Auto: managed installation, then PATH. An explicit path overrides auto detection.": "Auto: managed installation, then PATH. An explicit path overrides auto detection.",
"Configuration directory": "Configuration directory",
"Project configuration": "Project configuration",
"Select Ruff Configuration Directory": "Select Ruff Configuration Directory",
"Empty uses the nearest project pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. A configured directory must contain one of them.": "Empty uses the nearest project pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. A configured directory must contain one of them.",
"Ruff reads the nearest pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. Use it before the language server when preferred.": "Ruff reads the nearest pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. Use it before the language server when preferred.",
"Run Ruff on save for enabled languages when it is the selected formatting provider.": "Run Ruff on save for enabled languages when it is the selected formatting provider.",
"Ruff formatting failed": "Ruff formatting failed",
"Ruff formatting timed out": "Ruff formatting timed out",
"Ruff configuration path does not exist: {path}": "Ruff configuration path does not exist: {path}",
"Ruff configuration directory has no ruff.toml, .ruff.toml, or pyproject.toml with [tool.ruff]: {path}": "Ruff configuration directory has no ruff.toml, .ruff.toml, or pyproject.toml with [tool.ruff]: {path}",
"Show lines added and deleted": "Show lines added and deleted",
"{added} line added": "{added} line added",
"{added} lines added": "{added} lines added",
+13
Переглянути файл
@@ -1714,15 +1714,24 @@
"Reformat Code": "重新格式化代码",
"Reformat with Prettier": "使用 Prettier 重新格式化",
"Reformat with oxfmt": "使用 oxfmt 重新格式化",
"Reformat with Ruff": "使用 Ruff 重新格式化",
"Prettier path": "Prettier 路径",
"oxfmt path": "oxfmt 路径",
"Ruff path": "Ruff 路径",
"Auto: nearest project node_modules, managed installation, then PATH. An explicit path overrides auto detection.": "自动:依次查找最近的项目 node_modules、受管安装和 PATH。指定路径将覆盖自动检测。",
"Auto: managed installation, then PATH. An explicit path overrides auto detection.": "自动:依次查找受管安装和 PATH。指定路径将覆盖自动检测。",
"Configuration directory": "配置目录",
"Project configuration": "项目配置",
"Select Ruff Configuration Directory": "选择 Ruff 配置目录",
"Empty uses the nearest project pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. A configured directory must contain one of them.": "留空时使用最近的 pyproject.toml([tool.ruff])、ruff.toml 或 .ruff.toml。指定目录必须包含其中一种配置。",
"Formatting provider": "格式化工具",
"Automatic (language server first)": "自动(优先语言服务器)",
"Run on save": "保存时运行",
"Run Prettier on save for enabled languages when it is the selected formatting provider.": "对已启用的语言,在选择 Prettier 作为格式化工具时于保存时运行。",
"Run oxfmt on save for enabled languages when it is the selected formatting provider.": "对已启用的语言,在选择 oxfmt 作为格式化工具时于保存时运行。",
"Run Ruff on save for enabled languages when it is the selected formatting provider.": "对已启用的语言,在选择 Ruff 作为格式化工具时于保存时运行。",
"oxfmt formats an enabled language instead of Prettier below an oxfmt configuration file (.oxfmtrc.json, oxfmt.config.ts) or when it is the selected provider.": "在 oxfmt 配置文件(.oxfmtrc.json、oxfmt.config.ts)所在目录及其子目录中,或选择 oxfmt 作为格式化工具时,oxfmt 将代替 Prettier 格式化已启用的语言。",
"Ruff reads the nearest pyproject.toml ([tool.ruff]), ruff.toml, or .ruff.toml. Use it before the language server when preferred.": "Ruff 会读取最近的 pyproject.toml([tool.ruff])、ruff.toml 或 .ruff.toml。选择优先使用 Ruff 时,将优先于语言服务器。",
"Enabled languages": "启用的语言",
"Reformat Code needs {name}. Install it via {method}?": "重新格式化代码需要 {name}。是否通过 {method} 安装?",
"Install Missing Tool": "安装缺失的工具",
@@ -1730,6 +1739,10 @@
"Prettier formatting timed out": "Prettier 格式化超时",
"oxfmt formatting failed": "oxfmt 格式化失败",
"oxfmt formatting timed out": "oxfmt 格式化超时",
"Ruff formatting failed": "Ruff 格式化失败",
"Ruff formatting timed out": "Ruff 格式化超时",
"Ruff configuration path does not exist: {path}": "Ruff 配置路径不存在:{path}",
"Ruff configuration directory has no ruff.toml, .ruff.toml, or pyproject.toml with [tool.ruff]: {path}": "Ruff 配置目录中没有 ruff.toml、.ruff.toml 或包含 [tool.ruff] 的 pyproject.toml:{path}",
"{name} does not support selection formatting for this language; formatted the whole file": "{name} 不支持此语言的选区格式化,已格式化整个文件",
"Formatting timed out": "格式化超时",
"{name} executable": "{name} 可执行文件",
+1
Переглянути файл
@@ -91,6 +91,7 @@ const UNTRANSLATED = new Set<string>([
"CMake",
"Prettier",
"oxfmt",
"Ruff",
"IntelliJ IDEA",
"PHPStan",
// Go environment variables and placeholder values