kateprojectworker.cpp 20.8 KB
Newer Older
Christoph Cullmann's avatar
Christoph Cullmann committed
1
2
/*  This file is part of the Kate project.
 *
3
 *  SPDX-FileCopyrightText: 2012 Christoph Cullmann <cullmann@kde.org>
Christoph Cullmann's avatar
Christoph Cullmann committed
4
 *
5
 *  SPDX-License-Identifier: LGPL-2.0-or-later
Christoph Cullmann's avatar
Christoph Cullmann committed
6
7
 */

8
#include "kateprojectworker.h"
Christoph Cullmann's avatar
Christoph Cullmann committed
9

Christoph Cullmann's avatar
Christoph Cullmann committed
10
11
12
13
14
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
15
#include <QRegularExpression>
16
#include <QSet>
17
#include <QSettings>
18
#include <QThread>
Alexander Lohnau's avatar
Alexander Lohnau committed
19
#include <QTime>
20
#include <QtConcurrentFilter>
Christoph Cullmann's avatar
Christoph Cullmann committed
21

22
23
#include <algorithm>

24
KateProjectWorker::KateProjectWorker(const QString &baseDir, const QString &indexDir, const QVariantMap &projectMap, bool force)
25
    : m_baseDir(baseDir)
26
    , m_indexDir(indexDir)
27
    , m_projectMap(projectMap)
28
    , m_force(force)
Christoph Cullmann's avatar
Christoph Cullmann committed
29
{
30
    Q_ASSERT(!m_baseDir.isEmpty());
Christoph Cullmann's avatar
Christoph Cullmann committed
31
32
}

33
void KateProjectWorker::run()
Christoph Cullmann's avatar
Christoph Cullmann committed
34
{
35
36
37
38
39
    /**
     * Create dummy top level parent item and empty map inside shared pointers
     * then load the project recursively
     */
    KateProjectSharedQStandardItem topLevel(new QStandardItem());
40
    KateProjectSharedQHashStringItem file2Item(new QHash<QString, KateProjectItem *>());
41
    loadProject(topLevel.data(), m_projectMap, file2Item.data());
42

43
44
45
46
47
48
    /**
     * sort the stuff once recursively, this is a LOT faster than once sorting the list
     * as we have normally not all stuff in on level of directory
     */
    topLevel->sortChildren(0);

49
50
51
52
53
54
55
56
57
58
59
60
61
    /**
     * decide if we need to create an index
     * if we need to do so, we will need to create a copy of the file list for later use
     * before this was default on, which is dangerous for large repositories, e.g. out-of-memory or out-of-disk
     * if specified in project map; use that setting, otherwise fall back to global setting
     */
    bool indexEnabled = !m_indexDir.isEmpty();
    const QVariantMap ctagsMap = m_projectMap[QStringLiteral("ctags")].toMap();
    auto indexValue = ctagsMap[QStringLiteral("enable")];
    if (!indexValue.isNull()) {
        indexEnabled = indexValue.toBool();
    }

62
63
    /**
     * create some local backup of some data we need for further processing!
64
65
66
67
68
69
70
71
72
73
     * this is expensive, therefore only really do this if required!
     */
    QStringList files;
    if (indexEnabled) {
        files = file2Item->keys();
    }

    /**
     * hand out our model item & mapping to the main thread
     * that will let Kate already show the project, even before index processing starts
74
     */
75
    Q_EMIT loadDone(topLevel, file2Item);
76

77
78
79
80
81
82
83
84
85
86
87
88
89
90
    /**
     * without indexing, we are even done with all stuff here
     */
    if (!indexEnabled) {
        Q_EMIT loadIndexDone(KateProjectSharedProjectIndex());
        return;
    }

    /**
     * create new index, this will do the loading in the constructor
     * wrap it into shared pointer for transfer to main thread
     */
    KateProjectSharedProjectIndex index(new KateProjectIndex(m_baseDir, m_indexDir, files, ctagsMap, m_force));
    Q_EMIT loadIndexDone(index);
Christoph Cullmann's avatar
Christoph Cullmann committed
91
92
}

