Add ICommitMessageProvider2 for caret positioning in commit messages
[egit/eclipse.git] / org.eclipse.egit.ui / src / org / eclipse / egit / ui / internal / dialogs / CommitMessageBuilder.java
blobd841f8ee6d8f39b6f20cdf9b718b6318620169d1
1 /*******************************************************************************
2 * Copyright (C) 2017, Stefan Rademacher <stefan.rademacher@tk.de>
5 * All rights reserved. This program and the accompanying materials
6 * are made available under the terms of the Eclipse Public License v1.0
7 * which accompanies this distribution, and is available at
8 * http://www.eclipse.org/legal/epl-v10.html
9 *******************************************************************************/
10 package org.eclipse.egit.ui.internal.dialogs;
12 import java.text.MessageFormat;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collection;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Set;
20 import org.eclipse.core.resources.IFile;
21 import org.eclipse.core.resources.IResource;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IConfigurationElement;
24 import org.eclipse.core.runtime.Platform;
25 import org.eclipse.egit.core.internal.util.ProjectUtil;
26 import org.eclipse.egit.core.internal.util.ResourceUtil;
27 import org.eclipse.egit.ui.Activator;
28 import org.eclipse.egit.ui.CommitMessageWithCaretPosition;
29 import org.eclipse.egit.ui.ICommitMessageProvider;
30 import org.eclipse.egit.ui.ICommitMessageProvider2;
31 import org.eclipse.egit.ui.internal.UIText;
32 import org.eclipse.jgit.annotations.NonNull;
33 import org.eclipse.jgit.lib.Repository;
35 class CommitMessageBuilder {
37 /**
38 * Constant for the extension point for the commit message provider.
40 private static final String COMMIT_MESSAGE_PROVIDER_ID = "org.eclipse.egit.ui.commitMessageProvider"; //$NON-NLS-1$
42 private static final String MESSAGE_SEPARATOR = "\n\n"; //$NON-NLS-1$
44 private final IResource[] resourcesArray;
46 @NonNull
47 private final Repository repository;
49 private boolean isMessageEmpty;
51 /**
52 * Creates a CommitMessageBuilder for the specified repository and the
53 * files, that are about to be committed.
55 * @param repository
56 * the repository, messages are built for
57 * @param paths
58 * list of file paths, selected for the next commit
60 CommitMessageBuilder(@NonNull Repository repository,
61 Collection<String> paths) {
62 this.repository = repository;
63 this.resourcesArray = toResourceArray(paths);
66 /**
67 * Returns an object, containing the commit message and the caret position
68 * within that message.
70 * @return a commit message with caret position. The caret position is 0, if
71 * there was no {@link ICommitMessageProvider2} providing a caret
72 * position.
74 CommitMessageWithCaretPosition build() {
75 StringBuilder finalMessage = new StringBuilder();
76 int caretPosition = CommitMessageWithCaretPosition.NO_POSITION;
77 isMessageEmpty = true;
79 for (ICommitMessageProvider provider : getCommitMessageProviders()) {
80 String message = ""; //$NON-NLS-1$
81 try {
82 if (provider instanceof ICommitMessageProvider2) {
83 CommitMessageWithCaretPosition commitMessageWithPosition = ((ICommitMessageProvider2) provider)
84 .getCommitMessageWithPosition(resourcesArray);
85 if (commitMessageWithPosition != null) {
86 caretPosition = updateCaretPosition(finalMessage,
87 caretPosition, commitMessageWithPosition,
88 (ICommitMessageProvider2) provider);
90 message = getCommitMessage(commitMessageWithPosition);
91 } else {
92 message = append(
93 provider.getMessage(resourcesArray));
95 } catch (RuntimeException e) {
96 Activator.logError(e.getMessage(), e);
98 finalMessage.append(message);
99 isMessageEmpty = finalMessage.length() == 0;
101 return new CommitMessageWithCaretPosition(finalMessage.toString(),
102 Math.max(0, caretPosition));
105 private String getCommitMessage(
106 CommitMessageWithCaretPosition messageWithPosition) {
107 if (messageWithPosition == null) {
108 return ""; //$NON-NLS-1$
109 } else {
110 return append(messageWithPosition.getMessage());
114 private String append(String msg) {
115 StringBuilder returnMsg = new StringBuilder();
116 if (msg != null && !msg.trim().isEmpty()) {
117 if (!isMessageEmpty) {
118 returnMsg.append(MESSAGE_SEPARATOR);
120 returnMsg.append(msg);
122 return returnMsg.toString();
125 @SuppressWarnings("boxing")
126 private int updateCaretPosition(StringBuilder currentMessage,
127 int currentCaretPosition,
128 CommitMessageWithCaretPosition messageWithPosition,
129 ICommitMessageProvider2 provider) {
130 int pos = currentCaretPosition;
131 if (currentCaretPosition == CommitMessageWithCaretPosition.NO_POSITION) {
132 String providedMessage = messageWithPosition.getMessage();
133 if (providedMessage == null || providedMessage.trim().isEmpty()) {
134 return pos;
136 int providedCaretPosition = messageWithPosition
137 .getDesiredCaretPosition();
138 if (providedCaretPosition == CommitMessageWithCaretPosition.NO_POSITION) {
139 return pos;
141 if (providedCaretPosition > providedMessage.length()
142 || providedCaretPosition < 0) {
143 Activator.logWarning(
144 MessageFormat.format(
145 UIText.CommitDialog_CaretPositionOutOfBounds,
146 provider.getClass().getName(),
147 providedCaretPosition),
148 null);
149 return CommitMessageWithCaretPosition.NO_POSITION;
151 } else {
152 pos = currentMessage.length();
153 if (currentMessage.length() > 0) {
154 pos += MESSAGE_SEPARATOR.length();
156 pos += providedCaretPosition;
158 } else {
159 Activator
160 .logWarning(
161 MessageFormat.format(
162 UIText.CommitDialog_IgnoreCaretPosition,
163 provider.getClass().getName()),
164 null);
167 return pos;
170 List<ICommitMessageProvider> getCommitMessageProviders() {
171 List<ICommitMessageProvider> providers = new ArrayList<>();
173 IConfigurationElement[] configs = Platform.getExtensionRegistry()
174 .getConfigurationElementsFor(COMMIT_MESSAGE_PROVIDER_ID);
175 for (IConfigurationElement config : configs) {
176 Object provider;
177 String contributorName = "<unknown>"; //$NON-NLS-1$
178 String extensionId = "<unknown>"; //$NON-NLS-1$
179 try {
180 extensionId = config.getDeclaringExtension()
181 .getUniqueIdentifier();
182 contributorName = config.getContributor().getName();
183 provider = config.createExecutableExtension("class");//$NON-NLS-1$
184 if (provider instanceof ICommitMessageProvider) {
185 providers.add((ICommitMessageProvider) provider);
186 } else {
187 Activator.logError(
188 MessageFormat.format(
189 UIText.CommitDialog_WrongTypeOfCommitMessageProvider,
190 extensionId, contributorName),
191 null);
193 } catch (CoreException | RuntimeException e) {
194 Activator
195 .logError(
196 MessageFormat.format(
197 UIText.CommitDialog_ErrorCreatingCommitMessageProvider,
198 extensionId, contributorName),
202 return providers;
205 private IResource[] toResourceArray(Collection<String> paths) {
206 Set<IResource> resources = new HashSet<>();
207 for (String path : paths) {
208 IFile file = null;
209 if (path != null) {
210 file = ResourceUtil.getFileForLocation(repository, path,
211 false);
213 if (file != null) {
214 resources.add(file.getProject());
217 if (resources.size() == 0) {
218 resources
219 .addAll(Arrays
220 .asList(ProjectUtil.getProjects(repository)));
222 return resources.toArray(new IResource[0]);