Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 40403b4dfb8a7e4f0aee7aab4cf6e02349360973 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*******************************************************************************
 * Copyright (c) 2000, 2010 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Eugene Kuleshov <eu@md.pp.ru> - Bug 173959 add mechanism for navigating from team annotation to corresponding task
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.ui.operations;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.DefaultInformationControl.IInformationPresenter;
import org.eclipse.jface.text.revisions.Revision;
import org.eclipse.jface.text.revisions.RevisionInformation;
import org.eclipse.jface.text.source.LineRange;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.AnnotateListener;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.util.KnownRepositories;
import org.eclipse.team.internal.ccvs.ui.*;
import org.eclipse.team.internal.ccvs.ui.Policy;
import org.eclipse.team.internal.core.TeamPlugin;
import org.eclipse.team.ui.TeamUI;
import org.eclipse.team.ui.history.*;
import org.eclipse.ui.*;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditor;

import com.ibm.icu.text.DateFormat;

/**
 * An operation to fetch the annotations for a file from the repository and
 * display them in the annotations view.
 */
public class ShowAnnotationOperation extends CVSOperation {
    
    private final ICVSResource fCVSResource;
    private final String fRevision;
    private final boolean fBinary;

    public ShowAnnotationOperation(IWorkbenchPart part, ICVSResource cvsResource, String revision, boolean binary) {
        super(part);
        fCVSResource= cvsResource;
        fRevision= revision;
        fBinary = binary;
    }

    @Override
	protected void execute(IProgressMonitor monitor) throws CVSException, InterruptedException {
    	
		monitor.beginTask(null, 100);

		// Get the annotations from the repository.
		final AnnotateListener listener= new AnnotateListener();
		fetchAnnotation(listener, fCVSResource, fRevision, Policy.subMonitorFor(monitor, 80));

		// this is not needed if there is no live annotate
		final RevisionInformation information = createRevisionInformation(listener, Policy.subMonitorFor(monitor, 20));

		// Open the view and display it from the UI thread.
		final Display display= getPart().getSite().getShell().getDisplay();
		display.asyncExec(() -> {
			try {
				// is there an open editor for the given input? If yes, use live annotate
				final AbstractDecoratedTextEditor editor = getEditor(listener);
				if (editor != null) {
					editor.showRevisionInformation(information, "org.eclipse.quickdiff.providers.CVSReferenceProvider"); //$NON-NLS-1$
					final IWorkbenchPage page = getPart().getSite().getPage();
					showHistoryView(page, editor);
					page.activate(editor);
				}
			} catch (PartInitException e) {
				CVSException.wrapException(e);
			}
		});
		
		monitor.done();
	}
    
    /**
     * Shows the history view, creating it if necessary, but does not give it focus.
     * 
     * @param page the workbench page to operate in
     * @param editor the editor that is showing the file
     * @return the history view
     * @throws PartInitException
     */
    private IHistoryView showHistoryView(IWorkbenchPage page, AbstractDecoratedTextEditor editor) throws PartInitException {
    	Object object = fCVSResource.getIResource();
    	if (object == null)
    		object = editor.getEditorInput();
		IHistoryView historyView= TeamUI.showHistoryFor(page, object, null);
    	IHistoryPage historyPage = historyView.getHistoryPage();
    	if (historyPage instanceof CVSHistoryPage){
    		CVSHistoryPage cvsHistoryPage = (CVSHistoryPage) historyPage;
    		cvsHistoryPage.setMode(CVSHistoryPage.REMOTE_MODE);
    		// We need to call link to ensure that the history page gets linked
			// even if the page input did not change
    		cvsHistoryPage.linkWithEditor();
    	}
    	return historyView;
    }

    @Override
	protected String getTaskName() {
        return CVSUIMessages.ShowAnnotationOperation_taskName;
    }

	protected boolean hasCharset(ICVSResource cvsResource, InputStream contents) {
		try {
			return TeamPlugin.getCharset(cvsResource.getName(), contents) != null;
		} catch (IOException e) {
			// Assume that the contents do have a charset
			return true;
		}
	}

	private AbstractDecoratedTextEditor getEditor(AnnotateListener listener) throws PartInitException {
		IResource resource= fCVSResource.getIResource();
		if (resource instanceof IFile){
			return RevisionAnnotationController.openEditor(getPart().getSite().getPage(), (IFile)resource);
		}
		if (fCVSResource instanceof ICVSRemoteResource) {
			return RevisionAnnotationController.openEditor(getPart().getSite().getPage(), fCVSResource, new RemoteAnnotationStorage((ICVSRemoteFile)fCVSResource, listener.getContents()));
		}
        return null;
	}

