/******************************************************************************* * Copyright (C) 2017 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.text.MessageFormat; import org.eclipse.egit.ui.UIUtils; import org.eclipse.egit.ui.internal.UIText; import org.eclipse.jface.bindings.keys.KeyStroke; import org.eclipse.jface.fieldassist.ContentProposal; import org.eclipse.jface.fieldassist.ContentProposalAdapter; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.TextContentAdapter; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Text; import org.eclipse.ui.IWorkbenchCommandConstants; /** * A component for text fields that are intended for branch name input. When a * blank is typed, it is automatically replaced by an underscore. Adds content * assist that offers a normalized branch name as replacement if the current * text does not form a valid ref. Characters that are definitely invalid * trigger automatic content assist. */ public class BranchNameNormalizer { private static final String UNDERSCORE = "_"; //$NON-NLS-1$ private static final String REGEX_BLANK = "\\h|\\v"; //$NON-NLS-1$ private static final char[] BRANCH_NAME_NORMALIZER_ACTIVATION_CHARS = "\\~^:?[*@<>|\"" //$NON-NLS-1$ .toCharArray(); private final ControlDecoration decorator; private boolean visible; /** * Creates a new {@link BranchNameNormalizer} using * {@link UIText#BranchNameNormalizer_Tooltip} as tooltip text. * * @param text * {@link Text} to operate on */ public BranchNameNormalizer(Text text) { this(text, UIText.BranchNameNormalizer_Tooltip); } /** * Creates a new {@link BranchNameNormalizer}. * * @param text * {@link Text} to operate on * @param tooltipText * to show on the bulb decorator */ public BranchNameNormalizer(Text text, String tooltipText) { KeyStroke stroke = UIUtils.getKeystrokeOfBestActiveBindingFor( IWorkbenchCommandConstants.EDIT_CONTENT_ASSIST); if (stroke == null) { stroke = KeyStroke.getInstance(SWT.MOD1, ' '); } if (tooltipText == null || tooltipText.isEmpty()) { decorator = UIUtils.addBulbDecorator(text, null); } else { decorator = UIUtils.addBulbDecorator(text, MessageFormat.format(tooltipText, stroke.format())); } decorator.hide(); ContentProposalAdapter proposer = new ContentProposalAdapter(text, new TextContentAdapter(), (c, p) -> { if (c.isEmpty() || Repository .isValidRefName(Constants.R_HEADS + c)) { return null; } String normalized = Repository.normalizeBranchName(c); if (normalized == null || normalized.isEmpty()) { return new ContentProposal[0]; } return new ContentProposal[] { new ContentProposal(normalized) }; }, stroke, BRANCH_NAME_NORMALIZER_ACTIVATION_CHARS); proposer.setProposalAcceptanceStyle( ContentProposalAdapter.PROPOSAL_REPLACE); text.addVerifyListener( e -> e.text = e.text.replaceAll(REGEX_BLANK, UNDERSCORE)); text.addModifyListener(e -> { String input = text.getText(); boolean doProposeCorrection = !input.isEmpty() && !Repository.isValidRefName(Constants.R_HEADS + input); setVisible(doProposeCorrection); }); } /** * Determines whether the decorator is visible. * * @return {@code true} if the decorator is visible. */ public boolean isVisible() { return visible; } /** * Changes the visibility of the decorator. * * @param visible * {@code true} to show the decorator; {@code false} otherwise */ public void setVisible(boolean visible) { this.visible = visible; if (visible) { decorator.show(); } else { decorator.hide(); } } }