diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/ColumnPresentationAdapterFactory.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/ColumnPresentationAdapterFactory.java index 8eb3802f74..7e16743f9e 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/ColumnPresentationAdapterFactory.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/ColumnPresentationAdapterFactory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006, 2015 IBM Corporation and others. + * Copyright (c) 2006, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -16,6 +16,7 @@ import org.eclipse.core.runtime.IAdapterFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory; import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor; +import org.eclipse.debug.ui.IWatchExpressionCellEditorFactory; import org.eclipse.jdt.debug.core.IJavaStackFrame; import org.eclipse.jdt.debug.core.IJavaVariable; @@ -28,6 +29,7 @@ public class ColumnPresentationAdapterFactory implements IAdapterFactory { private static final IColumnPresentationFactory fgColumnPresentation = new JavaVariableColumnPresentationFactory(); private static final IElementEditor fgEEJavaVariable = new JavaVariableEditor(); + private static final IWatchExpressionCellEditorFactory fgWatchExpressionCellEditorFactory = new JavaWatchExpressionCellEditorFactory(); /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdapterFactory#getAdapter(java.lang.Object, java.lang.Class) @@ -44,6 +46,9 @@ public T getAdapter(Object adaptableObject, Class adapterType) { if (IColumnPresentationFactory.class.equals(adapterType)) { return (T) fgColumnPresentation; } + if (IWatchExpressionCellEditorFactory.class.equals(adapterType)) { + return (T) fgWatchExpressionCellEditorFactory; + } } return null; } @@ -55,7 +60,8 @@ public T getAdapter(Object adaptableObject, Class adapterType) { public Class[] getAdapterList() { return new Class[]{ IColumnPresentationFactory.class, - IElementEditor.class}; + IElementEditor.class, + IWatchExpressionCellEditorFactory.class}; } } diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JDISourceViewerCellEditor.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JDISourceViewerCellEditor.java new file mode 100644 index 0000000000..85f0a35259 --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JDISourceViewerCellEditor.java @@ -0,0 +1,196 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui.variables; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.IHandler; +import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; +import org.eclipse.jdt.internal.debug.ui.JDISourceViewer; +import org.eclipse.jdt.internal.debug.ui.contentassist.CurrentFrameContext; +import org.eclipse.jdt.internal.debug.ui.contentassist.JavaDebugContentAssistProcessor; +import org.eclipse.jdt.internal.debug.ui.display.DisplayViewerConfiguration; +import org.eclipse.jdt.ui.text.IJavaPartitions; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.contentassist.ICompletionListener; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.source.ContentAssistantFacade; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.FocusAdapter; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; + +/** + * A {@link CellEditor} for the Expressions view that wraps a {@link JDISourceViewer} configured with Java content assist. + */ +public class JDISourceViewerCellEditor extends CellEditor { + + private JDISourceViewer fViewer; + private String fValue = ""; //$NON-NLS-1$ + /** Tracks whether the content-assist proposal popup is currently open. */ + private boolean fProposalPopupOpen = false; + private IHandlerService fHandlerService; + private IHandler fContentAssistHandler; + private IHandlerActivation fContentAssistActivation; + + public JDISourceViewerCellEditor(Composite parent) { + super(parent, SWT.NONE); + } + + @Override + protected Control createControl(Composite parent) { + fViewer = new JDISourceViewer(parent, null, SWT.BORDER | SWT.SINGLE | SWT.LEFT_TO_RIGHT); + + Document document = new Document(); + JDIDebugUIPlugin.getDefault().getJavaTextTools().setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING); + + fViewer.setInput(document); + fViewer.configure(new DisplayViewerConfiguration() { + @Override + public IContentAssistProcessor getContentAssistantProcessor() { + return new JavaDebugContentAssistProcessor(new CurrentFrameContext()); + } + }); + fViewer.setEditable(true); + + ContentAssistantFacade facade = fViewer.getContentAssistantFacade(); + if (facade != null) { + facade.addCompletionListener(new ICompletionListener() { + @Override + public void assistSessionStarted(org.eclipse.jface.text.contentassist.ContentAssistEvent event) { + fProposalPopupOpen = true; + } + @Override + public void assistSessionEnded(org.eclipse.jface.text.contentassist.ContentAssistEvent event) { + fProposalPopupOpen = false; + } + @Override + public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) { + // not needed + } + }); + } + + fContentAssistHandler = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws org.eclipse.core.commands.ExecutionException { + fViewer.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); + return null; + } + }; + fHandlerService = PlatformUI.getWorkbench().getAdapter(IHandlerService.class); + + fViewer.getTextWidget().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + activateContentAssistHandler(); + } + @Override + public void focusLost(FocusEvent e) { + deactivateContentAssistHandler(); + if (!fProposalPopupOpen) { + JDISourceViewerCellEditor.this.focusLost(); + } + } + }); + + fViewer.appendVerifyKeyListener(e -> { + if (!e.doit) { + return; + } + if (e.keyCode == ' ' && (e.stateMask & SWT.CTRL) != 0) { + fViewer.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); + e.doit = false; + } else if (!fProposalPopupOpen) { + if (e.keyCode == SWT.CR && (e.stateMask & SWT.SHIFT) == 0) { + fireApplyEditorValue(); + deactivate(); + e.doit = false; + } else if (e.keyCode == SWT.ESC) { + fireCancelEditor(); + e.doit = false; + } + } + }); + + return fViewer.getControl(); + } + + @Override + protected Object doGetValue() { + if (fViewer != null && fViewer.getDocument() != null) { + return fViewer.getDocument().get(); + } + return fValue; + } + + @Override + protected boolean dependsOnExternalFocusListener() { + return false; + } + + @Override + protected void doSetFocus() { + if (fViewer != null) { + fViewer.getTextWidget().setFocus(); + } + } + + @Override + protected void doSetValue(Object value) { + fValue = (value == null) ? "" : value.toString(); //$NON-NLS-1$ + if (fViewer != null && fViewer.getDocument() != null) { + fViewer.getDocument().set(fValue); + // Move cursor to end + fViewer.getTextWidget().setCaretOffset(fValue.length()); + } + } + + @Override + public void deactivate() { + deactivateContentAssistHandler(); + super.deactivate(); + } + + @Override + public void dispose() { + deactivateContentAssistHandler(); + if (fViewer != null) { + fViewer.dispose(); + } + super.dispose(); + } + + private void activateContentAssistHandler() { + if (fHandlerService != null && fContentAssistActivation == null) { + fContentAssistActivation = fHandlerService.activateHandler(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS, fContentAssistHandler); + } + } + + private void deactivateContentAssistHandler() { + if (fHandlerService != null && fContentAssistActivation != null) { + fHandlerService.deactivateHandler(fContentAssistActivation); + fContentAssistActivation = null; + } + } + +} diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaWatchExpressionCellEditorFactory.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaWatchExpressionCellEditorFactory.java new file mode 100644 index 0000000000..c1ce65f37c --- /dev/null +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/variables/JavaWatchExpressionCellEditorFactory.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.ui.variables; + +import java.util.Optional; + +import org.eclipse.debug.ui.IWatchExpressionCellEditorFactory; +import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; +import org.eclipse.jdt.internal.debug.ui.contentassist.CurrentFrameContext; +import org.eclipse.jdt.internal.debug.ui.contentassist.JavaDebugContentAssistProcessor; +import org.eclipse.jdt.internal.debug.ui.display.DisplayViewerConfiguration; +import org.eclipse.jdt.ui.text.IJavaPartitions; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.swt.widgets.Composite; + +public class JavaWatchExpressionCellEditorFactory implements IWatchExpressionCellEditorFactory { + + @Override + public Optional createCellEditor(Composite parent) { + return Optional.of(new JDISourceViewerCellEditor(parent)); + } + + @Override + public void configureSourceViewer(SourceViewer viewer) { + viewer.configure(new DisplayViewerConfiguration() { + @Override + public IContentAssistProcessor getContentAssistantProcessor() { + return new JavaDebugContentAssistProcessor(new CurrentFrameContext()); + } + }); + } + + @Override + public void prepareDocument(IDocument document) { + JDIDebugUIPlugin.getDefault().getJavaTextTools() + .setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING); + } +}