Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@ public Object executeCommand(String commandId, List<Object> arguments, IProgress
return handleBuildTarget(arguments);
case "bazel-jdt.setActiveDebugProject":
return handleSetActiveDebugProject(arguments);
case "bazel-jdt.clearActiveDebugProject":
return handleClearActiveDebugProject();
default:
return null;
}
}

private Object handleClearActiveDebugProject() {
BazelRuntimeClasspathEntryResolver.clearActiveDebugProject();
return null;
}

private Object handleImportProject(List<Object> arguments) {
try {
BazelBridge bridge = BazelBridge.getInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,72 @@ static String resolveFallbackJar(String missingPath, String workspacePath) {
if (outputBase == null) return null;

String repoName = extractRepoName(path);
if (repoName == null) return null;

File repoDir = new File(outputBase, "external/" + repoName);
if (!repoDir.isDirectory()) {
repoDir = findBzlmodRepoDir(outputBase, repoName);
if (repoName != null) {
File repoDir = new File(outputBase, "external/" + repoName);
if (!repoDir.isDirectory()) {
repoDir = findBzlmodRepoDir(outputBase, repoName);
}
if (repoDir != null && repoDir.isDirectory()) {
String found = findJarInDirectory(repoDir, MAX_JAR_SEARCH_DEPTH);
if (found != null) {
LOG.info("Fallback JAR resolved: " + path + " -> " + found);
return found;
}
}
return null;
}
if (repoDir == null || !repoDir.isDirectory()) return null;

String found = findJarInDirectory(repoDir, MAX_JAR_SEARCH_DEPTH);
if (found != null) {
LOG.info("Fallback JAR resolved: " + path + " -> " + found);
}
return found;
return resolveBuildOutputJar(path, outputBase);
});
}

static String resolveBuildOutputJar(String missingPath, String outputBase) {
String artifactName = extractArtifactNameFromLibJar(missingPath);
if (artifactName == null) return null;

File repoDir = findCandidateRepoDir(outputBase, artifactName);
if (repoDir == null) return null;

String found = findJarInDirectory(repoDir, MAX_JAR_SEARCH_DEPTH);
if (found != null) {
LOG.info("Build-output JAR resolved: " + missingPath + " -> " + found);
}
return found;
}

static String extractArtifactNameFromLibJar(String path) {
String fileName = path;
int lastSlash = path.lastIndexOf('/');
if (lastSlash >= 0) {
fileName = path.substring(lastSlash + 1);
}
if (!fileName.startsWith("lib") || !fileName.endsWith(".jar")) return null;
String name = fileName.substring(3, fileName.length() - 4);
if (name.isEmpty()) return null;
return name;
}

static File findCandidateRepoDir(String outputBase, String artifactName) {
File externalRoot = new File(outputBase, "external");
if (!externalRoot.isDirectory()) return null;

File exact1 = new File(externalRoot, artifactName + "_" + artifactName);
if (exact1.isDirectory()) return exact1;

File exact2 = new File(externalRoot, artifactName);
if (exact2.isDirectory()) return exact2;

File[] prefixMatches = externalRoot.listFiles((dir, name) ->
name.startsWith(artifactName + "_"));
if (prefixMatches != null && prefixMatches.length > 0) {
for (File candidate : prefixMatches) {
if (candidate.isDirectory()) return candidate;
}
}

return findBzlmodRepoDir(outputBase, artifactName);
}

static String extractRepoName(String path) {
int idx = path.indexOf("/external/");
if (idx < 0) return null;
Expand Down
1 change: 1 addition & 0 deletions bazel-jdt-bridge/java-bridge/src/main/resources/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<command id="bazel-jdt.shutdown"/>
<command id="bazel-jdt.buildTarget"/>
<command id="bazel-jdt.setActiveDebugProject"/>
<command id="bazel-jdt.clearActiveDebugProject"/>
</delegateCommandHandler>
</extension>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,171 @@ public void resolveFallbackJarReturnsNullWhenNoJarExists() throws IOException {
}

@Test
public void resolveFallbackJarReturnsNullForNonExternalPath() {
public void resolveFallbackJarResolvesLibJarViaBuildOutput() throws IOException {
String outputBase = tempDir.toString();
File repoDir = new File(outputBase, "external/junit_junit/jar");
assertTrue(repoDir.mkdirs());
File jar = new File(repoDir, "junit-4.13.2.jar");
assertTrue(jar.createNewFile());

String wsPath = tempDir.resolve("workspace").toString();
BazelExternalRepoResolver.setOutputBaseForTest(wsPath, outputBase);

String result = BazelExternalRepoResolver.resolveFallbackJar(
"/workspace/bazel-out/darwin_arm64-fastbuild/bin/3rdparty/libjunit.jar", wsPath);
assertNotNull("Should resolve lib<name>.jar via build-output fallback", result);
assertEquals(jar.getAbsolutePath(), result);
}

@Test
public void resolveFallbackJarReturnsNullForNonExternalNonLibPath() {
String wsPath = tempDir.resolve("workspace").toString();
BazelExternalRepoResolver.setOutputBaseForTest(wsPath, tempDir.toString());

String result = BazelExternalRepoResolver.resolveFallbackJar(
"/workspace/bazel-out/bin/3rdparty/libjunit.jar", wsPath);
"/workspace/bazel-out/bin/3rdparty/junit.jar", wsPath);
assertNull(result);
}

// --- extractArtifactNameFromLibJar tests ---

@Test
public void extractArtifactNameFromValidLibJar() {
assertEquals("junit",
BazelExternalRepoResolver.extractArtifactNameFromLibJar(
"/workspace/bazel-out/bin/3rdparty/libjunit.jar"));
}

@Test
public void extractArtifactNameFromLibJarWithUnderscore() {
assertEquals("hamcrest_core",
BazelExternalRepoResolver.extractArtifactNameFromLibJar(
"bazel-out/bin/3rdparty/libhamcrest_core.jar"));
}

@Test
public void extractArtifactNameReturnsNullForNonLibPrefix() {
assertNull(BazelExternalRepoResolver.extractArtifactNameFromLibJar(
"bazel-out/bin/3rdparty/junit.jar"));
}

@Test
public void extractArtifactNameReturnsNullForNonJarExtension() {
assertNull(BazelExternalRepoResolver.extractArtifactNameFromLibJar(
"bazel-out/bin/3rdparty/libfoo.txt"));
}

@Test
public void extractArtifactNameReturnsNullForEmptyName() {
assertNull(BazelExternalRepoResolver.extractArtifactNameFromLibJar(
"bazel-out/bin/3rdparty/lib.jar"));
}

@Test
public void extractArtifactNameFromFileNameOnly() {
assertEquals("guava",
BazelExternalRepoResolver.extractArtifactNameFromLibJar("libguava.jar"));
}

// --- findCandidateRepoDir tests ---

@Test
public void findCandidateRepoDirExactDoubleMatch() throws IOException {
String outputBase = tempDir.toString();
File exact = new File(outputBase, "external/junit_junit");
assertTrue(exact.mkdirs());

File result = BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "junit");
assertNotNull(result);
assertEquals(exact.getAbsolutePath(), result.getAbsolutePath());
}

@Test
public void findCandidateRepoDirExactSingleMatch() throws IOException {
String outputBase = tempDir.toString();
File exact = new File(outputBase, "external/guava");
assertTrue(exact.mkdirs());

File result = BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "guava");
assertNotNull(result);
assertEquals(exact.getAbsolutePath(), result.getAbsolutePath());
}

