This commit is contained in:
Aiden Fuller 2025-03-29 13:59:36 +11:00
parent a0b277c4dd
commit 769af09515
8 changed files with 261 additions and 198 deletions

View file

@ -0,0 +1,78 @@
package org.example;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.ui.Refreshable;
import com.intellij.openapi.util.IconLoader;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.io.File;
/**
* An action that adds a prefix to the commit message based on the current branch name.
* This action is added to the commit dialog toolbar for easy access.
*/
public class AddCommitPrefixAction extends AnAction {
@Override
public void update(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null) {
e.getPresentation().setEnabledAndVisible(false);
return;
}
// Get the settings
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
// Only show the button if the plugin is enabled and prefixing mode is MANUAL
boolean visible = settings.isEnabled() && settings.getPrefixingMode() == CommitPrefixerSettings.PrefixingMode.MANUAL;
e.getPresentation().setEnabledAndVisible(visible);
// Set the icon if one is specified
if (visible) {
e.getPresentation().setIcon(AllIcons.Duplicates.SendToTheRight);
}
}
@Override
public void actionPerformed(@NotNull AnActionEvent e) {
Project project = e.getProject();
if (project == null) {
return;
}
// Get the commit message panel
Refreshable refreshable = Refreshable.PANEL_KEY.getData(e.getDataContext());
if (!(refreshable instanceof CheckinProjectPanel panel)) {
return;
}
// Get the settings
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
if (!settings.isEnabled()) {
return;
}
try {
// Get the current commit message
String commitMessage = panel.getCommitMessage();
// Add the prefix to the commit message
String prefixedMessage = CommitPrefixerUtil.addPrefixToCommitMessage(project, commitMessage);
// If the message was changed, update it
if (!prefixedMessage.equals(commitMessage)) {
panel.setCommitMessage(prefixedMessage);
}
} catch (Exception ex) {
// Log the error but don't show it to the user
// We don't want to interrupt the commit process
}
}
}

View file