93
void KateProjectWorker::loadProject(QStandardItem *parent, const QVariantMap &project, QHash<QString, KateProjectItem *> *file2Item)
Christoph Cullmann's avatar
Christoph Cullmann committed
94
95
{
    /**
96
     * recurse to sub-projects FIRST
Christoph Cullmann's avatar
Christoph Cullmann committed
97
     */
98
    const QVariantList subGroups = project[QStringLiteral("projects")].toList();
99
    for (const QVariant &subGroupVariant : subGroups) {
100
101
102
        /**
         * convert to map and get name, else skip
         */
103
        const QVariantMap subProject = subGroupVariant.toMap();
104
105
        const QString keyName = QStringLiteral("name");
        if (subProject[keyName].toString().isEmpty()) {
106
107
108
109
110
111
            continue;
        }

        /**
         * recurse
         */
112
        QStandardItem *subProjectItem = new KateProjectItem(KateProjectItem::Project, subProject[keyName].toString());
113
114
115
        loadProject(subProjectItem, subProject, file2Item);
        parent->appendRow(subProjectItem);
    }
Christoph Cullmann's avatar
Christoph Cullmann committed
116
117

    /**
118
     * load all specified files
Christoph Cullmann's avatar
Christoph Cullmann committed
119
     */
120
    const QString keyFiles = QStringLiteral("files");
121
    const QVariantList files = project[keyFiles].toList();
Michal Humpula's avatar
Michal Humpula committed
122
123
124
    for (const QVariant &fileVariant : files) {
        loadFilesEntry(parent, fileVariant.toMap(), file2Item);
    }
Christoph Cullmann's avatar
Christoph Cullmann committed
125
126
127
128
129
130
131
132
}

/**
 * small helper to construct directory parent items
 * @param dir2Item map for path => item
 * @param path current path we need item for
 * @return correct parent item for given path, will reuse existing ones
 */
133
static QStandardItem *directoryParent(const QDir &base, QHash<QString, QStandardItem *> &dir2Item, QString path)
Christoph Cullmann's avatar
Christoph Cullmann committed
134
{
135
136
137
    /**
     * throw away simple /
     */
138
    if (path == QLatin1String("/")) {
139
140
        path = QString();
    }
Christoph Cullmann's avatar
Christoph Cullmann committed
141
142

    /**
143
     * quick check: dir already seen?
Christoph Cullmann's avatar
Christoph Cullmann committed
144
     */
145
146
147
    const auto existingIt = dir2Item.find(path);
    if (existingIt != dir2Item.end()) {
        return existingIt.value();
148
    }
Christoph Cullmann's avatar
Christoph Cullmann committed
149
150

    /**
151
     * else: construct recursively
Christoph Cullmann's avatar
Christoph Cullmann committed
152
     */
153
    const int slashIndex = path.lastIndexOf(QLatin1Char('/'));
Christoph Cullmann's avatar
Christoph Cullmann committed
154
155

    /**
156
157
     * no slash?
     * simple, no recursion, append new item toplevel
Christoph Cullmann's avatar
Christoph Cullmann committed
158
     */
159
    if (slashIndex < 0) {
160
        const auto item = new KateProjectItem(KateProjectItem::Directory, path);
161
        item->setData(base.absoluteFilePath(path), Qt::UserRole);
162
163
164
        dir2Item[path] = item;
        dir2Item[QString()]->appendRow(item);
        return item;
Christoph Cullmann's avatar
Christoph Cullmann committed
165
166
    }

167
    /**
168
     * else, split and recurse
169
     */
170
171
    const QString leftPart = path.left(slashIndex);
    const QString rightPart = path.right(path.size() - (slashIndex + 1));
172
173

    /**
174
     * special handling if / with nothing on one side are found
175
     */
176
    if (leftPart.isEmpty() || rightPart.isEmpty()) {
177
        return directoryParent(base, dir2Item, leftPart.isEmpty() ? rightPart : leftPart);
178
    }
179
180

    /**
181
     * else: recurse on left side
182
     */
183
    const auto item = new KateProjectItem(KateProjectItem::Directory, rightPart);
184
    item->setData(base.absoluteFilePath(path), Qt::UserRole);
185
    dir2Item[path] = item;
186
    directoryParent(base, dir2Item, leftPart)->appendRow(item);
187
    return item;
188
189
}

