From ed061e10cd07f14b6934cb77d8aae136a34d9ab5 Mon Sep 17 00:00:00 2001 From: sougandhs Date: Mon, 15 Dec 2025 07:40:05 +0530 Subject: [PATCH] Support Source Edit in Compare with Clipboard Support for modifying source in compare clipboard editor and Use MultiPageEditorPart for getting active editor and in ClipboardReplace & Clipboard Compare --- .../compare/internal/ClipboardCompare.java | 177 +--------- .../internal/ClipboardCompareProcess.java | 328 ++++++++++++++++++ .../compare/internal/ClipboardReplace.java | 9 +- 3 files changed, 341 insertions(+), 173 deletions(-) create mode 100644 team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompareProcess.java diff --git a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompare.java b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompare.java index 90461dd5a25..45dcbe387e6 100644 --- a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompare.java +++ b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompare.java @@ -13,46 +13,13 @@ *******************************************************************************/ package org.eclipse.compare.internal; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.nio.charset.StandardCharsets; - -import org.eclipse.compare.CompareConfiguration; -import org.eclipse.compare.CompareEditorInput; -import org.eclipse.compare.CompareUI; -import org.eclipse.compare.IEncodedStreamContentAccessor; -import org.eclipse.compare.ITypedElement; -import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jface.action.IAction; import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.ISelection; -import org.eclipse.osgi.util.NLS; -import org.eclipse.swt.dnd.Clipboard; -import org.eclipse.swt.dnd.TextTransfer; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; -import org.eclipse.ui.IEditorPart; -import org.eclipse.ui.IObjectActionDelegate; -import org.eclipse.ui.IViewPart; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.IWorkbenchPart; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.forms.editor.FormEditor; -import org.eclipse.ui.texteditor.ITextEditor; - -public class ClipboardCompare extends BaseCompareAction implements IObjectActionDelegate { - private final String clipboard = "Clipboard"; //$NON-NLS-1$ - private final String compareFailed = "Comparision Failed"; //$NON-NLS-1$ - private IWorkbenchPart activePart; +public class ClipboardCompare extends BaseCompareAction { @Override protected void run(ISelection selection) { @@ -60,146 +27,16 @@ protected void run(ISelection selection) { Shell parentShell = CompareUIPlugin.getShell(); for (IFile file : files) { try { - processComparison(file, parentShell); + ClipboardCompareProcess pro = new ClipboardCompareProcess(); + pro.processComparison(parentShell, file); } catch (Exception e) { - MessageDialog.openError(parentShell, compareFailed, e.getMessage()); + MessageDialog.openError(parentShell, "Comparison Failed", e.getMessage()); //$NON-NLS-1$ } } } - @Override - protected boolean isEnabled(ISelection selection) { - return Utilities.getFiles(selection).length == 1 && getClipboard() != null; - } - - /** - * Process comparison with selection or entire editor contents with contents in - * clipboard - * - * @param file Editor file - * @param parentShell The shell containing this window's controls - * @throws IOException, CoreException - */ - private void processComparison(IFile file, Shell parentShell) throws IOException, CoreException { - String cb = getClipboard().toString(); - String fileName = file.getName(); - IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); - IEditorPart editor = page.getActiveEditor(); - if (activePart instanceof IViewPart) { - String fileContents = new String(file.getContents().readAllBytes(), file.getCharset()); - showComparison(fileContents, fileName, cb, parentShell); - return; - } - final String selectionContents; - if (editor instanceof FormEditor fromEditor) { - editor = fromEditor.getActiveEditor(); - } - if (editor instanceof ITextEditor txtEditor) { - ISelection selection = txtEditor.getSelectionProvider().getSelection(); - if (selection instanceof ITextSelection textSelection) { - selectionContents = textSelection.getText(); - if (selectionContents.isEmpty()) { - String fileContents = new String(file.getContents().readAllBytes(), file.getCharset()); - showComparison(fileContents, fileName, cb, parentShell); - } else { - showComparison(selectionContents, fileName, cb, parentShell); - } - return; - } - } - if (editor instanceof CompareEditor existingCompare) { // if selection is from compare editor itself - ISelection selection = existingCompare.getSite().getSelectionProvider().getSelection(); - if (selection instanceof ITextSelection textSelection) { - String selectedText = textSelection.getText(); - String fileContents = new String(file.getContents().readAllBytes(), file.getCharset()); - showComparison(fileContents, fileName, selectedText, parentShell); - } - } - } - - /** - * Shows comparison result - * - * @param source Either selection from current editor or entire - * editor if no selection - * @param fileName Editor file name - * @param clipboardContents Contents in clipboard - * @param parentShell The shell containing this window's controls - */ - private void showComparison(String source, String fileName, String clipboardContents, Shell parentShell) { - class ClipboardTypedElement implements ITypedElement, IEncodedStreamContentAccessor { - private final String name; - private final String content; - - public ClipboardTypedElement(String name, String content) { - this.name = name; - this.content = content; - } - - @Override - public String getName() { - return name; - } - - @Override - public Image getImage() { - return null; - } - - @Override - public String getType() { - return null; - } - - @Override - public String getCharset() throws CoreException { - return "UTF-8"; //$NON-NLS-1$ - } - - @Override - public InputStream getContents() throws CoreException { - return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); - } - - } - if (source == null) { - MessageDialog.openInformation(parentShell, compareFailed, "Failed to process selected file"); //$NON-NLS-1$ - return; - } - CompareConfiguration config = new CompareConfiguration(); - config.setLeftLabel(fileName); - config.setRightLabel(clipboard); - config.setLeftEditable(true); - config.setRightEditable(true); - CompareEditorInput compareInput = new CompareEditorInput(config) { - @Override - protected Object prepareInput(IProgressMonitor monitor) - throws InvocationTargetException, InterruptedException { - return new DiffNode(new ClipboardTypedElement(fileName, source), - new ClipboardTypedElement(clipboard, clipboardContents)); - - } - }; - compareInput.setTitle(NLS.bind(CompareMessages.CompareWithClipboardTitle, fileName)); - CompareUI.openCompareEditor(compareInput); - } - - /** - * Returns Clipboard Object or null if there is nothing in clipboard - * - * @returns Clipboard Object or null - */ - private Object getClipboard() { - Clipboard clip = new Clipboard(Display.getDefault()); - try { - return clip.getContents(TextTransfer.getInstance()); - } finally { - clip.dispose(); - } - } @Override - public void setActivePart(IAction action, IWorkbenchPart targetPart) { - this.activePart = targetPart; + protected boolean isEnabled(ISelection selection) { + return Utilities.getFiles(selection).length == 1 && ClipboardCompareProcess.getClipboard() != null; } - -} +} \ No newline at end of file diff --git a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompareProcess.java b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompareProcess.java new file mode 100644 index 00000000000..b78b65f53e1 --- /dev/null +++ b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardCompareProcess.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * + * 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.compare.internal; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.nio.charset.StandardCharsets; + +import org.eclipse.compare.CompareConfiguration; +import org.eclipse.compare.CompareEditorInput; +import org.eclipse.compare.CompareUI; +import org.eclipse.compare.IEditableContent; +import org.eclipse.compare.IEncodedStreamContentAccessor; +import org.eclipse.compare.ITypedElement; +import org.eclipse.compare.ResourceNode; +import org.eclipse.compare.structuremergeviewer.DiffNode; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IResourceChangeListener; +import org.eclipse.core.resources.IResourceDelta; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.dnd.Clipboard; +import org.eclipse.swt.dnd.TextTransfer; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IReusableEditor; +import org.eclipse.ui.IViewPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.part.MultiPageEditorPart; +import org.eclipse.ui.texteditor.ITextEditor; + +public class ClipboardCompareProcess implements IObjectActionDelegate { + + private final String clipboard = "Clipboard"; //$NON-NLS-1$ + private IFile currentResource; + private IWorkbenchPart activePart; + private IReusableEditor compareEditor; + IResourceChangeListener listener; + private String partialSelection; + + public void processComparison(Shell parentShell, IFile file) throws IOException, CoreException { + currentResource = file; + String cb = getClipboard().toString(); + String fileName = currentResource.getName(); + + IWorkbenchWindow window = CompareUIPlugin.getActiveWorkbenchWindow(); + if (window == null) + return; + + IWorkbenchPage page = window.getActivePage(); + if (page == null) + return; + + IEditorPart editor = page.getActiveEditor(); + if (activePart instanceof IViewPart) { + showComparison(fileName, cb, parentShell, false, 0, 0); + return; + } + + if (editor instanceof MultiPageEditorPart mpe) { + Object selectedPage = mpe.getSelectedPage(); + if (selectedPage instanceof IEditorPart e) { + editor = e; + } + } + + if (editor instanceof ITextEditor txtEditor) { + ISelection selection = txtEditor.getSelectionProvider().getSelection(); + if (selection instanceof ITextSelection textSelection) { + String selected = textSelection.getText(); + + if (selected.isEmpty()) { + showComparison(fileName, cb, parentShell, false, 0, 0); + } else { + showComparison(fileName, cb, parentShell, true, textSelection.getOffset(), + textSelection.getLength()); + } + return; + } + } + showComparison(fileName, cb, parentShell, false, 0, 0); + } + + private void showComparison(String fileName, String clipboardContents, Shell parentShell, + boolean isPartialSelection, int offset, int length) { + class ClipboardTypedElement implements ITypedElement, IEncodedStreamContentAccessor { + private final String name; + private final String content; + + public ClipboardTypedElement(String name, String content) { + this.name = name; + this.content = content; + } + + @Override + public String getName() { + return name; + } + + @Override + public Image getImage() { + return null; + } + + @Override + public String getType() { + return null; + } + + @Override + public String getCharset() { + return "UTF-8"; //$NON-NLS-1$ + } + + @Override + public InputStream getContents() { + return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); + } + } + class EditableFileNode extends ResourceNode implements IEditableContent { + + public EditableFileNode(IFile file) { + super(file); + } + + @Override + public InputStream getContents() throws CoreException { + IFile file = (IFile) getResource(); + try { + String content = new String(file.getContents().readAllBytes(), file.getCharset()); + if (!isPartialSelection) { + return new ByteArrayInputStream(content.getBytes(file.getCharset())); + } + if (partialSelection != null) { + return new ByteArrayInputStream(partialSelection.getBytes(file.getCharset())); + } + int start = Math.max(0, Math.min(offset, content.length())); + int end = Math.max(start, Math.min(offset + length, content.length())); + + String selectedPart = content.substring(start, end); + + return new ByteArrayInputStream(selectedPart.getBytes(file.getCharset())); + } catch (IOException e) { + ILog.of(getClass()).error(e.getMessage(), e); + } + + return new ByteArrayInputStream(file.readAllBytes()); + } + + @Override + public void setContent(byte[] newContent) { + try { + IFile file = (IFile) getResource(); + String charset = file.getCharset(); + + if (!isPartialSelection) { + file.setContents(new ByteArrayInputStream(newContent), IResource.FORCE | IResource.KEEP_HISTORY, + null); + } else { + String original = new String(file.getContents().readAllBytes(), charset); + + String updated = new String(newContent, charset); + partialSelection = updated; + int start = Math.max(0, Math.min(offset, original.length())); + int end = Math.max(start, Math.min(start + length, original.length())); + + String result = original.substring(0, start) + updated + original.substring(end); + file.setContents(new ByteArrayInputStream(result.getBytes(charset)), + IResource.FORCE | IResource.KEEP_HISTORY, null); + } + + } catch (Exception e) { + ILog.of(getClass()).error(e.getMessage(), e); + } + } + + @Override + public boolean isEditable() { + return true; + } + } + + CompareConfiguration config = new CompareConfiguration(); + config.setLeftLabel(fileName); + config.setRightLabel(clipboard); + config.setLeftEditable(true); + config.setRightEditable(true); + + final CompareEditorInput[] inputHolder = new CompareEditorInput[1]; + CompareEditorInput input = new CompareEditorInput(config) { + @Override + protected Object prepareInput(IProgressMonitor monitor) + throws InvocationTargetException, InterruptedException { + + ITypedElement left = new EditableFileNode(currentResource); + ITypedElement right = new ClipboardTypedElement(clipboard, clipboardContents); + return new DiffNode(left, right); + } + }; + input.setTitle(NLS.bind(CompareMessages.CompareWithClipboardTitle, fileName)); + inputHolder[0] = input; + + CompareUI.openCompareEditor(input); + listener = event -> { + try { + if (event.getDelta() == null) { + return; + } + event.getDelta().accept(delta -> { + if (delta.getResource() instanceof IFile file) { + if (!file.equals(currentResource)) { + return true; + } + + if ((delta.getFlags() & IResourceDelta.CONTENT) == 0) { + return true; + } + Display.getDefault().asyncExec(() -> { + if (compareEditor != null) { + + CompareUI.reuseCompareEditor(inputHolder[0], compareEditor); + } + }); + } + return true; + }); + + } catch (Exception e) { + ILog.of(getClass()).error(e.getMessage(), e); + } + }; + ResourcesPlugin.getWorkspace().addResourceChangeListener(listener); + IWorkbenchPage page = CompareUIPlugin.getActiveWorkbenchWindow().getActivePage(); + IEditorPart editor = page.getActiveEditor(); + if (editor instanceof IReusableEditor reusable) { + compareEditor = reusable; + + } + page.addPartListener(new IPartListener2() { + + @Override + public void partClosed(IWorkbenchPartReference partRef) { + IWorkbenchPart part = partRef.getPart(false); + if (part == compareEditor) { + ResourcesPlugin.getWorkspace().removeResourceChangeListener(listener); + page.removePartListener(this); + } + } + + @Override + public void partOpened(IWorkbenchPartReference ref) { + } + + @Override + public void partActivated(IWorkbenchPartReference ref) { + } + + @Override + public void partDeactivated(IWorkbenchPartReference ref) { + } + + @Override + public void partHidden(IWorkbenchPartReference ref) { + } + + @Override + public void partVisible(IWorkbenchPartReference ref) { + } + + @Override + public void partInputChanged(IWorkbenchPartReference ref) { + } + + @Override + public void partBroughtToTop(IWorkbenchPartReference ref) { + } + }); + } + + public static Object getClipboard() { + Clipboard clip = new Clipboard(Display.getDefault()); + try { + return clip.getContents(TextTransfer.getInstance()); + } finally { + clip.dispose(); + } + } + + @Override + public void run(IAction action) { + } + + @Override + public void selectionChanged(IAction action, ISelection selection) { + } + + @Override + public void setActivePart(IAction action, IWorkbenchPart targetPart) { + this.activePart = targetPart; + } +} \ No newline at end of file diff --git a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardReplace.java b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardReplace.java index 2faa7b4171e..7ec5f975661 100644 --- a/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardReplace.java +++ b/team/bundles/org.eclipse.compare/compare/org/eclipse/compare/internal/ClipboardReplace.java @@ -31,7 +31,7 @@ import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.forms.editor.FormEditor; +import org.eclipse.ui.part.MultiPageEditorPart; import org.eclipse.ui.texteditor.ITextEditor; public class ClipboardReplace extends BaseCompareAction { @@ -43,8 +43,11 @@ protected void run(ISelection selection) { try { IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); IEditorPart editor = page.getActiveEditor(); - if (editor instanceof FormEditor fromEditor) { - editor = fromEditor.getActiveEditor(); + if (editor instanceof MultiPageEditorPart mpe) { + Object page2 = mpe.getSelectedPage(); + if (page2 instanceof IEditorPart e) { + editor = e; + } } IEditorInput input = editor.getEditorInput(); if (input instanceof IFileEditorInput ed) {