diff --git a/core/src/main/scala/codesearch/core/search/Search.scala b/core/src/main/scala/codesearch/core/search/Search.scala index 73c98cd..cdb9297 100644 --- a/core/src/main/scala/codesearch/core/search/Search.scala +++ b/core/src/main/scala/codesearch/core/search/Search.scala @@ -10,14 +10,14 @@ import cats.syntax.option._ import codesearch.core.config.{Config, SnippetConfig} import codesearch.core.index.directory.СindexDirectory import codesearch.core.index.repository.Extensions +import codesearch.core.regex.RegexConstructor import codesearch.core.search.Search.{CSearchPage, CSearchResult, CodeSnippet, Package, PackageResult, snippetConfig} import codesearch.core.search.SnippetsGrouper.SnippetInfo +import codesearch.core.syntax.stream._ import codesearch.core.util.Helper.readFileAsync import fs2.{Pipe, Stream} import io.chrisdavenport.log4cats.SelfAwareStructuredLogger import io.chrisdavenport.log4cats.slf4j.Slf4jLogger -import codesearch.core.regex.RegexConstructor -import codesearch.core.syntax.stream._ import scala.sys.process.Process @@ -34,16 +34,14 @@ trait Search { def search(request: SearchRequest): IO[CSearchPage] = { for { lines <- csearch(request) - snippetsInfo = Stream .emits(lines) - .through(SnippetsGrouper.groupLines(snippetConfig)) + .through(SnippetsGrouper.groupLines(snippetConfig, request.query)) - filteredSnippetsInfo = if (request.withoutTests) + filteredSnippetsInfo: Stream[fs2.Pure, SnippetsGrouper.SnippetInfo] = if (request.withoutTests) snippetsInfo.filterNot(snippetInfo => isTestInPath(snippetInfo.filePath)) else snippetsInfo - results <- filteredSnippetsInfo .drop(snippetConfig.pageSize * (request.page - 1)) .take(snippetConfig.pageSize) @@ -52,7 +50,8 @@ trait Search { .compile .toList totalMatches = filteredSnippetsInfo.fold(0)((total, snippet) => total + snippet.totalMatches).compile.toList.last - } yield CSearchPage(results.sortBy(_.pack.name), totalMatches) + snippetCount = filteredSnippetsInfo.fold(0)((total, _) => total + 1).compile.toList.last + } yield CSearchPage(results.sortBy(_.pack.name), totalMatches, snippetCount) } /** @@ -167,7 +166,8 @@ object Search { */ final case class CSearchPage( data: Seq[PackageResult], - total: Int + total: Int, + snippetCount: Int ) /** diff --git a/core/src/main/scala/codesearch/core/search/SnippetsGrouper.scala b/core/src/main/scala/codesearch/core/search/SnippetsGrouper.scala index 680dba3..2e5b3c8 100644 --- a/core/src/main/scala/codesearch/core/search/SnippetsGrouper.scala +++ b/core/src/main/scala/codesearch/core/search/SnippetsGrouper.scala @@ -15,7 +15,7 @@ object SnippetsGrouper { */ case class SnippetInfo(filePath: String, lines: NonEmptyVector[Int], totalMatches: Int) - private case class ResultRow(path: String, lineNumber: Int) + private case class ResultRow(path: String, lineNumber: Int, matchedString: Array[String]) /** * Transforms raw search output in format `filePath:lineNumber:matchedString` to snippets grouping matched lines @@ -23,31 +23,38 @@ object SnippetsGrouper { * * @param config config for creating a snippet */ - def groupLines[F[_]](config: SnippetConfig): Pipe[F, String, SnippetInfo] = { lines => + def groupLines[F[_]](config: SnippetConfig, query: String): Pipe[F, String, SnippetInfo] = { lines => for { (_, resultRows) <- lines.map { row => - val Array(path, lineNumber) = row.split(":").take(2) //filePath:lineNumber:matchedString - ResultRow(path, lineNumber.toInt) + val result = row.split(":") // filePath:lineNumber:matchedString + ResultRow(result(0), result(1).toInt, result.drop(2)) }.groupAdjacentBy(_.path) snippet <- Stream.emits { - groupRowsToSnippets(config)(resultRows) + groupRowsToSnippets(config, query)(resultRows) } } yield snippet } - private def groupRowsToSnippets(config: SnippetConfig)(rows: Chunk[ResultRow]): Seq[SnippetInfo] = { + private def groupRowsToSnippets(config: SnippetConfig, query: String)(rows: Chunk[ResultRow]): Seq[SnippetInfo] = { rows.foldLeft(Vector.empty[SnippetInfo]) { (snippets, row) => + val count = math.max(countSubstring(row.matchedString, query), 1) snippets.lastOption match { case Some(snippet) => if (row.lineNumber < snippet.lines.last + config.linesAfter) { snippets.init :+ snippet.copy(lines = snippet.lines :+ row.lineNumber, - totalMatches = snippet.totalMatches + 1) + totalMatches = snippet.totalMatches + count) } else { - snippets :+ SnippetInfo(row.path, NonEmptyVector.one(row.lineNumber), 1) + snippets :+ SnippetInfo(row.path, NonEmptyVector.one(row.lineNumber), count) } case None => - snippets :+ SnippetInfo(row.path, NonEmptyVector.one(row.lineNumber), 1) + snippets :+ SnippetInfo(row.path, NonEmptyVector.one(row.lineNumber), count) } } } + + private def countSubstring(str: Array[String], sub: String): Int = { + str.foldLeft(0) { (count, str) => + count + str.sliding(sub.length).count(_ == sub) + } + } } diff --git a/core/src/test/scala/codesearch/core/search/SnippetsGrouperSpec.scala b/core/src/test/scala/codesearch/core/search/SnippetsGrouperSpec.scala index dcbf868..28f3594 100644 --- a/core/src/test/scala/codesearch/core/search/SnippetsGrouperSpec.scala +++ b/core/src/test/scala/codesearch/core/search/SnippetsGrouperSpec.scala @@ -20,7 +20,7 @@ class SnippetsGrouperSpec extends WordSpec with Matchers { val snippets: List[SnippetInfo] = fs2.Stream .emits(matchedLines) .through( - SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5)) + SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5), "import") ) .compile .toList @@ -44,7 +44,7 @@ class SnippetsGrouperSpec extends WordSpec with Matchers { val snippets: List[SnippetInfo] = fs2.Stream .emits(matchedLines) .through( - SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5)) + SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5), "deriving") ) .compile .toList @@ -73,7 +73,7 @@ class SnippetsGrouperSpec extends WordSpec with Matchers { val snippets: List[SnippetInfo] = fs2.Stream .emits(matchedLines) .through( - SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5)) + SnippetsGrouper.groupLines(SnippetConfig(pageSize = 30, linesBefore = 5, linesAfter = 5), "import") ) .compile .toList diff --git a/web-server/app/codesearch/web/controllers/SearchController.scala b/web-server/app/codesearch/web/controllers/SearchController.scala index 862344b..26c3a10 100644 --- a/web-server/app/codesearch/web/controllers/SearchController.scala +++ b/web-server/app/codesearch/web/controllers/SearchController.scala @@ -65,7 +65,7 @@ trait SearchController[V <: DefaultTable] { self: InjectedController => ) db.updated.flatMap { updated => searchEngine.search(searchRequest) map { - case CSearchPage(results, total) => + case CSearchPage(results, total, snippetcount) => Ok( views.html.searchResults( updated = TimeAgo.using(updated.getTime), @@ -81,7 +81,8 @@ trait SearchController[V <: DefaultTable] { self: InjectedController => page = searchRequest.page, totalMatches = total, callURI = searchRequest.callURI(host).toString, - lang = lang + lang = lang, + snippetCount = snippetcount ) ) } unsafeToFuture diff --git a/web-server/app/views/pagination.scala.html b/web-server/app/views/pagination.scala.html index 2756750..a342306 100644 --- a/web-server/app/views/pagination.scala.html +++ b/web-server/app/views/pagination.scala.html @@ -1,6 +1,6 @@ @import codesearch.core.search.Search.snippetConfig -@(page: Int = 0, totalMatches: Int, callURI: String = "#") +@(page: Int = 0, totalMatches: Int, callURI: String = "#", snippetCount: Int = 0) @if(page != 0) { @@ -14,7 +14,7 @@ @for(curPage <- 1 to 10) { - @{if ((curPage - 1) * snippetConfig.pageSize + 1 <= math.min(1000, totalMatches)) { + @{if ((curPage - 1) * snippetConfig.pageSize + 1 <= math.min(1000, snippetCount)) {
  • @@ -22,8 +22,8 @@
  • } } } -
  • - +
  • + Next diff --git a/web-server/app/views/searchResults.scala.html b/web-server/app/views/searchResults.scala.html index 09b385d..f6f0165 100644 --- a/web-server/app/views/searchResults.scala.html +++ b/web-server/app/views/searchResults.scala.html @@ -14,7 +14,8 @@ page: Int, totalMatches: Int, callURI: String, - lang: String + lang: String, + snippetCount: Int ) @headExtra = { @@ -26,5 +27,5 @@ @wrapper(s"Codesearch | $lang", headExtra) { @searchBox(s"/$lang/search", query, filter, filePath, insensitive, space, precise, sources, withoutTests) @resultFrame(lang, insensitive, space, precise, query, updated, packages, totalMatches) - @pagination(page, totalMatches, callURI) + @pagination(page, totalMatches, callURI, snippetCount: Int) }