* @brief Grep-like content search across the read roots; literal or regex. */
| 523 | * @brief Grep-like content search across the read roots; literal or regex. |
| 524 | */ |
| 525 | QJsonObject AI::FileSandbox::search(const QJsonObject& args) const |
| 526 | { |
| 527 | const auto query = args.value(QStringLiteral("query")).toString(); |
| 528 | if (query.isEmpty()) |
| 529 | return failure(QStringLiteral("missing_query")); |
| 530 | |
| 531 | const bool isRegex = args.value(QStringLiteral("isRegex")).toBool(false); |
| 532 | const auto pattern = isRegex ? query : QRegularExpression::escape(query); |
| 533 | QRegularExpression needle(pattern, QRegularExpression::CaseInsensitiveOption); |
| 534 | if (!needle.isValid()) |
| 535 | return failure(QStringLiteral("bad_regex"), needle.errorString()); |
| 536 | |
| 537 | const auto files = gatherSearchFiles(readRoots(), kMaxSearchFiles); |
| 538 | bool truncated = files.size() >= kMaxSearchFiles; |
| 539 | QJsonArray hits; |
| 540 | for (const auto& file : files) { |
| 541 | searchFile(file, workspaceRoot(), needle, hits, truncated); |
| 542 | if (hits.size() >= kMaxSearchHits) { |
| 543 | truncated = true; |
| 544 | break; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | QJsonObject out; |
| 549 | out[QStringLiteral("ok")] = true; |
| 550 | out[QStringLiteral("query")] = query; |
| 551 | out[QStringLiteral("filesScanned")] = files.size(); |
| 552 | out[QStringLiteral("count")] = hits.size(); |
| 553 | out[QStringLiteral("hits")] = hits; |
| 554 | out[QStringLiteral("truncated")] = truncated; |
| 555 | return out; |
| 556 | } |
| 557 | |
| 558 | //-------------------------------------------------------------------------------------------------- |
| 559 | // write / append |
nothing calls this directly
no test coverage detected