in plugin/src/software/aws/toolkits/eclipse/amazonq/util/QInvocationSession.java [179:293]
private synchronized void queryAsync(final InlineCompletionParams params, final int invocationOffset) {
var uuid = UUID.randomUUID();
Activator.getLogger().info(uuid + " queried made at " + invocationOffset);
var future = ThreadingUtils.executeAsyncTaskAndReturnFuture(() -> {
try {
var session = QInvocationSession.getInstance();
List<InlineCompletionItem> newSuggestions = new ArrayList<InlineCompletionItem>();
List<String> sessionId = new ArrayList<String>();
long requestInvocation = System.currentTimeMillis();
// request lsp for suggestions
var response = Activator.getLspProvider().getAmazonQServer().get()
.inlineCompletionWithReferences(params);
response.thenAccept(result -> {
sessionId.add(result.getSessionId());
var suggestions = result.getItems().parallelStream().map(item -> {
if (isTabOnly) {
String sanitizedText = replaceSpacesWithTabs(item.getInsertText(), tabSize);
item.setInsertText(sanitizedText);
}
return item;
}).collect(Collectors.toList());
newSuggestions.addAll(suggestions);
}).get();
Display.getDefault().asyncExec(() -> {
unresolvedTasks.remove(uuid);
if (newSuggestions == null || newSuggestions.isEmpty() || sessionId.get(0) == null || sessionId.get(0).isEmpty()) {
if (!session.isPreviewingSuggestions()) {
end();
}
Activator.getLogger().info(uuid + " returned with no result.");
if (params.getContext().getTriggerKind() == InlineCompletionTriggerKind.Invoke) {
Display display = Display.getDefault();
String message = "Q returned no suggestions";
QEclipseEditorUtils.showToast(message, display, 2000);
}
return;
} else {
Activator.getLogger().info(uuid + " returned with " + newSuggestions.size() + " results.");
}
suggestionsContext.setSessionId(sessionId.get(0));
suggestionsContext.setRequestedAtEpoch(requestInvocation);
suggestionsContext.getDetails()
.addAll(newSuggestions.stream().map(QSuggestionContext::new).collect(Collectors.toList()));
initializeSuggestionCompletionResults();
// If the caret positions has moved on from the invocation offset, we need to
// see if there exists in the suggestions fetched
// one more suggestions that qualify for what has been typed since the
// invocation.
// Note that we should not remove the ones that have been disqualified by the
// content typed since the user might still want to explore them.
int currentIdxInSuggestion = 0;
boolean hasAMatch = false;
var viewer = session.getViewer();
if (viewer == null || viewer.getTextWidget() == null || viewer.getTextWidget().getCaretOffset() < invocationOffset) {
// discard all suggestions since the current caret is behind request position
updateCompletionStates(new ArrayList<String>());
end();
return;
}
if (viewer != null && viewer.getTextWidget() != null && viewer.getTextWidget().getCaretOffset() > invocationOffset) {
var widget = viewer.getTextWidget();
int currentOffset = widget.getCaretOffset();
String prefix = widget.getTextRange(invocationOffset, currentOffset - invocationOffset);
// Computes the typed prefix and typeahead length from when user invocation happened to
// before suggestions are first shown in UI
// Note: This computation may change later on but follows the same pattern for consistency across IDEs for now
session.initialTypeaheadLength = Optional.of(prefix.length());
for (int i = 0; i < newSuggestions.size(); i++) {
if (newSuggestions.get(i).getInsertText().startsWith(prefix)) {
currentIdxInSuggestion = i;
hasAMatch = true;
break;
}
}
// indicates that typeahead prefix does not match any suggestions
if (invocationOffset != currentOffset && !hasAMatch) {
// all suggestions filtered out, mark them as discarded
updateCompletionStates(new ArrayList<String>());
end();
return;
}
// if typeahead exists, mark all suggestions except for current suggestion index with match as discarded
// As of Jan 25, current logic blocks users from toggling between suggestions when a typeahead exists in QToggleSuggestionsHandler
if (invocationOffset != currentOffset && hasAMatch) {
var currentSuggestion = suggestionsContext.getDetails().get(currentIdxInSuggestion);
var filteredSuggestions = List.of(currentSuggestion.getInlineCompletionItem().getItemId());
updateCompletionStates(filteredSuggestions);
}
}
session.invocationOffset = invocationOffset;
suggestionsContext.setCurrentIndex(currentIdxInSuggestion);
session.transitionToPreviewingState();
attachListeners();
session.primeListeners();
session.getViewer().getTextWidget().redraw();
});
} catch (InterruptedException e) {
Activator.getLogger().error("Inline completion interrupted", e);
} catch (ExecutionException e) {
Activator.getLogger().error("Error executing inline completion", e);
}
});
unresolvedTasks.put(uuid, future);
}