aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/silversearcher/silversearcherparser.cpp
blob: 1a2f5e55e87e0ca70f7e8ef447082975f3cce768 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// Copyright (C) 2017 Przemyslaw Gorszkowski <pgorszkowski@gmail.com>.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "silversearcherparser.h"

using namespace Utils;

namespace SilverSearcher {

/*
// Example output (searching for "ab"):

:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/imagecontainer.cpp
149;60 2:    static const bool dontUseSharedMemory = qEnvironmentVariableIsSet("DESIGNER_DONT_USE_SHARED_MEMORY");
198;65 2:            qCInfo(imageContainerDebug()) << Q_FUNC_INFO << "Not able to create image:" << imageWidth << imageHeight << imageFormat;

:/home/jarek/dev/creator-master/src/libs/qmlpuppetcommunication/container/propertyabstractcontainer.cpp
4;18 2:#include "propertyabstractcontainer.h"
10;8 2,35 2:PropertyAbstractContainer::PropertyAbstractContainer()
*/

static QStringView nextLine(QStringView *remainingInput)
{
    const int newLinePos = remainingInput->indexOf('\n');
    if (newLinePos < 0) {
        QStringView ret = *remainingInput;
        *remainingInput = QStringView();
        return ret;
    }
    QStringView ret = remainingInput->left(newLinePos);
    *remainingInput = remainingInput->mid(newLinePos + 1);
    return ret;
}

static bool parseNumber(QStringView numberString, int *number)
{
    for (const auto &c : numberString) {
        if (!c.isDigit())
            return false;
    }
    bool ok = false;
    int parsedNumber = numberString.toInt(&ok);
    if (ok)
        *number = parsedNumber;
    return ok;
}

static bool parseLineNumber(QStringView *remainingInput, int *lineNumber)
{
    const int lineNumberDelimiterPos = remainingInput->indexOf(';');
    if (lineNumberDelimiterPos < 0)
        return false;

    if (!parseNumber(remainingInput->left(lineNumberDelimiterPos), lineNumber))
        return false;

    *remainingInput = remainingInput->mid(lineNumberDelimiterPos + 1);
    return true;
}

static bool parseLineHit(QStringView hitString, QPair<int, int> *hit)
{
    const int hitSpaceDelimiterPos = hitString.indexOf(' ');
    if (hitSpaceDelimiterPos < 0)
        return false;

    int hitStart = -1;
    if (!parseNumber(hitString.left(hitSpaceDelimiterPos), &hitStart))
        return false;

    int hitLength = -1;
    if (!parseNumber(hitString.mid(hitSpaceDelimiterPos + 1), &hitLength))
        return false;

    *hit = {hitStart, hitLength};
    return true;
}

static bool parseLineHits(QStringView *remainingInput, QList<QPair<int, int>> *hits)
{
    const int hitsDelimiterPos = remainingInput->indexOf(':');
    if (hitsDelimiterPos < 0)
        return false;

    const QStringView hitsString = remainingInput->left(hitsDelimiterPos);
    const QList<QStringView> hitStrings = hitsString.split(',', Qt::SkipEmptyParts);
    for (const auto hitString : hitStrings) {
        QPair<int, int> hit;
        if (!parseLineHit(hitString, &hit))
            return false;
        hits->append(hit);
    }
    *remainingInput = remainingInput->mid(hitsDelimiterPos + 1);
    return true;
}

SearchResultItems parse(const QFuture<void> &future, const QString &input,
                        const std::optional<QRegularExpression> &regExp, FilePath *lastFilePath)
{
    QTC_ASSERT(lastFilePath, return {});
    SearchResultItems items;

    QStringView remainingInput(input);
    while (true) {
        if (future.isCanceled())
            return {};

        if (remainingInput.isEmpty())
            break;

        const QStringView filePathLine = nextLine(&remainingInput);
        if (filePathLine.isEmpty()) {
            *lastFilePath = {}; // Clear the parser state
            continue;
        }

        if (filePathLine.startsWith(':'))
            *lastFilePath = FilePath::fromPathPart(filePathLine.mid(1));

        while (true) {
            QStringView hitLine = nextLine(&remainingInput);
            if (hitLine.isEmpty())
                break;
            int lineNumber = -1;
            if (!parseLineNumber(&hitLine, &lineNumber))
                break;

            // List of pairs <hit start pos, hit length>
            QList<QPair<int, int>> hits;
            if (!parseLineHits(&hitLine, &hits))
                break;

            SearchResultItem item;
            item.setFilePath(*lastFilePath);
            item.setDisplayText(hitLine.toString());
            item.setUseTextEditorFont(true);
            for (const QPair<int, int> &hit : hits) {
                item.setMainRange(lineNumber, hit.first, hit.second);
                item.setUserData(
                    regExp ? regExp->match(hitLine.mid(hit.first, hit.second)).capturedTexts()
                           : QVariant());
                items.append(item);
            }
        }
    }
    return items;
}

SearchResultItems parse(const QString &input, const std::optional<QRegularExpression> &regExp)
{
    QPromise<void> promise;
    promise.start();
    FilePath dummy;
    return SilverSearcher::parse(promise.future(), input, regExp, &dummy);
}

} // namespace SilverSearcher