190
void KateProjectWorker::loadFilesEntry(QStandardItem *parent, const QVariantMap &filesEntry, QHash<QString, KateProjectItem *> *file2Item)
191
192
193
194
{
    QDir dir(m_baseDir);
    if (!dir.cd(filesEntry[QStringLiteral("directory")].toString())) {
        return;
195
196
    }

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
    /**
     * handle linked projects, if any
     * one can reference other projects by specifying the path to them
     */
    QStringList linkedProjects = filesEntry[QStringLiteral("projects")].toStringList();
    if (!linkedProjects.empty()) {
        /**
         * ensure project files are made absolute in respect to correct base dir
         */
        for (auto &project : linkedProjects) {
            project = dir.absoluteFilePath(project);
        }

        /**
         * users might have specified duplicates, this can't happen for the other ways
         */
        linkedProjects.removeDuplicates();

        /**
         * filter out all directories that have no .kateproject inside!
         */
        linkedProjects.erase(std::remove_if(linkedProjects.begin(),
                                            linkedProjects.end(),
                                            [](const QString &item) {
                                                const QFileInfo projectFile(item + QLatin1String("/.kateproject"));
                                                return !projectFile.exists() || !projectFile.isFile();
                                            }),
                             linkedProjects.end());

226
227
228
229
230
231
232
233
234
        /**
         * we sort the projects, below we require that we walk them in order:
         * lala
         * lala/test
         * mow
         * mow/test2
         */
        std::sort(linkedProjects.begin(), linkedProjects.end());

235
236
237
238
        /**
         * now add our projects to the current item parent
         * later the tree view will e.g. allow to jump to the sub-projects
         */
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
        QHash<QString, QStandardItem *> dir2Item;
        dir2Item[QString()] = parent;
        for (const auto &filePath : linkedProjects) {
            /**
             * cheap file name computation
             * we do this A LOT, QFileInfo is very expensive just for this operation
             */
            const int slashIndex = filePath.lastIndexOf(QLatin1Char('/'));
            const QString fileName = (slashIndex < 0) ? filePath : filePath.mid(slashIndex + 1);
            const QString filePathName = (slashIndex < 0) ? QString() : filePath.left(slashIndex);

            /**
             * construct the item with right directory prefix
             * already hang in directories in tree
             */
            KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::LinkedProject, fileName);
            fileItem->setData(filePath, Qt::UserRole);

            /**
             * projects are directories, register them, we walk in order over the projects
             * even if the nest, toplevel ones would have been done before!
             */
            dir2Item[dir.relativeFilePath(filePath)] = fileItem;

            // get the directory's relative path to the base directory
            QString dirRelPath = dir.relativeFilePath(filePathName);
            // if the relative path is ".", clean it up
            if (dirRelPath == QLatin1Char('.')) {
                dirRelPath = QString();
            }

            // put in our item to the right directory parent
271
            directoryParent(dir, dir2Item, dirRelPath)->appendRow(fileItem);
272
273
274
275
276
277
278
279
        }

        /**
         * files with linked projects will ignore all other stuff inside
         */
        return;
    }

280
281
282
    /**
     * get list of files for this directory, might query the VCS
     */
283
    QStringList files = findFiles(dir, filesEntry);
Christoph Cullmann's avatar
Christoph Cullmann committed
284

285
286
287
288
    /**
     * sort out non-files
     * even for git, that just reports non-directories, we need to filter out e.g. sym-links to directories
     */
289
290
291
    QtConcurrent::blockingFilter(files, [](const QString &item) {
        return QFileInfo(item).isFile();
    });
292
293
294
    /**
     * we might end up with nothing to add at all
     */
295
296
297
298
    if (files.isEmpty()) {
        return;
    }

Christoph Cullmann's avatar
Christoph Cullmann committed
299
    /**
300
     * construct paths first in tree and items in a map
Christoph Cullmann's avatar
Christoph Cullmann committed
301
     */
302
    QHash<QString, QStandardItem *> dir2Item;
303
    dir2Item[QString()] = parent;
