Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn2018-02-13 07:38:55 +0000
committerGerrit Code Review @ Eclipse.org2018-02-13 07:38:55 +0000
commit29140317b95679445b78fb51cb2a6506c9e5e36a (patch)
treea8d9a0b8425a3648ed22ebe49c6eb17cbd10ed57
parent7231e69ff2d1b809f5b754a0bb778765d0b23bbd (diff)
parentffa3b9913f7de7974e8aa6e5609ee1ab55c4c3fe (diff)
downloadegit-29140317b95679445b78fb51cb2a6506c9e5e36a.tar.gz
egit-29140317b95679445b78fb51cb2a6506c9e5e36a.tar.xz
egit-29140317b95679445b78fb51cb2a6506c9e5e36a.zip
Merge changes from topic 'push-branch-wizard'
* changes: Include local branch name in proposals Prevent NPE in RefContentProposal.appendObjectSummary Prevent MissingObjectException being logged in ref content proposal Asynchronous content proposals for upstream refs in PushBranchPage Generalize UIUtils.addContentProposalToText a bit more Make the PushWizardDialog a NonBlockingWizardDialog Remove extra progress popup Extract the asynchronous future from FetchGerritChangePage
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIUtils.java115
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java18
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousListOperation.java96
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousRefProposalProvider.java176
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefContentProposal.java54
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefSpecPanel.java34
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CancelableFuture.java326
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/NonBlockingWizardDialog.java25
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchDestinationPage.java2
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java396
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchSourcePage.java2
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/gerrit/GerritConfigurationPage.java3
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/pull/PullWizardPage.java2
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchPage.java125
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchWizard.java1
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java2
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushWizardDialog.java4
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/RefSpecDialog.java6
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties8
19 files changed, 966 insertions, 429 deletions
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIUtils.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIUtils.java
index e38f1a1543..98c8f078eb 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIUtils.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/UIUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2015 SAP AG and others.
+ * Copyright (c) 2010, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,6 +18,7 @@ import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -33,7 +34,6 @@ import org.eclipse.egit.ui.internal.RepositorySaveableFilter;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.components.RefContentProposal;
-import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.bindings.Trigger;
import org.eclipse.jface.bindings.TriggerSequence;
@@ -47,6 +47,7 @@ import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.jface.fieldassist.IContentProposal;
import org.eclipse.jface.fieldassist.IContentProposalProvider;
+import org.eclipse.jface.fieldassist.IControlContentAdapter;
import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.resource.FontRegistry;
import org.eclipse.jface.resource.ImageDescriptor;
@@ -61,6 +62,7 @@ import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.osgi.util.NLS;
@@ -214,6 +216,58 @@ public class UIUtils {
}
/**
+ * A {@link ContentProposalAdapter} with a <em>public</em>
+ * {@link #openProposalPopup()} method.
+ */
+ public static class ExplicitContentProposalAdapter
+ extends ContentProposalAdapter {
+
+ /**
+ * Construct a content proposal adapter that can assist the user with
+ * choosing content for the field.
+ *
+ * @param control
+ * the control for which the adapter is providing content
+ * assist. May not be {@code null}.
+ * @param controlContentAdapter
+ * the {@link IControlContentAdapter} used to obtain and
+ * update the control's contents as proposals are accepted.
+ * May not be {@code null}.
+ * @param proposalProvider
+ * the {@link IContentProposalProvider}> used to obtain
+ * content proposals for this control.
+ * @param keyStroke
+ * the keystroke that will invoke the content proposal popup.
+ * If this value is {@code null}, then proposals will be
+ * activated automatically when any of the auto activation
+ * characters are typed.
+ * @param autoActivationCharacters
+ * characters that trigger auto-activation of content
+ * proposal. If specified, these characters will trigger
+ * auto-activation of the proposal popup, regardless of
+ * whether an explicit invocation keyStroke was specified. If
+ * this parameter is {@code null}, then only a specified
+ * keyStroke will invoke content proposal. If this parameter
+ * is {@code null} and the keyStroke parameter is
+ * {@code null}, then all alphanumeric characters will
+ * auto-activate content proposal.
+ */
+ public ExplicitContentProposalAdapter(Control control,
+ IControlContentAdapter controlContentAdapter,
+ IContentProposalProvider proposalProvider,
+ KeyStroke keyStroke, char[] autoActivationCharacters) {
+ super(control, controlContentAdapter, proposalProvider, keyStroke,
+ autoActivationCharacters);
+ }
+
+ @Override
+ public void openProposalPopup() {
+ // Make this method accessible
+ super.openProposalPopup();
+ }
+ }
+
+ /**
* @param id
* see {@link FontRegistry#get(String)}
* @return the font
@@ -465,11 +519,17 @@ public class UIUtils {
* the repository
* @param refListProvider
* provides the {@link Ref}s to show in the proposal
+ * @param upstream
+ * {@code true} if the candidates provided by the
+ * {@code refListProvider} are from an upstream repository
+ * @return the content proposal adapter set on the {@code textField}
*/
- public static final void addRefContentProposalToText(Text textField,
+ public static final ExplicitContentProposalAdapter addRefContentProposalToText(
+ Text textField,
Repository repository,
- IContentProposalCandidateProvider<Ref> refListProvider) {
- UIUtils.<Ref> addContentProposalToText(textField,
+ IContentProposalCandidateProvider<Ref> refListProvider,
+ boolean upstream) {
+ return UIUtils.<Ref> addContentProposalToText(textField,
refListProvider, (pattern, ref) -> {
String shortenedName = Repository
.shortenRefName(ref.getName());
@@ -478,8 +538,9 @@ public class UIUtils {
&& !pattern.matcher(shortenedName).matches()) {
return null;
}
- return new RefContentProposal(repository, ref);
- }, UIText.UIUtils_StartTypingForRemoteRefMessage,
+ return new RefContentProposal(repository, ref, upstream);
+ }, null,
+ UIText.UIUtils_StartTypingForRemoteRefMessage,
UIText.UIUtils_PressShortcutForRemoteRefMessage);
}
@@ -497,20 +558,32 @@ public class UIUtils {
* @param factory
* {@link IContentProposalFactory} to use to create proposals
* from candidates
+ * @param patternProvider
+ * to convert the current text of the field into a pattern
+ * suitable for filtering the candidates. If {@code null}, a
+ * default pattern is constructed using
+ * {@link #createProposalPattern(String)}.
* @param startTypingMessage
* hover message if no content assist key binding is active
* @param shortcutMessage
* hover message if a content assist key binding is active,
* should have a "{0}" placeholder that will be filled by the
* appropriate keystroke
+ * @return the content proposal adapter set on the {@code textField}
*/
- public static final <T> void addContentProposalToText(Text textField,
+ public static final <T> ExplicitContentProposalAdapter addContentProposalToText(
+ Text textField,
IContentProposalCandidateProvider<T> candidateProvider,
- IContentProposalFactory<T> factory, String startTypingMessage,
+ IContentProposalFactory<T> factory,
+ Function<String, Pattern> patternProvider,
+ String startTypingMessage,
String shortcutMessage) {
KeyStroke stroke = UIUtils
.getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
if (stroke == null) {
+ if (startTypingMessage == null) {
+ return null;
+ }
addBulbDecorator(textField, startTypingMessage);
} else {
addBulbDecorator(textField,
@@ -521,16 +594,19 @@ public class UIUtils {
public IContentProposal[] getProposals(String contents, int position) {
List<IContentProposal> resultList = new ArrayList<>();
- Pattern pattern = createProposalPattern(contents);
Collection<? extends T> candidates = candidateProvider
.getCandidates();
- if (candidates != null) {
- for (final T candidate : candidates) {
- IContentProposal proposal = factory.getProposal(pattern,
- candidate);
- if (proposal != null) {
- resultList.add(proposal);
- }
+ if (candidates == null) {
+ return null;
+ }
+ Pattern pattern = patternProvider != null
+ ? patternProvider.apply(contents)
+ : createProposalPattern(contents);
+ for (final T candidate : candidates) {
+ IContentProposal proposal = factory.getProposal(pattern,
+ candidate);
+ if (proposal != null) {
+ resultList.add(proposal);
}
}
return resultList.toArray(new IContentProposal[resultList
@@ -538,12 +614,13 @@ public class UIUtils {
}
};
- ContentProposalAdapter adapter = new ContentProposalAdapter(textField,
- new TextContentAdapter(), cp, stroke,
+ ExplicitContentProposalAdapter adapter = new ExplicitContentProposalAdapter(
+ textField, new TextContentAdapter(), cp, stroke,
UIUtils.VALUE_HELP_ACTIVATIONCHARS);
// set the acceptance style to always replace the complete content
adapter.setProposalAcceptanceStyle(
ContentProposalAdapter.PROPOSAL_REPLACE);
+ return adapter;
}
/**
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 902a667ef2..782f948579 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
@@ -143,6 +143,12 @@ public class UIText extends NLS {
public static String AddToIndexCommand_addingFilesFailed;
/** */
+ public static String AsynchronousRefProposalProvider_FetchingRemoteRefsMessage;
+
+ /** */
+ public static String AsynchronousRefProposalProvider_ShowingProposalsJobName;
+
+ /** */
public static String RemoveFromIndexAction_removingFiles;
/** */
@@ -1556,6 +1562,9 @@ public class UIText extends NLS {
public static String RefContentProposal_errorReadingObject;
/** */
+ public static String RefContentProposal_newRemoteObject;
+
+ /** */
public static String RefContentProposal_tag;
/** */
@@ -1568,6 +1577,9 @@ public class UIText extends NLS {
public static String RefContentProposal_unknownObject;
/** */
+ public static String RefContentProposal_unknownRemoteObject;
+
+ /** */
public static String ReflogView_DateColumnHeader;
/** */
@@ -2852,9 +2864,6 @@ public class UIText extends NLS {
public static String FetchGerritChangePage_CreatingTagTaskName;
/** */
- public static String FetchGerritChangePage_FetchingRemoteRefsMessage;
-
- /** */
public static String FetchGerritChangePage_FetchingTaskName;
/** */
@@ -2882,9 +2891,6 @@ public class UIText extends NLS {
public static String FetchGerritChangePage_PageTitle;
/** */
- public static String FetchGerritChangePage_ShowingProposalsJobName;
-
- /** */
public static String FetchGerritChangePage_SuggestedRefNamePattern;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousListOperation.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousListOperation.java
new file mode 100644
index 0000000000..d6340840bb
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousListOperation.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.components;
+
+import java.lang.reflect.InvocationTargetException;
+import java.net.URISyntaxException;
+import java.text.MessageFormat;
+import java.util.Collection;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.core.op.ListRemoteOperation;
+import org.eclipse.egit.ui.Activator;
+import org.eclipse.egit.ui.UIPreferences;
+import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.dialogs.CancelableFuture;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.transport.URIish;
+
+/**
+ * A {@link ListRemoteOperation} that is run asynchronously as a
+ * {@link CancelableFuture}.
+ *
+ * @param <T>
+ * result type
+ */
+public abstract class AsynchronousListOperation<T>
+ extends CancelableFuture<Collection<T>> {
+
+ private final Repository repository;
+
+ private final String uriText;
+
+ private ListRemoteOperation listOp;
+
+ /**
+ * Creates a new {@link AsynchronousListOperation}.
+ *
+ * @param repository
+ * local repository for which to run the operation
+ * @param uriText
+ * upstream URI
+ */
+ public AsynchronousListOperation(Repository repository, String uriText) {
+ this.repository = repository;
+ this.uriText = uriText;
+ }
+
+ @Override
+ protected String getJobTitle() {
+ return MessageFormat.format(
+ UIText.AsynchronousRefProposalProvider_FetchingRemoteRefsMessage,
+ uriText);
+ }
+
+ @Override
+ protected void prepareRun() throws InvocationTargetException {
+ try {
+ listOp = new ListRemoteOperation(repository, new URIish(uriText),
+ Activator.getDefault().getPreferenceStore()
+ .getInt(UIPreferences.REMOTE_CONNECTION_TIMEOUT));
+ } catch (URISyntaxException e) {
+ throw new InvocationTargetException(e);
+ }
+ }
+
+ @Override
+ protected void run(IProgressMonitor monitor)
+ throws InterruptedException, InvocationTargetException {
+ listOp.run(monitor);
+ set(convert(listOp.getRemoteRefs()));
+ }
+
+ /**
+ * Transforms the {@link Ref}s obtained into the final objects. May just
+ * return the input if the generic type T is Ref, or may post-process the
+ * results as appropriate.
+ *
+ * @param refs
+ * obtained from the upstream repository
+ * @return final result
+ */
+ protected abstract Collection<T> convert(Collection<Ref> refs);
+
+ @Override
+ protected void done() {
+ listOp = null;
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousRefProposalProvider.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousRefProposalProvider.java
new file mode 100644
index 0000000000..e52de02e9e
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/AsynchronousRefProposalProvider.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.components;
+
+import java.lang.reflect.InvocationTargetException;
+import java.text.MessageFormat;
+import java.util.Collection;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.egit.ui.UIUtils.ExplicitContentProposalAdapter;
+import org.eclipse.egit.ui.UIUtils.IContentProposalCandidateProvider;
+import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.dialogs.CancelableFuture;
+import org.eclipse.egit.ui.internal.dialogs.NonBlockingWizardDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.wizard.IWizardContainer;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.widgets.Text;
+import org.eclipse.ui.progress.WorkbenchJob;
+
+/**
+ * An {@link IContentProposalCandidateProvider} that is intended to be used with
+ * an asynchronous {@link CancelableFuture} to get proposals in the background.
+ * <p>
+ * Note that is must be used with an {@link ExplicitContentProposalAdapter},
+ * which must be made known to it via
+ * {@link #setContentProposalAdapter(ExplicitContentProposalAdapter)
+ * setContentProposalAdapter()}.
+ */
+public class AsynchronousRefProposalProvider
+ implements IContentProposalCandidateProvider<Ref> {
+
+ private final IWizardContainer container;
+
+ private final Text textField;
+
+ private final Supplier<String> uriProvider;
+
+ private final Function<String, CancelableFuture<Collection<Ref>>> listProvider;
+
+ private ExplicitContentProposalAdapter contentProposer;
+
+ /**
+ * Creates a new {@link AsynchronousRefProposalProvider}. Because this is
+ * supposed to run truly asynchronously, typically in a
+ * {@link NonBlockingWizardDialog}, it needs to know the text field it
+ * belongs to and also the URI it was run for. It opens the proposals only
+ * if both are in a state where it still makes sense to show the proposals.
+ *
+ * @param container
+ * this candidate provider will be run for
+ * @param uriProvider
+ * a function returning the upstream URI to get proposals from
+ * @param textField
+ * this candidate provider belongs to
+ * @param listProvider
+ * a function that provides the CancelableFuture used to obtain
+ * the upstream refs
+ */
+ public AsynchronousRefProposalProvider(
+ IWizardContainer container, Text textField,
+ Supplier<String> uriProvider,
+ Function<String, CancelableFuture<Collection<Ref>>> listProvider) {
+ this.container = container;
+ this.textField = textField;
+ this.uriProvider = uriProvider;
+ this.listProvider = listProvider;
+ }
+
+ /**
+ * Makes the content proposal adapter known to this candidate provider. This
+ * is needed to be able to open the proposal popup asynchronously. If set to
+ * {@code null}, proposals will not be opened.
+ *
+ * @param adapter
+ * to set
+ */
+ public void setContentProposalAdapter(
+ ExplicitContentProposalAdapter adapter) {
+ contentProposer = adapter;
+ }
+
+ @Override
+ public Collection<? extends Ref> getCandidates() {
+ String uri = uriProvider.get();
+ if (uri == null) {
+ return null;
+ }
+ CancelableFuture<Collection<Ref>> list = listProvider.apply(uri);
+ try {
+ if (!list.isFinished()) {
+ IRunnableWithProgress operation = monitor -> {
+ monitor.beginTask(MessageFormat.format(
+ UIText.AsynchronousRefProposalProvider_FetchingRemoteRefsMessage,
+ uri), IProgressMonitor.UNKNOWN);
+ Collection<Ref> result = list.get();
+ if (monitor.isCanceled()) {
+ return;
+ }
+ // If we get here, the ChangeList future is done.
+ if (result == null || result.isEmpty()) {
+ // Don't bother if we didn't get any results
+ return;
+ }
+ // If we do have results now, open the proposals.
+ Job showProposals = new WorkbenchJob(
+ UIText.AsynchronousRefProposalProvider_ShowingProposalsJobName) {
+
+ @Override
+ public boolean shouldRun() {
+ return super.shouldRun() && contentProposer != null;
+ }
+
+ @Override
+ public IStatus runInUIThread(
+ IProgressMonitor uiMonitor) {
+ // But only if we're not disposed, the focus is
+ // still (or again) in the Change field, and the uri
+ // is still the same
+ try {
+ if (container instanceof NonBlockingWizardDialog) {
+ // Otherwise the dialog was blocked anyway,
+ // and focus will be restored
+ if (textField.isDisposed()
+ || !textField.isVisible()
+ || textField != textField
+ .getDisplay()
+ .getFocusControl()) {
+ return Status.CANCEL_STATUS;
+ }
+ String uriNow = uriProvider.get();
+ if (!uriNow.equals(uri)) {
+ return Status.CANCEL_STATUS;
+ }
+ }
+ contentProposer.openProposalPopup();
+ } catch (SWTException e) {
+ // Disposed already
+ return Status.CANCEL_STATUS;
+ } finally {
+ uiMonitor.done();
+ }
+ return Status.OK_STATUS;
+ }
+
+ };
+ showProposals.schedule();
+ };
+ if (container instanceof NonBlockingWizardDialog) {
+ NonBlockingWizardDialog dialog = (NonBlockingWizardDialog) container;
+ dialog.run(operation, () -> list
+ .cancel(CancelableFuture.CancelMode.ABANDON));
+ } else {
+ container.run(true, true, operation);
+ }
+ return null;
+ }
+ return list.get();
+ } catch (InterruptedException | InvocationTargetException e) {
+ return null;
+ }
+ }
+
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefContentProposal.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefContentProposal.java
index d332d61006..ad4a3d3139 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefContentProposal.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefContentProposal.java
@@ -14,6 +14,7 @@ import java.io.IOException;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.jface.fieldassist.IContentProposal;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -57,12 +58,14 @@ public class RefContentProposal implements IContentProposal {
private static void appendObjectSummary(final StringBuilder sb,
final String type, final PersonIdent author, final String message) {
sb.append(type);
- sb.append(" "); //$NON-NLS-1$
- sb.append(UIText.RefContentProposal_by);
- sb.append(" "); //$NON-NLS-1$
- sb.append(author.getName());
- sb.append("\n"); //$NON-NLS-1$
- sb.append(author.getWhen());
+ if (author != null) {
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(UIText.RefContentProposal_by);
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(author.getName());
+ sb.append("\n"); //$NON-NLS-1$
+ sb.append(author.getWhen());
+ }
sb.append("\n\n"); //$NON-NLS-1$
final int newLine = message.indexOf('\n');
final int last = (newLine != -1 ? newLine : message.length());
@@ -76,6 +79,12 @@ public class RefContentProposal implements IContentProposal {
private final ObjectId objectId;
/**
+ * Whether the ref is an upstream ref. For upstream refs, it's OK to have a
+ * missing object; it just means we haven't fetched yet.
+ */
+ private final boolean upstream;
+
+ /**
* Create content proposal for specified ref.
*
* @param repo
@@ -84,9 +93,11 @@ public class RefContentProposal implements IContentProposal {
* @param ref
* ref being a content proposal. May have null or locally
* non-existent object id.
+ * @param upstream
+ * {@code true} if the ref comes from an upstream repository
*/
- public RefContentProposal(final Repository repo, final Ref ref) {
- this(repo, ref.getName(), ref.getObjectId());
+ public RefContentProposal(Repository repo, Ref ref, boolean upstream) {
+ this(repo, ref.getName(), ref.getObjectId(), upstream);
}
/**
@@ -100,12 +111,15 @@ public class RefContentProposal implements IContentProposal {
* @param objectId
* object being pointed by this ref name. May be null or locally
* non-existent object.
+ * @param upstream
+ * {@code true} if the ref comes from an upstream repository
*/
- public RefContentProposal(final Repository repo, final String refName,
- final ObjectId objectId) {
+ public RefContentProposal(Repository repo, String refName,
+ ObjectId objectId, boolean upstream) {
this.db = repo;
this.refName = refName;
this.objectId = objectId;
+ this.upstream = upstream;
}
@Override
@@ -120,10 +134,23 @@ public class RefContentProposal implements IContentProposal {
@Override
public String getDescription() {
- if (objectId == null)
+ if (objectId == null) {
return null;
+ } else if (upstream && objectId.equals(ObjectId.zeroId())) {
+ return refName + '\n' + UIText.RefContentProposal_newRemoteObject;
+ }
try (ObjectReader reader = db.newObjectReader()) {
- final ObjectLoader loader = reader.open(objectId);
+ ObjectLoader loader = null;
+ try {
+ loader = reader.open(objectId);
+ } catch (MissingObjectException e) {
+ if (upstream) {
+ return refName + '\n' + objectId.abbreviate(7).name()
+ + " - " //$NON-NLS-1$
+ + UIText.RefContentProposal_unknownRemoteObject;
+ }
+ throw e;
+ }
final StringBuilder sb = new StringBuilder();
sb.append(refName);
sb.append('\n');
@@ -157,7 +184,8 @@ public class RefContentProposal implements IContentProposal {
return sb.toString();
} catch (IOException e) {
Activator.logError(NLS.bind(
- UIText.RefContentProposal_errorReadingObject, objectId), e);
+ UIText.RefContentProposal_errorReadingObject, objectId,
+ refName), e);
return null;
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefSpecPanel.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefSpecPanel.java
index 6375beef66..e29d69a30d 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefSpecPanel.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/components/RefSpecPanel.java
@@ -1736,13 +1736,15 @@ public class RefSpecPanel {
}
});
set.addAll(refs);
- if (HEAD != null)
+ if (HEAD != null) {
set.add(HEAD);
+ }
final List<RefContentProposal> result = new ArrayList<>(
set.size());
- for (final Ref r : set)
- result.add(new RefContentProposal(localDb, r));
+ for (final Ref r : set) {
+ result.add(new RefContentProposal(localDb, r, HEAD == null));
+ }
return result;
}
@@ -1817,27 +1819,33 @@ public class RefSpecPanel {
// contents contains wildcards
// check if contents can be safely added as wildcard spec
- if (isValidRefExpression(contents))
- result.add(new RefContentProposal(localDb, contents, null));
+ if (isValidRefExpression(contents)) {
+ result.add(new RefContentProposal(localDb, contents, null,
+ true));
+ }
// let's expand wildcards
final String regex = ".*" //$NON-NLS-1$
+ contents.replace("*", ".*").replace("?", ".?") + ".*"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
final Pattern p = Pattern.compile(regex);
- for (final RefContentProposal prop : proposals)
- if (p.matcher(prop.getContent()).matches())
+ for (final RefContentProposal prop : proposals) {
+ if (p.matcher(prop.getContent()).matches()) {
result.add(prop);
+ }
+ }
} else {
- for (final RefContentProposal prop : proposals)
- if (prop.getContent().contains(contents))
+ for (final RefContentProposal prop : proposals) {
+ if (prop.getContent().contains(contents)) {
result.add(prop);
+ }
+ }
if (tryResolvingLocally && result.isEmpty()) {
final ObjectId id = tryResolveLocalRef(contents);
- if (id != null)
- result
- .add(new RefContentProposal(localDb, contents,
- id));
+ if (id != null) {
+ result.add(new RefContentProposal(localDb, contents, id,
+ false));
+ }
}
}
return result.toArray(new IContentProposal[0]);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CancelableFuture.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CancelableFuture.java
new file mode 100644
index 0000000000..d7786d774d
--- /dev/null
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/CancelableFuture.java
@@ -0,0 +1,326 @@
+/*******************************************************************************
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *******************************************************************************/
+package org.eclipse.egit.ui.internal.dialogs;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeEvent;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.jobs.JobChangeAdapter;
+import org.eclipse.egit.ui.Activator;
+
+/**
+ * A {@code CancelableFuture} is a "Future" using Eclipse jobs to asynchronously
+ * perform long tasks. The {@link #get() get()} method blocks until the result
+ * is available or the future is canceled. The first call to {@link #get()
+ * get()} starts a background job running the {@link #run(IProgressMonitor)
+ * run()} operation, which is supposed to call {@link #set(Object) set()} to set
+ * its result. Pre-fetching is possible by calling {@link #start()} directly.
+ * <p>
+ * The main differences to a {@link java.util.concurrent.FutureTask} are:
+ * </p>
+ * <ul>
+ * <li>This class is not that much optimized.</li>
+ * <li>Cancellation behaves differently. A {@code FutureTask} that is canceled
+ * before completed ignores any result that might still arrive, and always says
+ * it's been canceled. A {@code CancelableFuture} accepts a result even after
+ * having been canceled, so a subsequent {@link #get()} may get that result. In
+ * both, cancellation wakes up all waiting calls to {@link #get()}.</li>
+ * <li>A {@code CancelableFuture} automatically runs the task in a background
+ * job, whereas a {@code FutureTask} would need an extra
+ * {@link java.util.concurrent.ExecutorService ExecutorService} to
+ * asynchronously run its task.</li>
+ * <li>This class uses Eclipse {@link Job}s instead of Threads directly.</li>
+ * </ul>
+ *
+ * @param <V>
+ * the type of the result value
+ */
+public abstract class CancelableFuture<V> {
+
+ /**
+ * Determines how to cancel a not yet completed future. Irrespective of the
+ * mechanism, the job may actually terminate normally, and subsequent calls
+ * to {@link CancelableFuture#get() get()} may return a result.
+ */
+ public static enum CancelMode {
+ /**
+ * Tries to cancel the job, which may decide to ignore the request.
+ * Callers to {@link CancelableFuture#get() get()} will remain blocked
+ * until the job terminates.
+ */
+ CANCEL,
+ /**
+ * Tries to cancel the job, which may decide to ignore the request.
+ * Outstanding {@link CancelableFuture#get() get()} calls will be woken
+ * up and may throw {@link InterruptedException} or return a result if
+ * the job terminated in the meantime.
+ */
+ ABANDON,
+ /**
+ * Tries to cancel the job, and if that doesn't succeed immediately,
+ * interrupts the job's thread. Outstanding calls to
+ * {@link CancelableFuture#get() get()} will be woken up and may throw
+ * {@link InterruptedException} or return a result if the job terminated
+ * in the meantime.
+ */
+ INTERRUPT
+ }
+
+ private static enum State {
+ PRISTINE, SCHEDULED, CANCELING, INTERRUPT, CANCELED, DONE
+ }
+
+ private State state = State.PRISTINE;
+
+ private V result;
+
+ private InterruptibleJob job;
+
+ /**
+ * Tries to cancel the future. See {@link CancelMode} for semantics.
+ *
+ * @param cancellation
+ * {@link CancelMode} defining how to cancel. If {@code null}
+ * defaults to {@link CancelMode#CANCEL}.
+ *
+ * @return {@code true} if the future was canceled (its job is not running
+ * anymore), {@code false} otherwise.
+ */
+ public synchronized boolean cancel(CancelMode cancellation) {
+ CancelMode mode = cancellation == null ? CancelMode.CANCEL
+ : cancellation;
+ switch (state) {
+ case PRISTINE:
+ finish(false);
+ return true;
+ case SCHEDULED:
+ state = State.CANCELING;
+ boolean canceled = job.cancel();
+ if (canceled) {
+ state = State.CANCELED;
+ } else if (mode == CancelMode.INTERRUPT) {
+ interrupt();
+ } else if (mode == CancelMode.ABANDON) {
+ notifyAll();
+ }
+ return canceled;
+ case CANCELING:
+ // cancel(CANCEL|ABANDON) was called before.
+ if (mode == CancelMode.INTERRUPT) {
+ interrupt();
+ } else if (mode == CancelMode.ABANDON) {
+ notifyAll();
+ }
+ return false;
+ case INTERRUPT:
+ if (mode != CancelMode.CANCEL) {
+ notifyAll();
+ }
+ return false;
+ case CANCELED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Tells whether or not the future's background job is still running.
+ *
+ * @return {@code true} if the future's background job isn't running anymore
+ * (was canceled or terminated normally)
+ */
+ public synchronized boolean isFinished() {
+ return state == State.CANCELED || state == State.DONE;
+ }
+
+ /**
+ * Tells whether the future completed normally.
+ *
+ * @return {@code true} if the future completed normally, {@code false}
+ * otherwise
+ */
+ public synchronized boolean isDone() {
+ return state == State.DONE;
+ }
+
+ /**
+ * Retrieves the result. If the result is not yet available, the method
+ * blocks until it is or {@link #cancel(CancelMode)} is called with
+ * {@link CancelMode#ABANDON} or {@link CancelMode#INTERRUPT}.
+ *
+ * @return the result, which may be {@code null} if the future was canceled
+ * @throws InterruptedException
+ * if waiting was interrupted
+ * @throws InvocationTargetException
+ * if the future's job cannot be created
+ */
+ public synchronized V get()
+ throws InterruptedException, InvocationTargetException {
+ switch (state) {
+ case DONE:
+ case CANCELED:
+ return result;
+ case PRISTINE:
+ start();
+ return get();
+ default:
+ wait();
+ if (state == State.CANCELING || state == State.INTERRUPT) {
+ // canceled with ABANDON or INTERRUPT
+ throw new InterruptedException();
+ }
+ return get();
+ }
+ }
+
+ private synchronized void finish(boolean done) {
+ state = done ? State.DONE : State.CANCELED;
+ job = null;
+ try {
+ done();
+ } finally {
+ // We're done, wake up all outstanding get() calls.
+ notifyAll();
+ }
+ }
+
+ private synchronized void interrupt() {
+ state = State.INTERRUPT;
+ job.interrupt();
+ notifyAll(); // Abandon outstanding get() calls
+ }
+
+ /**
+ * Sets the future's result.
+ *
+ * @param value
+ * to use as the result
+ */
+ protected void set(V value) {
+ result = value;
+ }
+
+ /**
+ * Called by {@link #start()} before the background job is scheduled.
+ * Subclasses may override to initialize data before the job starts.
+ *
+ * @throws InvocationTargetException
+ * on errors
+ */
+ protected void prepareRun() throws InvocationTargetException {
+ // Default does nothing
+ }
+
+ /**
+ * Obtain a job title for the background job executing the future's task.
+ *
+ * @return the title
+ */
+ protected String getJobTitle() {
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Performs the future's task.
+ *
+ * @param monitor
+ * for progress reporting and cancellation
+ * @throws InterruptedException
+ * if canceled
+ * @throws InvocationTargetException
+ * on other errors
+ */
+ protected abstract void run(IProgressMonitor monitor)
+ throws InterruptedException, InvocationTargetException;
+
+ /**
+ * Called when the future is done, i.e., either completed normally, was
+ * canceled, or failed to start. Subclasses may override to do clean-up.
+ */
+ protected void done() {
+ // Default does nothing
+ }
+
+ /**
+ * On the first call, starts a background job to fetch the result.
+ * Subsequent calls do nothing and return immediately. Before creating and
+ * scheduling the job, {@link #prepareRun()} is called.
+ *
+ * @throws InvocationTargetException
+ * propagated from {@link #prepareRun()}
+ */
+ public synchronized void start() throws InvocationTargetException {
+ if (job != null || state != State.PRISTINE) {
+ return;
+ }
+ try {
+ prepareRun();
+ } catch (InvocationTargetException e) {
+ finish(false);
+ throw e;
+ }
+ job = new InterruptibleJob(getJobTitle()) {
+
+ @Override
+ protected IStatus run(IProgressMonitor monitor) {
+ try {
+ CancelableFuture.this.run(monitor);
+ return Status.OK_STATUS;
+ } catch (InterruptedException e) {
+ return Status.CANCEL_STATUS;
+ } catch (InvocationTargetException e) {
+ synchronized (CancelableFuture.this) {
+ if (state == State.CANCELING
+ || state == State.INTERRUPT) {
+ return Status.CANCEL_STATUS;
+ }
+ }
+ return Activator.createErrorStatus(e.getLocalizedMessage(),
+ e);
+ } catch (RuntimeException e) {
+ return Activator.createErrorStatus(e.getLocalizedMessage(),
+ e);
+ }
+ }
+
+ };
+ job.addJobChangeListener(new JobChangeAdapter() {
+
+ @Override
+ public void done(IJobChangeEvent event) {
+ IStatus status = event.getResult();
+ finish(status != null && status.isOK());
+ }
+
+ });
+ job.setUser(false);
+ job.setSystem(true);
+ state = State.SCHEDULED;
+ job.schedule();
+ }
+
+ private static abstract class InterruptibleJob extends Job {
+
+ public InterruptibleJob(String name) {
+ super(name);
+ }
+
+ public void interrupt() {
+ Thread thread = getThread();
+ if (thread != null) {
+ thread.interrupt();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/NonBlockingWizardDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/NonBlockingWizardDialog.java
index 033ccc0a11..d8605f5c04 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/NonBlockingWizardDialog.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/dialogs/NonBlockingWizardDialog.java
@@ -85,26 +85,17 @@ public class NonBlockingWizardDialog extends MinimumSizeWizardDialog {
}
/**
- * If {@code fork} is {@code true}, this implementation does <em>not</em>
- * block but schedules a true background job. Such background jobs are
- * queued and will execute one after another. They are canceled when the
- * current wizard page changes, or when the dialog closes.
- * </p>
- */
- @Override
- public void run(boolean fork, boolean cancelable,
- IRunnableWithProgress runnable)
- throws InvocationTargetException, InterruptedException {
- if (!fork) {
- super.run(fork, cancelable, runnable);
- }
- run(runnable, null);
- }
-
- /**
* Runs the given {@code runnable} in a background job, reporting progress
* through the dialog's progress monitor, if any, and invoking
* {@code onCancel} if the job is canceled.
+ * <p>
+ * The dialog is <em>not</em> made inactive while the job runs.
+ * </p>
+ * <p>
+ * Such background jobs are queued and will execute one after another. They
+ * are canceled when the current wizard page changes, or when the dialog
+ * closes.
+ * </p>
*
* @param runnable
* to run
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchDestinationPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchDestinationPage.java
index a22e9de2b8..58c849b76b 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchDestinationPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchDestinationPage.java
@@ -94,7 +94,7 @@ public class FetchDestinationPage extends WizardPage {
GridDataFactory.fillDefaults().grab(true, false).applyTo(
destinationText);
UIUtils.addRefContentProposalToText(sourceText, repository,
- () -> getRemoteRefs());
+ () -> getRemoteRefs(), true);
force = new Button(main, SWT.CHECK);
force.setText(UIText.FetchDestinationPage_ForceCheckbox);
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java
index 0df477cedc..cad680e295 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2017 SAP AG and others.
+ * Copyright (c) 2010, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -26,7 +26,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
@@ -41,37 +40,32 @@ import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
-import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.Job;
-import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.egit.core.internal.gerrit.GerritUtil;
import org.eclipse.egit.core.op.CreateLocalBranchOperation;
-import org.eclipse.egit.core.op.ListRemoteOperation;
import org.eclipse.egit.core.op.TagOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.JobFamilies;
import org.eclipse.egit.ui.UIPreferences;
import org.eclipse.egit.ui.UIUtils;
+import org.eclipse.egit.ui.UIUtils.ExplicitContentProposalAdapter;
import org.eclipse.egit.ui.internal.ActionUtils;
import org.eclipse.egit.ui.internal.UIText;
import org.eclipse.egit.ui.internal.ValidationUtils;
import org.eclipse.egit.ui.internal.branch.BranchOperationUI;
+import org.eclipse.egit.ui.internal.components.AsynchronousListOperation;
import org.eclipse.egit.ui.internal.components.BranchNameNormalizer;
import org.eclipse.egit.ui.internal.dialogs.AbstractBranchSelectionDialog;
import org.eclipse.egit.ui.internal.dialogs.BranchEditDialog;
import org.eclipse.egit.ui.internal.dialogs.NonBlockingWizardDialog;
import org.eclipse.egit.ui.internal.gerrit.GerritDialogSettings;
-import org.eclipse.jface.bindings.keys.KeyStroke;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.IPageChangeProvider;
import org.eclipse.jface.dialogs.IPageChangedListener;
import org.eclipse.jface.dialogs.PageChangedEvent;
-import org.eclipse.jface.fieldassist.ContentProposalAdapter;
import org.eclipse.jface.fieldassist.IContentProposal;
-import org.eclipse.jface.fieldassist.IContentProposalProvider;
-import org.eclipse.jface.fieldassist.TextContentAdapter;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.JFaceResources;
@@ -110,7 +104,6 @@ import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
-import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.progress.WorkbenchJob;
@@ -542,7 +535,7 @@ public class FetchGerritChangePage extends WizardPage {
private void preFetch(ChangeList list) {
try {
- list.fetch();
+ list.start();
} catch (InvocationTargetException e) {
Activator.handleError(e.getLocalizedMessage(), e.getCause(), true);
}
@@ -724,22 +717,26 @@ public class FetchGerritChangePage extends WizardPage {
}
ChangeList list = changeRefs.get(uriCombo.getText());
if (list != null && list.isDone()) {
- if (change.getPatchSetNumber() != null) {
- if (!list.getResult().contains(change)) {
- setErrorMessage(
- UIText.FetchGerritChangePage_UnknownChangeRefMessage);
- return;
- }
- } else {
- Change fromGerrit = findHighestPatchSet(
- list.getResult(),
- change.getChangeNumber().intValue());
- if (fromGerrit == null) {
- setErrorMessage(NLS.bind(
- UIText.FetchGerritChangePage_NoSuchChangeMessage,
- change.getChangeNumber()));
- return;
+ try {
+ if (change.getPatchSetNumber() != null) {
+ if (!list.get().contains(change)) {
+ setErrorMessage(
+ UIText.FetchGerritChangePage_UnknownChangeRefMessage);
+ return;
+ }
+ } else {
+ Change fromGerrit = findHighestPatchSet(list.get(),
+ change.getChangeNumber().intValue());
+ if (fromGerrit == null) {
+ setErrorMessage(NLS.bind(
+ UIText.FetchGerritChangePage_NoSuchChangeMessage,
+ change.getChangeNumber()));
+ return;
+ }
}
+ } catch (InterruptedException
+ | InvocationTargetException e) {
+ // Ignore: since we're done, this should never occur
}
}
} else {
@@ -768,7 +765,7 @@ public class FetchGerritChangePage extends WizardPage {
IWizardContainer container = getContainer();
IRunnableWithProgress operation = monitor -> {
monitor.beginTask(MessageFormat.format(
- UIText.FetchGerritChangePage_FetchingRemoteRefsMessage,
+ UIText.AsynchronousRefProposalProvider_FetchingRemoteRefsMessage,
uriText), IProgressMonitor.UNKNOWN);
Collection<Change> result = list.get();
if (monitor.isCanceled()) {
@@ -781,7 +778,7 @@ public class FetchGerritChangePage extends WizardPage {
}
// If we do have results now, open the proposals.
Job showProposals = new WorkbenchJob(
- UIText.FetchGerritChangePage_ShowingProposalsJobName) {
+ UIText.AsynchronousRefProposalProvider_ShowingProposalsJobName) {
@Override
public boolean shouldRun() {
@@ -1007,7 +1004,7 @@ public class FetchGerritChangePage extends WizardPage {
throws OperationCanceledException {
if (changeList != null) {
monitor.subTask(NLS.bind(
- UIText.FetchGerritChangePage_FetchingRemoteRefsMessage,
+ UIText.AsynchronousRefProposalProvider_FetchingRemoteRefsMessage,
uri));
Collection<Change> changes;
try {
@@ -1132,72 +1129,29 @@ public class FetchGerritChangePage extends WizardPage {
private ExplicitContentProposalAdapter addRefContentProposalToText(
final Text textField) {
- KeyStroke stroke = UIUtils
- .getKeystrokeOfBestActiveBindingFor(IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST);
- if (stroke != null) {
- UIUtils.addBulbDecorator(textField, NLS.bind(
- UIText.FetchGerritChangePage_ContentAssistTooltip,
- stroke.format()));
- }
- IContentProposalProvider cp = new IContentProposalProvider() {
-
- @Override
- public IContentProposal[] getProposals(String contents, int position) {
- Collection<Change> proposals;
- try {
- proposals = getRefsForContentAssist(contents);
- } catch (InvocationTargetException e) {
- Activator.handleError(e.getMessage(), e, true);
- return null;
- } catch (InterruptedException e) {
- return null;
- }
-
- if (proposals == null) {
- return null;
- }
- List<IContentProposal> resultList = new ArrayList<>();
- String input = contents;
- Matcher matcher = GERRIT_CHANGE_REF_PATTERN.matcher(contents);
- if (matcher.find()) {
- input = matcher.group(2);
- }
- Pattern pattern = UIUtils.createProposalPattern(input);
- for (final Change ref : proposals) {
- if (pattern != null && !pattern
- .matcher(ref.getChangeNumber().toString())
- .matches()) {
- continue;
- }
- resultList.add(new ChangeContentProposal(ref));
- }
- return resultList
- .toArray(new IContentProposal[resultList.size()]);
+ return UIUtils.addContentProposalToText(textField, () -> {
+ try {
+ return getRefsForContentAssist(textField.getText());
+ } catch (InvocationTargetException e) {
+ Activator.handleError(e.getMessage(), e, true);
+ return null;
+ } catch (InterruptedException e) {
+ return null;
}
- };
-
- ExplicitContentProposalAdapter adapter = new ExplicitContentProposalAdapter(
- textField, cp, stroke);
- // set the acceptance style to always replace the complete content
- adapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);
- return adapter;
- }
-
- private static class ExplicitContentProposalAdapter
- extends ContentProposalAdapter {
-
- public ExplicitContentProposalAdapter(Control control,
- IContentProposalProvider proposalProvider,
- KeyStroke keyStroke) {
- super(control, new TextContentAdapter(), proposalProvider,
- keyStroke, null);
- }
-
- @Override
- public void openProposalPopup() {
- // Make this method accessible
- super.openProposalPopup();
- }
+ }, (pattern, ref) -> {
+ if (pattern == null || pattern
+ .matcher(ref.getChangeNumber().toString()).matches()) {
+ return new ChangeContentProposal(ref);
+ }
+ return null;
+ }, s -> {
+ String input = s;
+ Matcher matcher = GERRIT_CHANGE_REF_PATTERN.matcher(input);
+ if (matcher.find()) {
+ input = matcher.group(2);
+ }
+ return UIUtils.createProposalPattern(input);
+ }, null, UIText.FetchGerritChangePage_ContentAssistTooltip);
}
final static class Change implements Comparable<Change> {
@@ -1340,254 +1294,26 @@ public class FetchGerritChangePage extends WizardPage {
}
/**
- * A {@code ChangeList} is a "Future", loading the list of change refs
- * asynchronously from the remote repository. The {@link ChangeList#get()
- * get()} method blocks until the result is available or the future is
- * canceled. Pre-fetching is possible by calling {@link ChangeList#fetch()}
- * directly.
+ * A {@code ChangeList} loads the list of change refs asynchronously from
+ * the remote repository.
*/
- private static class ChangeList {
-
- /**
- * Determines how to cancel a not-yet-completed future. Irrespective of
- * the mechanism, the job may actually terminate normally, and
- * subsequent calls to get() may return a result.
- */
- public static enum CancelMode {
- /**
- * Tries to cancel the job, which may decide to ignore the request.
- * Callers to get() will remain blocked until the job terminates.
- */
- CANCEL,
- /**
- * Tries to cancel the job, which may decide to ignore the request.
- * Outstanding get() calls will be woken up and may throw
- * InterruptedException or return a result if the job terminated in
- * the meantime.
- */
- ABANDON,
- /**
- * Tries to cancel the job, and if that doesn't succeed immediately,
- * interrupts the job's thread. Outstanding calls to get() will be
- * woken up and may throw InterruptedException or return a result if
- * the job terminated in the meantime.
- */
- INTERRUPT
- }
-
- private static enum State {
- PRISTINE, SCHEDULED, CANCELING, INTERRUPT, CANCELED, DONE
- }
-
- private final Repository repository;
-
- private final String uriText;
-
- private State state = State.PRISTINE;
-
- private Set<Change> result;
-
- private InterruptibleJob job;
+ private static class ChangeList extends AsynchronousListOperation<Change> {
public ChangeList(Repository repository, String uriText) {
- this.repository = repository;
- this.uriText = uriText;
- }
-
- /**
- * Tries to cancel the future. {@code cancel(false)} tries a normal job
- * cancellation, which may or may not terminated the job (it may decide
- * not to react to cancellation requests).
- *
- * @param cancellation
- * {@link CancelMode} defining how to cancel
- *
- * @return {@code true} if the future was canceled (its job is not
- * running anymore), {@code false} otherwise.
- */
- public synchronized boolean cancel(CancelMode cancellation) {
- CancelMode mode = cancellation == null ? CancelMode.CANCEL
- : cancellation;
- switch (state) {
- case PRISTINE:
- finish(false);
- return true;
- case SCHEDULED:
- state = State.CANCELING;
- boolean canceled = job.cancel();
- if (canceled) {
- state = State.CANCELED;
- } else if (mode == CancelMode.INTERRUPT) {
- interrupt();
- } else if (mode == CancelMode.ABANDON) {
- notifyAll();
- }
- return canceled;
- case CANCELING:
- // cancel(CANCEL|ABANDON) was called before.
- if (mode == CancelMode.INTERRUPT) {
- interrupt();
- } else if (mode == CancelMode.ABANDON) {
- notifyAll();
- }
- return false;
- case INTERRUPT:
- if (mode != CancelMode.CANCEL) {
- notifyAll();
- }
- return false;
- case CANCELED:
- return true;
- default:
- return false;
- }
- }
-
- public synchronized boolean isFinished() {
- return state == State.CANCELED || state == State.DONE;
- }
-
- public synchronized boolean isDone() {
- return state == State.DONE;
- }
-
- /**
- * Retrieves the result. If the result is not yet available, the method
- * blocks until it is or {@link #cancel(CancelMode)} is called with
- * {@link CancelMode#ABANDON} or {@link CancelMode#INTERRUPT}.
- *
- * @return the result, which may be {@code null} if the future was
- * canceled
- * @throws InterruptedException
- * if waiting was interrupted
- * @throws InvocationTargetException
- * if the future's job cannot be created
- */
- public synchronized Collection<Change> get()
- throws InterruptedException, InvocationTargetException {
- switch (state) {
- case DONE:
- case CANCELED:
- return result;
- case PRISTINE:
- fetch();
- return get();
- default:
- wait();
- if (state == State.CANCELING || state == State.INTERRUPT) {
- // canceled with ABANDON or INTERRUPT
- throw new InterruptedException();
- }
- return get();
- }
- }
-
- public synchronized Collection<Change> getResult() {
- if (isFinished()) {
- return result;
- }
- throw new IllegalStateException(
- "Fetching change list is not finished"); //$NON-NLS-1$
- }
-
- private synchronized void finish(boolean done) {
- state = done ? State.DONE : State.CANCELED;
- job = null;
- notifyAll(); // We're done, wake up all outstanding get() calls
- }
-
- private synchronized void interrupt() {
- state = State.INTERRUPT;
- job.interrupt();
- notifyAll(); // Abandon outstanding get() calls
- }
-
- /**
- * On the first call, starts a background job to fetch the result.
- * Subsequent calls do nothing and return immediately.
- *
- * @throws InvocationTargetException
- * if starting the job fails
- */
- public synchronized void fetch() throws InvocationTargetException {
- if (job != null || state != State.PRISTINE) {
- return;
- }
- ListRemoteOperation listOp;
- try {
- listOp = new ListRemoteOperation(repository,
- new URIish(uriText),
- Activator.getDefault().getPreferenceStore().getInt(
- UIPreferences.REMOTE_CONNECTION_TIMEOUT));
- } catch (URISyntaxException e) {
- finish(false);
- throw new InvocationTargetException(e);
- }
- job = new InterruptibleJob(MessageFormat.format(
- UIText.FetchGerritChangePage_FetchingRemoteRefsMessage,
- uriText)) {
-
- @Override
- protected IStatus run(IProgressMonitor monitor) {
- try {
- listOp.run(monitor);
- } catch (InterruptedException e) {
- return Status.CANCEL_STATUS;
- } catch (InvocationTargetException e) {
- synchronized (ChangeList.this) {
- if (state == State.CANCELING
- || state == State.INTERRUPT) {
- // JGit may report a TransportException when the
- // thread is interrupted. Let's just pretend we
- // canceled before. Also, if the user canceled
- // already, he's not interested in errors
- // anymore.
- return Status.CANCEL_STATUS;
- }
- }
- return Activator
- .createErrorStatus(e.getLocalizedMessage(), e);
- }
- List<Change> changes = new ArrayList<>();
- for (Ref ref : listOp.getRemoteRefs()) {
- Change change = Change.fromRef(ref.getName());
- if (change != null) {
- changes.add(change);
- }
- }
- Collections.sort(changes, Collections.reverseOrder());
- result = new LinkedHashSet<>(changes);
- return Status.OK_STATUS;
- }
-
- };
- job.addJobChangeListener(new JobChangeAdapter() {
-
- @Override
- public void done(IJobChangeEvent event) {
- IStatus status = event.getResult();
- finish(status != null && status.isOK());
- }
-
- });
- job.setUser(false);
- job.setSystem(true);
- state = State.SCHEDULED;
- job.schedule();
+ super(repository, uriText);
}
- private static abstract class InterruptibleJob extends Job {
-
- public InterruptibleJob(String name) {
- super(name);
- }
-
- public void interrupt() {
- Thread thread = getThread();
- if (thread != null) {
- thread.interrupt();
+ @Override
+ protected Collection<Change> convert(Collection<Ref> refs) {
+ List<Change> changes = new ArrayList<>();
+ for (Ref ref : refs) {
+ Change change = Change.fromRef(ref.getName());
+ if (change != null) {
+ changes.add(change);
}
}
+ Collections.sort(changes, Collections.reverseOrder());
+ return new LinkedHashSet<>(changes);
}
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchSourcePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchSourcePage.java
index f1ff6c2cea..a78c532db2 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchSourcePage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchSourcePage.java
@@ -89,7 +89,7 @@ public class FetchSourcePage extends WizardPage {
});
GridDataFactory.fillDefaults().grab(true, false).applyTo(sourceText);
UIUtils.addRefContentProposalToText(sourceText, repository,
- () -> getRemoteRefs());
+ () -> getRemoteRefs(), true);
checkPage();
setControl(main);
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/gerrit/GerritConfigurationPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/gerrit/GerritConfigurationPage.java
index 985b61dc28..c5dce09919 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/gerrit/GerritConfigurationPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/gerrit/GerritConfigurationPage.java
@@ -317,7 +317,8 @@ class GerritConfigurationPage extends WizardPage {
return null;
}
return new ContentProposal(refName);
- }, UIText.GerritConfigurationPage_BranchTooltipStartTyping,
+ }, null,
+ UIText.GerritConfigurationPage_BranchTooltipStartTyping,
UIText.GerritConfigurationPage_BranchTooltipHover);
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/pull/PullWizardPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/pull/PullWizardPage.java
index 02b2eadce4..ec80bb6abc 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/pull/PullWizardPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/pull/PullWizardPage.java
@@ -172,7 +172,7 @@ public class PullWizardPage extends WizardPage {
.getRefsForContentAssist(false, true);
}
return Collections.emptyList();
- });
+ }, true);
remoteBranchNameText.setText(getSuggestedBranchName());
remoteBranchNameText.addModifyListener(new ModifyListener() {
@Override
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchPage.java
index eb9626bfda..6ca9b14e42 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2016 Robin Stocker <robin@nibor.org> and others.
+ * Copyright (c) 2013, 2018 Robin Stocker <robin@nibor.org> and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -8,29 +8,36 @@
package org.eclipse.egit.ui.internal.push;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.eclipse.egit.core.internal.Utils;
import org.eclipse.egit.core.op.CreateLocalBranchOperation;
import org.eclipse.egit.ui.Activator;
import org.eclipse.egit.ui.UIUtils;
+import org.eclipse.egit.ui.internal.CommonUtils;
import org.eclipse.egit.ui.internal.UIIcons;
import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.components.AsynchronousListOperation;
+import org.eclipse.egit.ui.internal.components.AsynchronousRefProposalProvider;
import org.eclipse.egit.ui.internal.components.BranchNameNormalizer;
-import org.eclipse.egit.ui.internal.components.RefContentAssistProvider;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.IRemoteSelectionListener;
import org.eclipse.egit.ui.internal.components.RemoteSelectionCombo.SelectionType;
import org.eclipse.egit.ui.internal.components.UpstreamConfigComponent;
import org.eclipse.egit.ui.internal.components.UpstreamConfigComponent.UpstreamConfigSelectionListener;
+import org.eclipse.egit.ui.internal.dialogs.CancelableFuture;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
@@ -43,7 +50,9 @@ import org.eclipse.jgit.lib.BranchConfig;
import org.eclipse.jgit.lib.BranchConfig.BranchRebaseMode;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Ref.Storage;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.revwalk.RevCommit;
@@ -91,8 +100,6 @@ public class PushBranchPage extends WizardPage {
private Text remoteBranchNameText;
- private RefContentAssistProvider assist;
-
private BranchRebaseMode upstreamConfig;
private UpstreamConfigComponent upstreamConfigComponent;
@@ -104,6 +111,8 @@ public class PushBranchPage extends WizardPage {
private Set<Resource> disposables = new HashSet<>();
+ private Map<String, FutureRefs> refs = new HashMap<>();
+
/**
* Create the page.
*
@@ -161,6 +170,12 @@ public class PushBranchPage extends WizardPage {
@Override
public void createControl(Composite parent) {
+ parent.addDisposeListener(event -> {
+ for (CancelableFuture<Collection<Ref>> l : refs.values()) {
+ l.cancel(CancelableFuture.CancelMode.INTERRUPT);
+ }
+ refs.clear();
+ });
try {
this.remoteConfigs = RemoteConfig.getAllRemoteConfigs(repository.getConfig());
Collections.sort(remoteConfigs, new Comparator<RemoteConfig>() {
@@ -288,15 +303,30 @@ public class PushBranchPage extends WizardPage {
GridDataFactory.fillDefaults().grab(true, false).span(2, 1)
.applyTo(remoteBranchNameText);
remoteBranchNameText.setText(getSuggestedBranchName());
- UIUtils.addRefContentProposalToText(remoteBranchNameText,
- this.repository, () -> {
- if (PushBranchPage.this.assist != null) {
- return PushBranchPage.this.assist
- .getRefsForContentAssist(false, true);
+ AsynchronousRefProposalProvider candidateProvider = new AsynchronousRefProposalProvider(
+ getContainer(), remoteBranchNameText, () -> {
+ RemoteConfig config = remoteSelectionCombo
+ .getSelectedRemote();
+ if (config == null) {
+ return null;
}
- return Collections.emptyList();
+ List<URIish> uris = config.getURIs();
+ if (uris == null || uris.isEmpty()) {
+ return null;
+ }
+ return uris.get(0).toString();
+ }, uri -> {
+ FutureRefs list = refs.get(uri);
+ if (list == null) {
+ list = new FutureRefs(repository, uri,
+ getLocalBranchName());
+ refs.put(uri, list);
+ }
+ return list;
});
-
+ candidateProvider.setContentProposalAdapter(
+ UIUtils.addRefContentProposalToText(remoteBranchNameText,
+ this.repository, candidateProvider, true));
if (this.ref != null) {
upstreamConfigComponent = new UpstreamConfigComponent(inputPanel,
SWT.NONE);
@@ -464,6 +494,13 @@ public class PushBranchPage extends WizardPage {
}
}
+ private String getLocalBranchName() {
+ if (ref != null && !ref.getName().startsWith(Constants.R_REMOTES)) {
+ return Repository.shortenRefName(ref.getName());
+ }
+ return null;
+ }
+
private String getSuggestedBranchName() {
if (ref != null && !ref.getName().startsWith(Constants.R_REMOTES)) {
StoredConfig config = repository.getConfig();
@@ -483,9 +520,22 @@ public class PushBranchPage extends WizardPage {
private void setRefAssist(RemoteConfig config) {
if (config != null && config.getURIs().size() > 0) {
- this.assist = new RefContentAssistProvider(
- PushBranchPage.this.repository, config.getURIs().get(0),
- getShell());
+ String uriText = config.getURIs().get(0).toString();
+ FutureRefs list = refs.get(uriText);
+ if (list == null) {
+ list = new FutureRefs(repository, uriText,
+ getLocalBranchName());
+ refs.put(uriText, list);
+ preFetch(list);
+ }
+ }
+ }
+
+ private void preFetch(FutureRefs list) {
+ try {
+ list.start();
+ } catch (InvocationTargetException e) {
+ Activator.handleError(e.getLocalizedMessage(), e.getCause(), true);
}
}
@@ -523,4 +573,51 @@ public class PushBranchPage extends WizardPage {
for (Resource disposable : this.disposables)
disposable.dispose();
}
+
+ /**
+ * {@code FutureRefs} are loaded asynchronously from the upstream
+ * repository.
+ */
+ private static class FutureRefs extends AsynchronousListOperation<Ref> {
+
+ private final String localBranchName;
+
+ public FutureRefs(Repository repository, String uriText,
+ String localBranchName) {
+ super(repository, uriText);
+ this.localBranchName = localBranchName;
+ }
+
+ @Override
+ protected Collection<Ref> convert(Collection<Ref> refs) {
+ List<Ref> filtered = new ArrayList<>();
+ String localFullName = localBranchName != null
+ ? Constants.R_HEADS + localBranchName : null;
+ boolean localBranchFound = false;
+ // Restrict to branches
+ for (Ref ref : refs) {
+ String name = ref.getName();
+ if (name.startsWith(Constants.R_HEADS)) {
+ filtered.add(ref);
+ if (localFullName != null
+ && localFullName.equalsIgnoreCase(name)) {
+ localBranchFound = true;
+ }
+ }
+ }
+ // Sort them
+ Collections.sort(filtered, CommonUtils.REF_ASCENDING_COMPARATOR);
+ // Add a new remote ref for localBranchName in front if it doesn't
+ // exist
+ if (localFullName != null && !localBranchFound) {
+ List<Ref> newRefs = new ArrayList<>(filtered.size() + 1);
+ newRefs.add(new ObjectIdRef.Unpeeled(Storage.NEW, localFullName,
+ ObjectId.zeroId()));
+ newRefs.addAll(filtered);
+ filtered = newRefs;
+ }
+ return filtered;
+ }
+ }
+
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchWizard.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchWizard.java
index c48f346dc3..b9f214314e 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchWizard.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushBranchWizard.java
@@ -105,6 +105,7 @@ public class PushBranchWizard extends Wizard {
}
};
+ setNeedsProgressMonitor(true);
setDefaultPageImageDescriptor(UIIcons.WIZBAN_PUSH);
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java
index d2ec499abe..034b86efba 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java
@@ -510,7 +510,7 @@ public class PushToGerritPage extends WizardPage {
return null;
}
return new ContentProposal(refName);
- }, UIText.PushToGerritPage_ContentProposalStartTypingText,
+ }, null, UIText.PushToGerritPage_ContentProposalStartTypingText,
UIText.PushToGerritPage_ContentProposalHoverText);
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushWizardDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushWizardDialog.java
index d3897d2083..c1712bd7c9 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushWizardDialog.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushWizardDialog.java
@@ -8,9 +8,9 @@
package org.eclipse.egit.ui.internal.push;
import org.eclipse.egit.ui.internal.UIText;
+import org.eclipse.egit.ui.internal.dialogs.NonBlockingWizardDialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.wizard.IWizard;
-import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
@@ -18,7 +18,7 @@ import org.eclipse.swt.widgets.Shell;
/**
* A dialog dedicated to {@link PushBranchWizard}, customizing button labels
*/
-public class PushWizardDialog extends WizardDialog {
+public class PushWizardDialog extends NonBlockingWizardDialog {
/**
* @param parentShell
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/RefSpecDialog.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/RefSpecDialog.java
index 6eafd866d8..403aa1f28e 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/RefSpecDialog.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/RefSpecDialog.java
@@ -159,7 +159,8 @@ public class RefSpecDialog extends TitleAndImageDialog {
});
// content assist for source
UIUtils.addRefContentProposalToText(sourceText, repo,
- () -> assistProvider.getRefsForContentAssist(true, pushMode));
+ () -> assistProvider.getRefsForContentAssist(true, pushMode),
+ !pushMode);
// suggest remote tracking branch
if (!pushMode) {
@@ -198,7 +199,8 @@ public class RefSpecDialog extends TitleAndImageDialog {
});
// content assist for destination
UIUtils.addRefContentProposalToText(destinationText, repo,
- () -> assistProvider.getRefsForContentAssist(false, pushMode));
+ () -> assistProvider.getRefsForContentAssist(false, pushMode),
+ pushMode);
// force update
forceButton = new Button(main, SWT.CHECK);
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 06a20e4012..9d37794af5 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
@@ -56,6 +56,8 @@ AddRemoteWizard_Title=Add Remote
AddSubmoduleWizard_WindowTitle=Add Submodule
AddToIndexAction_addingFiles=Adding Files to Index
AddToIndexCommand_addingFilesFailed=Adding files failed
+AsynchronousRefProposalProvider_FetchingRemoteRefsMessage=Fetching remote refs from {0}
+AsynchronousRefProposalProvider_ShowingProposalsJobName=Showing proposals
RemoveFromIndexAction_removingFiles=Removing file from Index
BlameInformationControl_Author=Author: {0} <{1}> {2}
BlameInformationControl_Commit=Commit {0}
@@ -537,11 +539,13 @@ RefContentProposal_blob=blob
RefContentProposal_branch=branch
RefContentProposal_by=by
RefContentProposal_commit=commit
-RefContentProposal_errorReadingObject=Unable to read object {0} for content proposal assistance
+RefContentProposal_errorReadingObject=Unable to read object {0} for content proposal assistance (ref = {1})
+RefContentProposal_newRemoteObject=New object will be created at the remote repository
RefContentProposal_tag=tag
RefContentProposal_trackingBranch=tracking branch
RefContentProposal_tree=tree
RefContentProposal_unknownObject=locally unknown object
+RefContentProposal_unknownRemoteObject=locally unknown object: upstream is ahead of local repository and hasn't been fetched yet
ReflogView_DateColumnHeader=Date
ReflogView_ErrorOnLoad=Loading the reflog encountered an error; see the Error Log for more information
ReflogView_ErrorOnOpenCommit=Error opening commit
@@ -960,7 +964,6 @@ FetchGerritChangePage_ContentAssistDescription=Patch set {0} of change {1}
FetchGerritChangePage_ContentAssistTooltip=Press {0} to see a filtered list of changes
FetchGerritChangePage_CreatingBranchTaskName=Creating branch
FetchGerritChangePage_CreatingTagTaskName=Creating tag
-FetchGerritChangePage_FetchingRemoteRefsMessage=Fetching remote refs from {0}
FetchGerritChangePage_FetchingTaskName=Fetching change {0}
FetchGerritChangePage_GeneratedTagMessage=Generated for Gerrit change {0}
FetchGerritChangePage_GetChangeTaskName=Get change from Gerrit
@@ -970,7 +973,6 @@ FetchGerritChangePage_MissingChangeMessage=Please provide a change
FetchGerritChangePage_NoSuchChangeMessage=The Gerrit server has no change number {0}
FetchGerritChangePage_PageMessage=Please select a Gerrit URI and change to fetch
FetchGerritChangePage_PageTitle=Fetch a change from Gerrit into repository {0}
-FetchGerritChangePage_ShowingProposalsJobName=Showing proposals
FetchGerritChangePage_SuggestedRefNamePattern=change/{0}/{1}
FetchGerritChangePage_TagNameText=Tag &name:
FetchGerritChangePage_TagRadio=Create and check out a &tag

Back to the top