@Test
public void findCandidateRepoDirPrefersDoubleOverSingle() throws IOException {
String outputBase = tempDir.toString();
File single = new File(outputBase, "external/junit");
assertTrue(single.mkdirs());
File double_ = new File(outputBase, "external/junit_junit");
assertTrue(double_.mkdirs());

File result = BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "junit");
assertNotNull(result);
assertEquals(double_.getAbsolutePath(), result.getAbsolutePath());
}

@Test
public void findCandidateRepoDirPrefixMatch() throws IOException {
String outputBase = tempDir.toString();
File prefixed = new File(outputBase, "external/guava_guava_jre");
assertTrue(prefixed.mkdirs());

File result = BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "guava");
assertNotNull(result);
assertEquals(prefixed.getAbsolutePath(), result.getAbsolutePath());
}

@Test
public void findCandidateRepoDirBzlmodMatch() throws IOException {
String outputBase = tempDir.toString();
File bzlmod = new File(outputBase, "external/rules_jvm_external~~maven~junit");
assertTrue(bzlmod.mkdirs());

File result = BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "junit");
assertNotNull(result);
assertEquals(bzlmod.getAbsolutePath(), result.getAbsolutePath());
}

@Test
public void findCandidateRepoDirNoMatch() throws IOException {
String outputBase = tempDir.toString();
File externalDir = new File(outputBase, "external");
assertTrue(externalDir.mkdirs());

assertNull(BazelExternalRepoResolver.findCandidateRepoDir(outputBase, "nonexistent"));
}

