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
4 changes: 2 additions & 2 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"java.debugger.launch.modulePaths.auto": "Automatically resolve the module paths of current project.",
"java.debugger.launch.modulePaths.runtime": "The module paths within 'runtime' scope of current project.",
"java.debugger.launch.modulePaths.test": "The module paths within 'test' scope of current project.",
"java.debugger.launch.modulePaths.exclude": "The path after '!' will be excluded from the modulePaths.",
"java.debugger.launch.modulePaths.exclude": "The path after '!' will be excluded from the modulePaths. A slash (forwards or backwards) will treat the path as an exact match.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This should say “A trailing slash or backslash will treat the path as an exact match.” The current wording suggests that any slash triggers exact matching, while the implementation only checks the final character. Please make the same change to the classPaths.exclude description.

"java.debugger.launch.classPaths.description": "The classpaths for launching the JVM. If not specified, the debugger will automatically resolve from current project.",
"java.debugger.launch.classPaths.auto": "Automatically resolve the classpaths of current project.",
"java.debugger.launch.classPaths.runtime": "The classpaths within 'runtime' scope of current project.",
"java.debugger.launch.classPaths.test": "The classpaths within 'test' scope of current project.",
"java.debugger.launch.classPaths.exclude": "The path after '!' will be excluded from the classpaths.",
"java.debugger.launch.classPaths.exclude": "The path after '!' will be excluded from the classpaths. A slash (forwards or backwards) will treat the path as an exact match.",
"java.debugger.launch.sourcePaths.description": "The extra source directories of the program. The debugger looks for source code from project settings by default. This option allows the debugger to look for source code in extra directories.",
"java.debugger.launch.encoding.description": "The file.encoding setting for the JVM. Possible values can be found in https://docs.oracle.com/javase/8/docs/technotes/guides/intl/encoding.doc.html.",
"java.debugger.launch.cwd.description": "The working directory of the program. Defaults to the current workspace root.",
Expand Down
20 changes: 15 additions & 5 deletions src/configurationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,25 +490,35 @@ export class JavaDebugConfigurationProvider implements vscode.DebugConfiguration
const excludes: Map<string, boolean> = new Map<string, boolean>();
for (const p of paths) {
if (p.startsWith("!")) {
let exclude = p.substr(1);
let exclude = p.slice(1);
let isDirect: boolean;

if (/[\\/]$/.test(exclude)) {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

before normalization, and using the exact code-block suggested, per feedback!

exclude = exclude.slice(0, -1);
isDirect = true;
} else {
isDirect = this.isFilePath(exclude);
}

if (!path.isAbsolute(exclude)) {
exclude = path.join(folder?.uri.fsPath || "", exclude);
}

// use Uri to normalize the fs path
excludes.set(vscode.Uri.file(exclude).fsPath, this.isFilePath(exclude));
excludes.set(vscode.Uri.file(exclude).fsPath, isDirect);
continue;
}

result.push(vscode.Uri.file(p).fsPath);
}

return result.filter((r) => {
for (const [excludedPath, isFile] of excludes.entries()) {
if (isFile && r === excludedPath) {
for (const [excludedPath, isDirect] of excludes.entries()) {
if (isDirect && r === excludedPath) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Could you normalize trailing separators consistently on both sides before this exact comparison? Currently, ["C:\\workspace\\lib\\", "!C:\\workspace\\lib\\"] leaves the separator on r but removes it from excludedPath, so the directory is not excluded. Please also add forward-slash and backslash tests where the included entry ends in a separator, taking care not to alter filesystem roots.

return false;
}

if (!isFile && r.startsWith(excludedPath)) {
if (!isDirect && r.startsWith(excludedPath)) {
return false;
}
}
Expand Down
96 changes: 96 additions & 0 deletions test/configurationProvider.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

import * as assert from "assert";
import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import * as vscode from "vscode";

import { JavaDebugConfigurationProvider } from "../src/configurationProvider";

interface TestWorkspace {
root: string;
folder: vscode.WorkspaceFolder;
libDir: string;
jarPath: string;
}

type FilterExcluded = (
folder: vscode.WorkspaceFolder | undefined,
paths: string[],
) => Promise<string[]>;

function createTestWorkspace(): TestWorkspace {
const root = fs.mkdtempSync(path.join(os.tmpdir(), "java-debug-cp-test-"));
const libDir = path.join(root, "lib");
fs.mkdirSync(libDir);
const jarPath = path.join(libDir, "foo.jar");
fs.writeFileSync(jarPath, "");
return {
root,
folder: {
uri: vscode.Uri.file(root),
name: "test-workspace",
index: 0,
},
libDir,
jarPath,
};
}

function getFilterExcluded(provider: JavaDebugConfigurationProvider): FilterExcluded {
return (provider as unknown as { filterExcluded: FilterExcluded }).filterExcluded.bind(provider);
}

suite("JavaDebugConfigurationProvider", () => {
const workspaces: TestWorkspace[] = [];

suiteSetup(() => {
// configurationProvider requires ../package.json relative to out/src/
const outPackageJson = path.join(__dirname, "../package.json");
if (!fs.existsSync(outPackageJson)) {
fs.copyFileSync(path.join(__dirname, "../../package.json"), outPackageJson);
}
});

teardown(() => {
while (workspaces.length > 0) {
const workspace = workspaces.pop()!;
fs.rmSync(workspace.root, { recursive: true, force: true });
}
});

suite("filterExcluded exact-match exclusions", () => {
async function assertExactDirectoryExclusion(
excludeSuffix: "\\" | "/",
label: string,
): Promise<void> {
const workspace = createTestWorkspace();
workspaces.push(workspace);

const libDirFs = vscode.Uri.file(workspace.libDir).fsPath;
const jarFs = vscode.Uri.file(workspace.jarPath).fsPath;
const filterExcluded = getFilterExcluded(new JavaDebugConfigurationProvider());
const result = await filterExcluded(workspace.folder, [
libDirFs,
jarFs,
`!${workspace.libDir}${excludeSuffix}`,
]);

assert.deepStrictEqual(
result,
[jarFs],
`${label}: trailing slash should exact-exclude only the directory entry, not paths beneath it`,
);
}

test("treats a trailing backslash as an exact match (Windows-style paths)", async () => {
await assertExactDirectoryExclusion("\\", "Windows-style");
});

test("treats a trailing forward slash as an exact match (Linux-style paths)", async () => {
await assertExactDirectoryExclusion("/", "Linux-style");
});
});
});