1 package com.proalpha.pds.gitutils.search;
4 import java.io.IOException;
5 import java.nio.file.Files;
6 import java.util.HashSet;
9 import org.eclipse.core.filesystem.EFS;
10 import org.eclipse.core.filesystem.IFileStore;
11 import org.eclipse.core.runtime.IProgressMonitor;
12 import org.eclipse.core.runtime.IStatus;
13 import org.eclipse.core.runtime.Status;
14 import org.eclipse.jface.text.BadLocationException;
15 import org.eclipse.jface.text.IDocument;
16 import org.eclipse.jface.viewers.CellLabelProvider;
17 import org.eclipse.jface.viewers.DoubleClickEvent;
18 import org.eclipse.jface.viewers.IDoubleClickListener;
19 import org.eclipse.jface.viewers.ISelection;
20 import org.eclipse.jface.viewers.ISelectionChangedListener;
21 import org.eclipse.jface.viewers.IStructuredContentProvider;
22 import org.eclipse.jface.viewers.IStructuredSelection;
23 import org.eclipse.jface.viewers.SelectionChangedEvent;
24 import org.eclipse.jface.viewers.TableViewer;
25 import org.eclipse.jface.viewers.TableViewerColumn;
26 import org.eclipse.jface.viewers.Viewer;
27 import org.eclipse.jface.viewers.ViewerCell;
28 import org.eclipse.jface.viewers.ViewerComparator;
29 import org.eclipse.search.ui.IQueryListener;
30 import org.eclipse.search.ui.ISearchQuery;
31 import org.eclipse.search.ui.ISearchResult;
32 import org.eclipse.search.ui.ISearchResultListener;
33 import org.eclipse.search.ui.ISearchResultPage;
34 import org.eclipse.search.ui.ISearchResultViewPart;
35 import org.eclipse.search.ui.NewSearchUI;
36 import org.eclipse.search.ui.SearchResultEvent;
37 import org.eclipse.search.ui.text.AbstractTextSearchResult;
38 import org.eclipse.search.ui.text.FilterUpdateEvent;
39 import org.eclipse.search.ui.text.Match;
40 import org.eclipse.search.ui.text.MatchEvent;
41 import org.eclipse.search.ui.text.RemoveAllEvent;
42 import org.eclipse.swt.SWT;
43 import org.eclipse.swt.custom.SashForm;
44 import org.eclipse.swt.custom.StyledText;
45 import org.eclipse.swt.graphics.Font;
46 import org.eclipse.swt.graphics.FontData;
47 import org.eclipse.swt.layout.GridData;
48 import org.eclipse.swt.layout.GridLayout;
49 import org.eclipse.swt.widgets.Composite;
50 import org.eclipse.swt.widgets.Control;
51 import org.eclipse.swt.widgets.Display;
52 import org.eclipse.swt.widgets.Table;
53 import org.eclipse.swt.widgets.TableItem;
54 import org.eclipse.ui.IEditorPart;
55 import org.eclipse.ui.IMemento;
56 import org.eclipse.ui.IWorkbenchPage;
57 import org.eclipse.ui.PartInitException;
58 import org.eclipse.ui.PlatformUI;
59 import org.eclipse.ui.ide.IDE;
60 import org.eclipse.ui.part.Page;
61 import org.eclipse.ui.part.PageBook;
62 import org.eclipse.ui.progress.UIJob;
63 import org.eclipse.ui.texteditor.IDocumentProvider;
64 import org.eclipse.ui.texteditor.ITextEditor;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
72 private Control fBusyLabel;
74 private IQueryListener fQueryListener;
75 private ISearchResultListener fListener;
76 private ISearchResultViewPart fViewPart;
77 private PageBook fPagebook;
78 private SashForm sashForm;
79 private Set<Object> fBatchedUpdates;
81 private StyledText textArea;
82 private TableViewer fViewer;
83 private boolean fBatchedClearAll;
84 private boolean fIsBusyShown;
85 private volatile boolean fIsUIUpdateScheduled =
false;
86 private volatile boolean fScheduleEnsureSelection =
false;
89 fBatchedUpdates =
new HashSet<>();
90 fBatchedClearAll =
false;
92 fListener =
new ISearchResultListener() {
93 public void searchResultChanged(SearchResultEvent e) {
94 handleSearchResultChanged(e);
99 public void setID(String
id) {
103 public String getID() {
107 public String getLabel() {
108 AbstractTextSearchResult result = fInput;
109 if (result ==
null) {
112 return result.getLabel();
115 protected void configureTableViewer(TableViewer viewer) {
117 customizeTable(viewer);
118 viewer.setContentProvider(
new GitGrepContentProvider());
119 viewer.setLabelProvider(
new GitGrepLabelProvider());
120 viewer.setComparator(
new ViewerComparator() {
123 public int compare(Viewer viewer, Object e1, Object e2) {
127 return el1.compareTo(el2);
131 public void sort(Viewer viewer, Object[] elements) {
133 super.sort(viewer, elements);
138 public void createControl(Composite parent) {
139 GridLayout gridLayout =
new GridLayout();
140 gridLayout.numColumns = 2;
142 fQueryListener = createQueryListener();
144 fPagebook =
new PageBook(parent, SWT.NULL);
145 fPagebook.setLayoutData(
new GridData(GridData.FILL_BOTH));
146 fBusyLabel = createBusyControl();
148 sashForm =
new SashForm(fPagebook, SWT.HORIZONTAL);
149 sashForm.setLayoutData(
new GridData(GridData.FILL_BOTH));
150 sashForm.setLayout(gridLayout);
152 createViewer(sashForm);
153 createTextArea(sashForm);
155 showBusyLabel(fIsBusyShown);
156 NewSearchUI.addQueryListener(fQueryListener);
159 private void scheduleUIUpdate() {
160 if (!fIsUIUpdateScheduled) {
161 fIsUIUpdateScheduled =
true;
162 new UpdateUIJob().schedule();
166 private IQueryListener createQueryListener() {
167 return new IQueryListener() {
168 public void queryAdded(ISearchQuery query) {
172 public void queryRemoved(ISearchQuery query) {
176 public void queryStarting(
final ISearchQuery query) {
177 final Runnable runnable1 =
new Runnable() {
180 AbstractTextSearchResult result = fInput;
182 if (result ==
null || !result.getQuery().equals(query)) {
188 asyncExec(runnable1);
191 public void queryFinished(
final ISearchQuery query) {
192 postEnsureSelection();
197 protected void postEnsureSelection() {
198 fScheduleEnsureSelection =
true;
202 private void createViewer(Composite parent) {
203 GridData gridData =
new GridData();
204 gridData.horizontalAlignment = GridData.FILL;
205 gridData.grabExcessHorizontalSpace =
true;
206 gridData.grabExcessVerticalSpace =
true;
207 gridData.verticalAlignment = GridData.FILL;
209 TableViewer viewer =
new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
211 fViewer.getTable().setLayoutData(gridData);
212 configureTableViewer(viewer);
214 getSite().setSelectionProvider(fViewer);
216 fViewer.addSelectionChangedListener(
new ISelectionChangedListener() {
217 public void selectionChanged(SelectionChangedEvent event) {
220 fillTextArea(selectedElement);
224 if ((selectedElement.getBlock() ==
null) || (selectedElement.getLineNr() ==
null)) {
228 File fileToOpen =
new File(selectedElement.getAbsolutePath());
230 if (fileToOpen.exists() && fileToOpen.isFile()) {
231 byte[] fileContent =
null;
233 fileContent = Files.readAllBytes(fileToOpen.toPath());
234 }
catch (IOException e) {
235 logger.debug(
"file content could not be read because ", e);
238 textArea.setText(
new String(fileContent));
239 textArea.setTopIndex(Integer.parseInt(selectedElement.getLineNr()) - 1);
240 textArea.setEditable(
false);
243 logger.error(
"The file {} doesn't exist.", fileToOpen);
249 IStructuredSelection selection = (IStructuredSelection) fViewer.getSelection();
251 if (selection.isEmpty()) {
256 return selectedElement;
260 fViewer.addDoubleClickListener(
new IDoubleClickListener() {
262 public void doubleClick(DoubleClickEvent event) {
264 fViewer = (TableViewer) event.getSource();
265 IStructuredSelection el = (IStructuredSelection) fViewer.getSelection();
268 File fileToOpen =
new File(elem.getAbsolutePath());
270 if (fileToOpen.exists() && fileToOpen.isFile()) {
271 IFileStore fileStore = EFS.getLocalFileSystem().getStore(fileToOpen.toURI());
272 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
275 IEditorPart editor = IDE.openEditorOnFileStore(page, fileStore);
277 if (editor instanceof ITextEditor) {
278 IDocumentProvider provider = ((ITextEditor) editor).getDocumentProvider();
279 IDocument document = provider.getDocument(editor.getEditorInput());
283 start = document.getLineOffset(Integer.parseInt(elem.getLineNr()) - 1);
284 }
catch (NumberFormatException | BadLocationException e) {
285 logger.debug(
"line number could not be parsed to int because ", e);
288 ((ITextEditor) editor).selectAndReveal(start, 0);
291 }
catch (PartInitException e) {
293 logger.error(e.getMessage(), e);
296 logger.error(
"The file {} doesn't exist.", fileToOpen);
301 fViewPart.updateLabel();
304 private void createTextArea(Composite parent) {
305 GridData gridData =
new GridData();
306 gridData.grabExcessHorizontalSpace =
true;
307 gridData.horizontalAlignment = GridData.FILL;
308 gridData.verticalAlignment = GridData.FILL;
309 gridData.grabExcessVerticalSpace =
true;
310 textArea =
new StyledText(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
311 textArea.setLayoutData(gridData);
312 FontData defaultFont =
new FontData(
"Consolas", 10, SWT.NORMAL);
313 Font font =
new Font(parent.getDisplay(), defaultFont);
315 textArea.setFont(font);
318 private void customizeTable(TableViewer viewer) {
320 TableViewerColumn columnObject =
new TableViewerColumn(viewer, SWT.NONE);
321 TableViewerColumn columnContent =
new TableViewerColumn(viewer, SWT.NONE);
322 TableViewerColumn columnLine =
new TableViewerColumn(viewer, SWT.NONE);
323 columnObject.getColumn().setText(
"Objectname");
324 columnObject.getColumn().setResizable(
true);
325 columnObject.getColumn().setWidth(110);
326 columnContent.getColumn().setText(
"Line");
327 columnContent.getColumn().setResizable(
true);
328 columnContent.getColumn().setWidth(50);
329 columnLine.getColumn().setText(
"Content");
330 columnLine.getColumn().setResizable(
true);
331 columnLine.getColumn().setWidth(700);
332 viewer.getTable().setHeaderVisible(
true);
333 viewer.getTable().setLinesVisible(
true);
334 FontData defaultFont =
new FontData(
"Consolas", 10, SWT.NORMAL);
335 Font font =
new Font(viewer.getTable().getDisplay(), defaultFont);
336 viewer.getTable().setFont(font);
339 private Control createBusyControl() {
340 Table busyLabel =
new Table(fPagebook, SWT.NONE);
341 TableItem item =
new TableItem(busyLabel, SWT.NONE);
342 item.setText(
"searching....");
343 busyLabel.setLayoutData(
new GridData(GridData.FILL_HORIZONTAL));
347 private void updateBusyLabel() {
348 AbstractTextSearchResult result = fInput;
349 boolean shouldShowBusy = result !=
null && NewSearchUI.isQueryRunning(result.getQuery())
350 && result.getMatchCount() == 0;
351 if (shouldShowBusy == fIsBusyShown) {
354 fIsBusyShown = shouldShowBusy;
355 showBusyLabel(fIsBusyShown);
358 private void showBusyLabel(
boolean shouldShowBusy) {
359 if (shouldShowBusy) {
360 fPagebook.showPage(fBusyLabel);
362 fPagebook.showPage(sashForm);
366 private void disposeViewer() {
367 fViewer.getControl().dispose();
371 public void dispose() {
374 NewSearchUI.removeQueryListener(fQueryListener);
377 public void setFocus() {
378 Control control = fViewer.getControl();
379 if (control !=
null && !control.isDisposed()) {
384 public Control getControl() {
388 public void setInput(ISearchResult newSearch, Object viewState) {
390 textArea.setText(
"");
392 if (oldSearch !=
null) {
393 fViewer.setInput(
null);
394 oldSearch.removeListener(fListener);
398 fInput.addListener(fListener);
399 fViewer.setInput(fInput);
400 if (viewState instanceof ISelection) {
401 fViewer.setSelection((ISelection) viewState,
true);
409 public Object getUIState() {
410 return fViewer.getSelection();
413 public void setViewPart(ISearchResultViewPart part) {
417 protected ISearchResultViewPart getViewPart() {
421 public void restoreState(IMemento memento) {
425 public void saveState(IMemento memento) {
429 protected void handleSearchResultChanged(
final SearchResultEvent e) {
430 if (e instanceof MatchEvent) {
431 postUpdate(((MatchEvent) e).getMatches());
432 }
else if (e instanceof RemoveAllEvent) {
434 }
else if (e instanceof FilterUpdateEvent) {
435 postUpdate(((FilterUpdateEvent) e).getUpdatedMatches());
439 private synchronized void postUpdate(Match[] matches) {
440 for (Match match : matches) {
441 fBatchedUpdates.add(match.getElement());
446 private void postClear() {
447 fBatchedClearAll =
true;
448 fBatchedUpdates.clear();
452 private void asyncExec(
final Runnable runnable) {
453 final Control control = getControl();
454 if (control !=
null && !control.isDisposed()) {
455 Display currentDisplay = Display.getCurrent();
456 if (currentDisplay ==
null || !currentDisplay.equals(control.getDisplay())) {
457 control.getDisplay().asyncExec(
new Runnable() {
459 if (!control.isDisposed()) {
470 private class UpdateUIJob
extends UIJob {
472 public UpdateUIJob() {
473 super(
"Update Search Results View");
477 public IStatus runInUIThread(IProgressMonitor monitor) {
478 Control control = getControl();
479 if (control ==
null || control.isDisposed()) {
480 return Status.OK_STATUS;
484 if (hasMoreUpdates() || isQueryRunning()) {
487 fIsUIUpdateScheduled =
false;
489 if (fScheduleEnsureSelection) {
490 fScheduleEnsureSelection =
false;
493 fViewPart.updateLabel();
494 return Status.OK_STATUS;
497 private boolean hasMoreUpdates() {
498 return fBatchedClearAll || fBatchedUpdates.isEmpty();
501 private void runBatchedClear() {
502 synchronized (
this) {
503 if (!fBatchedClearAll) {
506 fBatchedClearAll =
false;
509 fViewPart.updateLabel();
513 private synchronized void runBatchedUpdates() {
515 fBatchedUpdates.clear();
519 private boolean isQueryRunning() {
520 AbstractTextSearchResult result = fInput;
521 if (result !=
null) {
522 return NewSearchUI.isQueryRunning(result.getQuery());
528 public boolean belongsTo(Object family) {
534 class GitGrepLabelProvider
extends CellLabelProvider {
535 public String getColumnText(Object element,
int columnIndex) {
536 switch (columnIndex) {
548 public void update(ViewerCell cell) {
549 cell.setText(getColumnText((GitGrepSearchResultElement) cell.getElement(), cell.getColumnIndex()));
553 class GitGrepContentProvider
implements IStructuredContentProvider {
555 private TableViewer paGitGrepTableViewer;
556 private GitGrepSearchResult paGitGrepSearchResult;
558 public Object[] getElements(Object inputElement) {
560 paGitGrepSearchResult = (GitGrepSearchResult) inputElement;
561 return paGitGrepSearchResult.getElements();
571 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
572 paGitGrepTableViewer = (TableViewer) viewer;
573 paGitGrepSearchResult = (GitGrepSearchResult) newInput;
574 paGitGrepTableViewer.refresh();
577 public void elementsChanged(Object[] updatedElements) {
579 for (
int i = 0; i < updatedElements.length; i++) {
580 if (paGitGrepSearchResult.getMatchCount(updatedElements[i]) > 0) {
581 if (paGitGrepTableViewer.testFindItem(updatedElements[i]) !=
null) {
582 paGitGrepTableViewer.refresh(updatedElements[i]);
584 paGitGrepTableViewer.add(updatedElements[i]);
587 paGitGrepTableViewer.remove(updatedElements[i]);
593 public void dispose() {