Skip to content

Improve occurrences highlighting #1941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2017 Red Hat Inc. and others.
* Copyright (c) 2016-2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand All @@ -18,75 +18,140 @@

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.manipulation.CoreASTProvider;
import org.eclipse.jdt.internal.core.manipulation.search.BreakContinueTargetFinder;
import org.eclipse.jdt.internal.core.manipulation.search.ExceptionOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.ImplementOccurrencesFinder;
import org.eclipse.jdt.internal.core.manipulation.search.MethodExitsFinder;
import org.eclipse.jdt.internal.core.manipulation.search.IOccurrencesFinder.OccurrenceLocation;
import org.eclipse.jdt.internal.core.manipulation.search.OccurrencesFinder;
import org.eclipse.jdt.ls.core.internal.JDTUtils;
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightKind;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentPositionParams;

@SuppressWarnings("restriction")
public class DocumentHighlightHandler{

private List<DocumentHighlight> computeOccurrences(ITypeRoot unit, int line, int column, IProgressMonitor monitor) {
if (unit != null) {
try {
int offset = JsonRpcHelpers.toOffset(unit.getBuffer(), line, column);
OccurrencesFinder finder = new OccurrencesFinder();
CompilationUnit ast = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor);
if (ast != null) {
String error = finder.initialize(ast, offset, 0);
if (error == null){
List<DocumentHighlight> result = new ArrayList<>();
OccurrenceLocation[] occurrences = finder.getOccurrences();
if (occurrences != null) {
for (OccurrenceLocation loc : occurrences) {
if (monitor.isCanceled()) {
return Collections.emptyList();
}
result.add(convertToHighlight(unit, loc));
}
}
return result;
}
}
} catch (JavaModelException e) {
JavaLanguageServerPlugin.logException("Problem with compute occurrences for" + unit.getElementName(), e);
}
/**
* Handler for {@code textDocument/documentHighlight} requests.
*/
public class DocumentHighlightHandler {

/**
* Handles a {@code textDocument/documentHighlight} request.
*
* @param params the position at which to find highlights
* @param monitor the progress monitor
* @return the document highlights for the given position
*/
public static List<DocumentHighlight> documentHighlight(TextDocumentPositionParams params, IProgressMonitor monitor) {
ITypeRoot typeRoot = JDTUtils.resolveTypeRoot(params.getTextDocument().getUri());
if (typeRoot == null || monitor.isCanceled()) {
return Collections.emptyList();
}
CompilationUnit ast = CoreASTProvider.getInstance().getAST(typeRoot, CoreASTProvider.WAIT_YES, monitor);
if (ast == null || monitor.isCanceled()) {
return Collections.emptyList();
}

int offset = JsonRpcHelpers.toOffset(typeRoot,
params.getPosition().getLine(), params.getPosition().getCharacter());
ASTNode node = NodeFinder.perform(ast, offset, 0);
if (monitor.isCanceled()) {
return Collections.emptyList();
}

return findHighlights(ast, node, monitor);
}

/**
* Finds {@link DocumentHighlight}s in a {@link CompilationUnit}.
* The highlights are searched using the following {@link IOccurrencesFinder}s:
* <ol>
* <li>{@link ExceptionOccurrencesFinder}</li>
* <li>{@link MethodExitsFinder}</li>
* <li>{@link BreakContinueTargetFinder}</li>
* <li>{@link ImplementOccurrencesFinder}</li>
* <li>{@link OccurrencesFinder}</li>
* </ol>
*
* @param ast the {@link CompilationUnit}
* @param node the selected {@link ASTNode} to find highlights for
* @param monitor the progress monitor
* @return the highlights, or an empty list if none were found
*/
private static List<DocumentHighlight> findHighlights(CompilationUnit ast, ASTNode node, IProgressMonitor monitor) {
IOccurrencesFinder finder;

finder = new ExceptionOccurrencesFinder();
if (finder.initialize(ast, node) == null) {
return convertToHighlights(ast, finder.getOccurrences());
}
if (monitor.isCanceled()) {
return Collections.emptyList();
}

finder = new MethodExitsFinder();
if (finder.initialize(ast, node) == null) {
return convertToHighlights(ast, finder.getOccurrences());
}
if (monitor.isCanceled()) {
return Collections.emptyList();
}

finder = new BreakContinueTargetFinder();
if (finder.initialize(ast, node) == null) {
return convertToHighlights(ast, finder.getOccurrences());
}
if (monitor.isCanceled()) {
return Collections.emptyList();
}

finder = new ImplementOccurrencesFinder();
if (finder.initialize(ast, node) == null) {
return convertToHighlights(ast, finder.getOccurrences());
}
if (monitor.isCanceled()) {
return Collections.emptyList();
}

finder = new OccurrencesFinder();
if (finder.initialize(ast, node) == null) {
return convertToHighlights(ast, finder.getOccurrences());
}

return Collections.emptyList();
}

private DocumentHighlight convertToHighlight(ITypeRoot unit, OccurrenceLocation occurrence)
throws JavaModelException {
DocumentHighlight h = new DocumentHighlight();
if ((occurrence.getFlags() | IOccurrencesFinder.F_WRITE_OCCURRENCE) == IOccurrencesFinder.F_WRITE_OCCURRENCE) {
h.setKind(DocumentHighlightKind.Write);
} else if ((occurrence.getFlags()
| IOccurrencesFinder.F_READ_OCCURRENCE) == IOccurrencesFinder.F_READ_OCCURRENCE) {
h.setKind(DocumentHighlightKind.Read);
private static List<DocumentHighlight> convertToHighlights(CompilationUnit ast, OccurrenceLocation[] locations) {
List<DocumentHighlight> highlights = new ArrayList<>(locations.length);
for (OccurrenceLocation loc : locations) {
highlights.add(convertToHighlight(ast, loc));
}
int[] loc = JsonRpcHelpers.toLine(unit.getBuffer(), occurrence.getOffset());
int[] endLoc = JsonRpcHelpers.toLine(unit.getBuffer(), occurrence.getOffset() + occurrence.getLength());

h.setRange(new Range(
new Position(loc[0], loc[1]),
new Position(endLoc[0],endLoc[1])
));
return h;
return highlights;
}

public List<? extends DocumentHighlight> documentHighlight(TextDocumentPositionParams position, IProgressMonitor monitor) {
ITypeRoot type = JDTUtils.resolveTypeRoot(position.getTextDocument().getUri());
return computeOccurrences(type, position.getPosition().getLine(),
position.getPosition().getCharacter(), monitor);
private static DocumentHighlight convertToHighlight(CompilationUnit ast, OccurrenceLocation occurrence) {
DocumentHighlight highlight = new DocumentHighlight();
if ((occurrence.getFlags() & IOccurrencesFinder.F_WRITE_OCCURRENCE) != 0) {
highlight.setKind(DocumentHighlightKind.Write);
} else {
// highlight kind for symbols should be either Read or Write (not Text), see
// https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#textDocument_documentHighlight
highlight.setKind(DocumentHighlightKind.Read);
}

int[] startPos = JsonRpcHelpers.toLine(ast.getTypeRoot(), occurrence.getOffset());
int[] endPos = JsonRpcHelpers.toLine(ast.getTypeRoot(), occurrence.getOffset() + occurrence.getLength());
highlight.setRange(new Range(
new Position(startPos[0], startPos[1]),
new Position(endPos[0], endPos[1])
));
return highlight;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2016-2020 Red Hat Inc. and others.
* Copyright (c) 2016-2022 Red Hat Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -620,8 +620,7 @@ public CompletableFuture<List<? extends Location>> findLinks(FindLinksParams par
@Override
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(DocumentHighlightParams position) {
logInfo(">> document/documentHighlight");
DocumentHighlightHandler handler = new DocumentHighlightHandler();
return computeAsync((monitor) -> handler.documentHighlight(position, monitor));
return computeAsync((monitor) -> DocumentHighlightHandler.documentHighlight(position, monitor));
}

/* (non-Javadoc)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Microsoft Corporation and others.
* Copyright (c) 2020-2022 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -75,6 +75,9 @@ public void registerCapabilities(InitializeResult initializeResult) {
if (!preferenceManager.getClientPreferences().isCompletionDynamicRegistered()) {
capabilities.setCompletionProvider(CompletionHandler.DEFAULT_COMPLETION_OPTIONS);
}
if (!preferenceManager.getClientPreferences().isDocumentHighlightDynamicRegistered()) {
capabilities.setDocumentHighlightProvider(Boolean.TRUE);
}
TextDocumentSyncOptions textDocumentSyncOptions = new TextDocumentSyncOptions();
textDocumentSyncOptions.setOpenClose(Boolean.TRUE);
textDocumentSyncOptions.setSave(new SaveOptions(Boolean.TRUE));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020 Microsoft Corporation and others.
* Copyright (c) 2020-2022 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
Expand Down Expand Up @@ -43,6 +43,7 @@
import org.eclipse.jdt.ls.core.internal.handlers.BaseDocumentLifeCycleHandler;
import org.eclipse.jdt.ls.core.internal.handlers.CompletionHandler;
import org.eclipse.jdt.ls.core.internal.handlers.CompletionResolveHandler;
import org.eclipse.jdt.ls.core.internal.handlers.DocumentHighlightHandler;
import org.eclipse.jdt.ls.core.internal.handlers.DocumentSymbolHandler;
import org.eclipse.jdt.ls.core.internal.handlers.FoldingRangeHandler;
import org.eclipse.jdt.ls.core.internal.handlers.HoverHandler;
Expand All @@ -67,6 +68,8 @@
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.DidSaveTextDocumentParams;
import org.eclipse.lsp4j.DocumentHighlight;
import org.eclipse.lsp4j.DocumentHighlightParams;
import org.eclipse.lsp4j.DocumentSymbol;
import org.eclipse.lsp4j.DocumentSymbolParams;
import org.eclipse.lsp4j.FoldingRange;
Expand Down Expand Up @@ -227,6 +230,10 @@ public void initialized() {
registerCapability(Preferences.HOVER_ID, Preferences.HOVER, null);
}

if (preferenceManager.getClientPreferences().isDocumentHighlightDynamicRegistered()) {
registerCapability(Preferences.DOCUMENT_HIGHLIGHT_ID, Preferences.DOCUMENT_HIGHLIGHT);
}

if (preferenceManager.getClientPreferences().isWorkspaceChangeWatchedFilesDynamicRegistered()) {
projectsManager.registerWatchers();
}
Expand Down Expand Up @@ -410,6 +417,12 @@ public CompletableFuture<SemanticTokens> semanticTokensFull(SemanticTokensParams
documentLifeCycleHandler.new DocumentMonitor(params.getTextDocument().getUri())));
}

@Override
public CompletableFuture<List<? extends DocumentHighlight>> documentHighlight(DocumentHighlightParams position) {
logInfo(">> document/documentHighlight");
return computeAsync((monitor) -> DocumentHighlightHandler.documentHighlight(position, monitor));
}

private void waitForLifecycleJobs(IProgressMonitor monitor) {
JobHelpers.waitForJobs(BaseDocumentLifeCycleHandler.DOCUMENT_LIFE_CYCLE_JOBS, monitor);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,49 @@
package org.sample;

public class Highlight {

public void test() {
String string = "";
string.toString();
string = "";
string.toString();
}

import java.io.IOException;

public class Highlight implements FooInterface {

private String str = "string";

public String getFoo() throws IOException {
if (str.contains("!")) {
throw new IOException();
}
str = "bar";
if (str.length() == 0) {
throw new RuntimeException();
}
loop: while (!str.isEmpty()) {
for (;;) {
if (str.contains("foo")) {
break loop;
}
continue;
}
}
str = String.format(str);
return str + "foo";
}

public int getBar() {
String str = "bar";
return str.length();
}

@Override
public void foo() {
// TODO Auto-generated method stub
}

@Override
public void bar() {
// TODO Auto-generated method stub
}

}

interface FooInterface {
public void foo();
public void bar();
}
Loading