дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
feat(php): show PHPStan errors in the editor on save (#1518)
PHPStan results were only visible in a terminal. Saving a PHP file in a trusted project with vendor/bin/phpstan now runs phpstan analyse --error-format=json on the saved files (debounced, from the project root, on the selected PHP with XDEBUG_MODE=off) and shows its errors at their lines in the editor and Problems, with the PHPStan identifier as the code; saving the fixed code clears them. Settings > Editor > Inspections lists PHPStan under PHP with each reported identifier, so rules can be turned off or re-graded, and Settings > Languages & Frameworks > PHP > PHPStan selects another configuration file. A failing run is reported once as a notification; a newer save cancels a check in progress, and Restricted Mode stops it. Closes #1212 Co-authored-by: DanielADK <[email protected]>
Цей коміт міститься в:
1 джерело
324dded5c1
коміт
c495c09416
24 files changed
+1163
-17
No files matched your search
@@ -62,6 +62,42 @@ describe("plugin inspections", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("PHPStan inspections", () => {
|
||||
it("list each identifier below PHPStan, from seen codes and stored overrides", () => {
|
||||
const tree = buildInspectionTree(
|
||||
[{ name: "PHP", servers: ["intelephense"] }],
|
||||
[
|
||||
{
|
||||
producer: "phpstan",
|
||||
code: "return.type",
|
||||
source: "PHPStan",
|
||||
sample: "Should return int but returns string.",
|
||||
},
|
||||
{ producer: "build", code: "E0308", source: "rustc", sample: "" },
|
||||
],
|
||||
[{ "builtin:phpstan:constant.notFound": { enabled: false }, "builtin:build:E1": { enabled: false } }],
|
||||
);
|
||||
const php = find(tree, "group:PHP")!;
|
||||
expect(php.children.map((n) => n.id)).toEqual(["lsp:intelephense", "builtin:phpstan"]);
|
||||
const phpstan = find(tree, "PHP/builtin:phpstan")!;
|
||||
expect(phpstan.children.map((n) => [n.id, n.name])).toEqual([
|
||||
["builtin:phpstan:constant.notFound", "constant.notFound"],
|
||||
["builtin:phpstan:return.type", "return.type"],
|
||||
]);
|
||||
expect(phpstan.children[1]!.description).toBe(
|
||||
"PHPStan problems with code return.type.\n\nExample: Should return int but returns string.",
|
||||
);
|
||||
expect(pathToInspection(tree, "builtin:phpstan:return.type")).toEqual([
|
||||
"group:PHP",
|
||||
"PHP/builtin:phpstan",
|
||||
"PHP/builtin:phpstan:return.type",
|
||||
]);
|
||||
// Other built-in checks stay single inspections.
|
||||
expect(find(tree, "General/builtin:build")!.children).toEqual([]);
|
||||
expect(checkState({ "builtin:phpstan:constant.notFound": { enabled: false } }, phpstan)).toBe("mixed");
|
||||
});
|
||||
});
|
||||
|
||||
describe("inspections tree", () => {
|
||||
const tree = buildInspectionTree(languages, seen, [{ "lsp:ruff:E501": { severity: "typo" } }]);
|
||||
|
||||
@@ -72,6 +108,7 @@ describe("inspections tree", () => {
|
||||
"Java",
|
||||
"JavaScript",
|
||||
"Other",
|
||||
"PHP",
|
||||
"Proofreading",
|
||||
"Python",
|
||||
"SQL",
|
||||
|
||||
@@ -63,7 +63,12 @@ describe("languages settings", () => {
|
||||
languageLevel: "",
|
||||
includePaths: [],
|
||||
stubs: [],
|
||||
phpstanConfig: "",
|
||||
});
|
||||
expect(validateLanguages({ php: { phpstanConfig: " build/phpstan.neon " } }).php.phpstanConfig).toBe(
|
||||
"build/phpstan.neon",
|
||||
);
|
||||
expect(validateLanguages({ php: { phpstanConfig: 3 } }).php.phpstanConfig).toBe("");
|
||||
const lists = validateLanguages({ php: { includePaths: [" ../platform ", "", 3, "../platform"], stubs: "redis" } });
|
||||
expect(lists.php.includePaths).toEqual(["../platform"]);
|
||||
expect(lists.php.stubs).toEqual([]);
|
||||
|
||||
@@ -162,6 +162,16 @@ export function PhpPage() {
|
||||
/>
|
||||
</Row>
|
||||
</Section>
|
||||
<Section title="PHPStan">
|
||||
<PathInput
|
||||
k="languages.php.phpstanConfig"
|
||||
label="Configuration file"
|
||||
placeholder="phpstan.neon, phpstan.neon.dist or phpstan.dist.neon"
|
||||
hint="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."
|
||||
title="Select PHPStan Configuration File"
|
||||
file
|
||||
/>
|
||||
</Section>
|
||||
<Section title="CLI Interpreter">
|
||||
<SdkRow
|
||||
k="languages.php.interpreter"
|
||||
|
||||
@@ -60,18 +60,22 @@ function serverNode(
|
||||
: languages
|
||||
? tx`Problems the ${server} language server reports in ${languages} files. Severity "As reported" keeps the level the server assigns; a code below can override it.`
|
||||
: tx`Problems the ${server} language server reports. Severity "As reported" keeps the level the server assigns; a code below can override it.`,
|
||||
children: [...codes]
|
||||
.sort((a, b) => a.code.localeCompare(b.code, undefined, { numeric: true }))
|
||||
.map((c) => ({
|
||||
key: `${group}/${codeInspectionId(id, c.code)}`,
|
||||
id: codeInspectionId(id, c.code),
|
||||
name: c.code,
|
||||
description: codeDescription(plugin ?? server, c),
|
||||
children: [],
|
||||
})),
|
||||
children: codeNodes(group, id, plugin ?? server, codes),
|
||||
};
|
||||
}
|
||||
|
||||
function codeNodes(group: string, id: string, owner: string, codes: readonly SeenCode[]): InspectionNode[] {
|
||||
return [...codes]
|
||||
.sort((a, b) => a.code.localeCompare(b.code, undefined, { numeric: true }))
|
||||
.map((c) => ({
|
||||
key: `${group}/${codeInspectionId(id, c.code)}`,
|
||||
id: codeInspectionId(id, c.code),
|
||||
name: c.code,
|
||||
description: codeDescription(owner, c),
|
||||
children: [],
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* The inspection tree: one group per language that has language servers, the
|
||||
* built-in checks under their category, and servers known only from seen
|
||||
@@ -98,6 +102,9 @@ export function buildInspectionTree(
|
||||
const m = /^lsp:(plugin:[^:]+|[^:]+)(?::(.+))?$/.exec(id);
|
||||
if (m?.[2]) addCode({ producer: m[1]!, code: m[2], source: "", sample: "" });
|
||||
else if (m && !codes.has(m[1]!)) codes.set(m[1]!, new Map());
|
||||
const builtin = BUILTIN_INSPECTIONS.find((b) => b.byCode && id.startsWith(`${b.id}:`));
|
||||
if (builtin)
|
||||
addCode({ producer: builtin.producer, code: id.slice(builtin.id.length + 1), source: "", sample: "" });
|
||||
}
|
||||
}
|
||||
const servedBy = new Map<string, string[]>();
|
||||
@@ -125,7 +132,7 @@ export function buildInspectionTree(
|
||||
id: b.id,
|
||||
name: b.name,
|
||||
description: b.description,
|
||||
children: [],
|
||||
children: b.byCode ? codeNodes(b.group, b.id, b.name, [...(codes.get(b.producer)?.values() ?? [])]) : [],
|
||||
});
|
||||
}
|
||||
for (const [producer, list] of codes) {
|
||||
|
||||
@@ -153,6 +153,8 @@ export const LANGUAGE_PAGES: PageSpec[] = [
|
||||
"php",
|
||||
"composer",
|
||||
"phpunit",
|
||||
"phpstan",
|
||||
"phpstan.neon",
|
||||
"xampp",
|
||||
"herd",
|
||||
"homebrew",
|
||||
|
||||
Reference in new issue
Заблокувати користувача