// --- resolveBuildOutputJar end-to-end tests ---

@Test
public void resolveBuildOutputJarEndToEnd() throws IOException {
String outputBase = tempDir.toString();
File repoDir = new File(outputBase, "external/hamcrest_core/jar");
assertTrue(repoDir.mkdirs());
File jar = new File(repoDir, "hamcrest-core-1.3.jar");
assertTrue(jar.createNewFile());

String result = BazelExternalRepoResolver.resolveBuildOutputJar(
"bazel-out/k8-fastbuild/bin/3rdparty/libhamcrest_core.jar", outputBase);
assertNotNull(result);
assertEquals(jar.getAbsolutePath(), result);
}

@Test
public void resolveBuildOutputJarReturnsNullForNonLibJar() {
String result = BazelExternalRepoResolver.resolveBuildOutputJar(
"bazel-out/bin/3rdparty/junit.jar", tempDir.toString());
assertNull(result);
}

@Test
public void resolveBuildOutputJarReturnsNullWhenNoRepoDir() throws IOException {
String outputBase = tempDir.toString();
File externalDir = new File(outputBase, "external");
assertTrue(externalDir.mkdirs());

String result = BazelExternalRepoResolver.resolveBuildOutputJar(
"bazel-out/bin/3rdparty/libunknown.jar", outputBase);
assertNull(result);
}

Expand Down
36 changes: 36 additions & 0 deletions bazel-jdt-bridge/vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,21 @@ function activateFull(context: vscode.ExtensionContext, workspaceRoot: string) {
)
);

context.subscriptions.push(
vscode.debug.onDidTerminateDebugSession(async (session) => {
if (session.type === 'java') {
try {
await vscode.commands.executeCommand(
'java.execute.workspaceCommand',
'bazel-jdt.clearActiveDebugProject'
);
} catch {
// LSP connection may be closed — safe to ignore
}
}
})
);

let dependencyPackageCache: string[] = [];

const bazelprojectPattern = new vscode.RelativePattern(workspaceRoot, '.bazelproject');
Expand Down Expand Up @@ -89,6 +104,7 @@ function activateFull(context: vscode.ExtensionContext, workspaceRoot: string) {
}
}

refreshTestDiscovery();
vscode.window.showInformationMessage('Bazel project re-imported (scope changed)');
} catch {
// Silently ignore — re-import is best-effort
Expand All @@ -103,6 +119,13 @@ function activateFull(context: vscode.ExtensionContext, workspaceRoot: string) {
statusBarItem,
);

// Trigger test discovery after JDT.LS finishes initial import
const javaExt = vscode.extensions.getExtension('redhat.java');
const javaApi = javaExt?.exports;
if (javaApi?.serverReady) {
javaApi.serverReady().then(() => refreshTestDiscovery()).catch(() => {});
}

// On-demand dependency source loading: monitor opened Java files
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(async (doc) => {
Expand Down Expand Up @@ -156,6 +179,19 @@ function setupCreationOnlyWatcher(context: vscode.ExtensionContext, workspaceRoo
context.subscriptions.push(watcher);
}

async function refreshTestDiscovery(): Promise<void> {
try {
const testExtension = vscode.extensions.getExtension('vscjava.vscode-java-test');
if (!testExtension) return;
if (!testExtension.isActive) {
await testExtension.activate();
}
await vscode.commands.executeCommand('testing.refreshTests');
} catch {
// Test discovery is best-effort — never block import
}
}

export async function deactivate() {
try {
await vscode.commands.executeCommand(
Expand Down
Loading