From d2a91ed1a0e4d840f5e106b0365dfa184d5b393f Mon Sep 17 00:00:00 2001 From: con <qtc-committer@nokia.com> Date: Mon, 11 Oct 2010 11:34:17 +0200 Subject: [PATCH] Use the encoding settings when doing multi-file searches. Task-number: QTCREATORBUG-65 --- src/libs/utils/filesearch.cpp | 164 +++++++++++------- src/libs/utils/filesearch.h | 11 +- .../projectexplorer/allprojectsfind.cpp | 25 ++- src/plugins/texteditor/findincurrentfile.cpp | 13 +- src/plugins/texteditor/findinfiles.cpp | 5 +- src/plugins/texteditor/itexteditor.cpp | 15 ++ src/plugins/texteditor/itexteditor.h | 1 + tests/manual/search/latin1.txt | 1 + tests/manual/search/search.pro | 1 + 9 files changed, 161 insertions(+), 75 deletions(-) create mode 100644 tests/manual/search/latin1.txt create mode 100644 tests/manual/search/search.pro diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp index e0e31c5db42..36f688543ba 100644 --- a/src/libs/utils/filesearch.cpp +++ b/src/libs/utils/filesearch.cpp @@ -82,20 +82,20 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future, bool caseInsensitive = !(flags & QTextDocument::FindCaseSensitively); bool wholeWord = (flags & QTextDocument::FindWholeWords); - QByteArray sa = searchTerm.toUtf8(); - int scMaxIndex = sa.length()-1; - const char *sc = sa.constData(); + const QString searchTermLower = searchTerm.toLower(); + const QString searchTermUpper = searchTerm.toUpper(); - QByteArray sal = searchTerm.toLower().toUtf8(); - const char *scl = sal.constData(); + int termLength = searchTerm.length(); + int termMaxIndex = termLength - 1; + const QChar *termData = searchTerm.constData(); + const QChar *termDataLower = searchTermLower.constData(); + const QChar *termDataUpper = searchTermUpper.constData(); - QByteArray sau = searchTerm.toUpper().toUtf8(); - const char *scu = sau.constData(); - - int chunkSize = qMax(100000, sa.length()); + int chunkSize = qMax(100000, 2 * termLength); QFile file; - QBuffer buffer; + QString str; + QTextStream stream; FileSearchResultList results; while (files->hasNext()) { const QString &s = files->next(); @@ -105,83 +105,104 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future, future.setProgressValueAndText(files->currentProgress(), msgCanceled(searchTerm, numMatches, numFilesSearched)); break; } - QIODevice *device; + + bool needsToCloseFile = false; if (fileToContentsMap.contains(s)) { - buffer.setData(fileToContentsMap.value(s).toLocal8Bit()); - device = &buffer; + str = fileToContentsMap.value(s); + stream.setString(&str); } else { file.setFileName(s); - device = &file; + if (!file.open(QIODevice::ReadOnly)) + continue; + needsToCloseFile = true; + stream.setDevice(&file); + stream.setCodec(files->encoding()); } - if (!device->open(QIODevice::ReadOnly)) - continue; - int lineNr = 1; - const char *startOfLastLine = NULL; + int lineNr = 1; + const QChar *startOfLastLine = NULL; bool firstChunk = true; - while (!device->atEnd()) { - if (!firstChunk) - device->seek(device->pos()-sa.length()+1); + while (!stream.atEnd()) { + int chunkProcessingStart = 0; + if (!firstChunk) { + // we need one additional char to the left and right + // for whole word searches + // so we jump back two additional chars, and start at index 1 + stream.seek(stream.pos() - termLength - 1); + chunkProcessingStart = 1; + } + firstChunk = false; - const QByteArray chunk = device->read(chunkSize); - const char *chunkPtr = chunk.constData(); - startOfLastLine = chunkPtr; - for (const char *regionPtr = chunkPtr; regionPtr < chunkPtr + chunk.length()-scMaxIndex; ++regionPtr) { - const char *regionEnd = regionPtr + scMaxIndex; + const QString chunk = stream.read(chunkSize); + int chunkLength = chunk.length(); + const QChar *chunkPtr = chunk.constData(); + // we need one additional char to the right for whole word searches, + // except at the very end + const QChar *chunkProcessingEnd = (stream.atEnd() ? chunkPtr + chunkLength : chunkPtr + chunkLength - 1); - if (*regionPtr == '\n') { + startOfLastLine = chunkPtr; + for (const QChar *regionPtr = chunkPtr + chunkProcessingStart; + regionPtr + termMaxIndex < chunkProcessingEnd; + ++regionPtr) { + const QChar *regionEnd = regionPtr + termMaxIndex; + if (*regionPtr == QLatin1Char('\n')) { startOfLastLine = regionPtr + 1; ++lineNr; - } - else if ( + } else if ( /* optimization check for start and end of region */ // case sensitive - (!caseInsensitive && *regionPtr == sc[0] && *regionEnd == sc[scMaxIndex]) + (!caseInsensitive && *regionPtr == termData[0] && *regionEnd == termData[termMaxIndex]) || // case insensitive - (caseInsensitive && (*regionPtr == scl[0] || *regionPtr == scu[0]) - && (*regionEnd == scl[scMaxIndex] || *regionEnd == scu[scMaxIndex])) + (caseInsensitive && (*regionPtr == termDataLower[0] || *regionPtr == termDataUpper[0]) + && (*regionEnd == termDataLower[termMaxIndex] || *regionEnd == termDataUpper[termMaxIndex])) ) { - const char *afterRegion = regionEnd + 1; - const char *beforeRegion = regionPtr - 1; bool equal = true; - if (wholeWord && - ( isalnum((unsigned char)*beforeRegion) - || (*beforeRegion == '_') - || isalnum((unsigned char)*afterRegion) - || (*afterRegion == '_'))) { + + // whole word check + const QChar *beforeRegion = regionPtr - 1; + const QChar *afterRegion = regionEnd + 1; + if (wholeWord && ( + ((beforeRegion >= chunkPtr) && (beforeRegion->isLetterOrNumber() || ((*beforeRegion) == QLatin1Char('_')))) || + ((afterRegion < chunkPtr + chunkLength) && (afterRegion->isLetterOrNumber() || ((*afterRegion) == QLatin1Char('_')))) + )) { equal = false; } - int regionIndex = 1; - for (const char *regionCursor = regionPtr + 1; regionCursor < regionEnd; ++regionCursor, ++regionIndex) { - if ( // case sensitive - (!caseInsensitive && equal && *regionCursor != sc[regionIndex]) - || - // case insensitive - (caseInsensitive && equal && *regionCursor != sc[regionIndex] && *regionCursor != scl[regionIndex] && *regionCursor != scu[regionIndex]) - ) { - equal = false; + if (equal) { + // check all chars + int regionIndex = 1; + for (const QChar *regionCursor = regionPtr + 1; regionCursor < regionEnd; ++regionCursor, ++regionIndex) { + if ( // case sensitive + (!caseInsensitive && *regionCursor != termData[regionIndex]) + || + // case insensitive + (caseInsensitive && *regionCursor != termData[regionIndex] + && *regionCursor != termDataLower[regionIndex] && *regionCursor != termDataUpper[regionIndex]) + ) { + equal = false; + } } } if (equal) { - int textLength = chunk.length() - (startOfLastLine - chunkPtr); + int textLength = chunkLength - (startOfLastLine - chunkPtr); if (textLength > 0) { - QByteArray res; + QString res; res.reserve(256); int i = 0; int n = 0; - while (startOfLastLine[i] != '\n' && startOfLastLine[i] != '\r' && i < textLength && n++ < 256) + while (startOfLastLine[i] != QLatin1Char('\n') && startOfLastLine[i] != QLatin1Char('\r') && i < textLength && n++ < 256) res.append(startOfLastLine[i++]); - results << FileSearchResult(s, lineNr, QString::fromUtf8(res), - regionPtr - startOfLastLine, sa.length(), + res.squeeze(); + results << FileSearchResult(s, lineNr, res, + regionPtr - startOfLastLine, termLength, QStringList()); ++numMatches; } } } } - firstChunk = false; } + ++numFilesSearched; if (future.isProgressUpdateNeeded()) { if (!results.isEmpty()) { @@ -191,7 +212,11 @@ void runFileSearch(QFutureInterface<FileSearchResultList> &future, future.setProgressRange(0, files->maxProgress()); future.setProgressValueAndText(files->currentProgress(), msgFound(searchTerm, numMatches, numFilesSearched)); } - device->close(); + + // clean up + if (needsToCloseFile) + file.close(); + } if (!results.isEmpty()) { future.reportResult(results); @@ -240,6 +265,7 @@ void runFileSearchRegExp(QFutureInterface<FileSearchResultList> &future, continue; needsToCloseFile = true; stream.setDevice(&file); + stream.setCodec(files->encoding()); } int lineNr = 1; QString line; @@ -334,14 +360,16 @@ QString Utils::expandRegExpReplacement(const QString &replaceText, const QString FileIterator::FileIterator() : m_list(QStringList()), m_iterator(0), - m_index(0) + m_index(-1) { } -FileIterator::FileIterator(const QStringList &fileList) +FileIterator::FileIterator(const QStringList &fileList, + const QList<QTextCodec *> encodings) : m_list(fileList), - m_iterator(new QStringListIterator(m_list)), - m_index(0) + m_iterator(new QStringListIterator(m_list)), + m_encodings(encodings), + m_index(-1) { } @@ -371,7 +399,14 @@ int FileIterator::maxProgress() const int FileIterator::currentProgress() const { - return m_index; + return m_index + 1; +} + +QTextCodec * FileIterator::encoding() const +{ + if (m_index >= 0 && m_index < m_encodings.size()) + return m_encodings.at(m_index); + return QTextCodec::codecForLocale(); } // #pragma mark -- SubDirFileIterator @@ -380,9 +415,11 @@ namespace { const int MAX_PROGRESS = 1000; } -SubDirFileIterator::SubDirFileIterator(const QStringList &directories, const QStringList &filters) +SubDirFileIterator::SubDirFileIterator(const QStringList &directories, const QStringList &filters, + QTextCodec *encoding) : m_filters(filters), m_progress(0) { + m_encoding = (encoding == 0 ? QTextCodec::codecForLocale() : encoding); qreal maxPer = MAX_PROGRESS/directories.count(); foreach (const QString &directoryEntry, directories) { if (!directoryEntry.isEmpty()) { @@ -457,3 +494,8 @@ int SubDirFileIterator::currentProgress() const { return qMin(qRound(m_progress), MAX_PROGRESS); } + +QTextCodec * SubDirFileIterator::encoding() const +{ + return m_encoding; +} diff --git a/src/libs/utils/filesearch.h b/src/libs/utils/filesearch.h index 6b203ca9e6a..035ced96d13 100644 --- a/src/libs/utils/filesearch.h +++ b/src/libs/utils/filesearch.h @@ -37,6 +37,7 @@ #include <QtCore/QMap> #include <QtCore/QStack> #include <QtCore/QDir> +#include <QtCore/QTextCodec> #include <QtGui/QTextDocument> namespace Utils { @@ -45,32 +46,38 @@ class QTCREATOR_UTILS_EXPORT FileIterator { public: FileIterator(); - explicit FileIterator(const QStringList &fileList); + explicit FileIterator(const QStringList &fileList, + const QList<QTextCodec *> encodings); ~FileIterator(); virtual bool hasNext() const; virtual QString next(); + virtual QTextCodec *encoding() const; virtual int maxProgress() const; virtual int currentProgress() const; private: QStringList m_list; QStringListIterator *m_iterator; + QList<QTextCodec *> m_encodings; int m_index; }; class QTCREATOR_UTILS_EXPORT SubDirFileIterator : public FileIterator { public: - SubDirFileIterator(const QStringList &directories, const QStringList &filters); + SubDirFileIterator(const QStringList &directories, const QStringList &filters, + QTextCodec *encoding = 0); bool hasNext() const; QString next(); + QTextCodec *encoding() const; int maxProgress() const; int currentProgress() const; private: QStringList m_filters; + QTextCodec *m_encoding; mutable QStack<QDir> m_dirs; mutable QStack<qreal> m_progressValues; mutable QStack<bool> m_processedValues; diff --git a/src/plugins/projectexplorer/allprojectsfind.cpp b/src/plugins/projectexplorer/allprojectsfind.cpp index 8c437c62676..637394313f6 100644 --- a/src/plugins/projectexplorer/allprojectsfind.cpp +++ b/src/plugins/projectexplorer/allprojectsfind.cpp @@ -32,8 +32,11 @@ #include "project.h" #include "session.h" #include "projectexplorer.h" +#include "editorconfiguration.h" #include <utils/qtcassert.h> +#include <texteditor/itexteditor.h> +#include <coreplugin/editormanager/editormanager.h> #include <QtCore/QDebug> #include <QtCore/QFileInfo> @@ -85,25 +88,33 @@ Utils::FileIterator *AllProjectsFind::files() const foreach (const QString &filter, nameFilters) { filterRegs << QRegExp(filter, Qt::CaseInsensitive, QRegExp::Wildcard); } - QStringList files; - QStringList projectFiles; + QMap<QString, QTextCodec *> openEditorEncodings = TextEditor::ITextEditor::openedTextEditorsEncodings(); + QMap<QString, QTextCodec *> encodings; foreach (const Project *project, projects()) { - projectFiles = project->files(Project::AllFiles); + QStringList projectFiles = project->files(Project::AllFiles); + QStringList filteredFiles; if (!filterRegs.isEmpty()) { foreach (const QString &file, projectFiles) { foreach (const QRegExp ®, filterRegs) { if (reg.exactMatch(file) || reg.exactMatch(QFileInfo(file).fileName())) { - files.append(file); + filteredFiles.append(file); break; } } } } else { - files += projectFiles; + filteredFiles = projectFiles; + } + foreach (const QString &fileName, filteredFiles) { + QTextCodec *codec = openEditorEncodings.value(fileName); + if (!codec) + codec = project->editorConfiguration()->defaultTextCodec(); + if (!codec) + codec = Core::EditorManager::instance()->defaultTextEncoding(); + encodings.insert(fileName, codec); } } - files.removeDuplicates(); - return new Utils::FileIterator(files); + return new Utils::FileIterator(encodings.keys(), encodings.values()); } QWidget *AllProjectsFind::createConfigWidget() diff --git a/src/plugins/texteditor/findincurrentfile.cpp b/src/plugins/texteditor/findincurrentfile.cpp index 002d1ab145e..938a0de3f10 100644 --- a/src/plugins/texteditor/findincurrentfile.cpp +++ b/src/plugins/texteditor/findincurrentfile.cpp @@ -28,6 +28,7 @@ **************************************************************************/ #include "findincurrentfile.h" +#include "itexteditor.h" #include <coreplugin/icore.h> #include <coreplugin/editormanager/editormanager.h> @@ -40,6 +41,7 @@ #include <QtGui/QVBoxLayout> using namespace Find; +using namespace TextEditor; using namespace TextEditor::Internal; FindInCurrentFile::FindInCurrentFile(SearchResultWindow *resultWindow) @@ -64,10 +66,13 @@ QString FindInCurrentFile::displayName() const Utils::FileIterator *FindInCurrentFile::files() const { - QStringList fileList; - if (isEnabled()) - fileList << m_currentFile->fileName(); - return new Utils::FileIterator(fileList); + Q_ASSERT(isEnabled()); + QString fileName = m_currentFile->fileName(); + QMap<QString, QTextCodec *> openEditorEncodings = ITextEditor::openedTextEditorsEncodings(); + QTextCodec *codec = openEditorEncodings.value(fileName); + if (!codec) + codec = Core::EditorManager::instance()->defaultTextEncoding(); + return new Utils::FileIterator(QStringList() << fileName, QList<QTextCodec *>() << codec); } bool FindInCurrentFile::isEnabled() const diff --git a/src/plugins/texteditor/findinfiles.cpp b/src/plugins/texteditor/findinfiles.cpp index 43ac897abd7..3c8d56f05a5 100644 --- a/src/plugins/texteditor/findinfiles.cpp +++ b/src/plugins/texteditor/findinfiles.cpp @@ -29,6 +29,8 @@ #include "findinfiles.h" +#include <coreplugin/editormanager/editormanager.h> + #include <QtCore/QtDebug> #include <QtCore/QSettings> #include <QtCore/QDir> @@ -66,7 +68,8 @@ void FindInFiles::findAll(const QString &txt, Find::FindFlags findFlags) Utils::FileIterator *FindInFiles::files() const { return new Utils::SubDirFileIterator(QStringList() << m_directory->currentText(), - fileNameFilters()); + fileNameFilters(), + Core::EditorManager::instance()->defaultTextEncoding()); } QWidget *FindInFiles::createConfigWidget() diff --git a/src/plugins/texteditor/itexteditor.cpp b/src/plugins/texteditor/itexteditor.cpp index 0c196e7614d..c62d951f794 100644 --- a/src/plugins/texteditor/itexteditor.cpp +++ b/src/plugins/texteditor/itexteditor.cpp @@ -31,6 +31,8 @@ #include <coreplugin/editormanager/editormanager.h> +#include <QtCore/QTextCodec> + using namespace TextEditor; QMap<QString, QString> ITextEditor::openedTextEditorsContents() @@ -45,3 +47,16 @@ QMap<QString, QString> ITextEditor::openedTextEditorsContents() } return workingCopy; } + +QMap<QString, QTextCodec *> TextEditor::ITextEditor::openedTextEditorsEncodings() +{ + QMap<QString, QTextCodec *> workingCopy; + foreach (Core::IEditor *editor, Core::EditorManager::instance()->openedEditors()) { + ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor); + if (!textEditor) + continue; + QString fileName = textEditor->file()->fileName(); + workingCopy[fileName] = textEditor->textCodec(); + } + return workingCopy; +} diff --git a/src/plugins/texteditor/itexteditor.h b/src/plugins/texteditor/itexteditor.h index 16af52ef1fc..fb98bbb8f75 100644 --- a/src/plugins/texteditor/itexteditor.h +++ b/src/plugins/texteditor/itexteditor.h @@ -119,6 +119,7 @@ public: virtual QTextCodec *textCodec() const = 0; static QMap<QString, QString> openedTextEditorsContents(); + static QMap<QString, QTextCodec *> openedTextEditorsEncodings(); signals: void contentsChanged(); diff --git a/tests/manual/search/latin1.txt b/tests/manual/search/latin1.txt new file mode 100644 index 00000000000..09044014b3a --- /dev/null +++ b/tests/manual/search/latin1.txt @@ -0,0 +1 @@ +نِüؤضـك diff --git a/tests/manual/search/search.pro b/tests/manual/search/search.pro new file mode 100644 index 00000000000..7c382caa325 --- /dev/null +++ b/tests/manual/search/search.pro @@ -0,0 +1 @@ +OTHER_FILES = latin1.txt -- GitLab