diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClient.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClient.java index 06e8094f7ea..8472d8d4cd4 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClient.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClient.java @@ -17,6 +17,7 @@ import org.eclipse.che.api.workspace.shared.dto.NewProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.resource.Path; import java.util.List; @@ -257,7 +258,7 @@ public interface ProjectServiceClient { * @see ItemReference * @since 4.4.0 */ - Promise> search(QueryExpression expression); + Promise> search(QueryExpression expression); /** * Gets list of {@link SourceEstimation} for all supposed project types. diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClientImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClientImpl.java index b3d0d8b781b..bfa8480998a 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClientImpl.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/project/ProjectServiceClientImpl.java @@ -15,8 +15,10 @@ import org.eclipse.che.api.project.shared.dto.CopyOptions; import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.project.shared.dto.MoveOptions; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.project.shared.dto.TreeElement; +import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.Promise; import org.eclipse.che.api.workspace.shared.dto.NewProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; @@ -24,6 +26,7 @@ import org.eclipse.che.ide.MimeType; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.machine.WsAgentStateController; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.dto.DtoFactory; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.rest.AsyncRequestFactory; @@ -31,23 +34,18 @@ import org.eclipse.che.ide.rest.StringUnmarshaller; import org.eclipse.che.ide.rest.UrlBuilder; import org.eclipse.che.ide.ui.loaders.request.LoaderFactory; -import org.eclipse.che.ide.websocket.Message; -import org.eclipse.che.ide.websocket.MessageBuilder; -import org.eclipse.che.ide.websocket.WebSocketException; -import org.eclipse.che.ide.websocket.rest.RequestCallback; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.gwt.http.client.RequestBuilder.DELETE; -import static com.google.gwt.http.client.RequestBuilder.POST; import static com.google.gwt.http.client.RequestBuilder.PUT; import static com.google.gwt.safehtml.shared.UriUtils.encodeAllowEscapes; -import static org.eclipse.che.api.promises.client.callback.AsyncPromiseHelper.createFromAsyncRequest; import static org.eclipse.che.ide.MimeType.APPLICATION_JSON; import static org.eclipse.che.ide.rest.HTTPHeader.ACCEPT; -import static org.eclipse.che.ide.rest.HTTPHeader.CONTENTTYPE; import static org.eclipse.che.ide.rest.HTTPHeader.CONTENT_TYPE; /** @@ -143,7 +141,7 @@ public Promise importProject(Path path, SourceStorageDto source) { /** {@inheritDoc} */ @Override - public Promise> search(QueryExpression expression) { + public Promise> search(QueryExpression expression) { final String url = encodeAllowEscapes(getBaseUrl() + SEARCH + (isNullOrEmpty(expression.getPath()) ? Path.ROOT : path(expression.getPath()))); @@ -164,7 +162,13 @@ public Promise> search(QueryExpression expression) { return reqFactory.createGetRequest(url + queryParameters.toString().replaceFirst("&", "?")) .header(ACCEPT, MimeType.APPLICATION_JSON) .loader(loaderFactory.newLoader("Searching...")) - .send(unmarshaller.newListUnmarshaller(ItemReference.class)); + .send(unmarshaller.newListUnmarshaller(SearchResultDto.class)).then( + (Function, List>)arg -> { + if (arg.isEmpty()) { + return Collections.emptyList(); + } + return arg.stream().map(SearchResult::new).collect(Collectors.toList()); + }); } /** {@inheritDoc} */ diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/Container.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/Container.java index de00b420bdc..43c300285a9 100644 --- a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/Container.java +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/Container.java @@ -21,6 +21,8 @@ import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.util.NameUtils; +import java.util.List; + /** * Interface for resource which may contain other resources (termed its members). *

@@ -436,7 +438,7 @@ public interface Container extends Resource { * @return the {@link Promise} with array of found results * @since 4.4.0 */ - Promise search(String fileMask, String contentMask); + Promise> search(String fileMask, String contentMask); /** * Returns the plain list of file tree with given {@code depth}. diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchOccurrenceImpl.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchOccurrenceImpl.java new file mode 100644 index 00000000000..9986d0af683 --- /dev/null +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchOccurrenceImpl.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.resources; + +import org.eclipse.che.api.project.shared.SearchOccurrence; + +/** + * @author Vitalii Parfonov + */ + +public class SearchOccurrenceImpl implements SearchOccurrence { + + private float score; + private int endOffset; + private int startOffset; + private String phrase; + private String lineContent; + private int lineNumber; + + public SearchOccurrenceImpl(SearchOccurrence searchOccurrence) { + score = searchOccurrence.getScore(); + endOffset = searchOccurrence.getEndOffset(); + startOffset = searchOccurrence.getStartOffset(); + phrase = searchOccurrence.getPhrase(); + lineContent = searchOccurrence.getLineContent(); + lineNumber = searchOccurrence.getLineNumber(); + } + + @Override + public float getScore() { + return score; + } + + @Override + public void setScore(float score) { + this.score = score; + } + + @Override + public String getPhrase() { + return phrase; + } + + @Override + public void setPhrase(String phrase) { + this.phrase = phrase; + } + + @Override + public int getEndOffset() { + return endOffset; + } + + @Override + public void setEndOffset(int endOffset) { + this.endOffset = endOffset; + } + + @Override + public int getStartOffset() { + return startOffset; + } + + @Override + public void setStartOffset(int startOffset) { + this.startOffset = startOffset; + } + + @Override + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + @Override + public int getLineNumber() { + return lineNumber; + } + + @Override + public void setLineContent(String lineContent) { + this.lineContent = lineContent; + } + + @Override + public String getLineContent() { + return lineContent; + } +} diff --git a/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchResult.java b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchResult.java new file mode 100644 index 00000000000..da33a3fabca --- /dev/null +++ b/ide/che-core-ide-api/src/main/java/org/eclipse/che/ide/api/resources/SearchResult.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.api.resources; + +import org.eclipse.che.api.project.shared.Constants; +import org.eclipse.che.api.project.shared.SearchOccurrence; +import org.eclipse.che.api.project.shared.dto.SearchOccurrenceDto; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Vitalii Parfonov + */ + +public class SearchResult { + + private String name; + private String path; + private String project; + private String contentUrl; + private List occurrences; + + public SearchResult(SearchResultDto searchResultDto) { + name = searchResultDto.getItemReference().getName(); + path = searchResultDto.getItemReference().getPath(); + project = searchResultDto.getItemReference().getProject(); + if (searchResultDto.getItemReference().getLink(Constants.LINK_REL_GET_CONTENT) != null) { + contentUrl = searchResultDto.getItemReference().getLink(Constants.LINK_REL_GET_CONTENT).getHref(); + } + final List dtos = searchResultDto.getSearchOccurrences(); + occurrences = new ArrayList<>(dtos.size()); + for (SearchOccurrence dto : dtos) { + occurrences.add(new SearchOccurrenceImpl(dto)); + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getProject() { + return project; + } + + public void setProject(String project) { + this.project = project; + } + + public List getOccurrences() { + return occurrences; + } + + public void setOccurrences(List occurrences) { + this.occurrences = occurrences; + } + + public String getContentUrl() { + return contentUrl; + } + + public void setContentUrl(String contentUrl) { + this.contentUrl = contentUrl; + } +} diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/Resources.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/Resources.java index 6c99730dce5..f32a34d8ba7 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/Resources.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/Resources.java @@ -182,6 +182,9 @@ public interface Resources extends Tree.Resources, @Source("part/che-logo.svg") SVGResource cheLogo(); + @Source("searchMatch.svg") + SVGResource searchMatch(); + /** Interface for css resources. */ interface CoreCss extends CssResource { String editorPaneMenuDelimiter(); @@ -213,7 +216,10 @@ interface CoreCss extends CssResource { String tagsPanel(); - @ClassName("codeassistant-highlight") - String codeassistantHighlight(); + @ClassName("found-highlight") + String foundPhraseHighlight(); + + @ClassName("search-match") + String searchMatch(); } } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/editor/page/text/MacroCompletionProposal.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/editor/page/text/MacroCompletionProposal.java index a9a19ec1e81..76fb50f7b0e 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/editor/page/text/MacroCompletionProposal.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/command/editor/page/text/MacroCompletionProposal.java @@ -89,7 +89,7 @@ private void appendPlain(SafeHtmlBuilder builder, String text) { } private void appendHighlighted(SafeHtmlBuilder builder, String text) { - builder.appendHtmlConstant(""); + builder.appendHtmlConstant(""); builder.appendEscaped(text); builder.appendHtmlConstant(""); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileView.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileView.java index 153f359afc1..00011668d33 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileView.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileView.java @@ -12,7 +12,7 @@ import com.google.inject.ImplementedBy; -import org.eclipse.che.api.project.shared.dto.ItemReference; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.ide.api.mvp.View; import org.eclipse.che.ide.resource.Path; @@ -67,6 +67,6 @@ interface ActionDelegate { * @param items * items of suggestions */ - void showItems(List items); + void showItems(List items); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileViewImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileViewImpl.java index 34ad6bbb90f..157647d136d 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileViewImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/navigation/NavigateToFileViewImpl.java @@ -34,7 +34,8 @@ import elemental.dom.Element; import elemental.html.TableCellElement; import elemental.html.TableElement; -import org.eclipse.che.api.project.shared.dto.ItemReference; + +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.api.autocomplete.AutoCompleteResources; @@ -69,7 +70,7 @@ interface NavigateToFileViewImplUiBinder extends UiBinder list; + private SimpleList list; @UiField FlowPanel suggestionsPanel; @@ -140,14 +141,14 @@ public void onResize(ResizeEvent event) { } } - private final SimpleList.ListItemRenderer listItemRenderer = - new SimpleList.ListItemRenderer() { + private final SimpleList.ListItemRenderer listItemRenderer = + new SimpleList.ListItemRenderer() { @Override - public void render(Element itemElement, ItemReference itemData) { + public void render(Element itemElement, SearchResultDto itemData) { TableCellElement label = Elements.createTDElement(); TableCellElement path = Elements.createTDElement(); - Path itemPath = Path.valueOf(itemData.getPath()); + Path itemPath = Path.valueOf(itemData.getItemReference().getPath()); label.setInnerHTML(itemPath.lastSegment()); path.setInnerHTML("(" + itemPath.parent() + ")"); @@ -181,7 +182,7 @@ public void hidePopup() { } @Override - public void showItems(List items) { + public void showItems(List items) { // Hide popup if it is nothing to show if (items.isEmpty()) { suggestionsContainer.getElement().setInnerHTML(""); @@ -201,11 +202,11 @@ public void showItems(List items) { final TableElement itemHolder = Elements.createTableElement(); suggestionsContainer.getElement().appendChild(((com.google.gwt.dom.client.Element) itemHolder)); list = SimpleList.create((SimpleList.View) suggestionsContainer.getElement().cast(), - (Element)suggestionsContainer.getElement(), - itemHolder, - resources.defaultSimpleListCss(), - listItemRenderer, - eventDelegate); + (Element)suggestionsContainer.getElement(), + itemHolder, + resources.defaultSimpleListCss(), + listItemRenderer, + eventDelegate); list.render(items); list.getSelectionModel().setSelectedItem(0); @@ -247,15 +248,15 @@ private void updatePositionAndSize() { suggestionsPanel.getElement().getStyle().setHeight(newHeight, Style.Unit.PX); } - private final SimpleList.ListEventDelegate eventDelegate = new SimpleList.ListEventDelegate() { + private final SimpleList.ListEventDelegate eventDelegate = new SimpleList.ListEventDelegate() { @Override - public void onListItemClicked(Element listItemBase, ItemReference itemData) { + public void onListItemClicked(Element listItemBase, SearchResultDto itemData) { list.getSelectionModel().setSelectedItem(itemData); } @Override - public void onListItemDoubleClicked(Element listItemBase, ItemReference itemData) { - delegate.onFileSelected(Path.valueOf(itemData.getPath()));; + public void onListItemDoubleClicked(Element listItemBase, SearchResultDto itemData) { + delegate.onFileSelected(Path.valueOf(itemData.getItemReference().getPath())); } }; @@ -297,9 +298,9 @@ void handleKeyDown(KeyDownEvent event) { case KeyCodes.KEY_ENTER: event.stopPropagation(); event.preventDefault(); - ItemReference selectedItem = list.getSelectionModel().getSelectedItem(); + SearchResultDto selectedItem = list.getSelectionModel().getSelectedItem(); if (selectedItem != null) { - delegate.onFileSelected(Path.valueOf(selectedItem.getPath()));; + delegate.onFileSelected(Path.valueOf(selectedItem.getItemReference().getPath())); } return; diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ContainerImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ContainerImpl.java index 59f7f686cfd..88ec28420c1 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ContainerImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ContainerImpl.java @@ -14,6 +14,7 @@ import com.google.common.base.Optional; import org.eclipse.che.api.core.model.project.ProjectConfig; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.promises.client.Function; import org.eclipse.che.api.promises.client.FunctionException; @@ -25,8 +26,11 @@ import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceDelta; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.resource.Path; +import java.util.List; + import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; @@ -177,7 +181,7 @@ public Promise synchronize(ResourceDelta...deltas) { /** {@inheritDoc} */ @Override - public Promise search(String fileMask, String contentMask) { + public Promise> search(String fileMask, String contentMask) { return resourceManager.search(this, fileMask, contentMask); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java index 98fb8d27858..e9eb6756cf2 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/impl/ResourceManager.java @@ -22,6 +22,7 @@ import org.eclipse.che.api.core.model.project.ProjectConfig; import org.eclipse.che.api.core.model.project.SourceStorage; import org.eclipse.che.api.core.rest.shared.dto.Link; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.project.shared.dto.TreeElement; @@ -53,6 +54,7 @@ import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.ResourceDelta; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.api.resources.marker.Marker; import org.eclipse.che.ide.api.resources.marker.MarkerChangedEvent; import org.eclipse.che.ide.api.vcs.VcsStatus; @@ -73,7 +75,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Strings.isNullOrEmpty; -import static java.lang.System.arraycopy; import static java.util.Arrays.copyOf; import static java.util.Arrays.stream; import static org.eclipse.che.ide.api.resources.Resource.FILE; @@ -1098,7 +1099,7 @@ private Promise onExternalDeltaRemoved(final ResourceDelta delta) { return promises.resolve(null); } - protected Promise search(final Container container, String fileMask, String contentMask) { + protected Promise> search(final Container container, String fileMask, String contentMask) { QueryExpression queryExpression = new QueryExpression(); if (!isNullOrEmpty(contentMask)) { queryExpression.setText(contentMask); @@ -1110,58 +1111,7 @@ protected Promise search(final Container container, String fileMask, queryExpression.setPath(container.getLocation().toString()); } - return ps.search(queryExpression).thenPromise(references -> { - if (references.isEmpty()) { - return promises.resolve(NO_RESOURCES); - } - - int maxDepth = 0; - - final Path[] paths = new Path[references.size()]; - - for (int i = 0; i < paths.length; i++) { - final Path path = Path.valueOf(references.get(i).getPath()); - paths[i] = path; - - if (path.segmentCount() > maxDepth) { - maxDepth = path.segmentCount(); - } - } - - return getRemoteResources(container, maxDepth, true).then((Function)resources -> { - Resource[] filtered = NO_RESOURCES; - Path[] mutablePaths = paths; - - outer: - for (Resource resource : resources) { - if (resource.getResourceType() != FILE) { - continue; - } - - for (int i = 0; i < mutablePaths.length; i++) { - Path path = mutablePaths[i]; - - if (path.segmentCount() == resource.getLocation().segmentCount() && path.equals(resource.getLocation())) { - Resource[] tmpFiltered = copyOf(filtered, filtered.length + 1); - tmpFiltered[filtered.length] = resource; - filtered = tmpFiltered; - - //reduce the size of mutablePaths by removing already checked item - int size = mutablePaths.length; - int numMoved = mutablePaths.length - i - 1; - if (numMoved > 0) { - arraycopy(mutablePaths, i + 1, mutablePaths, i, numMoved); - } - mutablePaths = copyOf(mutablePaths, --size); - - continue outer; - } - } - } - - return filtered; - }); - }); + return ps.search(queryExpression); } Promise estimate(Container container, String projectType) { diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/FileNode.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/FileNode.java index 60f124fa9e7..e0e3a7081d5 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/FileNode.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/FileNode.java @@ -19,6 +19,7 @@ import org.eclipse.che.ide.api.data.tree.settings.NodeSettings; import org.eclipse.che.ide.api.editor.EditorAgent; import org.eclipse.che.ide.api.resources.File; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.project.node.icon.NodeIconProvider; import org.eclipse.che.ide.project.shared.NodesResources; @@ -50,6 +51,7 @@ public FileNode(@Assisted File resource, this.editorAgent = editorAgent; } + /** {@inheritDoc} */ @Override public void actionPerformed() { diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/ResourceNode.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/ResourceNode.java index 6fc86edf5bb..858b0da5607 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/ResourceNode.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/resources/tree/ResourceNode.java @@ -28,6 +28,7 @@ import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.api.resources.marker.MarkerChangedEvent; import org.eclipse.che.ide.api.resources.marker.PresentableTextMarker; import org.eclipse.che.ide.api.resources.marker.Marker; diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/FullTextSearchPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/FullTextSearchPresenter.java index d8f3c3db7c7..b363757487b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/FullTextSearchPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/FullTextSearchPresenter.java @@ -18,10 +18,12 @@ import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.resources.Container; -import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.search.presentation.FindResultPresenter; +import java.util.List; + import static com.google.common.base.Strings.isNullOrEmpty; /** @@ -74,9 +76,9 @@ public void apply(Optional optionalContainer) throws OperationExcepti } final Container container = optionalContainer.get(); - container.search(view.getFileMask(), prepareQuery(text)).then(new Operation() { + container.search(view.getFileMask(), prepareQuery(text)).then(new Operation> () { @Override - public void apply(Resource[] result) throws OperationException { + public void apply(List result) throws OperationException { view.close(); findResultPresenter.handleResponse(result, text); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/factory/FindResultNodeFactory.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/factory/FindResultNodeFactory.java index b93f709c325..c59ea6ee1c1 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/factory/FindResultNodeFactory.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/factory/FindResultNodeFactory.java @@ -10,8 +10,13 @@ *******************************************************************************/ package org.eclipse.che.ide.search.factory; -import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.api.project.shared.SearchOccurrence; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.search.presentation.FindResultGroupNode; +import org.eclipse.che.ide.search.presentation.FoundItemNode; +import org.eclipse.che.ide.search.presentation.FoundOccurrenceNode; + +import java.util.List; /** * Factory for creating tree element for the result of searching. @@ -28,5 +33,9 @@ public interface FindResultNodeFactory { * requested text to search * @return new instance of {@link FindResultGroupNode} */ - FindResultGroupNode newResultNode(Resource[] result, String request); + FindResultGroupNode newResultNode(List result, String request); + + FoundItemNode newFoundItemNode(SearchResult searchResult, String request); + + FoundOccurrenceNode newFoundOccurrenceNode(SearchOccurrence searchOccurrence, String itemPath); } diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultGroupNode.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultGroupNode.java index a97efd0a776..c53a770cc1f 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultGroupNode.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultGroupNode.java @@ -14,27 +14,21 @@ import com.google.inject.assistedinject.Assisted; import org.eclipse.che.api.promises.client.Promise; -import org.eclipse.che.api.promises.client.js.Promises; +import org.eclipse.che.api.promises.client.PromiseProvider; import org.eclipse.che.ide.CoreLocalizationConstant; import org.eclipse.che.ide.api.data.tree.AbstractTreeNode; import org.eclipse.che.ide.api.data.tree.Node; -import org.eclipse.che.ide.api.resources.File; -import org.eclipse.che.ide.api.resources.Resource; -import org.eclipse.che.ide.resources.tree.FileNode; -import org.eclipse.che.ide.resources.tree.ResourceNode; +import org.eclipse.che.ide.api.resources.SearchResult; +import org.eclipse.che.ide.search.factory.FindResultNodeFactory; import org.eclipse.che.ide.ui.smartTree.compare.NameComparator; import org.eclipse.che.ide.ui.smartTree.presentation.HasPresentation; import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation; -import org.eclipse.che.ide.util.Pair; import javax.validation.constraints.NotNull; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static org.eclipse.che.ide.api.resources.Resource.FILE; -import static org.eclipse.che.ide.api.theme.Style.getEditorInfoTextColor; - /** * Tree node represent search result. * @@ -44,19 +38,22 @@ public class FindResultGroupNode extends AbstractTreeNode implements HasPresentation { private final CoreLocalizationConstant locale; - private final ResourceNode.NodeFactory nodeFactory; - private NodePresentation nodePresentation; - private Resource[] findResults; - private String request; + private NodePresentation nodePresentation; + private FindResultNodeFactory nodeFactory; + private PromiseProvider promiseProvider; + private List findResults; + private String request; @Inject public FindResultGroupNode(CoreLocalizationConstant locale, - ResourceNode.NodeFactory nodeFactory, - @Assisted Resource[] findResult, + FindResultNodeFactory nodeFactory, + PromiseProvider promiseProvider, + @Assisted List findResult, @Assisted String request) { this.locale = locale; this.nodeFactory = nodeFactory; + this.promiseProvider = promiseProvider; this.findResults = findResult; this.request = request; } @@ -65,25 +62,14 @@ public FindResultGroupNode(CoreLocalizationConstant locale, @Override protected Promise> getChildrenImpl() { List fileNodes = new ArrayList<>(); - for (Resource resource : findResults) { - if (resource.getResourceType() != FILE) { - continue; - } - - FileNode node = nodeFactory.newFileNode((File)resource, null); - - NodePresentation presentation = node.getPresentation(true); - presentation.setInfoText(resource.getLocation().toString()); - presentation.setInfoTextWrapper(Pair.of("(", ")")); - presentation.setInfoTextCss("color:" + getEditorInfoTextColor() + ";font-size: 11px"); - - fileNodes.add(node); + for (SearchResult searchResult : findResults) { + FoundItemNode foundItemNode = nodeFactory.newFoundItemNode(searchResult, request); + fileNodes.add(foundItemNode); } - //sort nodes by file name Collections.sort(fileNodes, new NameComparator()); - return Promises.resolve(fileNodes); + return promiseProvider.resolve(fileNodes); } /** {@inheritDoc} */ @@ -115,8 +101,12 @@ public boolean isLeaf() { /** {@inheritDoc} */ @Override public void updatePresentation(@NotNull NodePresentation presentation) { - StringBuilder resultTitle = new StringBuilder("Find Occurrences of '" + request + "\' (" + findResults.length + " occurrence"); - if (findResults.length > 1) { + int total = 0; + for (SearchResult searchResult : findResults) { + total += searchResult.getOccurrences().size(); + } + StringBuilder resultTitle = new StringBuilder("Found occurrences of '" + request + "\' (" + total + " occurrence"); + if (total > 1) { resultTitle.append("s)"); } else { resultTitle.append(")"); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultPresenter.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultPresenter.java index ad1bd03a5e5..5e058998f3b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultPresenter.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultPresenter.java @@ -22,10 +22,10 @@ import org.eclipse.che.ide.api.parts.PartStackType; import org.eclipse.che.ide.api.parts.WorkspaceAgent; import org.eclipse.che.ide.api.parts.base.BasePresenter; -import org.eclipse.che.ide.api.resources.Resource; import org.eclipse.che.ide.api.resources.ResourceChangedEvent; import org.eclipse.che.ide.api.resources.ResourceChangedEvent.ResourceChangedHandler; import org.eclipse.che.ide.api.resources.ResourceDelta; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.api.selection.Selection; import org.eclipse.che.ide.resources.tree.ResourceNode; import org.eclipse.che.ide.ui.smartTree.Tree; @@ -98,7 +98,7 @@ public void go(AcceptsOneWidget container) { * @param request * requested text */ - public void handleResponse(Resource[] resources, String request) { + public void handleResponse(List resources, String request) { workspaceAgent.openPart(this, PartStackType.INFORMATION); workspaceAgent.setActivePart(this); view.showResults(resources, request); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultView.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultView.java index cf59555d806..8c71fdbac5b 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultView.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultView.java @@ -15,7 +15,7 @@ import org.eclipse.che.ide.api.data.tree.Node; import org.eclipse.che.ide.api.mvp.View; import org.eclipse.che.ide.api.parts.base.BaseActionDelegate; -import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.ui.smartTree.Tree; import java.util.List; @@ -43,7 +43,7 @@ public interface FindResultView extends View { * @param request * requested text */ - void showResults(Resource[] resources, String request); + void showResults(List resources, String request); Tree getTree(); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultViewImpl.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultViewImpl.java index a0b8a03cb88..0419269ce99 100644 --- a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultViewImpl.java +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FindResultViewImpl.java @@ -17,7 +17,7 @@ import org.eclipse.che.ide.api.data.tree.NodeInterceptor; import org.eclipse.che.ide.api.parts.PartStackUIResources; import org.eclipse.che.ide.api.parts.base.BaseView; -import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.search.factory.FindResultNodeFactory; import org.eclipse.che.ide.ui.smartTree.NodeLoader; import org.eclipse.che.ide.ui.smartTree.NodeStorage; @@ -25,6 +25,7 @@ import org.eclipse.che.ide.ui.smartTree.event.SelectionChangedEvent; import java.util.Collections; +import java.util.List; /** * Implementation for FindResult view. @@ -69,7 +70,7 @@ protected void focusView() { /** {@inheritDoc} */ @Override - public void showResults(Resource[] resources, String request) { + public void showResults(List resources, String request) { tree.getNodeStorage().clear(); tree.getNodeStorage().add(findResultNodeFactory.newResultNode(resources, request)); tree.expandAll(); diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundItemNode.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundItemNode.java new file mode 100644 index 00000000000..121df74af5d --- /dev/null +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundItemNode.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.search.presentation; + +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.che.api.project.shared.SearchOccurrence; +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.api.promises.client.PromiseProvider; +import org.eclipse.che.ide.api.data.tree.AbstractTreeNode; +import org.eclipse.che.ide.api.data.tree.Node; +import org.eclipse.che.ide.api.resources.SearchResult; +import org.eclipse.che.ide.search.factory.FindResultNodeFactory; +import org.eclipse.che.ide.ui.smartTree.presentation.HasPresentation; +import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation; + +import javax.inject.Inject; +import javax.validation.constraints.NotNull; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Instance describe file node where found given text + * + * @author Vitalii Parfonov + */ + +public class FoundItemNode extends AbstractTreeNode implements HasPresentation { + + private NodePresentation nodePresentation; + private FindResultNodeFactory nodeFactory; + private PromiseProvider promiseProvider; + private SearchResult searchResult; + private String request; + + @Inject + public FoundItemNode(FindResultNodeFactory nodeFactory, + PromiseProvider promiseProvider, + @Assisted SearchResult searchResult, + @Assisted String request) { + this.nodeFactory = nodeFactory; + this.promiseProvider = promiseProvider; + this.searchResult = searchResult; + this.request = request; + } + + /** {@inheritDoc} */ + @Override + public NodePresentation getPresentation(boolean update) { + if (nodePresentation == null) { + nodePresentation = new NodePresentation(); + updatePresentation(nodePresentation); + } + + if (update) { + updatePresentation(nodePresentation); + } + return nodePresentation; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return searchResult.getName(); + } + + /** {@inheritDoc} */ + @Override + public boolean isLeaf() { + return false; + } + + /** {@inheritDoc} */ + @Override + public void updatePresentation(@NotNull NodePresentation presentation) { + StringBuilder resultTitle = new StringBuilder(searchResult.getPath()); + resultTitle.append(" ("); + resultTitle.append(searchResult.getOccurrences().size()); + resultTitle.append(" occurrence"); + if (searchResult.getOccurrences().size() > 1) { + resultTitle.append('s'); + } + resultTitle.append(" of '"); + resultTitle.append(request); + resultTitle.append('\''); + resultTitle.append(" found)"); + presentation.setPresentableText(resultTitle.toString()); + } + + @Override + protected Promise> getChildrenImpl() { + List fileNodes; + List occurrences = searchResult.getOccurrences(); + occurrences.sort(Comparator.comparingInt((SearchOccurrence searchOccurrence) -> searchOccurrence.getLineNumber())); + fileNodes = occurrences.stream().map(occurrence -> nodeFactory + .newFoundOccurrenceNode(occurrence, searchResult.getPath())).collect(Collectors.toList()); + return promiseProvider.resolve(fileNodes); + } +} diff --git a/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundOccurrenceNode.java b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundOccurrenceNode.java new file mode 100644 index 00000000000..86dde6c5d11 --- /dev/null +++ b/ide/che-core-ide-app/src/main/java/org/eclipse/che/ide/search/presentation/FoundOccurrenceNode.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.ide.search.presentation; + +import elemental.html.SpanElement; + +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.dom.client.Element; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.che.api.project.shared.SearchOccurrence; +import org.eclipse.che.api.promises.client.Promise; +import org.eclipse.che.ide.Resources; +import org.eclipse.che.ide.api.app.AppContext; +import org.eclipse.che.ide.api.data.tree.AbstractTreeNode; +import org.eclipse.che.ide.api.data.tree.HasAction; +import org.eclipse.che.ide.api.data.tree.Node; +import org.eclipse.che.ide.api.editor.EditorAgent; +import org.eclipse.che.ide.api.editor.EditorPartPresenter; +import org.eclipse.che.ide.api.editor.OpenEditorCallbackImpl; +import org.eclipse.che.ide.api.editor.text.LinearRange; +import org.eclipse.che.ide.api.editor.texteditor.TextEditor; +import org.eclipse.che.ide.resource.Path; +import org.eclipse.che.ide.ui.smartTree.TreeStyles; +import org.eclipse.che.ide.ui.smartTree.presentation.HasPresentation; +import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation; + +import javax.inject.Inject; +import javax.validation.constraints.NotNull; +import java.util.List; + +import static org.eclipse.che.ide.util.dom.Elements.createSpanElement; +import static org.eclipse.che.ide.util.dom.Elements.createTextNode; + +/** + * Instance describe occurrence where found give text. + * If in file found several occurrence, will be created dedicated instance for each occurrence + * + * @author Vitalii Parfonov + */ + +public class FoundOccurrenceNode extends AbstractTreeNode implements HasPresentation, HasAction { + + + private NodePresentation nodePresentation; + private AppContext appContext; + private TreeStyles styles; + private Resources resources; + private EditorAgent editorAgent; + private String itemPath; + private SearchOccurrence searchOccurrence; + + @Inject + public FoundOccurrenceNode(AppContext appContext, + TreeStyles styles, + Resources resources, + EditorAgent editorAgent, + @Assisted SearchOccurrence searchOccurrence, + @Assisted String itemPath) { + this.appContext = appContext; + this.styles = styles; + this.resources = resources; + this.editorAgent = editorAgent; + this.itemPath = itemPath; + this.searchOccurrence = searchOccurrence; + } + + @Override + @SuppressWarnings("Duplicates") + public void actionPerformed() { + final EditorPartPresenter editorPartPresenter = editorAgent.getOpenedEditor(Path.valueOf(itemPath)); + if (editorPartPresenter != null) { + selectRange(editorPartPresenter); + Scheduler.get().scheduleDeferred(() -> editorAgent.activateEditor(editorPartPresenter)); + return; + } + + appContext.getWorkspaceRoot().getFile(itemPath).then(file -> { + if (file.isPresent()) { + editorAgent.openEditor(file.get(), new OpenEditorCallbackImpl() { + @Override + public void onEditorOpened(EditorPartPresenter editor) { + selectRange(editor); + } + }); + } + }); + } + + private void selectRange(EditorPartPresenter editor) { + if (editor instanceof TextEditor) { + ((TextEditor)editor).getDocument().setSelectedRange( + LinearRange.createWithStart(searchOccurrence.getStartOffset()).andEnd(searchOccurrence.getEndOffset()), + true); + } + } + + + /** {@inheritDoc} */ + @Override + public NodePresentation getPresentation(boolean update) { + if (nodePresentation == null) { + nodePresentation = new NodePresentation(); + updatePresentation(nodePresentation); + return nodePresentation; + } + if (update) { + updatePresentation(nodePresentation); + } + return nodePresentation; + } + + /** {@inheritDoc} */ + @Override + public String getName() { + return searchOccurrence.getPhrase(); + } + + /** {@inheritDoc} */ + @Override + public boolean isLeaf() { + return true; + } + + /** {@inheritDoc} */ + @Override + //TODO + public void updatePresentation(@NotNull NodePresentation presentation) { + SpanElement spanElement = createSpanElement(styles.styles().presentableTextContainer()); + SpanElement lineNumberElement = createSpanElement(); + lineNumberElement.setInnerHTML(String.valueOf(searchOccurrence.getLineNumber() + 1) + ":   "); + spanElement.appendChild(lineNumberElement); + SpanElement textElement = createSpanElement(); + String phrase = searchOccurrence.getPhrase(); + String matchedLine = searchOccurrence.getLineContent(); + if (matchedLine != null && phrase != null) { + String startOfLine = matchedLine.substring(0, matchedLine.indexOf(phrase)); + String endOfLine = matchedLine.substring(matchedLine.indexOf(phrase) + phrase.length()); + textElement.appendChild(createTextNode(startOfLine)); + SpanElement highlightElement = createSpanElement(resources.coreCss().searchMatch()); + highlightElement.setInnerText(phrase); + textElement.appendChild(highlightElement); + textElement.appendChild(createTextNode(endOfLine)); + } else { + textElement.appendChild(createTextNode("Can't find sources")); + } + spanElement.appendChild(textElement); + presentation.setPresentableIcon(resources.searchMatch()); + presentation.setUserElement((Element)spanElement); + } + + @Override + protected Promise> getChildrenImpl() { + return null; + } +} diff --git a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/Core.css b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/Core.css index 61fd04421db..a0186967028 100644 --- a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/Core.css +++ b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/Core.css @@ -798,7 +798,13 @@ a { background-color: scrollbarHoverBackgroundColor; } -.codeassistant-highlight { +.found-highlight { color: completionPopupItemHighlightTextColor; } +.search-match { + background: linear-gradient(to bottom, #c1be9e 6%, #B89C07 64%); + border-radius: 2px; + padding-right: 2px; + padding-left: 2px; + diff --git a/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/searchMatch.svg b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/searchMatch.svg new file mode 100644 index 00000000000..47ac8be33e3 --- /dev/null +++ b/ide/che-core-ide-app/src/main/resources/org/eclipse/che/ide/searchMatch.svg @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/FullTextSearchPresenterTest.java b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/FullTextSearchPresenterTest.java index cf4386d861a..bcb1cb7eb29 100644 --- a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/FullTextSearchPresenterTest.java +++ b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/FullTextSearchPresenterTest.java @@ -19,7 +19,7 @@ import org.eclipse.che.commons.lang.NameGenerator; import org.eclipse.che.ide.api.app.AppContext; import org.eclipse.che.ide.api.resources.Container; -import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.resource.Path; import org.eclipse.che.ide.search.presentation.FindResultPresenter; import org.junit.Before; @@ -29,6 +29,9 @@ import org.mockito.Captor; import org.mockito.Mock; +import java.util.Collections; +import java.util.List; + import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; @@ -60,13 +63,13 @@ public class FullTextSearchPresenterTest { @Captor private ArgumentCaptor>> optionalContainerCaptor; @Mock - private Promise searchResultPromise; + private Promise> searchResultPromise; @Captor - private ArgumentCaptor> searchResultCaptor; + private ArgumentCaptor>> searchResultCaptor; @Captor private ArgumentCaptor> operationErrorCapture; @Captor - private ArgumentCaptor> operationSuccessCapture; + private ArgumentCaptor>> operationSuccessCapture; FullTextSearchPresenter fullTextSearchPresenter; @@ -100,11 +103,11 @@ public void searchShouldBeSuccessfullyFinished() throws Exception { optionalContainerCaptor.getValue().apply(Optional.of(searchContainer)); verify(searchResultPromise).then(searchResultCaptor.capture()); - searchResultCaptor.getValue().apply(new Resource[0]); + searchResultCaptor.getValue().apply(Collections.emptyList()); verify(view, never()).showErrorMessage(anyString()); verify(view).close(); - verify(findResultPresenter).handleResponse(eq(new Resource[0]), eq(SEARCHED_TEXT)); + verify(findResultPresenter).handleResponse(eq(Collections.emptyList()), eq(SEARCHED_TEXT)); } @@ -123,13 +126,13 @@ public void searchWholeWordUnSelect() throws Exception { optionalContainerCaptor.getValue().apply(Optional.of(searchContainer)); verify(searchResultPromise).then(searchResultCaptor.capture()); - searchResultCaptor.getValue().apply(new Resource[0]); + searchResultCaptor.getValue().apply(Collections.emptyList()); verify(searchContainer).search(anyString(), eq("*" + search + "*")); verify(view).isWholeWordsOnly(); verify(view, never()).showErrorMessage(anyString()); verify(view).close(); - verify(findResultPresenter).handleResponse(eq(new Resource[0]), eq(search)); + verify(findResultPresenter).handleResponse(eq(Collections.emptyList()), eq(search)); } @Test @@ -147,13 +150,13 @@ public void searchWholeWordSelect() throws Exception { optionalContainerCaptor.getValue().apply(Optional.of(searchContainer)); verify(searchResultPromise).then(searchResultCaptor.capture()); - searchResultCaptor.getValue().apply(new Resource[0]); + searchResultCaptor.getValue().apply(Collections.emptyList()); verify(searchContainer).search(anyString(), eq(search)); verify(view).isWholeWordsOnly(); verify(view, never()).showErrorMessage(anyString()); verify(view).close(); - verify(findResultPresenter).handleResponse(eq(new Resource[0]), eq(search)); + verify(findResultPresenter).handleResponse(eq(Collections.emptyList()), eq(search)); } @Test @@ -165,11 +168,11 @@ public void searchHasDoneWithSomeError() throws Exception { fullTextSearchPresenter.search(SEARCHED_TEXT); verify(optionalContainerPromise).then(optionalContainerCaptor.capture()); - optionalContainerCaptor.getValue().apply(Optional.absent()); + optionalContainerCaptor.getValue().apply(Optional.absent()); verify(view).showErrorMessage(anyString()); verify(view, never()).close(); - verify(findResultPresenter, never()).handleResponse(any(Resource[].class), anyString()); + verify(findResultPresenter, never()).handleResponse(any(List.class), anyString()); } @Test @@ -181,7 +184,7 @@ public void onEnterClickedWhenAcceptButtonInFocus() throws Exception { when(appContext.getWorkspaceRoot()).thenReturn(workspaceRoot); when(workspaceRoot.getContainer(any(Path.class))).thenReturn(optionalContainerPromise); when(searchContainer.search(anyString(), anyString())).thenReturn(searchResultPromise); - Resource[] result = new Resource[0]; + List result = Collections.emptyList(); fullTextSearchPresenter.onEnterClicked(); diff --git a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/presentation/FindResultPresenterTest.java b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/presentation/FindResultPresenterTest.java index 5adc6eadcec..a36e2865829 100644 --- a/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/presentation/FindResultPresenterTest.java +++ b/ide/che-core-ide-app/src/test/java/org/eclipse/che/ide/search/presentation/FindResultPresenterTest.java @@ -18,7 +18,6 @@ import org.eclipse.che.ide.Resources; import org.eclipse.che.ide.api.parts.PartStackType; import org.eclipse.che.ide.api.parts.WorkspaceAgent; -import org.eclipse.che.ide.api.resources.Resource; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -80,10 +79,10 @@ public void methodGoShouldBePerformed() { @Test public void responseShouldBeHandled() throws Exception { - findResultPresenter.handleResponse(Matchers.any(), anyString()); + findResultPresenter.handleResponse(Matchers.any(), anyString()); verify(workspaceAgent).openPart(findResultPresenter, PartStackType.INFORMATION); verify(workspaceAgent).setActivePart(findResultPresenter); - verify(view).showResults(Matchers.any(), anyString()); + verify(view).showResults(Matchers.any(), anyString()); } } diff --git a/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/BasicActiveFileHandler.java b/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/BasicActiveFileHandler.java index dc8fc630025..62076287959 100644 --- a/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/BasicActiveFileHandler.java +++ b/plugins/plugin-debugger/che-plugin-debugger-ide/src/main/java/org/eclipse/che/plugin/debugger/ide/debug/BasicActiveFileHandler.java @@ -17,6 +17,7 @@ import com.google.inject.Singleton; import org.eclipse.che.api.debug.shared.model.Location; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.promises.client.Operation; import org.eclipse.che.api.promises.client.OperationException; import org.eclipse.che.ide.api.app.AppContext; @@ -27,8 +28,11 @@ import org.eclipse.che.ide.api.resources.File; import org.eclipse.che.ide.api.resources.Project; import org.eclipse.che.ide.api.resources.Resource; +import org.eclipse.che.ide.api.resources.SearchResult; import org.eclipse.che.ide.api.resources.VirtualFile; +import java.util.List; + /** * @author Anatoliy Bazko */ @@ -163,15 +167,15 @@ protected void findInWorkspace(final Location location, } protected void searchSource(final Location location, final AsyncCallback callback) { - appContext.getWorkspaceRoot().search(getPath(location), "").then(new Operation() { + appContext.getWorkspaceRoot().search(getPath(location), "").then(new Operation>() { @Override - public void apply(Resource[] resources) throws OperationException { - if (resources.length == 0) { - callback.onFailure(new IllegalArgumentException(location + " not found.")); + public void apply(List resources) throws OperationException { + if (resources.isEmpty()) { + callback.onFailure(new IllegalArgumentException(location.getTarget() + " not found.")); return; } - appContext.getWorkspaceRoot().getFile(resources[0].getLocation()).then(new Operation>() { + appContext.getWorkspaceRoot().getFile(resources.get(0).getPath()).then(new Operation>() { @Override public void apply(Optional file) throws OperationException { if (file.isPresent()) { diff --git a/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/SearchOccurrence.java b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/SearchOccurrence.java new file mode 100644 index 00000000000..800607c106f --- /dev/null +++ b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/SearchOccurrence.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.project.shared; + +/** + * Contain information about occurrence of found phrase like: + * - start and send offsets in the file for found phrase; + * - number and content of line where given phrase found; + * - found phrase itself; + * @author Vitalii Parfonov + */ + +public interface SearchOccurrence { + /** + * + * @return + */ + float getScore(); + + /** + * + * @param score + */ + void setScore(float score); + + /** + * Found phrase + * (eg if you try to find 'hel' and text contain 'hello', in this case phrase will be 'hello') + * + * @return + */ + String getPhrase(); + + /** + * + * @param phrase + */ + void setPhrase(String phrase); + + /** + * End offset in file of found phrase + * @return + */ + int getEndOffset(); + + /** + * + * @param endOffset + */ + void setEndOffset(int endOffset); + + /** + * Begin offset in file of found phrase + * @return + */ + int getStartOffset(); + + /** + * + * @param startOffset + */ + void setStartOffset(int startOffset); + + /** + * Number of line where phrase found + * @param lineNumber + */ + void setLineNumber(int lineNumber); + + /** + * Number of line where phrase found + * @return + */ + int getLineNumber(); + + + /** + * Content of the line where phrase found + * @param lineContent + */ + void setLineContent(String lineContent); + + + /** + * Content of the line where phrase found + * @return + */ + String getLineContent(); +} diff --git a/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/ProjectSearchResponseDto.java b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/ProjectSearchResponseDto.java index b6838b439d6..dec2c978c67 100644 --- a/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/ProjectSearchResponseDto.java +++ b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/ProjectSearchResponseDto.java @@ -16,7 +16,7 @@ @DTO public interface ProjectSearchResponseDto { - List getItemReferences(); + List getItemReferences(); - ProjectSearchResponseDto withItemReferences(List itemReferences); + ProjectSearchResponseDto withItemReferences(List itemReferences); } diff --git a/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchOccurrenceDto.java b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchOccurrenceDto.java new file mode 100644 index 00000000000..d4bd09a2df2 --- /dev/null +++ b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchOccurrenceDto.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.project.shared.dto; + +import org.eclipse.che.api.project.shared.SearchOccurrence; +import org.eclipse.che.dto.shared.DTO; + +/** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @author Vitalii Parfonov + */ +@DTO +public interface SearchOccurrenceDto extends SearchOccurrence { + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param score + * @return + */ + SearchOccurrenceDto withScore(float score); + + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param phrase + * @return + */ + SearchOccurrenceDto withPhrase(String phrase); + + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param endOffset + */ + SearchOccurrenceDto withEndOffset(int endOffset); + + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param startOffset + */ + SearchOccurrenceDto withStartOffset(int startOffset); + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param lineNumber + * @return + */ + SearchOccurrenceDto withLineNumber(int lineNumber); + + /** + * @see org.eclipse.che.api.project.shared.SearchOccurrence + * @param lineContent + * @return + */ + SearchOccurrenceDto withLineContent(String lineContent); + +} diff --git a/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchResultDto.java b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchResultDto.java new file mode 100644 index 00000000000..1a0da8f41bc --- /dev/null +++ b/wsagent/che-core-api-project-shared/src/main/java/org/eclipse/che/api/project/shared/dto/SearchResultDto.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.project.shared.dto; + +import org.eclipse.che.dto.shared.DTO; + +import java.util.List; + +/** + * @author Vitalii Parfonov + */ +@DTO +public interface SearchResultDto { + + ItemReference getItemReference(); + + void setItemReference(ItemReference itemReference); + + SearchResultDto withItemReference(ItemReference itemReference); + + List getSearchOccurrences(); + + void setSearchOccurrences(List searchOccurrences); + + SearchResultDto withSearchOccurrences(List searchOccurrences); +} diff --git a/wsagent/che-core-api-project/pom.xml b/wsagent/che-core-api-project/pom.xml index e2efc6c3e06..eb531f4e492 100644 --- a/wsagent/che-core-api-project/pom.xml +++ b/wsagent/che-core-api-project/pom.xml @@ -78,6 +78,11 @@ org.apache.lucene lucene-core + + org.apache.lucene + lucene-highlighter + 5.2.1 + org.apache.lucene lucene-queryparser diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/project/server/ProjectService.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/project/server/ProjectService.java index 56ca97f5a25..6593f918885 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/project/server/ProjectService.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/project/server/ProjectService.java @@ -44,6 +44,8 @@ import org.eclipse.che.api.project.shared.dto.MoveOptions; import org.eclipse.che.api.project.shared.dto.ProjectSearchRequestDto; import org.eclipse.che.api.project.shared.dto.ProjectSearchResponseDto; +import org.eclipse.che.api.project.shared.dto.SearchOccurrenceDto; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.project.shared.dto.TreeElement; import org.eclipse.che.api.vfs.VirtualFile; @@ -51,6 +53,7 @@ import org.eclipse.che.api.vfs.search.SearchResult; import org.eclipse.che.api.vfs.search.SearchResultEntry; import org.eclipse.che.api.vfs.search.Searcher; +import org.eclipse.che.api.vfs.search.impl.LuceneSearcher; import org.eclipse.che.api.workspace.shared.dto.NewProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; import org.eclipse.che.api.workspace.shared.dto.SourceStorageDto; @@ -79,6 +82,7 @@ import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; @@ -95,6 +99,8 @@ import static org.eclipse.che.api.project.shared.Constants.LINK_REL_CREATE_BATCH_PROJECTS; import static org.eclipse.che.api.project.shared.Constants.LINK_REL_CREATE_PROJECT; import static org.eclipse.che.api.project.shared.Constants.LINK_REL_GET_PROJECTS; +import static org.eclipse.che.api.vfs.util.ReadFileUtils.Line; +import static org.eclipse.che.api.vfs.util.ReadFileUtils.getLine; import static org.eclipse.che.dto.server.DtoFactory.newDto; /** @@ -875,22 +881,22 @@ public ItemReference getItem(@ApiParam(value = "Path to resource. Can be project @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Search for resources", notes = "Search for resources applying a number of search filters as query parameters", - response = ItemReference.class, + response = SearchResult.class, responseContainer = "List") @ApiResponses({@ApiResponse(code = 200, message = "OK"), @ApiResponse(code = 403, message = "User not authorized to call this operation"), @ApiResponse(code = 404, message = "Not found"), @ApiResponse(code = 409, message = "Conflict error"), @ApiResponse(code = 500, message = "Internal Server Error")}) - public List search(@ApiParam(value = "Path to resource, i.e. where to search?", required = true) + public List search(@ApiParam(value = "Path to resource, i.e. where to search?", required = true) @PathParam("path") String path, - @ApiParam(value = "Resource name") + @ApiParam(value = "Resource name") @QueryParam("name") String name, - @ApiParam(value = "Search keywords") + @ApiParam(value = "Search keywords") @QueryParam("text") String text, - @ApiParam(value = "Maximum items to display. If this parameter is dropped, there are no limits") + @ApiParam(value = "Maximum items to display. If this parameter is dropped, there are no limits") @QueryParam("maxItems") @DefaultValue("-1") int maxItems, - @ApiParam(value = "Skip count") + @ApiParam(value = "Skip count") @QueryParam("skipCount") int skipCount) throws NotFoundException, ForbiddenException, ConflictException, @@ -912,22 +918,52 @@ public List search(@ApiParam(value = "Path to resource, i.e. wher .setName(name) .setText(text) .setMaxItems(maxItems) - .setSkipCount(skipCount); + .setSkipCount(skipCount) + .setIncludePositions(true); final SearchResult result = searcher.search(expr); final List searchResultEntries = result.getResults(); - final List items = new ArrayList<>(searchResultEntries.size()); - final FolderEntry root = projectManager.getProjectsRoot(); + return prepareResults(searchResultEntries); + } - for (SearchResultEntry searchResultEntry : searchResultEntries) { - final VirtualFileEntry child = root.getChild(searchResultEntry.getFilePath()); + /** + * Prepare result for client, add additional information like line number and line content where found given text + * @param searchResultEntries + * @return + * @throws ServerException + */ + private List prepareResults(List searchResultEntries) throws ServerException { + List results = new ArrayList<>(searchResultEntries.size()); + FolderEntry root = projectManager.getProjectsRoot(); + + for (SearchResultEntry searchResultEntry : searchResultEntries) { + VirtualFileEntry child = root.getChild(searchResultEntry.getFilePath()); if (child != null && child.isFile()) { - items.add(injectFileLinks(asDto((FileEntry)child))); + ItemReference itemReference = injectFileLinks(asDto((FileEntry)child)); + File file = child.getVirtualFile().toIoFile(); + List datas = searchResultEntry.getData(); + List searchOccurrences = new ArrayList<>(datas.size()); + for (LuceneSearcher.OffsetData data : datas) { + try { + Line line = getLine(file, data.startOffset); + SearchOccurrenceDto searchOccurrenceDto = DtoFactory.getInstance().createDto(SearchOccurrenceDto.class) + .withPhrase(data.phrase) + .withScore(data.score) + .withStartOffset(data.startOffset) + .withEndOffset(data.endOffset) + .withLineNumber(line.getLineNumber()) + .withLineContent(line.getLineContent()); + searchOccurrences.add(searchOccurrenceDto); + } catch (IOException e) { + throw new ServerException(e); + } + } + SearchResultDto searchResultDto = DtoFactory.getInstance().createDto(SearchResultDto.class); + results.add(searchResultDto.withItemReference(itemReference).withSearchOccurrences(searchOccurrences)); } } - - return items; + return results; } @Inject diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/QueryExpression.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/QueryExpression.java index f0f3c4a916c..401a8329b7d 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/QueryExpression.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/QueryExpression.java @@ -10,13 +10,14 @@ *******************************************************************************/ package org.eclipse.che.api.vfs.search; -/** Container for parameters of query that executed by Searcher.*/ +/** Container for parameters of query that executed by Searcher. */ public class QueryExpression { private String name; private String path; private String text; private int skipCount; private int maxItems; + private boolean includePositions; /** Optional file path parameter. Only file with the specified path or children are included in result. */ public String getPath() { @@ -68,6 +69,18 @@ public QueryExpression setMaxItems(int maxItems) { return this; } + /** + * search for term position information or not. + */ + public boolean isIncludePositions() { + return includePositions; + } + + public QueryExpression setIncludePositions(boolean includePositions) { + this.includePositions = includePositions; + return this; + } + @Override public String toString() { return "QueryExpression{" + diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/SearchResultEntry.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/SearchResultEntry.java index 3d1265a06b7..b6bc644504c 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/SearchResultEntry.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/SearchResultEntry.java @@ -10,14 +10,27 @@ *******************************************************************************/ package org.eclipse.che.api.vfs.search; +import org.eclipse.che.api.vfs.search.impl.LuceneSearcher.OffsetData; + +import java.util.List; + /** * Single item in {@code SearchResult}. */ public class SearchResultEntry { private final String filePath; - public SearchResultEntry(String filePath) { + private final List data; + + + public SearchResultEntry(String filePath, List data) { this.filePath = filePath; + this.data = data; + } + + + public List getData() { + return data; } /** Path of file that matches the search criteria. */ diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/impl/LuceneSearcher.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/impl/LuceneSearcher.java index 46898e10a35..0616d1b3e7b 100644 --- a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/impl/LuceneSearcher.java +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/search/impl/LuceneSearcher.java @@ -10,15 +10,20 @@ *******************************************************************************/ package org.eclipse.che.api.vfs.search.impl; +import com.google.common.io.CharStreams; + import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.Tokenizer; import org.apache.lucene.analysis.core.LowerCaseFilter; import org.apache.lucene.analysis.core.WhitespaceTokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; +import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.StringField; import org.apache.lucene.document.TextField; +import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.Term; @@ -33,6 +38,8 @@ import org.apache.lucene.search.SearcherFactory; import org.apache.lucene.search.SearcherManager; import org.apache.lucene.search.TopDocs; +import org.apache.lucene.search.highlight.QueryScorer; +import org.apache.lucene.search.highlight.TokenSources; import org.apache.lucene.store.Directory; import org.apache.lucene.util.IOUtils; import org.eclipse.che.api.core.ForbiddenException; @@ -53,6 +60,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; +import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -206,10 +215,59 @@ public SearchResult search(QueryExpression query) throws ServerException { final int totalHitsNum = topDocs.totalHits; List results = newArrayList(); + List offsetData = Collections.emptyList(); for (int i = 0; i < topDocs.scoreDocs.length; i++) { ScoreDoc scoreDoc = topDocs.scoreDocs[i]; - String filePath = luceneSearcher.doc(scoreDoc.doc).getField(PATH_FIELD).stringValue(); - results.add(new SearchResultEntry(filePath)); + int docId = scoreDoc.doc; + Document doc = luceneSearcher.doc(docId); + if (query.isIncludePositions()) { + offsetData = new ArrayList<>(); + String txt = doc.get(TEXT_FIELD); + if (txt != null) { + IndexReader reader = luceneSearcher.getIndexReader(); + + TokenStream tokenStream = TokenSources.getTokenStream(TEXT_FIELD, + reader.getTermVectors(docId), + txt, + luceneIndexWriter.getAnalyzer(), + -1); + + CharTermAttribute termAtt = tokenStream.addAttribute(CharTermAttribute.class); + OffsetAttribute offsetAtt = tokenStream.addAttribute(OffsetAttribute.class); + + QueryScorer queryScorer = new QueryScorer(luceneQuery); + //TODO think about this constant + queryScorer.setMaxDocCharsToAnalyze(1_000_000); + TokenStream newStream = queryScorer.init(tokenStream); + if (newStream != null) { + tokenStream = newStream; + } + queryScorer.startFragment(null); + + tokenStream.reset(); + + int startOffset, endOffset; + //TODO think about this constant + for (boolean next = tokenStream.incrementToken(); next && (offsetAtt.startOffset() < 1_000_000); + next = tokenStream.incrementToken()) { + startOffset = offsetAtt.startOffset(); + endOffset = offsetAtt.endOffset(); + + if ((endOffset > txt.length()) || (startOffset > txt.length())) { + throw new ServerException( + "Token " + termAtt.toString() + " exceeds length of provided text size " + txt.length()); + } + + float res = queryScorer.getTokenScore(); + if (res > 0.0F && startOffset <= endOffset) { + String tokenText = txt.substring(startOffset, endOffset); + offsetData.add(new OffsetData(tokenText, startOffset, endOffset, docId, res)); + } + } + } + } + String filePath = doc.getField(PATH_FIELD).stringValue(); + results.add(new SearchResultEntry(filePath, offsetData)); } final long elapsedTimeMillis = System.currentTimeMillis() - startTime; @@ -276,6 +334,7 @@ private ScoreDoc skipScoreDocs(IndexSearcher luceneSearcher, Query luceneQuery, scoreDoc = topDocs.scoreDocs[lastScoreDocIndex]; } + return scoreDoc; } @@ -383,7 +442,11 @@ protected Document createDocument(VirtualFile virtualFile, Reader reader) throws doc.add(new StringField(PATH_FIELD, virtualFile.getPath().toString(), Field.Store.YES)); doc.add(new TextField(NAME_FIELD, virtualFile.getName(), Field.Store.YES)); if (reader != null) { - doc.add(new TextField(TEXT_FIELD, reader)); + try { + doc.add(new TextField(TEXT_FIELD, CharStreams.toString(reader), Field.Store.YES)); + } catch (IOException e) { + throw new ServerException(e.getLocalizedMessage(), e); + } } return doc; } @@ -396,4 +459,23 @@ private boolean shouldIndexContent(VirtualFile virtualFile) { } return true; } + + public static class OffsetData { + + public String phrase; + public int startOffset; + public int endOffset; + public int docId; + public float score; + + public OffsetData(String phrase, int startOffset, int endOffset, int docId, float score) { + super(); + this.phrase = phrase; + this.startOffset = startOffset; + this.endOffset = endOffset; + this.docId = docId; + this.score = score; + } + + } } diff --git a/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/util/ReadFileUtils.java b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/util/ReadFileUtils.java new file mode 100644 index 00000000000..2cffb8fc0cc --- /dev/null +++ b/wsagent/che-core-api-project/src/main/java/org/eclipse/che/api/vfs/util/ReadFileUtils.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.vfs.util; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; +import java.io.RandomAccessFile; + +import static java.lang.Character.toChars; +import static java.lang.System.lineSeparator; + +/** + * @author Vitalii Parfonov + */ + +public class ReadFileUtils { + + + /** + * Read given file and return {@link Line} by given offset + * @param file + * @param offset + * @return + * @throws IOException + */ + public static Line getLine(File file, int offset) throws IOException { + if (file.length() < offset) { + throw new IOException("File is not long enough"); + } + try (LineNumberReader r = new LineNumberReader(new FileReader(file))) { + + int count = 0; + int read = 0; + int startPosition = 0; + while (read != -1 && count < offset) { + read = r.read(); + final char[] chars = toChars(read); + if (lineSeparator().equals(new String(chars))) { + startPosition = count; + } + count++; + } + + if (count == offset) { + int lineNumber = r.getLineNumber(); + String s = r.readLine(); + int t = offset + s.length(); + int len = t - startPosition; + r.close(); + + RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r"); + if (lineNumber > 0) { //skip previous new line symbol + startPosition++; + len--; + } + byte [] chars = new byte[len]; + randomAccessFile.seek(startPosition); + randomAccessFile.read(chars, 0, len); + return new Line(lineNumber, new String(chars)); + } else { + throw new IOException("File is not long enough"); + } + } + + } + + /** + * Describe line number and it content + */ + public static class Line { + private int lineNumber; + private String lineContent; + + Line(int lineNumber, String lineContent) { + this.lineNumber = lineNumber; + this.lineContent = lineContent; + } + + + /** + * @return line content + */ + public String getLineContent() { + return lineContent; + } + + /** + * @return number of line in the file keep in mind first line will be 0 not 1 + */ + public int getLineNumber() { + return lineNumber; + } + } +} diff --git a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java index d901ec78865..2877ec755ad 100644 --- a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java +++ b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/project/server/ProjectServiceTest.java @@ -43,6 +43,7 @@ import org.eclipse.che.api.project.shared.dto.CopyOptions; import org.eclipse.che.api.project.shared.dto.ItemReference; import org.eclipse.che.api.project.shared.dto.MoveOptions; +import org.eclipse.che.api.project.shared.dto.SearchResultDto; import org.eclipse.che.api.project.shared.dto.SourceEstimation; import org.eclipse.che.api.project.shared.dto.TreeElement; import org.eclipse.che.api.user.server.spi.UserDao; @@ -52,6 +53,7 @@ import org.eclipse.che.api.vfs.impl.file.FileTreeWatcher; import org.eclipse.che.api.vfs.impl.file.FileWatcherNotificationHandler; import org.eclipse.che.api.vfs.impl.file.LocalVirtualFileSystemProvider; +import org.eclipse.che.api.vfs.search.SearchResult; import org.eclipse.che.api.vfs.search.impl.FSLuceneSearcherProvider; import org.eclipse.che.api.vfs.watcher.FileWatcherManager; import org.eclipse.che.api.workspace.shared.dto.ProjectConfigDto; @@ -1754,11 +1756,11 @@ public void testSearchByName() throws Exception { "http://localhost:8080/api/project/search/my_project?name=test.txt", "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 2); Set paths = new LinkedHashSet<>(2); - for (ItemReference itemReference : result) { - paths.add(itemReference.getPath()); + for (SearchResultDto resultDto : result) { + paths.add(resultDto.getItemReference().getPath()); } Assert.assertTrue(paths.contains("/my_project/a/b/test.txt")); Assert.assertTrue(paths.contains("/my_project/x/y/test.txt")); @@ -1776,10 +1778,10 @@ public void testSearchByText() throws Exception { "http://localhost:8080/api/project/search/my_project?text=searchhit", "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 2); Set paths = new LinkedHashSet<>(1); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/__test.txt")); } @@ -1795,10 +1797,10 @@ public void testSearchByTextWhenFileWasNotIndexed() throws Exception { "http://localhost:8080/api/project/search/my_project?text=searchhit", "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 1); Set paths = new LinkedHashSet<>(1); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/__test.txt")); Assert.assertFalse(paths.contains("/my_project/" + EXCLUDE_SEARCH_PATH + "/_test")); } @@ -1826,10 +1828,10 @@ public void testSearchParticularSequenceWords() throws Exception { launcher.service(GET, "http://localhost:8080/api/project/search/my_project" + queryToSearch, "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 1); Set paths = new LinkedHashSet<>(1); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/containsSearchText.txt")); } @@ -1856,10 +1858,10 @@ public void testSearchParticularSequenceWordsWithAnyEnding() throws Exception { launcher.service(GET, "http://localhost:8080/api/project/search/my_project" + queryToSearch, "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 2); Set paths = new LinkedHashSet<>(2); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/containsSearchText.txt")); Assert.assertTrue(paths.contains("/my_project/a/b/containsSearchTextAlso.txt")); Assert.assertFalse(paths.contains("/my_project/c/notContainsSearchText.txt")); @@ -1885,10 +1887,10 @@ public void testSearchWordWithAnyEnding() throws Exception { launcher.service(GET, "http://localhost:8080/api/project/search/my_project" + queryToSearch, "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 2); Set paths = new LinkedHashSet<>(2); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/containsSearchText.txt")); Assert.assertTrue(paths.contains("/my_project/a/b/containsSearchTextAlso.txt")); Assert.assertFalse(paths.contains("/my_project/c/notContainsSearchText.txt")); @@ -1914,10 +1916,10 @@ public void testSearchTextWhenExcludeSomeText() throws Exception { launcher.service(GET, "http://localhost:8080/api/project/search/my_project" + queryToSearch, "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 1); Set paths = new LinkedHashSet<>(1); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/containsSearchText.txt")); Assert.assertFalse(paths.contains("/my_project/b/notContainsSearchText.txt")); Assert.assertFalse(paths.contains("/my_project/c/alsoContainsSearchText")); @@ -1943,10 +1945,10 @@ public void testSearchTextWithEscapedCharachters() throws Exception { ContainerResponse response = launcher.service(GET, "http://localhost:8080/api/project/search/my_project" + queryToSearch, "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 1); Set paths = new LinkedHashSet<>(1); - paths.addAll(result.stream().map(ItemReference::getPath).collect(Collectors.toList())); + paths.addAll(result.stream().map((SearchResultDto t) -> t.getItemReference().getPath()).collect(Collectors.toList())); Assert.assertTrue(paths.contains("/my_project/x/y/test.txt")); } @@ -1962,11 +1964,11 @@ public void testSearchByNameAndText() throws Exception { "http://localhost:8080/api/project/search/my_project?text=test&name=test.txt", "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 2); assertEqualsNoOrder(new Object[]{ - result.get(0).getPath(), - result.get(1).getPath() + result.get(0).getItemReference().getPath(), + result.get(1).getItemReference().getPath() }, new Object[]{ "/my_project/a/b/test.txt", @@ -1986,9 +1988,9 @@ public void testSearchFromWSRoot() throws Exception { "http://localhost:8080/api/project/search/?text=test&name=test.txt", "http://localhost:8080/api", null, null, null); assertEquals(response.getStatus(), 200, "Error: " + response.getEntity()); - List result = (List)response.getEntity(); + List result = (List)response.getEntity(); assertEquals(result.size(), 1); - Assert.assertTrue(result.get(0).getPath().equals("/my_project/c/test.txt")); + Assert.assertTrue(result.get(0).getItemReference().getPath().equals("/my_project/c/test.txt")); } private void validateFileLinks(ItemReference item) { diff --git a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/FSLuceneSearcherTest.java b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/FSLuceneSearcherTest.java index 11a5eb8633a..58a68c288f1 100644 --- a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/FSLuceneSearcherTest.java +++ b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/FSLuceneSearcherTest.java @@ -46,8 +46,9 @@ public class FSLuceneSearcherTest { private static final String[] TEST_CONTENT = { "Apollo set several major human spaceflight milestones", "Maybe you should think twice", - "To be or not to be", - "In early 1961, direct ascent was generally the mission mode in favor at NASA" + "To be or not to be beeeee lambergeeene", + "In early 1961, direct ascent was generally the mission mode in favor at NASA", + "Time to think" }; private File indexDirectory; @@ -191,6 +192,37 @@ public void searchesByTextAndFileName() throws Exception { assertEquals(newArrayList("/folder/xxx.txt"), paths); } + @Test + public void searchesByFullTextAndFileName() throws Exception { + VirtualFileSystem virtualFileSystem = virtualFileSystem(); + VirtualFile folder = virtualFileSystem.getRoot().createFolder("folder"); + folder.createFile("xxx.txt", TEST_CONTENT[2]); + folder.createFile("zzz.txt", TEST_CONTENT[2]); + searcher.init(virtualFileSystem); + + SearchResult result = searcher.search(new QueryExpression().setText("*be*").setName("xxx.txt").setIncludePositions(true)); + List paths = result.getFilePaths(); + assertEquals(newArrayList("/folder/xxx.txt"), paths); + assertEquals(result.getResults().get(0).getData().size(), 4); + + } + + + @Test + public void searchesByFullTextAndFileName2() throws Exception { + VirtualFileSystem virtualFileSystem = virtualFileSystem(); + VirtualFile folder = virtualFileSystem.getRoot().createFolder("folder"); + folder.createFile("xxx.txt", TEST_CONTENT[2]); + folder.createFile("zzz.txt", TEST_CONTENT[4]); + searcher.init(virtualFileSystem); + + SearchResult result = searcher.search(new QueryExpression().setText("*to*").setIncludePositions(true)); + List paths = result.getFilePaths(); + assertEquals(paths.size(), 2); + assertEquals(result.getResults().get(0).getData().size(), 2); + + } + @DataProvider public Object[][] searchByName() { return new Object[][]{ @@ -200,7 +232,7 @@ public Object[][] searchByName() { {"file name.txt", "file name"}, {"prefixFileName.txt", "prefixF*"}, {"name.with.dot.txt", "name.With.Dot.txt"}, - }; + }; } @Test(dataProvider = "searchByName") @@ -208,11 +240,11 @@ public void searchFileByName(String fileName, String searchedFileName) throws Ex VirtualFileSystem virtualFileSystem = virtualFileSystem(); VirtualFile folder = virtualFileSystem.getRoot().createFolder("parent/child"); VirtualFile folder2 = virtualFileSystem.getRoot().createFolder("folder2"); - folder.createFile(NameGenerator.generate(null,10), TEST_CONTENT[3]); + folder.createFile(NameGenerator.generate(null, 10), TEST_CONTENT[3]); folder.createFile(fileName, TEST_CONTENT[2]); - folder.createFile(NameGenerator.generate(null,10), TEST_CONTENT[1]); - folder2.createFile(NameGenerator.generate(null,10), TEST_CONTENT[2]); - folder2.createFile(NameGenerator.generate(null,10), TEST_CONTENT[2]); + folder.createFile(NameGenerator.generate(null, 10), TEST_CONTENT[1]); + folder2.createFile(NameGenerator.generate(null, 10), TEST_CONTENT[2]); + folder2.createFile(NameGenerator.generate(null, 10), TEST_CONTENT[2]); searcher.init(virtualFileSystem); List paths = searcher.search(new QueryExpression().setName(searchedFileName)).getFilePaths(); @@ -284,7 +316,7 @@ public void excludesFilesFromIndexWithFilter() throws Exception { @Test public void limitsNumberOfSearchResultsWhenMaxItemIsSet() throws Exception { VirtualFileSystem virtualFileSystem = virtualFileSystem(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 125; i++) { virtualFileSystem.getRoot().createFile(String.format("file%02d", i), TEST_CONTENT[i % TEST_CONTENT.length]); } searcher.init(virtualFileSystem); @@ -298,18 +330,17 @@ public void limitsNumberOfSearchResultsWhenMaxItemIsSet() throws Exception { @Test public void generatesQueryExpressionForRetrievingNextPageOfResults() throws Exception { VirtualFileSystem virtualFileSystem = virtualFileSystem(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 125; i++) { virtualFileSystem.getRoot().createFile(String.format("file%02d", i), TEST_CONTENT[i % TEST_CONTENT.length]); } searcher.init(virtualFileSystem); SearchResult result = searcher.search(new QueryExpression().setText("spaceflight").setMaxItems(7)); - assertEquals(25, result.getTotalHits()); + assertEquals(result.getTotalHits(), 25); Optional optionalNextPageQueryExpression = result.getNextPageQueryExpression(); assertTrue(optionalNextPageQueryExpression.isPresent()); - QueryExpression nextPageQueryExpression = optionalNextPageQueryExpression.get(); assertEquals("spaceflight", nextPageQueryExpression.getText()); assertEquals(7, nextPageQueryExpression.getSkipCount()); @@ -319,19 +350,19 @@ public void generatesQueryExpressionForRetrievingNextPageOfResults() throws Exce @Test public void retrievesSearchResultWithPages() throws Exception { VirtualFileSystem virtualFileSystem = virtualFileSystem(); - for (int i = 0; i < 100; i++) { + for (int i = 0; i < 125; i++) { virtualFileSystem.getRoot().createFile(String.format("file%02d", i), TEST_CONTENT[i % TEST_CONTENT.length]); } searcher.init(virtualFileSystem); SearchResult firstPage = searcher.search(new QueryExpression().setText("spaceflight").setMaxItems(8)); - assertEquals(8, firstPage.getFilePaths().size()); + assertEquals(firstPage.getFilePaths().size(),8); QueryExpression nextPageQueryExpression = firstPage.getNextPageQueryExpression().get(); nextPageQueryExpression.setMaxItems(100); SearchResult lastPage = searcher.search(nextPageQueryExpression); - assertEquals(17, lastPage.getFilePaths().size()); + assertEquals(lastPage.getFilePaths().size(), 17); assertTrue(Collections.disjoint(firstPage.getFilePaths(), lastPage.getFilePaths())); } diff --git a/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/ReadFileUtilsTest.java b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/ReadFileUtilsTest.java new file mode 100644 index 00000000000..2ccc8877e90 --- /dev/null +++ b/wsagent/che-core-api-project/src/test/java/org/eclipse/che/api/vfs/search/impl/ReadFileUtilsTest.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2012-2017 Codenvy, S.A. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + *******************************************************************************/ +package org.eclipse.che.api.vfs.search.impl; + +import org.eclipse.che.api.vfs.util.ReadFileUtils; +import org.testng.annotations.Test; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.testng.Assert.assertEquals; + +public class ReadFileUtilsTest { + + String TEST_CONTENT2 = "Apollo set several major human spaceflight milestones,\n" + + "Maybe you should think twice,\n" + + "To be or not to be beeeee lambergeeene,\n" + + "In early 1961, direct ascent was generally the mission mode in favor at NASA,\n" + + "Time to think"; + + @Test + public void genLineByOffset() throws Exception { + final Path tempFile = Files.createTempFile("my", "_test"); + Files.write(tempFile, TEST_CONTENT2.getBytes()); + ReadFileUtils.Line line = ReadFileUtils.getLine(tempFile.toFile(), 10); + assertEquals(line.getLineNumber(), 0); + assertEquals(line.getLineContent(), "Apollo set several major human spaceflight milestones,"); + + line = ReadFileUtils.getLine(tempFile.toFile(), 85); + assertEquals(line.getLineNumber(), 2); + assertEquals(line.getLineContent(), "To be or not to be beeeee lambergeeene,"); + + line = ReadFileUtils.getLine(tempFile.toFile(), 130); + assertEquals(line.getLineNumber(), 3); + assertEquals(line.getLineContent(), "In early 1961, direct ascent was generally the mission mode in favor at NASA,"); + } + + + @Test(expectedExceptions = IOException.class, + expectedExceptionsMessageRegExp = "File is not long enough") + public void genLineByOffsetShouldFail() throws Exception { + final Path tempFile = Files.createTempFile("my", "_test"); + Files.write(tempFile, TEST_CONTENT2.getBytes()); + ReadFileUtils.getLine(tempFile.toFile(), 10000); + + } + + + + + + +}