304
    for (const QString &filePath : files) {
305
        /**
306
307
         * cheap file name computation
         * we do this A LOT, QFileInfo is very expensive just for this operation
308
         */
309
310
311
        const int slashIndex = filePath.lastIndexOf(QLatin1Char('/'));
        const QString fileName = (slashIndex < 0) ? filePath : filePath.mid(slashIndex + 1);
        const QString filePathName = (slashIndex < 0) ? QString() : filePath.left(slashIndex);
312
313

        /**
314
315
         * construct the item with right directory prefix
         * already hang in directories in tree
316
         */
317
        KateProjectItem *fileItem = new KateProjectItem(KateProjectItem::File, fileName);
318
319
        fileItem->setData(filePath, Qt::UserRole);
        (*file2Item)[filePath] = fileItem;
Valentin Rouet's avatar
Valentin Rouet committed
320
321

        // get the directory's relative path to the base directory
322
        QString dirRelPath = dir.relativeFilePath(filePathName);
Valentin Rouet's avatar
Valentin Rouet committed
323
        // if the relative path is ".", clean it up
324
        if (dirRelPath == QLatin1Char('.')) {
Valentin Rouet's avatar
Valentin Rouet committed
325
326
327
            dirRelPath = QString();
        }

328
        // put in our item to the right directory parent
329
        directoryParent(dir, dir2Item, dirRelPath)->appendRow(fileItem);
330
331
    }
}
332

333
QStringList KateProjectWorker::findFiles(const QDir &dir, const QVariantMap &filesEntry)
334
{
335
336
337
    /**
     * shall we collect files recursively or not?
     */
338
    const bool recursive = !filesEntry.contains(QLatin1String("recursive")) || filesEntry[QStringLiteral("recursive")].toBool();
339

340
341
342
343
    /**
     * try the different version control systems first
     */

344
345
    if (filesEntry[QStringLiteral("git")].toBool()) {
        return filesFromGit(dir, recursive);
346
    }
347

348
    if (filesEntry[QStringLiteral("svn")].toBool()) {
349
        return filesFromSubversion(dir, recursive);
350
    }
351

352
    if (filesEntry[QStringLiteral("hg")].toBool()) {
353
        return filesFromMercurial(dir, recursive);
354
    }
355
356

    if (filesEntry[QStringLiteral("darcs")].toBool()) {
357
        return filesFromDarcs(dir, recursive);
358
359
360
361
362
363
364
    }

    /**
     * if we arrive here, we have some manual specification of files, no VCS
     */

    /**
365
     * try explicit list of stuff
366
     */
367
    QStringList userGivenFilesList = filesEntry[QStringLiteral("list")].toStringList();
368
    if (!userGivenFilesList.empty()) {
369
370
371
372
373
374
375
376
377
        /**
         * make the files absolute relative to current dir
         * all code later requires this and the filesFrom... routines do this, too, internally
         * even without this, the tree views will show them, but opening them will create new elements!
         */
        for (auto &file : userGivenFilesList) {
            file = dir.absoluteFilePath(file);
        }

378
379
380
381
382
383
384
385
386
387
388
389
        /**
         * users might have specified duplicates, this can't happen for the other ways
         */
        userGivenFilesList.removeDuplicates();
        return userGivenFilesList;
    }

    /**
     * if nothing found for that, try to use filters to scan the directory
     * here we only get files
     */
    return filesFromDirectory(dir, recursive, filesEntry[QStringLiteral("filters")].toStringList());
390
}
Christoph Cullmann's avatar
Christoph Cullmann committed
391

392
QStringList KateProjectWorker::filesFromGit(const QDir &dir, bool recursive)
393
{
394
    /**
Christoph Cullmann's avatar
Christoph Cullmann committed
395
     * query files via ls-files and make them absolute afterwards
396
397
     */
    const QStringList relFiles = gitLsFiles(dir);
398
    QStringList files;
399
    for (const QString &relFile : relFiles) {
400
        if (!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) {
401
402
            continue;
        }
403

404
405
406
407
408
409
410
        files.append(dir.absolutePath() + QLatin1Char('/') + relFile);
    }
    return files;
}

