Commit d2a91ed1 authored by con's avatar con
Browse files

Use the encoding settings when doing multi-file searches.

Task-number: QTCREATORBUG-65
parent 38d3090a
......@@ -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;
}
......@@ -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;
......
......@@ -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 &reg, 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()
......
......@@ -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
......
......@@ -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()
......
......@@ -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;
}
......@@ -119,6 +119,7 @@ public:
virtual QTextCodec *textCodec() const = 0;
static QMap<QString, QString> openedTextEditorsContents();
static QMap<QString, QTextCodec *> openedTextEditorsEncodings();
signals:
void contentsChanged();
......
OTHER_FILES = latin1.txt
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment