Add right margin guideline to commit message editor

Add a right margin to the commit message editor, defaulting it to
72 columns and enabled.  Also add a configuration option to
"Settings->Version Control" for toggling the margin and setting its
width.
This commit is contained in:
Daniel Marcotte
2013-02-14 19:04:47 +04:00
committed by Kirill Likhodedov
parent 7bb9fcc008
commit 513eb87edd
7 changed files with 233 additions and 28 deletions
@@ -99,7 +99,20 @@ public class EditorTextFieldProviderImpl implements EditorTextFieldProvider {
}
};
}
@NotNull
@Override
public EditorTextField getEditorField(@NotNull Language language,
@NotNull Project project,
@NotNull final AdHocEditorCustomizer customization) {
return new MyEditorTextField(language, project) {
@Override
protected void applyFeatures(@NotNull EditorCustomization[] customizations, @NotNull EditorEx editor) {
customization.customize(editor);
}
};
}
private abstract static class MyEditorTextField extends LanguageTextField {
MyEditorTextField(Language language, @NotNull Project project) {
@@ -44,4 +44,21 @@ public interface EditorTextFieldProvider {
EditorTextField getEditorField(@NotNull Language language, @NotNull Project project,
@NotNull Iterable<EditorCustomization.Feature> enabledFeatures,
@NotNull Iterable<EditorCustomization.Feature> disabledFeatures);
/**
* Alternative to {@link #getEditorField(com.intellij.lang.Language, com.intellij.openapi.project.Project, Iterable, Iterable)} which
* allows you to define arbitrary customizations by implementing an {@link AdHocEditorCustomizer}.
*
* @param language target language used by document that will be displayed by returned editor
* @param project target project
* @param customization implementation of {@link AdHocEditorCustomizer} which will be called at editor creation
*
*/
@NotNull
EditorTextField getEditorField(@NotNull Language language, @NotNull Project project,
@NotNull AdHocEditorCustomizer customization);
interface AdHocEditorCustomizer {
void customize(EditorEx editor);
}
}
@@ -131,6 +131,8 @@ checkbox.force.non.empty.messages=Force non-&empty checkin comments
tooltip.text.action.applicable.to.vcses=Applicable to: {0}
version.control.main.configurable.name=Version Control
configuration.tab.name.general.settings=General Settings
configuration.commit.message.margin.title=Commit message right margin
configuration.commit.message.margin.prompt=Commit message right margin (columns):
button.configure=&Configure VCS...
action.name.rollback=Rollback
command.name.rollback.change=Rollback Change
@@ -91,6 +91,8 @@ public final class VcsConfiguration implements PersistentStateComponent<Element>
public boolean LIMIT_HISTORY = true;
public int MAXIMUM_HISTORY_ROWS = 1000;
public String UPDATE_FILTER_SCOPE_NAME;
public boolean USE_COMMIT_MESSAGE_MARGIN = true;
public int COMMIT_MESSAGE_MARGIN_SIZE = 72;
public enum StandardOption {
ADD(VcsBundle.message("vcs.command.name.add")),
@@ -0,0 +1,124 @@
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.openapi.vcs.configurable;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsConfiguration;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class VcsCommitMessageMarginConfigurable implements Configurable {
private JCheckBox myEnableMarginCheckbox;
private JTextField myRightMarginTextField;
private final VcsConfiguration myConfiguration;
public VcsCommitMessageMarginConfigurable(Project project) {
myConfiguration = VcsConfiguration.getInstance(project);
}
@Nls
@Override
public String getDisplayName() {
return VcsBundle.message("configuration.commit.message.margin.title");
}
@Nullable
@Override
public String getHelpTopic() {
return null;
}
@Nullable
@Override
public JComponent createComponent() {
JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
myEnableMarginCheckbox = new JCheckBox(VcsBundle.message("configuration.commit.message.margin.prompt"), myConfiguration.USE_COMMIT_MESSAGE_MARGIN);
myRightMarginTextField = new JTextField(Integer.toString(myConfiguration.COMMIT_MESSAGE_MARGIN_SIZE), 5);
myRightMarginTextField.setEnabled(myEnableMarginCheckbox.isSelected());
wrapper.add(myEnableMarginCheckbox);
wrapper.add(myRightMarginTextField);
myEnableMarginCheckbox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myRightMarginTextField.setEnabled(myEnableMarginCheckbox.isSelected());
}
});
return wrapper;
}
@Override
public boolean isModified() {
if (myEnableMarginCheckbox.isSelected() != myConfiguration.USE_COMMIT_MESSAGE_MARGIN) {
return true;
}
if (getValidRightMargin() == null || getValidRightMargin() != myConfiguration.COMMIT_MESSAGE_MARGIN_SIZE) {
return true;
}
return false;
}
/**
* If possible, returns a valid user-input right margin (i.e. an int greater than zero).
* Otherwise, returns null.
*/
private Integer getValidRightMargin() {
if (myRightMarginTextField == null) {
return null;
}
try {
Integer rightMargin = Integer.parseInt(myRightMarginTextField.getText());
return rightMargin == null || rightMargin < 0 ? null : rightMargin;
} catch (NumberFormatException e) {
return null;
}
}
@Override
public void apply() throws ConfigurationException {
myConfiguration.USE_COMMIT_MESSAGE_MARGIN = myEnableMarginCheckbox.isSelected();
Integer rightMargin = getValidRightMargin();
if (rightMargin == null) {
// invalid right margin provided; revert to previous setting
myRightMarginTextField.setText(Integer.toString(myConfiguration.COMMIT_MESSAGE_MARGIN_SIZE));
} else {
myConfiguration.COMMIT_MESSAGE_MARGIN_SIZE = rightMargin;
}
}
@Override
public void reset() {
myEnableMarginCheckbox.setSelected(myConfiguration.USE_COMMIT_MESSAGE_MARGIN);
myRightMarginTextField.setText(Integer.toString(myConfiguration.COMMIT_MESSAGE_MARGIN_SIZE));
}
@Override
public void disposeUIResources() {
}
}
@@ -79,6 +79,7 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
private JCheckBox myShowChangedRecursively;
private final VcsLimitHistoryConfigurable myLimitHistory;
private final VcsUpdateInfoScopeFilterConfigurable myScopeFilterConfig;
private VcsCommitMessageMarginConfigurable myCommitMessageMarginConfigurable;
private class MyDirectoryRenderer extends ColoredTableCellRenderer {
private final Project myProject;
@@ -285,6 +286,7 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
myScopeFilterConfig.reset();
myBaseRevisionTexts.setSelected(myVcsConfiguration.INCLUDE_TEXT_INTO_SHELF);
myShowChangedRecursively.setSelected(myVcsConfiguration.SHOW_DIRTY_RECURSIVELY);
myCommitMessageMarginConfigurable.reset();
}
public static DefaultComboBoxModel buildVcsWrappersModel(final Project project) {
@@ -367,6 +369,7 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
panel.add(createShowChangedOption(), gb.nextLine().next());
panel.add(createShowVcsRootErrorNotificationOption(), gb.nextLine().next());
panel.add(myScopeFilterConfig.createComponent(), gb.nextLine().next());
panel.add(createUseCommitMessageRightMargin(), gb.nextLine().next().fillCellHorizontally());
return panel;
}
@@ -486,6 +489,11 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
return myShowVcsRootErrorNotification;
}
private JComponent createUseCommitMessageRightMargin() {
myCommitMessageMarginConfigurable = new VcsCommitMessageMarginConfigurable(myProject);
return myCommitMessageMarginConfigurable.createComponent();
}
private JComponent createShowRecursivelyDirtyOption() {
myShowChangedRecursively = new JCheckBox("Show directories with changed descendants", myVcsConfiguration.SHOW_DIRTY_RECURSIVELY);
return myShowChangedRecursively;
@@ -503,6 +511,7 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
myVcsConfiguration.INCLUDE_TEXT_INTO_SHELF = myBaseRevisionTexts.isSelected();
myVcsConfiguration.SHOW_VCS_ERROR_NOTIFICATIONS = myShowVcsRootErrorNotification.isSelected();
myVcsConfiguration.SHOW_DIRTY_RECURSIVELY = myShowChangedRecursively.isSelected();
myCommitMessageMarginConfigurable.apply();
initializeModel();
}
@@ -517,6 +526,9 @@ public class VcsDirectoryConfigurationPanel extends JPanel implements Configurab
if (myVcsConfiguration.SHOW_DIRTY_RECURSIVELY != myShowChangedRecursively.isSelected()) {
return true;
}
if (myCommitMessageMarginConfigurable.isModified()) {
return true;
}
return !myModel.getItems().equals(myVcsManager.getDirectoryMappings());
}
@@ -20,6 +20,8 @@ import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorSettings;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileTypes;
@@ -32,8 +34,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.util.EnumSet;
import java.util.Set;
public class CommitMessage extends AbstractDataProviderPanel implements Disposable, CommitMessageI {
@@ -50,12 +50,7 @@ public class CommitMessage extends AbstractDataProviderPanel implements Disposab
public CommitMessage(Project project, final boolean withSeparator) {
super(new BorderLayout());
boolean checkSpelling = true;
VcsConfiguration configuration = VcsConfiguration.getInstance(project);
if (configuration != null) {
checkSpelling = configuration.CHECK_COMMIT_MESSAGE_SPELLING;
}
myEditorField = createEditorField(project, checkSpelling);
myEditorField = createEditorField(project);
myProject = project;
// Note that we assume here that editor used for commit message processing uses font family implied by LAF (in contrast,
@@ -105,24 +100,60 @@ public class CommitMessage extends AbstractDataProviderPanel implements Disposab
setText(currentDescription);
}
private static EditorTextField createEditorField(final Project project, final boolean checkSpelling) {
EditorTextField editorField = createCommitTextEditor(project, checkSpelling);
private static EditorTextField createEditorField(final Project project) {
EditorTextField editorField = createCommitTextEditor(project, false);
editorField.getDocument().putUserData(DATA_CONTEXT_KEY, DataManager.getInstance().getDataContext(editorField.getComponent()));
return editorField;
}
public static EditorTextField createCommitTextEditor(Project project, boolean checkSpelling) {
EditorTextFieldProvider service = ServiceManager.getService(project, EditorTextFieldProvider.class);
Set<EditorCustomization.Feature> enabledFeatures = EnumSet.of(EditorCustomization.Feature.SOFT_WRAP);
Set<EditorCustomization.Feature> disabledFeatures = EnumSet.of(EditorCustomization.Feature.ADDITIONAL_PAGE_AT_BOTTOM);
if (checkSpelling) {
enabledFeatures.add(EditorCustomization.Feature.SPELL_CHECK);
}
else {
disabledFeatures.add(EditorCustomization.Feature.SPELL_CHECK);
/**
* Creates a text editor appropriate for creating commit messages.
*
* @param project project this commit message editor is intended for
* @param forceSpellCheckOn if false, {@link com.intellij.openapi.vcs.VcsConfiguration#CHECK_COMMIT_MESSAGE_SPELLING} will control
* whether or not the editor has spell check enabled
* @return a commit message editor
*/
public static EditorTextField createCommitTextEditor(final Project project, boolean forceSpellCheckOn) {
final boolean checkSpelling;
final boolean useCommitMessageMargin;
final int commitMessageMarginSize;
VcsConfiguration configuration = VcsConfiguration.getInstance(project);
if (configuration != null) {
checkSpelling = forceSpellCheckOn || configuration.CHECK_COMMIT_MESSAGE_SPELLING;
useCommitMessageMargin = configuration.USE_COMMIT_MESSAGE_MARGIN;
commitMessageMarginSize = configuration.COMMIT_MESSAGE_MARGIN_SIZE;
} else {
checkSpelling = true;
useCommitMessageMargin = false;
commitMessageMarginSize = -1;
}
return service.getEditorField(FileTypes.PLAIN_TEXT.getLanguage(), project, enabledFeatures, disabledFeatures);
EditorTextFieldProvider service = ServiceManager.getService(project, EditorTextFieldProvider.class);
return service.getEditorField(FileTypes.PLAIN_TEXT.getLanguage(),
project,
new EditorTextFieldProvider.AdHocEditorCustomizer() {
@Override
public void customize(EditorEx editor) {
editor.getScrollPane().setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
editor.setHorizontalScrollbarVisible(true);
EditorSettings settings = editor.getSettings();
settings.setAdditionalColumnsCount(3);
settings.setVirtualSpace(false);
settings.setUseSoftWraps(true);
settings.setAdditionalPageAtBottom(true);
toggleEditorSpellchecking(project, editor, checkSpelling);
if (useCommitMessageMargin) {
editor.setColorsScheme(EditorColorsManager.getInstance().getGlobalScheme());
settings.setRightMarginShown(true);
settings.setRightMargin(commitMessageMarginSize);
}
}
});
}
@Nullable
@@ -168,20 +199,24 @@ public class CommitMessage extends AbstractDataProviderPanel implements Disposab
return;
}
EditorEx editorEx = (EditorEx)editor;
EditorCustomization[] customizations = Extensions.getExtensions(EditorCustomization.EP_NAME, myProject);
EditorCustomization.Feature feature = EditorCustomization.Feature.SPELL_CHECK;
toggleEditorSpellchecking(myProject, editorEx, check);
}
private static void toggleEditorSpellchecking(Project project, EditorEx editorEx, boolean spellCheckingEnabled) {
EditorCustomization[] customizations = Extensions.getExtensions(EditorCustomization.EP_NAME, project);
EditorCustomization.Feature spellCheckFeature = EditorCustomization.Feature.SPELL_CHECK;
for (EditorCustomization customization : customizations) {
if (customization.getSupportedFeatures().contains(feature)) {
if (check) {
customization.addCustomization(editorEx, feature);
if (customization.getSupportedFeatures().contains(spellCheckFeature)) {
if (spellCheckingEnabled) {
customization.addCustomization(editorEx, spellCheckFeature);
}
else {
customization.removeCustomization(editorEx, feature);
customization.removeCustomization(editorEx, spellCheckFeature);
}
}
}
}
public void dispose() {
}