Skip to content
Open
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
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e.test/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Tests for language server bundle (Incubation)
Bundle-SymbolicName: org.eclipse.lsp4e.test;singleton:=true
Bundle-Version: 0.16.12.qualifier
Bundle-Version: 0.16.13.qualifier
Fragment-Host: org.eclipse.lsp4e
Bundle-Vendor: Eclipse LSP4E
Bundle-RequiredExecutionEnvironment: JavaSE-21
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e.test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</parent>
<artifactId>org.eclipse.lsp4e.test</artifactId>
<packaging>eclipse-test-plugin</packaging>
<version>0.16.12-SNAPSHOT</version>
<version>0.16.13-SNAPSHOT</version>

<properties>
<os-jvm-flags /> <!-- for the default case -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
import org.eclipse.lsp4j.InlayHint;
import org.eclipse.lsp4j.InlayHintLabelPart;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.lsp4j.jsonrpc.messages.Either;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.widgets.Display;
Expand Down Expand Up @@ -76,6 +79,29 @@ public void singleLabelPartCommand(MockLanguageServerFactory factory) throws Exc
assertEquals(MockLanguageServer.SUPPORTED_COMMAND_ID, executedCommand.getCommand());
assertEquals(command.getArguments(), executedCommand.getArguments());
}

@Test
void inlayHintWithTextEdit() throws Exception {
IFile file = TestUtils.createUniqueTestFile(project, "lspt", "x = [1, 2]");
ITextViewer textViewer = TestUtils.openTextViewer(file);
IDocument document = textViewer.getDocument();

final var provider = new InlayHintProvider();

// Create inlayhint with text edit
InlayHint inlayHint = new InlayHint(new Position(0, 0), Either.forLeft(": list[int]"));
inlayHint.setTextEdits(List.of(new TextEdit(new Range(new Position(0, 1), new Position(0, 1)), ": list[int]")));

// Simulate user clicking on inlayhint
LanguageServerWrapper wrapper = LanguageServiceAccessor.getLSWrapper(project,
LanguageServersRegistry.getInstance().getDefinition(MOCK_SERVER_ID));
final var sut = new LSPLineContentCodeMining(inlayHint, document, wrapper, provider);
MouseEvent mouseEvent = createMouseEvent();
sut.getAction().accept(mouseEvent);

// Text edit should be applied.
assertEquals("x: list[int] = [1, 2]", document.get());
}