QStringList KateProjectWorker::gitLsFiles(const QDir &dir)
{
411
412
413
414
415
416
417
    /**
     * git ls-files -z results a bytearray where each entry is \0-terminated.
     * NOTE: Without -z, Umlauts such as "Der Bäcker/Das Brötchen.txt" do not work (#389415)
     *
     * use --recurse-submodules, there since git 2.11 (released 2016)
     * our own submodules handling code leads to file duplicates
     */
418
    QStringList args;
419
    args << QStringLiteral("ls-files") << QStringLiteral("-z") << QStringLiteral("--recurse-submodules") << QStringLiteral(".");
420
421
422

    QProcess git;
    git.setWorkingDirectory(dir.absolutePath());
423
    git.start(QStringLiteral("git"), args, QProcess::ReadOnly);
424
    QStringList files;
425
    if (!git.waitForStarted() || !git.waitForFinished(-1)) {
426
427
        return files;
    }
428

429
    const QList<QByteArray> byteArrayList = git.readAllStandardOutput().split('\0');
430
    for (const QByteArray &byteArray : byteArrayList) {
431
432
433
434
        const QString fileName = QString::fromUtf8(byteArray);
        if (!fileName.isEmpty()) {
            files << fileName;
        }
435
    }
436

437
438
439
440
441
    // untracked files
    {
        args.clear();
        args << QStringLiteral("ls-files") << QStringLiteral("-z") << QStringLiteral("--others") << QStringLiteral("--exclude-standard") << QStringLiteral(".");
        git.setArguments(args);
442
        git.start(QProcess::ReadOnly);
443
444
445
446
447
448
449
450
451
452
453
454
455
456

        if (!git.waitForStarted() || !git.waitForFinished(-1)) {
            return files;
        }

        const QList<QByteArray> byteArrayList = git.readAllStandardOutput().split('\0');
        for (const QByteArray &byteArray : byteArrayList) {
            const QString fileName = QString::fromUtf8(byteArray);
            if (!fileName.isEmpty()) {
                files << fileName;
            }
        }
    }

457
458
    return files;
}
459

460
QStringList KateProjectWorker::filesFromMercurial(const QDir &dir, bool recursive)
461
462
{
    QStringList files;
463

464
465
466
467
    QProcess hg;
    hg.setWorkingDirectory(dir.absolutePath());
    QStringList args;
    args << QStringLiteral("manifest") << QStringLiteral(".");
468
    hg.start(QStringLiteral("hg"), args, QProcess::ReadOnly);
469
    if (!hg.waitForStarted() || !hg.waitForFinished(-1)) {
470
471
        return files;
    }
472

Laurent Montel's avatar
Laurent Montel committed
473
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
Alexander Lohnau's avatar
Alexander Lohnau committed
474
475
    const QStringList relFiles =
        QString::fromLocal8Bit(hg.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts);
Laurent Montel's avatar
Laurent Montel committed
476
477
478
#else
    const QStringList relFiles = QString::fromLocal8Bit(hg.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), Qt::SkipEmptyParts);
#endif
479

480
    for (const QString &relFile : relFiles) {
481
        if (!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) {
482
            continue;
483
        }
484
485
486
487
488
489
490

        files.append(dir.absolutePath() + QLatin1Char('/') + relFile);
    }

    return files;
}

491
QStringList KateProjectWorker::filesFromSubversion(const QDir &dir, bool recursive)
492
493
494
495
496
497
498
499
500
501
502
503
{
    QStringList files;

    QProcess svn;
    svn.setWorkingDirectory(dir.absolutePath());
    QStringList args;
    args << QStringLiteral("status") << QStringLiteral("--verbose") << QStringLiteral(".");
    if (recursive) {
        args << QStringLiteral("--depth=infinity");
    } else {
        args << QStringLiteral("--depth=files");
    }
504
    svn.start(QStringLiteral("svn"), args, QProcess::ReadOnly);
505
    if (!svn.waitForStarted() || !svn.waitForFinished(-1)) {
506
        return files;
Christoph Cullmann's avatar
Christoph Cullmann committed
507
    }
508

Christoph Cullmann's avatar
Christoph Cullmann committed
509
    /**
510
     * get output and split up into lines
Christoph Cullmann's avatar
Christoph Cullmann committed
511
     */
Laurent Montel's avatar
Laurent Montel committed
512
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
513
    const QStringList lines = QString::fromLocal8Bit(svn.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts);
Laurent Montel's avatar
Laurent Montel committed
514
515
516
#else
    const QStringList lines = QString::fromLocal8Bit(svn.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), Qt::SkipEmptyParts);
#endif
Christoph Cullmann's avatar
Christoph Cullmann committed
517
518

    /**
519
     * remove start of line that is no filename, sort out unknown and ignore
520
     */
521
522
523
    bool first = true;
    int prefixLength = -1;

