diff options
Diffstat (limited to 'src/plugins/cppeditor/cpplocatorfilter.cpp')
-rw-r--r-- | src/plugins/cppeditor/cpplocatorfilter.cpp | 395 |
1 files changed, 302 insertions, 93 deletions
diff --git a/src/plugins/cppeditor/cpplocatorfilter.cpp b/src/plugins/cppeditor/cpplocatorfilter.cpp index 083c5df1b2f..cd1add7786e 100644 --- a/src/plugins/cppeditor/cpplocatorfilter.cpp +++ b/src/plugins/cppeditor/cpplocatorfilter.cpp @@ -4,60 +4,49 @@ #include "cpplocatorfilter.h" #include "cppeditorconstants.h" +#include "cppeditorplugin.h" #include "cppeditortr.h" +#include "cpplocatordata.h" +#include "cppmodelmanager.h" #include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> + +#include <extensionsystem/pluginmanager.h> + #include <utils/algorithm.h> +#include <utils/async.h> +#include <utils/fuzzymatcher.h> #include <QRegularExpression> -#include <algorithm> -#include <numeric> +using namespace Core; +using namespace CPlusPlus; +using namespace Utils; namespace CppEditor { -CppLocatorFilter::CppLocatorFilter(CppLocatorData *locatorData) - : m_data(locatorData) -{ - setId(Constants::LOCATOR_FILTER_ID); - setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME)); - setDefaultShortcutString(":"); - setDefaultIncludedByDefault(false); -} +using EntryFromIndex = std::function<LocatorFilterEntry(const IndexItem::Ptr &)>; -CppLocatorFilter::~CppLocatorFilter() = default; - -Core::LocatorFilterEntry CppLocatorFilter::filterEntryFromIndexItem(IndexItem::Ptr info) -{ - const QVariant id = QVariant::fromValue(info); - Core::LocatorFilterEntry filterEntry(this, info->scopedSymbolName(), id, info->icon()); - if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum) - filterEntry.extraInfo = info->shortNativeFilePath(); - else - filterEntry.extraInfo = info->symbolType(); - - return filterEntry; -} - -QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor( - QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) +void matchesFor(QPromise<void> &promise, const LocatorStorage &storage, + IndexItem::ItemType wantedType, const EntryFromIndex &converter) { - QList<Core::LocatorFilterEntry> entries[int(MatchLevel::Count)]; - const Qt::CaseSensitivity caseSensitivityForPrefix = caseSensitivity(entry); - const IndexItem::ItemType wanted = matchTypes(); - - const QRegularExpression regexp = createRegExp(entry); + const QString input = storage.input(); + LocatorFilterEntries entries[int(ILocatorFilter::MatchLevel::Count)]; + const Qt::CaseSensitivity caseSensitivityForPrefix = ILocatorFilter::caseSensitivity(input); + const QRegularExpression regexp = ILocatorFilter::createRegExp(input); if (!regexp.isValid()) - return {}; - const bool hasColonColon = entry.contains("::"); - const QRegularExpression shortRegexp = - hasColonColon ? createRegExp(entry.mid(entry.lastIndexOf("::") + 2)) : regexp; + return; - m_data->filterAllFiles([&](const IndexItem::Ptr &info) -> IndexItem::VisitorResult { - if (future.isCanceled()) + const bool hasColonColon = input.contains("::"); + const QRegularExpression shortRegexp = hasColonColon + ? ILocatorFilter::createRegExp(input.mid(input.lastIndexOf("::") + 2)) : regexp; + CppLocatorData *locatorData = CppModelManager::instance()->locatorData(); + locatorData->filterAllFiles([&](const IndexItem::Ptr &info) { + if (promise.isCanceled()) return IndexItem::Break; const IndexItem::ItemType type = info->type(); - if (type & wanted) { + if (type & wantedType) { const QString symbolName = info->symbolName(); QString matchString = hasColonColon ? info->scopedSymbolName() : symbolName; int matchOffset = hasColonColon ? matchString.size() - symbolName.size() : 0; @@ -70,7 +59,7 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor( } if (match.hasMatch()) { - Core::LocatorFilterEntry filterEntry = filterEntryFromIndexItem(info); + LocatorFilterEntry filterEntry = converter(info); // Highlight the matched characters, therefore it may be necessary // to update the match if the displayName is different from matchString @@ -78,103 +67,323 @@ QList<Core::LocatorFilterEntry> CppLocatorFilter::matchesFor( match = shortRegexp.match(filterEntry.displayName); matchOffset = 0; } - filterEntry.highlightInfo = highlightInfo(match); + filterEntry.highlightInfo = ILocatorFilter::highlightInfo(match); if (matchInParameterList && filterEntry.highlightInfo.startsDisplay.isEmpty()) { match = regexp.match(filterEntry.extraInfo); - filterEntry.highlightInfo - = highlightInfo(match, Core::LocatorFilterEntry::HighlightInfo::ExtraInfo); + filterEntry.highlightInfo = ILocatorFilter::highlightInfo( + match, LocatorFilterEntry::HighlightInfo::ExtraInfo); } else if (matchOffset > 0) { for (int &start : filterEntry.highlightInfo.startsDisplay) start -= matchOffset; } if (matchInParameterList) - entries[int(MatchLevel::Normal)].append(filterEntry); - else if (filterEntry.displayName.startsWith(entry, caseSensitivityForPrefix)) - entries[int(MatchLevel::Best)].append(filterEntry); - else if (filterEntry.displayName.contains(entry, caseSensitivityForPrefix)) - entries[int(MatchLevel::Better)].append(filterEntry); + entries[int(ILocatorFilter::MatchLevel::Normal)].append(filterEntry); + else if (filterEntry.displayName.startsWith(input, caseSensitivityForPrefix)) + entries[int(ILocatorFilter::MatchLevel::Best)].append(filterEntry); + else if (filterEntry.displayName.contains(input, caseSensitivityForPrefix)) + entries[int(ILocatorFilter::MatchLevel::Better)].append(filterEntry); else - entries[int(MatchLevel::Good)].append(filterEntry); + entries[int(ILocatorFilter::MatchLevel::Good)].append(filterEntry); } } if (info->type() & IndexItem::Enum) return IndexItem::Continue; - else - return IndexItem::Recurse; + return IndexItem::Recurse; }); for (auto &entry : entries) { if (entry.size() < 1000) - Utils::sort(entry, Core::LocatorFilterEntry::compareLexigraphically); + Utils::sort(entry, LocatorFilterEntry::compareLexigraphically); } - return std::accumulate(std::begin(entries), std::end(entries), QList<Core::LocatorFilterEntry>()); + storage.reportOutput(std::accumulate(std::begin(entries), std::end(entries), + LocatorFilterEntries())); +} + +LocatorMatcherTask locatorMatcher(IndexItem::ItemType type, const EntryFromIndex &converter) +{ + using namespace Tasking; + + TreeStorage<LocatorStorage> storage; + + const auto onSetup = [=](Async<void> &async) { + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + async.setConcurrentCallData(matchesFor, *storage, type, converter); + }; + return {AsyncTask<void>(onSetup), storage}; +} + +LocatorMatcherTask allSymbolsMatcher() +{ + const auto converter = [](const IndexItem::Ptr &info) { + LocatorFilterEntry filterEntry; + filterEntry.displayName = info->scopedSymbolName(); + filterEntry.displayIcon = info->icon(); + filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; + if (info->type() == IndexItem::Class || info->type() == IndexItem::Enum) + filterEntry.extraInfo = info->shortNativeFilePath(); + else + filterEntry.extraInfo = info->symbolType(); + return filterEntry; + }; + return locatorMatcher(IndexItem::All, converter); } -void CppLocatorFilter::accept(const Core::LocatorFilterEntry &selection, - QString *newText, int *selectionStart, int *selectionLength) const +LocatorMatcherTask classMatcher() { - Q_UNUSED(newText) - Q_UNUSED(selectionStart) - Q_UNUSED(selectionLength) - IndexItem::Ptr info = qvariant_cast<IndexItem::Ptr>(selection.internalData); - Core::EditorManager::openEditorAt({info->filePath(), info->line(), info->column()}, - {}, - Core::EditorManager::AllowExternalEditor); + const auto converter = [](const IndexItem::Ptr &info) { + LocatorFilterEntry filterEntry; + filterEntry.displayName = info->symbolName(); + filterEntry.displayIcon = info->icon(); + filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; + filterEntry.extraInfo = info->symbolScope().isEmpty() + ? info->shortNativeFilePath() + : info->symbolScope(); + filterEntry.filePath = info->filePath(); + return filterEntry; + }; + return locatorMatcher(IndexItem::Class, converter); } -CppClassesFilter::CppClassesFilter(CppLocatorData *locatorData) - : CppLocatorFilter(locatorData) +LocatorMatcherTask functionMatcher() +{ + const auto converter = [](const IndexItem::Ptr &info) { + QString name = info->symbolName(); + QString extraInfo = info->symbolScope(); + info->unqualifiedNameAndScope(name, &name, &extraInfo); + if (extraInfo.isEmpty()) + extraInfo = info->shortNativeFilePath(); + else + extraInfo.append(" (" + info->filePath().fileName() + ')'); + LocatorFilterEntry filterEntry; + filterEntry.displayName = name + info->symbolType(); + filterEntry.displayIcon = info->icon(); + filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; + filterEntry.extraInfo = extraInfo; + + return filterEntry; + }; + return locatorMatcher(IndexItem::Function, converter); +} + +QList<IndexItem::Ptr> itemsOfCurrentDocument(const FilePath ¤tFileName) +{ + if (currentFileName.isEmpty()) + return {}; + + QList<IndexItem::Ptr> results; + const Snapshot snapshot = CppModelManager::instance()->snapshot(); + if (const Document::Ptr thisDocument = snapshot.document(currentFileName)) { + SearchSymbols search; + search.setSymbolsToSearchFor(SymbolSearcher::Declarations | + SymbolSearcher::Enums | + SymbolSearcher::Functions | + SymbolSearcher::Classes); + IndexItem::Ptr rootNode = search(thisDocument); + rootNode->visitAllChildren([&](const IndexItem::Ptr &info) { + results.append(info); + return IndexItem::Recurse; + }); + } + return results; +} + +LocatorFilterEntry::HighlightInfo highlightInfo(const QRegularExpressionMatch &match, + LocatorFilterEntry::HighlightInfo::DataType dataType) +{ + const FuzzyMatcher::HighlightingPositions positions = + FuzzyMatcher::highlightingPositions(match); + + return LocatorFilterEntry::HighlightInfo(positions.starts, positions.lengths, dataType); +} + +void matchesForCurrentDocument(QPromise<void> &promise, const LocatorStorage &storage, + const FilePath ¤tFileName) +{ + const QString input = storage.input(); + const QRegularExpression regexp = ILocatorFilter::createRegExp(input); + if (!regexp.isValid()) + return; + + struct Entry + { + LocatorFilterEntry entry; + IndexItem::Ptr info; + }; + QList<Entry> goodEntries; + QList<Entry> betterEntries; + const QList<IndexItem::Ptr> items = itemsOfCurrentDocument(currentFileName); + for (const IndexItem::Ptr &info : items) { + if (promise.isCanceled()) + break; + + QString matchString = info->symbolName(); + if (info->type() == IndexItem::Declaration) + matchString = info->representDeclaration(); + else if (info->type() == IndexItem::Function) + matchString += info->symbolType(); + + QRegularExpressionMatch match = regexp.match(matchString); + if (match.hasMatch()) { + const bool betterMatch = match.capturedStart() == 0; + QString name = matchString; + QString extraInfo = info->symbolScope(); + if (info->type() == IndexItem::Function) { + if (info->unqualifiedNameAndScope(matchString, &name, &extraInfo)) { + name += info->symbolType(); + match = regexp.match(name); + } + } + + LocatorFilterEntry filterEntry; + filterEntry.displayName = name; + filterEntry.displayIcon = info->icon(); + filterEntry.linkForEditor = {info->filePath(), info->line(), info->column()}; + filterEntry.extraInfo = extraInfo; + if (match.hasMatch()) { + filterEntry.highlightInfo = highlightInfo(match, + LocatorFilterEntry::HighlightInfo::DisplayName); + } else { + match = regexp.match(extraInfo); + filterEntry.highlightInfo = + highlightInfo(match, LocatorFilterEntry::HighlightInfo::ExtraInfo); + } + + if (betterMatch) + betterEntries.append({filterEntry, info}); + else + goodEntries.append({filterEntry, info}); + } + } + + // entries are unsorted by design! + betterEntries += goodEntries; + + QHash<QString, QList<Entry>> possibleDuplicates; + for (const Entry &e : std::as_const(betterEntries)) + possibleDuplicates[e.info->scopedSymbolName() + e.info->symbolType()] << e; + for (auto it = possibleDuplicates.cbegin(); it != possibleDuplicates.cend(); ++it) { + const QList<Entry> &duplicates = it.value(); + if (duplicates.size() == 1) + continue; + QList<Entry> declarations; + QList<Entry> definitions; + for (const Entry &candidate : duplicates) { + const IndexItem::Ptr info = candidate.info; + if (info->type() != IndexItem::Function) + break; + if (info->isFunctionDefinition()) + definitions << candidate; + else + declarations << candidate; + } + if (definitions.size() == 1 + && declarations.size() + definitions.size() == duplicates.size()) { + for (const Entry &decl : std::as_const(declarations)) { + Utils::erase(betterEntries, [&decl](const Entry &e) { + return e.info == decl.info; + }); + } + } + } + storage.reportOutput(Utils::transform(betterEntries, + [](const Entry &entry) { return entry.entry; })); +} + +FilePath currentFileName() +{ + IEditor *currentEditor = EditorManager::currentEditor(); + return currentEditor ? currentEditor->document()->filePath() : FilePath(); +} + +LocatorMatcherTask currentDocumentMatcher() +{ + using namespace Tasking; + + TreeStorage<LocatorStorage> storage; + + const auto onSetup = [=](Async<void> &async) { + async.setFutureSynchronizer(ExtensionSystem::PluginManager::futureSynchronizer()); + async.setConcurrentCallData(matchesForCurrentDocument, *storage, currentFileName()); + }; + return {AsyncTask<void>(onSetup), storage}; +} + +using MatcherCreator = std::function<Core::LocatorMatcherTask()>; + +static MatcherCreator creatorForType(MatcherType type) +{ + switch (type) { + case MatcherType::AllSymbols: return &allSymbolsMatcher; + case MatcherType::Classes: return &classMatcher; + case MatcherType::Functions: return &functionMatcher; + case MatcherType::CurrentDocumentSymbols: return ¤tDocumentMatcher; + } + return {}; +} + +LocatorMatcherTasks cppMatchers(MatcherType type) +{ + const MatcherCreator creator = creatorForType(type); + if (!creator) + return {}; + return {creator()}; +} + +CppAllSymbolsFilter::CppAllSymbolsFilter() +{ + setId(Constants::LOCATOR_FILTER_ID); + setDisplayName(Tr::tr(Constants::LOCATOR_FILTER_DISPLAY_NAME)); + setDescription(Tr::tr(Constants::LOCATOR_FILTER_DESCRIPTION)); + setDefaultShortcutString(":"); +} + +LocatorMatcherTasks CppAllSymbolsFilter::matchers() +{ + return {allSymbolsMatcher()}; +} + + +CppClassesFilter::CppClassesFilter() { setId(Constants::CLASSES_FILTER_ID); setDisplayName(Tr::tr(Constants::CLASSES_FILTER_DISPLAY_NAME)); + setDescription(Tr::tr(Constants::CLASSES_FILTER_DESCRIPTION)); setDefaultShortcutString("c"); - setDefaultIncludedByDefault(false); } -CppClassesFilter::~CppClassesFilter() = default; - -Core::LocatorFilterEntry CppClassesFilter::filterEntryFromIndexItem(IndexItem::Ptr info) +LocatorMatcherTasks CppClassesFilter::matchers() { - const QVariant id = QVariant::fromValue(info); - Core::LocatorFilterEntry filterEntry(this, info->symbolName(), id, info->icon()); - filterEntry.extraInfo = info->symbolScope().isEmpty() - ? info->shortNativeFilePath() - : info->symbolScope(); - filterEntry.filePath = info->filePath(); - return filterEntry; + return {classMatcher()}; } -CppFunctionsFilter::CppFunctionsFilter(CppLocatorData *locatorData) - : CppLocatorFilter(locatorData) +CppFunctionsFilter::CppFunctionsFilter() { setId(Constants::FUNCTIONS_FILTER_ID); setDisplayName(Tr::tr(Constants::FUNCTIONS_FILTER_DISPLAY_NAME)); + setDescription(Tr::tr(Constants::FUNCTIONS_FILTER_DESCRIPTION)); setDefaultShortcutString("m"); - setDefaultIncludedByDefault(false); } -CppFunctionsFilter::~CppFunctionsFilter() = default; - -Core::LocatorFilterEntry CppFunctionsFilter::filterEntryFromIndexItem(IndexItem::Ptr info) +LocatorMatcherTasks CppFunctionsFilter::matchers() { - const QVariant id = QVariant::fromValue(info); - - QString name = info->symbolName(); - QString extraInfo = info->symbolScope(); - info->unqualifiedNameAndScope(name, &name, &extraInfo); - if (extraInfo.isEmpty()) { - extraInfo = info->shortNativeFilePath(); - } else { - extraInfo.append(" (" + info->filePath().fileName() + ')'); - } + return {functionMatcher()}; +} - Core::LocatorFilterEntry filterEntry(this, name + info->symbolType(), id, info->icon()); - filterEntry.extraInfo = extraInfo; +CppCurrentDocumentFilter::CppCurrentDocumentFilter() +{ + setId(Constants::CURRENT_DOCUMENT_FILTER_ID); + setDisplayName(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DISPLAY_NAME)); + setDescription(Tr::tr(Constants::CURRENT_DOCUMENT_FILTER_DESCRIPTION)); + setDefaultShortcutString("."); + setPriority(High); +} - return filterEntry; +LocatorMatcherTasks CppCurrentDocumentFilter::matchers() +{ + return {currentDocumentMatcher()}; } } // namespace CppEditor |