diff options
Diffstat (limited to 'src/plugins')
77 files changed, 848 insertions, 335 deletions
diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 97b8e5de5b2..8ba60128755 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -101,7 +101,7 @@ public: setSummaryText(displayName()); auto uninstallPreviousPackage = new QCheckBox(this); - uninstallPreviousPackage->setText(tr("Uninstall previous package")); + uninstallPreviousPackage->setText(AndroidDeployQtStep::tr("Uninstall previous package")); uninstallPreviousPackage->setChecked(step->uninstallPreviousPackage() > AndroidDeployQtStep::Keep); uninstallPreviousPackage->setEnabled(step->uninstallPreviousPackage() != AndroidDeployQtStep::ForceUnintall); diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 11de94c3951..a4a1591cf47 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -68,13 +68,12 @@ void ClangCodeModelPlugin::generateCompilationDB() { using namespace CppTools; ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); - if (!project) + if (!project || !project->activeTarget()) return; - m_generatorWatcher.setFuture(QtConcurrent::run( - &Utils::generateCompilationDB, - project->projectDirectory(), - CppModelManager::instance()->projectInfo(project))); + m_generatorWatcher.setFuture( + QtConcurrent::run(&Utils::generateCompilationDB, + CppModelManager::instance()->projectInfo(project))); } static bool isDBGenerationEnabled(ProjectExplorer::Project *project) diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp index db34c904d46..6f1397c4874 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.cpp @@ -214,6 +214,9 @@ void ClangCompletionAssistProcessor::handleAvailableCompletions(const CodeComple setAsyncProposalAvailable(createFunctionHintProposal(completions)); return; } + + if (!m_fallbackToNormalCompletion) + return; // else: Proceed with a normal completion in case: // 1) it was not a function call, but e.g. a function declaration like "void f(" // 2) '{' meant not a constructor call. @@ -286,6 +289,14 @@ static QByteArray modifyInput(QTextDocument *doc, int endOfExpression) { return modifiedInput; } +static QChar lastPrecedingNonWhitespaceChar(const ClangCompletionAssistInterface *interface) +{ + int pos = interface->position(); + while (pos >= 0 && interface->characterAt(pos).isSpace()) + --pos; + return pos >= 0 ? interface->characterAt(pos) : QChar::Null; +} + IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper() { ClangCompletionContextAnalyzer analyzer(m_interface.data(), m_interface->languageFeatures()); @@ -323,6 +334,8 @@ IAssistProposal *ClangCompletionAssistProcessor::startCompletionHelper() } case ClangCompletionContextAnalyzer::PassThroughToLibClangAfterLeftParen: { m_sentRequestType = FunctionHintCompletion; + if (lastPrecedingNonWhitespaceChar(m_interface.data()) == ',') + m_fallbackToNormalCompletion = false; m_requestSent = sendCompletionRequest(analyzer.positionForClang(), QByteArray(), analyzer.functionNameStart()); break; diff --git a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h index 03711f0fa8f..5e8c772235e 100644 --- a/src/plugins/clangcodemodel/clangcompletionassistprocessor.h +++ b/src/plugins/clangcodemodel/clangcompletionassistprocessor.h @@ -97,6 +97,7 @@ private: CompletionRequestType m_sentRequestType = NormalCompletion; bool m_requestSent = false; bool m_addSnippets = false; // For type == Type::NormalCompletion + bool m_fallbackToNormalCompletion = true; }; } // namespace Internal diff --git a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp index ad48a60685b..a2c32a537ad 100644 --- a/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp +++ b/src/plugins/clangcodemodel/clangcompletioncontextanalyzer.cpp @@ -110,6 +110,8 @@ int ClangCompletionContextAnalyzer::startOfFunctionCall(int endOfOperator) const functionNameSelector.setPosition(functionNameStart); functionNameSelector.setPosition(index, QTextCursor::KeepAnchor); const QString functionName = functionNameSelector.selectedText().trimmed(); + if (functionName.isEmpty() && m_completionOperator == T_LBRACE) + return endOfOperator; return functionName.isEmpty() ? -1 : functionNameStart; } @@ -139,7 +141,10 @@ void ClangCompletionContextAnalyzer::handleCommaInFunctionCall() const int start = expressionUnderCursor.startOfFunctionCall(textCursor); m_positionEndOfExpression = start; m_positionForProposal = start + 1; // After '(' of function call - m_completionOperator = T_LPAREN; + if (m_interface->characterAt(start) == '(') + m_completionOperator = T_LPAREN; + else + m_completionOperator = T_LBRACE; } } diff --git a/src/plugins/clangcodemodel/clangutils.cpp b/src/plugins/clangcodemodel/clangutils.cpp index 481b0774f8f..fa42dedf2df 100644 --- a/src/plugins/clangcodemodel/clangutils.cpp +++ b/src/plugins/clangcodemodel/clangutils.cpp @@ -34,12 +34,13 @@ #include <coreplugin/idocument.h> #include <cpptools/baseeditordocumentparser.h> #include <cpptools/compileroptionsbuilder.h> +#include <cpptools/cppcodemodelsettings.h> #include <cpptools/cppmodelmanager.h> +#include <cpptools/cpptoolsreuse.h> #include <cpptools/editordocumenthandle.h> #include <cpptools/projectpart.h> -#include <cpptools/cppcodemodelsettings.h> -#include <cpptools/cpptoolsreuse.h> #include <projectexplorer/buildconfiguration.h> +#include <projectexplorer/kitinformation.h> #include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/target.h> @@ -300,12 +301,24 @@ QString diagnosticCategoryPrefixRemoved(const QString &text) return text; } -static ::Utils::FileName buildDirectory(const CppTools::ProjectPart &projectPart) +static ::Utils::FileName compilerPath(const CppTools::ProjectPart &projectPart) { ProjectExplorer::Target *target = projectPart.project->activeTarget(); if (!target) return ::Utils::FileName(); + ProjectExplorer::ToolChain *toolchain = ProjectExplorer::ToolChainKitAspect::toolChain( + target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID); + + return toolchain->compilerCommand(); +} + +static ::Utils::FileName buildDirectory(const ProjectExplorer::Project &project) +{ + ProjectExplorer::Target *target = project.activeTarget(); + if (!target) + return ::Utils::FileName(); + ProjectExplorer::BuildConfiguration *buildConfig = target->activeBuildConfiguration(); if (!buildConfig) return ::Utils::FileName(); @@ -313,42 +326,84 @@ static ::Utils::FileName buildDirectory(const CppTools::ProjectPart &projectPart return buildConfig->buildDirectory(); } -static QJsonObject createFileObject(CompilerOptionsBuilder &optionsBuilder, - const ProjectFile &projFile, - const ::Utils::FileName &buildDir) +static QStringList projectPartArguments(const ProjectPart &projectPart) { - const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); - optionsBuilder.updateFileLanguage(kind); + QStringList args; + args << compilerPath(projectPart).toString(); + args << "-c"; + if (projectPart.toolchainType != ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID) { + args << "--target=" + projectPart.toolChainTargetTriple; + args << (projectPart.toolChainWordWidth == ProjectPart::WordWidth64Bit + ? QLatin1String("-m64") + : QLatin1String("-m32")); + } + args << projectPart.compilerFlags; + for (const ProjectExplorer::HeaderPath &headerPath : projectPart.headerPaths) { + if (headerPath.type == ProjectExplorer::HeaderPathType::User) { + args << "-I" + headerPath.path; + } else if (headerPath.type == ProjectExplorer::HeaderPathType::System) { + args << (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID + ? "-I" + : "-isystem") + + headerPath.path; + } + } + for (const ProjectExplorer::Macro ¯o : projectPart.projectMacros) { + args.append(QString::fromUtf8( + macro.toKeyValue(macro.type == ProjectExplorer::MacroType::Define ? "-D" : "-U"))); + } + return args; +} + +static QJsonObject createFileObject(const ::Utils::FileName &buildDir, + const QStringList &arguments, + const ProjectPart &projectPart, + const ProjectFile &projFile) +{ QJsonObject fileObject; fileObject["file"] = projFile.path; - QJsonArray args = QJsonArray::fromStringList(optionsBuilder.options()); - args.prepend(kind == ProjectFile::CXXSource ? "clang++" : "clang"); + QJsonArray args = QJsonArray::fromStringList(arguments); + + const ProjectFile::Kind kind = ProjectFile::classify(projFile.path); + if (projectPart.toolchainType == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID + || projectPart.toolchainType == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID) { + if (ProjectFile::isC(kind)) + args.append("/TC"); + else if (ProjectFile::isCxx(kind)) + args.append("/TP"); + } else { + QStringList langOption + = createLanguageOptionGcc(kind, + projectPart.languageExtensions + & ::Utils::LanguageExtension::ObjectiveC); + for (const QString &langOptionPart : langOption) + args.append(langOptionPart); + } args.append(QDir::toNativeSeparators(projFile.path)); fileObject["arguments"] = args; fileObject["directory"] = buildDir.toString(); return fileObject; } -void generateCompilationDB(::Utils::FileName projectDir, CppTools::ProjectInfo projectInfo) +void generateCompilationDB(CppTools::ProjectInfo projectInfo) { - QFile compileCommandsFile(projectDir.toString() + "/compile_commands.json"); + const ::Utils::FileName buildDir = buildDirectory(*projectInfo.project()); + QTC_ASSERT(!buildDir.isEmpty(), return;); + QDir dir(buildDir.toString()); + if (!dir.exists()) + dir.mkpath(dir.path()); + QFile compileCommandsFile(buildDir.toString() + "/compile_commands.json"); const bool fileOpened = compileCommandsFile.open(QIODevice::WriteOnly | QIODevice::Truncate); if (!fileOpened) return; compileCommandsFile.write("["); - for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) { - const ::Utils::FileName buildDir = buildDirectory(*projectPart); - - CompilerOptionsBuilder optionsBuilder(*projectPart, - UseSystemHeader::No, - UseTweakedHeaderPaths::No); - optionsBuilder.build(CppTools::ProjectFile::Unclassified, - CppTools::UsePrecompiledHeaders::No); + for (ProjectPart::Ptr projectPart : projectInfo.projectParts()) { + const QStringList args = projectPartArguments(*projectPart); for (const ProjectFile &projFile : projectPart->files) { - const QJsonObject json = createFileObject(optionsBuilder, projFile, buildDir); + const QJsonObject json = createFileObject(buildDir, args, *projectPart, projFile); if (compileCommandsFile.size() > 1) compileCommandsFile.write(","); compileCommandsFile.write('\n' + QJsonDocument(json).toJson().trimmed()); diff --git a/src/plugins/clangcodemodel/clangutils.h b/src/plugins/clangcodemodel/clangutils.h index 4db869896b0..8ced14f5ad6 100644 --- a/src/plugins/clangcodemodel/clangutils.h +++ b/src/plugins/clangcodemodel/clangutils.h @@ -70,7 +70,7 @@ QString diagnosticCategoryPrefixRemoved(const QString &text); ::Utils::CodeModelIcon::Type iconTypeForToken(const ClangBackEnd::TokenInfoContainer &token); -void generateCompilationDB(::Utils::FileName projectDir, CppTools::ProjectInfo projectInfo); +void generateCompilationDB(CppTools::ProjectInfo projectInfo); class DiagnosticTextInfo { diff --git a/src/plugins/clangformat/ClangFormat.json.in b/src/plugins/clangformat/ClangFormat.json.in index 1c818c2f9e4..f270fea580e 100644 --- a/src/plugins/clangformat/ClangFormat.json.in +++ b/src/plugins/clangformat/ClangFormat.json.in @@ -2,7 +2,7 @@ \"Name\" : \"ClangFormat\", \"Version\" : \"$$QTCREATOR_VERSION\", \"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\", - \"Experimental\" : true, + \"DisabledByDefault\" : true, \"Vendor\" : \"The Qt Company Ltd\", \"Copyright\" : \"(C) $$QTCREATOR_COPYRIGHT_YEAR The Qt Company Ltd\", \"License\" : [ \"Commercial Usage\", diff --git a/src/plugins/clangformat/clangformatbaseindenter.cpp b/src/plugins/clangformat/clangformatbaseindenter.cpp index a53eb50adb2..dcb5edaeb13 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.cpp +++ b/src/plugins/clangformat/clangformatbaseindenter.cpp @@ -29,9 +29,10 @@ #include <utils/algorithm.h> #include <utils/fileutils.h> -#include <utils/textutils.h> #include <utils/qtcassert.h> +#include <utils/textutils.h> +#include <QDebug> #include <QTextDocument> namespace ClangFormat { @@ -126,9 +127,85 @@ void trimRHSWhitespace(const QTextBlock &block) cursor.endEditBlock(); } +QTextBlock reverseFindLastEmptyBlock(QTextBlock start) +{ + if (start.position() > 0) { + start = start.previous(); + while (start.position() > 0 && start.text().trimmed().isEmpty()) + start = start.previous(); + if (!start.text().trimmed().isEmpty()) + start = start.next(); + } + return start; +} + +enum class CharacterContext { AfterComma, LastAfterComma, NewStatement, Continuation, Unknown }; + +QChar findFirstNonWhitespaceCharacter(const QTextBlock ¤tBlock) +{ + const QTextDocument *doc = currentBlock.document(); + int currentPos = currentBlock.position(); + while (currentPos < doc->characterCount() && doc->characterAt(currentPos).isSpace()) + ++currentPos; + return currentPos < doc->characterCount() ? doc->characterAt(currentPos) : QChar::Null; +} + +CharacterContext characterContext(const QTextBlock ¤tBlock, + const QTextBlock &previousNonEmptyBlock) +{ + const QString prevLineText = previousNonEmptyBlock.text().trimmed(); + const QChar firstNonWhitespaceChar = findFirstNonWhitespaceCharacter(currentBlock); + if (prevLineText.endsWith(',')) { + // We don't need to add comma in case it's the last argument. + if (firstNonWhitespaceChar == '}' || firstNonWhitespaceChar == ')') + return CharacterContext::LastAfterComma; + return CharacterContext::AfterComma; + } + + if (prevLineText.endsWith(';') || prevLineText.endsWith('{') || prevLineText.endsWith('}') + || firstNonWhitespaceChar == QChar::Null) { + return CharacterContext::NewStatement; + } + + return CharacterContext::Continuation; +} + +bool nextBlockExistsAndEmpty(const QTextBlock ¤tBlock) +{ + QTextBlock nextBlock = currentBlock.next(); + if (!nextBlock.isValid() || nextBlock.position() == currentBlock.position()) + return false; + + return nextBlock.text().trimmed().isEmpty(); +} + +QByteArray dummyTextForContext(CharacterContext context, bool closingBraceBlock) +{ + if (closingBraceBlock + && (context == CharacterContext::NewStatement + || context == CharacterContext::Continuation)) { + return QByteArray(); + } + + switch (context) { + case CharacterContext::Unknown: + QTC_ASSERT(false, return "";); + case CharacterContext::AfterComma: + return "a,"; + case CharacterContext::NewStatement: + return "a;"; + case CharacterContext::Continuation: + case CharacterContext::LastAfterComma: + return "& a &"; + } +} + // Add extra text in case of the empty line or the line starting with ')'. // Track such extra pieces of text in isInsideModifiedLine(). -int forceIndentWithExtraText(QByteArray &buffer, const QTextBlock &block, bool secondTry) +int forceIndentWithExtraText(QByteArray &buffer, + CharacterContext &charContext, + const QTextBlock &block, + bool secondTry) { const QString blockText = block.text(); int firstNonWhitespace = Utils::indexOf(blockText, @@ -143,26 +220,33 @@ int forceIndentWithExtraText(QByteArray &buffer, const QTextBlock &block, bool s const bool closingParenBlock = firstNonWhitespace >= 0 && blockText.at(firstNonWhitespace) == ')'; + const bool closingBraceBlock = firstNonWhitespace >= 0 + && blockText.at(firstNonWhitespace) == '}'; int extraLength = 0; - if (firstNonWhitespace < 0 || closingParenBlock) { - //This extra text works for the most cases. - QByteArray dummyText("a;a;"); - - // Search for previous character - QTextBlock prevBlock = block.previous(); - bool prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); - while (prevBlockIsEmpty) { - prevBlock = prevBlock.previous(); - prevBlockIsEmpty = prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty(); + QByteArray dummyText; + if (firstNonWhitespace < 0 && charContext != CharacterContext::Unknown + && nextBlockExistsAndEmpty(block)) { + // If the next line is also empty it's safer to use a comment line. + dummyText = "//"; + } else if (firstNonWhitespace < 0 || closingParenBlock || closingBraceBlock) { + if (charContext == CharacterContext::LastAfterComma) { + charContext = CharacterContext::AfterComma; + } else if (charContext == CharacterContext::Unknown || firstNonWhitespace >= 0) { + QTextBlock lastBlock = reverseFindLastEmptyBlock(block); + if (lastBlock.position() > 0) + lastBlock = lastBlock.previous(); + + // If we don't know yet the dummy text, let's guess it and use for this line and before. + charContext = characterContext(block, lastBlock); } - if (closingParenBlock || prevBlock.text().endsWith(',')) - dummyText = "&& a"; - buffer.insert(utf8Offset, dummyText); - extraLength += dummyText.length(); + dummyText = dummyTextForContext(charContext, closingBraceBlock); } + buffer.insert(utf8Offset, dummyText); + extraLength += dummyText.length(); + if (secondTry) { int nextLinePos = buffer.indexOf('\n', utf8Offset); if (nextLinePos < 0) @@ -170,7 +254,7 @@ int forceIndentWithExtraText(QByteArray &buffer, const QTextBlock &block, bool s if (nextLinePos > 0) { // If first try was not successful try to put ')' in the end of the line to close possibly - // unclosed parentheses. + // unclosed parenthesis. // TODO: Does it help to add different endings depending on the context? buffer.insert(nextLinePos, ')'); extraLength += 1; @@ -300,18 +384,6 @@ bool doNotIndentInContext(QTextDocument *doc, int pos) return false; } -QTextBlock reverseFindLastEmptyBlock(QTextBlock start) -{ - if (start.position() > 0) { - start = start.previous(); - while (start.position() > 0 && start.text().trimmed().isEmpty()) - start = start.previous(); - if (!start.text().trimmed().isEmpty()) - start = start.next(); - } - return start; -} - int formattingRangeStart(const QTextBlock ¤tBlock, const QByteArray &buffer, int documentRevision) @@ -355,23 +427,14 @@ TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision()); adjustFormatStyleForLineBreak(style, replacementsToKeep); - if (typedChar == QChar::Null) { - if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) { - if (utf8Offset > 0) { - buffer.insert(utf8Offset - 1, " //"); - utf8Offset += 3; - } + if (replacementsToKeep == ReplacementsToKeep::OnlyIndent) { + CharacterContext currentCharContext = CharacterContext::Unknown; + // Iterate backwards to reuse the same dummy text for all empty lines. + for (int index = endBlock.blockNumber(); index >= startBlock.blockNumber(); --index) { utf8Length += forceIndentWithExtraText(buffer, - cursorPositionInEditor < 0 - ? endBlock - : m_doc->findBlock(cursorPositionInEditor), + currentCharContext, + m_doc->findBlockByNumber(index), secondTry); - } else { - for (int index = startBlock.blockNumber(); index <= endBlock.blockNumber(); ++index) { - utf8Length += forceIndentWithExtraText(buffer, - m_doc->findBlockByNumber(index), - secondTry); - } } } @@ -432,9 +495,24 @@ TextEditor::Replacements ClangFormatBaseIndenter::format( static_cast<unsigned int>(utf8RangeLength)); } + clang::format::FormatStyle style = styleForFile(); + const std::string assumedFileName = m_fileName.toString().toStdString(); + clang::tooling::Replacements clangReplacements = clang::format::sortIncludes(style, + buffer.data(), + ranges, + assumedFileName); + auto changedCode = clang::tooling::applyAllReplacements(buffer.data(), clangReplacements); + QTC_ASSERT(changedCode, { + qDebug() << QString::fromStdString(llvm::toString(changedCode.takeError())); + return TextEditor::Replacements(); + }); + ranges = clang::tooling::calculateRangesAfterReplacements(clangReplacements, ranges); + clang::format::FormattingAttemptStatus status; - const clang::tooling::Replacements clangReplacements - = reformat(styleForFile(), buffer.data(), ranges, m_fileName.toString().toStdString(), &status); + const clang::tooling::Replacements formatReplacements + = reformat(style, *changedCode, ranges, m_fileName.toString().toStdString(), &status); + clangReplacements = clangReplacements.merge(formatReplacements); + const TextEditor::Replacements toReplace = utf16Replacements(m_doc, buffer, clangReplacements); applyReplacements(m_doc, toReplace); @@ -443,7 +521,6 @@ TextEditor::Replacements ClangFormatBaseIndenter::format( TextEditor::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBlock, const QTextBlock &endBlock, - const QByteArray &buffer, const QChar &typedChar, int cursorPositionInEditor) { @@ -461,10 +538,12 @@ TextEditor::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBlo cursorPositionInEditor += startBlock.position() - startBlockPosition; } + const QByteArray buffer = m_doc->toPlainText().toUtf8(); + ReplacementsToKeep replacementsToKeep = ReplacementsToKeep::OnlyIndent; if (formatWhileTyping() && (cursorPositionInEditor == -1 || cursorPositionInEditor >= startBlockPosition) - && (typedChar == QChar::Null || typedChar == ';' || typedChar == '}')) { + && (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 // the indented block - don't trigger extra formatting in this case. @@ -487,9 +566,7 @@ void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock, const QChar &typedChar, int cursorPositionInEditor) { - const QByteArray buffer = m_doc->toPlainText().toUtf8(); - applyReplacements(m_doc, - indentsFor(startBlock, endBlock, buffer, typedChar, cursorPositionInEditor)); + applyReplacements(m_doc, indentsFor(startBlock, endBlock, typedChar, cursorPositionInEditor)); } void ClangFormatBaseIndenter::indent(const QTextCursor &cursor, @@ -533,15 +610,14 @@ int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, const TextEditor::TabSettings & /*tabSettings*/, int cursorPositionInEditor) { - const QByteArray buffer = m_doc->toPlainText().toUtf8(); TextEditor::Replacements toReplace = indentsFor(block, block, - buffer, QChar::Null, cursorPositionInEditor); if (toReplace.empty()) return -1; + const QByteArray buffer = m_doc->toPlainText().toUtf8(); return indentationForBlock(toReplace, buffer, block); } @@ -553,13 +629,12 @@ TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks( TextEditor::IndentationForBlock ret; if (blocks.isEmpty()) return ret; - const QByteArray buffer = m_doc->toPlainText().toUtf8(); TextEditor::Replacements toReplace = indentsFor(blocks.front(), blocks.back(), - buffer, QChar::Null, cursorPositionInEditor); + const QByteArray buffer = m_doc->toPlainText().toUtf8(); for (const QTextBlock &block : blocks) ret.insert(block.blockNumber(), indentationForBlock(toReplace, buffer, block)); return ret; diff --git a/src/plugins/clangformat/clangformatbaseindenter.h b/src/plugins/clangformat/clangformatbaseindenter.h index b412b57d6d4..87b5e0c927b 100644 --- a/src/plugins/clangformat/clangformatbaseindenter.h +++ b/src/plugins/clangformat/clangformatbaseindenter.h @@ -81,7 +81,6 @@ private: int cursorPositionInEditor); TextEditor::Replacements indentsFor(QTextBlock startBlock, const QTextBlock &endBlock, - const QByteArray &buffer, const QChar &typedChar, int cursorPositionInEditor); TextEditor::Replacements replacements(QByteArray buffer, diff --git a/src/plugins/clangformat/clangformatconfigwidget.cpp b/src/plugins/clangformat/clangformatconfigwidget.cpp index faaf6d937ff..72e1778999a 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.cpp +++ b/src/plugins/clangformat/clangformatconfigwidget.cpp @@ -59,6 +59,24 @@ ClangFormatConfigWidget::ClangFormatConfigWidget(ProjectExplorer::Project *proje { m_ui->setupUi(this); + m_preview = new TextEditor::SnippetEditorWidget(this); + m_ui->horizontalLayout_2->addWidget(m_preview); + if (m_project) { + m_ui->applyButton->show(); + hideGlobalCheckboxes(); + m_ui->overrideDefault->setChecked( + m_project->namedSettings(Constants::OVERRIDE_FILE_ID).toBool()); + } else { + m_ui->applyButton->hide(); + showGlobalCheckboxes(); + m_ui->overrideDefault->setChecked(ClangFormatSettings::instance().overrideDefaultFile()); + } + + connect(m_ui->overrideDefault, &QCheckBox::toggled, this, [this](bool checked) { + if (checked) + createStyleFileIfNeeded(!m_project); + initialize(); + }); initialize(); } @@ -81,15 +99,19 @@ void ClangFormatConfigWidget::showGlobalCheckboxes() m_ui->formatOnSave->show(); } +static bool projectConfigExists() +{ + return Utils::FileName::fromString(Core::ICore::userResourcePath()) + .appendPath("clang-format") + .appendPath(currentProjectUniqueId()) + .appendPath((Constants::SETTINGS_FILE_NAME)) + .exists(); +} + void ClangFormatConfigWidget::initialize() { - m_ui->projectHasClangFormat->show(); - m_ui->clangFormatOptionsTable->show(); - m_ui->applyButton->show(); - hideGlobalCheckboxes(); + m_ui->projectHasClangFormat->hide(); - m_preview = new TextEditor::SnippetEditorWidget(this); - m_ui->horizontalLayout_2->addWidget(m_preview); m_preview->setPlainText(QLatin1String(CppTools::Constants::DEFAULT_CODE_STYLE_SNIPPETS[0])); m_preview->textDocument()->setIndenter(new ClangFormatIndenter(m_preview->document())); m_preview->textDocument()->setFontSettings(TextEditor::TextEditorSettings::fontSettings()); @@ -103,21 +125,15 @@ void ClangFormatConfigWidget::initialize() if (lastItem->spacerItem()) m_ui->verticalLayout->removeItem(lastItem); - if (m_project - && !m_project->projectDirectory().appendPath(Constants::SETTINGS_FILE_NAME).exists()) { - m_ui->projectHasClangFormat->setText(tr("No .clang-format file for the project.")); + if (!m_ui->overrideDefault->isChecked()) { m_ui->clangFormatOptionsTable->hide(); - m_ui->applyButton->hide(); + m_preview->hide(); m_ui->verticalLayout->addStretch(1); - - connect(m_ui->createFileButton, &QPushButton::clicked, this, [this]() { - createStyleFileIfNeeded(false); - initialize(); - }); return; } - m_ui->createFileButton->hide(); + m_ui->clangFormatOptionsTable->show(); + m_preview->show(); Utils::FileName fileName; if (m_project) { @@ -126,19 +142,13 @@ void ClangFormatConfigWidget::initialize() fileName = m_project->projectFilePath().appendPath("snippet.cpp"); } else { const Project *currentProject = SessionManager::startupProject(); - if (!currentProject - || !currentProject->projectDirectory() - .appendPath(Constants::SETTINGS_FILE_NAME) - .exists()) { + if (!currentProject || !projectConfigExists()) { m_ui->projectHasClangFormat->hide(); } else { m_ui->projectHasClangFormat->setText( - tr("Current project has its own .clang-format file " + tr("Current project has its own overridden .clang-format file " "and can be configured in Projects > Code Style > C++.")); } - createStyleFileIfNeeded(true); - showGlobalCheckboxes(); - m_ui->applyButton->hide(); fileName = Utils::FileName::fromString(Core::ICore::userResourcePath()) .appendPath("snippet.cpp"); } @@ -160,7 +170,7 @@ void ClangFormatConfigWidget::fillTable() { clang::format::FormatStyle style = m_project ? currentProjectStyle() : currentGlobalStyle(); - std::string configText = clang::format::configurationAsText(style); + const std::string configText = clang::format::configurationAsText(style); m_ui->clangFormatOptionsTable->setPlainText(QString::fromStdString(configText)); } @@ -168,13 +178,19 @@ ClangFormatConfigWidget::~ClangFormatConfigWidget() = default; void ClangFormatConfigWidget::apply() { + ClangFormatSettings &settings = ClangFormatSettings::instance(); if (!m_project) { - ClangFormatSettings &settings = ClangFormatSettings::instance(); settings.setFormatCodeInsteadOfIndent(m_ui->formatAlways->isChecked()); settings.setFormatWhileTyping(m_ui->formatWhileTyping->isChecked()); settings.setFormatOnSave(m_ui->formatOnSave->isChecked()); - settings.write(); + settings.setOverrideDefaultFile(m_ui->overrideDefault->isChecked()); + } else { + m_project->setNamedSettings(Constants::OVERRIDE_FILE_ID, m_ui->overrideDefault->isChecked()); } + settings.write(); + + if (!m_ui->overrideDefault->isChecked()) + return; const QString text = m_ui->clangFormatOptionsTable->toPlainText(); clang::format::FormatStyle style; @@ -184,16 +200,18 @@ void ClangFormatConfigWidget::apply() QMessageBox::warning(this, tr("Error in ClangFormat configuration"), QString::fromStdString(error.message())); - fillTable(); - updatePreview(); + if (m_ui->overrideDefault->isChecked()) { + fillTable(); + updatePreview(); + } return; } - QString filePath; + QString filePath = Core::ICore::userResourcePath(); if (m_project) - filePath = m_project->projectDirectory().appendPath(Constants::SETTINGS_FILE_NAME).toString(); - else - filePath = Core::ICore::userResourcePath() + "/" + Constants::SETTINGS_FILE_NAME; + filePath += "/clang-format/" + currentProjectUniqueId(); + filePath += "/" + QLatin1String(Constants::SETTINGS_FILE_NAME); + QFile file(filePath); if (!file.open(QFile::WriteOnly)) return; @@ -201,7 +219,8 @@ void ClangFormatConfigWidget::apply() file.write(text.toUtf8()); file.close(); - updatePreview(); + if (m_ui->overrideDefault->isChecked()) + updatePreview(); } } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatconfigwidget.ui b/src/plugins/clangformat/clangformatconfigwidget.ui index 41e40ef1fdc..c8225981f59 100644 --- a/src/plugins/clangformat/clangformatconfigwidget.ui +++ b/src/plugins/clangformat/clangformatconfigwidget.ui @@ -55,6 +55,13 @@ </widget> </item> <item> + <widget class="QCheckBox" name="overrideDefault"> + <property name="text"> + <string>Override Clang Format configuration file</string> + </property> + </widget> + </item> + <item> <layout class="QHBoxLayout" name="horizontalLayout_2"> <item> <widget class="QPlainTextEdit" name="clangFormatOptionsTable"/> @@ -64,13 +71,6 @@ <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> - <widget class="QPushButton" name="createFileButton"> - <property name="text"> - <string>Create Clang Format Configuration File</string> - </property> - </widget> - </item> - <item> <widget class="QPushButton" name="applyButton"> <property name="text"> <string>Apply</string> diff --git a/src/plugins/clangformat/clangformatconstants.h b/src/plugins/clangformat/clangformatconstants.h index 8b7cbcf9bd2..2635dbedeac 100644 --- a/src/plugins/clangformat/clangformatconstants.h +++ b/src/plugins/clangformat/clangformatconstants.h @@ -34,5 +34,7 @@ static const char SETTINGS_ID[] = "ClangFormat"; static const char FORMAT_CODE_INSTEAD_OF_INDENT_ID[] = "ClangFormat.FormatCodeInsteadOfIndent"; static const char FORMAT_WHILE_TYPING_ID[] = "ClangFormat.FormatWhileTyping"; static const char FORMAT_CODE_ON_SAVE_ID[] = "ClangFormat.FormatCodeOnSave"; +static const char OVERRIDE_FILE_ID[] = "ClangFormat.OverrideFile"; +static const char OPEN_CURRENT_CONFIG_ID[] = "ClangFormat.OpenCurrentConfig"; } // namespace Constants } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatindenter.cpp b/src/plugins/clangformat/clangformatindenter.cpp index 42cedb7b6c9..381762ee7f2 100644 --- a/src/plugins/clangformat/clangformatindenter.cpp +++ b/src/plugins/clangformat/clangformatindenter.cpp @@ -57,7 +57,7 @@ bool ClangFormatIndenter::formatWhileTyping() const Utils::optional<TabSettings> ClangFormatIndenter::tabSettings() const { - FormatStyle style = currentProjectStyle(); + FormatStyle style = styleForFile(); TabSettings tabSettings; switch (style.UseTab) { diff --git a/src/plugins/clangformat/clangformatplugin.cpp b/src/plugins/clangformat/clangformatplugin.cpp index 932234f9822..e82802ab303 100644 --- a/src/plugins/clangformat/clangformatplugin.cpp +++ b/src/plugins/clangformat/clangformatplugin.cpp @@ -26,16 +26,23 @@ #include "clangformatplugin.h" #include "clangformatconfigwidget.h" +#include "clangformatconstants.h" #include "clangformatindenter.h" +#include "clangformatutils.h" #include <utils/qtcassert.h> -#include <coreplugin/icore.h> -#include <coreplugin/icontext.h> +#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/command.h> -#include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/coreconstants.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/editormanager/ieditor.h> +#include <coreplugin/icontext.h> +#include <coreplugin/icore.h> +#include <coreplugin/idocument.h> + +#include <cppeditor/cppeditorconstants.h> #include <cpptools/cppcodestylepreferencesfactory.h> #include <cpptools/cpptoolsconstants.h> @@ -103,6 +110,48 @@ bool ClangFormatPlugin::initialize(const QStringList &arguments, QString *errorS Q_UNUSED(errorString); #ifdef KEEP_LINE_BREAKS_FOR_NON_EMPTY_LINES_BACKPORTED replaceCppCodeStyle(); + + Core::ActionContainer *contextMenu = Core::ActionManager::actionContainer( + CppEditor::Constants::M_CONTEXT); + if (contextMenu) { + auto openClangFormatConfigAction + = new QAction(tr("Open Used .clang-format Configuration File"), this); + Core::Command *command + = Core::ActionManager::registerAction(openClangFormatConfigAction, + Constants::OPEN_CURRENT_CONFIG_ID); + contextMenu->addSeparator(); + contextMenu->addAction(command); + + if (Core::EditorManager::currentEditor()) { + const Core::IDocument *doc = Core::EditorManager::currentEditor()->document(); + if (doc) + openClangFormatConfigAction->setData(doc->filePath().toString()); + } + + connect(openClangFormatConfigAction, + &QAction::triggered, + this, + [openClangFormatConfigAction]() { + const QString fileName = openClangFormatConfigAction->data().toString(); + if (!fileName.isEmpty()) { + const QString clangFormatConfigPath = configForFile( + Utils::FileName::fromString(fileName)); + Core::EditorManager::openEditor(clangFormatConfigPath); + } + }); + + connect(Core::EditorManager::instance(), + &Core::EditorManager::currentEditorChanged, + this, + [openClangFormatConfigAction](Core::IEditor *editor) { + if (!editor) + return; + + const Core::IDocument *doc = editor->document(); + if (doc) + openClangFormatConfigAction->setData(doc->filePath().toString()); + }); + } #endif return true; } diff --git a/src/plugins/clangformat/clangformatsettings.cpp b/src/plugins/clangformat/clangformatsettings.cpp index 8be2b3cefee..8f70130ae3a 100644 --- a/src/plugins/clangformat/clangformatsettings.cpp +++ b/src/plugins/clangformat/clangformatsettings.cpp @@ -46,6 +46,8 @@ ClangFormatSettings::ClangFormatSettings() .toBool(); m_formatOnSave = settings->value(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), false) .toBool(); + m_overrideDefaultFile = settings->value(QLatin1String(Constants::OVERRIDE_FILE_ID), false) + .toBool(); settings->endGroup(); } @@ -57,6 +59,7 @@ void ClangFormatSettings::write() const m_formatCodeInsteadOfIndent); settings->setValue(QLatin1String(Constants::FORMAT_WHILE_TYPING_ID), m_formatWhileTyping); settings->setValue(QLatin1String(Constants::FORMAT_CODE_ON_SAVE_ID), m_formatOnSave); + settings->setValue(QLatin1String(Constants::OVERRIDE_FILE_ID), m_overrideDefaultFile); settings->endGroup(); } @@ -90,4 +93,14 @@ bool ClangFormatSettings::formatOnSave() const return m_formatOnSave; } +void ClangFormatSettings::setOverrideDefaultFile(bool enable) +{ + m_overrideDefaultFile = enable; +} + +bool ClangFormatSettings::overrideDefaultFile() const +{ + return m_overrideDefaultFile; +} + } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatsettings.h b/src/plugins/clangformat/clangformatsettings.h index 0ea9ed9747e..9344cf34a96 100644 --- a/src/plugins/clangformat/clangformatsettings.h +++ b/src/plugins/clangformat/clangformatsettings.h @@ -25,6 +25,8 @@ #pragma once +#include <QString> + namespace ClangFormat { class ClangFormatSettings @@ -43,10 +45,14 @@ public: void setFormatOnSave(bool enable); bool formatOnSave() const; + + void setOverrideDefaultFile(bool enable); + bool overrideDefaultFile() const; private: bool m_formatCodeInsteadOfIndent = false; bool m_formatWhileTyping = false; bool m_formatOnSave = false; + bool m_overrideDefaultFile = false; }; } // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatutils.cpp b/src/plugins/clangformat/clangformatutils.cpp index babe94da24f..74c4165a34b 100644 --- a/src/plugins/clangformat/clangformatutils.cpp +++ b/src/plugins/clangformat/clangformatutils.cpp @@ -26,6 +26,7 @@ #include "clangformatutils.h" #include "clangformatconstants.h" +#include "clangformatsettings.h" #include <coreplugin/icore.h> #include <cpptools/cppcodestylesettings.h> @@ -33,6 +34,8 @@ #include <projectexplorer/project.h> #include <projectexplorer/session.h> +#include <QCryptographicHash> + using namespace clang; using namespace format; using namespace llvm; @@ -42,6 +45,104 @@ using namespace TextEditor; namespace ClangFormat { +static clang::format::FormatStyle qtcStyle() +{ + clang::format::FormatStyle style = getLLVMStyle(); + style.Language = FormatStyle::LK_Cpp; + style.AccessModifierOffset = -4; + style.AlignAfterOpenBracket = FormatStyle::BAS_Align; + style.AlignConsecutiveAssignments = false; + style.AlignConsecutiveDeclarations = false; + style.AlignEscapedNewlines = FormatStyle::ENAS_DontAlign; + style.AlignOperands = true; + style.AlignTrailingComments = true; + style.AllowAllParametersOfDeclarationOnNextLine = true; + style.AllowShortBlocksOnASingleLine = false; + style.AllowShortCaseLabelsOnASingleLine = false; + style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_Inline; + style.AllowShortIfStatementsOnASingleLine = false; + style.AllowShortLoopsOnASingleLine = false; + style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_None; + style.AlwaysBreakBeforeMultilineStrings = false; + style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes; + style.BinPackArguments = false; + style.BinPackParameters = false; + style.BraceWrapping.AfterClass = true; + style.BraceWrapping.AfterControlStatement = false; + style.BraceWrapping.AfterEnum = false; + style.BraceWrapping.AfterFunction = true; + style.BraceWrapping.AfterNamespace = false; + style.BraceWrapping.AfterObjCDeclaration = false; + style.BraceWrapping.AfterStruct = true; + style.BraceWrapping.AfterUnion = false; + style.BraceWrapping.BeforeCatch = false; + style.BraceWrapping.BeforeElse = false; + style.BraceWrapping.IndentBraces = false; + style.BraceWrapping.SplitEmptyFunction = false; + style.BraceWrapping.SplitEmptyRecord = false; + style.BraceWrapping.SplitEmptyNamespace = false; + style.BreakBeforeBinaryOperators = FormatStyle::BOS_All; + style.BreakBeforeBraces = FormatStyle::BS_Custom; + style.BreakBeforeTernaryOperators = true; + style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeComma; + style.BreakAfterJavaFieldAnnotations = false; + style.BreakStringLiterals = true; + style.ColumnLimit = 100; + style.CommentPragmas = "^ IWYU pragma:"; + style.CompactNamespaces = false; + style.ConstructorInitializerAllOnOneLineOrOnePerLine = false; + style.ConstructorInitializerIndentWidth = 4; + style.ContinuationIndentWidth = 4; + style.Cpp11BracedListStyle = true; + style.DerivePointerAlignment = false; + style.DisableFormat = false; + style.ExperimentalAutoDetectBinPacking = false; + style.FixNamespaceComments = true; + style.ForEachMacros = {"forever", "foreach", "Q_FOREACH", "BOOST_FOREACH"}; + style.IncludeStyle.IncludeCategories = {{"^<Q.*", 200}}; + style.IncludeStyle.IncludeIsMainRegex = "(Test)?$"; + style.IndentCaseLabels = false; + style.IndentWidth = 4; + style.IndentWrappedFunctionNames = false; + style.JavaScriptQuotes = FormatStyle::JSQS_Leave; + style.JavaScriptWrapImports = true; + style.KeepEmptyLinesAtTheStartOfBlocks = false; + // Do not add QT_BEGIN_NAMESPACE/QT_END_NAMESPACE as this will indent lines in between. + style.MacroBlockBegin = ""; + style.MacroBlockEnd = ""; + style.MaxEmptyLinesToKeep = 1; + style.NamespaceIndentation = FormatStyle::NI_None; + style.ObjCBlockIndentWidth = 4; + style.ObjCSpaceAfterProperty = false; + style.ObjCSpaceBeforeProtocolList = true; + style.PenaltyBreakAssignment = 150; + style.PenaltyBreakBeforeFirstCallParameter = 300; + style.PenaltyBreakComment = 500; + style.PenaltyBreakFirstLessLess = 400; + style.PenaltyBreakString = 600; + style.PenaltyExcessCharacter = 50; + style.PenaltyReturnTypeOnItsOwnLine = 300; + style.PointerAlignment = FormatStyle::PAS_Right; + style.ReflowComments = false; + style.SortIncludes = true; + style.SortUsingDeclarations = true; + style.SpaceAfterCStyleCast = true; + style.SpaceAfterTemplateKeyword = false; + style.SpaceBeforeAssignmentOperators = true; + style.SpaceBeforeParens = FormatStyle::SBPO_ControlStatements; + style.SpaceInEmptyParentheses = false; + style.SpacesBeforeTrailingComments = 1; + style.SpacesInAngles = false; + style.SpacesInContainerLiterals = false; + style.SpacesInCStyleCastParentheses = false; + style.SpacesInParentheses = false; + style.SpacesInSquareBrackets = false; + style.Standard = FormatStyle::LS_Cpp11; + style.TabWidth = 4; + style.UseTab = FormatStyle::UT_Never; + return style; +} + static void applyTabSettings(clang::format::FormatStyle &style, const TabSettings &settings) { style.IndentWidth = static_cast<unsigned>(settings.m_indentSize); @@ -63,72 +164,86 @@ static void applyTabSettings(clang::format::FormatStyle &style, const TabSetting } } -static void applyCppCodeStyleSettings(clang::format::FormatStyle &style, - const CppCodeStyleSettings &settings) +static bool useGlobalOverriddenSettings() { - style.IndentCaseLabels = settings.indentSwitchLabels; - style.AlignOperands = settings.alignAssignments; - style.NamespaceIndentation = FormatStyle::NI_None; - if (settings.indentNamespaceBody) - style.NamespaceIndentation = FormatStyle::NI_All; + return ClangFormatSettings::instance().overrideDefaultFile(); +} - style.BraceWrapping.IndentBraces = false; - if (settings.indentBlockBraces) { - if (settings.indentClassBraces && settings.indentEnumBraces - && settings.indentNamespaceBraces && settings.indentFunctionBraces) { - style.BraceWrapping.IndentBraces = true; - } else { - style.BreakBeforeBraces = FormatStyle::BS_GNU; - } - } +QString currentProjectUniqueId() +{ + const Project *project = SessionManager::startupProject(); + if (!project) + return QString(); - if (settings.bindStarToIdentifier || settings.bindStarToRightSpecifier) - style.PointerAlignment = FormatStyle::PAS_Right; - else - style.PointerAlignment = FormatStyle::PAS_Left; + return QString::fromUtf8(QCryptographicHash::hash(project->projectFilePath().toString().toUtf8(), + QCryptographicHash::Md5) + .toHex(0)); +} + +static bool useProjectOverriddenSettings() +{ + const Project *project = SessionManager::startupProject(); + return project ? project->namedSettings(Constants::OVERRIDE_FILE_ID).toBool() : false; +} - style.AccessModifierOffset = settings.indentAccessSpecifiers - ? 0 - : - static_cast<int>(style.IndentWidth); +static Utils::FileName globalPath() +{ + return Utils::FileName::fromString(Core::ICore::userResourcePath()); } static Utils::FileName projectPath() { const Project *project = SessionManager::startupProject(); if (project) - return project->projectDirectory(); + return globalPath().appendPath("clang-format").appendPath(currentProjectUniqueId()); return Utils::FileName(); } -static Utils::FileName globalPath() +static QString findConfig(Utils::FileName fileName) { - return Utils::FileName::fromString(Core::ICore::userResourcePath()); + QDir parentDir(fileName.parentDir().toString()); + while (!parentDir.exists(Constants::SETTINGS_FILE_NAME) + && !parentDir.exists(Constants::SETTINGS_FILE_ALT_NAME)) { + if (!parentDir.cdUp()) + return QString(); + } + + if (parentDir.exists(Constants::SETTINGS_FILE_NAME)) + return parentDir.filePath(Constants::SETTINGS_FILE_NAME); + return parentDir.filePath(Constants::SETTINGS_FILE_ALT_NAME); } -static QString configForFile(Utils::FileName fileName) +static QString configForFile(Utils::FileName fileName, bool checkForSettings) { - Utils::FileName topProjectPath = projectPath(); - if (topProjectPath.isEmpty()) - return QString(); + QDir overrideDir; + if (!checkForSettings || useProjectOverriddenSettings()) { + overrideDir = projectPath().toString(); + if (!overrideDir.isEmpty() && overrideDir.exists(Constants::SETTINGS_FILE_NAME)) + return overrideDir.filePath(Constants::SETTINGS_FILE_NAME); + } - QDir projectDir(fileName.parentDir().toString()); - while (!projectDir.exists(Constants::SETTINGS_FILE_NAME) - && !projectDir.exists(Constants::SETTINGS_FILE_ALT_NAME)) { - if (projectDir.path() == topProjectPath.toString() - || !Utils::FileName::fromString(projectDir.path()).isChildOf(topProjectPath) - || !projectDir.cdUp()) { - return QString(); - } + if (!checkForSettings || useGlobalOverriddenSettings()) { + overrideDir = globalPath().toString(); + if (!overrideDir.isEmpty() && overrideDir.exists(Constants::SETTINGS_FILE_NAME)) + return overrideDir.filePath(Constants::SETTINGS_FILE_NAME); } - if (projectDir.exists(Constants::SETTINGS_FILE_NAME)) - return projectDir.filePath(Constants::SETTINGS_FILE_NAME); - return projectDir.filePath(Constants::SETTINGS_FILE_ALT_NAME); + return findConfig(fileName); +} + +QString configForFile(Utils::FileName fileName) +{ + return configForFile(fileName, true); +} + +Utils::FileName assumedPathForConfig(const QString &configFile) +{ + Utils::FileName fileName = Utils::FileName::fromString(configFile); + return fileName.parentDir().appendPath("test.cpp"); } -static clang::format::FormatStyle constructStyle(bool isGlobal, - const QByteArray &baseStyle = QByteArray()) +static clang::format::FormatStyle constructStyle(const QByteArray &baseStyle = QByteArray()) { if (!baseStyle.isEmpty()) { // Try to get the style for this base style. @@ -144,21 +259,7 @@ static clang::format::FormatStyle constructStyle(bool isGlobal, // Fallthrough to the default style. } - FormatStyle style = getLLVMStyle(); - style.BreakBeforeBraces = FormatStyle::BS_Custom; - - const CppCodeStyleSettings codeStyleSettings = isGlobal - ? CppCodeStyleSettings::currentGlobalCodeStyle() - : CppCodeStyleSettings::currentProjectCodeStyle() - .value_or(CppCodeStyleSettings::currentGlobalCodeStyle()); - const TabSettings tabSettings = isGlobal - ? CppCodeStyleSettings::currentGlobalTabSettings() - : CppCodeStyleSettings::currentProjectTabSettings(); - - applyTabSettings(style, tabSettings); - applyCppCodeStyleSettings(style, codeStyleSettings); - - return style; + return qtcStyle(); } void createStyleFileIfNeeded(bool isGlobal) @@ -169,9 +270,21 @@ void createStyleFileIfNeeded(bool isGlobal) if (QFile::exists(configFile)) return; + QDir().mkpath(path.parentDir().toString()); + if (!isGlobal) { + const Project *project = SessionManager::startupProject(); + Utils::FileName possibleProjectConfig = project->rootProjectDirectory().appendPath( + Constants::SETTINGS_FILE_NAME); + if (possibleProjectConfig.exists()) { + // Just copy th .clang-format if current project has one. + QFile::copy(possibleProjectConfig.toString(), configFile); + return; + } + } + std::fstream newStyleFile(configFile.toStdString(), std::fstream::out); if (newStyleFile.is_open()) { - newStyleFile << clang::format::configurationAsText(constructStyle(isGlobal)); + newStyleFile << clang::format::configurationAsText(constructStyle()); newStyleFile.close(); } } @@ -198,17 +311,13 @@ static QByteArray configBaseStyleName(const QString &configFile) .trimmed(); } -clang::format::FormatStyle styleForFile(Utils::FileName fileName) +static clang::format::FormatStyle styleForFile(Utils::FileName fileName, bool checkForSettings) { - bool isGlobal = false; - QString configFile = configForFile(fileName); - if (configFile.isEmpty()) { - Utils::FileName path = fileName = globalPath(); - fileName.appendPath(Constants::SAMPLE_FILE_NAME); - createStyleFileIfNeeded(true); - configFile = path.appendPath(Constants::SETTINGS_FILE_NAME).toString(); - } + QString configFile = configForFile(fileName, checkForSettings); + if (configFile.isEmpty()) + return constructStyle(); + fileName = assumedPathForConfig(configFile); Expected<FormatStyle> style = format::getStyle("file", fileName.toString().toStdString(), "none"); @@ -219,17 +328,21 @@ clang::format::FormatStyle styleForFile(Utils::FileName fileName) // do nothing }); - return constructStyle(isGlobal, configBaseStyleName(configFile)); + return constructStyle(configBaseStyleName(configFile)); } -clang::format::FormatStyle currentProjectStyle() +clang::format::FormatStyle styleForFile(Utils::FileName fileName) { - return styleForFile(projectPath().appendPath(Constants::SAMPLE_FILE_NAME)); + return styleForFile(fileName, true); } -clang::format::FormatStyle currentGlobalStyle() +clang::format::FormatStyle currentProjectStyle() { - return styleForFile(globalPath().appendPath(Constants::SAMPLE_FILE_NAME)); + return styleForFile(projectPath().appendPath(Constants::SAMPLE_FILE_NAME), false); } +clang::format::FormatStyle currentGlobalStyle() +{ + return styleForFile(globalPath().appendPath(Constants::SAMPLE_FILE_NAME), false); } +} // namespace ClangFormat diff --git a/src/plugins/clangformat/clangformatutils.h b/src/plugins/clangformat/clangformatutils.h index 41e2b5611fa..bd530769430 100644 --- a/src/plugins/clangformat/clangformatutils.h +++ b/src/plugins/clangformat/clangformatutils.h @@ -25,6 +25,7 @@ #pragma once +#include <coreplugin/id.h> #include <utils/fileutils.h> #include <clang/Format/Format.h> @@ -37,10 +38,13 @@ namespace ClangFormat { // Creates the style for the current project or the global style if needed. void createStyleFileIfNeeded(bool isGlobal); +QString currentProjectUniqueId(); + clang::format::FormatStyle currentProjectStyle(); clang::format::FormatStyle currentGlobalStyle(); // Is the style from the matching .clang-format file or global one if it's not found. +QString configForFile(Utils::FileName fileName); clang::format::FormatStyle styleForFile(Utils::FileName fileName); } diff --git a/src/plugins/clangtools/clangtidyclazytool.cpp b/src/plugins/clangtools/clangtidyclazytool.cpp index 2dc381f6a3d..43858cc8650 100644 --- a/src/plugins/clangtools/clangtidyclazytool.cpp +++ b/src/plugins/clangtools/clangtidyclazytool.cpp @@ -452,12 +452,12 @@ void ClangTidyClazyTool::handleStateUpdate() if (issuesFound) message = tr("Running - %n diagnostics", nullptr, issuesFound); else - message = tr("Running - No diagnostics", nullptr, issuesFound); + message = tr("Running - No diagnostics"); } else { if (issuesFound) message = tr("Finished - %n diagnostics", nullptr, issuesFound); else - message = tr("Finished - No diagnostics", nullptr, issuesFound); + message = tr("Finished - No diagnostics"); } Debugger::showPermanentStatusMessage(message); diff --git a/src/plugins/coreplugin/find/searchresulttreeview.cpp b/src/plugins/coreplugin/find/searchresulttreeview.cpp index 6a9b7067bf4..0ca09fcefd1 100644 --- a/src/plugins/coreplugin/find/searchresulttreeview.cpp +++ b/src/plugins/coreplugin/find/searchresulttreeview.cpp @@ -78,6 +78,21 @@ void SearchResultTreeView::addResults(const QList<SearchResultItem> &items, Sear } } +void SearchResultTreeView::keyPressEvent(QKeyEvent *event) +{ + if ((event->key() == Qt::Key_Return + || event->key() == Qt::Key_Enter) + && event->modifiers() == 0 + && currentIndex().isValid() + && state() != QAbstractItemView::EditingState) { + const SearchResultItem item + = model()->data(currentIndex(), ItemDataRoles::ResultItemRole).value<SearchResultItem>(); + emit jumpToSearchResult(item); + return; + } + TreeView::keyPressEvent(event); +} + void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index) { if (model()->data(index, ItemDataRoles::IsGeneratedRole).toBool()) diff --git a/src/plugins/coreplugin/find/searchresulttreeview.h b/src/plugins/coreplugin/find/searchresulttreeview.h index 961940fe017..eb11d29a7d9 100644 --- a/src/plugins/coreplugin/find/searchresulttreeview.h +++ b/src/plugins/coreplugin/find/searchresulttreeview.h @@ -49,6 +49,8 @@ public: SearchResultTreeModel *model() const; void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode); + void keyPressEvent(QKeyEvent *event) override; + signals: void jumpToSearchResult(const SearchResultItem &item); diff --git a/src/plugins/coreplugin/helpitem.h b/src/plugins/coreplugin/helpitem.h index 15ff585ca82..c258eed0d8a 100644 --- a/src/plugins/coreplugin/helpitem.h +++ b/src/plugins/coreplugin/helpitem.h @@ -84,10 +84,9 @@ public: const Links &links() const; const Links bestLinks() const; const QString keyword() const; - -private: bool isFuzzyMatch() const; +private: QUrl m_helpUrl; QStringList m_helpIds; QString m_docMark; diff --git a/src/plugins/coreplugin/locator/locatorwidget.cpp b/src/plugins/coreplugin/locator/locatorwidget.cpp index 2421bd776ab..994c9e5d0e8 100644 --- a/src/plugins/coreplugin/locator/locatorwidget.cpp +++ b/src/plugins/coreplugin/locator/locatorwidget.cpp @@ -252,6 +252,14 @@ void LocatorModel::addEntries(const QList<LocatorFilterEntry> &entries) CompletionList::CompletionList(QWidget *parent) : Utils::TreeView(parent) { + // on macOS and Windows the popup doesn't really get focus, so fake the selection color + // which would then just be a very light gray, but should look as if it had focus + QPalette p = palette(); + p.setBrush(QPalette::Inactive, + QPalette::Highlight, + p.brush(QPalette::Normal, QPalette::Highlight)); + setPalette(p); + setItemDelegate(new CompletionDelegate(this)); setRootIsDecorated(false); setUniformRowHeights(true); diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index 3f19d4b9b94..eb411bdaa9d 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -507,7 +507,6 @@ void OutputPaneManager::showPage(int idx, int flags) ensurePageVisible(idx); IOutputPane *out = g_outputPanes.at(idx).pane; - out->visibilityChanged(true); if (flags & IOutputPane::WithFocus) { if (out->canFocus()) out->setFocus(); @@ -538,7 +537,10 @@ void OutputPaneManager::setCurrentIndex(int idx) m_outputWidgetPane->setCurrentIndex(idx); m_opToolBarWidgets->setCurrentIndex(idx); - IOutputPane *pane = g_outputPanes.at(idx).pane; + OutputPaneData &data = g_outputPanes[idx]; + IOutputPane *pane = data.pane; + data.button->show(); + data.buttonVisible = true; pane->visibilityChanged(true); bool canNavigate = pane->canNavigate(); @@ -574,8 +576,6 @@ void OutputPaneManager::popupMenu() data.button->hide(); data.buttonVisible = false; } else { - data.button->show(); - data.buttonVisible = true; showPage(idx, IOutputPane::ModeSwitch); } } diff --git a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp index e19a94513d7..4f03a681f06 100644 --- a/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp +++ b/src/plugins/cpptools/clangdiagnosticconfigswidget.cpp @@ -860,11 +860,11 @@ void ClangDiagnosticConfigsWidget::syncClazyChecksGroupBox() return !m_clazySortFilterProxyModel->filterAcceptsRow(index.row(), index.parent()); }; const bool hasEnabledButHidden = m_clazyTreeModel->hasEnabledButNotVisibleChecks(isHidden); - const QString title = hasEnabledButHidden ? tr("Checks (%1 enabled, some are filtered out)") - : tr("Checks (%1 enabled)"); - - const QStringList checks = m_clazyTreeModel->enabledChecks(); - m_clazyChecks->checksGroupBox->setTitle(title.arg(checks.count())); + const int checksCount = m_clazyTreeModel->enabledChecks().count(); + const QString title = hasEnabledButHidden ? tr("Checks (%n enabled, some are filtered out)", + nullptr, checksCount) + : tr("Checks (%n enabled)", nullptr, checksCount); + m_clazyChecks->checksGroupBox->setTitle(title); } void ClangDiagnosticConfigsWidget::updateConfig(const ClangDiagnosticConfig &config) diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index e5685dc5e05..8dfe0c79773 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -172,7 +172,7 @@ void CompilerOptionsBuilder::addSyntaxOnly() isClStyle() ? add("/Zs") : add("-fsyntax-only"); } -static QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt) +QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt) { QStringList options; @@ -741,6 +741,15 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() continue; } + if (option.startsWith("/Y", Qt::CaseSensitive) + || (option.startsWith("/F", Qt::CaseSensitive) && option != "/F")) { + // Precompiled header flags. + // Skip also the next option if it's not glued to the current one. + if (option.size() > 3) + skipNext = true; + continue; + } + // Check whether a language version is already used. QString theOption = option; if (theOption.startsWith("-std=")) { diff --git a/src/plugins/cpptools/compileroptionsbuilder.h b/src/plugins/cpptools/compileroptionsbuilder.h index 03564ab6164..349279710ad 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.h +++ b/src/plugins/cpptools/compileroptionsbuilder.h @@ -40,6 +40,7 @@ enum class UseBuildSystemWarnings : char { Yes, No }; CPPTOOLS_EXPORT QStringList XclangArgs(const QStringList &args); CPPTOOLS_EXPORT QStringList clangArgsForCl(const QStringList &args); +CPPTOOLS_EXPORT QStringList createLanguageOptionGcc(ProjectFile::Kind fileKind, bool objcExt); class CPPTOOLS_EXPORT CompilerOptionsBuilder { diff --git a/src/plugins/cpptools/cppfilesettingspage.ui b/src/plugins/cpptools/cppfilesettingspage.ui index 9edf2cd5dae..7bb13449acc 100644 --- a/src/plugins/cpptools/cppfilesettingspage.ui +++ b/src/plugins/cpptools/cppfilesettingspage.ui @@ -99,10 +99,10 @@ These prefixes are used in addition to current file name on Switch Header/Source <item row="7" column="1"> <widget class="QCheckBox" name="headerPragmaOnceCheckBox"> <property name="toolTip"> - <string>Uses #pragma once instead of #ifndef include guards.</string> + <string>Uses "#pragma once" instead of "#ifndef" include guards.</string> </property> <property name="text"> - <string>Use '#pragma once' instead of '#ifndef' guards</string> + <string>Use "#pragma once" instead of "#ifndef" guards</string> </property> </widget> </item> diff --git a/src/plugins/cpptools/cpptoolsplugin.cpp b/src/plugins/cpptools/cpptoolsplugin.cpp index 8068c27d0bd..d2ca6d5d0b8 100644 --- a/src/plugins/cpptools/cpptoolsplugin.cpp +++ b/src/plugins/cpptools/cpptoolsplugin.cpp @@ -211,7 +211,7 @@ bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error) expander->registerVariable( "Cpp:PragmaOnce", - tr("Insert #pragma once instead of #ifndef include guards into header file"), + tr("Insert \"#pragma once\" instead of \"#ifndef\" include guards into header file"), [] { return usePragmaOnce() ? QString("true") : QString(); }); return true; diff --git a/src/plugins/debugger/console/console.h b/src/plugins/debugger/console/console.h index c46add0a864..4690283a346 100644 --- a/src/plugins/debugger/console/console.h +++ b/src/plugins/debugger/console/console.h @@ -58,7 +58,7 @@ public: QWidget *outputWidget(QWidget *) override; QList<QWidget *> toolBarWidgets() const override; - QString displayName() const override { return tr("Debugger Console"); } + QString displayName() const override { return tr("QML Debugger Console"); } int priorityInStatusBar() const override; void clearContents() override; void visibilityChanged(bool visible) override; diff --git a/src/plugins/debugger/debuggeritemmanager.cpp b/src/plugins/debugger/debuggeritemmanager.cpp index adc81bee691..4a7fb5d4a30 100644 --- a/src/plugins/debugger/debuggeritemmanager.cpp +++ b/src/plugins/debugger/debuggeritemmanager.cpp @@ -748,7 +748,8 @@ void DebuggerItemManagerPrivate::autoDetectGdbOrLldbDebuggers() dir.setPath(base.toFileInfo().absoluteFilePath()); foreach (const QString &entry, dir.entryList()) { if (entry.startsWith("lldb-platform-") - || entry.startsWith("lldb-gdbserver-")) { + || entry.startsWith("lldb-gdbserver-") + || entry.startsWith("lldb-mi-")) { continue; } suspects.append(FileName::fromString(dir.absoluteFilePath(entry))); diff --git a/src/plugins/debugger/debuggerkitinformation.cpp b/src/plugins/debugger/debuggerkitinformation.cpp index b85962bd0ed..9783184346a 100644 --- a/src/plugins/debugger/debuggerkitinformation.cpp +++ b/src/plugins/debugger/debuggerkitinformation.cpp @@ -152,24 +152,7 @@ DebuggerKitAspect::DebuggerKitAspect() setPriority(28000); } -QVariant DebuggerKitAspect::defaultValue(const Kit *k) const -{ - const Abi toolChainAbi = ToolChainKitAspect::targetAbi(k); - const Utils::FileNameList paths = Environment::systemEnvironment().path(); - QVariant nextBestFit; - for (const DebuggerItem &item : DebuggerItemManager::debuggers()) { - for (const Abi &targetAbi : item.abis()) { - if (targetAbi.isCompatibleWith(toolChainAbi)) { - if (paths.contains(item.command())) - return item.id(); // prefer debuggers found in PATH over those found elsewhere - if (nextBestFit.isNull()) - nextBestFit = item.id(); - } - } - } - - return nextBestFit; -} +QVariant DebuggerKitAspect::defaultValue(const Kit *) const { return QVariant(); } void DebuggerKitAspect::setup(Kit *k) { diff --git a/src/plugins/debugger/debuggermainwindow.cpp b/src/plugins/debugger/debuggermainwindow.cpp index 096c03db16c..0ddb983dbe0 100644 --- a/src/plugins/debugger/debuggermainwindow.cpp +++ b/src/plugins/debugger/debuggermainwindow.cpp @@ -119,6 +119,7 @@ public: void resetCurrentPerspective(); int indexInChooser(Perspective *perspective) const; void fixupLayoutIfNeeded(); + void updatePerspectiveChooserWidth(); DebuggerMainWindow *q = nullptr; Perspective *m_currentPerspective = nullptr; @@ -416,10 +417,17 @@ void DebuggerMainWindowPrivate::selectPerspective(Perspective *perspective) fixupLayoutIfNeeded(); } + updatePerspectiveChooserWidth(); +} + +void DebuggerMainWindowPrivate::updatePerspectiveChooserWidth() +{ + Perspective *perspective = m_currentPerspective; int index = indexInChooser(m_currentPerspective); if (index == -1) { - if (Perspective *parent = Perspective::findPerspective(m_currentPerspective->d->m_parentPerspectiveId)) - index = indexInChooser(parent); + perspective = Perspective::findPerspective(m_currentPerspective->d->m_parentPerspectiveId); + if (perspective) + index = indexInChooser(perspective); } if (index != -1) { diff --git a/src/plugins/debugger/debuggerruncontrol.cpp b/src/plugins/debugger/debuggerruncontrol.cpp index 5bb4845073d..19b40113383 100644 --- a/src/plugins/debugger/debuggerruncontrol.cpp +++ b/src/plugins/debugger/debuggerruncontrol.cpp @@ -61,6 +61,7 @@ #include <utils/temporarydirectory.h> #include <utils/temporaryfile.h> #include <utils/url.h> +#include <utils/winutils.h> #include <coreplugin/icontext.h> #include <coreplugin/icore.h> @@ -565,6 +566,17 @@ void DebuggerRunTool::start() if (!fixupParameters()) return; + if (m_runParameters.cppEngineType == CdbEngineType + && Utils::is64BitWindowsBinary(m_runParameters.inferior.executable) + && !Utils::is64BitWindowsBinary(m_runParameters.debugger.executable)) { + reportFailure( + DebuggerPlugin::tr( + "%1 is a 64 bit executable which can not be debugged by a 32 bit Debugger.\n" + "Please select a 64 bit Debugger in the kit settings for this kit.") + .arg(m_runParameters.inferior.executable)); + return; + } + Utils::globalMacroExpander()->registerFileVariables( "DebuggedExecutable", tr("Debugged executable"), [this] { return m_runParameters.inferior.executable; } diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index f0c76422575..9b4f6056f3e 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -395,6 +395,8 @@ void LldbEngine::handleResponse(const QString &response) handleOutputNotification(item); else if (name == "pid") notifyInferiorPid(item.toProcessHandle()); + else if (name == "breakpointmodified") + handleInterpreterBreakpointModified(item); } } @@ -607,6 +609,31 @@ void LldbEngine::handleOutputNotification(const GdbMi &output) showMessage(data, ch); } +void LldbEngine::handleInterpreterBreakpointModified(const GdbMi &bpItem) +{ + QTC_ASSERT(bpItem.childCount(), return); + QString id = bpItem.childAt(0).m_data; + + Breakpoint bp = breakHandler()->findBreakpointByResponseId(id); + if (!bp) // FIXME adapt whole bp handling and turn into soft assert + return; + + // this function got triggered by a lldb internal breakpoint event + // avoid asserts regarding unexpected state transitions + switch (bp->state()) { + case BreakpointInsertionRequested: // was a pending bp + bp->gotoState(BreakpointInsertionProceeding, BreakpointInsertionRequested); + break; + case BreakpointInserted: // was an inserted, gets updated now + bp->gotoState(BreakpointUpdateRequested, BreakpointInserted); + notifyBreakpointChangeProceeding(bp); + break; + default: + break; + } + updateBreakpointData(bp, bpItem, false); +} + void LldbEngine::loadSymbols(const QString &moduleName) { Q_UNUSED(moduleName) diff --git a/src/plugins/debugger/lldb/lldbengine.h b/src/plugins/debugger/lldb/lldbengine.h index f06798dd392..6c5b3c2c21c 100644 --- a/src/plugins/debugger/lldb/lldbengine.h +++ b/src/plugins/debugger/lldb/lldbengine.h @@ -120,6 +120,7 @@ private: void handleStateNotification(const GdbMi &item); void handleLocationNotification(const GdbMi &location); void handleOutputNotification(const GdbMi &output); + void handleInterpreterBreakpointModified(const GdbMi &item); void handleResponse(const QString &data); void updateAll() override; diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index 7abdee2c7c7..a887dc53715 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -657,7 +657,7 @@ void HelpPluginPrivate::showContextHelp(const HelpItem &contextHelp) .arg(contextHelp.helpIds().join(", ")) .arg(HelpPlugin::tr("No documentation available."))); } - } else if (links.size() == 1) { + } else if (links.size() == 1 && !contextHelp.isFuzzyMatch()) { showHelpUrl(links.front().second, LocalHelpManager::contextHelpOption()); } else { QMap<QString, QUrl> map; diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index efd4c8fb6fa..5553e58ecce 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -208,17 +208,6 @@ void Client::openDocument(Core::IDocument *document) connect(textDocument, &QObject::destroyed, this, [this, textDocument]{ m_resetAssistProvider.remove(textDocument); }); - if (BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(textDocument)) { - if (QPointer<TextEditorWidget> widget = editor->editorWidget()) { - connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget](){ - // TODO This would better be a compressing timer - QTimer::singleShot(50, this, [this, widget]() { - if (widget) - cursorPositionChanged(widget); - }); - }); - } - } } m_openedDocument.append(document->filePath()); @@ -344,7 +333,7 @@ void Client::documentContentsChanged(Core::IDocument *document) if (textDocument) { using namespace TextEditor; - if (BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(textDocument)) + for (BaseTextEditor *editor : BaseTextEditor::textEditorsForDocument(textDocument)) if (TextEditorWidget *widget = editor->editorWidget()) widget->setRefactorMarkers(RefactorMarker::filterOutType(widget->refactorMarkers(), id())); requestDocumentSymbols(textDocument); @@ -766,6 +755,11 @@ const DynamicCapabilities &Client::dynamicCapabilities() const return m_dynamicCapabilities; } +const BaseClientInterface *Client::clientInterface() const +{ + return m_clientInterface.data(); +} + void Client::log(const ShowMessageParams &message, Core::MessageManager::PrintToOutputPaneFlag flag) { diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 2d92424f42d..635512fa287 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -147,6 +147,7 @@ public: const LanguageServerProtocol::ServerCapabilities &capabilities() const; const DynamicCapabilities &dynamicCapabilities() const; + const BaseClientInterface *clientInterface() const; signals: void initialized(LanguageServerProtocol::ServerCapabilities capabilities); diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp index b67db95dde1..05980617207 100644 --- a/src/plugins/languageclient/languageclientinterface.cpp +++ b/src/plugins/languageclient/languageclientinterface.cpp @@ -110,7 +110,7 @@ StdIOClientInterface::~StdIOClientInterface() Utils::SynchronousProcess::stopProcess(m_process); } -bool StdIOClientInterface::needsRestart(const StdIOSettings *settings) +bool StdIOClientInterface::needsRestart(const StdIOSettings *settings) const { return m_executable != settings->m_executable || m_arguments != settings->m_arguments; } diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h index 3dc2602d524..182965ab882 100644 --- a/src/plugins/languageclient/languageclientinterface.h +++ b/src/plugins/languageclient/languageclientinterface.h @@ -74,7 +74,7 @@ public: StdIOClientInterface &operator=(const StdIOClientInterface &) = delete; StdIOClientInterface &operator=(StdIOClientInterface &&) = delete; - bool needsRestart(const StdIOSettings *settings); + bool needsRestart(const StdIOSettings *settings) const; bool start() override; diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index b097f25a378..1cd162dc51a 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -125,7 +125,10 @@ void LanguageClientManager::deleteClient(Client *client) QTC_ASSERT(client, return); client->disconnect(); managerInstance->m_clients.removeAll(client); - client->deleteLater(); + if (managerInstance->m_shuttingDown) + delete client; + else + client->deleteLater(); } void LanguageClientManager::shutdown() @@ -210,6 +213,18 @@ void LanguageClientManager::editorOpened(Core::IEditor *editor) (const QTextCursor &cursor){ findUsages(filePath, cursor); }); + connect(widget, &TextEditorWidget::cursorPositionChanged, this, [this, widget](){ + // TODO This would better be a compressing timer + QTimer::singleShot(50, this, + [this, widget = QPointer<TextEditorWidget>(widget)]() { + if (widget) { + for (Client *client : this->reachableClients()) { + if (client->isSupportedDocument(widget->textDocument())) + client->cursorPositionChanged(widget); + } + } + }); + }); } } } diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 276fac15fc9..a337a72b7b6 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -491,7 +491,9 @@ bool StdIOSettings::needsRestart() const { if (BaseSettings::needsRestart()) return true; - if (auto stdIOInterface = qobject_cast<StdIOClientInterface *>(m_client)) + if (m_client.isNull()) + return false; + if (auto stdIOInterface = qobject_cast<const StdIOClientInterface *>(m_client->clientInterface())) return stdIOInterface->needsRestart(this); return false; } diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp index f81be2427cc..7a28c5e3a95 100644 --- a/src/plugins/languageclient/languageclientutils.cpp +++ b/src/plugins/languageclient/languageclientutils.cpp @@ -133,12 +133,10 @@ void updateCodeActionRefactoringMarker(Client *client, TextDocument* doc = TextDocument::textDocumentForFileName(uri.toFileName()); if (!doc) return; - BaseTextEditor *editor = BaseTextEditor::textEditorForDocument(doc); - if (!editor) + const QVector<BaseTextEditor *> editors = BaseTextEditor::textEditorsForDocument(doc); + if (editors.isEmpty()) return; - TextEditorWidget *editorWidget = editor->editorWidget(); - const QList<Diagnostic> &diagnostics = action.diagnostics().value_or(QList<Diagnostic>()); RefactorMarkers markers; @@ -181,7 +179,10 @@ void updateCodeActionRefactoringMarker(Client *client, marker.cursor = endOfLineCursor(diagnostic.range().start().toTextCursor(doc->document())); markers << marker; } - editorWidget->setRefactorMarkers(markers + editorWidget->refactorMarkers()); + for (BaseTextEditor *editor : editors) { + if (TextEditorWidget *editorWidget = editor->editorWidget()) + editorWidget->setRefactorMarkers(markers + editorWidget->refactorMarkers()); + } } } // namespace LanguageClient diff --git a/src/plugins/nim/settings/nimtoolssettingswidget.ui b/src/plugins/nim/settings/nimtoolssettingswidget.ui index 7322c6a8eca..28c0d1ac31d 100644 --- a/src/plugins/nim/settings/nimtoolssettingswidget.ui +++ b/src/plugins/nim/settings/nimtoolssettingswidget.ui @@ -10,9 +10,6 @@ <height>300</height> </rect> </property> - <property name="windowTitle"> - <string>Form</string> - </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> <widget class="QGroupBox" name="groupBox"> diff --git a/src/plugins/perfprofiler/perftracepointdialog.cpp b/src/plugins/perfprofiler/perftracepointdialog.cpp index 07a2c905519..5dc638bbbef 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.cpp +++ b/src/plugins/perfprofiler/perftracepointdialog.cpp @@ -96,7 +96,7 @@ PerfTracePointDialog::~PerfTracePointDialog() void PerfTracePointDialog::runScript() { - m_ui->label->setText(tr("Executing script ...")); + m_ui->label->setText(tr("Executing script...")); m_ui->textEdit->setReadOnly(true); m_ui->privilegesChooser->setEnabled(false); m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); diff --git a/src/plugins/perfprofiler/perftracepointdialog.h b/src/plugins/perfprofiler/perftracepointdialog.h index 83e2199dad7..ca5d4e2ae52 100644 --- a/src/plugins/perfprofiler/perftracepointdialog.h +++ b/src/plugins/perfprofiler/perftracepointdialog.h @@ -41,6 +41,8 @@ namespace Ui { class PerfTracePointDialog; } class PerfTracePointDialog : public QDialog { + Q_OBJECT + public: PerfTracePointDialog(); ~PerfTracePointDialog(); diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 7b7b88967d0..437f9d58ee4 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -59,7 +59,6 @@ SUBDIRS = \ languageclient \ cppcheck \ compilationdatabaseprojectmanager \ - perfprofiler \ qmlpreview \ studiowelcome @@ -70,9 +69,9 @@ qtHaveModule(serialport) { } qtHaveModule(quick) { - SUBDIRS += qmlprofiler + SUBDIRS += qmlprofiler perfprofiler } else { - warning("QmlProfiler plugin has been disabled since the Qt Quick module is not available.") + warning("QmlProfiler and PerfProfiler plugins have been disabled since the Qt Quick module is not available.") } qtHaveModule(help) { diff --git a/src/plugins/projectexplorer/devicesupport/desktopdevicefactory.h b/src/plugins/projectexplorer/devicesupport/desktopdevicefactory.h index 1ef030e67f6..e69e2842509 100644 --- a/src/plugins/projectexplorer/devicesupport/desktopdevicefactory.h +++ b/src/plugins/projectexplorer/devicesupport/desktopdevicefactory.h @@ -32,6 +32,8 @@ namespace Internal { class DesktopDeviceFactory : public IDeviceFactory { + Q_OBJECT + public: DesktopDeviceFactory(); }; diff --git a/src/plugins/projectexplorer/kit.cpp b/src/plugins/projectexplorer/kit.cpp index e92e66c5b20..cc0ca3a39fd 100644 --- a/src/plugins/projectexplorer/kit.cpp +++ b/src/plugins/projectexplorer/kit.cpp @@ -275,11 +275,9 @@ void Kit::fix() void Kit::setup() { KitGuard g(this); - // Process the KitAspects in reverse order: They may only be based on other information - // lower in the stack. const QList<KitAspect *> aspects = KitManager::kitAspects(); - for (int i = aspects.count() - 1; i >= 0; --i) - aspects.at(i)->setup(this); + for (KitAspect * const aspect : aspects) + aspect->setup(this); } void Kit::upgrade() diff --git a/src/plugins/projectexplorer/kitmanager.cpp b/src/plugins/projectexplorer/kitmanager.cpp index 3d40aef1ef8..07aa313fcca 100644 --- a/src/plugins/projectexplorer/kitmanager.cpp +++ b/src/plugins/projectexplorer/kitmanager.cpp @@ -71,10 +71,12 @@ class KitManagerPrivate public: Kit *m_defaultKit = nullptr; bool m_initialized = false; - std::vector<std::unique_ptr<KitAspect>> m_informationList; std::vector<std::unique_ptr<Kit>> m_kitList; std::unique_ptr<PersistentSettingsWriter> m_writer; QSet<Id> m_irrelevantAspects; + + // Sorted by priority, in descending order. + std::vector<std::unique_ptr<KitAspect>> m_informationList; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 8b8c5711428..9c86a706869 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1027,6 +1027,8 @@ bool static hasFlagEffectOnMacros(const QString &flag) return false; // Skip include paths if (f.startsWith("w", Qt::CaseInsensitive)) return false; // Skip warning options + if (f.startsWith("Y") || (f.startsWith("F") && f != "F")) + return false; // Skip pch-related flags } return true; } diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index ed7c16bc1fe..5d7d296e70d 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -2045,8 +2045,12 @@ void ProjectExplorerPluginPrivate::updateWelcomePage() void ProjectExplorerPluginPrivate::currentModeChanged(Id mode, Id oldMode) { - if (oldMode == Constants::MODE_SESSION) - ICore::saveSettings(); + if (oldMode == Constants::MODE_SESSION) { + // Saving settings directly in a mode change is not a good idea, since the mode change + // can be part of a bigger change. Save settings after that bigger change had a chance to + // complete. + QTimer::singleShot(0, ICore::instance(), &ICore::saveSettings); + } if (mode == Core::Constants::MODE_WELCOME) updateWelcomePage(); } diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp index 68a0431789d..4d71670e7d1 100644 --- a/src/plugins/pythoneditor/pythoneditorplugin.cpp +++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp @@ -363,7 +363,7 @@ static QStringList readLinesJson(const Utils::FileName &projectFile, // This assumes te project file is formed with only one field called // 'files' that has a list associated of the files to include in the project. if (content.isEmpty()) { - *errorMessage = PythonProject::tr("Unable read \"%1\": The file is empty.") + *errorMessage = PythonProject::tr("Unable to read \"%1\": The file is empty.") .arg(projectFile.toUserOutput()); return lines; } @@ -372,7 +372,7 @@ static QStringList readLinesJson(const Utils::FileName &projectFile, const QJsonDocument doc = QJsonDocument::fromJson(content, &error); if (doc.isNull()) { const int line = content.left(error.offset).count('\n') + 1; - *errorMessage = PythonProject::tr("Unable parse %1:%2: %3") + *errorMessage = PythonProject::tr("Unable to parse \"%1\":%2: %3") .arg(projectFile.toUserOutput()).arg(line) .arg(error.errorString()); return lines; diff --git a/src/plugins/qmldesigner/designercore/include/qmltimeline.h b/src/plugins/qmldesigner/designercore/include/qmltimeline.h index a92a2921198..c04fd454c0c 100644 --- a/src/plugins/qmldesigner/designercore/include/qmltimeline.h +++ b/src/plugins/qmldesigner/designercore/include/qmltimeline.h @@ -71,10 +71,10 @@ public: void toogleRecording(bool b) const; void resetGroupRecording() const; + bool hasKeyframeGroup(const ModelNode &node, const PropertyName &propertyName) const; private: void addKeyframeGroupIfNotExists(const ModelNode &node, const PropertyName &propertyName); - bool hasKeyframeGroup(const ModelNode &node, const PropertyName &propertyName) const; QList<QmlTimelineKeyframeGroup> allKeyframeGroups() const; }; diff --git a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp index 745fdc4d00b..65ed482bfb1 100644 --- a/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp +++ b/src/plugins/qmldesigner/designercore/model/qmlobjectnode.cpp @@ -63,7 +63,7 @@ void QmlObjectNode::setVariantProperty(const PropertyName &name, const QVariant timelineFrames.setValue(value, frame); return; - } else if (modelNode().hasId() && timelineIsActive()) { + } else if (modelNode().hasId() && timelineIsActive() && currentTimeline().hasKeyframeGroup(modelNode(), name)) { QmlTimelineKeyframeGroup timelineFrames(currentTimeline().keyframeGroup(modelNode(), name)); Q_ASSERT(timelineFrames.isValid()); diff --git a/src/plugins/qmldesigner/designersettings.h b/src/plugins/qmldesigner/designersettings.h index 9b73dc1faf8..bdaa22bb4b2 100644 --- a/src/plugins/qmldesigner/designersettings.h +++ b/src/plugins/qmldesigner/designersettings.h @@ -65,7 +65,6 @@ const char REFORMAT_UI_QML_FILES[] = "ReformatUiQmlFiles"; /* These setti const char IGNORE_DEVICE_PIXEL_RATIO[] = "IgnoreDevicePixelRaio"; /* The settings can be used to turn off the feature, if there are serious issues */ const char STANDALONE_MODE[] = "StandAloneMode"; const char ENABLE_TIMELINEVIEW[] = "EnableTimelineView"; -const char ENABLE_TIMELINEVIEW_ENVVAR[] = "QTC_ENABLE_QTQUICKTIMELINE_EDITOR"; } class DesignerSettings : public QHash<QByteArray, QVariant> diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineanimationform.ui b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineanimationform.ui index 8bc3e6c9737..5d13cfa7264 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineanimationform.ui +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineanimationform.ui @@ -10,14 +10,11 @@ <height>176</height> </rect> </property> - <property name="windowTitle"> - <string>Form</string> - </property> <layout class="QGridLayout" name="gridLayout"> <item row="4" column="3"> <widget class="QLabel" name="label_16"> <property name="text"> - <string>Loops</string> + <string>Loops:</string> </property> </widget> </item> @@ -117,7 +114,7 @@ </size> </property> <property name="text"> - <string>Animation ID</string> + <string>Animation ID:</string> </property> </widget> </item> @@ -162,14 +159,14 @@ <item row="5" column="1"> <widget class="QLabel" name="label_23"> <property name="text"> - <string>Finished</string> + <string>Finished:</string> </property> </widget> </item> <item row="4" column="7"> <widget class="QLabel" name="label_17"> <property name="text"> - <string>Ping Pong</string> + <string>Ping pong</string> </property> </widget> </item> @@ -205,7 +202,7 @@ </size> </property> <property name="text"> - <string>Transition to state</string> + <string>Transition to state:</string> </property> </widget> </item> @@ -222,7 +219,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Running in Base State</string> + <string>Running in base state</string> </property> </widget> </item> @@ -257,7 +254,7 @@ <item row="3" column="1"> <widget class="QLabel" name="label_12"> <property name="text"> - <string>Start Frame</string> + <string>Start frame:</string> </property> </widget> </item> @@ -290,14 +287,14 @@ <item row="3" column="7"> <widget class="QLabel" name="label_14"> <property name="text"> - <string>Duration</string> + <string>Duration:</string> </property> </widget> </item> <item row="3" column="3"> <widget class="QLabel" name="label_13"> <property name="text"> - <string>End Frame</string> + <string>End frame:</string> </property> </widget> </item> diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineconstants.h b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineconstants.h index 40e4900442d..6c012beec1e 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineconstants.h +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineconstants.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. @@ -46,13 +46,13 @@ const int priorityTimelineCategory = 110; const char timelineCategoryDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", "Timeline"); const char timelineCopyKeyframesDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", - "Copy All Keyframes from item"); + "Copy All Keyframes"); const char timelinePasteKeyframesDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", - "Paste Keyframes to item"); + "Paste Keyframes"); const char timelineInsertKeyframesDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", - "Insert Keyframes for item"); + "Add Keyframes at Current Frame"); const char timelineDeleteKeyframesDisplayName[] = QT_TRANSLATE_NOOP("QmlDesignerContextMenu", - "Delete All Keyframes for item"); + "Delete All Keyframes"); const char timelineStatusBarFrameNumber[] = QT_TRANSLATE_NOOP("QmlDesignerTimeline", "Frame %1"); diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineform.ui b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineform.ui index 5eea257be19..b8b47e4c701 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineform.ui +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelineform.ui @@ -10,9 +10,6 @@ <height>170</height> </rect> </property> - <property name="windowTitle"> - <string>Form</string> - </property> <layout class="QGridLayout" name="gridLayout"> <item row="1" column="8" colspan="2"> <spacer name="horizontalSpacer_11"> @@ -84,7 +81,7 @@ <item row="4" column="0"> <widget class="QLabel" name="label_8"> <property name="text"> - <string>Expression Binding</string> + <string>Expression binding:</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> @@ -107,7 +104,7 @@ <item row="2" column="4"> <widget class="QLabel" name="label_7"> <property name="text"> - <string>End Frame</string> + <string>End frame:</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> @@ -200,7 +197,7 @@ <bool>false</bool> </property> <property name="text"> - <string>Expression Binding</string> + <string>Expression binding</string> </property> </widget> </item> @@ -214,7 +211,7 @@ <item row="1" column="0"> <widget class="QLabel" name="label_5"> <property name="text"> - <string>Timeline ID</string> + <string>Timeline ID:</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> @@ -243,7 +240,7 @@ <item row="2" column="1"> <widget class="QLabel" name="label_6"> <property name="text"> - <string>Start Frame</string> + <string>Start frame:</string> </property> <property name="alignment"> <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinepropertyitem.h b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinepropertyitem.h index 5cd241b7a33..2b8c00c59bc 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinepropertyitem.h +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinepropertyitem.h @@ -44,6 +44,8 @@ class TimelineToolButton; class TimelineKeyframeItem : public TimelineMovableAbstractItem { + Q_DECLARE_TR_FUNCTIONS(TimelineKeyframeItem) + public: explicit TimelineKeyframeItem(TimelinePropertyItem *parent, const ModelNode &frame); ~TimelineKeyframeItem() override; @@ -84,6 +86,8 @@ private: class TimelinePropertyItem : public TimelineItem { + Q_OBJECT + public: enum { Type = TimelineConstants::timelinePropertyItemUserType }; diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesectionitem.h b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesectionitem.h index 3c34ba39299..26db04f7579 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesectionitem.h +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesectionitem.h @@ -40,6 +40,8 @@ class TimelineSectionItem; class TimelineBarItem : public TimelineMovableAbstractItem { + Q_DECLARE_TR_FUNCTIONS(TimelineBarItem) + enum class Location { Undefined, Center, Left, Right }; public: diff --git a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesettingsmodel.cpp b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesettingsmodel.cpp index 6ce3731222e..47426d97551 100644 --- a/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesettingsmodel.cpp +++ b/src/plugins/qmldesigner/qmldesignerextension/timelineeditor/timelinesettingsmodel.cpp @@ -103,7 +103,7 @@ QWidget *TimelineEditorDelegate::createEditor(QWidget *parent, switch (index.column()) { case TimelineSettingsModel::TimelineRow: { QTC_ASSERT(comboBox, return widget); - comboBox->addItem(tr("None")); + comboBox->addItem(TimelineSettingsModel::tr("None")); for (const auto &timeline : timelineSettingsModel->timelineView()->getTimelines()) { if (!timeline.modelNode().id().isEmpty()) comboBox->addItem(timeline.modelNode().id()); @@ -111,7 +111,7 @@ QWidget *TimelineEditorDelegate::createEditor(QWidget *parent, } break; case TimelineSettingsModel::AnimationRow: { QTC_ASSERT(comboBox, return widget); - comboBox->addItem(tr("None")); + comboBox->addItem(TimelineSettingsModel::tr("None")); for (const auto &animation : timelineSettingsModel->timelineView()->getAnimations(qmlTimeline)) { if (!animation.id().isEmpty()) diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp index 09e10c3b0c1..84b95cccc3b 100644 --- a/src/plugins/qmldesigner/qmldesignerplugin.cpp +++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp @@ -194,8 +194,7 @@ bool QmlDesignerPlugin::delayedInitialize() d->settings.fromSettings(Core::ICore::settings()); d->viewManager.registerViewTakingOwnership(new QmlDesigner::Internal::ConnectionView); - if (DesignerSettings::getValue(DesignerSettingsKey::ENABLE_TIMELINEVIEW).toBool() - || qEnvironmentVariableIsSet(DesignerSettingsKey::ENABLE_TIMELINEVIEW_ENVVAR)) + if (DesignerSettings::getValue(DesignerSettingsKey::ENABLE_TIMELINEVIEW).toBool()) d->viewManager.registerViewTakingOwnership(new QmlDesigner::TimelineView); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::SourceTool); d->viewManager.registerFormEditorToolTakingOwnership(new QmlDesigner::ColorTool); diff --git a/src/plugins/qmldesigner/settingspage.cpp b/src/plugins/qmldesigner/settingspage.cpp index e9bd2d1cb46..a7d6bc2bb09 100644 --- a/src/plugins/qmldesigner/settingspage.cpp +++ b/src/plugins/qmldesigner/settingspage.cpp @@ -159,6 +159,8 @@ DesignerSettings SettingsPageWidget::settings() const m_ui.showPropertyEditorWarningsCheckBox->isChecked()); settings.insert(DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT, m_ui.showWarnExceptionsCheckBox->isChecked()); + settings.insert(DesignerSettingsKey::ENABLE_TIMELINEVIEW, + m_ui.featureTimelineEditorCheckBox->isChecked()); return settings; } @@ -224,9 +226,13 @@ void SettingsPageWidget::setSettings(const DesignerSettings &settings) m_ui.controls2StyleComboBox->setCurrentText(m_ui.styleLineEdit->text()); + m_ui.featureTimelineEditorCheckBox->setChecked(settings.value( + DesignerSettingsKey::ENABLE_TIMELINEVIEW).toBool()); + if (settings.value(DesignerSettingsKey::STANDALONE_MODE).toBool()) { m_ui.emulationGroupBox->hide(); m_ui.debugGroupBox->hide(); + m_ui.featuresGroupBox->hide(); } } @@ -262,7 +268,8 @@ void SettingsPage::apply() << DesignerSettingsKey::PUPPET_KILL_TIMEOUT << DesignerSettingsKey::FORWARD_PUPPET_OUTPUT << DesignerSettingsKey::DEBUG_PUPPET - << DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT; + << DesignerSettingsKey::ENABLE_MODEL_EXCEPTION_OUTPUT + << DesignerSettingsKey::ENABLE_TIMELINEVIEW; foreach (const QByteArray &key, restartNecessaryKeys) { if (currentSettings.value(key) != newSettings.value(key)) { diff --git a/src/plugins/qmldesigner/settingspage.ui b/src/plugins/qmldesigner/settingspage.ui index 567f0f7d80e..7da62b300aa 100644 --- a/src/plugins/qmldesigner/settingspage.ui +++ b/src/plugins/qmldesigner/settingspage.ui @@ -411,6 +411,22 @@ </layout> </item> <item> + <widget class="QGroupBox" name="featuresGroupBox"> + <property name="title"> + <string>Features</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <widget class="QCheckBox" name="featureTimelineEditorCheckBox"> + <property name="text"> + <string>Enable Timeline editor</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> <widget class="QGroupBox" name="debugGroupBox"> <property name="title"> <string>Debugging</string> diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index 1df46d885b7..a6e5850076b 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -287,10 +287,10 @@ void QmlProfilerTool::updateRunActions() d->m_startAction->setToolTip(tr("A QML Profiler analysis is still in progress.")); d->m_stopAction->setEnabled(true); } else { - QString whyNot = tr("Start QML Profiler analysis."); + QString tooltip = tr("Start QML Profiler analysis."); bool canRun = ProjectExplorerPlugin::canRunStartupProject - (ProjectExplorer::Constants::QML_PROFILER_RUN_MODE, &whyNot); - d->m_startAction->setToolTip(whyNot); + (ProjectExplorer::Constants::QML_PROFILER_RUN_MODE, &tooltip); + d->m_startAction->setToolTip(tooltip); d->m_startAction->setEnabled(canRun); d->m_stopAction->setEnabled(false); } diff --git a/src/plugins/qtsupport/qtoutputformatter.cpp b/src/plugins/qtsupport/qtoutputformatter.cpp index 778d3096009..2b7933ea8a5 100644 --- a/src/plugins/qtsupport/qtoutputformatter.cpp +++ b/src/plugins/qtsupport/qtoutputformatter.cpp @@ -31,6 +31,7 @@ #include <utils/algorithm.h> #include <utils/ansiescapecodehandler.h> #include <utils/fileinprojectfinder.h> +#include <utils/hostosinfo.h> #include <utils/theme/theme.h> #include <QPlainTextEdit> @@ -412,16 +413,16 @@ void QtSupportPlugin::testQtOutputFormatter_data() << " Loc: [../TestProject/test.cpp(123)]" << 9 << 37 << "../TestProject/test.cpp(123)" << "../TestProject/test.cpp" << 123 << -1; - - QTest::newRow("Windows failed QTest link") - << "..\\TestProject\\test.cpp(123) : failure location" - << 0 << 28 << "..\\TestProject\\test.cpp(123)" - << "..\\TestProject\\test.cpp" << 123 << -1; - - QTest::newRow("Windows failed QTest link with carriage return") - << "..\\TestProject\\test.cpp(123) : failure location\r" - << 0 << 28 << "..\\TestProject\\test.cpp(123)" - << "..\\TestProject\\test.cpp" << 123 << -1; + if (HostOsInfo::isWindowsHost()) { + QTest::newRow("Windows failed QTest link") + << "..\\TestProject\\test.cpp(123) : failure location" + << 0 << 28 << "..\\TestProject\\test.cpp(123)" + << "../TestProject/test.cpp" << 123 << -1; + QTest::newRow("Windows failed QTest link with carriage return") + << "..\\TestProject\\test.cpp(123) : failure location\r" + << 0 << 28 << "..\\TestProject\\test.cpp(123)" + << "../TestProject/test.cpp" << 123 << -1; + } } void QtSupportPlugin::testQtOutputFormatter() diff --git a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp index 728aea666d4..f525f777d0e 100644 --- a/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp +++ b/src/plugins/remotelinux/abstractremotelinuxdeployservice.cpp @@ -203,7 +203,7 @@ void AbstractRemoteLinuxDeployService::handleDeviceSetupDone(bool success) } else { connect(d->connection, &SshConnection::connected, this, &AbstractRemoteLinuxDeployService::handleConnected); - emit progressMessage(tr("Connecting to device '%1' (%2)") + emit progressMessage(tr("Connecting to device \"%1\" (%2).") .arg(deviceConfiguration()->displayName()) .arg(deviceConfiguration()->sshParameters().host())); if (d->connection->state() == SshConnection::Unconnected) diff --git a/src/plugins/texteditor/basehoverhandler.cpp b/src/plugins/texteditor/basehoverhandler.cpp index 72e35ba5bb3..92dae73d591 100644 --- a/src/plugins/texteditor/basehoverhandler.cpp +++ b/src/plugins/texteditor/basehoverhandler.cpp @@ -145,7 +145,7 @@ void BaseHoverHandler::decorateToolTip() if (Qt::mightBeRichText(toolTip())) setToolTip(toolTip().toHtmlEscaped()); - if (lastHelpItemIdentified().isValid()) { + if (lastHelpItemIdentified().isValid() && !lastHelpItemIdentified().isFuzzyMatch()) { const QString &helpContents = lastHelpItemIdentified().extractContent(false); if (!helpContents.isEmpty()) { m_toolTip = toolTip().toHtmlEscaped(); diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index 4178dc7195f..5b60b885ab4 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -258,6 +258,16 @@ void Highlighter::handleShutdown() delete highlightRepository(); } +static bool isOpeningParenthesis(QChar c) +{ + return c == QLatin1Char('{') || c == QLatin1Char('[') || c == QLatin1Char('('); +} + +static bool isClosingParenthesis(QChar c) +{ + return c == QLatin1Char('}') || c == QLatin1Char(']') || c == QLatin1Char(')'); +} + void Highlighter::highlightBlock(const QString &text) { if (!definition().isValid()) @@ -266,8 +276,21 @@ void Highlighter::highlightBlock(const QString &text) KSyntaxHighlighting::State state = TextDocumentLayout::userData(block)->syntaxState(); state = highlightLine(text, state); block = block.next(); + + Parentheses parentheses; + int pos = 0; + for (const QChar &c : text) { + if (isOpeningParenthesis(c)) + parentheses.push_back(Parenthesis(Parenthesis::Opened, c, pos)); + else if (isClosingParenthesis(c)) + parentheses.push_back(Parenthesis(Parenthesis::Closed, c, pos)); + pos++; + } + TextDocumentLayout::setParentheses(currentBlock(), parentheses); + if (block.isValid()) TextDocumentLayout::userData(block)->setSyntaxState(state); + formatSpaces(text); } void Highlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) diff --git a/src/plugins/texteditor/highlightersettingspage.ui b/src/plugins/texteditor/highlightersettingspage.ui index fcd4919b128..7b1e6c9e0da 100644 --- a/src/plugins/texteditor/highlightersettingspage.ui +++ b/src/plugins/texteditor/highlightersettingspage.ui @@ -127,6 +127,9 @@ </item> <item> <widget class="QPushButton" name="resetCache"> + <property name="toolTip"> + <string>Reset definitions remembered for files that can be associated with more than one highlighter definition.</string> + </property> <property name="text"> <string>Reset Remembered Definitions</string> </property> diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 36abfc01e29..3c4c8cd516e 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -8508,13 +8508,14 @@ BaseTextEditor *BaseTextEditor::currentTextEditor() return qobject_cast<BaseTextEditor *>(EditorManager::currentEditor()); } -BaseTextEditor *BaseTextEditor::textEditorForDocument(TextDocument *textDocument) +QVector<BaseTextEditor *> BaseTextEditor::textEditorsForDocument(TextDocument *textDocument) { + QVector<BaseTextEditor *> ret; for (IEditor *editor : Core::DocumentModel::editorsForDocument(textDocument)) { if (auto textEditor = qobject_cast<BaseTextEditor *>(editor)) - return textEditor; + ret << textEditor; } - return nullptr; + return ret; } TextEditorWidget *BaseTextEditor::editorWidget() const diff --git a/src/plugins/texteditor/texteditor.h b/src/plugins/texteditor/texteditor.h index d792a1b12c7..3d079bf77f3 100644 --- a/src/plugins/texteditor/texteditor.h +++ b/src/plugins/texteditor/texteditor.h @@ -111,7 +111,7 @@ public: virtual void finalizeInitialization() {} static BaseTextEditor *currentTextEditor(); - static BaseTextEditor *textEditorForDocument(TextDocument *textDocument); + static QVector<BaseTextEditor *> textEditorsForDocument(TextDocument *textDocument); TextEditorWidget *editorWidget() const; TextDocument *textDocument() const; diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index d5bf895ee47..29231afb82c 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -2,7 +2,19 @@ DEFINES += TEXTEDITOR_LIBRARY QT += gui-private network printsupport xml CONFIG += exceptions CONFIG += include_source_dir # For the highlighter autotest. + +include(../../shared/syntax/syntax_shared.pri) +isEmpty(KSYNTAXHIGHLIGHTING_LIB_DIR) | isEmpty(KSYNTAXHIGHLIGHTING_INCLUDE_DIR) { + QTC_LIB_DEPENDS += syntax-highlighting +} else { + unix:!disable_external_rpath { + !macos: QMAKE_LFLAGS += -Wl,-z,origin + QMAKE_LFLAGS += -Wl,-rpath,$$shell_quote($${KSYNTAXHIGHLIGHTING_LIB_DIR}) + } +} + include(../../qtcreatorplugin.pri) + SOURCES += texteditorplugin.cpp \ plaintexteditorfactory.cpp \ textdocument.cpp \ diff --git a/src/plugins/texteditor/texteditor_dependencies.pri b/src/plugins/texteditor/texteditor_dependencies.pri index c624e4cfb23..85ccb3ded7f 100644 --- a/src/plugins/texteditor/texteditor_dependencies.pri +++ b/src/plugins/texteditor/texteditor_dependencies.pri @@ -2,7 +2,21 @@ QTC_PLUGIN_NAME = TextEditor QTC_LIB_DEPENDS += \ aggregation \ extensionsystem \ - utils \ - syntax-highlighting + utils + QTC_PLUGIN_DEPENDS += \ coreplugin + +# needed for plugins that depend on TextEditor plugin +include(../../shared/syntax/syntax_shared.pri) +!isEmpty(KSYNTAXHIGHLIGHTING_LIB_DIR):!isEmpty(KSYNTAXHIGHLIGHTING_INCLUDE_DIR) { + INCLUDEPATH *= $${KSYNTAXHIGHLIGHTING_INCLUDE_DIR} + LIBS *= -L$$KSYNTAXHIGHLIGHTING_LIB_DIR -lKF5SyntaxHighlighting + + linux { + QMAKE_LFLAGS += -Wl,-z,origin + QMAKE_LFLAGS += -Wl,-rpath,$$shell_quote($${KSYNTAXHIGHLIGHTING_LIB_DIR}) + } +} else { + QTC_LIB_DEPENDS += syntax-highlighting +} |