	private void fetchAnnotation(AnnotateListener listener, ICVSResource cvsResource, String revision, IProgressMonitor monitor) throws CVSException {
    
        monitor = Policy.monitorFor(monitor);
        monitor.beginTask(null, 100);
        
        final ICVSFolder folder = cvsResource.getParent();
        final FolderSyncInfo info = folder.getFolderSyncInfo();
        final ICVSRepositoryLocation location = KnownRepositories.getInstance().getRepository(info.getRoot());
        
        final Session session = new Session(location, folder, true /*output to console*/);
        session.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
        try {
            final Command.QuietOption quietness = CVSProviderPlugin.getPlugin().getQuietness();
            try {
                CVSProviderPlugin.getPlugin().setQuietness(Command.VERBOSE);
				List<Object> localOptions = new ArrayList<>();
                if (revision != null) {
                    localOptions.add(Annotate.makeRevisionOption(revision));
                }
                if (fBinary) {
                    localOptions.add(Annotate.FORCE_BINARY_ANNOTATE);
                }
                final IStatus status = Command.ANNOTATE.execute(session, Command.NO_GLOBAL_OPTIONS, localOptions.toArray(new LocalOption[localOptions.size()]), new ICVSResource[]{cvsResource}, listener, Policy.subMonitorFor(monitor, 90));
                if (status.getCode() == CVSStatus.SERVER_ERROR) {
                    throw new CVSServerException(status);
                }
            } finally {
                CVSProviderPlugin.getPlugin().setQuietness(quietness);
                monitor.done();
            }
        } finally {
            session.close();
        }
    }

    private RevisionInformation createRevisionInformation(final AnnotateListener listener, IProgressMonitor monitor) throws CVSException {
		Map<String, ILogEntry> logEntriesByRevision = new HashMap<>();
		if (fCVSResource instanceof ICVSFile) {
			try {
				ILogEntry[] logEntries= ((ICVSFile) fCVSResource).getLogEntries(monitor);
				for (int i= 0; i < logEntries.length; i++) {
					ILogEntry entry= logEntries[i];
					logEntriesByRevision.put(entry.getRevision(), entry);
				}
			} catch (CVSException e) {
				throw e;
			} catch (TeamException e) {
				// XXX why does getLogEntries throw TeamException?
				throw CVSException.wrapException(e);
			}
		}

		final CommitterColors colors= CommitterColors.getDefault();
		RevisionInformation info= new RevisionInformation();

		class AnnotationControlCreator implements IInformationControlCreator {
			private final boolean isResizable;

			public AnnotationControlCreator(boolean isResizable) {
				this.isResizable= isResizable;
			}

			@Override
			public IInformationControl createInformationControl(Shell parent) {
				IInformationPresenter presenter = (display, hoverInfo, presentation, maxWidth, maxHeight) -> {

					// decorate header
					StyleRange styleRange = new StyleRange();
					styleRange.start = 0;
					styleRange.length = hoverInfo.indexOf('\n');
					styleRange.fontStyle = SWT.BOLD;
					presentation.addStyleRange(styleRange);

					return hoverInfo;
				};
				if (isResizable)
					return new DefaultInformationControl(parent, (ToolBarManager) null, presenter);
				else
					return new DefaultInformationControl(parent, EditorsUI.getTooltipAffordanceString(), presenter);
			}
		}

		info.setHoverControlCreator(new AnnotationControlCreator(false));
		info.setInformationPresenterControlCreator(new AnnotationControlCreator(true));
		
		HashMap<String, Revision> sets = new HashMap<>();
		List annotateBlocks= listener.getCvsAnnotateBlocks();
		for (Iterator blocks= annotateBlocks.iterator(); blocks.hasNext();) {
			final CVSAnnotateBlock block= (CVSAnnotateBlock) blocks.next();
			final String revisionString= block.getRevision();
			Revision revision= sets.get(revisionString);
			if (revision == null) {
				final ILogEntry entry= logEntriesByRevision.get(revisionString);
				if (entry == null)
					continue;
				
				revision= new Revision() {
					private String fCommitter= null;
					
					@Override
					public Object getHoverInfo() {
						return entry.getAuthor()
								+ " " + entry.getRevision() + " " + DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT).format(entry.getDate()) //$NON-NLS-1$ //$NON-NLS-2$
								+ "\n\n" + entry.getComment(); //$NON-NLS-1$
					}
					
					private String getCommitterId() {
						if (fCommitter == null)
							fCommitter= block.toString().substring(0, block.toString().indexOf(' '));
						return fCommitter;
					}
					
					@Override
					public String getId() {
						return revisionString;
					}
					
					@Override
					public Date getDate() {
						return entry.getDate();
					}
					
					@Override
					public RGB getColor() {
						return colors.getCommitterRGB(getCommitterId());
					}

					@Override
					public String getAuthor() {
						return getCommitterId();
					}
				};
				sets.put(revisionString, revision);
				info.addRevision(revision);
			}
			revision.addRange(new LineRange(block.getStartLine(), block.getEndLine() - block.getStartLine() + 1));
		}
		
		return info;
	}
}

Back to the top