From cd3a37d142dd2d991306794bef1360b2859c25a1 Mon Sep 17 00:00:00 2001 From: Thomas Wolf Date: Sat, 3 Feb 2018 20:33:22 +0100 Subject: [PATCH] Generalize UIUtils.addContentProposalToText a bit more Move the ExplicitContentProposalAdapter from FetchGerritChangePage to UIUtils, use it, and make addContentProposalToText() return it. Also add a function parameter to convert the input string into a filtering pattern. Use this generalized addContentProposalToText method in FetchGerritChangePage. Change-Id: I5da95666f0c08de68426c51b18c1e74e6e04f4d0 Signed-off-by: Thomas Wolf --- .../src/org/eclipse/egit/ui/UIUtils.java | 107 +++++++++++++++++---- .../ui/internal/fetch/FetchGerritChangePage.java | 93 +++++------------- .../internal/gerrit/GerritConfigurationPage.java | 3 +- .../egit/ui/internal/push/PushToGerritPage.java | 2 +- 4 files changed, 116 insertions(+), 89 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 e38f1a154..75b42c262 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 public + * {@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,13 @@ public class UIUtils { * the repository * @param refListProvider * provides the {@link Ref}s to show in the proposal + * @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 refListProvider) { - UIUtils. addContentProposalToText(textField, + return UIUtils. addContentProposalToText(textField, refListProvider, (pattern, ref) -> { String shortenedName = Repository .shortenRefName(ref.getName()); @@ -479,7 +535,8 @@ public class UIUtils { return null; } return new RefContentProposal(repository, ref); - }, UIText.UIUtils_StartTypingForRemoteRefMessage, + }, null, + UIText.UIUtils_StartTypingForRemoteRefMessage, UIText.UIUtils_PressShortcutForRemoteRefMessage); } @@ -497,20 +554,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 void addContentProposalToText(Text textField, + public static final ExplicitContentProposalAdapter addContentProposalToText( + Text textField, IContentProposalCandidateProvider candidateProvider, - IContentProposalFactory factory, String startTypingMessage, + IContentProposalFactory factory, + Function 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 +590,19 @@ public class UIUtils { public IContentProposal[] getProposals(String contents, int position) { List resultList = new ArrayList<>(); - Pattern pattern = createProposalPattern(contents); Collection 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 +610,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/fetch/FetchGerritChangePage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/fetch/FetchGerritChangePage.java index ba07b1c3d..0754420a7 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 @@ -50,6 +50,7 @@ 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; @@ -60,17 +61,13 @@ import org.eclipse.egit.ui.internal.dialogs.BranchEditDialog; import org.eclipse.egit.ui.internal.dialogs.CancelableFuture; 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; @@ -109,7 +106,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; @@ -1135,72 +1131,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 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 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 { 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 985b61dc2..c5dce0991 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/push/PushToGerritPage.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/push/PushToGerritPage.java index d2ec499ab..034b86efb 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); } } -- 2.11.4.GIT