Skip to content
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

Adds code reference view #29

Merged
merged 10 commits into from
Sep 25, 2024
8 changes: 8 additions & 0 deletions plugin/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
category="amazonq"
inject="true">
</view>
<view
category="amazonq"
class="software.aws.toolkits.eclipse.amazonq.views.AmazonQCodeReferenceView"
icon="icons/AmazonQ.png"
id="software.aws.toolkits.eclipse.amazonq.views.AmazonQCodeReferenceView"
name="Amazon Q Code Reference"
restorable="true">
</view>
</extension>

<extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public final Object execute(final ExecutionEvent event) throws ExecutionExceptio
var suggestion = QInvocationSession.getInstance().getCurrentSuggestion();
var widget = QInvocationSession.getInstance().getViewer().getTextWidget();
Display display = widget.getDisplay();
display.syncExec(() -> this.insertSuggestion(suggestion));
display.syncExec(() -> this.insertSuggestion(suggestion.getInsertText()));
return null;
}

Expand All @@ -37,6 +37,8 @@ private void insertSuggestion(final String suggestion) {
doc.replace(insertOffset, 0, suggestion);
widget.setCaretOffset(insertOffset + suggestion.length());
QInvocationSession.getInstance().transitionToDecisionMade();
QInvocationSession.getInstance().getViewer().getTextWidget().redraw();
QInvocationSession.getInstance().executeCallbackForCodeReference();
QInvocationSession.getInstance().end();
} catch (BadLocationException e) {
PluginLogger.error(e.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ public class InlineCompletionItem {

private String itemId;
private String insertText;
private InlineCompletionReference[] references;

public final String getItemId() {
return itemId;
Expand All @@ -23,4 +24,12 @@ public final String getInsertText() {
public final void setInsertText(final String insertText) {
this.insertText = insertText;
}

public final InlineCompletionReference[] getReferences() {
return references;
}

public final void setReferences(final InlineCompletionReference[] references) {
this.references = references;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

package software.aws.toolkits.eclipse.amazonq.lsp.model;

public class InlineCompletionReference {
private String referenceName;
private String referenceUrl;
private String licenseName;
private InlineCompletionReferencePosition position;

public final void setReferenceName(final String referenceName) {
this.referenceName = referenceName;
}

public final void setReferenceUrl(final String referenceUrl) {
this.referenceUrl = referenceUrl;
}

public final void setLicenseName(final String licenseName) {
this.licenseName = licenseName;
}

public final void setPosition(final InlineCompletionReferencePosition position) {
this.position = position;
}

public final String getReferenceName() {
return referenceName;
}

public final String getReferenceUrl() {
return referenceUrl;
}

public final String getLicenseName() {
return licenseName;
}

public final InlineCompletionReferencePosition getPosition() {
return position;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.

package software.aws.toolkits.eclipse.amazonq.lsp.model;

public class InlineCompletionReferencePosition {
private int startCharacter;
private int endCharacter;

public final void setStartCharacter(final int startCharacter) {
this.startCharacter = startCharacter;
}

public final void setEndCharacter(final int endCharacter) {
this.endCharacter = endCharacter;
}

public final int getStartCharacter() {
return startCharacter;
}

public final int getEndCharacter() {
return endCharacter;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package software.aws.toolkits.eclipse.amazonq.util;

import software.aws.toolkits.eclipse.amazonq.lsp.model.InlineCompletionItem;

@FunctionalInterface
public interface CodeReferenceAcceptanceCallback {
void onCallback(InlineCompletionItem suggestionItem, int startLine);
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public final void paintControl(final PaintEvent e) {
var widget = QInvocationSession.getInstance().getViewer().getTextWidget();

var location = widget.getLocationAtOffset(widget.getCaretOffset());
var suggestion = QInvocationSession.getInstance().getCurrentSuggestion();
var suggestion = QInvocationSession.getInstance().getCurrentSuggestion().getInsertText();
int invocationOffset = QInvocationSession.getInstance().getInvocationOffset();
var suggestionParts = suggestion.split("\\R");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public void verifyKey(final VerifyEvent event) {
qInvocationSessionInstance.setCaretMovementReason(CaretMovementReason.TEXT_INPUT);

// Here we conduct typeahead logic
String currentSuggestion = qInvocationSessionInstance.getCurrentSuggestion().trim();
String currentSuggestion = qInvocationSessionInstance.getCurrentSuggestion().getInsertText().trim();
int currentOffset = widget.getCaretOffset();
qInvocationSessionInstance
.setHasBeenTypedahead(currentOffset - qInvocationSessionInstance.getInvocationOffset() > 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public final class QInvocationSession extends QResource {
private boolean isLastKeyNewLine = false;
private int[] headOffsetAtLine = new int[500];
private boolean hasBeenTypedahead = false;
private CodeReferenceAcceptanceCallback codeReferenceAcceptanceCallback = null;
private Runnable unsetVerticalIndent;

// Private constructor to prevent instantiation
Expand Down Expand Up @@ -123,10 +124,8 @@ public void invoke() {
AuthUtils.updateToken().get();
}

List<String> newSuggestions = LspProvider.getAmazonQServer().get()
.inlineCompletionWithReferences(params).thenApply(result -> result.getItems().stream()
.map(InlineCompletionItem::getInsertText).collect(Collectors.toList()))
.get();
List<InlineCompletionItem> newSuggestions = LspProvider.getAmazonQServer().get()
.inlineCompletionWithReferences(params).thenApply(result -> result.getItems()).get();

Display.getDefault().asyncExec(() -> {
if (newSuggestions == null || newSuggestions.isEmpty()) {
Expand Down Expand Up @@ -287,7 +286,7 @@ public int getHeadOffsetAtLine(final int lineNum) throws IllegalArgumentExceptio
return headOffsetAtLine[lineNum];
}

public String getCurrentSuggestion() {
public InlineCompletionItem getCurrentSuggestion() {
if (suggestionsContext == null) {
PluginLogger.warn("QSuggestion context is null");
return null;
Expand All @@ -303,7 +302,7 @@ public String getCurrentSuggestion() {
throw new IllegalStateException("QSuggestion showing discarded suggestions");
}

return details.get(index).getSuggestion();
return details.get(index).getInlineCompletionItem();
}

public void decrementCurrentSuggestionIndex() {
Expand All @@ -328,6 +327,19 @@ public boolean hasBeenTypedahead() {
return hasBeenTypedahead;
}

public void registerCallbackForCodeReference(final CodeReferenceAcceptanceCallback codeReferenceAcceptanceCallback) {
this.codeReferenceAcceptanceCallback = codeReferenceAcceptanceCallback;
}

public void executeCallbackForCodeReference() {
if (codeReferenceAcceptanceCallback != null) {
var selectedSuggestion = getCurrentSuggestion();
var widget = viewer.getTextWidget();
int startLine = widget.getLineAtOffset(invocationOffset);
codeReferenceAcceptanceCallback.onCallback(selectedSuggestion, startLine);
}
}

public void setVerticalIndent(int line, int height) {
var widget = viewer.getTextWidget();
widget.setLineVerticalIndent(line, height);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@

package software.aws.toolkits.eclipse.amazonq.util;

import software.aws.toolkits.eclipse.amazonq.lsp.model.InlineCompletionItem;

public final class QSuggestionContext {
private String suggestion;
private InlineCompletionItem inlineCompletionItem;
private QSuggestionState state;

public QSuggestionContext(final String suggestion) {
this.suggestion = suggestion;
public QSuggestionContext(final InlineCompletionItem inlineCompletionItem) {
this.inlineCompletionItem = inlineCompletionItem;
state = QSuggestionState.UNSEEN;
}

public String getSuggestion() {
return suggestion;
public InlineCompletionItem getInlineCompletionItem() {
return inlineCompletionItem;
}

public QSuggestionState getState() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package software.aws.toolkits.eclipse.amazonq.views;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import software.aws.toolkits.eclipse.amazonq.util.QInvocationSession;

public final class AmazonQCodeReferenceView extends ViewPart {

public static final String ID = "software.aws.toolkits.eclipse.amazonq.views.AmazonQCodeReferenceView";
private static final String CR_TEMPLATE = """
[%s] Accepted recommendation with code
%s
provided with reference under %s from repository %s. Added to %s (lines from %d to %d)
""";

private StyledText textArea;

@Override
public void createPartControl(final Composite parent) {
if (textArea == null) {
textArea = new StyledText(parent, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL);
}

QInvocationSession qInvocationSessionInstance = QInvocationSession.getInstance();

qInvocationSessionInstance.registerCallbackForCodeReference((item, startLine) -> {
var references = item.getReferences();
var editor = qInvocationSessionInstance.getEditor();
String fqfn = editor.getTitle();
if (references != null && references.length > 0) {
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy, HH:mm:ss a");
String formattedDateTime = now.format(formatter);
int suggestionTextDepth = item.getInsertText().split("\n").length;
for (var reference : references) {
String itemToShow = String.format(CR_TEMPLATE, formattedDateTime, item.getInsertText(),
reference.getLicenseName(), reference.getReferenceUrl(),
fqfn, startLine, startLine + suggestionTextDepth);
int boldStart = textArea.getCharCount();
int boldLength = itemToShow.split("\n", 2)[0].length();

StyleRange styleRange = new StyleRange();
styleRange.start = boldStart;
styleRange.length = boldLength;
styleRange.fontStyle = SWT.BOLD;

textArea.append(itemToShow);
textArea.append("\n");
textArea.setStyleRange(styleRange);

}
}
});

textArea.append(
"Your organization controls whether suggestions include code with references. To update these settings, please contact your admin.\n");
}

@Override
public void setFocus() {
return;
}
}