mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-08 08:52:12 +00:00
fix(lsp): skip widening for indexed source files (#2052)
Fixes #2034 Call hierarchy, Find Usages and rename widen a server's answer by opening project files that mention the symbol's name and asking again. For JDT LS those files are mostly sources it already analyses, so nearly every incoming-calls request ran a second, slow round before anything was shown. Widening now asks the server which candidates are already in its analysis scope (optional `openidea.workspace.coveredFiles` command, served by the bundled JDT extension using the live source search scope) and only opens the rest; a missing or failing answer keeps the previous behaviour. Covers call hierarchy, references and rename. Hierarchy on a multi-module Maven fixture: median 703 ms → 224 ms, one query round.
This commit is contained in:
1 parent
af19880390
commit
292ddc4d2a
9 files changed
+270
-6
No files matched your search
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: OpenIDEA Java usages
|
||||
Bundle-SymbolicName: ai.openidea.usages;singleton:=true
|
||||
Bundle-Version: 1.8.0
|
||||
Bundle-Version: 1.9.0
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-21
|
||||
Require-Bundle: org.eclipse.jdt.ls.core,org.eclipse.jdt.core,
|
||||
org.eclipse.jdt.core.manipulation,org.eclipse.core.runtime,
|
||||
|
||||
@@ -20,6 +20,13 @@ first and runs the full search after it. Rows in library classes (`jdt:` URIs) c
|
||||
line as `text`, from the contents the client would request for the class, so the client
|
||||
need not fetch thousands of class files to show them.
|
||||
|
||||
`openidea.workspace.coveredFiles([candidateUri, ...])` returns candidate URIs already
|
||||
covered by the current JDT source search scope. It checks imported compilation units,
|
||||
classpath inclusion/exclusion rules and test-source policy; it does not infer coverage
|
||||
from directory names. References, rename and incoming calls leave those files closed
|
||||
and only widen with the remaining candidates. A missing or failing command retains
|
||||
widening. The result is query-local, so imports and classpath changes need no cache invalidation.
|
||||
|
||||
## Search plan and access semantics
|
||||
|
||||
For source fields on the verified JDT LS implementation, a single plan merges
|
||||
@@ -69,7 +76,7 @@ is not a claim of full IntelliJ feature parity.
|
||||
## Rebuild and delivery
|
||||
|
||||
The checked-in jar is embedded by ide-sdk `include_bytes!`, installed as
|
||||
`java-usages-1.8.0.jar`, and added alongside existing initialization bundles.
|
||||
`java-usages-1.9.0.jar`, and added alongside existing initialization bundles.
|
||||
Users need no compiler, Maven or Python. Manifest/install version must change
|
||||
when replacing the bundle because OSGi caches bundles.
|
||||
|
||||
@@ -108,6 +115,10 @@ configuration index (until the JAR or the project's classpath changes), so a sou
|
||||
longer resolves every Spring Boot property again; its reproducible SHA-256 is
|
||||
`728ab33b5e01fa5e496818f25ddbc487458a92339026899b0e22abda5adfd67b`.
|
||||
|
||||
Version 1.9.0 adds the analysis-scope command to avoid redundant semantic queries;
|
||||
its reproducible SHA-256 is
|
||||
`ae04182d6a86dfdd6f6befaaeb28907fd9e4c022ceedb445f0b32ea917dca379`.
|
||||
|
||||
```sh
|
||||
export PATH=/path/to/jdk25/bin:$PATH
|
||||
python3 java/java-usages/build.py /path/to/jdtls
|
||||
|
||||
Binary file not shown.
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<plugin>
|
||||
<extension point="org.eclipse.jdt.ls.core.delegateCommandHandler">
|
||||
<delegateCommandHandler class="ai.openidea.usages.CoveredFilesCommand">
|
||||
<command id="openidea.workspace.coveredFiles"/>
|
||||
</delegateCommandHandler>
|
||||
<delegateCommandHandler class="ai.openidea.usages.UsagesCommand">
|
||||
<command id="openidea.java.usages"/>
|
||||
</delegateCommandHandler>
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package ai.openidea.usages;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.eclipse.core.resources.ResourcesPlugin;
|
||||
import org.eclipse.core.runtime.IProgressMonitor;
|
||||
import org.eclipse.core.runtime.OperationCanceledException;
|
||||
import org.eclipse.jdt.core.*;
|
||||
import org.eclipse.jdt.core.search.IJavaSearchScope;
|
||||
import org.eclipse.jdt.core.search.SearchEngine;
|
||||
import org.eclipse.jdt.ls.core.internal.IDelegateCommandHandler;
|
||||
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
|
||||
import org.eclipse.jdt.ls.core.internal.managers.ProjectsManager;
|
||||
import org.eclipse.jdt.ls.core.internal.preferences.Preferences.SearchScope;
|
||||
|
||||
/** Candidate files already included in the workspace's semantic source searches. */
|
||||
public final class CoveredFilesCommand implements IDelegateCommandHandler {
|
||||
@Override
|
||||
public Object executeCommand(String command, List<Object> arguments, IProgressMonitor monitor) throws Exception {
|
||||
var workspace = ResourcesPlugin.getWorkspace().getRoot();
|
||||
IJavaProject[] projects = JavaCore.create(workspace).getJavaProjects();
|
||||
var setting = JavaLanguageServerPlugin.getPreferencesManager().getPreferences().getSearchScope();
|
||||
var scope = SearchEngine.createJavaSearchScope(setting == SearchScope.main, projects,
|
||||
IJavaSearchScope.SOURCES | IJavaSearchScope.REFERENCED_PROJECTS);
|
||||
List<String> covered = new ArrayList<>();
|
||||
for (Object candidate : (List<?>) arguments.get(0)) {
|
||||
if (monitor.isCanceled()) throw new OperationCanceledException();
|
||||
String uri = (String) candidate;
|
||||
for (var file : workspace.findFilesForLocationURI(URI.create(uri))) {
|
||||
if (!(JavaCore.create(file) instanceof ICompilationUnit unit) || !unit.exists()) continue;
|
||||
IJavaProject project = unit.getJavaProject();
|
||||
// The default project holds standalone open files, not an indexed source tree.
|
||||
if (ProjectsManager.getDefaultProject().equals(project.getProject()) || !project.isOnClasspath(file)) continue;
|
||||
if (scope.encloses(unit)) {
|
||||
covered.add(uri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return covered;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user