@ -2,7 +2,6 @@ package org.example;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.CheckinProjectPanel;
import com.intellij.openapi.vcs.changes.CommitContext;
import com.intellij.openapi.vcs.checkin.CheckinHandler;
@ -13,13 +12,6 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Factory for creating commit handlers that prefix commit messages with information from branch names.
@ -40,27 +32,6 @@ public class CommitPrefixerCheckinHandlerFactory extends CheckinHandlerFactory {
public CommitPrefixerCheckinHandler(@NotNull CheckinProjectPanel panel) {
this.panel = panel;
this.project = panel.getProject();
// Pre-fill the commit message if the PRE_FILL mode is selected
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
if (settings.isEnabled() && settings.getPrefixingMode() == CommitPrefixerSettings.PrefixingMode.PRE_FILL) {
try {
// Get the current branch name
String branchName = getCurrentBranchName();
if (branchName != null && !branchName.isEmpty()) {
// Get the current commit message
String commitMessage = panel.getCommitMessage();
// Apply the prefix to the commit message
String prefixedMessage = prefixCommitMessage(branchName, commitMessage, settings);
// Set the prefixed commit message
panel.setCommitMessage(prefixedMessage);
}
} catch (Exception e) {
LOG.error("Error pre-filling commit message", e);
}
}
}
@Nullable
@ -69,16 +40,14 @@ public class CommitPrefixerCheckinHandlerFactory extends CheckinHandlerFactory {
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
// Create a checkbox to enable/disable the prefixer for this commit
JBCheckBox checkBox = new JBCheckBox("Prefix commit message with branch information");
JBCheckBox checkBox = new JBCheckBox("Enable commit message prefixing");
checkBox.setSelected(settings.isEnabled());
checkBox.addActionListener(e -> settings.setEnabled(checkBox.isSelected()));
return new RefreshableOnComponent() {
@Override
public JComponent getComponent() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(checkBox, BorderLayout.WEST);
return panel;
return checkBox;
}
@Override
@ -102,93 +71,25 @@ public class CommitPrefixerCheckinHandlerFactory extends CheckinHandlerFactory {
public ReturnResult beforeCheckin() {
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
// Skip if the plugin is disabled or if the mode is PRE_FILL
if (!settings.isEnabled() || settings.getPrefixingMode() == CommitPrefixerSettings.PrefixingMode.PRE_FILL) {
return ReturnResult.COMMIT;
}
// Only automatically add the prefix if automatic prefixing is enabled
if (settings.isEnabled() && settings.getPrefixingMode() == CommitPrefixerSettings.PrefixingMode.AUTOMATIC) {
try {
// Get the current commit message
String commitMessage = panel.getCommitMessage();
try {
// Get the current branch name
String branchName = getCurrentBranchName();
if (branchName == null || branchName.isEmpty()) {
LOG.warn("Could not determine current branch name");
return ReturnResult.COMMIT;
// Add the prefix to the commit message
String prefixedMessage = CommitPrefixerUtil.addPrefixToCommitMessage(project, commitMessage);
// If the message was changed, update it
if (!prefixedMessage.equals(commitMessage)) {
panel.setCommitMessage(prefixedMessage);
}
} catch (Exception e) {
LOG.error("Error adding prefix to commit message", e);
}
// Get the current commit message
String commitMessage = panel.getCommitMessage();
// Apply the prefix to the commit message
String prefixedMessage = prefixCommitMessage(branchName, commitMessage, settings);
// Set the prefixed commit message
panel.setCommitMessage(prefixedMessage);
} catch (Exception e) {
LOG.error("Error prefixing commit message", e);
}
return ReturnResult.COMMIT;
}
/**
* Gets the current branch name using Git command line.
*/
@Nullable
private String getCurrentBranchName() {
try {
// Get the project base directory
String basePath = project.getBasePath();
if (basePath == null) {
return null;
}
// Run 'git branch --show-current' command
ProcessBuilder processBuilder = new ProcessBuilder("git", "branch", "--show-current");
processBuilder.directory(new File(basePath));
Process process = processBuilder.start();
// Read the output
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String branch = reader.readLine();
process.waitFor();
return branch;
}
} catch (Exception e) {
LOG.error("Error getting current branch name", e);
return null;
}
}
/**
* Prefixes the commit message with information extracted from the branch name.
*/
private String prefixCommitMessage(String branchName, String commitMessage, CommitPrefixerSettings settings) {
try {
// Compile the regex pattern
Pattern pattern = Pattern.compile(settings.getBranchExtractionPattern());
Matcher matcher = pattern.matcher(branchName);
// If the pattern matches, extract the information and format the commit message
if (matcher.matches()) {
String format = settings.getCommitMessageFormat();
// Replace $1, $2, etc. with the corresponding capture groups
for (int i = 1; i <= matcher.groupCount(); i++) {
format = format.replace("$" + i, matcher.group(i));
}
// Replace $MESSAGE with the original commit message
format = format.replace("$MESSAGE", commitMessage);
return format;
}
} catch (PatternSyntaxException e) {
LOG.error("Invalid regex pattern: " + settings.getBranchExtractionPattern(), e);
}
// If the pattern doesn't match or there's an error, return the original commit message
return commitMessage;
}
}
}
}

View file

@ -3,9 +3,11 @@ package org.example;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.Project;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.util.ui.FormBuilder;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;
@ -17,11 +19,12 @@ import java.awt.*;
* Provides a settings UI for the Commit Prefixer plugin.
*/
public class CommitPrefixerConfigurable implements Configurable {
private final Project project;
private JBTextField branchExtractionPatternField;
private JBTextField commitMessageFormatField;
private JBCheckBox enabledCheckBox;
private JComboBox<CommitPrefixerSettings.PrefixingMode> prefixingModeComboBox;
private ComboBox<CommitPrefixerSettings.PrefixingMode> prefixingModeComboBox;
public CommitPrefixerConfigurable(Project project) {
this.project = project;
@ -39,9 +42,11 @@ public class CommitPrefixerConfigurable implements Configurable {
branchExtractionPatternField = new JBTextField();
commitMessageFormatField = new JBTextField();
enabledCheckBox = new JBCheckBox("Enable commit message prefixing");
// Create a combo box for the prefixing mode
prefixingModeComboBox = new JComboBox<>(CommitPrefixerSettings.PrefixingMode.values());
// Create a dropdown for prefixing mode
prefixingModeComboBox = new ComboBox<>(CommitPrefixerSettings.PrefixingMode.values());
// Set a custom renderer to display the enum's display name instead of its toString() value
prefixingModeComboBox.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
@ -57,24 +62,24 @@ public class CommitPrefixerConfigurable implements Configurable {
JBLabel branchPatternHelp = new JBLabel("<html>Pattern to extract information from branch names.<br>" +
"Example: (bug|feature)/(\\d+)-(.+)<br>" +
"This matches 'bug/12345-description' or 'feature/12345-description'</html>");
branchPatternHelp.setForeground(Color.GRAY);
branchPatternHelp.setForeground(JBColor.GRAY);
JBLabel formatHelp = new JBLabel("<html>Format for the commit message prefix.<br>" +
"Use $1, $2, etc. to reference capture groups from the regex pattern.<br>" +
"Use $MESSAGE to reference the original commit message.<br>" +
"Example: #$2 - $MESSAGE</html>");
formatHelp.setForeground(Color.GRAY);
formatHelp.setForeground(JBColor.GRAY);
JBLabel prefixingModeHelp = new JBLabel("<html>When to add the prefix to the commit message:<br>" +
"<b>Before commit</b>: Add the prefix right before the commit is made<br>" +
"<b>Pre-fill in commit dialog</b>: Add the prefix when the commit dialog opens</html>");
prefixingModeHelp.setForeground(Color.GRAY);
JBLabel prefixingHelp = new JBLabel("<html>Select how the prefix should be added to commit messages:<br>" +
"<b>Pre-fill in commit dialog</b>: You need to click the 'Add Prefix' button in the commit dialog.<br>" +
"<b>Before commit</b>: The prefix will be added automatically before commit.</html>");
prefixingHelp.setForeground(JBColor.GRAY);
// Build the form
FormBuilder builder = FormBuilder.createFormBuilder()
.addComponent(enabledCheckBox)
.addLabeledComponent("Prefixing Mode:", prefixingModeComboBox)
.addComponent(prefixingModeHelp)
.addComponent(prefixingHelp)
.addLabeledComponent("Branch Extraction Pattern:", branchExtractionPatternField)
.addComponent(branchPatternHelp)
.addLabeledComponent("Commit Message Format:", commitMessageFormatField)
@ -86,6 +91,7 @@ public class CommitPrefixerConfigurable implements Configurable {
@Override
public boolean isModified() {
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
return !branchExtractionPatternField.getText().equals(settings.getBranchExtractionPattern()) ||
!commitMessageFormatField.getText().equals(settings.getCommitMessageFormat()) ||
enabledCheckBox.isSelected() != settings.isEnabled() ||
@ -115,4 +121,4 @@ public class CommitPrefixerConfigurable implements Configurable {
public String getHelpTopic() {
return "Commit Prefixer Settings";
}
}
}

View file

@ -16,12 +16,23 @@ import org.jetbrains.annotations.Nullable;
storages = {@Storage("commitPrefixerSettings.xml")}
)
public class CommitPrefixerSettings implements PersistentStateComponent<CommitPrefixerSettings> {
/**
* Enum defining when the prefix should be added to the commit message.
*/
// Default regex pattern: matches "bug/12345-description" or "feature/12345-description"
private String branchExtractionPattern = "(bug|feature)/(\\d+)-(.+)";
// Default format pattern: uses the second capture group from the regex
private String commitMessageFormat = "#$2 - $MESSAGE";
// Whether the plugin is enabled
private boolean enabled = true;
// The prefixing mode
private PrefixingMode prefixingMode = PrefixingMode.MANUAL;
// Enum for prefixing modes
public enum PrefixingMode {
BEFORE_COMMIT("Before commit"),
PRE_FILL("Pre-fill in commit dialog");
MANUAL("Pre-fill in commit dialog"),
AUTOMATIC("Before commit");
private final String displayName;
@ -34,17 +45,6 @@ public class CommitPrefixerSettings implements PersistentStateComponent<CommitPr
}
}
// Default regex pattern: matches "bug/12345-description" or "feature/12345-description"
private String branchExtractionPattern = "(bug|feature)/(\\d+)-(.+)";
// Default format pattern: uses the second capture group from the regex
private String commitMessageFormat = "#$2 - $MESSAGE";
// Whether the plugin is enabled
private boolean enabled = true;
// When to add the prefix to the commit message
private PrefixingMode prefixingMode = PrefixingMode.BEFORE_COMMIT;
public static CommitPrefixerSettings getInstance(Project project) {
return project.getService(CommitPrefixerSettings.class);
@ -92,4 +92,4 @@ public class CommitPrefixerSettings implements PersistentStateComponent<CommitPr
public void setPrefixingMode(PrefixingMode prefixingMode) {
this.prefixingMode = prefixingMode;
}
}
}

View file

@ -0,0 +1,125 @@
package org.example;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Utility class for commit message prefixing operations.
* This class centralizes the logic for getting the current branch name and prefixing commit messages.
*/
public class CommitPrefixerUtil {
private static final Logger LOG = Logger.getInstance(CommitPrefixerUtil.class);
/**
* Gets the current branch name using Git command line.
*
* @param project The current project
* @return The current branch name, or null if it couldn't be determined
*/
public static String getCurrentBranchName(Project project) {
try {
// Get the project base directory
String basePath = project.getBasePath();
if (basePath == null) {
return null;
}
// Run 'git branch --show-current' command
ProcessBuilder processBuilder = new ProcessBuilder("git", "branch", "--show-current");
processBuilder.directory(new File(basePath));
Process process = processBuilder.start();
// Read the output
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
String branch = reader.readLine();
process.waitFor();
return branch;
}
} catch (Exception e) {
LOG.error("Error getting current branch name", e);
return null;
}
}
/**
* Prefixes the commit message with information extracted from the branch name.
*
* @param branchName The current branch name
* @param commitMessage The original commit message
* @param settings The commit prefixer settings
* @return The prefixed commit message, or the original message if prefixing failed
*/
public static String prefixCommitMessage(String branchName, String commitMessage, CommitPrefixerSettings settings) {
try {
// Compile the regex pattern
Pattern pattern = Pattern.compile(settings.getBranchExtractionPattern());
Matcher matcher = pattern.matcher(branchName);
// If the pattern matches, extract the information and format the commit message
if (matcher.matches()) {
String format = settings.getCommitMessageFormat();
// Replace $1, $2, etc. with the corresponding capture groups
for (int i = 1; i <= matcher.groupCount(); i++) {
format = format.replace("$" + i, matcher.group(i));
}
// Replace $MESSAGE with the original commit message
format = format.replace("$MESSAGE", commitMessage);
return format;
}
} catch (PatternSyntaxException e) {
LOG.error("Invalid regex pattern: " + settings.getBranchExtractionPattern(), e);
}
// If the pattern doesn't match or there's an error, return the original commit message
return commitMessage;
}
/**
* Adds a prefix to the commit message if it doesn't already have one.
*
* @param project The current project
* @param commitMessage The original commit message
* @return The prefixed commit message, or the original message if prefixing failed or wasn't needed
*/
public static String addPrefixToCommitMessage(Project project, String commitMessage) {
CommitPrefixerSettings settings = CommitPrefixerSettings.getInstance(project);
if (!settings.isEnabled()) {
return commitMessage;
}
try {
// Get the current branch name
String branchName = getCurrentBranchName(project);
if (branchName == null || branchName.isEmpty()) {
return commitMessage;
}
// Generate the prefixed message
String prefix = prefixCommitMessage(branchName, "", settings);
if (prefix == null || prefix.isEmpty()) {
return commitMessage;
}
// Check if the prefix already exists in the commit message
if (commitMessage.startsWith(prefix)) {
return commitMessage;
}
// Add the prefix to the commit message
return prefix + commitMessage;
} catch (Exception e) {
LOG.error("Error adding prefix to commit message", e);
return commitMessage;
}
}
}

View file

@ -1,28 +0,0 @@
package org.example;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
/**
* A simple action that displays a "Hello World" message when triggered.
*/
public class HelloWorldAction extends AnAction {
@Override
public void actionPerformed(AnActionEvent e) {
Project project = e.getProject();
Messages.showMessageDialog(
project,
"Hello, World!",
"Hello World Plugin",
Messages.getInformationIcon()
);
}
@Override
public String toString() {
return "";
}
}

View file

@ -1,17 +0,0 @@
package org.example;
//TIP To <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> icon in the gutter.
public class Main {
public static void main(String[] args) {
//TIP Press <shortcut actionId="ShowIntentionActions"/> with your caret at the highlighted text
// to see how IntelliJ IDEA suggests fixing it.
System.out.printf("Hello and welcome!");
for (int i = 1; i <= 5; i++) {
//TIP Press <shortcut actionId="Debug"/> to start debugging your code. We have set one <icon src="AllIcons.Debugger.Db_set_breakpoint"/> breakpoint
// for you, but you can always add more by pressing <shortcut actionId="ToggleLineBreakpoint"/>.
System.out.println("i = " + i);
}
}
}

View file

@ -42,14 +42,12 @@
</extensions>
<actions>
<!-- Add your actions here -->
<action id="org.example.HelloWorldAction"
class="org.example.HelloWorldAction"
text="Hello World"
description="Displays a Hello World message">
<add-to-group group-id="ToolsMenu" anchor="last"/>
<keyboard-shortcut keymap="$default" first-keystroke="ctrl alt H"/>
<!-- Add the commit prefix action to the VCS menu -->
<action id="org.example.AddCommitPrefixAction"
class="org.example.AddCommitPrefixAction"
text="Add Prefix"
description="Add a prefix to the commit message based on the current branch name">
<add-to-group group-id="Vcs.MessageActionGroup" anchor="first"/>
</action>
</actions>
</idea-plugin>