aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2018-07-05 16:53:00 -0400
committerThomas Wolf2018-07-07 06:55:56 -0400
commitb5764723c1bef281a3d7d00d6f64af1d016472ca (patch)
treebac154a75282563838785d579cf592957608fa6a
parent96d430a13d0e9210ccf563d42ba94c202d578d18 (diff)
downloadegit-b5764723c1bef281a3d7d00d6f64af1d016472ca.zip
egit-b5764723c1bef281a3d7d00d6f64af1d016472ca.tar.gz
egit-b5764723c1bef281a3d7d00d6f64af1d016472ca.tar.xz
Better default editor opening for file revisions
EGit tried already to open the configured editor based on content types or file name associations. On failure, it would fall back to the default internal text editor. Unfortunately, opening a default editor failed always for editors that do require some physical file. Thus opening an external editor always failed, and EGit showed the file as text. Even if the file was binary. Newly, make FileRevisionEditorInput implement IPathEditorInput. Eclipse uses this to invoke external editors, so when getPath() is called, we can write the blob to a temporary file, so that the external editor has something to read from. This is the same technique as used in InternalClassFileEditorInput in JDT. Incidentally this also makes showing a git revision of an HTML page in the internal web browser work. The file is created inside a temporary directory; this enables us to use a simple file name (revision & original file name), which looks nicer in programs that show the file name than a (partially) auto-generated name as we'd get from createTempFile(). The file is set as read-only: a user cannot modify a git revision. We attempt to delete the temporary file and directory when the JVM terminates to avoid filling the system tmp directory. Bug: 463906 Change-Id: I355524f46a8ef66745f287adf718dfb8ea197b4a Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java49
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java3
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/FileRevisionEditorInput.java77
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties1
4 files changed, 104 insertions, 26 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
index 4d094a5..4fe6911 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/EgitUiEditorUtils.java
@@ -53,10 +53,15 @@ import org.eclipse.ui.texteditor.ITextEditor;
public class EgitUiEditorUtils {
/**
+ * Opens an {@link IFileRevision} is the configured editor.
+ *
* @param page
+ * to open the editor in
* @param revision
+ * to open
* @param monitor
- * @return the part
+ * for progress reporting
+ * @return the part; may be {@code null} if an external editor was opened
* @throws CoreException
*/
public static IEditorPart openEditor(IWorkbenchPage page,
@@ -94,35 +99,39 @@ public class EgitUiEditorUtils {
SubMonitor progress = SubMonitor.convert(monitor, 1);
FileRevisionEditorInput fileRevEditorInput = FileRevisionEditorInput
.createEditorInputFor(revision, progress.newChild(1));
- openEditor(page, fileRevEditorInput, EditorsUI.DEFAULT_TEXT_EDITOR_ID);
+ page.openEditor(fileRevEditorInput, EditorsUI.DEFAULT_TEXT_EDITOR_ID);
}
/**
+ * Opens an editor for a {@link FileRevisionEditorInput}.
+ *
* @param page
+ * to open the editor in
* @param editorInput
- * @return the part
+ * to open
+ * @return the part; may be {@code null} if an external editor was opened
* @throws PartInitException
*/
public static IEditorPart openEditor(IWorkbenchPage page,
FileRevisionEditorInput editorInput) throws PartInitException {
- String id = getEditorId(editorInput);
- return openEditor(page, editorInput, id);
+ return openEditor(page, editorInput, getEditor(editorInput));
}
/**
* @param page
* @param editorInput
- * @param editorId
+ * @param editor
* @return the part
* @throws PartInitException
*/
private static IEditorPart openEditor(IWorkbenchPage page,
- FileRevisionEditorInput editorInput, String editorId)
+ FileRevisionEditorInput editorInput, IEditorDescriptor editor)
throws PartInitException {
+ String editorId = editor.getId();
try {
IEditorPart part = page.openEditor(editorInput, editorId,
OpenStrategy.activateOnOpen());
- if (part == null) {
+ if (part == null && !editor.isOpenExternal()) {
throw new PartInitException(NLS.bind(
UIText.EgitUiUtils_CouldNotOpenEditorMessage, editorId));
}
@@ -167,24 +176,22 @@ public class EgitUiEditorUtils {
return null;
}
- private static String getEditorId(FileRevisionEditorInput editorInput) {
- String id = getEditorId(editorInput.getFileRevision().getName(),
- getContentType(editorInput));
- return id;
- }
-
- private static String getEditorId(String fileName, IContentType type) {
+ private static IEditorDescriptor getEditor(
+ FileRevisionEditorInput editorInput) {
IEditorRegistry registry = PlatformUI.getWorkbench()
.getEditorRegistry();
+ String fileName = editorInput.getFileRevision().getName();
+ IContentType type = getContentType(editorInput);
IEditorDescriptor descriptor = registry
.getDefaultEditor(fileName, type);
- String id;
- if (descriptor == null || descriptor.isOpenExternal()) {
- id = EditorsUI.DEFAULT_TEXT_EDITOR_ID;
- } else {
- id = descriptor.getId();
+ if (descriptor != null) {
+ descriptor = IDE.overrideDefaultEditorAssociation(editorInput, type,
+ descriptor);
+ }
+ if (descriptor == null) {
+ descriptor = registry.findEditor(EditorsUI.DEFAULT_TEXT_EDITOR_ID);
}
- return id;
+ return descriptor;
}
private static IContentType getContentType(
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
index 9e76587..78abd80 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
@@ -3055,6 +3055,9 @@ public class UIText extends NLS {
public static String FileRevisionEditorInput_NameAndRevisionTitle;
/** */
+ public static String FileRevisionEditorInput_cannotWriteTempFile;
+
+ /** */
public static String FileTreeContentProvider_NonWorkspaceResourcesNode;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/FileRevisionEditorInput.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/FileRevisionEditorInput.java
index 8042ed5..dd3ad10 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/FileRevisionEditorInput.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/revision/FileRevisionEditorInput.java
@@ -13,8 +13,13 @@
*******************************************************************************/
package org.eclipse.egit.ui.internal.revision;
+import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
+import java.nio.file.Files;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
import java.util.Date;
import org.eclipse.core.resources.IEncodedStorage;
@@ -24,13 +29,15 @@ import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.PlatformObject;
import org.eclipse.egit.core.AdapterUtils;
+import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.PreferenceBasedDateFormatter;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.resource.ImageDescriptor;
-import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.IPersistableElement;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.model.IWorkbenchAdapter;
@@ -39,12 +46,17 @@ import org.eclipse.ui.model.IWorkbenchAdapter;
* An Editor input for file revisions
*/
public class FileRevisionEditorInput extends PlatformObject implements
- IWorkbenchAdapter, IStorageEditorInput {
+ IWorkbenchAdapter, IStorageEditorInput, IPathEditorInput {
+
+ private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
+ "yyyyMMdd_HHmmss"); //$NON-NLS-1$
private final Object fileRevision;
private final IStorage storage;
+ private IPath tmpFile = null;
+
/**
* @param revision
* the file revision
@@ -190,9 +202,9 @@ public class FileRevisionEditorInput extends PlatformObject implements
public String getName() {
IFileRevision rev = AdapterUtils.adapt(this, IFileRevision.class);
if (rev != null) {
- return NLS.bind(
+ return MessageFormat.format(
UIText.FileRevisionEditorInput_NameAndRevisionTitle,
- new String[] { rev.getName(), rev.getContentIdentifier() });
+ rev.getName(), rev.getContentIdentifier());
}
IFileState state = AdapterUtils.adapt(this, IFileState.class);
if (state != null) {
@@ -200,7 +212,6 @@ public class FileRevisionEditorInput extends PlatformObject implements
.formatDate(new Date(state.getModificationTime()));
}
return storage.getName();
-
}
@Override
@@ -289,4 +300,60 @@ public class FileRevisionEditorInput extends PlatformObject implements
}
return null;
}
+
+ @Override
+ public IPath getPath() {
+ if (tmpFile == null) {
+ tmpFile = writeTempFile();
+ }
+ return tmpFile;
+ }
+
+ private IPath writeTempFile() {
+ java.nio.file.Path path;
+ try {
+ String tempName = getRevisionPrefix() + storage.getName();
+ // Same name length limit as in DirCacheCheckout.checkoutEntry()
+ if (tempName.length() > 200) {
+ tempName = storage.getName();
+ }
+ path = Files.createTempDirectory("egit") //$NON-NLS-1$
+ .resolve(tempName);
+ try (InputStream in = storage.getContents()) {
+ Files.copy(in, path);
+ }
+ path = path.toAbsolutePath();
+ } catch (CoreException | IOException e) {
+ Activator.logError(MessageFormat.format(
+ UIText.FileRevisionEditorInput_cannotWriteTempFile,
+ storage.getName()), e);
+ // We mustn't return null; doing so might cause an NPE in
+ // WorkBenchPage.busyOpenEditor()
+ return new Path(""); //$NON-NLS-1$
+ }
+ File file = path.toFile();
+ file.setReadOnly();
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ if (file.setWritable(true) && file.delete()) {
+ file.getParentFile().delete();
+ } else {
+ // Couldn't delete: re-set as read-only
+ file.setReadOnly();
+ }
+ }));
+ return new Path(path.toString());
+ }
+
+ private String getRevisionPrefix() {
+ IFileRevision rev = AdapterUtils.adapt(this, IFileRevision.class);
+ if (rev != null) {
+ return rev.getContentIdentifier() + '_';
+ }
+ IFileState state = AdapterUtils.adapt(this, IFileState.class);
+ if (state != null) {
+ return DATE_FORMAT.format(new Date(state.getModificationTime()))
+ + '_';
+ }
+ return ""; //$NON-NLS-1$
+ }
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
index ae69c0f..f51b402 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
@@ -1027,6 +1027,7 @@ FetchWizard_windowTitleWithSource=Fetch from: {0}
FileDiffContentProvider_errorGettingDifference=Can''t get file difference of {0}.
FileDiffLabelProvider_RenamedFromToolTip=Renamed from {0}
FileRevisionEditorInput_NameAndRevisionTitle={0} {1}
+FileRevisionEditorInput_cannotWriteTempFile=Cannot write temporary file for {0}.
FileTreeContentProvider_NonWorkspaceResourcesNode=Non-workspace files
FindToolbar_NextTooltip=Go to next commit matching the search criteria
FindToolbar_PreviousTooltip=Go to previous commit matching the search criteria