OSDN Git Service

C++ editor: Normalize line endings for find usages
[qt-creator-jp/qt-creator-jp.git] / src / plugins / cpptools / cppfindreferences.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at info@qt.nokia.com.
30 **
31 **************************************************************************/
32
33 #include "cppfindreferences.h"
34 #include "cpptoolsconstants.h"
35
36 #include <texteditor/basetexteditor.h>
37 #include <texteditor/basefilefind.h>
38 #include <find/searchresultwindow.h>
39 #include <extensionsystem/pluginmanager.h>
40 #include <utils/filesearch.h>
41 #include <utils/fileutils.h>
42 #include <coreplugin/progressmanager/progressmanager.h>
43 #include <coreplugin/progressmanager/futureprogress.h>
44 #include <coreplugin/editormanager/editormanager.h>
45 #include <coreplugin/icore.h>
46 #include <coreplugin/infobar.h>
47
48 #include <ASTVisitor.h>
49 #include <AST.h>
50 #include <Control.h>
51 #include <Literals.h>
52 #include <TranslationUnit.h>
53 #include <Symbols.h>
54 #include <Names.h>
55 #include <Scope.h>
56
57 #include <cplusplus/ModelManagerInterface.h>
58 #include <cplusplus/CppDocument.h>
59 #include <cplusplus/Overview.h>
60 #include <cplusplus/FindUsages.h>
61
62 #include <QtCore/QTime>
63 #include <QtCore/QTimer>
64 #include <QtCore/QtConcurrentRun>
65 #include <QtCore/QtConcurrentMap>
66 #include <QtCore/QDir>
67 #include <QtGui/QApplication>
68 #include <qtconcurrent/runextensions.h>
69
70 #include <functional>
71
72 using namespace CppTools::Internal;
73 using namespace CPlusPlus;
74
75 static QString getSource(const QString &fileName,
76                          const CppModelManagerInterface::WorkingCopy &workingCopy)
77 {
78     if (workingCopy.contains(fileName)) {
79         return workingCopy.source(fileName);
80     } else {
81         Utils::FileReader reader;
82         if (!reader.fetch(fileName, QFile::Text)) // ### FIXME error reporting
83             return QString();
84
85         return QString::fromLocal8Bit(reader.data()); // ### FIXME encoding
86     }
87 }
88
89 namespace {
90
91 class ProcessFile: public std::unary_function<QString, QList<Usage> >
92 {
93     const CppModelManagerInterface::WorkingCopy workingCopy;
94     const Snapshot snapshot;
95     Document::Ptr symbolDocument;
96     Symbol *symbol;
97
98 public:
99     ProcessFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
100                 const Snapshot snapshot,
101                 Document::Ptr symbolDocument,
102                 Symbol *symbol)
103         : workingCopy(workingCopy), snapshot(snapshot), symbolDocument(symbolDocument), symbol(symbol)
104     { }
105
106     QList<Usage> operator()(const QString &fileName)
107     {
108         QList<Usage> usages;
109         const Identifier *symbolId = symbol->identifier();
110
111         if (Document::Ptr previousDoc = snapshot.document(fileName)) {
112             Control *control = previousDoc->control();
113             if (! control->findIdentifier(symbolId->chars(), symbolId->size()))
114                 return usages; // skip this document, it's not using symbolId.
115         }
116
117         Document::Ptr doc;
118         QByteArray source;
119         const QString unpreprocessedSource = getSource(fileName, workingCopy);
120
121         if (symbolDocument && fileName == symbolDocument->fileName())
122             doc = symbolDocument;
123         else {
124             source = snapshot.preprocessedCode(unpreprocessedSource, fileName);
125             doc = snapshot.documentFromSource(source, fileName);
126             doc->tokenize();
127         }
128
129         Control *control = doc->control();
130         if (control->findIdentifier(symbolId->chars(), symbolId->size()) != 0) {
131             if (doc != symbolDocument)
132                 doc->check();
133
134             FindUsages process(unpreprocessedSource.toUtf8(), doc, snapshot);
135             process(symbol);
136
137             usages = process.usages();
138         }
139
140         return usages;
141     }
142 };
143
144 class UpdateUI: public std::binary_function<QList<Usage> &, QList<Usage>, void>
145 {
146     QFutureInterface<Usage> *future;
147
148 public:
149     UpdateUI(QFutureInterface<Usage> *future): future(future) {}
150
151     void operator()(QList<Usage> &, const QList<Usage> &usages)
152     {
153         foreach (const Usage &u, usages)
154             future->reportResult(u);
155
156         future->setProgressValue(future->progressValue() + 1);
157     }
158 };
159
160 } // end of anonymous namespace
161
162 CppFindReferences::CppFindReferences(CppModelManagerInterface *modelManager)
163     : QObject(modelManager),
164       _modelManager(modelManager),
165       _resultWindow(Find::SearchResultWindow::instance())
166 {
167     m_watcher.setPendingResultsLimit(1);
168     connect(&m_watcher, SIGNAL(resultsReadyAt(int,int)), this, SLOT(displayResults(int,int)));
169     connect(&m_watcher, SIGNAL(finished()), this, SLOT(searchFinished()));
170 }
171
172 CppFindReferences::~CppFindReferences()
173 {
174 }
175
176 QList<int> CppFindReferences::references(Symbol *symbol, const LookupContext &context) const
177 {
178     QList<int> references;
179
180     FindUsages findUsages(context);
181     findUsages(symbol);
182     references = findUsages.references();
183
184     return references;
185 }
186
187 static void find_helper(QFutureInterface<Usage> &future,
188                         const CppModelManagerInterface::WorkingCopy workingCopy,
189                         const LookupContext context,
190                         CppFindReferences *findRefs,
191                         Symbol *symbol)
192 {
193     const Identifier *symbolId = symbol->identifier();
194     Q_ASSERT(symbolId != 0);
195
196     const Snapshot snapshot = context.snapshot();
197
198     const QString sourceFile = QString::fromUtf8(symbol->fileName(), symbol->fileNameLength());
199     QStringList files(sourceFile);
200
201     if (symbol->isClass() || symbol->isForwardClassDeclaration() || (symbol->enclosingScope() && ! symbol->isStatic() &&
202                                                                      symbol->enclosingScope()->isNamespace())) {
203         foreach (const Document::Ptr &doc, context.snapshot()) {
204             if (doc->fileName() == sourceFile)
205                 continue;
206
207             Control *control = doc->control();
208
209             if (control->findIdentifier(symbolId->chars(), symbolId->size()))
210                 files.append(doc->fileName());
211         }
212     } else {
213         DependencyTable dependencyTable = findRefs->updateDependencyTable(snapshot);
214         files += dependencyTable.filesDependingOn(sourceFile);
215     }
216     files.removeDuplicates();
217
218     future.setProgressRange(0, files.size());
219
220     ProcessFile process(workingCopy, snapshot, context.thisDocument(), symbol);
221     UpdateUI reduce(&future);
222
223     QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
224
225     future.setProgressValue(files.size());
226 }
227
228 void CppFindReferences::findUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context)
229 {
230     Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);
231
232     connect(search, SIGNAL(activated(Find::SearchResultItem)),
233             this, SLOT(openEditor(Find::SearchResultItem)));
234
235     findAll_helper(symbol, context);
236 }
237
238 void CppFindReferences::renameUsages(CPlusPlus::Symbol *symbol, const CPlusPlus::LookupContext &context,
239                                      const QString &replacement)
240 {
241     if (const Identifier *id = symbol->identifier()) {
242         const QString textToReplace = replacement.isEmpty()
243                 ? QString::fromUtf8(id->chars(), id->size()) : replacement;
244
245         Find::SearchResult *search = _resultWindow->startNewSearch(
246                 Find::SearchResultWindow::SearchAndReplace, QLatin1String("CppEditor"));
247         _resultWindow->setTextToReplace(textToReplace);
248
249         connect(search, SIGNAL(activated(Find::SearchResultItem)),
250                 this, SLOT(openEditor(Find::SearchResultItem)));
251
252         connect(search, SIGNAL(replaceButtonClicked(QString,QList<Find::SearchResultItem>)),
253                 SLOT(onReplaceButtonClicked(QString,QList<Find::SearchResultItem>)));
254
255         findAll_helper(symbol, context);
256     }
257 }
258
259 void CppFindReferences::findAll_helper(Symbol *symbol, const LookupContext &context)
260 {
261     if (! (symbol && symbol->identifier()))
262         return;
263
264     _resultWindow->popup(true);
265
266     const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
267
268     Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
269
270     QFuture<Usage> result;
271
272     result = QtConcurrent::run(&find_helper, workingCopy, context, this, symbol);
273     m_watcher.setFuture(result);
274
275     Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
276                                                               CppTools::Constants::TASK_SEARCH);
277
278     connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
279 }
280
281 void CppFindReferences::onReplaceButtonClicked(const QString &text,
282                                                const QList<Find::SearchResultItem> &items)
283 {
284     const QStringList fileNames = TextEditor::BaseFileFind::replaceAll(text, items);
285     if (!fileNames.isEmpty()) {
286         _modelManager->updateSourceFiles(fileNames);
287         _resultWindow->hide();
288     }
289 }
290
291 void CppFindReferences::displayResults(int first, int last)
292 {
293     for (int index = first; index != last; ++index) {
294         Usage result = m_watcher.future().resultAt(index);
295         _resultWindow->addResult(result.path,
296                                  result.line,
297                                  result.lineText,
298                                  result.col,
299                                  result.len);
300     }
301 }
302
303 void CppFindReferences::searchFinished()
304 {
305     _resultWindow->finishSearch();
306     emit changed();
307 }
308
309 void CppFindReferences::openEditor(const Find::SearchResultItem &item)
310 {
311     if (item.path.size() > 0) {
312         TextEditor::BaseTextEditorWidget::openEditorAt(item.path.first(), item.lineNumber, item.textMarkPos,
313                                                  QString(),
314                                                  Core::EditorManager::ModeSwitch);
315     } else {
316         Core::EditorManager::instance()->openEditor(item.text, QString(), Core::EditorManager::ModeSwitch);
317     }
318 }
319
320
321 namespace {
322
323 class FindMacroUsesInFile: public std::unary_function<QString, QList<Usage> >
324 {
325     const CppModelManagerInterface::WorkingCopy workingCopy;
326     const Snapshot snapshot;
327     const Macro &macro;
328
329 public:
330     FindMacroUsesInFile(const CppModelManagerInterface::WorkingCopy &workingCopy,
331                         const Snapshot snapshot,
332                         const Macro &macro)
333         : workingCopy(workingCopy), snapshot(snapshot), macro(macro)
334     { }
335
336     QList<Usage> operator()(const QString &fileName)
337     {
338         QList<Usage> usages;
339
340         const Document::Ptr &doc = snapshot.document(fileName);
341         QByteArray source;
342
343         foreach (const Document::MacroUse &use, doc->macroUses()) {
344             const Macro &useMacro = use.macro();
345             if (useMacro.line() == macro.line()
346                 && useMacro.fileName() == macro.fileName())
347                 {
348                 if (source.isEmpty())
349                     source = getSource(fileName, workingCopy).toLatin1(); // ### FIXME: Encoding?
350
351                 unsigned lineStart;
352                 const QString &lineSource = matchingLine(use.begin(), source, &lineStart);
353                 usages.append(Usage(fileName, lineSource, use.beginLine(),
354                                     use.begin() - lineStart, use.length()));
355             }
356         }
357
358         return usages;
359     }
360
361     // ### FIXME: Pretty close to FindUsages::matchingLine.
362     static QString matchingLine(unsigned position, const QByteArray &source,
363                                 unsigned *lineStart = 0)
364     {
365         const char *beg = source.constData();
366         const char *start = beg + position;
367         for (; start != beg - 1; --start) {
368             if (*start == '\n')
369                 break;
370         }
371
372         ++start;
373
374         const char *end = start + 1;
375         for (; *end; ++end) {
376             if (*end == '\n')
377                 break;
378         }
379
380         if (lineStart)
381             *lineStart = start - beg;
382
383         // ### FIXME: Encoding?
384         const QString matchingLine = QString::fromUtf8(start, end - start);
385         return matchingLine;
386     }
387 };
388
389 } // end of anonymous namespace
390
391 static void findMacroUses_helper(QFutureInterface<Usage> &future,
392                                  const CppModelManagerInterface::WorkingCopy workingCopy,
393                                  const Snapshot snapshot,
394                                  CppFindReferences *findRefs,
395                                  const Macro macro)
396 {
397     // ensure the dependency table is updated
398     DependencyTable dependencies = findRefs->updateDependencyTable(snapshot);
399
400     const QString& sourceFile = macro.fileName();
401     QStringList files(sourceFile);
402     files += dependencies.filesDependingOn(sourceFile);
403     files.removeDuplicates();
404
405     future.setProgressRange(0, files.size());
406
407     FindMacroUsesInFile process(workingCopy, snapshot, macro);
408     UpdateUI reduce(&future);
409     QtConcurrent::blockingMappedReduced<QList<Usage> > (files, process, reduce);
410
411     future.setProgressValue(files.size());
412 }
413
414 void CppFindReferences::findMacroUses(const Macro &macro)
415 {
416     Find::SearchResult *search = _resultWindow->startNewSearch(Find::SearchResultWindow::SearchOnly);
417
418     _resultWindow->popup(true);
419
420     connect(search, SIGNAL(activated(Find::SearchResultItem)),
421             this, SLOT(openEditor(Find::SearchResultItem)));
422
423     const Snapshot snapshot = _modelManager->snapshot();
424     const CppModelManagerInterface::WorkingCopy workingCopy = _modelManager->workingCopy();
425
426     // add the macro definition itself
427     {
428         // ### FIXME: Encoding?
429         const QByteArray &source = getSource(macro.fileName(), workingCopy).toLatin1();
430         _resultWindow->addResult(macro.fileName(), macro.line(),
431                                  source.mid(macro.offset(), macro.length()), 0, macro.length());
432     }
433
434     QFuture<Usage> result;
435     result = QtConcurrent::run(&findMacroUses_helper, workingCopy, snapshot, this, macro);
436     m_watcher.setFuture(result);
437
438     Core::ProgressManager *progressManager = Core::ICore::instance()->progressManager();
439     Core::FutureProgress *progress = progressManager->addTask(result, tr("Searching"),
440                                                               CppTools::Constants::TASK_SEARCH);
441     connect(progress, SIGNAL(clicked()), _resultWindow, SLOT(popup()));
442 }
443
444 DependencyTable CppFindReferences::updateDependencyTable(CPlusPlus::Snapshot snapshot)
445 {
446     DependencyTable oldDeps = dependencyTable();
447     if (oldDeps.isValidFor(snapshot))
448         return oldDeps;
449
450     DependencyTable newDeps;
451     newDeps.build(snapshot);
452     setDependencyTable(newDeps);
453     return newDeps;
454 }
455
456 DependencyTable CppFindReferences::dependencyTable() const
457 {
458     QMutexLocker locker(&m_depsLock);
459     Q_UNUSED(locker);
460     return m_deps;
461 }
462
463 void CppFindReferences::setDependencyTable(const CPlusPlus::DependencyTable &newTable)
464 {
465     QMutexLocker locker(&m_depsLock);
466     Q_UNUSED(locker);
467     m_deps = newTable;
468 }