diff --git a/src/libs/clangsupport/commandlinebuilder.h b/src/libs/clangsupport/commandlinebuilder.h index aa384f1180d..7367f080360 100644 --- a/src/libs/clangsupport/commandlinebuilder.h +++ b/src/libs/clangsupport/commandlinebuilder.h @@ -48,10 +48,11 @@ public: FilePathView outputPath = {}, FilePathView includePchPath = {}) { - commandLine.reserve(128); + commandLine.reserve(1024); addCompiler(projectInfo.language); addToolChainArguments(toolChainArguments); + addExtraFlags(); addLanguage(projectInfo, sourceType); addLanguageVersion(projectInfo); addNoStdIncAndNoStdLibInc(projectInfo.language); @@ -79,6 +80,8 @@ public: commandLine.emplace_back(argument); } + void addExtraFlags() { commandLine.emplace_back("-DNOMINMAX"); } + static const char *language(const ProjectInfo &projectInfo, InputFileType sourceType) { switch (projectInfo.language) { diff --git a/src/libs/clangsupport/filecontainerv2.cpp b/src/libs/clangsupport/filecontainerv2.cpp index 96cc528750a..af5d89a787b 100644 --- a/src/libs/clangsupport/filecontainerv2.cpp +++ b/src/libs/clangsupport/filecontainerv2.cpp @@ -30,9 +30,9 @@ namespace V2 { QDebug operator<<(QDebug debug, const FileContainer &container) { - debug.nospace() << "FileContainer(" - << container.filePath << ", " + debug.nospace() << "FileContainer(" << container.filePath << ", " << container.commandLineArguments << ", " + << container.unsavedFileContent.hasContent() << ", " << container.documentRevision; debug.nospace() << ")"; diff --git a/src/libs/utils/textutils.cpp b/src/libs/utils/textutils.cpp index f72dd89321c..21fb3caf511 100644 --- a/src/libs/utils/textutils.cpp +++ b/src/libs/utils/textutils.cpp @@ -140,6 +140,13 @@ QTextCursor wordStartCursor(const QTextCursor &textCursor) return cursor; } +QString wordUnderCursor(const QTextCursor &cursor) +{ + QTextCursor tc(cursor); + tc.select(QTextCursor::WordUnderCursor); + return tc.selectedText(); +} + int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffer, int line) { if (textDocument->blockCount() < line) diff --git a/src/libs/utils/textutils.h b/src/libs/utils/textutils.h index 1d4e8f7ffbf..895023903ee 100644 --- a/src/libs/utils/textutils.h +++ b/src/libs/utils/textutils.h @@ -53,6 +53,7 @@ QTCREATOR_UTILS_EXPORT QTextCursor selectAt(QTextCursor textCursor, uint line, u QTCREATOR_UTILS_EXPORT QTextCursor flippedCursor(const QTextCursor &cursor); QTCREATOR_UTILS_EXPORT QTextCursor wordStartCursor(const QTextCursor &cursor); +QTCREATOR_UTILS_EXPORT QString wordUnderCursor(const QTextCursor &cursor); QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffer, diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index 1b316bea55f..6d06b058122 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -39,9 +39,6 @@ namespace ClangFormat { static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style, ReplacementsToKeep replacementsToKeep) { - if (replacementsToKeep == ReplacementsToKeep::All) - return; - style.MaxEmptyLinesToKeep = 2; style.SortIncludes = false; style.SortUsingDeclarations = false; @@ -67,9 +64,10 @@ static llvm::StringRef clearExtraNewline(llvm::StringRef text) } static clang::tooling::Replacements filteredReplacements( + const QByteArray &buffer, const clang::tooling::Replacements &replacements, - int offset, - int utf8LineLengthBeforeCursor, + int utf8Offset, + int utf8Length, int extraEmptySpaceOffset, ReplacementsToKeep replacementsToKeep) { @@ -77,14 +75,13 @@ static clang::tooling::Replacements filteredReplacements( for (const clang::tooling::Replacement &replacement : replacements) { int replacementOffset = static_cast(replacement.getOffset()); const bool replacementDoesNotMatchRestriction - = (replacementsToKeep == ReplacementsToKeep::OnlyIndent - && replacementOffset != offset - 1) - || (replacementsToKeep == ReplacementsToKeep::IndentAndBefore - && replacementOffset > offset + utf8LineLengthBeforeCursor - 1); + = replacementOffset >= utf8Offset + utf8Length + || (replacementsToKeep == ReplacementsToKeep::OnlyIndent + && (replacementOffset < utf8Offset - 1 || buffer.at(replacementOffset) != '\n')); if (replacementDoesNotMatchRestriction) continue; - if (replacementOffset >= offset - 1) + if (replacementOffset >= utf8Offset - 1) replacementOffset += extraEmptySpaceOffset; llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent @@ -151,13 +148,13 @@ static int previousEmptyLinesLength(const QTextBlock ¤tBlock) } static void modifyToIndentEmptyLines( - QByteArray &buffer, int offset, int &length, const QTextBlock &block, bool secondTry) + QByteArray &buffer, int utf8Offset, const QTextBlock &block, bool secondTry) { const QString blockText = block.text(); int firstNonWhitespace = Utils::indexOf(blockText, [](const QChar &ch) { return !ch.isSpace(); }); if (firstNonWhitespace > 0) - offset += firstNonWhitespace; + utf8Offset += firstNonWhitespace; const bool closingParenBlock = firstNonWhitespace >= 0 && blockText.at(firstNonWhitespace) == ')'; @@ -176,12 +173,11 @@ static void modifyToIndentEmptyLines( if (closingParenBlock || prevBlock.text().endsWith(',')) dummyText = "&& a"; - length += dummyText.length(); - buffer.insert(offset, dummyText); + buffer.insert(utf8Offset, dummyText); } if (secondTry) { - int nextLinePos = buffer.indexOf('\n', offset); + int nextLinePos = buffer.indexOf('\n', utf8Offset); if (nextLinePos < 0) nextLinePos = buffer.size() - 1; @@ -190,7 +186,6 @@ static void modifyToIndentEmptyLines( // unclosed parentheses. // TODO: Does it help to add different endings depending on the context? buffer.insert(nextLinePos, ')'); - length += 1; } } } @@ -251,13 +246,13 @@ static TextEditor::Replacements utf16Replacements(const QTextBlock &block, return convertedReplacements; } -static void applyReplacements(const QTextBlock &block, const TextEditor::Replacements &replacements) +static void applyReplacements(QTextDocument *doc, const TextEditor::Replacements &replacements) { if (replacements.empty()) return; int fullOffsetShift = 0; - QTextCursor editCursor(block); + QTextCursor editCursor(doc); for (const TextEditor::Replacement &replacement : replacements) { editCursor.beginEditBlock(); editCursor.setPosition(replacement.offset + fullOffsetShift); @@ -305,24 +300,12 @@ void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, int cursorPositionInEditor) { if (cursor.hasSelection()) { - // Calling currentBlock.next() might be unsafe because we change the document. - // Let's operate with block numbers instead. - const int startNumber = m_doc->findBlock(cursor.selectionStart()).blockNumber(); - const int endNumber = m_doc->findBlock(cursor.selectionEnd()).blockNumber(); - for (int currentBlockNumber = startNumber; currentBlockNumber <= endNumber; - ++currentBlockNumber) { - const QTextBlock currentBlock = m_doc->findBlockByNumber(currentBlockNumber); - if (currentBlock.isValid()) { - const int blocksAmount = m_doc->blockCount(); - indentBlock(currentBlock, typedChar, cursorPositionInEditor); - - // Only blocks before current might be added/removed, so it's safe to modify the index. - if (blocksAmount != m_doc->blockCount()) - currentBlockNumber += (m_doc->blockCount() - blocksAmount); - } - } + indentBlocks(m_doc->findBlock(cursor.selectionStart()), + m_doc->findBlock(cursor.selectionEnd()), + typedChar, + cursorPositionInEditor); } else { - indentBlock(cursor.block(), typedChar, cursorPositionInEditor); + indentBlocks(cursor.block(), cursor.block(), typedChar, cursorPositionInEditor); } } @@ -341,70 +324,55 @@ void ClangFormatBaseIndenter::reindent(const QTextCursor &cursor, indent(cursor, QChar::Null, cursorPositionInEditor); } -TextEditor::Replacements ClangFormatBaseIndenter::format(const QTextCursor &cursor, - int cursorPositionInEditor) +TextEditor::Replacements ClangFormatBaseIndenter::format( + const TextEditor::RangesInLines &rangesInLines) { - int utf8Offset; - int utf8Length; + if (rangesInLines.empty()) + return TextEditor::Replacements(); + + int utf8Offset = -1; QTextBlock block; const QByteArray buffer = m_doc->toPlainText().toUtf8(); - if (cursor.hasSelection()) { - block = m_doc->findBlock(cursor.selectionStart()); - const QTextBlock end = m_doc->findBlock(cursor.selectionEnd()); - utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); - QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements();); - utf8Length = selectedLines(m_doc, block, end).toUtf8().size(); - } else { - block = cursor.block(); - utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); - QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements();); + std::vector ranges; + ranges.reserve(rangesInLines.size()); - utf8Length = block.text().toUtf8().size(); + for (auto &range : rangesInLines) { + const int utf8StartOffset = Utils::Text::utf8NthLineOffset(m_doc, buffer, range.startLine); + const QTextBlock end = m_doc->findBlockByNumber(range.endLine - 1); + int utf8RangeLength = end.text().toUtf8().size(); + if (range.endLine > range.startLine) { + utf8RangeLength += Utils::Text::utf8NthLineOffset(m_doc, buffer, range.endLine) + - utf8StartOffset; + } + ranges.emplace_back(static_cast(utf8StartOffset), + static_cast(utf8RangeLength)); + + if (utf8Offset < 0) { + utf8Offset = utf8StartOffset; + block = m_doc->findBlockByNumber(range.startLine - 1); + } } - const TextEditor::Replacements toReplace = replacements(buffer, - utf8Offset, - utf8Length, - block, - cursorPositionInEditor, - ReplacementsToKeep::All, - QChar::Null); - applyReplacements(block, toReplace); + clang::format::FormatStyle style = styleForFile(); + clang::format::FormattingAttemptStatus status; + const clang::tooling::Replacements clangReplacements + = reformat(style, buffer.data(), ranges, m_fileName.toString().toStdString(), &status); + const TextEditor::Replacements toReplace = utf16Replacements(block, + utf8Offset, + buffer, + clangReplacements); + applyReplacements(m_doc, toReplace); return toReplace; } -TextEditor::Replacements ClangFormatBaseIndenter::format( - const QTextCursor &cursor, - const TextEditor::TabSettings & /*tabSettings*/, - int cursorPositionInEditor) -{ - return format(cursor, cursorPositionInEditor); -} - -void ClangFormatBaseIndenter::indentBeforeCursor(const QTextBlock &block, - const QChar &typedChar, - int cursorPositionInEditor) -{ - const QByteArray buffer = m_doc->toPlainText().toUtf8(); - const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); - QTC_ASSERT(utf8Offset >= 0, return;); - const TextEditor::Replacements toReplace = replacements(buffer, - utf8Offset, - 0, - block, - cursorPositionInEditor, - ReplacementsToKeep::IndentAndBefore, - typedChar); - applyReplacements(block, toReplace); -} - static bool doNotIndentInContext(QTextDocument *doc, int pos) { const QChar character = doc->characterAt(pos); const QTextBlock currentBlock = doc->findBlock(pos); const QString text = currentBlock.text().left(pos - currentBlock.position()); + // NOTE: check if "<<" and ">>" always work correctly. switch (character.toLatin1()) { default: break; @@ -419,33 +387,30 @@ static bool doNotIndentInContext(QTextDocument *doc, int pos) if (pos > 0 && doc->characterAt(pos - 1) != ':') return true; break; - case '<': - case '>': - // "<<" and ">>" could be problematic - if (pos > 0 && doc->characterAt(pos - 1) == character) - return true; - break; } return false; } -void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, - const QChar &typedChar, - int cursorPositionInEditor) +void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock, + const QTextBlock &endBlock, + const QChar &typedChar, + int cursorPositionInEditor) { - QTextBlock currentBlock = block; - const int blockPosition = currentBlock.position(); - trimFirstNonEmptyBlock(currentBlock); - if (typedChar != QChar::Null && cursorPositionInEditor > 0 && m_doc->characterAt(cursorPositionInEditor - 1) == typedChar && doNotIndentInContext(m_doc, cursorPositionInEditor - 1)) { return; } + const int startBlockPosition = startBlock.position(); + trimFirstNonEmptyBlock(startBlock); + if (cursorPositionInEditor >= 0) + cursorPositionInEditor += startBlock.position() - startBlockPosition; + + ReplacementsToKeep replacementsToKeep = ReplacementsToKeep::OnlyIndent; if (formatWhileTyping() - && (cursorPositionInEditor == -1 || cursorPositionInEditor >= blockPosition) + && (cursorPositionInEditor == -1 || cursorPositionInEditor >= startBlockPosition) && (typedChar == QChar::Null || typedChar == ';' || typedChar == '}')) { // Format before current position only in case the cursor is inside the indented block. // So if cursor position is less then the block position then the current line is before @@ -453,26 +418,23 @@ void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, // cursorPositionInEditor == -1 means the consition matches automatically. // Format only before newline or complete statement not to break code. - if (cursorPositionInEditor >= 0) - cursorPositionInEditor += currentBlock.position() - blockPosition; - else - cursorPositionInEditor = currentBlock.position(); - - indentBeforeCursor(currentBlock, typedChar, cursorPositionInEditor); - return; + replacementsToKeep = ReplacementsToKeep::IndentAndBefore; } const QByteArray buffer = m_doc->toPlainText().toUtf8(); - const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); + const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, + buffer, + startBlock.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return;); + const int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size(); - applyReplacements(currentBlock, + applyReplacements(m_doc, replacements(buffer, utf8Offset, - 0, - currentBlock, - cursorPositionInEditor, - ReplacementsToKeep::OnlyIndent, + utf8Length, + startBlock, + endBlock, + replacementsToKeep, typedChar)); } @@ -481,12 +443,13 @@ void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { - indentBlock(block, typedChar, cursorPositionInEditor); + indentBlocks(block, block, typedChar, cursorPositionInEditor); } -int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, int cursorPositionInEditor) +int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, int /*cursorPositionInEditor*/) { trimFirstNonEmptyBlock(block); + const QByteArray buffer = m_doc->toPlainText().toUtf8(); const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1); QTC_ASSERT(utf8Offset >= 0, return 0;); @@ -495,7 +458,7 @@ int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, int cursorPositi utf8Offset, 0, block, - cursorPositionInEditor, + block, ReplacementsToKeep::OnlyIndent); if (toReplace.empty()) @@ -535,10 +498,19 @@ void ClangFormatBaseIndenter::formatOrIndent(const QTextCursor &cursor, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { - if (formatCodeInsteadOfIndent()) - format(cursor, cursorPositionInEditor); - else + if (formatCodeInsteadOfIndent()) { + QTextBlock start; + QTextBlock end; + if (cursor.hasSelection()) { + start = m_doc->findBlock(cursor.selectionStart()); + end = m_doc->findBlock(cursor.selectionEnd()); + } else { + start = end = cursor.block(); + } + format({{start.blockNumber() + 1, end.blockNumber() + 1}}); + } else { indent(cursor, QChar::Null, cursorPositionInEditor); + } } clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const @@ -574,43 +546,38 @@ static int formattingRangeStart(const QTextBlock ¤tBlock, TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer, int utf8Offset, int utf8Length, - const QTextBlock &block, - int cursorPositionInEditor, + const QTextBlock &startBlock, + const QTextBlock &endBlock, ReplacementsToKeep replacementsToKeep, const QChar &typedChar, bool secondTry) const { + QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return TextEditor::Replacements()); + clang::format::FormatStyle style = styleForFile(); int originalOffsetUtf8 = utf8Offset; int originalLengthUtf8 = utf8Length; QByteArray originalBuffer = buffer; - int utf8LineLengthBeforeCursor = 0; - if (cursorPositionInEditor > 0 && typedChar != QChar::Null) { - // Format starting with the electric character if it's present. - utf8LineLengthBeforeCursor - = block.text().left(cursorPositionInEditor - block.position()).toUtf8().size(); + int rangeStart = 0; + if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) + rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision()); + + int extraEmptySpaceOffset = previousEmptyLinesLength(startBlock); + utf8Offset -= extraEmptySpaceOffset; + buffer.remove(utf8Offset, extraEmptySpaceOffset); + + adjustFormatStyleForLineBreak(style, replacementsToKeep); + if (typedChar == QChar::Null && startBlock == endBlock) { + modifyToIndentEmptyLines(buffer, utf8Offset, startBlock, secondTry); + utf8Length = 0; } - int extraEmptySpaceOffset = 0; - int rangeStart = 0; - if (replacementsToKeep != ReplacementsToKeep::All) { - if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) - rangeStart = formattingRangeStart(block, buffer, lastSaveRevision()); - - extraEmptySpaceOffset = previousEmptyLinesLength(block); - utf8Offset -= extraEmptySpaceOffset; - buffer.remove(utf8Offset, extraEmptySpaceOffset); - - adjustFormatStyleForLineBreak(style, replacementsToKeep); - modifyToIndentEmptyLines(buffer, utf8Offset, utf8Length, block, secondTry); - - if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) { - buffer.insert(utf8Offset - 1, " //"); - extraEmptySpaceOffset -= 3; - utf8Offset += 3; - } + if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) { + buffer.insert(utf8Offset - 1, " //"); + extraEmptySpaceOffset -= 3; + utf8Offset += 3; } if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart) @@ -630,9 +597,10 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer clang::tooling::Replacements filtered; if (status.FormatComplete) { - filtered = filteredReplacements(clangReplacements, + filtered = filteredReplacements(buffer, + clangReplacements, utf8Offset, - utf8LineLengthBeforeCursor, + utf8Length, extraEmptySpaceOffset, replacementsToKeep); } @@ -642,14 +610,14 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer return replacements(originalBuffer, originalOffsetUtf8, originalLengthUtf8, - block, - cursorPositionInEditor, + startBlock, + endBlock, replacementsToKeep, typedChar, true); } - return utf16Replacements(block, originalOffsetUtf8, originalBuffer, filtered); + return utf16Replacements(startBlock, originalOffsetUtf8, originalBuffer, filtered); } } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h index 2017f8ba646..272cbd648fd 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.h +++ b/src/plugins/clangformat/clangformatbaseindenter.h @@ -53,9 +53,8 @@ public: void formatOrIndent(const QTextCursor &cursor, const TextEditor::TabSettings &tabSettings, int cursorPositionInEditor = -1) override; - TextEditor::Replacements format(const QTextCursor &cursor, - const TextEditor::TabSettings &tabSettings, - int cursorPositionInEditor = -1) override; + TextEditor::Replacements format( + const TextEditor::RangesInLines &rangesInLines = TextEditor::RangesInLines()) override; void indentBlock(const QTextBlock &block, const QChar &typedChar, @@ -75,18 +74,17 @@ protected: virtual int lastSaveRevision() const { return 0; } private: - TextEditor::Replacements format(const QTextCursor &cursor, int cursorPositionInEditor); void indent(const QTextCursor &cursor, const QChar &typedChar, int cursorPositionInEditor); - void indentBlock(const QTextBlock &block, const QChar &typedChar, int cursorPositionInEditor); + void indentBlocks(const QTextBlock &startBlock, + const QTextBlock &endBlock, + const QChar &typedChar, + int cursorPositionInEditor); int indentFor(const QTextBlock &block, int cursorPositionInEditor); - void indentBeforeCursor(const QTextBlock &block, - const QChar &typedChar, - int cursorPositionInEditor); TextEditor::Replacements replacements(QByteArray buffer, int utf8Offset, int utf8Length, - const QTextBlock &block, - int cursorPositionInEditor, + const QTextBlock &startBlock, + const QTextBlock &endBlock, ReplacementsToKeep replacementsToKeep, const QChar &typedChar = QChar::Null, bool secondTry = false) const; diff --git a/src/plugins/clangpchmanager/projectupdater.cpp b/src/plugins/clangpchmanager/projectupdater.cpp index 74b63a8f1ad..4e5cf950178 100644 --- a/src/plugins/clangpchmanager/projectupdater.cpp +++ b/src/plugins/clangpchmanager/projectupdater.cpp @@ -37,6 +37,9 @@ #include #include #include +#include +#include +#include #include @@ -140,8 +143,10 @@ HeaderAndSources ProjectUpdater::headerAndSourcesFromProjectPart( HeaderAndSources headerAndSources; headerAndSources.reserve(std::size_t(projectPart->files.size()) * 3 / 2); - for (const CppTools::ProjectFile &projectFile : projectPart->files) - addToHeaderAndSources(headerAndSources, projectFile); + for (const CppTools::ProjectFile &projectFile : projectPart->files) { + if (projectFile.active) + addToHeaderAndSources(headerAndSources, projectFile); + } std::sort(headerAndSources.sources.begin(), headerAndSources.sources.end()); std::sort(headerAndSources.headers.begin(), headerAndSources.headers.end()); @@ -231,6 +236,21 @@ ClangBackEnd::IncludeSearchPaths convertToIncludeSearchPaths( return paths; } +QString projectDirectory(ProjectExplorer::Project *project) +{ + if (project) + return project->rootProjectDirectory().toString(); + + return {}; +} + +QString buildDirectory(ProjectExplorer::Project *project) +{ + if (project && project->activeTarget() && project->activeTarget()->activeBuildConfiguration()) + return project->activeTarget()->activeBuildConfiguration()->buildDirectory().toString(); + + return {}; +} } // namespace ProjectUpdater::SystemAndProjectIncludeSearchPaths ProjectUpdater::createIncludeSearchPaths( @@ -239,7 +259,9 @@ ProjectUpdater::SystemAndProjectIncludeSearchPaths ProjectUpdater::createInclude CppTools::HeaderPathFilter filter(projectPart, CppTools::UseTweakedHeaderPaths::Yes, CLANG_VERSION, - CLANG_RESOURCE_DIR); + CLANG_RESOURCE_DIR, + projectDirectory(projectPart.project), + buildDirectory(projectPart.project)); filter.process(); return {convertToIncludeSearchPaths(filter.systemHeaderPaths, filter.builtInHeaderPaths), @@ -277,6 +299,13 @@ ClangBackEnd::ProjectPartContainers ProjectUpdater::toProjectPartContainers( std::vector projectPartContainers; projectPartContainers.reserve(projectParts.size()); + projectParts.erase(std::remove_if(projectParts.begin(), + projectParts.end(), + [](const CppTools::ProjectPart *projectPart) { + return !projectPart->selectedForBuilding; + }), + projectParts.end()); + std::transform(projectParts.begin(), projectParts.end(), std::back_inserter(projectPartContainers), diff --git a/src/plugins/clangpchmanager/qtcreatorprojectupdater.h b/src/plugins/clangpchmanager/qtcreatorprojectupdater.h index fa008f14aec..4d66a2f8c30 100644 --- a/src/plugins/clangpchmanager/qtcreatorprojectupdater.h +++ b/src/plugins/clangpchmanager/qtcreatorprojectupdater.h @@ -28,6 +28,7 @@ #include "pchmanagerprojectupdater.h" #include +#include #include @@ -49,8 +50,9 @@ CLANGPCHMANAGER_EXPORT std::vector createGenera CLANGPCHMANAGER_EXPORT std::vector createProjectParts(ProjectExplorer::Project *project); } -template -class QtCreatorProjectUpdater : public ProjectUpdaterType +template +class QtCreatorProjectUpdater : public ProjectUpdaterType, + public ProjectExplorer::ExtraCompilerFactoryObserver { public: template @@ -90,6 +92,15 @@ public: ProjectUpdaterType::removeGeneratedFiles({ClangBackEnd::FilePath{filePath}}); } +protected: + void newExtraCompiler(const ProjectExplorer::Project *, + const Utils::FileName &, + const Utils::FileNameList &targets) override + { + for (const Utils::FileName &target : targets) + abstractEditorUpdated(target.toString(), {}); + } + private: void connectToCppModelManager() { diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp index 88b17fb42a8..4be95a2744c 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.cpp @@ -89,6 +89,10 @@ bool FixitsRefactoringFile::apply() = CppTools::CppCodeStyleSettings::currentProjectTabSettings(); // Apply changes + std::unique_ptr indenter; + QString lastFilename; + ReplacementOperations operationsForFile; + for (int i=0; i < m_replacementOperations.size(); ++i) { ReplacementOperation &op = *m_replacementOperations[i]; if (op.apply) { @@ -103,15 +107,19 @@ bool FixitsRefactoringFile::apply() // Apply QTextDocument *doc = document(op.fileName); - std::unique_ptr indenter(factory->createIndenter(doc)); - indenter->setFileName(Utils::FileName::fromString(op.fileName)); + if (lastFilename != op.fileName) { + if (indenter) + format(*indenter, doc, operationsForFile, i); + operationsForFile.clear(); + indenter = std::unique_ptr(factory->createIndenter(doc)); + indenter->setFileName(Utils::FileName::fromString(op.fileName)); + } QTextCursor cursor(doc); cursor.setPosition(op.pos); cursor.setPosition(op.pos + op.length, QTextCursor::KeepAnchor); cursor.insertText(op.text); - - tryToFormat(*indenter, tabSettings, doc, op, i); + operationsForFile.push_back(&op); } } @@ -130,28 +138,29 @@ bool FixitsRefactoringFile::apply() return true; } -void FixitsRefactoringFile::tryToFormat(TextEditor::Indenter &indenter, - const TextEditor::TabSettings &tabSettings, - QTextDocument *doc, - const ReplacementOperation &op, - int currentIndex) +void FixitsRefactoringFile::format(TextEditor::Indenter &indenter, + QTextDocument *doc, + const ReplacementOperations &operationsForFile, + int firstOperationIndex) { - QTextCursor cursor(doc); - cursor.beginEditBlock(); - cursor.setPosition(op.pos); - cursor.movePosition(QTextCursor::Right, - QTextCursor::KeepAnchor, - op.text.length()); - const Replacements replacements = indenter.format(cursor, tabSettings); - cursor.endEditBlock(); + if (operationsForFile.isEmpty()) + return; + + TextEditor::RangesInLines ranges; + for (int i = 0; i < operationsForFile.size(); ++i) { + const ReplacementOperation &op = *operationsForFile.at(i); + const int start = doc->findBlock(op.pos).blockNumber() + 1; + const int end = doc->findBlock(op.pos + op.length).blockNumber() + 1; + ranges.push_back({start, end}); + } + const Replacements replacements = indenter.format(ranges); if (replacements.empty()) return; - if (hasIntersection(op.fileName, replacements, currentIndex + 1)) - doc->undo(&cursor); - else - shiftAffectedReplacements(op.fileName, replacements, currentIndex + 1); + shiftAffectedReplacements(operationsForFile.front()->fileName, + replacements, + firstOperationIndex + 1); } QTextDocument *FixitsRefactoringFile::document(const QString &filePath) const diff --git a/src/plugins/clangtools/clangfixitsrefactoringchanges.h b/src/plugins/clangtools/clangfixitsrefactoringchanges.h index c1fcf38c092..cf715b34872 100644 --- a/src/plugins/clangtools/clangfixitsrefactoringchanges.h +++ b/src/plugins/clangtools/clangfixitsrefactoringchanges.h @@ -67,11 +67,10 @@ private: QTextDocument *document(const QString &filePath) const; void shiftAffectedReplacements(const ReplacementOperation &op, int startIndex); - void tryToFormat(TextEditor::Indenter &indenter, - const TextEditor::TabSettings &tabSettings, - QTextDocument *doc, - const ReplacementOperation &op, - int currentIndex); + void format(TextEditor::Indenter &indenter, + QTextDocument *doc, + const ReplacementOperations &operationsForFile, + int firstOperationIndex); void shiftAffectedReplacements(const QString &fileName, const TextEditor::Replacements &replacements, int startIndex); diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index 0583a3aa0f3..5b3874b48e4 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -300,8 +300,8 @@ ClangTidyClazyTool::ClangTidyClazyTool() }); connect(m_applyFixitsButton, &QToolButton::clicked, [this]() { QVector diagnosticItems; - m_diagnosticModel->rootItem()->forChildrenAtLevel(2, [&](TreeItem *item){ - diagnosticItems += static_cast(item); + m_diagnosticModel->forItemsAtLevel<2>([&](DiagnosticItem *item){ + diagnosticItems += item; }); ApplyFixIts(diagnosticItems).apply(m_diagnosticModel); diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index 4225970221d..c52f670e703 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -81,7 +81,7 @@ private: }; ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) - : Utils::TreeModel<>(parent) + : ClangToolsDiagnosticModelBase(parent) , m_filesWatcher(std::make_unique()) { setHeader({tr("Diagnostic")}); @@ -146,7 +146,7 @@ void ClangToolsDiagnosticModel::clear() m_filePathToItem.clear(); m_diagnostics.clear(); clearAndSetupCache(); - Utils::TreeModel<>::clear(); + ClangToolsDiagnosticModelBase::clear(); } void ClangToolsDiagnosticModel::updateItems(const DiagnosticItem *changedItem) @@ -174,10 +174,9 @@ void ClangToolsDiagnosticModel::clearAndSetupCache() void ClangToolsDiagnosticModel::onFileChanged(const QString &path) { - rootItem()->forChildrenAtLevel(2, [&](Utils::TreeItem *item){ - auto diagnosticItem = static_cast(item); - if (diagnosticItem->diagnostic().location.filePath == path) - diagnosticItem->setFixItStatus(FixitStatus::Invalidated); + forItemsAtLevel<2>([&](DiagnosticItem *item){ + if (item->diagnostic().location.filePath == path) + item->setFixItStatus(FixitStatus::Invalidated); }); removeWatchedPath(path); } @@ -623,9 +622,10 @@ bool DiagnosticFilterModel::filterAcceptsRow(int sourceRow, // DiagnosticItem Utils::TreeItem *parentItem = model->itemForIndex(sourceParent); - if (auto filePathItem = dynamic_cast(parentItem)) { - auto diagnosticItem = dynamic_cast(filePathItem->childAt(sourceRow)); - QTC_ASSERT(diagnosticItem, return false); + QTC_ASSERT(parentItem, return true); + if (parentItem->level() == 1) { + auto filePathItem = static_cast(parentItem); + auto diagnosticItem = static_cast(filePathItem->childAt(sourceRow)); // Is the diagnostic explicitly suppressed? const Diagnostic &diag = diagnosticItem->diagnostic(); @@ -651,11 +651,12 @@ bool DiagnosticFilterModel::lessThan(const QModelIndex &l, const QModelIndex &r) { auto model = static_cast(sourceModel()); Utils::TreeItem *itemLeft = model->itemForIndex(l); - const bool isComparingDiagnostics = !dynamic_cast(itemLeft); + QTC_ASSERT(itemLeft, return QSortFilterProxyModel::lessThan(l, r)); + const bool isComparingDiagnostics = itemLeft->level() > 1; if (sortColumn() == Debugger::DetailedErrorView::DiagnosticColumn && isComparingDiagnostics) { bool result = false; - if (dynamic_cast(itemLeft)) { + if (itemLeft->level() == 2) { using Debugger::DiagnosticLocation; const int role = Debugger::DetailedErrorView::LocationRole; @@ -669,8 +670,11 @@ bool DiagnosticFilterModel::lessThan(const QModelIndex &l, const QModelIndex &r) result = std::tie(leftLoc.line, leftLoc.column, leftText) < std::tie(rightLoc.line, rightLoc.column, rightText); - } else if (auto left = dynamic_cast(itemLeft)) { - const auto right = dynamic_cast(model->itemForIndex(r)); + } else if (itemLeft->level() == 3) { + Utils::TreeItem *itemRight = model->itemForIndex(r); + QTC_ASSERT(itemRight, QSortFilterProxyModel::lessThan(l, r)); + const auto left = static_cast(itemLeft); + const auto right = static_cast(itemRight); result = left->index() < right->index(); } else { QTC_CHECK(false && "Unexpected item"); diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h index a8c4dcd011a..c0d64664bf2 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -100,7 +100,11 @@ private: ClangToolsDiagnosticModel *m_parentModel = nullptr; }; -class ClangToolsDiagnosticModel : public Utils::TreeModel<> +class ExplainingStepItem; + +using ClangToolsDiagnosticModelBase + = Utils::TreeModel; +class ClangToolsDiagnosticModel : public ClangToolsDiagnosticModelBase { Q_OBJECT diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index d058adac9ee..8a584915103 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -446,46 +446,39 @@ TextEditor::TabSettings CppEditorDocument::tabSettings() const return indenter()->tabSettings().value_or(TextEditor::TextDocument::tabSettings()); } -static int formatRange(QTextDocument *doc, - TextEditor::Indenter *indenter, - std::pair editedRange, - const TextEditor::TabSettings &tabSettings) -{ - QTextCursor cursor(doc); - cursor.setPosition(editedRange.first); - cursor.setPosition(editedRange.second, QTextCursor::KeepAnchor); - const int oldBlockCount = doc->blockCount(); - indenter->format(cursor, tabSettings); - return doc->blockCount() - oldBlockCount; -} - bool CppEditorDocument::save(QString *errorString, const QString &fileName, bool autoSave) { - if (indenter()->formatOnSave()) { - QTextCursor cursor(document()); - cursor.joinPreviousEditBlock(); + if (indenter()->formatOnSave() && !autoSave) { auto *layout = qobject_cast(document()->documentLayout()); const int documentRevision = layout->lastSaveRevision; - std::pair editedRange; + TextEditor::RangesInLines editedRanges; + TextEditor::RangeInLines lastRange{-1, -1}; for (int i = 0; i < document()->blockCount(); ++i) { const QTextBlock block = document()->findBlockByNumber(i); if (block.revision() == documentRevision) { - if (editedRange.first != -1) - i += formatRange(document(), indenter(), editedRange, tabSettings()); + if (lastRange.startLine != -1) + editedRanges.push_back(lastRange); - editedRange = std::make_pair(-1, -1); + lastRange.startLine = lastRange.endLine = -1; continue; } // block.revision() != documentRevision - if (editedRange.first == -1) - editedRange.first = block.position(); - editedRange.second = block.position() + block.length(); + if (lastRange.startLine == -1) + lastRange.startLine = block.blockNumber() + 1; + lastRange.endLine = block.blockNumber() + 1; + } + + if (lastRange.startLine != -1) + editedRanges.push_back(lastRange); + + if (!editedRanges.empty()) { + QTextCursor cursor(document()); + cursor.joinPreviousEditBlock(); + indenter()->format(editedRanges); + cursor.endEditBlock(); } - if (editedRange.first != -1) - formatRange(document(), indenter(), editedRange, tabSettings()); - cursor.endEditBlock(); } return TextEditor::TextDocument::save(errorString, fileName, autoSave); diff --git a/src/plugins/cpptools/headerpathfilter.cpp b/src/plugins/cpptools/headerpathfilter.cpp index 342ee773b78..b514c468d76 100644 --- a/src/plugins/cpptools/headerpathfilter.cpp +++ b/src/plugins/cpptools/headerpathfilter.cpp @@ -48,6 +48,11 @@ void HeaderPathFilter::process() tweakHeaderPaths(); } +bool HeaderPathFilter::isProjectHeaderPath(const QString &path) const +{ + return path.startsWith(projectDirectory) || path.startsWith(buildDirectory); +} + void HeaderPathFilter::filterHeaderPath(const ProjectExplorer::HeaderPath &headerPath) { if (headerPath.path.isEmpty()) @@ -62,7 +67,10 @@ void HeaderPathFilter::filterHeaderPath(const ProjectExplorer::HeaderPath &heade systemHeaderPaths.push_back(headerPath); break; case HeaderPathType::User: - userHeaderPaths.push_back(headerPath); + if (isProjectHeaderPath(headerPath.path)) + userHeaderPaths.push_back(headerPath); + else + systemHeaderPaths.push_back(headerPath); break; } } @@ -133,4 +141,13 @@ void HeaderPathFilter::tweakHeaderPaths() } } +QString HeaderPathFilter::ensurePathWithSlashEnding(const QString &path) +{ + QString pathWithSlashEnding = path; + if (!pathWithSlashEnding.isEmpty() && *pathWithSlashEnding.rbegin() != '/') + pathWithSlashEnding.push_back('/'); + + return pathWithSlashEnding; +} + } // namespace CppTools diff --git a/src/plugins/cpptools/headerpathfilter.h b/src/plugins/cpptools/headerpathfilter.h index ccb5c709c09..8b4e7719942 100644 --- a/src/plugins/cpptools/headerpathfilter.h +++ b/src/plugins/cpptools/headerpathfilter.h @@ -34,11 +34,15 @@ class CPPTOOLS_EXPORT HeaderPathFilter public: HeaderPathFilter(const ProjectPart &projectPart, UseTweakedHeaderPaths useTweakedHeaderPaths = UseTweakedHeaderPaths::Yes, - const QString &clangVersion = QString(), - const QString &clangResourceDirectory = QString()) + const QString &clangVersion = {}, + const QString &clangResourceDirectory = {}, + const QString &projectDirectory = {}, + const QString &buildDirectory = {}) : projectPart{projectPart} , clangVersion{clangVersion} , clangResourceDirectory{clangResourceDirectory} + , projectDirectory(ensurePathWithSlashEnding(projectDirectory)) + , buildDirectory(ensurePathWithSlashEnding(buildDirectory)) , useTweakedHeaderPaths{useTweakedHeaderPaths} {} @@ -49,6 +53,10 @@ private: void tweakHeaderPaths(); + bool isProjectHeaderPath(const QString &path) const; + + static QString ensurePathWithSlashEnding(const QString &path); + public: ProjectExplorer::HeaderPaths builtInHeaderPaths; ProjectExplorer::HeaderPaths systemHeaderPaths; @@ -56,6 +64,8 @@ public: const ProjectPart &projectPart; const QString clangVersion; const QString clangResourceDirectory; + const QString projectDirectory; + const QString buildDirectory; const UseTweakedHeaderPaths useTweakedHeaderPaths; }; diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 2042bf6fa3a..771eac9be1d 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1451,8 +1451,9 @@ void DebuggerPluginPrivate::updatePresetState() if (startupRunConfigName.isEmpty() && startupProject) startupRunConfigName = startupProject->displayName(); + // Restrict width, otherwise Creator gets too wide, see QTCREATORBUG-21885 const QString startToolTip = - canRun ? tr("Start debugging of \"%1\"").arg(startupRunConfigName) : whyNot; + canRun ? tr("Start debugging of startup project") : whyNot; m_startAction.setToolTip(startToolTip); m_startAction.setText(canRun ? startToolTip : tr("Start Debugging")); diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 7924945bb80..30b7fbef01e 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -52,7 +52,7 @@ namespace ProjectExplorer { Q_GLOBAL_STATIC(QThreadPool, s_extraCompilerThreadPool); Q_GLOBAL_STATIC(QList, factories); - +Q_GLOBAL_STATIC(QVector, observers); class ExtraCompilerPrivate { public: @@ -310,7 +310,8 @@ void ExtraCompiler::setContent(const Utils::FileName &file, const QByteArray &co } } -ExtraCompilerFactory::ExtraCompilerFactory(QObject *parent) : QObject(parent) +ExtraCompilerFactory::ExtraCompilerFactory(QObject *parent) + : QObject(parent) { factories->append(this); } @@ -320,6 +321,14 @@ ExtraCompilerFactory::~ExtraCompilerFactory() factories->removeAll(this); } +void ExtraCompilerFactory::annouceCreation(const Project *project, + const Utils::FileName &source, + const Utils::FileNameList &targets) +{ + for (ExtraCompilerFactoryObserver *observer : *observers) + observer->newExtraCompiler(project, source, targets); +} + QList ExtraCompilerFactory::extraCompilerFactories() { return *factories(); @@ -455,4 +464,14 @@ void ProcessExtraCompiler::cleanUp() setCompileTime(QDateTime::currentDateTime()); } +ExtraCompilerFactoryObserver::ExtraCompilerFactoryObserver() +{ + observers->push_back(this); +} + +ExtraCompilerFactoryObserver::~ExtraCompilerFactoryObserver() +{ + observers->removeOne(this); +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index c93830b10ce..006555ffc63 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -137,6 +137,20 @@ private: QFutureWatcher *m_watcher = nullptr; }; +class PROJECTEXPLORER_EXPORT ExtraCompilerFactoryObserver +{ + friend class ExtraCompilerFactory; + +protected: + ExtraCompilerFactoryObserver(); + ~ExtraCompilerFactoryObserver(); + + virtual void newExtraCompiler(const Project *project, + const Utils::FileName &source, + const Utils::FileNameList &targets) + = 0; +}; + class PROJECTEXPLORER_EXPORT ExtraCompilerFactory : public QObject { Q_OBJECT @@ -147,8 +161,14 @@ public: virtual FileType sourceType() const = 0; virtual QString sourceTag() const = 0; - virtual ExtraCompiler *create(const Project *project, const Utils::FileName &source, - const Utils::FileNameList &targets) = 0; + virtual ExtraCompiler *create(const Project *project, + const Utils::FileName &source, + const Utils::FileNameList &targets) + = 0; + + void annouceCreation(const Project *project, + const Utils::FileName &source, + const Utils::FileNameList &targets); static QList extraCompilerFactories(); }; diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index ed66ee746be..0e78a50abcc 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1143,14 +1143,6 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); msubProjectContextMenu->addAction(cmd, Constants::G_PROJECT_FILES); - // unload project again, in right position - dd->m_unloadActionContextMenu = new Utils::ParameterAction(tr("Close Project"), tr("Close Project \"%1\""), - Utils::ParameterAction::EnabledWithParameter, this); - cmd = ActionManager::registerAction(dd->m_unloadActionContextMenu, Constants::UNLOADCM); - cmd->setAttribute(Command::CA_UpdateText); - cmd->setDescription(dd->m_unloadActionContextMenu->text()); - mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST); - dd->m_closeProjectFilesActionContextMenu = new Utils::ParameterAction( tr("Close All Files"), tr("Close All Files in Project \"%1\""), Utils::ParameterAction::EnabledWithParameter, this); @@ -1160,6 +1152,14 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er cmd->setDescription(dd->m_closeProjectFilesActionContextMenu->text()); mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST); + // unload project again, in right position + dd->m_unloadActionContextMenu = new Utils::ParameterAction(tr("Close Project"), tr("Close Project \"%1\""), + Utils::ParameterAction::EnabledWithParameter, this); + cmd = ActionManager::registerAction(dd->m_unloadActionContextMenu, Constants::UNLOADCM); + cmd->setAttribute(Command::CA_UpdateText); + cmd->setDescription(dd->m_unloadActionContextMenu->text()); + mprojectContextMenu->addAction(cmd, Constants::G_PROJECT_LAST); + // file properties action dd->m_filePropertiesAction = new QAction(tr("Properties..."), this); cmd = ActionManager::registerAction(dd->m_filePropertiesAction, Constants::FILEPROPERTIES, @@ -1854,18 +1854,15 @@ void ProjectExplorerPluginPrivate::setStartupProject(Project *project) bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project) { QTC_ASSERT(project, return false); - const Utils::FileNameList filesInProject = project->files(Project::AllFiles); QList openFiles = DocumentModel::openedDocuments(); - Utils::erase(openFiles, [filesInProject](const IDocument *doc) { - return !filesInProject.contains(doc->filePath()); + Utils::erase(openFiles, [project](const IDocument *doc) { + return !project->isKnownFile(doc->filePath()); }); for (const Project * const otherProject : SessionManager::projects()) { if (otherProject == project) continue; - const Utils::FileNameList filesInOtherProject - = otherProject->files(Project::AllFiles); - Utils::erase(openFiles, [filesInOtherProject](const IDocument *doc) { - return filesInOtherProject.contains(doc->filePath()); + Utils::erase(openFiles, [otherProject](const IDocument *doc) { + return otherProject->isKnownFile(doc->filePath()); }); } return EditorManager::closeDocuments(openFiles); diff --git a/src/plugins/qtsupport/qscxmlcgenerator.cpp b/src/plugins/qtsupport/qscxmlcgenerator.cpp index 1396c0ac477..4d1fc8e50a3 100644 --- a/src/plugins/qtsupport/qscxmlcgenerator.cpp +++ b/src/plugins/qtsupport/qscxmlcgenerator.cpp @@ -149,6 +149,8 @@ ExtraCompiler *QScxmlcGeneratorFactory::create( const Project *project, const Utils::FileName &source, const Utils::FileNameList &targets) { + annouceCreation(project, source, targets); + return new QScxmlcGenerator(project, source, targets, this); } diff --git a/src/plugins/qtsupport/uicgenerator.cpp b/src/plugins/qtsupport/uicgenerator.cpp index fd201a525c5..2029d69a56e 100644 --- a/src/plugins/qtsupport/uicgenerator.cpp +++ b/src/plugins/qtsupport/uicgenerator.cpp @@ -100,6 +100,8 @@ ExtraCompiler *UicGeneratorFactory::create(const Project *project, const Utils::FileName &source, const Utils::FileNameList &targets) { + annouceCreation(project, source, targets); + return new UicGenerator(project, source, targets, this); } diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp index 2d5f308ee53..728aea666d4 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp @@ -203,7 +203,9 @@ void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success) } else { connect(d->connection, &SshConnection::connected, this, &AbstractRemoteLinuxDeployService::handleConnected); - emit progressMessage(tr("Connecting to device...")); + emit progressMessage(tr("Connecting to device '%1' (%2)") + .arg(deviceConfiguration()->displayName()) + .arg(deviceConfiguration()->sshParameters().host())); if (d->connection->state() == SshConnection::Unconnected) d->connection->connectToHost(); } diff --git a/src/plugins/texteditor/indenter.h b/src/plugins/texteditor/indenter.h index 8a663be9037..31dda468008 100644 --- a/src/plugins/texteditor/indenter.h +++ b/src/plugins/texteditor/indenter.h @@ -58,6 +58,15 @@ public: using Replacements = std::vector; +class RangeInLines +{ +public: + int startLine; + int endLine; +}; + +using RangesInLines = std::vector; + class Indenter { public: @@ -91,9 +100,7 @@ public: } // By default just calls indent with default settings. - virtual Replacements format(const QTextCursor &/*cursor*/, - const TabSettings &/*tabSettings*/, - int /*cursorPositionInEditor*/ = -1) + virtual Replacements format(const RangesInLines & /*rangesInLines*/ = RangesInLines()) { return Replacements(); } diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 47b960b3024..0eadd070e33 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -8085,12 +8085,21 @@ void BaseTextEditor::setContextHelp(const HelpItem &item) void TextEditorWidget::contextHelpItem(const IContext::HelpCallback &callback) { + const QString fallbackWordUnderCursor = Text::wordUnderCursor(textCursor()); if (d->m_contextHelpItem.isEmpty() && !d->m_hoverHandlers.isEmpty()) { d->m_hoverHandlers.first()->contextHelpId(this, Text::wordStartCursor(textCursor()).position(), - callback); + [fallbackWordUnderCursor, callback](const HelpItem &item) { + if (item.isEmpty()) + callback(fallbackWordUnderCursor); + else + callback(item); + }); } else { - callback(d->m_contextHelpItem); + if (d->m_contextHelpItem.isEmpty()) + callback(fallbackWordUnderCursor); + else + callback(d->m_contextHelpItem); } } diff --git a/src/plugins/texteditor/textmark.cpp b/src/plugins/texteditor/textmark.cpp index cccf526b918..a881480d95d 100644 --- a/src/plugins/texteditor/textmark.cpp +++ b/src/plugins/texteditor/textmark.cpp @@ -291,6 +291,9 @@ void TextMark::addToToolTipLayout(QGridLayout *target) const // Right column: action icons/button if (!m_actions.isEmpty()) { auto actionsLayout = new QHBoxLayout; + QMargins margins = actionsLayout->contentsMargins(); + margins.setLeft(margins.left() + 5); + actionsLayout->setContentsMargins(margins); for (QAction *action : m_actions) { QTC_ASSERT(!action->icon().isNull(), continue); auto button = new QToolButton; diff --git a/src/plugins/welcome/introductionwidget.cpp b/src/plugins/welcome/introductionwidget.cpp index d10cb76be2f..c4b194c2cec 100644 --- a/src/plugins/welcome/introductionwidget.cpp +++ b/src/plugins/welcome/introductionwidget.cpp @@ -119,7 +119,7 @@ IntroductionWidget::IntroductionWidget(QWidget *parent) "Edit:Work with code and navigate your project." "Design:Work with UI designs for Qt Widgets or Qt Quick." "Debug:Analyze your application with a debugger or other " - "analyzer." + "analyzers." "Projects:Manage project settings." "Help:Browse the help database." "

")}, diff --git a/src/shared/qbs b/src/shared/qbs index 6fcbc1a996d..bceae1097f6 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 6fcbc1a996d1726d7df5e078663a095b235d5b00 +Subproject commit bceae1097f6a57f1339ea3a4e6827d6195860d31 diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index 58d749ec3a7..f4afaf9210d 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -246,8 +246,13 @@ struct Data // because we have a cycle dependency modifiedTimeChecker, buildDependencyCollector, database}; - ClangBackEnd::PchTaskGenerator pchTaskGenerator{buildDependencyProvider, pchTaskMerger}; - PchManagerServer clangPchManagerServer{includeWatcher, pchTaskGenerator, projectParts, generatedFiles}; + ClangBackEnd::PchTaskGenerator pchTaskGenerator{buildDependencyProvider, + pchTaskMerger, + progressCounter}; + PchManagerServer clangPchManagerServer{includeWatcher, + pchTaskGenerator, + projectParts, + generatedFiles}; TaskScheduler systemTaskScheduler{pchCreatorManager, pchTaskQueue, progressCounter, diff --git a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h index 50061a9e6cb..2b1f4d036da 100644 --- a/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h +++ b/src/tools/clangpchmanagerbackend/source/generatepchactionfactory.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -44,6 +45,9 @@ public: bool BeginInvocation(clang::CompilerInstance &compilerInstance) override { compilerInstance.getPreprocessorOpts().DisablePCHValidation = true; + compilerInstance.getPreprocessorOpts().AllowPCHWithCompilerErrors = true; + compilerInstance.getLangOpts().DelayedTemplateParsing = true; + compilerInstance.getDiagnosticOpts().ErrorLimit = 0; std::unique_ptr Input = llvm::MemoryBuffer::getMemBuffer(m_fileContent); compilerInstance.getPreprocessorOpts().addRemappedFile(m_filePath, Input.release()); diff --git a/src/tools/clangpchmanagerbackend/source/pchcreator.cpp b/src/tools/clangpchmanagerbackend/source/pchcreator.cpp index f4a10053622..b8227ce9645 100644 --- a/src/tools/clangpchmanagerbackend/source/pchcreator.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchcreator.cpp @@ -76,8 +76,6 @@ bool PchCreator::generatePch(NativeFilePathView path, Utils::SmallStringView con { clang::tooling::ClangTool tool = m_clangTool.createOutputTool(); - NativeFilePath headerFilePath{m_environment.pchBuildDirectory().toStdString(), "dummy.h"}; - auto action = std::make_unique(llvm::StringRef{path.data(), path.size()}, llvm::StringRef{content.data(), @@ -138,6 +136,16 @@ const ProjectPartPch &PchCreator::projectPartPch() void PchCreator::setUnsavedFiles(const V2::FileContainers &fileContainers) { + m_generatedFilePathIds.clear(); + m_generatedFilePathIds.reserve(fileContainers.size()); + std::transform(fileContainers.begin(), + fileContainers.end(), + std::back_inserter(m_generatedFilePathIds), + [&](const V2::FileContainer &fileContainer) { + return m_filePathCache.filePathId(fileContainer.filePath); + }); + std::sort(m_generatedFilePathIds.begin(), m_generatedFilePathIds.end()); + m_clangTool.addUnsavedFiles(fileContainers); } @@ -159,7 +167,14 @@ void PchCreator::clear() void PchCreator::doInMainThreadAfterFinished() { - m_clangPathwatcher.updateIdPaths({{m_projectPartPch.projectPartId, m_allInclues}}); + FilePathIds existingIncludes; + existingIncludes.reserve(m_allInclues.size()); + std::set_difference(m_allInclues.begin(), + m_allInclues.end(), + m_generatedFilePathIds.begin(), + m_generatedFilePathIds.end(), + std::back_inserter(existingIncludes)); + m_clangPathwatcher.updateIdPaths({{m_projectPartPch.projectPartId, existingIncludes}}); m_pchManagerClient.precompiledHeadersUpdated(ProjectPartPchs{m_projectPartPch}); } diff --git a/src/tools/clangpchmanagerbackend/source/pchcreator.h b/src/tools/clangpchmanagerbackend/source/pchcreator.h index b2c0c309e77..7c736dd2342 100644 --- a/src/tools/clangpchmanagerbackend/source/pchcreator.h +++ b/src/tools/clangpchmanagerbackend/source/pchcreator.h @@ -98,6 +98,7 @@ private: ProjectPartPch m_projectPartPch; FilePathCaching m_filePathCache; FilePathIds m_allInclues; + FilePathIds m_generatedFilePathIds; Environment &m_environment; PchManagerClientInterface &m_pchManagerClient; ClangPathWatcherInterface &m_clangPathwatcher; diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.cpp b/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.cpp index 82584ad1dd8..aab3bfe7d48 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.cpp @@ -27,9 +27,10 @@ #include "builddependenciesproviderinterface.h" #include "pchtasksmergerinterface.h" - #include "usedmacrofilter.h" +#include + #include namespace ClangBackEnd { @@ -40,6 +41,8 @@ void PchTaskGenerator::addProjectParts(ProjectPartContainers &&projectParts, PchTaskSets pchTaskSets; pchTaskSets.reserve(projectParts.size()); + m_progressCounter.addTotal(static_cast(projectParts.size())); + for (auto &projectPart : projectParts) { BuildDependency buildDependency = m_buildDependenciesProvider.create(projectPart); UsedMacroFilter filter{buildDependency.includes, @@ -68,6 +71,7 @@ void PchTaskGenerator::addProjectParts(ProjectPartContainers &&projectParts, projectPart.language, projectPart.languageVersion, projectPart.languageExtension}); + m_progressCounter.addProgress(1); } m_pchTasksMergerInterface.mergeTasks(std::move(pchTaskSets), std::move(toolChainArguments)); diff --git a/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.h b/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.h index b5669b7b3f3..38376ce2a21 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.h +++ b/src/tools/clangpchmanagerbackend/source/pchtaskgenerator.h @@ -35,14 +35,18 @@ namespace ClangBackEnd { class PchTasksMergerInterface; class BuildDependenciesProviderInterface; +class ProgressCounter; class PchTaskGenerator : public PchTaskGeneratorInterface { public: PchTaskGenerator(BuildDependenciesProviderInterface &buildDependenciesProvider, - PchTasksMergerInterface &pchTasksMergerInterface) + PchTasksMergerInterface &pchTasksMergerInterface, + ProgressCounter &progressCounter) : m_buildDependenciesProvider(buildDependenciesProvider) , m_pchTasksMergerInterface(pchTasksMergerInterface) + , m_progressCounter(progressCounter) + {} void addProjectParts(ProjectPartContainers &&projectParts, @@ -52,7 +56,7 @@ public: private: BuildDependenciesProviderInterface &m_buildDependenciesProvider; PchTasksMergerInterface &m_pchTasksMergerInterface; + ProgressCounter &m_progressCounter; }; - } // namespace ClangBackEnd diff --git a/src/tools/clangpchmanagerbackend/source/pchtasksmerger.cpp b/src/tools/clangpchmanagerbackend/source/pchtasksmerger.cpp index 636d9a32364..50851a838a0 100644 --- a/src/tools/clangpchmanagerbackend/source/pchtasksmerger.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchtasksmerger.cpp @@ -71,7 +71,7 @@ bool PchTasksMerger::hasDuplicates(const CompilerMacros &compilerMacros) return first.key == second.key; }); - return found == compilerMacros.end(); + return found != compilerMacros.end(); } IncludeSearchPaths mergeIncludeSearchPaths(IncludeSearchPaths &&first, IncludeSearchPaths &&second) @@ -89,7 +89,8 @@ bool PchTasksMerger::mergePchTasks(PchTask &firstTask, PchTask &secondTask) CompilerMacros macros = mergeMacros(firstTask.compilerMacros, secondTask.compilerMacros); - secondTask.isMerged = hasDuplicates(macros); + secondTask.isMerged = !hasDuplicates(macros); + if (secondTask.isMerged && firstTask.language == secondTask.language) { firstTask.projectPartIds = merge(std::move(firstTask.projectPartIds), diff --git a/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h b/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h index 0b39ae1f4f2..fe08cf71ac9 100644 --- a/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h +++ b/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h @@ -93,8 +93,8 @@ public: return std::tie(first.key, first.value) < std::tie(second.key, second.value); }); - systemCompilerMacros = filtercompilerMacros(indexedCompilerMacro, systemUsedMacros); - projectCompilerMacros = filtercompilerMacros(indexedCompilerMacro, projectUsedMacros); + systemCompilerMacros = filterCompilerMacros(indexedCompilerMacro, systemUsedMacros); + projectCompilerMacros = filterCompilerMacros(indexedCompilerMacro, projectUsedMacros); } private: @@ -150,10 +150,13 @@ private: std::sort(filtertedMacros.begin(), filtertedMacros.end()); + auto newEnd = std::unique(filtertedMacros.begin(), filtertedMacros.end()); + filtertedMacros.erase(newEnd, filtertedMacros.end()); + return filtertedMacros; } - static CompilerMacros filtercompilerMacros(const CompilerMacros &indexedCompilerMacro, + static CompilerMacros filterCompilerMacros(const CompilerMacros &indexedCompilerMacro, const Utils::SmallStringVector &usedMacros) { CompilerMacros filtertedCompilerMacros; diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index 1ceb4a14ce6..188d6fd2077 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp @@ -27,6 +27,7 @@ #include #include +#include namespace ClangBackEnd { @@ -100,6 +101,7 @@ newFrontendActionFactory(Factory *consumerFactory, bool BeginInvocation(clang::CompilerInstance &compilerInstance) override { compilerInstance.getLangOpts().DelayedTemplateParsing = false; + compilerInstance.getPreprocessorOpts().AllowPCHWithCompilerErrors = true; return clang::ASTFrontendAction::BeginInvocation(compilerInstance); } diff --git a/tests/unit/mockup/projectexplorer/buildconfiguration.h b/tests/unit/mockup/projectexplorer/buildconfiguration.h new file mode 100644 index 00000000000..75b5a293c45 --- /dev/null +++ b/tests/unit/mockup/projectexplorer/buildconfiguration.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include + +namespace ProjectExplorer { + +class BuildConfiguration +{ +public: + Utils::FileName buildDirectory() const { return {}; } +}; // namespace Target +} // namespace ProjectExplorer diff --git a/tests/unit/mockup/projectexplorer/project.h b/tests/unit/mockup/projectexplorer/project.h index 9e8f2e1ac39..cca7e49127d 100644 --- a/tests/unit/mockup/projectexplorer/project.h +++ b/tests/unit/mockup/projectexplorer/project.h @@ -25,6 +25,8 @@ #pragma once +#include "target.h" + #include #include @@ -35,13 +37,10 @@ class Project : public QObject { public: Project() = default; - Utils::FileName projectDirectory() const { - return Utils::FileName(); - } + Utils::FileName projectDirectory() const { return {}; } - Utils::FileName rootProjectDirectory() const { - return Utils::FileName(); - } + Utils::FileName rootProjectDirectory() const { return {}; } + + Target *activeTarget() const { return {}; } }; - -} +} // namespace ProjectExplorer diff --git a/tests/unit/mockup/projectexplorer/target.h b/tests/unit/mockup/projectexplorer/target.h new file mode 100644 index 00000000000..6ee2052ae29 --- /dev/null +++ b/tests/unit/mockup/projectexplorer/target.h @@ -0,0 +1,39 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "buildconfiguration.h" + +#include + +namespace ProjectExplorer { + +class Target +{ +public: + BuildConfiguration *activeBuildConfiguration() const { return {}; } +}; +} // namespace ProjectExplorer diff --git a/tests/unit/unittest/clangformat-test.cpp b/tests/unit/unittest/clangformat-test.cpp index 8b64fb19149..ea702f71a6e 100644 --- a/tests/unit/unittest/clangformat-test.cpp +++ b/tests/unit/unittest/clangformat-test.cpp @@ -408,6 +408,60 @@ TEST_F(ClangFormat, IndentIfBodyAndFormatBeforeIt) "}")); } +TEST_F(ClangFormat, DoNotFormatAfterTheFirstColon) +{ + insertLines({"{", + " Qt:", + "}"}); + + extendedIndenter.indentBlock(doc.findBlockByNumber(1), ':', TextEditor::TabSettings(), 9); + + ASSERT_THAT(documentLines(), ElementsAre("{", + " Qt:", + "}")); +} + +TEST_F(ClangFormat, OnlyIndentIncompleteStatementOnElectricalCharacter) +{ + insertLines({"{bar();", + "foo()", + "}"}); + + extendedIndenter.indentBlock(doc.findBlockByNumber(1), '(', TextEditor::TabSettings(), 12); + + ASSERT_THAT(documentLines(), ElementsAre("{bar();", + " foo()", + "}")); +} + +TEST_F(ClangFormat, IndentAndFormatCompleteStatementOnSemicolon) +{ + insertLines({"{bar();", + "foo();", + "}"}); + + extendedIndenter.indentBlock(doc.findBlockByNumber(1), ';', TextEditor::TabSettings(), 14); + + ASSERT_THAT(documentLines(), ElementsAre("{", + " bar();", + " foo();", + "}")); +} + +TEST_F(ClangFormat, IndentAndFormatCompleteStatementOnClosingScope) +{ + insertLines({"{bar();", + "foo();", + "}"}); + + extendedIndenter.indentBlock(doc.findBlockByNumber(1), '}', TextEditor::TabSettings(), 16); + + ASSERT_THAT(documentLines(), ElementsAre("{", + " bar();", + " foo();", + "}")); +} + TEST_F(ClangFormat, FormatBasicFile) { insertLines({"int main()", @@ -415,7 +469,7 @@ TEST_F(ClangFormat, FormatBasicFile) "int a;", "}"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("int main()", "{", @@ -430,7 +484,7 @@ TEST_F(ClangFormat, FormatEmptyLine) "", "}"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("int main() {}")); } @@ -441,7 +495,7 @@ TEST_F(ClangFormat, FormatLambda) "", "});"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("int b = foo([]() {", "", @@ -454,7 +508,7 @@ TEST_F(ClangFormat, FormatInitializerListInArguments) "args,", "{1, 2});"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("foo(arg1, args, {1, 2});")); } @@ -466,7 +520,7 @@ TEST_F(ClangFormat, FormatFunctionArgumentLambdaWithScope) "", "});"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("foo([]() {", @@ -481,7 +535,7 @@ TEST_F(ClangFormat, FormatScopeAsFunctionArgument) "", "});"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("foo({", @@ -494,7 +548,7 @@ TEST_F(ClangFormat, FormatStructuredBinding) insertLines({"auto [a,", "b] = c;"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("auto [a, b] = c;")); } @@ -504,7 +558,7 @@ TEST_F(ClangFormat, FormatStringLiteralContinuation) insertLines({"foo(bar, \"foo\"", "\"bar\");"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("foo(bar,", " \"foo\"", @@ -517,7 +571,7 @@ TEST_F(ClangFormat, FormatTemplateparameters) "B,", "C>"}); - indenter.format(cursor, TextEditor::TabSettings()); + indenter.format(cursor); ASSERT_THAT(documentLines(), ElementsAre("using Alias = Template")); } diff --git a/tests/unit/unittest/commandlinebuilder-test.cpp b/tests/unit/unittest/commandlinebuilder-test.cpp index c6bdd33d3fb..8eb7fafa3c3 100644 --- a/tests/unit/unittest/commandlinebuilder-test.cpp +++ b/tests/unit/unittest/commandlinebuilder-test.cpp @@ -139,6 +139,7 @@ TYPED_TEST(CommandLineBuilder, CHeader) ASSERT_THAT(builder.commandLine, ElementsAre("clang", + "-DNOMINMAX", "-x", "c-header", "-std=c11", @@ -154,7 +155,7 @@ TYPED_TEST(CommandLineBuilder, CSource) Builder builder{this->emptyProjectInfo, {}, InputFileType::Source, "/source/file.c"}; ASSERT_THAT(builder.commandLine, - ElementsAre("clang", "-x", "c", "-std=c11", "-nostdinc", "/source/file.c")); + ElementsAre("clang", "-DNOMINMAX", "-x", "c", "-std=c11", "-nostdinc", "/source/file.c")); } TYPED_TEST(CommandLineBuilder, ObjectiveCHeader) @@ -167,6 +168,7 @@ TYPED_TEST(CommandLineBuilder, ObjectiveCHeader) ASSERT_THAT(builder.commandLine, ElementsAre("clang", + "-DNOMINMAX", "-x", "objective-c-header", "-std=c11", @@ -184,6 +186,7 @@ TYPED_TEST(CommandLineBuilder, ObjectiveCSource) ASSERT_THAT(builder.commandLine, ElementsAre("clang", + "-DNOMINMAX", "-x", "objective-c", "-std=c11", @@ -200,6 +203,7 @@ TYPED_TEST(CommandLineBuilder, CppHeader) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -215,9 +219,15 @@ TYPED_TEST(CommandLineBuilder, CppSource) Builder builder{this->emptyProjectInfo, {}, InputFileType::Source, "/source/file.cpp"}; - ASSERT_THAT( - builder.commandLine, - ElementsAre("clang++", "-x", "c++", "-std=c++98", "-nostdinc", "-nostdinc++", "/source/file.cpp")); + ASSERT_THAT(builder.commandLine, + ElementsAre("clang++", + "-DNOMINMAX", + "-x", + "c++", + "-std=c++98", + "-nostdinc", + "-nostdinc++", + "/source/file.cpp")); } TYPED_TEST(CommandLineBuilder, ObjectiveCppHeader) @@ -230,6 +240,7 @@ TYPED_TEST(CommandLineBuilder, ObjectiveCppHeader) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "objective-c++-header", "-std=c++98", @@ -248,6 +259,7 @@ TYPED_TEST(CommandLineBuilder, ObjectiveCppSource) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "objective-c++", "-std=c++98", @@ -480,6 +492,7 @@ TYPED_TEST(CommandLineBuilder, IncludesOrder) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++11", @@ -505,7 +518,13 @@ TYPED_TEST(CommandLineBuilder, EmptySourceFile) Builder builder{this->emptyProjectInfo, {}, {}}; ASSERT_THAT(builder.commandLine, - ElementsAre("clang++", "-x", "c++-header", "-std=c++98", "-nostdinc", "-nostdinc++")); + ElementsAre("clang++", + "-DNOMINMAX", + "-x", + "c++-header", + "-std=c++98", + "-nostdinc", + "-nostdinc++")); } TYPED_TEST(CommandLineBuilder, SourceFile) @@ -514,6 +533,7 @@ TYPED_TEST(CommandLineBuilder, SourceFile) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -529,6 +549,7 @@ TYPED_TEST(CommandLineBuilder, EmptyOutputFile) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -547,6 +568,7 @@ TYPED_TEST(CommandLineBuilder, OutputFile) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -568,6 +590,7 @@ TYPED_TEST(CommandLineBuilder, IncludePchPath) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -590,6 +613,7 @@ TYPED_TEST(CommandLineBuilder, CompilerMacros) ASSERT_THAT(builder.commandLine, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", diff --git a/tests/unit/unittest/data/clangformat/.clang-format b/tests/unit/unittest/data/clangformat/.clang-format new file mode 100644 index 00000000000..953e0b44cd6 --- /dev/null +++ b/tests/unit/unittest/data/clangformat/.clang-format @@ -0,0 +1,115 @@ +# .clang-format for Qt Creator +# +# This is for clang-format >= 5.0. +# +# The configuration below follows the Qt Creator Coding Rules [1] as closely as +# possible. For documentation of the options, see [2]. +# +# Use ../../tests/manual/clang-format-for-qtc/test.cpp for documenting problems +# or testing changes. +# +# [1] https://doc-snapshots.qt.io/qtcreator-extending/coding-style.html +# [2] https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: DontAlign +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterClass: true + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false +BreakBeforeBinaryOperators: All +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - forever # avoids { wrapped to next line + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeCategories: + - Regex: '^ mockPchManagerClient; NiceMock mockClangPathWatcher; ClangBackEnd::PchCreator creator{environment, database, mockPchManagerClient, mockClangPathWatcher}; @@ -107,7 +103,8 @@ protected: {id(TESTDATA_DIR "/builddependencycollector/project/header2.h"), id(TESTDATA_DIR "/builddependencycollector/external/external1.h"), id(TESTDATA_DIR "/builddependencycollector/external/external2.h")}, - {id(TESTDATA_DIR "/builddependencycollector/project/header2.h"), + {id(generatedFilePath), + id(TESTDATA_DIR "/builddependencycollector/project/header2.h"), id(TESTDATA_DIR "/builddependencycollector/external/external1.h"), id(TESTDATA_DIR "/builddependencycollector/external/external2.h")}, {}, @@ -137,6 +134,7 @@ TEST_F(PchCreator, CreateProjectPartClangCompilerArguments) ASSERT_THAT(arguments, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", @@ -160,6 +158,7 @@ TEST_F(PchCreator, CreateProjectPartClangCompilerArgumentsWithSystemPch) ASSERT_THAT(arguments, ElementsAre("clang++", + "-DNOMINMAX", "-x", "c++-header", "-std=c++98", diff --git a/tests/unit/unittest/pchtaskgenerator-test.cpp b/tests/unit/unittest/pchtaskgenerator-test.cpp index 08a0079ad84..d85432baf25 100644 --- a/tests/unit/unittest/pchtaskgenerator-test.cpp +++ b/tests/unit/unittest/pchtaskgenerator-test.cpp @@ -29,6 +29,7 @@ #include "mockpchtasksmerger.h" #include +#include namespace { @@ -50,7 +51,11 @@ class PchTaskGenerator : public testing::Test protected: NiceMock mockBuildDependenciesProvider; NiceMock mockPchTaskMerger; - ClangBackEnd::PchTaskGenerator generator{mockBuildDependenciesProvider, mockPchTaskMerger}; + NiceMock> mockProgressCounterCallback; + ClangBackEnd::ProgressCounter progressCounter{mockProgressCounterCallback.AsStdFunction()}; + ClangBackEnd::PchTaskGenerator generator{mockBuildDependenciesProvider, + mockPchTaskMerger, + progressCounter}; ClangBackEnd::ProjectPartContainer projectPart1{ "ProjectPart1", {"--yi"}, @@ -132,6 +137,18 @@ TEST_F(PchTaskGenerator, AddProjectParts) generator.addProjectParts({projectPart1}, {"ToolChainArgument"}); } +TEST_F(PchTaskGenerator, ProgressCounter) +{ + ON_CALL(mockBuildDependenciesProvider, create(_)).WillByDefault(Return(buildDependency)); + + EXPECT_CALL(mockProgressCounterCallback, Call(0, 3)); + EXPECT_CALL(mockProgressCounterCallback, Call(1, 3)); + EXPECT_CALL(mockProgressCounterCallback, Call(2, 3)); + EXPECT_CALL(mockProgressCounterCallback, Call(3, 3)); + + generator.addProjectParts({projectPart1, projectPart1, projectPart1}, {"ToolChainArgument"}); +} + TEST_F(PchTaskGenerator, RemoveProjectParts) { ON_CALL(mockBuildDependenciesProvider, create(_)).WillByDefault(Return(buildDependency)); diff --git a/tests/unit/unittest/pchtasksmerger-test.cpp b/tests/unit/unittest/pchtasksmerger-test.cpp index e878438f339..c279da505e7 100644 --- a/tests/unit/unittest/pchtasksmerger-test.cpp +++ b/tests/unit/unittest/pchtasksmerger-test.cpp @@ -173,7 +173,7 @@ TEST_F(PchTasksMerger, MacrosCanBeMerged) CompilerMacros compilerMacros1{{"ER", "2", 2}, {"QI"}, {"SE", "4", 1}, {"SAN", "3", 3}}; CompilerMacros compilerMacros2{{"BA"}, {"ER", "2", 2}, {"YI", "1", 1}, {"SAN", "3", 3}}; - auto canBeMerged = Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); + auto canBeMerged = !Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); ASSERT_TRUE(canBeMerged); } @@ -183,7 +183,7 @@ TEST_F(PchTasksMerger, MacrosCannotBeMergedBecauseDifferentValue) CompilerMacros compilerMacros1{{"ER", "2", 2}, {"SE", "4", 1}, {"SAN", "3", 3}}; CompilerMacros compilerMacros2{{"ER", "1", 2}, {"YI", "1", 1}, {"SAN", "3", 3}}; - auto canBeMerged = Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); + auto canBeMerged = !Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); ASSERT_FALSE(canBeMerged); } @@ -193,7 +193,7 @@ TEST_F(PchTasksMerger, MacrosCannotBeMergedBecauseUndefinedMacro) CompilerMacros compilerMacros1{{"ER", "2", 2}, {"SE", "4", 1}, {"YI"}, {"SAN", "3", 3}}; CompilerMacros compilerMacros2{{"ER", "2", 2}, {"YI", "1", 1}, {"SAN", "3", 3}}; - auto canBeMerged = Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); + auto canBeMerged = !Merger::hasDuplicates(Merger::mergeMacros(compilerMacros1, compilerMacros2)); ASSERT_FALSE(canBeMerged); } diff --git a/tests/unit/unittest/projectupdater-test.cpp b/tests/unit/unittest/projectupdater-test.cpp index 293a4ef15ef..0ae47387d61 100644 --- a/tests/unit/unittest/projectupdater-test.cpp +++ b/tests/unit/unittest/projectupdater-test.cpp @@ -86,6 +86,7 @@ protected: projectPart.files.push_back(header2ProjectFile); projectPart.files.push_back(source1ProjectFile); projectPart.files.push_back(source2ProjectFile); + projectPart.files.push_back(nonActiveProjectFile); projectPart.displayName = "projectb"; projectPart.projectMacros = {{"FOO", "2"}, {"BAR", "1"}}; projectPartId = projectPart.id(); @@ -94,15 +95,19 @@ protected: projectPart2.files.push_back(header1ProjectFile); projectPart2.files.push_back(source2ProjectFile); projectPart2.files.push_back(source1ProjectFile); + projectPart2.files.push_back(nonActiveProjectFile); projectPart2.displayName = "projectaa"; projectPart2.projectMacros = {{"BAR", "1"}, {"FOO", "2"}}; projectPartId2 = projectPart2.id(); + nonBuildingProjectPart.files.push_back(cannotBuildSourceProjectFile); + nonBuildingProjectPart.displayName = "nonbuilding"; + nonBuildingProjectPart.selectedForBuilding = false; - Utils::SmallStringVector arguments{ClangPchManager::ProjectUpdater::toolChainArguments( - &projectPart)}; - Utils::SmallStringVector arguments2{ClangPchManager::ProjectUpdater::toolChainArguments( - &projectPart2)}; + Utils::SmallStringVector arguments{ + ClangPchManager::ProjectUpdater::toolChainArguments(&projectPart)}; + Utils::SmallStringVector arguments2{ + ClangPchManager::ProjectUpdater::toolChainArguments(&projectPart2)}; expectedContainer = {projectPartId.clone(), arguments.clone(), @@ -143,9 +148,14 @@ protected: CppTools::ProjectFile header1ProjectFile{QString(headerPaths[0]), CppTools::ProjectFile::CXXHeader}; CppTools::ProjectFile header2ProjectFile{QString(headerPaths[1]), CppTools::ProjectFile::CXXHeader}; CppTools::ProjectFile source1ProjectFile{QString(sourcePaths[0]), CppTools::ProjectFile::CXXSource}; - CppTools::ProjectFile source2ProjectFile{QString(sourcePaths[1]), CppTools::ProjectFile::CXXSource}; + CppTools::ProjectFile source2ProjectFile{QString(sourcePaths[1]), + CppTools::ProjectFile::CXXSource}; + CppTools::ProjectFile cannotBuildSourceProjectFile{QString("/cannot/build"), + CppTools::ProjectFile::CXXSource}; + CppTools::ProjectFile nonActiveProjectFile{QString("/foo"), CppTools::ProjectFile::CXXSource, false}; CppTools::ProjectPart projectPart; CppTools::ProjectPart projectPart2; + CppTools::ProjectPart nonBuildingProjectPart; ProjectPartContainer expectedContainer; ProjectPartContainer expectedContainer2; FileContainer generatedFile{{"/path/to", "header1.h"}, "content", {}}; @@ -240,7 +250,8 @@ TEST_F(ProjectUpdater, ConvertProjectPartToProjectPartContainer) TEST_F(ProjectUpdater, ConvertProjectPartToProjectPartContainersHaveSameSizeLikeProjectParts) { - auto containers = updater.toProjectPartContainers({&projectPart, &projectPart}); + auto containers = updater.toProjectPartContainers( + {&projectPart, &projectPart, &nonBuildingProjectPart}); ASSERT_THAT(containers, SizeIs(2)); } diff --git a/tests/unit/unittest/symbolindexer-test.cpp b/tests/unit/unittest/symbolindexer-test.cpp index 0e4e2072cdb..9283eea8b6c 100644 --- a/tests/unit/unittest/symbolindexer-test.cpp +++ b/tests/unit/unittest/symbolindexer-test.cpp @@ -267,6 +267,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsAddFilesInCollector) setFile(main1PathId, ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -297,6 +298,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsAddFilesWithPrecompiledHeaderInColl setFile(main1PathId, ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -330,6 +332,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsAddFilesWithoutPrecompiledHeaderInC setFile(main1PathId, ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -510,6 +513,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsInOrderWithoutProjectPartArtifact) setFile(main1PathId, ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -562,6 +566,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsInOrderWithProjectPartArtifact) setFile(Eq(main1PathId), ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -616,6 +621,7 @@ TEST_F(SymbolIndexer, UpdateProjectPartsCallsInOrderButGetsAnErrorForCollectingS setFile(main1PathId, ElementsAre("clang++", "-Wno-pragma-once-outside-header", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -673,6 +679,7 @@ TEST_F(SymbolIndexer, UpdateChangedPathCallsInOrder) setFile(Eq(sourceFileIds[0]), ElementsAre("clang++", "-DFOO", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -736,6 +743,7 @@ TEST_F(SymbolIndexer, UpdateChangedPathCallsInOrderButGetsAnErrorForCollectingSy setFile(Eq(sourceFileIds[0]), ElementsAre("clang++", "-DFOO", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -779,6 +787,7 @@ TEST_F(SymbolIndexer, UpdateChangedPathIsUsingPrecompiledHeader) setFile(Eq(sourceFileIds[0]), ElementsAre("clang++", "-DFOO", + "-DNOMINMAX", "-x", "c++", "-std=c++14", @@ -814,6 +823,7 @@ TEST_F(SymbolIndexer, UpdateChangedPathIsNotUsingPrecompiledHeaderIfItNotExists) setFile(Eq(sourceFileIds[0]), ElementsAre("clang++", "-DFOO", + "-DNOMINMAX", "-x", "c++", "-std=c++14", diff --git a/tests/unit/unittest/usedmacrofilter-test.cpp b/tests/unit/unittest/usedmacrofilter-test.cpp index b02cd7e5e02..bdde5417afb 100644 --- a/tests/unit/unittest/usedmacrofilter-test.cpp +++ b/tests/unit/unittest/usedmacrofilter-test.cpp @@ -46,7 +46,15 @@ protected: {3, SourceType::ProjectInclude, 0}, {4, SourceType::TopSystemInclude, 0}, {5, SourceType::TopProjectInclude, 0}}; - UsedMacros usedMacros{{"YI", 1}, {"ER", 2}, {"LIU", 2}, {"QI", 3}, {"SAN", 3}, {"SE", 4}, {"WU", 5}}; + UsedMacros usedMacros{{"YI", 1}, + {"ER", 2}, + {"SE", 2}, + {"LIU", 2}, + {"QI", 3}, + {"WU", 3}, + {"SAN", 3}, + {"SE", 4}, + {"WU", 5}}; CompilerMacros compileMacros{{"YI", "1", 1}, {"ER", "2", 2}, {"SAN", "3", 3},