Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ All notable changes to the "prettier-vscode" extension will be documented in thi
- Added support for TypeScript config files (`.prettierrc.ts`, `.prettierrc.cts`, `.prettierrc.mts`, `prettier.config.ts`, etc.) introduced in Prettier 3.5.0 - Thanks to [@dr2009](https://github.com/dr2009)
- Added `source.fixAll.prettier` code action for use with `editor.codeActionsOnSave` to run Prettier before other formatters like ESLint (#1277)
- Fixed issue where unnecessary TextEdits were applied when document was already formatted, which could cause spurious changes or cursor positioning issues (#3232)
- Added problem matchers for Prettier's `--check` output to display formatting issues in VS Code's Problems panel when running Prettier as a task - Thanks to [@riotrah](https://github.com/riotrah) (#2554)

## [11.0.0]

Expand Down
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,51 @@ If you would like to format a document that is configured to be ignored by Prett

The recommended way of integrating with linters is to let Prettier do the formatting and configure the linter to not deal with formatting rules. You can find instructions on how to configure each linter on the Prettier docs site. You can then use each of the linting extensions as you normally would. For details refer to the [Prettier documentation](https://prettier.io/docs/en/integrating-with-linters.html).

## Problem Matchers

This extension provides problem matchers for use with VS Code tasks that run `prettier --check`. Problem matchers scan task output and create entries in VS Code's Problems panel for any issues found.

### Available Problem Matchers

- **`$prettier`** - Matches Prettier syntax errors with line and column information
- **`$prettier-watch`** - Same as `$prettier` but for watch mode tasks (e.g., with `onchange`)
- **`$prettier-warn`** - Matches file-level warnings for unformatted files
- **`$prettier-warn-watch`** - Same as `$prettier-warn` but for watch mode tasks

### Usage

To use these problem matchers, add them to your task configuration in `.vscode/tasks.json`:

```json
{
"version": "2.0.0",
"tasks": [
{
"label": "Prettier Check",
"type": "shell",
"command": "npx prettier --check .",
"problemMatcher": ["$prettier", "$prettier-warn"]
}
]
}
```

For watch mode with a tool like `onchange`:

```json
{
"label": "Prettier Check (Watch)",
"type": "shell",
"command": "npx onchange '**/*.js' -- prettier --check {{changed}}",
"problemMatcher": ["$prettier-watch", "$prettier-warn-watch"],
"isBackground": true
}
```

The problem matchers will parse Prettier's output and display:
- Syntax errors with file, line, column, and error message
- Warnings for files that need formatting

### Using Code Actions on Save

You can use VS Code's `editor.codeActionsOnSave` to run Prettier before other formatters like ESLint. This is useful when you want to format with Prettier first and then apply ESLint fixes.
Expand Down
26 changes: 26 additions & 0 deletions THIRDPARTY.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,29 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

---

Problem Matcher Definitions

The MIT License (MIT)

Copyright (c) 2022-2022 Rayat Rahman

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
54 changes: 54 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,60 @@
"title": "%ext.command.forceFormatDocument.title%",
"when": "editorFocus"
}
],
"problemMatchers": [
{
"name": "prettier",
"label": "Prettier check problems",
"owner": "prettier",
"source": "prettier style check",
"applyTo": "closedDocuments",
"fileLocation": [
"relative",
"${workspaceRoot}"
],
"severity": "error",
"pattern": {
"regexp": "\\[(error)\\] ([^:]+): (.+) \\((\\d+):(\\d+)\\)",
"kind": "location",
"severity": 1,
"file": 2,
"message": 3,
"line": 4,
"column": 5
}
},
{
"base": "$prettier",
"name": "prettier-watch",
"label": "Prettier check watch problems (prettier + onchange watch mode)",
"background": {
"activeOnStart": true,
"beginsPattern": "Checking formatting",
"endsPattern": "(\\[warn\\] Code style issues found in the above file\\(s\\))|(All matched files use Prettier code style!)"
}
},
{
"base": "$prettier",
"name": "prettier-warn",
"label": "Prettier check warning problems (prettier file-level warnings)",
"severity": "warning",
"pattern": {
"regexp": "\\[warn\\] ([^\\s]+\\.[a-zA-Z0-9.-]+)",
"kind": "file",
"file": 1
}
},
{
"base": "$prettier-warn",
"name": "prettier-warn-watch",
"label": "Prettier check warning watch problems (prettier file-level warnings + onchange watch mode)",
"background": {
"activeOnStart": true,
"beginsPattern": "Checking formatting",
"endsPattern": "(\\[warn\\] Code style issues found in the above file\\(s\\))|(All matched files use Prettier code style!)"
}
}
]
},
"__metadata": {
Expand Down
215 changes: 215 additions & 0 deletions src/test/suite/problemMatcher.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
import * as assert from "assert";
import * as vscode from "vscode";
import { ensureExtensionActivated } from "./testUtils.js";

const EXTENSION_ID = "prettier.prettier-vscode";

/**
* Helper to get problem matchers from extension
*/
function getProblemMatchers() {
const extension = vscode.extensions.getExtension(EXTENSION_ID);
assert.ok(extension, "Extension should be available");
return extension.packageJSON.contributes.problemMatchers;
}

Comment on lines +6 to +15
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test file should follow the project convention of using ensureExtensionActivated() from testUtils.js in a before() hook at the top level, rather than manually checking and activating the extension in each test. This is the pattern used consistently across all other test files in the suite (e.g., format.test.ts, config.test.ts, module.test.ts, etc.).

Recommended refactoring:

import { ensureExtensionActivated } from "./testUtils.js";

describe("Problem Matchers", () => {
  before(async () => {
    await ensureExtensionActivated();
  });

  it("should have prettier problem matcher registered", () => {
    const extension = vscode.extensions.getExtension("prettier.prettier-vscode");
    assert.ok(extension, "Extension should be available");
    // ... rest of test
  });

This ensures consistent test setup and avoids redundant activation logic in every test.

Copilot uses AI. Check for mistakes.
/**
* Helper to get a specific problem matcher by name
*/
function getMatcher(name: string) {
const matchers = getProblemMatchers();
const matcher = matchers.find((m: { name: string }) => m.name === name);
assert.ok(matcher, `Should have '${name}' problem matcher`);
return matcher;
}

describe("Problem Matchers", () => {
before(async () => {
await ensureExtensionActivated();
});

it("should have problem matchers registered", () => {
const matchers = getProblemMatchers();
assert.ok(Array.isArray(matchers), "Problem matchers should be an array");
assert.ok(matchers.length > 0, "Should have at least one problem matcher");
});

it("should have prettier problem matcher registered", () => {
const prettierMatcher = getMatcher("prettier");
assert.strictEqual(
prettierMatcher.owner,
"prettier",
"Owner should be 'prettier'",
);
assert.ok(prettierMatcher.pattern, "Should have a pattern");
assert.ok(prettierMatcher.pattern.regexp, "Pattern should have a regexp");
});

it("should have prettier-warn problem matcher registered", () => {
const warnMatcher = getMatcher("prettier-warn");
assert.strictEqual(
warnMatcher.severity,
"warning",
"Severity should be 'warning'",
);
assert.ok(warnMatcher.pattern, "Should have a pattern");
});

it("should have prettier-watch problem matcher registered", () => {
const watchMatcher = getMatcher("prettier-watch");
assert.ok(
watchMatcher.background,
"Watch matcher should have background mode",
);
});

it("should have prettier-warn-watch problem matcher registered", () => {
const warnWatchMatcher = getMatcher("prettier-warn-watch");
assert.ok(
warnWatchMatcher.background,
"Watch matcher should have background mode",
);
});

describe("Pattern matching", () => {
it("prettier pattern should match error output with line and column", () => {
const prettierMatcher = getMatcher("prettier");
const pattern = new RegExp(prettierMatcher.pattern.regexp);

// Test cases from actual prettier output
const testCases = [
{
input: "[error] test.js: SyntaxError: Unexpected token (3:1)",
expected: {
severity: "error",
file: "test.js",
message: "SyntaxError: Unexpected token",
line: "3",
column: "1",
},
},
{
input:
'[error] src/file.d.ts: SyntaxError: Unexpected token, expected "," (10:5)',
expected: {
severity: "error",
file: "src/file.d.ts",
message: 'SyntaxError: Unexpected token, expected ","',
line: "10",
column: "5",
},
},
];

testCases.forEach((testCase) => {
const match = testCase.input.match(pattern);
assert.ok(match, `Should match: ${testCase.input}`);
assert.strictEqual(
match[1],
testCase.expected.severity,
"Should extract severity",
);
assert.strictEqual(
match[2],
testCase.expected.file,
"Should extract file",
);
assert.strictEqual(
match[3],
testCase.expected.message,
"Should extract message",
);
assert.strictEqual(
match[4],
testCase.expected.line,
"Should extract line",
);
assert.strictEqual(
match[5],
testCase.expected.column,
"Should extract column",
);
});
});

it("prettier-warn pattern should match warning output", () => {
const warnMatcher = getMatcher("prettier-warn");
const pattern = new RegExp(warnMatcher.pattern.regexp);

// Test cases from actual prettier output
const shouldMatch = [
{ input: "[warn] test.js", expected: "test.js" },
{ input: "[warn] src/bad.js", expected: "src/bad.js" },
{ input: "[warn] file.test.tsx", expected: "file.test.tsx" },
{ input: "[warn] component.d.ts", expected: "component.d.ts" },
{ input: "[warn] styles.module.css", expected: "styles.module.css" },
// Edge cases: directories with dots
{ input: "[warn] src/v2.0/file.js", expected: "src/v2.0/file.js" },
{
input: "[warn] dist/1.2.3/bundle.js",
expected: "dist/1.2.3/bundle.js",
},
// Edge cases: multiple dots in filename
{
input: "[warn] component.test.d.ts",
expected: "component.test.d.ts",
},
{ input: "[warn] api.v2.spec.ts", expected: "api.v2.spec.ts" },
// Edge cases: underscores and hyphens
{ input: "[warn] my-component_v2.js", expected: "my-component_v2.js" },
{
input: "[warn] user_profile-api.ts",
expected: "user_profile-api.ts",
},
{
input: "[warn] test_file-name.spec.js",
expected: "test_file-name.spec.js",
},
];

shouldMatch.forEach((testCase) => {
const match = testCase.input.match(pattern);
assert.ok(match, `Should match: ${testCase.input}`);
assert.strictEqual(
match[1],
testCase.expected,
`Should extract file: ${testCase.expected}`,
);
});

// Test cases that should NOT match (summary messages)
const shouldNotMatch = [
"[warn] Code style issues found in the above file.",
"[warn] Code style issues found in 3 files.",
"[warn] All matched files use Prettier code style!",
];

shouldNotMatch.forEach((testCase) => {
const match = testCase.input.match(pattern);
assert.ok(!match, `Should NOT match: ${testCase}`);
});
});

it("background patterns should match start and end markers", () => {
const watchMatcher = getMatcher("prettier-watch");
const beginsPattern = new RegExp(watchMatcher.background.beginsPattern);
const endsPattern = new RegExp(watchMatcher.background.endsPattern);

// Test begin pattern
assert.ok(
beginsPattern.test("Checking formatting..."),
"Should match begin pattern",
);

// Test end patterns
assert.ok(
endsPattern.test("[warn] Code style issues found in the above file(s)"),
"Should match end pattern for warnings",
);
assert.ok(
endsPattern.test("All matched files use Prettier code style!"),
"Should match end pattern for success",
);
});
});
Comment on lines +208 to +214
Copy link

Copilot AI Dec 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test cases for file path matching should include edge cases to ensure the regex pattern handles various file path formats correctly:

  • File paths with directories containing dots (e.g., [warn] src/v2.0/file.js)
  • File names with multiple dots (e.g., [warn] component.test.d.ts)
  • File paths with underscores or hyphens (e.g., [warn] my-component_v2.js)

These edge cases would help validate that the regex pattern in package.json (line 507) correctly matches all valid file paths while properly excluding summary messages.

Copilot uses AI. Check for mistakes.
});