private static InlayHintLabelPart createInlayLabelPart(String text, String commandID) {
final var labelPart = new InlayHintLabelPart(text);
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Language Server Protocol client for Eclipse IDE (Incubation)
Bundle-SymbolicName: org.eclipse.lsp4e;singleton:=true
Bundle-Version: 0.19.13.qualifier
Bundle-Version: 0.19.14.qualifier
Bundle-RequiredExecutionEnvironment: JavaSE-21
Require-Bundle: org.eclipse.core.runtime;bundle-version="3.12.0",
org.eclipse.equinox.common;bundle-version="3.8.0",
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.lsp4e/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</parent>
<artifactId>org.eclipse.lsp4e</artifactId>
<packaging>eclipse-plugin</packaging>
<version>0.19.13-SNAPSHOT</version>
<version>0.19.14-SNAPSHOT</version>

<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import org.eclipse.lsp4e.LanguageServerWrapper;
import org.eclipse.lsp4e.LanguageServers;
import org.eclipse.lsp4e.command.CommandExecutor;
import org.eclipse.lsp4e.internal.NullSafetyHelper;
import org.eclipse.lsp4j.Command;
import org.eclipse.lsp4j.ExecuteCommandOptions;
import org.eclipse.lsp4j.ExecuteCommandParams;
import org.eclipse.lsp4j.InlayHint;
Expand Down Expand Up @@ -111,7 +113,7 @@ private static boolean canResolveInlayHint(@Nullable ServerCapabilities capabili
Either<Boolean, InlayHintRegistrationOptions> inlayProvider = capabilities.getInlayHintProvider();
if (inlayProvider != null && inlayProvider.isRight()) {
InlayHintRegistrationOptions options = inlayProvider.getRight();
return options.getResolveProvider() != null && options.getResolveProvider().booleanValue();
return Boolean.TRUE.equals(options.getResolveProvider());
}
return false;
}
Expand All @@ -134,35 +136,67 @@ private static org.eclipse.jface.text.Position toPosition(Position position, IDo

@Override
public final @Nullable Consumer<MouseEvent> getAction() {
return inlayHint.getLabel().map(l -> null, this::labelPartAction);
List<InlayHintLabelPart> parts = inlayHint.getLabel().map(l -> null, r -> r);
boolean hasPartWithCommand = parts != null
&& parts.stream().map(InlayHintLabelPart::getCommand).anyMatch(Objects::nonNull);
boolean hasTextEdits = inlayHint.getTextEdits() != null && !inlayHint.getTextEdits().isEmpty();

if (!(hasPartWithCommand || hasTextEdits)) {
// Neither command nor text edits -> no action
return null;
}

return evt -> {
/*
* LSP does not define if commands or text edits take precedence. So for the
* sake of backwards compatibility, we first check for commands and then for
* text edits
*/
if (hasPartWithCommand && handlePartWithCommand(evt, NullSafetyHelper.castNonNull(parts))) {
return;
}
if (hasTextEdits) {
// Apply text edits
try {
LSPEclipseUtils.applyEdits(document, inlayHint.getTextEdits());
// The text of the inlay hint is integrated into the document.
// To avoid a brief moment where both the inserted text and the inlay hint are
// rendered, we set an empty label for the hint.
setLabel(""); //$NON-NLS-1$
} catch (BadLocationException e) {
// Location to modify document was no longer valid -> Ignore
}
}
};
}

private @Nullable Consumer<MouseEvent> labelPartAction(List<InlayHintLabelPart> labelParts) {
String title = getLabel();
if (title != null && !title.isEmpty()
&& labelParts.stream().map(InlayHintLabelPart::getCommand).anyMatch(Objects::nonNull)) {
return me -> findLabelPart(me, labelParts) //
.map(InlayHintLabelPart::getCommand) //
.filter(Objects::nonNull) //
.ifPresent(command -> {
ServerCapabilities serverCapabilities = wrapper.getServerCapabilities();
ExecuteCommandOptions provider = serverCapabilities == null ? null
: serverCapabilities.getExecuteCommandProvider();
String commandId = command.getCommand();
if (provider != null && provider.getCommands().contains(commandId)) {
LanguageServers.forDocument(document).computeAll((w, ls) -> {
if (w == wrapper) {
return ls.getWorkspaceService().executeCommand(
new ExecuteCommandParams(commandId, command.getArguments()));
}
return CompletableFuture.completedFuture(null);
});
} else {
CommandExecutor.executeCommandClientSide(command, document);
}
});
/**
* Returns true if the mouse event hit a label part with a command.
*/
private boolean handlePartWithCommand(MouseEvent evt, List<InlayHintLabelPart> parts) {
// Figure out if the user clicked on a specific label part which has a command
Optional<InlayHintLabelPart> clickedPart = findLabelPart(evt, NullSafetyHelper.castNonNull(parts));
Optional<Command> commandOpt = clickedPart.map(InlayHintLabelPart::getCommand);
if (commandOpt.isPresent()) {
Command command = commandOpt.get();
ServerCapabilities serverCapabilities = wrapper.getServerCapabilities();
ExecuteCommandOptions provider = serverCapabilities == null ? null
: serverCapabilities.getExecuteCommandProvider();
String commandId = command.getCommand();
if (provider != null && provider.getCommands().contains(commandId)) {
LanguageServers.forDocument(document).computeAll((w, ls) -> {
if (w == wrapper) {
return ls.getWorkspaceService()
.executeCommand(new ExecuteCommandParams(commandId, command.getArguments()));
}
return CompletableFuture.completedFuture(null);
});
} else {
CommandExecutor.executeCommandClientSide(command, document);
}
return true;
}
return null;
return false;
}

private Optional<InlayHintLabelPart> findLabelPart(MouseEvent me, List<InlayHintLabelPart> labelParts) {
Expand Down