Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bundles/org.eclipse.search/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.search; singleton:=true
Bundle-Version: 3.19.0.qualifier
Bundle-Version: 3.19.100.qualifier
Bundle-Activator: org.eclipse.search.internal.ui.SearchPlugin
Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -28,9 +28,6 @@
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Tree;

import org.eclipse.core.runtime.IAdaptable;

Expand Down Expand Up @@ -296,8 +293,8 @@ protected void fillContextMenu(IMenuManager mgr) {
addSortActions(mgr);
fActionGroup.setContext(new ActionContext(getSite().getSelectionProvider().getSelection()));
fActionGroup.fillContextMenu(mgr);
FileSearchQuery query= (FileSearchQuery) getInput().getQuery();
if (!query.getSearchString().isEmpty()) {
// the result may be provided by a client that doesn't use a FileSearchQuery
if (getInput().getQuery() instanceof FileSearchQuery query && !query.getSearchString().isEmpty()) {
IStructuredSelection selection = getViewer().getStructuredSelection();
if (!selection.isEmpty()) {
ReplaceAction replaceSelection= new ReplaceAction(getSite().getShell(), (FileSearchResult)getInput(), selection.toArray());
Expand Down Expand Up @@ -445,81 +442,53 @@ private boolean isQueryRunning() {
public String getLabel() {
String label= super.getLabel();
AbstractTextSearchResult result = getInput();
if (result == null || fContentProvider == null) {
return label;
}
String msg = label;
if (result != null) {
int itemCount = fContentProvider.getLeafCount(result);
if (showLineMatches()) {
int matchCount = result.getMatchCount();
if (itemCount < matchCount) {
msg = Messages.format(SearchMessages.FileSearchPage_limited_format_matches,
new Object[] { label, Integer.valueOf(itemCount), Integer.valueOf(matchCount) });
}
} else {
int fileCount = result.getElementsCount();
if (itemCount < fileCount) {
msg = Messages.format(SearchMessages.FileSearchPage_limited_format_files,
new Object[] { label, Integer.valueOf(itemCount), Integer.valueOf(fileCount) });
}
}
if (result.getActiveMatchFilters() != null && result.getActiveMatchFilters().length > 0) {
if (isQueryRunning()) {
String message = SearchMessages.FileSearchPage_filtered_message;
return Messages.format(message, new Object[] { msg });

} else {
int filteredOut = result.getMatchCount() - getFilteredMatchCount();
String message = SearchMessages.FileSearchPage_filteredWithCount_message;
return Messages.format(message, new Object[] { msg, String.valueOf(filteredOut) });
}
if (showLineMatches()) {
// leafs are matching lines, but the user is interested in matches: a leaf
// can represent more than one match, so the number of leafs must not be
// reported as number of matches
if (fContentProvider.isTruncated(result)) {
msg = Messages.format(SearchMessages.FileSearchPage_limited_format_matches,
new Object[] { label, Integer.valueOf(fContentProvider.getShownMatchCount(result)),
Integer.valueOf(result.getMatchCount()) });
}
}
return msg;
}

private int getFilteredMatchCount() {
StructuredViewer viewer = getViewer();
if (viewer instanceof TreeViewer) {
ITreeContentProvider tp = (ITreeContentProvider) viewer.getContentProvider();
return getMatchCount(tp, getRootElements((TreeViewer) getViewer()));
} else {
return getMatchCount((TableViewer) viewer);
}
}

private Object[] getRootElements(TreeViewer viewer) {
Tree t = viewer.getTree();
Item[] roots = t.getItems();
Object[] elements = new Object[roots.length];
for (int i = 0; i < elements.length; i++) {
elements[i] = roots[i].getData();
}
return elements;
}

private Object[] getRootElements(TableViewer viewer) {
Table t = viewer.getTable();
Item[] roots = t.getItems();
Object[] elements = new Object[roots.length];
for (int i = 0; i < elements.length; i++) {
elements[i] = roots[i].getData();
// leafs are files
int shownFileCount = fContentProvider.getLeafCount(result);
int fileCount = result.getElementsCount();
if (shownFileCount < fileCount) {
msg = Messages.format(SearchMessages.FileSearchPage_limited_format_files,
new Object[] { label, Integer.valueOf(shownFileCount), Integer.valueOf(fileCount) });
}
}
return elements;
}
if (result.getActiveMatchFilters() != null && result.getActiveMatchFilters().length > 0) {
if (isQueryRunning()) {
String message = SearchMessages.FileSearchPage_filtered_message;
return Messages.format(message, new Object[] { msg });

private int getMatchCount(ITreeContentProvider cp, Object[] elements) {
int count = 0;
for (Object element : elements) {
count += getDisplayedMatchCount(element);
Object[] children = cp.getChildren(element);
count += getMatchCount(cp, children);
} else {
int filteredOut = result.getMatchCount() - getUnfilteredMatchCount(result);
String message = SearchMessages.FileSearchPage_filteredWithCount_message;
return Messages.format(message, new Object[] { msg, String.valueOf(filteredOut) });
}
}
return count;
return msg;
}

private int getMatchCount(TableViewer viewer) {
/**
* @param result the search result
* @return the number of matches that are not hidden by the active match
* filters, independently from the element limit
*/
private int getUnfilteredMatchCount(AbstractTextSearchResult result) {
int count = 0;
for (Object element : getRootElements(viewer)) {
count += getDisplayedMatchCount(element);
for (Object element : result.getElements()) {
// don't use getDisplayedMatchCount(Object): this page only counts matches
// for line elements if line matches are shown
count += super.getDisplayedMatchCount(element);
}
return count;
}
Expand Down Expand Up @@ -568,7 +537,9 @@ protected void evaluateChangedElements(Match[] matches, Set<Object> changedEleme

private boolean showLineMatches() {
AbstractTextSearchResult input= getInput();
return getLayout() == FLAG_LAYOUT_TREE && input != null && !((FileSearchQuery) input.getQuery()).isFileNameSearch();
// the result may be provided by a client that doesn't use a FileSearchQuery
return getLayout() == FLAG_LAYOUT_TREE && input != null
&& input.getQuery() instanceof FileSearchQuery query && !query.isFileNameSearch();
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -51,6 +51,27 @@ public int getLeafCount(Object parentElement) {
return elementsCount;
}

@Override
public boolean isTruncated(Object parentElement) {
if (!(parentElement instanceof AbstractTextSearchResult searchResult)) {
return false;
}
int elementLimit = getElementLimit();
return elementLimit != -1 && searchResult.getElementsCount() > elementLimit;
}

@Override
public int getShownMatchCount(AbstractTextSearchResult result) {
if (result == null) {
return 0;
}
int count = 0;
for (Object element : getElements(result)) {
count += fPage.getDisplayedMatchCount(element);
}
return count;
}

@Override
public Object[] getElements(Object inputElement) {
if (inputElement instanceof FileSearchResult fileSearchResult) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2023 IBM Corporation and others.
* Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -17,12 +17,15 @@
*******************************************************************************/
package org.eclipse.search.internal.ui.text;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Spliterator;
import java.util.Spliterators;
Expand Down Expand Up @@ -50,6 +53,12 @@ public class FileTreeContentProvider implements ITreeContentProvider, IFileSearc
private final FileSearchPage fPage;
private final AbstractTreeViewer fTreeViewer;
private Map<Object, Set<Object>> fChildrenMap;
/**
* Upper bound for the number of children of an element in
* {@link #fChildrenMap}. Only updated while inserting, so it may be bigger than
* the real maximum after elements have been removed.
*/
private int fMaxChildrenCount;

FileTreeContentProvider(FileSearchPage page, AbstractTreeViewer viewer) {
fPage= page;
Expand Down Expand Up @@ -81,7 +90,9 @@ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
private synchronized void initialize(AbstractTextSearchResult result) {
fResult= result;
fChildrenMap= new HashMap<>();
boolean showLineMatches= !((FileSearchQuery) fResult.getQuery()).isFileNameSearch();
fMaxChildrenCount= 0;
// the result may be provided by a client that doesn't use a FileSearchQuery
boolean showLineMatches= fResult.getQuery() instanceof FileSearchQuery query && !query.isFileNameSearch();

if (result != null) {
Object[] elements= result.getElements();
Expand Down Expand Up @@ -137,7 +148,11 @@ private boolean insertChild(Object parent, Object child) {
children= new HashSet<>();
fChildrenMap.put(parent, children);
}
return children.add(child);
boolean added= children.add(child);
if (added && children.size() > fMaxChildrenCount) {
fMaxChildrenCount= children.size();
}
return added;
}

private boolean hasChild(Object parent, Object child) {
Expand Down Expand Up @@ -226,22 +241,127 @@ public Object[] getChildren(Object parentElement) {

@Override
public int getLeafCount(Object parentElement) {
Object[] children = getChildren(parentElement);
if (children.length == 0) {
Set<Object> children = fChildrenMap.get(parentElement);
if (children == null || children.isEmpty()) {
return 0;
}
return countShownLeafs(parentElement, getElementLimit());
}

/**
* Counts the leafs below the given element that are shown in the viewer. Like
* {@link #getChildren(Object)} only the first <code>elementLimit</code> children
* of an element are considered, subtrees hidden by the element limit are not
* traversed.
*
* @param element the element to count the shown leafs for
* @param elementLimit the element limit, <code>-1</code> for no limit
* @return number of leafs shown below the given element
*/
private int countShownLeafs(Object element, int elementLimit) {
Set<Object> children = fChildrenMap.get(element);
if (children == null || children.isEmpty()) {
return 1; // the element itself is a leaf
}
int count = 0;
for (Object object : children) {
boolean leaf = !hasChildren(object);
if (leaf) {
count++;
} else {
count += getLeafCount(object);
int index = 0;
for (Object child : children) {
if (elementLimit != -1 && index >= elementLimit) {
break; // the remaining children are hidden
}
count += countShownLeafs(child, elementLimit);
index++;
}
return count;
}

@Override
public boolean isTruncated(Object parentElement) {
int elementLimit = getElementLimit();
if (elementLimit == -1 || fMaxChildrenCount <= elementLimit) {
// no element can have more children than the element limit allows
return false;
}
return isTruncated(parentElement, elementLimit);
}

/**
* Elements are hidden if and only if a shown element has more children than the
* element limit allows, so only shown subtrees have to be traversed, and the
* traversal can stop at the first truncated element.
*
* @param element the element to check
* @param elementLimit the element limit, never <code>-1</code>
* @return <code>true</code> if elements below the given element are hidden
*/
private boolean isTruncated(Object element, int elementLimit) {
Set<Object> children = fChildrenMap.get(element);
if (children == null) {
return false;
}
if (children.size() > elementLimit) {
return true;
}
for (Object child : children) { // all children are shown
if (isTruncated(child, elementLimit)) {
return true;
}
}
return false;
}

@Override
public int getShownMatchCount(AbstractTextSearchResult result) {
if (result == null || !fChildrenMap.containsKey(result)) {
return 0;
}
Map<Object, Set<LineElement>> shownLines = new HashMap<>();
List<Object> shownFiles = new ArrayList<>();
collectShownLeafs(result, getElementLimit(), shownLines, shownFiles);

int count = 0;
// count the matches of the shown lines with one pass over the matches of
// each file instead of scanning them once per line
for (Entry<Object, Set<LineElement>> entry : shownLines.entrySet()) {
for (Match match : result.getMatches(entry.getKey())) {
if (isShown(result, match) && entry.getValue().contains(((FileMatch) match).getLineElement())) {
count++;
}
}
}
for (Object file : shownFiles) {
count += fPage.getDisplayedMatchCount(file);
}
return count;
}

private static boolean isShown(AbstractTextSearchResult result, Match match) {
// see AbstractTextSearchViewPage#getDisplayedMatchCount(Object): if no filters
// are set at all the filter state of a match is ignored
return result.getActiveMatchFilters() == null || !match.isFiltered();
}

private void collectShownLeafs(Object element, int elementLimit, Map<Object, Set<LineElement>> shownLines,
List<Object> shownFiles) {
Set<Object> children = fChildrenMap.get(element);
if (children == null || children.isEmpty()) {
if (element instanceof LineElement line) {
shownLines.computeIfAbsent(line.getParent(), k -> new HashSet<>()).add(line);
} else {
shownFiles.add(element);
}
return;
}
int index = 0;
for (Object child : children) {
if (elementLimit != -1 && index >= elementLimit) {
break;
}
collectShownLeafs(child, elementLimit, shownLines, shownFiles);
index++;
}
}

@Override
public boolean hasChildren(Object element) {
Set<Object> children = fChildrenMap.get(element);
Expand Down
Loading
Loading