blob: 1fc901487eba2898798f19664158590b3861a48e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 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 v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Mickael Istria (Red Hat Inc.) - initial implementation
* Michał Niewrzał (Rogue Wave Software Inc.)
* Angelo Zerr <angelo.zerr@gmail.com> - fix Bug 526255
* Lucas Bullen (Red Hat Inc.) - [Bug 517428] Requests sent before initialization
*******************************************************************************/
package org.eclipse.lsp4e.operations.references;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4e.ui.Messages;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ReferenceContext;
import org.eclipse.lsp4j.ReferenceParams;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.services.LanguageServer;
import org.eclipse.osgi.util.NLS;
import org.eclipse.search.internal.ui.text.FileMatch;
import org.eclipse.search.internal.ui.text.FileSearchQuery;
import org.eclipse.search.internal.ui.text.LineElement;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
import org.eclipse.search.ui.text.Match;
/**
* {@link ISearchQuery} implementation for LSP.
*
*/
public class LSSearchQuery extends FileSearchQuery {
private final @NonNull IDocument document;
private final @NonNull LanguageServer languageServer;
private final Position position;
private final String filename;
private LSSearchResult result;
private long startTime;
private CompletableFuture<List<? extends Location>> references;
/**
* LSP search query to "Find references" from the given offset of the given
* {@link LSPDocumentInfo}.
*
* @param document
* @param offset
* @param languageServer
* @throws BadLocationException
*/
public LSSearchQuery(@NonNull IDocument document, int offset, @NonNull LanguageServer languageServer)
throws BadLocationException {
super("", false, false, null); //$NON-NLS-1$
this.document = document;
this.languageServer = languageServer;
this.position = LSPEclipseUtils.toPosition(offset, document);
this.filename = Path.fromPortableString(LSPEclipseUtils.toUri(document).getPath()).lastSegment();
}
@Override
public IStatus run(IProgressMonitor monitor) throws OperationCanceledException {
startTime = System.currentTimeMillis();
// Cancel last references future if needed.
if (references != null) {
references.cancel(true);
}
AbstractTextSearchResult textResult = (AbstractTextSearchResult) getSearchResult();
textResult.removeAll();
try {
// Execute LSP "references" service
ReferenceParams params = new ReferenceParams();
params.setContext(new ReferenceContext(true));
params.setTextDocument(new TextDocumentIdentifier(LSPEclipseUtils.toUri(document).toString()));
params.setPosition(position);
languageServer.getTextDocumentService().references(params)
.thenAcceptAsync(locs -> {
// Loop for each LSP Location and convert it to Match search.
for (Location loc : locs) {
Match match = toMatch(loc);
result.addMatch(match);
}
});
return Status.OK_STATUS;
} catch (Exception ex) {
return new Status(IStatus.ERROR, LanguageServerPlugin.getDefault().getBundle().getSymbolicName(),
ex.getMessage(), ex);
}
}
/**
* Convert the given LSP {@link Location} to Eclipse search {@link Match}.
*
* @param location
* the LSP location to convert.
* @return the converted Eclipse search {@link Match}.
*/
private static Match toMatch(Location location) {
try {
IResource resource = LSPEclipseUtils.findResourceFor(location.getUri());
IDocument document = LSPEclipseUtils.getDocument(resource);
if (document != null) {
int startOffset = LSPEclipseUtils.toOffset(location.getRange().getStart(), document);
int endOffset = LSPEclipseUtils.toOffset(location.getRange().getEnd(), document);
IRegion lineInformation = document.getLineInformationOfOffset(startOffset);
LineElement lineEntry = new LineElement(resource, document.getLineOfOffset(startOffset),
lineInformation.getOffset(),
document.get(lineInformation.getOffset(), lineInformation.getLength()));
return new FileMatch((IFile) resource, startOffset, endOffset - startOffset, lineEntry);
}
Position startPosition = location.getRange().getStart();
LineElement lineEntry = new LineElement(resource, startPosition.getLine(), 0,
String.format("%s:%s", startPosition.getLine(), startPosition.getCharacter())); //$NON-NLS-1$
return new FileMatch((IFile) resource, 0, 0, lineEntry);
} catch (BadLocationException ex) {
LanguageServerPlugin.logError(ex);
}
return null;
}
@Override
public ISearchResult getSearchResult() {
if (result == null) {
result = new LSSearchResult(this);
}
return result;
}
@Override
public String getLabel() {
return Messages.LSSearchQuery_label;
}
@Override
public String getResultLabel(int nMatches) {
long time = 0;
if (startTime > 0) {
time = System.currentTimeMillis() - startTime;
}
if (nMatches == 1) {
return NLS.bind(Messages.LSSearchQuery_singularReference,
new Object[] { filename, position.getLine() + 1, position.getCharacter() + 1, time });
}
return NLS.bind(Messages.LSSearchQuery_pluralReferences,
new Object[] { filename, position.getLine() + 1, position.getCharacter() + 1, nMatches, time });
}
@Override
public boolean isFileNameSearch() {
// Return false to display lines where references are found
return false;
}
}