blob: e6afd16731723445a49d409b01535dfd674a0664 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 QNX Software Systems 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:
* QNX Software Systems - Initial API and implementation
* Markus Schorn (Wind River Systems)
* Norbert Ploett (Siemens AG)
* Anton Leherbauer (Wind River Systems)
* Ed Swartz (Nokia)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.IBinary;
import org.eclipse.cdt.core.model.IBuffer;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IIncludeReference;
import org.eclipse.cdt.core.model.ISourceRange;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.core.resources.FileStorage;
import org.eclipse.cdt.internal.core.resources.ResourceLookup;
import org.eclipse.cdt.internal.ui.ICStatusConstants;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.CEditorMessages;
import org.eclipse.cdt.internal.ui.editor.ITranslationUnitEditorInput;
import org.eclipse.cdt.internal.ui.text.LineComparator;
import org.eclipse.cdt.ui.CDTUITools;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.utils.UNCPathConverter;
import org.eclipse.compare.rangedifferencer.IRangeComparator;
import org.eclipse.compare.rangedifferencer.RangeDifference;
import org.eclipse.compare.rangedifferencer.RangeDifferencer;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IStorage;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.ISafeRunnable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextFileDocumentProvider;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.ResourceUtil;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.MultiEditorInput;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditor;
import com.ibm.icu.text.MessageFormat;
public class EditorUtility {
/**
* The ID of the default text editor
*/
public static final String DEFAULT_TEXT_EDITOR_ID = EditorsUI.DEFAULT_TEXT_EDITOR_ID;
private EditorUtility() {
}
/**
* Tests if a cu is currently shown in an editor
* @return the IEditorPart if shown, null if element is not open in an editor
*/
public static IEditorPart isOpenInEditor(Object inputElement) {
IEditorInput input = null;
try {
input = getEditorInput(inputElement);
} catch (CModelException x) {
//CUIPlugin.log(x.getStatus());
}
if (input != null) {
IWorkbenchPage p = CUIPlugin.getActivePage();
if (p != null) {
return p.findEditor(input);
}
}
return null;
}
/**
* Opens an editor for an element such as {@code ICElement},
* {@code IFile}, or {@code IStorage}.
* The editor is activated by default.
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement) throws CModelException, PartInitException {
return openInEditor(inputElement, true);
}
/**
* Opens an editor for an element (ICElement, IFile, IStorage...)
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement, boolean activate)
throws CModelException, PartInitException {
if (inputElement instanceof IFile) {
return openInEditor((IFile) inputElement, activate);
}
IEditorInput input = getEditorInput(inputElement);
if (input != null) {
return openInEditor(input, getEditorID(input, inputElement), activate);
}
return null;
}
/**
* Selects a C Element in an editor
*/
public static void revealInEditor(IEditorPart part, ICElement element) {
if (element == null) {
return;
}
if (part instanceof CEditor) {
((CEditor) part).setSelection(element);
} else if (part instanceof ITextEditor) {
if (element instanceof ISourceReference && !(element instanceof ITranslationUnit)) {
ISourceReference reference = (ISourceReference) element;
try {
ISourceRange range = reference.getSourceRange();
((ITextEditor) part).selectAndReveal(range.getIdStartPos(), range.getIdLength());
} catch (CModelException exc) {
CUIPlugin.log(exc.getStatus());
}
}
}
}
private static IEditorPart openInEditor(IFile file, boolean activate) throws PartInitException {
if (file == null)
return null;
if (!file.getProject().isAccessible()) {
closedProject(file.getProject());
return null;
}
try {
if (!file.isLinked(IResource.CHECK_ANCESTORS)) {
File tempFile = file.getRawLocation().toFile();
if (tempFile != null) {
String canonicalPath = null;
try {
canonicalPath = tempFile.getCanonicalPath();
} catch (IOException e) {
}
if (canonicalPath != null) {
IPath path = new Path(canonicalPath);
file = CUIPlugin.getWorkspace().getRoot().getFileForLocation(path);
}
}
}
IEditorInput input = getEditorInput(file);
if (input != null) {
return openInEditor(input, getEditorID(input, file), activate);
}
} catch (CModelException e) {
}
return null;
}
/**
* @deprecated use IResource.isLinked(IResource.CHECK_ANCESTORS) instead.
*/
@Deprecated
public static boolean isLinked(IFile file) {
if (file.isLinked())
return true;
IPath path = file.getLocation();
while (path.segmentCount() > 0) {
path = path.removeLastSegments(1);
IContainer[] containers = ResourcesPlugin.getWorkspace().getRoot().findContainersForLocation(path);
for (IContainer container : containers) {
if (container instanceof IFolder && ((IFolder) container).isLinked()) {
return true;
}
}
}
return false;
}
/**
* Open error dialog about closed project.
* @param project
*/
private static void closedProject(IProject project) {
MessageBox errorMsg = new MessageBox(CUIPlugin.getActiveWorkbenchShell(), SWT.ICON_ERROR | SWT.OK);
errorMsg.setText(CUIPlugin.getResourceString("EditorUtility.closedproject")); //$NON-NLS-1$
String desc = CUIPlugin.getResourceString("Editorutility.closedproject.description"); //$NON-NLS-1$
errorMsg.setMessage(MessageFormat.format(desc, new Object[] { project.getName() }));
errorMsg.open();
}
private static IEditorPart openInEditor(IEditorInput input, String editorID, boolean activate)
throws PartInitException {
if (input != null) {
IWorkbenchPage p = CUIPlugin.getActivePage();
if (p != null) {
IEditorPart editorPart = p.openEditor(input, editorID, activate);
return editorPart;
}
}
return null;
}
private static IEditorInput getEditorInput(ICElement element) throws CModelException {
while (element != null) {
if (element instanceof ISourceReference) {
ITranslationUnit tu = ((ISourceReference) element).getTranslationUnit();
if (tu != null) {
element = tu;
}
}
if (element instanceof IWorkingCopy && ((IWorkingCopy) element).isWorkingCopy())
element = ((IWorkingCopy) element).getOriginalElement();
if (element instanceof ITranslationUnit) {
ITranslationUnit unit = (ITranslationUnit) element;
IResource resource = unit.getResource();
if (resource instanceof IFile) {
return new FileEditorInput((IFile) resource);
}
return new ExternalEditorInput(unit);
}
if (element instanceof IBinary) {
IResource resource = element.getResource();
if (resource instanceof IFile) {
return new FileEditorInput((IFile) resource);
}
}
element = element.getParent();
}
return null;
}
public static IEditorInput getEditorInput(Object input) throws CModelException {
if (input instanceof ICElement) {
return getEditorInput((ICElement) input);
}
if (input instanceof IFile) {
return new FileEditorInput((IFile) input);
}
if (input instanceof IStorage) {
final IPath location = ((IStorage) input).getFullPath();
if (location != null) {
return new ExternalEditorInput(location);
}
}
return null;
}
/**
* Utility method to open an editor for the given file system location
* using {@link #getEditorInputForLocation(IPath, ICElement)} to create
* the editor input.
*
* @param location a file system location
* @param element an element related to the target file, may be {@code null}
* @throws PartInitException
*/
public static IEditorPart openInEditor(IPath location, ICElement element) throws PartInitException {
return openInEditor(location, element, true);
}
public static IEditorPart openInEditor(IPath location, ICElement element, boolean activate)
throws PartInitException {
IEditorInput input = getEditorInputForLocation(location, element);
return EditorUtility.openInEditor(input, getEditorID(input, element), activate);
}
public static IEditorPart openInEditor(URI locationURI, ICElement element) throws PartInitException {
IEditorInput input = getEditorInputForLocation(locationURI, element);
return EditorUtility.openInEditor(input, getEditorID(input, element), true);
}
/**
* Utility method to get an editor input for the given file system location.
* If the location denotes a workspace file, a {@code FileEditorInput}
* is returned, otherwise, the input is an {@code IURIEditorInput}
* assuming the location points to an existing file in the file system.
* The {@code ICElement} is used to determine the associated project
* in case the location can not be resolved to a workspace {@code IFile}.
*
* @param locationURI a valid file system location
* @param context an element related to the target file, may be {@code null}
* @return an editor input
*/
public static IEditorInput getEditorInputForLocation(URI locationURI, ICElement context) {
IFile resource = getWorkspaceFileAtLocation(locationURI, context);
if (resource != null) {
return new FileEditorInput(resource);
}
if (context == null) {
// try to synthesize a context for a location appearing on a project's
// include paths
try {
ICProject[] projects = CCorePlugin.getDefault().getCoreModel().getCModel().getCProjects();
outerFor: for (int i = 0; i < projects.length; i++) {
IIncludeReference[] includeReferences = projects[i].getIncludeReferences();
for (int j = 0; j < includeReferences.length; j++) {
// crecoskie test
// TODO FIXME
// include entries don't handle URIs yet, so fake it out for now
IPath path = URIUtil.toPath(locationURI);
if (path == null)
path = new Path(locationURI.getPath());
if (includeReferences[j].isOnIncludeEntry(path)) {
context = projects[i];
break outerFor;
}
}
}
if (context == null && projects.length > 0) {
// last resort: just take any of them
context = projects[0];
}
} catch (CModelException e) {
}
}
if (context != null) {
// try to get a translation unit from the location and associated element
ICProject cproject = context.getCProject();
if (cproject != null) {
ITranslationUnit unit = CoreModel.getDefault().createTranslationUnitFrom(cproject, locationURI);
if (unit != null) {
IFileStore fileStore = null;
try {
fileStore = EFS.getStore(locationURI);
} catch (CoreException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
if (fileStore != null)
return new ExternalEditorInput(unit);
}
// no translation unit - still try to get a sensible marker resource
// from the associated element
IResource markerResource = cproject.getProject();
return new ExternalEditorInput(locationURI, markerResource);
}
}
return new ExternalEditorInput(locationURI);
}
public static IEditorInput getEditorInputForLocation(IPath location, ICElement context) {
IFile resource = getWorkspaceFileAtLocation(location, context);
if (resource != null) {
return new FileEditorInput(resource);
}
if (location.isUNC()) {
return getEditorInputForLocation(UNCPathConverter.getInstance().toURI(location), context);
}
if (context == null) {
// try to synthesize a context for a location appearing on a project's
// include paths
try {
ICProject[] projects = CCorePlugin.getDefault().getCoreModel().getCModel().getCProjects();
outerFor: for (int i = 0; i < projects.length; i++) {
IIncludeReference[] includeReferences = projects[i].getIncludeReferences();
for (int j = 0; j < includeReferences.length; j++) {
if (includeReferences[j].isOnIncludeEntry(location)) {
context = projects[i];
break outerFor;
}
}
}
if (context == null && projects.length > 0) {
// last resort: just take any of them
context = projects[0];
}
} catch (CModelException e) {
}
}
if (context != null) {
// try to get a translation unit from the location and associated element
ICProject cproject = context.getCProject();
if (cproject != null) {
ITranslationUnit unit = CoreModel.getDefault().createTranslationUnitFrom(cproject, location);
if (unit != null) {
return new ExternalEditorInput(unit);
}
// no translation unit - still try to get a sensible marker resource
// from the associated element
IResource markerResource = cproject.getProject();
return new ExternalEditorInput(location, markerResource);
}
}
return new ExternalEditorInput(location);
}
/**
* Utility method to resolve a file system location to a workspace resource.
* If a context element is given and there are multiple matches in the workspace,
* a resource with the same project of the context element are preferred.
*
* @param location a valid file system location
* @param context an element related to the target file, may be {@code null}
* @return an {@code IFile} or {@code null}
*/
public static IFile getWorkspaceFileAtLocation(IPath location, ICElement context) {
IProject project = null;
if (context != null) {
ICProject cProject = context.getCProject();
if (cProject != null) {
project = cProject.getProject();
}
}
IFile file = ResourceLookup.selectFileForLocation(location, project);
if (file != null && file.isAccessible())
return file;
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
// workaround http://bugs.eclipse.org/233939
file = root.getFileForLocation(location);
if (file != null && file.isAccessible())
return file;
// try workspace relative path
if (location.segmentCount() >= 2) {
// @see IContainer#getFile for the required number of segments
file = root.getFile(location);
if (file != null && file.isAccessible())
return file;
}
return null;
}
/**
* Utility method to resolve a file system location to a workspace resource.
* If a context element is given and there are multiple matches in the workspace,
* a resource with the same project of the context element are preferred.
*
* @param locationURI a valid Eclipse file system URI
* @param context an element related to the target file, may be {@code null}
* @return an {@code IFile} or {@code null}
*/
public static IFile getWorkspaceFileAtLocation(URI locationURI, ICElement context) {
IProject project = null;
if (context != null) {
ICProject cProject = context.getCProject();
if (cProject != null) {
project = cProject.getProject();
}
}
IFile file = ResourceLookup.selectFileForLocationURI(locationURI, project);
if (file != null && file.isAccessible())
return file;
return null;
}
/**
* If the current active editor edits a c element return it, else
* return null
*/
public static ICElement getActiveEditorCInput() {
IWorkbenchPage page = CUIPlugin.getActivePage();
if (page != null) {
IEditorPart part = page.getActiveEditor();
if (part != null) {
return getEditorInputCElement(part);
}
}
return null;
}
public static ICElement getEditorInputCElement(IEditorPart part) {
IEditorInput editorInput = part.getEditorInput();
if (editorInput == null) {
return null;
}
return editorInput.getAdapter(ICElement.class);
}
/**
* Gets the working copy of an translation unit opened in an editor
*
* @param tu the original translation unit (or another working copy)
* @return the working copy of the translation unit, or null if not found
*/
public static ITranslationUnit getWorkingCopy(ITranslationUnit tu) {
if (tu == null)
return null;
if (tu.isWorkingCopy())
return tu;
return CDTUITools.getWorkingCopyManager().findSharedWorkingCopy(tu);
}
/**
* Determine the editor id from the given file name using
* the workspace-wide content-type definitions.
*
* @param name the file name
* @return a valid editor id, never {@code null}
*/
public static String getEditorID(String name) {
try {
IEditorDescriptor descriptor = IDE.getEditorDescriptor(name, true, false);
if (descriptor != null) {
return descriptor.getId();
}
} catch (PartInitException exc) {
// ignore
}
return DEFAULT_TEXT_EDITOR_ID;
}
/**
* Determine the editor id from the given editor input and optional input object.
* When a translation unit can be obtained, the project-specific content-type
* mechanism is used to determine the correct editor id.
* If that fails, the editor id is determined by file name and extension using
* the workspace-wide content-type definitions.
*
* @param input the editor input
* @param inputObject the input object (used to create the editor input) or {@code null}
* @return a valid editor id, never {@code null}
*/
public static String getEditorID(IEditorInput input, Object inputObject) {
ICElement cElement = null;
if (input instanceof IFileEditorInput) {
IFileEditorInput editorInput = (IFileEditorInput) input;
IFile file = editorInput.getFile();
// Try file specific editor.
try {
String editorID = file.getPersistentProperty(IDE.EDITOR_KEY);
if (editorID != null) {
IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
IEditorDescriptor desc = registry.findEditor(editorID);
if (desc != null) {
return editorID;
}
}
} catch (CoreException e) {
// do nothing
}
cElement = CoreModel.getDefault().create(file);
} else if (input instanceof ITranslationUnitEditorInput) {
ITranslationUnitEditorInput editorInput = (ITranslationUnitEditorInput) input;
cElement = editorInput.getTranslationUnit();
} else if (inputObject instanceof ICElement) {
cElement = (ICElement) inputObject;
}
// Choose an editor based on the content type
IContentType contentType = null;
if (cElement instanceof ITranslationUnit) {
String contentTypeId = ((ITranslationUnit) cElement).getContentTypeId();
if (contentTypeId != null) {
contentType = Platform.getContentTypeManager().getContentType(contentTypeId);
}
}
if (contentType == null) {
IProject project = null;
if (cElement != null) {
project = cElement.getCProject().getProject();
} else {
IFile file = ResourceUtil.getFile(input);
if (file != null) {
project = file.getProject();
}
}
contentType = CCorePlugin.getContentType(project, input.getName());
}
// handle binary files without content-type (e.g. executables without extension)
if (contentType == null && cElement != null) {
final int elementType = cElement.getElementType();
if (elementType == ICElement.C_ARCHIVE || elementType == ICElement.C_BINARY) {
contentType = Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_BINARYFILE);
}
}
IEditorRegistry registry = PlatformUI.getWorkbench().getEditorRegistry();
IEditorDescriptor desc = registry.getDefaultEditor(input.getName(), contentType);
if (desc != null) {
String editorID = desc.getId();
if (input instanceof IFileEditorInput) {
IFile file = ((IFileEditorInput) input).getFile();
IDE.setDefaultEditor(file, editorID);
}
return editorID;
}
return DEFAULT_TEXT_EDITOR_ID;
}
/**
* Maps the localized modifier name to a code in the same
* manner as #findModifier.
*
* @return the SWT modifier bit, or {@code 0} if no match was found
*/
public static int findLocalizedModifier(String token) {
if (token == null)
return 0;
if (token.equalsIgnoreCase(Action.findModifierString(SWT.CTRL)))
return SWT.CTRL;
if (token.equalsIgnoreCase(Action.findModifierString(SWT.SHIFT)))
return SWT.SHIFT;
if (token.equalsIgnoreCase(Action.findModifierString(SWT.ALT)))
return SWT.ALT;
if (token.equalsIgnoreCase(Action.findModifierString(SWT.COMMAND)))
return SWT.COMMAND;
return 0;
}
/**
* Returns the modifier string for the given SWT modifier
* modifier bits.
*
* @param stateMask the SWT modifier bits
* @return the modifier string
* @since 2.1.1
*/
public static String getModifierString(int stateMask) {
String modifierString = ""; //$NON-NLS-1$
if ((stateMask & SWT.CTRL) == SWT.CTRL)
modifierString = appendModifierString(modifierString, SWT.CTRL);
if ((stateMask & SWT.ALT) == SWT.ALT)
modifierString = appendModifierString(modifierString, SWT.ALT);
if ((stateMask & SWT.SHIFT) == SWT.SHIFT)
modifierString = appendModifierString(modifierString, SWT.SHIFT);
if ((stateMask & SWT.COMMAND) == SWT.COMMAND)
modifierString = appendModifierString(modifierString, SWT.COMMAND);
return modifierString;
}
/**
* Appends to modifier string of the given SWT modifier bit
* to the given modifierString.
*
* @param modifierString the modifier string
* @param modifier an int with SWT modifier bit
* @return the concatenated modifier string
* @since 2.1.1
*/
private static String appendModifierString(String modifierString, int modifier) {
if (modifierString == null)
modifierString = ""; //$NON-NLS-1$
String newModifierString = Action.findModifierString(modifier);
if (modifierString.length() == 0)
return newModifierString;
return NLS.bind(CEditorMessages.EditorUtility_concatModifierStrings,
new String[] { modifierString, newModifierString });
}
public static IStorage getStorage(IBinary bin) {
IStorage store = null;
try {
IBuffer buffer = bin.getBuffer();
if (buffer != null) {
store = new FileStorage(new ByteArrayInputStream(buffer.getContents().getBytes()), bin.getPath());
}
} catch (CModelException e) {
// nothing;
}
return store;
}
/**
* Returns the C project for a given editor input or {@code null} if no corresponding
* C project exists.
*
* @param input the editor input
* @return the corresponding C project
*
* @since 5.0
*/
public static ICProject getCProject(IEditorInput input) {
ICProject cProject = null;
if (input instanceof IFileEditorInput) {
IProject project = ((IFileEditorInput) input).getFile().getProject();
if (project != null) {
cProject = CoreModel.getDefault().create(project);
if (!cProject.exists())
cProject = null;
}
} else if (input instanceof ITranslationUnitEditorInput) {
final ITranslationUnit tu = ((ITranslationUnitEditorInput) input).getTranslationUnit();
if (tu != null) {
cProject = tu.getCProject();
} else if (input instanceof ExternalEditorInput) {
IResource resource = ((ExternalEditorInput) input).getMarkerResource();
if (resource instanceof IProject) {
cProject = CoreModel.getDefault().create((IProject) resource);
}
}
}
return cProject;
}
/**
* Returns an array of all editors that have an unsaved content. If the identical content is
* presented in more than one editor, only one of those editor parts is part of the result.
* @param skipNonResourceEditors if {@code true}, editors whose inputs do not adapt to {@link IResource}
* are not saved
*
* @return an array of dirty editor parts
* @since 5.3
*/
public static IEditorPart[] getDirtyEditors(boolean skipNonResourceEditors) {
Set<IEditorInput> inputs = new HashSet<>();
List<IEditorPart> result = new ArrayList<>();
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage[] pages = window.getPages();
for (IWorkbenchPage page : pages) {
IEditorPart[] editors = page.getDirtyEditors();
for (IEditorPart ep : editors) {
IEditorInput input = ep.getEditorInput();
if (inputs.add(input)) {
if (!skipNonResourceEditors || isResourceEditorInput(input)) {
result.add(ep);
}
}
}
}
}
return result.toArray(new IEditorPart[result.size()]);
}
private static boolean isResourceEditorInput(IEditorInput input) {
if (input instanceof MultiEditorInput) {
IEditorInput[] inputs = ((MultiEditorInput) input).getInput();
for (IEditorInput input2 : inputs) {
if (input2.getAdapter(IResource.class) != null) {
return true;
}
}
} else if (input.getAdapter(IResource.class) != null) {
return true;
}
return false;
}
/**
* Returns the editors to save before performing global C-related
* operations.
*
* @param saveUnknownEditors {@code true} iff editors with unknown buffer management should also be saved
* @return the editors to save
* @since 5.3
*/
public static IEditorPart[] getDirtyEditorsToSave(boolean saveUnknownEditors) {
Set<IEditorInput> inputs = new HashSet<>();
List<IEditorPart> result = new ArrayList<>();
IWorkbench workbench = PlatformUI.getWorkbench();
IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
for (IWorkbenchWindow window : windows) {
IWorkbenchPage[] pages = window.getPages();
for (IWorkbenchPage page : pages) {
IEditorPart[] editors = page.getDirtyEditors();
for (IEditorPart editor : editors) {
IEditorPart ep = editor;
IEditorInput input = ep.getEditorInput();
if (!mustSaveDirtyEditor(ep, input, saveUnknownEditors))
continue;
if (inputs.add(input))
result.add(ep);
}
}
}
return result.toArray(new IEditorPart[result.size()]);
}
private static boolean mustSaveDirtyEditor(IEditorPart ep, IEditorInput input, boolean saveUnknownEditors) {
/*
* Goal: save all editors that could interfere with refactoring operations.
*
* Always save all editors for translation units that are not working copies.
* (Leaving them dirty would cause problems, since the file buffer could have been
* modified but the C model is not reconciled.)
*
* If {@code saveUnknownEditors} is {@code true}, save all editors
* whose implementation is probably not based on file buffers.
*/
IResource resource = input.getAdapter(IResource.class);
if (resource == null)
return saveUnknownEditors;
ICElement element = CCorePlugin.getDefault().getCoreModel().create(resource);
if (element instanceof ITranslationUnit) {
ITranslationUnit tu = (ITranslationUnit) element;
if (getWorkingCopy(tu) == null) {
return true;
}
}
ITextEditor textEditor = getTextEditor(ep);
if (textEditor == null)
return saveUnknownEditors;
IDocumentProvider documentProvider = textEditor.getDocumentProvider();
if (!(documentProvider instanceof TextFileDocumentProvider))
return saveUnknownEditors;
return false;
}
/**
* Return the regions of all lines which have changed in the given buffer since the
* last save occurred. Each region in the result spans over the size of at least one line.
* If successive lines have changed a region spans over the size of all successive lines.
* The regions include line delimiters.
*
* @param buffer the buffer to compare contents from
* @param monitor to report progress to
* @return the regions of the changed lines
* @throws CoreException
* @since 5.1
*/
public static IRegion[] calculateChangedLineRegions(final ITextFileBuffer buffer, final IProgressMonitor monitor)
throws CoreException {
final IRegion[][] result = new IRegion[1][];
final IStatus[] errorStatus = new IStatus[] { Status.OK_STATUS };
try {
SafeRunner.run(new ISafeRunnable() {
@Override
public void handleException(Throwable exception) {
CUIPlugin.log(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID,
ICStatusConstants.EDITOR_CHANGED_REGION_CALCULATION, exception.getLocalizedMessage(),
exception));
String msg = Messages.EditorUtility_error_calculatingChangedRegions;
errorStatus[0] = new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID,
ICStatusConstants.EDITOR_CHANGED_REGION_CALCULATION, msg, exception);
result[0] = null;
}
@Override
public void run() throws Exception {
SubMonitor progress = SubMonitor.convert(monitor,
Messages.EditorUtility_calculatingChangedRegions_message, 4);
IFileStore fileStore = buffer.getFileStore();
ITextFileBufferManager fileBufferManager = FileBuffers.createTextFileBufferManager();
fileBufferManager.connectFileStore(fileStore, progress.split(3));
try {
IDocument currentDocument = buffer.getDocument();
IDocument oldDocument = ((ITextFileBuffer) fileBufferManager.getFileStoreFileBuffer(fileStore))
.getDocument();
result[0] = getChangedLineRegions(oldDocument, currentDocument);
} finally {
fileBufferManager.disconnectFileStore(fileStore, progress.split(1));
}
}
/**
* Return regions of all lines which differ comparing {@code oldDocument}s content
* with {@code currentDocument}s content. Successive lines are merged into one region.
*
* @param oldDocument a document containing the old content
* @param currentDocument a document containing the current content
* @return the changed regions
* @throws BadLocationException
*/
private IRegion[] getChangedLineRegions(IDocument oldDocument, IDocument currentDocument) {
/*
* Do not change the type of those local variables. We use Object
* here in order to prevent loading of the Compare plug-in at load
* time of this class.
*/
Object leftSide = new LineComparator(oldDocument);
Object rightSide = new LineComparator(currentDocument);
RangeDifference[] differences = RangeDifferencer.findDifferences((IRangeComparator) leftSide,
(IRangeComparator) rightSide);
// It holds that:
// 1. Ranges are sorted:
// forAll r1,r2 element differences: indexOf(r1) < indexOf(r2) -> r1.rightStart() < r2.rightStart();
// 2. Successive changed lines are merged into on RangeDifference
// forAll r1,r2 element differences: r1.rightStart() < r2.rightStart() -> r1.rightEnd() < r2.rightStart
List<IRegion> regions = new ArrayList<>();
final int numberOfLines = currentDocument.getNumberOfLines();
for (RangeDifference curr : differences) {
if (curr.kind() == RangeDifference.CHANGE) {
int startLine = Math.min(curr.rightStart(), numberOfLines - 1);
int endLine = curr.rightEnd() - 1;
IRegion startLineRegion;
try {
startLineRegion = currentDocument.getLineInformation(startLine);
if (startLine >= endLine) {
// startLine > endLine indicates a deletion of one or more lines.
// Deletions are ignored except at the end of the document.
if (startLine == endLine || startLineRegion.getOffset()
+ startLineRegion.getLength() == currentDocument.getLength()) {
regions.add(startLineRegion);
}
} else {
IRegion endLineRegion = currentDocument.getLineInformation(endLine);
int startOffset = startLineRegion.getOffset();
int endOffset = endLineRegion.getOffset() + endLineRegion.getLength();
regions.add(new Region(startOffset, endOffset - startOffset));
}
} catch (BadLocationException e) {
CUIPlugin.log(e);
}
}
}
return regions.toArray(new IRegion[regions.size()]);
}
});
} finally {
if (!errorStatus[0].isOK())
throw new CoreException(errorStatus[0]);
}
return result[0];
}
/**
* Returns the project contains the resource, which is currently open in the active editor.
* If the active part is no ITextEditor or if the editorInput is no FileEditorInput,
* {@code null} is returned.
*
* @return the project which the selected editor input belongs to or null
*/
public static IProject getProjectForActiveEditor() {
IProject project = null;
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window != null) {
IWorkbenchPage activePage = window.getActivePage();
if (activePage != null) {
ITextEditor activeEditor = getTextEditor(activePage.getActiveEditor());
if (activeEditor != null) {
IEditorInput editorInput = activeEditor.getEditorInput();
IFile file = ResourceUtil.getFile(editorInput);
if (file != null) {
project = file.getProject();
}
}
}
}
return project;
}
/**
* Tries to convert the given editor to an implementation of ITextEditor. Returns that implementation
* if possible and null otherwise.
*
* @param editor The editor to be converted or null if there is nothing to convert.
*/
public static ITextEditor getTextEditor(IEditorPart editor) {
return editor == null ? null : editor.getAdapter(ITextEditor.class);
}
}