524
    for (const QString &line : lines) {
525
        /**
526
         * get length of stuff to cut
527
         */
528
529
530
531
        if (first) {
            /**
             * try to find ., else fail
             */
532
            prefixLength = line.lastIndexOf(QLatin1Char('.'));
533
534
535
            if (prefixLength < 0) {
                break;
            }
536

537
538
539
540
            /**
             * skip first
             */
            first = false;
541
542
543
544
            continue;
        }

        /**
545
546
         * get file, if not unknown or ignored
         * prepend directory path
547
         */
548
549
550
551
552
553
554
555
        if ((line.size() > prefixLength) && line[0] != QLatin1Char('?') && line[0] != QLatin1Char('I')) {
            files.append(dir.absolutePath() + QLatin1Char('/') + line.right(line.size() - prefixLength));
        }
    }

    return files;
}

556
QStringList KateProjectWorker::filesFromDarcs(const QDir &dir, bool recursive)
557
558
559
560
561
562
563
564
565
566
567
568
{
    QStringList files;

    const QString cmd = QStringLiteral("darcs");
    QString root;

    {
        QProcess darcs;
        darcs.setWorkingDirectory(dir.absolutePath());
        QStringList args;
        args << QStringLiteral("list") << QStringLiteral("repo");

569
        darcs.start(cmd, args, QProcess::ReadOnly);
570

571
        if (!darcs.waitForStarted() || !darcs.waitForFinished(-1)) {
572
            return files;
573
        }
574
575
576
577
578

        auto str = QString::fromLocal8Bit(darcs.readAllStandardOutput());
        QRegularExpression exp(QStringLiteral("Root: ([^\\n\\r]*)"));
        auto match = exp.match(str);

579
        if (!match.hasMatch()) {
580
            return files;
581
        }
582
583
584
585
586
587
588
589
590

        root = match.captured(1);
    }

    QStringList relFiles;
    {
        QProcess darcs;
        QStringList args;
        darcs.setWorkingDirectory(dir.absolutePath());
591
        args << QStringLiteral("list") << QStringLiteral("files") << QStringLiteral("--no-directories") << QStringLiteral("--pending");
592

593
        darcs.start(cmd, args, QProcess::ReadOnly);
594

595
        if (!darcs.waitForStarted() || !darcs.waitForFinished(-1)) {
596
            return files;
597
        }
598

Laurent Montel's avatar
Laurent Montel committed
599
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
600
        relFiles = QString::fromLocal8Bit(darcs.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), QString::SkipEmptyParts);
Laurent Montel's avatar
Laurent Montel committed
601
602
603
#else
        relFiles = QString::fromLocal8Bit(darcs.readAllStandardOutput()).split(QRegularExpression(QStringLiteral("[\n\r]")), Qt::SkipEmptyParts);
#endif
604
605
    }

606
    for (const QString &relFile : relFiles) {
607
        const QString path = dir.relativeFilePath(root + QLatin1String("/") + relFile);
608

609
        if ((!recursive && (relFile.indexOf(QLatin1Char('/')) != -1)) || (recursive && (relFile.indexOf(QLatin1String("..")) == 0))) {
610
            continue;
611
        }
612
613
614
615
616
617
618

        files.append(dir.absoluteFilePath(path));
    }

    return files;
}

619
QStringList KateProjectWorker::filesFromDirectory(const QDir &_dir, bool recursive, const QStringList &filters)
620
{
621
622
623
    /**
     * setup our filters, we only want files!
     */
624
625
626
627
    QDir dir(_dir);
    dir.setFilter(QDir::Files);
    if (!filters.isEmpty()) {
        dir.setNameFilters(filters);
628
629
630
    }

    /**
631
     * construct flags for iterator
632
     */
633
634
635
    QDirIterator::IteratorFlags flags = QDirIterator::NoIteratorFlags;
    if (recursive) {
        flags = flags | QDirIterator::Subdirectories;
636
    }
637
638
639
640

    /**
     * create iterator and collect all files
     */
641
    QStringList files;
642
643
644
645
646
647
    QDirIterator dirIterator(dir, flags);
    while (dirIterator.hasNext()) {
        dirIterator.next();
        files.append(dirIterator.filePath());
    }
    return files;
Christoph Cullmann's avatar
Christoph Cullmann committed
648
}