Skip to content

Commit

Permalink
Adds code reference view (#29)
Browse files Browse the repository at this point in the history
* Adds code reference view

* Adds fqfn to file being changed

* Separates model reference model types into their own class files

* Includes changes to InlineCompletionItem

---------

Co-authored-by: Jonathan Breedlove <[email protected]>
  • Loading branch information
dingfeli and breedloj authored Sep 25, 2024
1 parent 487efa6 commit f17c365
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 14 deletions.
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;
}
}

0 comments on commit f17c365

Please sign in to comment.