diff --git a/platform/platform-resources/src/idea/VcsActions.xml b/platform/platform-resources/src/idea/VcsActions.xml index 9ca381f12921..738b0c6464b5 100644 --- a/platform/platform-resources/src/idea/VcsActions.xml +++ b/platform/platform-resources/src/idea/VcsActions.xml @@ -239,6 +239,7 @@ use-shortcut-of="Diff.ShowDiff" icon="AllIcons.Actions.Diff"/> + checkRightMargin(file, document, manager, isOnTheFly, line, RIGHT_MARGIN, - format("Body lines should not exceed %d characters", RIGHT_MARGIN), new WrapLineQuickFix())) + format("Body lines should not exceed %d characters", RIGHT_MARGIN), new WrapLineQuickFix(), + new ReformatCommitMessageQuickFix())) .filter(Objects::nonNull) .toArray(ProblemDescriptor[]::new); } + @Override + public boolean canReformat(@NotNull Project project, @NotNull Document document) { + return hasProblems(project, document); + } + + @Override + public void reformat(@NotNull Project project, @NotNull Document document) { + new WrapLineQuickFix().doApplyFix(project, document, null); + } + protected class WrapLineQuickFix extends BaseCommitMessageQuickFix { protected WrapLineQuickFix() { super("Wrap line"); } @Override - public void doApplyFix(@NotNull Project project, @NotNull Document document, @NotNull ProblemDescriptor descriptor) { + public void doApplyFix(@NotNull Project project, @NotNull Document document, @Nullable ProblemDescriptor descriptor) { Editor editor = CommitMessage.getEditor(document); if (editor != null) { - TextRange range = descriptor.getLineNumber() >= 0 ? getLineTextRange(document, descriptor.getLineNumber()) : EMPTY_RANGE; + TextRange range = descriptor != null && descriptor.getLineNumber() >= 0 + ? getLineTextRange(document, descriptor.getLineNumber()) + : getBodyRange(document); if (!range.isEmpty()) { wrapLines(project, editor, document, RIGHT_MARGIN, range); @@ -88,6 +101,11 @@ public class BodyLimitInspection extends BaseCommitMessageInspection { } } + @NotNull + private TextRange getBodyRange(@NotNull Document document) { + return document.getLineCount() > 1 ? TextRange.create(document.getLineStartOffset(1), document.getTextLength()) : EMPTY_RANGE; + } + private void wrapLines(@NotNull Project project, @NotNull Editor editor, @NotNull Document document, diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/ReformatCommitMessageAction.java b/platform/vcs-impl/src/com/intellij/vcs/commit/ReformatCommitMessageAction.java new file mode 100644 index 000000000000..59dd15463666 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/ReformatCommitMessageAction.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2017 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.vcs.commit; + +import com.intellij.codeInspection.ex.InspectionToolWrapper; +import com.intellij.codeInspection.ex.Tools; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsDataKeys; +import com.intellij.openapi.vcs.ui.CommitMessage; +import org.jetbrains.annotations.CalledWithWriteLock; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.stream.Stream; + +import static com.intellij.util.ObjectUtils.notNull; +import static com.intellij.util.ObjectUtils.tryCast; +import static java.util.stream.Collectors.toList; + +public class ReformatCommitMessageAction extends DumbAwareAction { + + public static final String NAME = "Reformat commit message"; + + public ReformatCommitMessageAction() { + setEnabledInModalContext(true); + } + + @Override + public void update(@NotNull AnActionEvent e) { + Project project = e.getProject(); + Document document = getCommitMessage(e); + + e.getPresentation().setEnabled(project != null && + document != null && + getEnabledInspections(project).anyMatch(inspection -> inspection.canReformat(project, document))); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + Project project = notNull(e.getProject()); + Document document = notNull(getCommitMessage(e)); + + CommandProcessor.getInstance().executeCommand(project, () -> + WriteAction.run(() -> reformat(project, document)), NAME, null); + } + + @CalledWithWriteLock + public static void reformat(@NotNull Project project, @NotNull Document document) { + List inspections = getEnabledInspections(project).collect(toList()); + + inspections.forEach(inspection -> inspection.reformat(project, document)); + } + + @Nullable + private static Document getCommitMessage(@NotNull AnActionEvent e) { + CommitMessage commitMessage = tryCast(e.getData(VcsDataKeys.COMMIT_MESSAGE_CONTROL), CommitMessage.class); + Editor editor = commitMessage != null ? commitMessage.getEditorField().getEditor() : null; + + return editor != null ? editor.getDocument() : null; + } + + @NotNull + private static Stream getEnabledInspections(@NotNull Project project) { + return CommitMessageInspectionProfile.getInstance(project).getAllEnabledInspectionTools(project).stream() + .map(Tools::getTool) + .map(InspectionToolWrapper::getTool) + .filter(BaseCommitMessageInspection.class::isInstance) + .map(BaseCommitMessageInspection.class::cast); + } +} diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/SubjectBodySeparationInspection.java b/platform/vcs-impl/src/com/intellij/vcs/commit/SubjectBodySeparationInspection.java index e2ab65a10554..1890ceeb2e4a 100644 --- a/platform/vcs-impl/src/com/intellij/vcs/commit/SubjectBodySeparationInspection.java +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/SubjectBodySeparationInspection.java @@ -44,26 +44,43 @@ public class SubjectBodySeparationInspection extends BaseCommitMessageInspection boolean isOnTheFly) { ProblemDescriptor descriptor = document.getLineCount() > 1 ? checkRightMargin(file, document, manager, isOnTheFly, 1, 0, - "Missing blank line between subject and body", new AddBlankLineQuickFix()) + "Missing blank line between subject and body", new AddBlankLineQuickFix(), + new ReformatCommitMessageQuickFix()) : null; return descriptor != null ? ar(descriptor) : null; } + @Override + public boolean canReformat(@NotNull Project project, @NotNull Document document) { + return hasProblems(project, document); + } + + @Override + public void reformat(@NotNull Project project, @NotNull Document document) { + new AddBlankLineQuickFix().doApplyFix(project, document, null); + } + protected static class AddBlankLineQuickFix extends BaseCommitMessageQuickFix { protected AddBlankLineQuickFix() { super("Add blank line"); } @Override - public void doApplyFix(@NotNull Project project, @NotNull Document document, @NotNull ProblemDescriptor descriptor) { - if (descriptor.getLineNumber() >= 0) { - TextRange lineRange = getLineTextRange(document, descriptor.getLineNumber()); + public void doApplyFix(@NotNull Project project, @NotNull Document document, @Nullable ProblemDescriptor descriptor) { + int line = descriptor != null && descriptor.getLineNumber() >= 0 ? descriptor.getLineNumber() : getFirstLine(document); + + if (line >= 0) { + TextRange lineRange = getLineTextRange(document, line); if (!lineRange.isEmpty()) { document.insertString(lineRange.getStartOffset(), "\n"); } } } + + private static int getFirstLine(@NotNull Document document) { + return document.getLineCount() > 1 ? 1 : -1; + } } }