Skip to content

Commit

Permalink
Support click on inlayhint label parts
Browse files Browse the repository at this point in the history
  • Loading branch information
BoykoAlex committed Feb 21, 2024
1 parent 8d714d2 commit 0491869
Show file tree
Hide file tree
Showing 2 changed files with 209 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*******************************************************************************
* Copyright (c) 2024 Broadcom, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Broadcom, Inc. - initial API and implementation
*******************************************************************************/
package org.eclipse.lsp4e.test.operations.inlayhint;

import static org.junit.Assert.assertEquals;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4e.LanguageServersRegistry;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.operations.inlayhint.InlayHintProvider;
import org.eclipse.lsp4e.operations.inlayhint.LSPLineContentCodeMining;
import org.eclipse.lsp4e.test.utils.AllCleanRule;
import org.eclipse.lsp4e.test.utils.TestUtils;
import org.eclipse.lsp4e.tests.mock.MockLanguageServer;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintLabelPart;
import org.eclipse.lsp4j.Position;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;

import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;

public class LSPLineContentCodeMiningTest {

private static final String MOCK_SERVER_ID = "org.eclipse.lsp4e.test.server";

@Rule
public AllCleanRule clear = new AllCleanRule();
private IProject project;

@Before
public void setUp() throws CoreException {
project = TestUtils.createProject(getClass().getName() + System.currentTimeMillis());
}

@Test
public void singleLabelPartCommand()
throws Exception {
final InlayHint inlay = createMultiLabelInlayHint(createInlayLabelPart("Label-Text", MockLanguageServer.SUPPORTED_COMMAND_ID));
Command command = inlay.getLabel().getRight().get(0).getCommand();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("bar", 42);
command.setArguments(Arrays.asList(new JsonPrimitive("Foo"), jsonObject));

// Setup test data
IFile file = TestUtils.createUniqueTestFile(project, "lspt", "test content");
ITextViewer textViewer = TestUtils.openTextViewer(file);
IDocument document = textViewer.getDocument();

MockLanguageServer languageServer = MockLanguageServer.INSTANCE;
InlayHintProvider provider = new InlayHintProvider();

LanguageServerWrapper wrapper = LanguageServiceAccessor.getLSWrapper(project, LanguageServersRegistry.getInstance().getDefinition(MOCK_SERVER_ID));

LSPLineContentCodeMining sut = new LSPLineContentCodeMining(inlay, document, wrapper, provider);
MouseEvent mouseEvent = createMouseEvent();
sut.getAction().accept(mouseEvent);

// We expect that the language server will be called to execute the command
ExecuteCommandParams executedCommand = languageServer.getWorkspaceService().getExecutedCommand().get(5,
TimeUnit.SECONDS);

assertEquals(MockLanguageServer.SUPPORTED_COMMAND_ID, executedCommand.getCommand());
assertEquals(command.getArguments(), executedCommand.getArguments());
}

private static InlayHintLabelPart createInlayLabelPart(String text, String commandID) {
InlayHintLabelPart labelPart = new InlayHintLabelPart(text);
Command command = new Command(text, commandID);
labelPart.setCommand(command);
return labelPart;
}

private static InlayHint createMultiLabelInlayHint(InlayHintLabelPart... parts) {
InlayHint inlay = new InlayHint();
inlay.setLabel(Arrays.asList(parts));
inlay.setPosition(new Position(0, 0));
return inlay;
}

private static MouseEvent createMouseEvent() {
Event event = new Event();
event.button = SWT.BUTTON1;
Display display = Display.getCurrent();
event.widget = display.getSystemTray();
return new MouseEvent(event);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022-23 Red Hat Inc. and others.
* Copyright (c) 2022-24 Red Hat Inc. and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
Expand All @@ -9,7 +9,9 @@
package org.eclipse.lsp4e.operations.inlayhint;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

Expand All @@ -22,23 +24,42 @@
import org.eclipse.jface.text.codemining.LineContentCodeMining;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4e.command.CommandExecutor;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintLabelPart;
import org.eclipse.lsp4j.InlayHintRegistrationOptions;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Display;

public class LSPLineContentCodeMining extends LineContentCodeMining {

private InlayHint inlayHint;
private final LanguageServerWrapper wrapper;
private IDocument document;

private Point location;
private FontData[] fontData;

public LSPLineContentCodeMining(InlayHint inlayHint, IDocument document, LanguageServerWrapper languageServerWrapper,
InlayHintProvider provider) throws BadLocationException {
super(toPosition(inlayHint.getPosition(), document), provider);
this.inlayHint = inlayHint;
this.wrapper = languageServerWrapper;
this.document = document;
setLabel(getInlayHintString(inlayHint));
}

Expand Down Expand Up @@ -91,4 +112,76 @@ private static org.eclipse.jface.text.Position toPosition(Position position, IDo
return new org.eclipse.jface.text.Position(start, 1);
}

@Override
public final Consumer<MouseEvent> getAction() {
return me -> {
String title= getLabel();
if (title != null && !title.isEmpty()) {
findLabelPart(me).ifPresent(labelPart -> {
final Command command = labelPart.getCommand();
if(command != null && command.getCommand() != null && !command.getCommand().isEmpty()) {
ExecuteCommandOptions provider = wrapper.getServerCapabilities().getExecuteCommandProvider();
if (provider != null && provider.getCommands().contains(command.getCommand())) {
wrapper.execute(ls -> ls.getWorkspaceService()
.executeCommand(new ExecuteCommandParams(command.getCommand(), command.getArguments())));
} else {
CommandExecutor.executeCommandClientSide(command, document);
}
}
});
}
};
}

private Optional<InlayHintLabelPart> findLabelPart(MouseEvent me) {
if (inlayHint.getLabel().isRight()) {
if (inlayHint.getLabel().getRight().size() == 1) {
return Optional.of(inlayHint.getLabel().getRight().get(0));
}
if (location != null && fontData != null) {
Point relativeLocation = new Point(me.x - location.x, me.y - location.y);
Display display = Display.getCurrent();
Image image = null;
GC gc = null;
Font font = null;
try {
image = new Image(display, 1, 1);
gc = new GC(image);
font = new Font(display, fontData);
gc.setFont(font);
Point origin = new Point(0, 0);
for (InlayHintLabelPart l : inlayHint.getLabel().getRight()) {
Point size = gc.stringExtent(l.getValue());
Rectangle bounds = new Rectangle(origin.x, origin.y, size.x, size.y);
if (bounds.contains(relativeLocation)) {
return Optional.of(l);
} else {
origin.x += size.x;
}
}
} finally {
if (font != null && !font.isDisposed()) {
font.dispose();
}
if (gc != null && !gc.isDisposed()) {
gc.dispose();
}
if (image != null && !image.isDisposed()) {
image.dispose();
}
}
}
}
return Optional.empty();
}

@Override
public Point draw(GC gc, StyledText textWidget, Color color, int x, int y) {
this.location = new Point(x, y);
Point size = super.draw(gc, textWidget, color, x, y);
this.fontData = gc.getFont().getFontData();
return size;
}


}

0 comments on commit 0491869

Please sign in to comment.