Skip to content

Commit 5e51ee6

Browse files
authored
Merge ADT support into main (#501)
Merges feature branch containing improvements for ADT ABAP support into main. Relevant PRs * adt support #487 * Support updating remote when ABAP files are edited #495 * handle the null case for contentType in adt plugin environment #496 * Fix: Handle removing paint listeners with ADT viewer and allow multiline text in inline chat #500
1 parent 4b354ce commit 5e51ee6

File tree

9 files changed

+606
-62
lines changed

9 files changed

+606
-62
lines changed

plugin/plugin.xml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,8 +567,29 @@
567567
<equals value="org.eclipse.ui.DefaultTextEditor"/>
568568
<equals value="org.eclipse.jdt.ui.CompilationUnitEditor"/>
569569
<equals value="org.eclipse.ui.genericeditor.GenericEditor"/>
570-
<equals value="software.aws.toolkits.eclipse.amazonq.views.AmazonQViewContainer"/>
571-
<equals value="com.sap.adt.programs.ui.AdtProgramEditor"/>
570+
<equals value="software.aws.toolkits.eclipse.amazonq.views.AmazonQViewContainer"/>
571+
<equals value="com.sap.adt.programs.ui.AdtProgramEditor"/>
572+
<equals value="com.sap.adt.programs.ui.AdtIncludeEditor"/>
573+
<equals value="com.sap.adt.abapClassEditor"/>
574+
<equals value="com.sap.adt.abapInterfaceEditor"/>
575+
<!-- SAP GUI Editor for .asprog files -->
576+
<equals value="com.sap.adt.sapgui.ui.editors.WinGuiEditor"/>
577+
<!-- CDS Service Definition Editor for .assrvds files -->
578+
<equals value="com.sap.adt.cds.srvd.ui.editors.SrvdSourceEditor"/>
579+
<!-- Behavior Definition Editor for .asbdef files -->
580+
<equals value="com.sap.wbobj.bdef.ui.editors.BdefSourceEditor"/>
581+
<!-- CDS Data Definition Language Editor for .asddls files -->
582+
<equals value="com.sap.adt.cds.ddl.ui.editors.DdlSourceEditor"/>
583+
<!-- Dictionary Table Editor for .astabldt files -->
584+
<equals value="com.sap.wbobj.dictionary.tabldt.ui.editors.DdicTableEditor"/>
585+
<!-- Dictionary Structure Editor for .astablds files -->
586+
<equals value="com.sap.wbobj.dictionary.tablds.ui.editors.DdicStructureEditor"/>
587+
<!-- ABAP Function Group Include Editor for .asfugr files -->
588+
<equals value="com.sap.adt.functions.ui.includes.abapFunctionGroupIncludeEditor"/>
589+
<!-- ABAP Function Module Editor for .asfunc files -->
590+
<equals value="com.sap.adt.functions.ui.fmodules.abapFunctionModuleEditor"/>
591+
<!-- ABAP Function Group Editor for .asfugr files -->
592+
<equals value="com.sap.adt.functions.ui.includes.abapFunctionGroupEditor"/>
572593
</or>
573594
</with>
574595
</and>

plugin/src/software/aws/toolkits/eclipse/amazonq/inlineChat/InlineChatUIManager.java

Lines changed: 78 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
package software.aws.toolkits.eclipse.amazonq.inlineChat;
55

6+
import java.util.Arrays;
7+
import java.util.HashSet;
68
import java.util.Optional;
79
import java.util.concurrent.CompletableFuture;
810
import java.util.concurrent.atomic.AtomicReference;
@@ -27,13 +29,12 @@
2729
import org.eclipse.swt.widgets.Composite;
2830
import org.eclipse.swt.widgets.Control;
2931
import org.eclipse.swt.widgets.Display;
32+
import org.eclipse.swt.widgets.Listener;
3033
import org.eclipse.swt.widgets.Text;
3134

3235
import software.aws.toolkits.eclipse.amazonq.chat.models.CursorState;
3336
import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
3437
import software.aws.toolkits.eclipse.amazonq.util.Constants;
35-
import software.aws.toolkits.eclipse.amazonq.util.PluginPlatform;
36-
import software.aws.toolkits.eclipse.amazonq.util.PluginUtils;
3738
import software.aws.toolkits.eclipse.amazonq.util.ToolkitNotification;
3839

3940
public final class InlineChatUIManager {
@@ -52,6 +53,7 @@ public final class InlineChatUIManager {
5253
private final String decidingMessage = "Accept (Enter) | Reject (Esc)";
5354
private boolean isDarkTheme;
5455
private int latestOffset;
56+
private Listener paintListenerRef = null;
5557

5658
private InlineChatUIManager() {
5759
// Prevent instantiation
@@ -134,53 +136,41 @@ protected Control createDialogArea(final Composite parent) {
134136
var composite = (Composite) super.createDialogArea(parent);
135137
composite.setLayout(new GridLayout(1, false));
136138

137-
inputField = new Text(composite, SWT.SEARCH | SWT.BORDER | SWT.SINGLE);
138-
if (PluginUtils.getPlatform() == PluginPlatform.WINDOWS) {
139-
Display.getDefault().asyncExec(() -> {
140-
inputField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
141-
inputField.setText(inputPromptMessage);
142-
});
139+
inputField = new Text(composite, SWT.BORDER | SWT.MULTI);
140+
Display.getDefault().asyncExec(() -> {
141+
inputField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
142+
inputField.setText(inputPromptMessage);
143+
});
143144

144-
inputField.addKeyListener(new KeyAdapter() {
145-
@Override
146-
public void keyPressed(final KeyEvent e) {
147-
// If this is the first character being typed
148-
boolean backspace = (e.keyCode == SWT.DEL || e.keyCode == SWT.BS);
149-
if (inputField.getText().equals(inputPromptMessage)) {
150-
if (!backspace) {
151-
inputField.setText("");
152-
inputField.setForeground(isDarkTheme
153-
? Display.getDefault().getSystemColor(SWT.COLOR_WHITE)
154-
: Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
155-
}
156-
e.doit = !backspace;
157-
} else if (backspace && inputField.getText().length() <= 1) {
158-
inputField.setText(inputPromptMessage);
159-
inputField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
145+
inputField.addKeyListener(new KeyAdapter() {
146+
@Override
147+
public void keyPressed(final KeyEvent e) {
148+
// If this is the first character being typed
149+
boolean backspace = (e.keyCode == SWT.DEL || e.keyCode == SWT.BS);
150+
if (inputField.getText().equals(inputPromptMessage)) {
151+
if (!backspace) {
152+
inputField.setText("");
153+
inputField.setForeground(isDarkTheme
154+
? Display.getDefault().getSystemColor(SWT.COLOR_WHITE)
155+
: Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
160156
}
157+
e.doit = !backspace;
158+
} else if (backspace && inputField.getText().length() <= 1) {
159+
inputField.setText(inputPromptMessage);
160+
inputField.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
161161
}
162-
});
163-
} else {
164-
inputField.setMessage(inputPromptMessage);
165-
}
162+
}
163+
});
166164

167165
GridData gridData = new GridData(GridData.FILL_HORIZONTAL);
168166
gridData.widthHint = 350;
167+
gridData.heightHint = 25;
169168
inputField.setLayoutData(gridData);
170169

171-
// Enforce maximum character count that can be entered into the input
172-
inputField.addVerifyListener(e -> {
173-
String currentText = inputField.getText();
174-
String newText = currentText.substring(0, e.start) + e.text + currentText.substring(e.end);
175-
if (newText.length() > maxInputLength) {
176-
e.doit = false; // Prevent the input
177-
}
178-
});
179-
180170
inputField.addKeyListener(new KeyAdapter() {
181171
@Override
182172
public void keyPressed(final KeyEvent e) {
183-
if (e.character == SWT.CR || e.character == SWT.LF) {
173+
if (e.keyCode == SWT.CR || e.keyCode == SWT.LF) {
184174
// Gather inputs and send back to controller
185175
var userInput = inputField.getText();
186176
if (userInputIsValid(userInput)) {
@@ -235,14 +225,51 @@ private void showPrompt(final String promptText) {
235225
latestOffset = task.getSelectionOffset();
236226
}
237227
currentPaintListener = createPaintListenerPrompt(widget, latestOffset, promptText, isDarkTheme);
238-
widget.addPaintListener(currentPaintListener);
228+
addPaintListenerAndCapture(widget, currentPaintListener);
239229
widget.redraw();
240230
} catch (Exception e) {
241231
Activator.getLogger().error("Failed to create paint listener: " + e.getMessage(), e);
242232
}
243233
});
244234
}
245235

236+
private void addPaintListenerAndCapture(final StyledText widget, final PaintListener paintListener) {
237+
var listenersBefore = new HashSet<>(Arrays.asList(widget.getListeners(SWT.Paint)));
238+
widget.addPaintListener(paintListener);
239+
var listenersAfter = widget.getListeners(SWT.Paint);
240+
for (var listener : listenersAfter) {
241+
if (!listenersBefore.contains(listener)) {
242+
if (isAdtPaintListener(listener)) {
243+
paintListenerRef = listener;
244+
}
245+
break;
246+
}
247+
}
248+
}
249+
250+
/**
251+
* ADT Viewers wrap the paint listener into an internal delegate(PaintListenerDelegate).
252+
* which needs to be captured to effectively remove it later
253+
* @param listener
254+
* @return listener ref
255+
*/
256+
private boolean isAdtPaintListener(final Listener listener) {
257+
try {
258+
// Use reflection to check if this wraps a PaintListenerDelegate
259+
if (listener.getClass().getName().contains("TypedListener")) {
260+
var eventListenerField = listener.getClass().getDeclaredField("eventListener");
261+
eventListenerField.setAccessible(true);
262+
Object eventListener = eventListenerField.get(listener);
263+
if (eventListener != null && eventListener.getClass().getName().contains("PaintListenerDelegate")) {
264+
return true;
265+
}
266+
}
267+
} catch (Exception e) {
268+
// Ignore reflection errors
269+
}
270+
return false;
271+
}
272+
246273
public void updatePromptPosition(final SessionState state) {
247274
try {
248275
int offset = ((ITextViewerExtension5) viewer).modelOffset2WidgetOffset(task.getSelectionOffset());
@@ -340,9 +367,17 @@ private void removeCurrentPaintListener() {
340367
return;
341368
}
342369
try {
343-
if (viewer.getTextWidget() != null && !viewer.getTextWidget().isDisposed() && currentPaintListener != null) {
344-
viewer.getTextWidget().removePaintListener(currentPaintListener);
345-
viewer.getTextWidget().redraw();
370+
var widget = viewer.getTextWidget();
371+
if (widget != null && !widget.isDisposed() && currentPaintListener != null) {
372+
// remove adt specific paint listener if present
373+
if (paintListenerRef != null) {
374+
widget.removeListener(SWT.Paint, paintListenerRef);
375+
paintListenerRef = null;
376+
}
377+
if (currentPaintListener != null) {
378+
widget.removePaintListener(currentPaintListener);
379+
}
380+
widget.redraw();
346381
currentPaintListener = null;
347382
}
348383
} catch (Exception e) {
@@ -364,7 +399,7 @@ private int calculateIndentOffset(final StyledText widget, final int currentOffs
364399
}
365400

366401
private boolean userInputIsValid(final String input) {
367-
return input != null && input.length() >= 2 && input.length() < maxInputLength;
402+
return input != null && input.length() >= 2;
368403
}
369404

370405
/**

plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspClientImpl.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.eclipse.core.resources.ResourcesPlugin;
2828
import org.eclipse.core.runtime.CoreException;
2929
import org.eclipse.core.runtime.IPath;
30-
3130
import org.eclipse.core.runtime.NullProgressMonitor;
3231
import org.eclipse.core.runtime.Path;
3332
import org.eclipse.jface.action.IAction;
@@ -90,6 +89,7 @@
9089
import software.aws.toolkits.eclipse.amazonq.util.QEclipseEditorUtils;
9190
import software.aws.toolkits.eclipse.amazonq.preferences.AmazonQPreferencePage;
9291
import software.aws.toolkits.eclipse.amazonq.telemetry.service.DefaultTelemetryService;
92+
import software.aws.toolkits.eclipse.amazonq.util.AbapUtil;
9393
import software.aws.toolkits.eclipse.amazonq.util.Constants;
9494
import software.aws.toolkits.eclipse.amazonq.util.ObjectMapperFactory;
9595
import software.aws.toolkits.eclipse.amazonq.util.ThemeDetector;
@@ -539,11 +539,19 @@ public final void didCopyFile(final Object params) {
539539

540540
@Override
541541
public final void didWriteFile(final Object params) {
542+
var path = extractFilePathFromParams(params);
543+
if (AbapUtil.isAbapFile(path)) {
544+
AbapUtil.updateAdtServer(path);
545+
}
542546
refreshProjects();
543547
}
544548

545549
@Override
546550
public final void didAppendFile(final Object params) {
551+
var path = extractFilePathFromParams(params);
552+
if (AbapUtil.isAbapFile(path)) {
553+
AbapUtil.updateAdtServer(path);
554+
}
547555
refreshProjects();
548556
}
549557

@@ -559,6 +567,7 @@ public final void didCreateDirectory(final Object params) {
559567

560568
private void refreshProjects() {
561569
WorkspaceUtils.refreshAllProjects();
570+
WorkspaceUtils.refreshAdtViews();
562571
}
563572

564573
private boolean isUriInWorkspace(final String uri) {
@@ -577,6 +586,15 @@ private boolean isUriInWorkspace(final String uri) {
577586
}
578587
}
579588

589+
private String extractFilePathFromParams(final Object params) {
590+
if (params instanceof Map) {
591+
var map = (Map<?, ?>) params;
592+
Object path = map.get("path");
593+
return path != null ? path.toString() : null;
594+
}
595+
return null;
596+
}
597+
580598
@Override
581599
public final void sendPinnedContext(final Object params) {
582600
Object updatedParams = params;

plugin/src/software/aws/toolkits/eclipse/amazonq/lsp/AmazonQLspServerBuilder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.eclipse.lsp4j.jsonrpc.messages.Message;
1515
import org.eclipse.lsp4j.jsonrpc.messages.RequestMessage;
1616
import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage;
17+
import org.apache.commons.lang3.StringUtils;
1718

1819
import com.google.gson.ToNumberPolicy;
1920

@@ -22,6 +23,7 @@
2223
import software.aws.toolkits.eclipse.amazonq.plugin.Activator;
2324
import software.aws.toolkits.eclipse.amazonq.telemetry.metadata.ClientMetadata;
2425
import software.aws.toolkits.eclipse.amazonq.telemetry.metadata.PluginClientMetadata;
26+
import software.aws.toolkits.eclipse.amazonq.util.AbapUtil;
2527

2628
public class AmazonQLspServerBuilder extends Builder<AmazonQLspServer> {
2729

@@ -67,11 +69,29 @@ private Map<String, Object> getInitializationOptions(final ClientMetadata metada
6769
return initOptions;
6870
}
6971

72+
private void sanitizeWorkspaceFoldersForAbap(final InitializeParams initParams) {
73+
try {
74+
if (initParams.getWorkspaceFolders() != null) {
75+
initParams.getWorkspaceFolders().forEach(folder -> {
76+
if (StringUtils.isNotBlank(folder.getUri()) && folder.getUri().startsWith(AbapUtil.SEMANTIC_FS_SCHEME)) {
77+
String convertedUri = AbapUtil.convertSemanticUriToPath(folder.getUri());
78+
if (StringUtils.isNotBlank(convertedUri)) {
79+
folder.setUri(convertedUri);
80+
}
81+
}
82+
});
83+
}
84+
} catch (Exception e) {
85+
Activator.getLogger().error("Error sanitizing workspace folders for ABAP", e);
86+
}
87+
}
88+
7089
@Override
7190
protected final MessageConsumer wrapMessageConsumer(final MessageConsumer consumer) {
7291
return super.wrapMessageConsumer((final Message message) -> {
7392
if (message instanceof RequestMessage && ((RequestMessage) message).getMethod().equals("initialize")) {
7493
InitializeParams initParams = (InitializeParams) ((RequestMessage) message).getParams();
94+
sanitizeWorkspaceFoldersForAbap(initParams);
7595
ClientMetadata metadata = PluginClientMetadata.getInstance();
7696
initParams.setClientInfo(new ClientInfo(USER_AGENT_CLIENT_NAME, metadata.getPluginVersion()));
7797
initParams.setInitializationOptions(getInitializationOptions(metadata));

0 commit comments

Comments
 (0)