Merge remote-tracking branch 'origin/4.9'

Change-Id: I0d3db14e583628b906d7fdeb800e004d98e52632
This commit is contained in:
Eike Ziller
2019-02-20 10:05:07 +01:00
36 changed files with 854 additions and 410 deletions

View File

@@ -833,6 +833,14 @@ class DumperBase:
self.putType('int')
self.putNumChild(0)
def putEnumItem(self, name, ival, typish):
buf = bytearray(struct.pack('i', ival))
val = self.Value(self)
val.ldata = bytes(buf)
val.type = self.createType(typish)
with SubItem(self, name):
self.putItem(val)
def putBoolItem(self, name, value):
with SubItem(self, name):
self.putValue(value)
@@ -855,8 +863,7 @@ class DumperBase:
self.putField('keyencoded', key.encoding)
self.putValue(value.value, value.encoding)
def putEnumValue(self, value, vals):
ival = value.integer()
def putEnumValue(self, ival, vals):
nice = vals.get(ival, None)
display = ('%d' % ival) if nice is None else ('%s (%d)' % (nice, ival))
self.putValue(display)

View File

@@ -194,7 +194,7 @@ def qdump_X_QModelIndex(d, value):
#gdb.execute('call free($mi)')
def qdump__Qt__ItemDataRole(d, value):
d.putEnumValue(value, {
d.putEnumValue(value.integer(), {
0 : "Qt::DisplayRole",
1 : "Qt::DecorationRole",
2 : "Qt::EditRole",
@@ -1412,6 +1412,31 @@ def qdump__QSizeF(d, value):
d.putPlainChildren(value)
def qdump__QSizePolicy__Policy(d, value):
d.putEnumValue(value.integer(), {
0 : 'QSizePolicy::Fixed',
1 : 'QSizePolicy::GrowFlag',
2 : 'QSizePolicy::ExpandFlag',
3 : 'QSizePolicy::MinimumExpanding (GrowFlag|ExpandFlag)',
4 : 'QSizePolicy::ShrinkFlag',
5 : 'QSizePolicy::Preferred (GrowFlag|ShrinkFlag)',
7 : 'QSizePolicy::Expanding (GrowFlag|ShrinkFlag|ExpandFlag)',
8 : 'QSizePolicy::IgnoreFlag',
13 : 'QSizePolicy::Ignored (ShrinkFlag|GrowFlag|IgnoreFlag)',
})
def qdump__QSizePolicy(d, value):
bits = value.integer()
d.putEmptyValue(-99)
d.putNumChild(1)
if d.isExpanded():
with Children(d):
d.putIntItem('horStretch', (bits >> 0) & 0xff)
d.putIntItem('verStretch', (bits >> 8) & 0xff)
d.putEnumItem('horPolicy', (bits >> 16) & 0xf, "@QSizePolicy::Policy")
d.putEnumItem('verPolicy', (bits >> 20) & 0xf, "@QSizePolicy::Policy")
def qform__QStack():
return arrayForms()

View File

@@ -165,5 +165,27 @@ int utf8NthLineOffset(const QTextDocument *textDocument, const QByteArray &buffe
return utf8Offset;
}
LineColumn utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset)
{
Utils::LineColumn lineColumn;
lineColumn.line = static_cast<int>(
std::count(utf8Buffer.begin(), utf8Buffer.begin() + utf8Offset, '\n'))
+ 1;
const int startOfLineOffset = utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
lineColumn.column = QString::fromUtf8(
utf8Buffer.mid(startOfLineOffset, utf8Offset - startOfLineOffset))
.length()
+ 1;
return lineColumn;
}
QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer, int currentUtf8Offset)
{
const int lineStartUtf8Offset = utf8Buffer.lastIndexOf('\n', currentUtf8Offset - 1) + 1;
const int lineEndUtf8Offset = utf8Buffer.indexOf('\n', currentUtf8Offset);
return QString::fromUtf8(
utf8Buffer.mid(lineStartUtf8Offset, lineEndUtf8Offset - lineStartUtf8Offset));
}
} // Text
} // Utils

View File

@@ -59,5 +59,9 @@ QTCREATOR_UTILS_EXPORT int utf8NthLineOffset(const QTextDocument *textDocument,
const QByteArray &buffer,
int line);
QTCREATOR_UTILS_EXPORT LineColumn utf16LineColumn(const QByteArray &utf8Buffer, int utf8Offset);
QTCREATOR_UTILS_EXPORT QString utf16LineTextInUtf8Buffer(const QByteArray &utf8Buffer,
int currentUtf8Offset);
} // Text
} // Utils

View File

@@ -48,6 +48,7 @@
#include <QRegularExpression>
namespace ClangCodeModel {
namespace Internal {
ClangCurrentDocumentFilter::ClangCurrentDocumentFilter()
{
@@ -108,7 +109,6 @@ QList<Core::LocatorFilterEntry> ClangCurrentDocumentFilter::matchesFor(
if (!regexp.isValid())
return goodEntries;
using Internal::ClangEditorDocumentProcessor;
ClangEditorDocumentProcessor *processor = ClangEditorDocumentProcessor::get(m_currentPath);
if (!processor)
return goodEntries;
@@ -170,4 +170,5 @@ void ClangCurrentDocumentFilter::onCurrentEditorChanged(Core::IEditor *newCurren
reset();
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -30,6 +30,7 @@
namespace Core { class IEditor; }
namespace ClangCodeModel {
namespace Internal {
class ClangCurrentDocumentFilter : public Core::ILocatorFilter
{
@@ -53,4 +54,5 @@ private:
QString m_currentPath;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -46,6 +46,7 @@
using namespace ClangCodeModel;
using Internal::ClangDiagnosticWidget;
using Internal::ClangFixItOperation;
namespace {
@@ -87,7 +88,7 @@ void openEditorAt(const ClangBackEnd::DiagnosticContainer &diagnostic)
void applyFixit(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
ClangCodeModel::ClangFixItOperation operation(Utf8String(), diagnostic.fixIts);
ClangFixItOperation operation(Utf8String(), diagnostic.fixIts);
operation.perform();
}

View File

@@ -26,6 +26,7 @@
#include "clangeditordocumentparser.h"
namespace ClangCodeModel {
namespace Internal {
ClangEditorDocumentParser::ClangEditorDocumentParser(const QString &filePath)
: BaseEditorDocumentParser(filePath)
@@ -46,4 +47,5 @@ void ClangEditorDocumentParser::updateImpl(const QFutureInterface<void> &,
setState(state_);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -28,6 +28,7 @@
#include <cpptools/baseeditordocumentparser.h>
namespace ClangCodeModel {
namespace Internal {
class ClangEditorDocumentParser : public CppTools::BaseEditorDocumentParser
{
@@ -41,4 +42,5 @@ private:
const UpdateParams &updateParams) override;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -32,6 +32,7 @@
#include <QTextDocument>
namespace ClangCodeModel {
namespace Internal {
using FileToFixits = QMap<QString, QVector<ClangBackEnd::FixItContainer>>;
using FileToFixitsIterator = QMapIterator<QString, QVector<ClangBackEnd::FixItContainer>>;
@@ -50,7 +51,7 @@ int ClangFixItOperation::priority() const
return 10;
}
QString ClangCodeModel::ClangFixItOperation::description() const
QString ClangFixItOperation::description() const
{
return QStringLiteral("Apply Fix: ") + fixItText.toString();
}
@@ -120,5 +121,5 @@ Utils::ChangeSet ClangFixItOperation::toChangeSet(
return changeSet;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -34,13 +34,13 @@
#include <QVector>
#include <QSharedPointer>
namespace TextEditor
{
namespace TextEditor {
class RefactoringChanges;
class RefactoringFile;
}
namespace ClangCodeModel {
namespace Internal {
class ClangFixItOperation : public TextEditor::QuickFixOperation
{
@@ -67,4 +67,5 @@ private:
QVector<ClangBackEnd::FixItContainer> fixItContainers;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -72,6 +72,7 @@ bool hasFixItAt(const QVector<ClangBackEnd::FixItContainer> &fixits,
} // anonymous namespace
namespace ClangCodeModel {
namespace Internal {
ClangFixItOperationsExtractor::ClangFixItOperationsExtractor(
const QVector<DiagnosticContainer> &diagnosticContainers)
@@ -114,4 +115,5 @@ void ClangFixItOperationsExtractor::extractFromDiagnostic(
}
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -30,6 +30,7 @@
#include <clangsupport/diagnosticcontainer.h>
namespace ClangCodeModel {
namespace Internal {
class ClangFixItOperationsExtractor
{
@@ -50,4 +51,5 @@ private:
TextEditor::QuickFixOperations operations;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -144,6 +144,7 @@ TextEditor::HighlightingResult toHighlightingResult(
} // anonymous
namespace ClangCodeModel {
namespace Internal {
HighlightingResultReporter::HighlightingResultReporter(
const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos)
@@ -219,4 +220,5 @@ QFuture<TextEditor::HighlightingResult> HighlightingResultReporter::start()
return future;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -35,6 +35,7 @@
#include <clangsupport/tokeninfocontainer.h>
namespace ClangCodeModel {
namespace Internal {
class HighlightingResultReporter:
public QObject,
@@ -67,4 +68,5 @@ private:
unsigned m_flushLine = 0;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -30,6 +30,7 @@
#include <cplusplus/Token.h>
namespace ClangCodeModel {
namespace Internal {
bool ClangPreprocessorAssistProposalItem::prematurelyApplies(const QChar &typedCharacter) const
{
@@ -156,4 +157,5 @@ bool ClangPreprocessorAssistProposalItem::isInclude() const
|| m_completionOperator == CPlusPlus::T_ANGLE_STRING_LITERAL;
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -31,6 +31,7 @@
#include <QString>
namespace ClangCodeModel {
namespace Internal {
class ClangPreprocessorAssistProposalItem final : public TextEditor::AssistProposalItemInterface
{
@@ -68,4 +69,5 @@ private:
mutable QChar m_typedCharacter;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -30,13 +30,14 @@
#include <utils/qtcassert.h>
namespace ClangCodeModel {
namespace Internal {
void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data,
CppTools::ProjectPart *,
RenameCallback &&renameSymbolsCallback)
{
Internal::ClangEditorDocumentProcessor *processor = Internal::ClangEditorDocumentProcessor::get(
data.filePath().toString());
ClangEditorDocumentProcessor *processor = ClangEditorDocumentProcessor::get(
data.filePath().toString());
const int startRevision = data.cursor().document()->revision();
using ClangBackEnd::SourceLocationsContainer;
@@ -79,4 +80,5 @@ void RefactoringEngine::startLocalRenaming(const CppTools::CursorInEditor &data,
m_watcher->setFuture(cursorFuture);
}
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -36,6 +36,7 @@ class RefactoringServerInterface;
}
namespace ClangCodeModel {
namespace Internal {
class RefactoringEngine : public CppTools::RefactoringEngineInterface
{
@@ -60,4 +61,5 @@ private:
std::unique_ptr<FutureCursorWatcher> m_watcher;
};
} // namespace Internal
} // namespace ClangRefactoring

View File

@@ -48,9 +48,12 @@
#include <QLayout>
#include <QString>
using namespace CppTools;
using namespace ClangCodeModel::Internal;
using namespace Utils;
namespace ClangCodeModel {
namespace Internal {
namespace {
@@ -75,9 +78,6 @@ static Core::Id categoryForSeverity(ClangBackEnd::DiagnosticSeverity severity)
ProjectExplorer::Project *projectForCurrentEditor()
{
using namespace CppTools;
using namespace ClangCodeModel::Internal;
const QString filePath = Utils::currentCppEditorDocumentFilePath();
if (filePath.isEmpty())
return nullptr;
@@ -90,35 +90,80 @@ ProjectExplorer::Project *projectForCurrentEditor()
return nullptr;
}
void disableDiagnosticInConfig(CppTools::ClangDiagnosticConfig &config,
enum class DiagnosticType { Clang, Tidy, Clazy };
DiagnosticType diagnosticType(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
if (!diagnostic.disableOption.isEmpty())
return DiagnosticType::Clang;
const Utils::DiagnosticTextInfo textInfo(diagnostic.text);
if (Utils::DiagnosticTextInfo::isClazyOption(textInfo.option()))
return DiagnosticType::Clazy;
return DiagnosticType::Tidy;
}
void disableDiagnosticInConfig(ClangDiagnosticConfig &config,
const ClangBackEnd::DiagnosticContainer &diagnostic)
{
// Clang check
if (!diagnostic.disableOption.isEmpty()) {
config.setClangOptions(config.clangOptions() + QStringList(diagnostic.disableOption));
return;
}
// Clazy check
using namespace ClangCodeModel::Utils;
DiagnosticTextInfo textInfo(diagnostic.text);
if (DiagnosticTextInfo::isClazyOption(textInfo.option())) {
switch (diagnosticType(diagnostic)) {
case DiagnosticType::Clang:
config.setClangOptions(config.clangOptions() + QStringList(diagnostic.disableOption));
break;
case DiagnosticType::Tidy:
config.setClangTidyChecks(config.clangTidyChecks() + QString(",-")
+ DiagnosticTextInfo(diagnostic.text).option());
break;
case DiagnosticType::Clazy: {
const DiagnosticTextInfo textInfo(diagnostic.text);
const QString checkName = DiagnosticTextInfo::clazyCheckName(textInfo.option());
QStringList newChecks = config.clazyChecks().split(',');
newChecks.removeOne(checkName);
config.setClazyChecks(newChecks.join(','));
return;
break;
}
}
}
// Tidy check
config.setClangTidyChecks(config.clangTidyChecks() + QString(",-") + textInfo.option());
ClangDiagnosticConfig diagnosticConfig(ClangProjectSettings &projectSettings,
CppCodeModelSettings &globalSettings)
{
ProjectExplorer::Project *project = projectForCurrentEditor();
QTC_ASSERT(project, return {});
// Get config id
Core::Id currentConfigId = projectSettings.warningConfigId();
if (projectSettings.useGlobalConfig())
currentConfigId = globalSettings.clangDiagnosticConfigId();
// Get config
ClangDiagnosticConfigsModel configsModel(globalSettings.clangCustomDiagnosticConfigs());
QTC_ASSERT(configsModel.hasConfigWithId(currentConfigId), return {});
return configsModel.configWithId(currentConfigId);
}
bool isDiagnosticConfigChangable(ProjectExplorer::Project *project,
const ClangBackEnd::DiagnosticContainer &diagnostic)
{
if (!project)
return false;
ClangProjectSettings &projectSettings = ClangModelManagerSupport::instance()->projectSettings(
project);
const QSharedPointer<CppCodeModelSettings> globalSettings = codeModelSettings();
const ClangDiagnosticConfig config = diagnosticConfig(projectSettings, *globalSettings);
if (config.clangTidyMode() == ClangDiagnosticConfig::TidyMode::File
&& diagnosticType(diagnostic) == DiagnosticType::Tidy) {
return false;
}
return true;
}
void disableDiagnosticInCurrentProjectConfig(const ClangBackEnd::DiagnosticContainer &diagnostic)
{
using namespace CppTools;
using namespace ClangCodeModel::Internal;
ProjectExplorer::Project *project = projectForCurrentEditor();
QTC_ASSERT(project, return );
@@ -127,15 +172,9 @@ void disableDiagnosticInCurrentProjectConfig(const ClangBackEnd::DiagnosticConta
project);
const QSharedPointer<CppCodeModelSettings> globalSettings = codeModelSettings();
// Get config id
Core::Id currentConfigId = projectSettings.warningConfigId();
if (projectSettings.useGlobalConfig())
currentConfigId = globalSettings->clangDiagnosticConfigId();
// Get config
ClangDiagnosticConfig config = diagnosticConfig(projectSettings, *globalSettings);
ClangDiagnosticConfigsModel configsModel(globalSettings->clangCustomDiagnosticConfigs());
QTC_ASSERT(configsModel.hasConfigWithId(currentConfigId), return );
ClangDiagnosticConfig config = configsModel.configWithId(currentConfigId);
// Create copy if needed
if (config.isReadOnly()) {
@@ -197,7 +236,6 @@ ClangTextMark::ClangTextMark(const FileName &fileName,
QAction *action = new QAction();
action->setIcon(QIcon::fromTheme("edit-copy", ::Utils::Icons::COPY.icon()));
QObject::connect(action, &QAction::triggered, [diagnostic]() {
using namespace ClangCodeModel::Internal;
const QString text = ClangDiagnosticWidget::createText({diagnostic},
ClangDiagnosticWidget::InfoBar);
QApplication::clipboard()->setText(text, QClipboard::Clipboard);
@@ -205,7 +243,8 @@ ClangTextMark::ClangTextMark(const FileName &fileName,
actions << action;
// Remove diagnostic warning action
if (projectForCurrentEditor()) {
ProjectExplorer::Project *project = projectForCurrentEditor();
if (project && isDiagnosticConfigChangable(project, diagnostic)) {
action = new QAction();
action->setIcon(::Utils::Icons::BROKEN.icon());
QObject::connect(action, &QAction::triggered, [diagnostic]() {
@@ -228,8 +267,6 @@ void ClangTextMark::updateIcon(bool valid)
bool ClangTextMark::addToolTipContent(QLayout *target) const
{
using Internal::ClangDiagnosticWidget;
QWidget *widget = ClangDiagnosticWidget::createWidget({m_diagnostic},
ClangDiagnosticWidget::ToolTip);
target->addWidget(widget);
@@ -243,5 +280,6 @@ void ClangTextMark::removedFromEditor()
m_removedFromEditorHandler(this);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -33,6 +33,7 @@
#include <functional>
namespace ClangCodeModel {
namespace Internal {
class ClangTextMark : public TextEditor::TextMark
{
@@ -56,4 +57,5 @@ private:
RemovedFromEditorHandler m_removedFromEditorHandler;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -36,10 +36,11 @@
namespace ClangFormat {
static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style,
ReplacementsToKeep replacementsToKeep)
namespace {
void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style,
ReplacementsToKeep replacementsToKeep)
{
style.MaxEmptyLinesToKeep = 2;
style.MaxEmptyLinesToKeep = 100;
style.SortIncludes = false;
style.SortUsingDeclarations = false;
@@ -56,33 +57,31 @@ static void adjustFormatStyleForLineBreak(clang::format::FormatStyle &style,
#endif
}
static llvm::StringRef clearExtraNewline(llvm::StringRef text)
llvm::StringRef clearExtraNewline(llvm::StringRef text)
{
while (text.startswith("\n\n"))
text = text.drop_front();
return text;
}
static clang::tooling::Replacements filteredReplacements(
const QByteArray &buffer,
const clang::tooling::Replacements &replacements,
int utf8Offset,
int utf8Length,
int extraEmptySpaceOffset,
ReplacementsToKeep replacementsToKeep)
clang::tooling::Replacements filteredReplacements(const QByteArray &buffer,
const clang::tooling::Replacements &replacements,
int utf8Offset,
int utf8Length,
ReplacementsToKeep replacementsToKeep)
{
clang::tooling::Replacements filtered;
for (const clang::tooling::Replacement &replacement : replacements) {
int replacementOffset = static_cast<int>(replacement.getOffset());
const bool replacementDoesNotMatchRestriction
= replacementOffset >= utf8Offset + utf8Length
|| (replacementsToKeep == ReplacementsToKeep::OnlyIndent
&& (replacementOffset < utf8Offset - 1 || buffer.at(replacementOffset) != '\n'));
if (replacementDoesNotMatchRestriction)
continue;
if (replacementOffset >= utf8Offset - 1)
replacementOffset += extraEmptySpaceOffset;
// Skip everything after.
if (replacementOffset >= utf8Offset + utf8Length)
return filtered;
const bool isNotIndentOrInRange = replacementOffset < utf8Offset - 1
|| buffer.at(replacementOffset) != '\n';
if (isNotIndentOrInRange && replacementsToKeep == ReplacementsToKeep::OnlyIndent)
continue;
llvm::StringRef text = replacementsToKeep == ReplacementsToKeep::OnlyIndent
? clearExtraNewline(replacement.getReplacementText())
@@ -106,17 +105,10 @@ static clang::tooling::Replacements filteredReplacements(
return filtered;
}
static void trimFirstNonEmptyBlock(const QTextBlock &currentBlock)
void trimRHSWhitespace(const QTextBlock &block)
{
QTextBlock prevBlock = currentBlock.previous();
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty())
prevBlock = prevBlock.previous();
if (prevBlock.text().trimmed().isEmpty())
return;
const QString initialText = prevBlock.text();
if (!initialText.at(initialText.size() - 1).isSpace())
const QString initialText = block.text();
if (!initialText.rbegin()->isSpace())
return;
auto lastNonSpace = std::find_if_not(initialText.rbegin(),
@@ -124,7 +116,7 @@ static void trimFirstNonEmptyBlock(const QTextBlock &currentBlock)
[](const QChar &letter) { return letter.isSpace(); });
const int extraSpaceCount = static_cast<int>(std::distance(initialText.rbegin(), lastNonSpace));
QTextCursor cursor(prevBlock);
QTextCursor cursor(block);
cursor.beginEditBlock();
cursor.movePosition(QTextCursor::Right,
QTextCursor::MoveAnchor,
@@ -134,31 +126,25 @@ static void trimFirstNonEmptyBlock(const QTextBlock &currentBlock)
cursor.endEditBlock();
}
// Returns the total langth of previous lines with pure whitespace.
static int previousEmptyLinesLength(const QTextBlock &currentBlock)
{
int length{0};
QTextBlock prevBlock = currentBlock.previous();
while (prevBlock.position() > 0 && prevBlock.text().trimmed().isEmpty()) {
length += prevBlock.text().length() + 1;
prevBlock = prevBlock.previous();
}
return length;
}
static void modifyToIndentEmptyLines(
QByteArray &buffer, int utf8Offset, const QTextBlock &block, bool secondTry)
// 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)
{
const QString blockText = block.text();
int firstNonWhitespace = Utils::indexOf(blockText,
[](const QChar &ch) { return !ch.isSpace(); });
int utf8Offset = Utils::Text::utf8NthLineOffset(block.document(),
buffer,
block.blockNumber() + 1);
if (firstNonWhitespace > 0)
utf8Offset += firstNonWhitespace;
else
utf8Offset += blockText.length();
const bool closingParenBlock = firstNonWhitespace >= 0
&& blockText.at(firstNonWhitespace) == ')';
int extraLength = 0;
if (firstNonWhitespace < 0 || closingParenBlock) {
//This extra text works for the most cases.
QByteArray dummyText("a;a;");
@@ -174,6 +160,7 @@ static void modifyToIndentEmptyLines(
dummyText = "&& a";
buffer.insert(utf8Offset, dummyText);
extraLength += dummyText.length();
}
if (secondTry) {
@@ -186,52 +173,45 @@ static void modifyToIndentEmptyLines(
// unclosed parentheses.
// TODO: Does it help to add different endings depending on the context?
buffer.insert(nextLinePos, ')');
extraLength += 1;
}
}
return extraLength;
}
static Utils::LineColumn utf16LineColumn(const QTextBlock &block,
int blockOffsetUtf8,
const QByteArray &utf8Buffer,
int utf8Offset)
bool isInsideModifiedLine(const QString &originalLine, const QString &modifiedLine, int column)
{
// If lastIndexOf('\n') returns -1 then we are fine to add 1 and get 0 offset.
const int lineStartUtf8Offset = utf8Offset == 0
? 0
: utf8Buffer.lastIndexOf('\n', utf8Offset - 1) + 1;
int line = block.blockNumber() + 1; // Init with the line corresponding the block.
if (utf8Offset < blockOffsetUtf8) {
line -= static_cast<int>(std::count(utf8Buffer.begin() + lineStartUtf8Offset,
utf8Buffer.begin() + blockOffsetUtf8,
'\n'));
} else {
line += static_cast<int>(std::count(utf8Buffer.begin() + blockOffsetUtf8,
utf8Buffer.begin() + lineStartUtf8Offset,
'\n'));
}
const QByteArray lineText = utf8Buffer.mid(lineStartUtf8Offset,
utf8Offset - lineStartUtf8Offset);
return Utils::LineColumn(line, QString::fromUtf8(lineText).size() + 1);
// Detect the cases when we have inserted extra text into the line to get the indentation.
return originalLine.length() < modifiedLine.length() && column != modifiedLine.length() + 1
&& (column > originalLine.length() || originalLine.trimmed().isEmpty()
|| !modifiedLine.startsWith(originalLine));
}
static TextEditor::Replacements utf16Replacements(const QTextBlock &block,
int blockOffsetUtf8,
const QByteArray &utf8Buffer,
const clang::tooling::Replacements &replacements)
TextEditor::Replacements utf16Replacements(const QTextDocument *doc,
const QByteArray &utf8Buffer,
const clang::tooling::Replacements &replacements)
{
TextEditor::Replacements convertedReplacements;
convertedReplacements.reserve(replacements.size());
for (const clang::tooling::Replacement &replacement : replacements) {
const Utils::LineColumn lineColUtf16 = utf16LineColumn(block,
blockOffsetUtf8,
utf8Buffer,
static_cast<int>(
replacement.getOffset()));
Utils::LineColumn lineColUtf16 = Utils::Text::utf16LineColumn(utf8Buffer,
static_cast<int>(
replacement.getOffset()));
if (!lineColUtf16.isValid())
continue;
const int utf16Offset = Utils::Text::positionInText(block.document(),
const QString lineText = doc->findBlockByNumber(lineColUtf16.line - 1).text();
const QString bufferLineText
= Utils::Text::utf16LineTextInUtf8Buffer(utf8Buffer,
static_cast<int>(replacement.getOffset()));
if (isInsideModifiedLine(lineText, bufferLineText, lineColUtf16.column))
continue;
lineColUtf16.column = std::min(lineColUtf16.column, lineText.length() + 1);
const int utf16Offset = Utils::Text::positionInText(doc,
lineColUtf16.line,
lineColUtf16.column);
const int utf16Length = QString::fromUtf8(
@@ -246,7 +226,7 @@ static TextEditor::Replacements utf16Replacements(const QTextBlock &block,
return convertedReplacements;
}
static void applyReplacements(QTextDocument *doc, const TextEditor::Replacements &replacements)
void applyReplacements(QTextDocument *doc, const TextEditor::Replacements &replacements)
{
if (replacements.empty())
return;
@@ -266,33 +246,240 @@ static void applyReplacements(QTextDocument *doc, const TextEditor::Replacements
}
}
static QString selectedLines(QTextDocument *doc,
const QTextBlock &startBlock,
const QTextBlock &endBlock)
QString selectedLines(QTextDocument *doc, const QTextBlock &startBlock, const QTextBlock &endBlock)
{
QString text = Utils::Text::textAt(QTextCursor(doc),
startBlock.position(),
std::max(0,
endBlock.position() + endBlock.length()
- startBlock.position() - 1));
while (!text.isEmpty() && text.rbegin()->isSpace())
text.chop(1);
return text;
return Utils::Text::textAt(QTextCursor(doc),
startBlock.position(),
std::max(0,
endBlock.position() + endBlock.length()
- startBlock.position() - 1));
}
int indentationForBlock(const TextEditor::Replacements &toReplace,
const QByteArray &buffer,
const QTextBlock &currentBlock)
{
const int utf8Offset = Utils::Text::utf8NthLineOffset(currentBlock.document(),
buffer,
currentBlock.blockNumber() + 1);
auto replacementIt = std::find_if(toReplace.begin(),
toReplace.end(),
[utf8Offset](const TextEditor::Replacement &replacement) {
return replacement.offset == utf8Offset - 1;
});
if (replacementIt == toReplace.end())
return -1;
int afterLineBreak = replacementIt->text.lastIndexOf('\n');
afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1;
return static_cast<int>(replacementIt->text.size() - afterLineBreak);
}
bool doNotIndentInContext(QTextDocument *doc, int pos)
{
const QChar character = doc->characterAt(pos);
const QTextBlock currentBlock = doc->findBlock(pos);
const QString text = currentBlock.text().left(pos - currentBlock.position());
// NOTE: check if "<<" and ">>" always work correctly.
switch (character.toLatin1()) {
default:
break;
case ':':
// Do not indent when it's the first ':' and it's not the 'case' line.
if (text.contains(QLatin1String("case")) || text.contains(QLatin1String("default"))
|| text.contains(QLatin1String("public")) || text.contains(QLatin1String("private"))
|| text.contains(QLatin1String("protected")) || text.contains(QLatin1String("signals"))
|| text.contains(QLatin1String("Q_SIGNALS"))) {
return false;
}
if (pos > 0 && doc->characterAt(pos - 1) != ':')
return true;
break;
}
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 &currentBlock,
const QByteArray &buffer,
int documentRevision)
{
QTextBlock prevBlock = currentBlock.previous();
while ((prevBlock.position() > 0 || prevBlock.length() > 0)
&& prevBlock.revision() != documentRevision) {
// Find the first block with not matching revision.
prevBlock = prevBlock.previous();
}
if (prevBlock.revision() == documentRevision)
prevBlock = prevBlock.next();
return Utils::Text::utf8NthLineOffset(prevBlock.document(), buffer, prevBlock.blockNumber() + 1);
}
} // namespace
ClangFormatBaseIndenter::ClangFormatBaseIndenter(QTextDocument *doc)
: TextEditor::Indenter(doc)
{}
TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks(
const QVector<QTextBlock> &blocks,
const TextEditor::TabSettings & /*tabSettings*/,
int cursorPositionInEditor)
TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer,
const QTextBlock &startBlock,
const QTextBlock &endBlock,
ReplacementsToKeep replacementsToKeep,
const QChar &typedChar,
bool secondTry) const
{
TextEditor::IndentationForBlock ret;
for (QTextBlock block : blocks)
ret.insert(block.blockNumber(), indentFor(block, cursorPositionInEditor));
return ret;
QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return TextEditor::Replacements());
clang::format::FormatStyle style = styleForFile();
QByteArray originalBuffer = buffer;
int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, startBlock.blockNumber() + 1);
QTC_ASSERT(utf8Offset >= 0, return TextEditor::Replacements(););
int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size();
int rangeStart = 0;
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore)
rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision());
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) {
buffer.insert(utf8Offset - 1, " //");
utf8Offset += 3;
}
adjustFormatStyleForLineBreak(style, replacementsToKeep);
if (typedChar == QChar::Null) {
for (int index = startBlock.blockNumber(); index <= endBlock.blockNumber(); ++index) {
utf8Length += forceIndentWithExtraText(buffer,
m_doc->findBlockByNumber(index),
secondTry);
}
}
if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart)
rangeStart = utf8Offset;
unsigned int rangeLength = static_cast<unsigned int>(utf8Offset + utf8Length - rangeStart);
std::vector<clang::tooling::Range> ranges{{static_cast<unsigned int>(rangeStart), rangeLength}};
clang::format::FormattingAttemptStatus status;
clang::tooling::Replacements clangReplacements = reformat(style,
buffer.data(),
ranges,
m_fileName.toString().toStdString(),
&status);
clang::tooling::Replacements filtered;
if (status.FormatComplete) {
filtered = filteredReplacements(buffer,
clangReplacements,
utf8Offset,
utf8Length,
replacementsToKeep);
}
const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent
&& typedChar == QChar::Null && !secondTry;
if (canTryAgain && filtered.empty()) {
return replacements(originalBuffer,
startBlock,
endBlock,
replacementsToKeep,
typedChar,
true);
}
return utf16Replacements(m_doc, buffer, filtered);
}
TextEditor::Replacements ClangFormatBaseIndenter::format(
const TextEditor::RangesInLines &rangesInLines)
{
if (rangesInLines.empty())
return TextEditor::Replacements();
const QByteArray buffer = m_doc->toPlainText().toUtf8();
std::vector<clang::tooling::Range> ranges;
ranges.reserve(rangesInLines.size());
for (auto &range : rangesInLines) {
const int utf8StartOffset = Utils::Text::utf8NthLineOffset(m_doc, buffer, range.startLine);
int utf8RangeLength = m_doc->findBlockByNumber(range.endLine - 1).text().toUtf8().size();
if (range.endLine > range.startLine) {
utf8RangeLength += Utils::Text::utf8NthLineOffset(m_doc, buffer, range.endLine)
- utf8StartOffset;
}
ranges.emplace_back(static_cast<unsigned int>(utf8StartOffset),
static_cast<unsigned int>(utf8RangeLength));
}
clang::format::FormattingAttemptStatus status;
const clang::tooling::Replacements clangReplacements
= reformat(styleForFile(), buffer.data(), ranges, m_fileName.toString().toStdString(), &status);
const TextEditor::Replacements toReplace = utf16Replacements(m_doc, buffer, clangReplacements);
applyReplacements(m_doc, toReplace);
return toReplace;
}
TextEditor::Replacements ClangFormatBaseIndenter::indentsFor(QTextBlock startBlock,
const QTextBlock &endBlock,
const QByteArray &buffer,
const QChar &typedChar,
int cursorPositionInEditor)
{
if (typedChar != QChar::Null && cursorPositionInEditor > 0
&& m_doc->characterAt(cursorPositionInEditor - 1) == typedChar
&& doNotIndentInContext(m_doc, cursorPositionInEditor - 1)) {
return TextEditor::Replacements();
}
startBlock = reverseFindLastEmptyBlock(startBlock);
const int startBlockPosition = startBlock.position();
if (startBlock.position() > 0) {
trimRHSWhitespace(startBlock.previous());
if (cursorPositionInEditor >= 0)
cursorPositionInEditor += startBlock.position() - startBlockPosition;
}
ReplacementsToKeep replacementsToKeep = ReplacementsToKeep::OnlyIndent;
if (formatWhileTyping()
&& (cursorPositionInEditor == -1 || cursorPositionInEditor >= startBlockPosition)
&& (typedChar == QChar::Null || typedChar == ';' || typedChar == '}')) {
// Format before current position only in case the cursor is inside the indented block.
// So if cursor position is less then the block position then the current line is before
// the indented block - don't trigger extra formatting in this case.
// cursorPositionInEditor == -1 means the consition matches automatically.
// Format only before newline or complete statement not to break code.
replacementsToKeep = ReplacementsToKeep::IndentAndBefore;
}
return replacements(buffer,
startBlock,
endBlock,
replacementsToKeep,
typedChar);
}
void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock,
const QTextBlock &endBlock,
const QChar &typedChar,
int cursorPositionInEditor)
{
const QByteArray buffer = m_doc->toPlainText().toUtf8();
applyReplacements(m_doc,
indentsFor(startBlock, endBlock, buffer, typedChar, cursorPositionInEditor));
}
void ClangFormatBaseIndenter::indent(const QTextCursor &cursor,
@@ -324,120 +511,6 @@ void ClangFormatBaseIndenter::reindent(const QTextCursor &cursor,
indent(cursor, QChar::Null, cursorPositionInEditor);
}
TextEditor::Replacements ClangFormatBaseIndenter::format(
const TextEditor::RangesInLines &rangesInLines)
{
if (rangesInLines.empty())
return TextEditor::Replacements();
int utf8Offset = -1;
QTextBlock block;
const QByteArray buffer = m_doc->toPlainText().toUtf8();
std::vector<clang::tooling::Range> ranges;
ranges.reserve(rangesInLines.size());
for (auto &range : rangesInLines) {
const int utf8StartOffset = Utils::Text::utf8NthLineOffset(m_doc, buffer, range.startLine);
const QTextBlock end = m_doc->findBlockByNumber(range.endLine - 1);
int utf8RangeLength = end.text().toUtf8().size();
if (range.endLine > range.startLine) {
utf8RangeLength += Utils::Text::utf8NthLineOffset(m_doc, buffer, range.endLine)
- utf8StartOffset;
}
ranges.emplace_back(static_cast<unsigned int>(utf8StartOffset),
static_cast<unsigned int>(utf8RangeLength));
if (utf8Offset < 0) {
utf8Offset = utf8StartOffset;
block = m_doc->findBlockByNumber(range.startLine - 1);
}
}
clang::format::FormatStyle style = styleForFile();
clang::format::FormattingAttemptStatus status;
const clang::tooling::Replacements clangReplacements
= reformat(style, buffer.data(), ranges, m_fileName.toString().toStdString(), &status);
const TextEditor::Replacements toReplace = utf16Replacements(block,
utf8Offset,
buffer,
clangReplacements);
applyReplacements(m_doc, toReplace);
return toReplace;
}
static bool doNotIndentInContext(QTextDocument *doc, int pos)
{
const QChar character = doc->characterAt(pos);
const QTextBlock currentBlock = doc->findBlock(pos);
const QString text = currentBlock.text().left(pos - currentBlock.position());
// NOTE: check if "<<" and ">>" always work correctly.
switch (character.toLatin1()) {
default:
break;
case ':':
// Do not indent when it's the first ':' and it's not the 'case' line.
if (text.contains(QLatin1String("case")) || text.contains(QLatin1String("default"))
|| text.contains(QLatin1String("public")) || text.contains(QLatin1String("private"))
|| text.contains(QLatin1String("protected")) || text.contains(QLatin1String("signals"))
|| text.contains(QLatin1String("Q_SIGNALS"))) {
return false;
}
if (pos > 0 && doc->characterAt(pos - 1) != ':')
return true;
break;
}
return false;
}
void ClangFormatBaseIndenter::indentBlocks(const QTextBlock &startBlock,
const QTextBlock &endBlock,
const QChar &typedChar,
int cursorPositionInEditor)
{
if (typedChar != QChar::Null && cursorPositionInEditor > 0
&& m_doc->characterAt(cursorPositionInEditor - 1) == typedChar
&& doNotIndentInContext(m_doc, cursorPositionInEditor - 1)) {
return;
}
const int startBlockPosition = startBlock.position();
trimFirstNonEmptyBlock(startBlock);
if (cursorPositionInEditor >= 0)
cursorPositionInEditor += startBlock.position() - startBlockPosition;
ReplacementsToKeep replacementsToKeep = ReplacementsToKeep::OnlyIndent;
if (formatWhileTyping()
&& (cursorPositionInEditor == -1 || cursorPositionInEditor >= startBlockPosition)
&& (typedChar == QChar::Null || typedChar == ';' || typedChar == '}')) {
// Format before current position only in case the cursor is inside the indented block.
// So if cursor position is less then the block position then the current line is before
// the indented block - don't trigger extra formatting in this case.
// cursorPositionInEditor == -1 means the consition matches automatically.
// Format only before newline or complete statement not to break code.
replacementsToKeep = ReplacementsToKeep::IndentAndBefore;
}
const QByteArray buffer = m_doc->toPlainText().toUtf8();
const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc,
buffer,
startBlock.blockNumber() + 1);
QTC_ASSERT(utf8Offset >= 0, return;);
const int utf8Length = selectedLines(m_doc, startBlock, endBlock).toUtf8().size();
applyReplacements(m_doc,
replacements(buffer,
utf8Offset,
utf8Length,
startBlock,
endBlock,
replacementsToKeep,
typedChar));
}
void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block,
const QChar &typedChar,
const TextEditor::TabSettings & /*tabSettings*/,
@@ -446,35 +519,40 @@ void ClangFormatBaseIndenter::indentBlock(const QTextBlock &block,
indentBlocks(block, block, typedChar, cursorPositionInEditor);
}
int ClangFormatBaseIndenter::indentFor(const QTextBlock &block, int /*cursorPositionInEditor*/)
{
trimFirstNonEmptyBlock(block);
const QByteArray buffer = m_doc->toPlainText().toUtf8();
const int utf8Offset = Utils::Text::utf8NthLineOffset(m_doc, buffer, block.blockNumber() + 1);
QTC_ASSERT(utf8Offset >= 0, return 0;);
const TextEditor::Replacements toReplace = replacements(buffer,
utf8Offset,
0,
block,
block,
ReplacementsToKeep::OnlyIndent);
if (toReplace.empty())
return -1;
const TextEditor::Replacement &replacement = toReplace.front();
int afterLineBreak = replacement.text.lastIndexOf('\n');
afterLineBreak = (afterLineBreak < 0) ? 0 : afterLineBreak + 1;
return static_cast<int>(replacement.text.size() - afterLineBreak);
}
int ClangFormatBaseIndenter::indentFor(const QTextBlock &block,
const TextEditor::TabSettings & /*tabSettings*/,
int cursorPositionInEditor)
{
return indentFor(block, cursorPositionInEditor);
const QByteArray buffer = m_doc->toPlainText().toUtf8();
TextEditor::Replacements toReplace = indentsFor(block,
block,
buffer,
QChar::Null,
cursorPositionInEditor);
if (toReplace.empty())
return -1;
return indentationForBlock(toReplace, buffer, block);
}
TextEditor::IndentationForBlock ClangFormatBaseIndenter::indentationForBlocks(
const QVector<QTextBlock> &blocks,
const TextEditor::TabSettings & /*tabSettings*/,
int cursorPositionInEditor)
{
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);
for (const QTextBlock &block : blocks)
ret.insert(block.blockNumber(), indentationForBlock(toReplace, buffer, block));
return ret;
}
bool ClangFormatBaseIndenter::isElectricCharacter(const QChar &ch) const
@@ -527,97 +605,4 @@ clang::format::FormatStyle ClangFormatBaseIndenter::styleForFile() const
return clang::format::getLLVMStyle();
}
static int formattingRangeStart(const QTextBlock &currentBlock,
const QByteArray &buffer,
int documentRevision)
{
QTextBlock prevBlock = currentBlock.previous();
while ((prevBlock.position() > 0 || prevBlock.length() > 0)
&& prevBlock.revision() != documentRevision) {
// Find the first block with not matching revision.
prevBlock = prevBlock.previous();
}
if (prevBlock.revision() == documentRevision)
prevBlock = prevBlock.next();
return Utils::Text::utf8NthLineOffset(prevBlock.document(), buffer, prevBlock.blockNumber() + 1);
}
TextEditor::Replacements ClangFormatBaseIndenter::replacements(QByteArray buffer,
int utf8Offset,
int utf8Length,
const QTextBlock &startBlock,
const QTextBlock &endBlock,
ReplacementsToKeep replacementsToKeep,
const QChar &typedChar,
bool secondTry) const
{
QTC_ASSERT(replacementsToKeep != ReplacementsToKeep::All, return TextEditor::Replacements());
clang::format::FormatStyle style = styleForFile();
int originalOffsetUtf8 = utf8Offset;
int originalLengthUtf8 = utf8Length;
QByteArray originalBuffer = buffer;
int rangeStart = 0;
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore)
rangeStart = formattingRangeStart(startBlock, buffer, lastSaveRevision());
int extraEmptySpaceOffset = previousEmptyLinesLength(startBlock);
utf8Offset -= extraEmptySpaceOffset;
buffer.remove(utf8Offset, extraEmptySpaceOffset);
adjustFormatStyleForLineBreak(style, replacementsToKeep);
if (typedChar == QChar::Null && startBlock == endBlock) {
modifyToIndentEmptyLines(buffer, utf8Offset, startBlock, secondTry);
utf8Length = 0;
}
if (replacementsToKeep == ReplacementsToKeep::IndentAndBefore) {
buffer.insert(utf8Offset - 1, " //");
extraEmptySpaceOffset -= 3;
utf8Offset += 3;
}
if (replacementsToKeep != ReplacementsToKeep::IndentAndBefore || utf8Offset < rangeStart)
rangeStart = utf8Offset;
unsigned int rangeLength = static_cast<unsigned int>(utf8Offset + utf8Length - rangeStart);
std::vector<clang::tooling::Range> ranges{{static_cast<unsigned int>(rangeStart), rangeLength}};
clang::format::FormattingAttemptStatus status;
clang::tooling::Replacements clangReplacements = reformat(style,
buffer.data(),
ranges,
m_fileName.toString().toStdString(),
&status);
clang::tooling::Replacements filtered;
if (status.FormatComplete) {
filtered = filteredReplacements(buffer,
clangReplacements,
utf8Offset,
utf8Length,
extraEmptySpaceOffset,
replacementsToKeep);
}
const bool canTryAgain = replacementsToKeep == ReplacementsToKeep::OnlyIndent
&& typedChar == QChar::Null && !secondTry;
if (canTryAgain && filtered.empty()) {
return replacements(originalBuffer,
originalOffsetUtf8,
originalLengthUtf8,
startBlock,
endBlock,
replacementsToKeep,
typedChar,
true);
}
return utf16Replacements(startBlock, originalOffsetUtf8, originalBuffer, filtered);
}
} // namespace ClangFormat

View File

@@ -79,10 +79,12 @@ private:
const QTextBlock &endBlock,
const QChar &typedChar,
int cursorPositionInEditor);
int indentFor(const QTextBlock &block, int cursorPositionInEditor);
TextEditor::Replacements indentsFor(QTextBlock startBlock,
const QTextBlock &endBlock,
const QByteArray &buffer,
const QChar &typedChar,
int cursorPositionInEditor);
TextEditor::Replacements replacements(QByteArray buffer,
int utf8Offset,
int utf8Length,
const QTextBlock &startBlock,
const QTextBlock &endBlock,
ReplacementsToKeep replacementsToKeep,

View File

@@ -19,6 +19,10 @@ Project {
"QMAKE_LIBRARY",
"QMAKE_BUILTIN_PRFS",
])
Properties {
condition: qbs.targetOS.contains("windows")
cpp.dynamicLibraries: "advapi32"
}
Export {
Depends { name: "ProParser" }
@@ -48,6 +52,8 @@ Project {
"qmakeparser.h",
"qmakevfs.cpp",
"qmakevfs.h",
"registry.cpp",
"registry_p.h",
]
}

View File

@@ -75,7 +75,12 @@ bool IoUtils::isRelativePath(const QString &path)
&& (path.at(2) == QLatin1Char('/') || path.at(2) == QLatin1Char('\\'))) {
return false;
}
// (... unless, of course, they're UNC, which qmake fails on anyway)
// ... unless, of course, they're UNC:
if (path.length() >= 2
&& (path.at(0).unicode() == '\\' || path.at(0).unicode() == '/')
&& path.at(1) == path.at(0)) {
return false;
}
#else
if (path.startsWith(QLatin1Char('/')))
return false;

View File

@@ -15,6 +15,7 @@ HEADERS += \
proitems.h \
prowriter.h \
qmakevfs.h \
registry_p.h \
ioutils.h
SOURCES += \
@@ -26,7 +27,9 @@ SOURCES += \
proitems.cpp \
prowriter.cpp \
qmakevfs.cpp \
registry.cpp \
ioutils.cpp
RESOURCES += proparser.qrc
DEFINES += QMAKE_BUILTIN_PRFS QMAKE_OVERRIDE_PRFS
win32: LIBS *= -ladvapi32

View File

@@ -31,6 +31,10 @@
#include "qmakevfs.h"
#include "ioutils.h"
#ifdef Q_OS_WIN
# include "registry_p.h"
#endif
#include <qbytearray.h>
#include <qdir.h>
#include <qfile.h>
@@ -88,7 +92,7 @@ enum ExpandFunc {
E_UPPER, E_LOWER, E_TITLE, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE,
E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS,
E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH,
E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE, E_GETENV
E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE, E_GETENV, E_READ_REGISTRY,
};
enum TestFunc {
@@ -153,6 +157,7 @@ void QMakeEvaluator::initFunctionStatics()
{ "system_quote", E_SYSTEM_QUOTE },
{ "shell_quote", E_SHELL_QUOTE },
{ "getenv", E_GETENV },
{ "read_registry", E_READ_REGISTRY },
};
statics.expands.reserve((int)(sizeof(expandInits)/sizeof(expandInits[0])));
for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i)
@@ -1265,6 +1270,41 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand(
ret << val;
}
break;
#ifdef Q_OS_WIN
case E_READ_REGISTRY: {
HKEY tree;
const auto par = args.at(0);
if (!par.compare(QLatin1String("HKCU"), Qt::CaseInsensitive)
|| !par.compare(QLatin1String("HKEY_CURRENT_USER"), Qt::CaseInsensitive)) {
tree = HKEY_CURRENT_USER;
} else if (!par.compare(QLatin1String("HKLM"), Qt::CaseInsensitive)
|| !par.compare(QLatin1String("HKEY_LOCAL_MACHINE"), Qt::CaseInsensitive)) {
tree = HKEY_LOCAL_MACHINE;
} else {
evalError(fL1S("read_registry(): invalid or unsupported registry tree %1.")
.arg(par.toQString()));
goto rrfail;
}
int flags = 0;
if (args.count() > 2) {
const auto opt = args.at(2);
if (opt == "32"
|| !opt.compare(QLatin1String("wow64_32key"), Qt::CaseInsensitive)) {
flags = KEY_WOW64_32KEY;
} else if (opt == "64"
|| !opt.compare(QLatin1String("wow64_64key"), Qt::CaseInsensitive)) {
flags = KEY_WOW64_64KEY;
} else {
evalError(fL1S("read_registry(): invalid option %1.")
.arg(opt.toQString()));
goto rrfail;
}
}
ret << ProString(qt_readRegistryKey(tree, args.at(1).toQString(m_tmp1), flags));
}
rrfail:
break;
#endif
default:
evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1)));
break;

View File

@@ -0,0 +1,158 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the qmake application of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore/qstringlist.h>
#include "registry_p.h"
namespace QMakeInternal {
#ifdef Q_OS_WIN32
/*
Returns the path part of a registry key.
e.g.
For a key
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
it returns
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\"
*/
static QString keyPath(const QString &rKey)
{
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
if (idx == -1)
return QString();
return rKey.left(idx + 1);
}
/*
Returns the name part of a registry key.
e.g.
For a key
"Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir"
it returns
"ProductDir"
*/
static QString keyName(const QString &rKey)
{
int idx = rKey.lastIndexOf(QLatin1Char('\\'));
if (idx == -1)
return rKey;
QString res(rKey.mid(idx + 1));
if (res == QLatin1String("Default") || res == QLatin1String("."))
res = QString();
return res;
}
#endif
QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey, unsigned long options)
{
QString result;
#ifdef Q_OS_WIN32
QString rSubkeyName = keyName(rSubkey);
QString rSubkeyPath = keyPath(rSubkey);
HKEY handle = nullptr;
LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0,
KEY_READ | options, &handle);
if (res != ERROR_SUCCESS)
return QString();
// get the size and type of the value
DWORD dataType;
DWORD dataSize;
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, &dataType, nullptr, &dataSize);
if (res != ERROR_SUCCESS) {
RegCloseKey(handle);
return QString();
}
// get the value
QByteArray data(dataSize, 0);
res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, nullptr,
reinterpret_cast<unsigned char*>(data.data()), &dataSize);
if (res != ERROR_SUCCESS) {
RegCloseKey(handle);
return QString();
}
switch (dataType) {
case REG_EXPAND_SZ:
case REG_SZ: {
result = QString::fromWCharArray(((const wchar_t *)data.constData()));
break;
}
case REG_MULTI_SZ: {
QStringList l;
int i = 0;
for (;;) {
QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i);
i += s.length() + 1;
if (s.isEmpty())
break;
l.append(s);
}
result = l.join(QLatin1String(", "));
break;
}
case REG_NONE:
case REG_BINARY: {
result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2);
break;
}
case REG_DWORD_BIG_ENDIAN:
case REG_DWORD: {
Q_ASSERT(data.size() == sizeof(int));
int i;
memcpy((char*)&i, data.constData(), sizeof(int));
result = QString::number(i);
break;
}
default:
qWarning("QSettings: unknown data %u type in windows registry", quint32(dataType));
break;
}
RegCloseKey(handle);
#else
Q_UNUSED(parentHandle);
Q_UNUSED(rSubkey)
Q_UNUSED(options);
#endif
return result;
}
} // namespace QMakeInternal

View File

@@ -0,0 +1,55 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <QtCore/qglobal.h>
#ifdef Q_OS_WIN32
#include <QtCore/qt_windows.h>
#else
typedef void* HKEY;
#endif
#include <QtCore/qstring.h>
namespace QMakeInternal {
/**
* Read a value from the Windows registry.
*
* If the key is not found, or the registry cannot be accessed (for example
* if this code is compiled for a platform other than Windows), a null
* string is returned.
*
* 32-bit code reads from the registry's 32 bit view (Wow6432Node),
* 64 bit code reads from the 64 bit view.
* Pass KEY_WOW64_32KEY to access the 32 bit view regardless of the
* application's architecture, KEY_WOW64_64KEY respectively.
*/
QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey,
unsigned long options = 0);
} // namespace QMakeInternal

View File

@@ -53,6 +53,29 @@ inline OutputIterator set_greedy_intersection(InputIterator1 first1,
return result;
}
template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare>
inline OutputIterator fill_with_second_values(InputIterator1 first1,
InputIterator1 last1,
InputIterator2 first2,
InputIterator2 last2,
OutputIterator result,
Compare comp)
{
while (first1 != last1 && first2 != last2)
if (comp(*first1, *first2)) {
*result = *first1;
++first1;
++result;
} else if (comp(*first2, *first1))
++first2;
else {
*result = *first2;
++first1;
++result;
}
return result;
}
class UsedMacroFilter
{
public:
@@ -160,7 +183,7 @@ private:
const Utils::SmallStringVector &usedMacros)
{
CompilerMacros filtertedCompilerMacros;
filtertedCompilerMacros.reserve(indexedCompilerMacro.size() + usedMacros.size());
filtertedCompilerMacros.reserve(usedMacros.size());
struct Compare
{
@@ -175,24 +198,13 @@ private:
}
};
set_greedy_intersection(indexedCompilerMacro.begin(),
indexedCompilerMacro.end(),
usedMacros.begin(),
fill_with_second_values(usedMacros.begin(),
usedMacros.end(),
indexedCompilerMacro.begin(),
indexedCompilerMacro.end(),
std::back_inserter(filtertedCompilerMacros),
Compare{});
auto split = filtertedCompilerMacros.end();
std::set_difference(usedMacros.begin(),
usedMacros.end(),
filtertedCompilerMacros.begin(),
filtertedCompilerMacros.end(),
std::back_inserter(filtertedCompilerMacros),
Compare{});
std::inplace_merge(filtertedCompilerMacros.begin(), split, filtertedCompilerMacros.end());
return filtertedCompilerMacros;
}

View File

@@ -5457,6 +5457,19 @@ void tst_Dumpers::dumper_data()
+ Check("e.e2", "(E::b2 | E::c2) (3)", "E::Enum2")
+ Check("e.e3", "(E::b3 | E::c3) (3)", "E::Enum3");
QTest::newRow("QSizePolicy")
<< Data("#include <QSizePolicy>\n",
"QSizePolicy qsp1;\n"
"qsp1.setHorizontalStretch(6);\n"
"qsp1.setVerticalStretch(7);\n"
"QSizePolicy qsp2(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);\n")
+ GuiProfile()
+ NoCdbEngine
+ Check("qsp1.horStretch", "6", "int")
+ Check("qsp1.verStretch", "7", "int")
+ Check("qsp2.horPolicy", "QSizePolicy::Preferred (GrowFlag|ShrinkFlag) (5)", "@QSizePolicy::Policy")
+ Check("qsp2.verPolicy", "QSizePolicy::MinimumExpanding (GrowFlag|ExpandFlag) (3)", "@QSizePolicy::Policy");
QTest::newRow("Array")
<< Data("",

View File

@@ -18,7 +18,8 @@ QtcAutotest {
"qmakeevaluator.h", "qmakeevaluator_p.h", "qmakeevaluator.cpp",
"qmakeglobals.h", "qmakeglobals.cpp",
"qmakeparser.h", "qmakeparser.cpp",
"qmakevfs.h", "qmakevfs.cpp"
"qmakevfs.h", "qmakevfs.cpp",
"registry_p.h", "registry.cpp",
]
}
Group {
@@ -27,4 +28,8 @@ QtcAutotest {
}
cpp.includePaths: base.concat([proParserGroup.prefix])
cpp.defines: base.concat("QT_USE_FAST_OPERATOR_PLUS")
Properties {
condition: qbs.targetOS.contains("windows")
cpp.dynamicLibraries: "advapi32"
}
}

View File

@@ -34,7 +34,7 @@
#include <QVector>
using ClangBackEnd::FixItContainer;
using ClangCodeModel::ClangFixItOperation;
using ClangCodeModel::Internal::ClangFixItOperation;
using ::testing::PrintToString;

View File

@@ -331,6 +331,40 @@ TEST_F(ClangFormat, NoExtraIndentAfterBraceInitialization)
"return 0;"));
}
TEST_F(ClangFormat, IndentMultipleEmptyLines)
{
insertLines({"{",
"",
"",
"",
"}"});
indenter.indent(cursor, QChar::Null, TextEditor::TabSettings());
ASSERT_THAT(documentLines(), ElementsAre("{",
" ",
" ",
" ",
"}"));
}
TEST_F(ClangFormat, IndentEmptyLineAndKeepPreviousEmptyLines)
{
insertLines({"{",
" ",
" ",
"",
"}"});
indenter.indentBlock(doc.findBlockByNumber(3), QChar::Null, TextEditor::TabSettings());
ASSERT_THAT(documentLines(), ElementsAre("{",
" ",
" ",
" ",
"}"));
}
TEST_F(ClangFormat, IndentFunctionBodyAndFormatBeforeIt)
{
insertLines({"int foo(int a, int b,",
@@ -469,7 +503,7 @@ TEST_F(ClangFormat, FormatBasicFile)
"int a;",
"}"});
indenter.format(cursor);
indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ElementsAre("int main()",
"{",
@@ -484,7 +518,7 @@ TEST_F(ClangFormat, FormatEmptyLine)
"",
"}"});
indenter.format(cursor);
indenter.format({{1, 4}});
ASSERT_THAT(documentLines(), ElementsAre("int main() {}"));
}
@@ -495,7 +529,7 @@ TEST_F(ClangFormat, FormatLambda)
"",
"});"});
indenter.format(cursor);
indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("int b = foo([]() {",
"",
@@ -508,7 +542,7 @@ TEST_F(ClangFormat, FormatInitializerListInArguments)
"args,",
"{1, 2});"});
indenter.format(cursor);
indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("foo(arg1, args, {1, 2});"));
}
@@ -520,7 +554,7 @@ TEST_F(ClangFormat, FormatFunctionArgumentLambdaWithScope)
"",
"});"});
indenter.format(cursor);
indenter.format({{1, 4}});
ASSERT_THAT(documentLines(),
ElementsAre("foo([]() {",
@@ -535,7 +569,7 @@ TEST_F(ClangFormat, FormatScopeAsFunctionArgument)
"",
"});"});
indenter.format(cursor);
indenter.format({{1, 4}});
ASSERT_THAT(documentLines(),
ElementsAre("foo({",
@@ -548,7 +582,7 @@ TEST_F(ClangFormat, FormatStructuredBinding)
insertLines({"auto [a,",
"b] = c;"});
indenter.format(cursor);
indenter.format({{1, 2}});
ASSERT_THAT(documentLines(), ElementsAre("auto [a, b] = c;"));
}
@@ -558,7 +592,7 @@ TEST_F(ClangFormat, FormatStringLiteralContinuation)
insertLines({"foo(bar, \"foo\"",
"\"bar\");"});
indenter.format(cursor);
indenter.format({{1, 2}});
ASSERT_THAT(documentLines(), ElementsAre("foo(bar,",
" \"foo\"",
@@ -571,7 +605,7 @@ TEST_F(ClangFormat, FormatTemplateparameters)
"B,",
"C>"});
indenter.format(cursor);
indenter.format({{1, 3}});
ASSERT_THAT(documentLines(), ElementsAre("using Alias = Template<A, B, C>"));
}

View File

@@ -43,6 +43,7 @@ using ClangBackEnd::Document;
using ClangBackEnd::Documents;
using ClangBackEnd::UnsavedFiles;
using ClangBackEnd::ChunksReportedMonitor;
using ClangCodeModel::Internal::HighlightingResultReporter;
namespace {
@@ -86,7 +87,7 @@ QVector<TokenInfoContainer> generateTokenInfos(uint count)
TEST_F(HighlightingResultReporter, StartAndFinish)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(noTokenInfos());
auto reporter = new ::HighlightingResultReporter(noTokenInfos());
auto future = reporter->start();
@@ -96,7 +97,7 @@ TEST_F(HighlightingResultReporter, StartAndFinish)
TEST_F(HighlightingResultReporter, ReportNothingIfNothingToReport)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(generateTokenInfos(0));
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(0));
auto future = reporter->start();
@@ -106,7 +107,7 @@ TEST_F(HighlightingResultReporter, ReportNothingIfNothingToReport)
TEST_F(HighlightingResultReporter, ReportSingleResultAsOneChunk)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(generateTokenInfos(1));
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(1));
reporter->setChunkSize(1);
auto future = reporter->start();
@@ -117,7 +118,7 @@ TEST_F(HighlightingResultReporter, ReportSingleResultAsOneChunk)
TEST_F(HighlightingResultReporter, ReportRestIfChunkSizeNotReached)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(generateTokenInfos(1));
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(1));
const int notReachedChunkSize = 100;
reporter->setChunkSize(notReachedChunkSize);
@@ -129,7 +130,7 @@ TEST_F(HighlightingResultReporter, ReportRestIfChunkSizeNotReached)
TEST_F(HighlightingResultReporter, ReportChunksWithoutRest)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(generateTokenInfos(4));
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(4));
reporter->setChunkSize(1);
auto future = reporter->start();
@@ -140,7 +141,7 @@ TEST_F(HighlightingResultReporter, ReportChunksWithoutRest)
TEST_F(HighlightingResultReporter, ReportSingleChunkAndRest)
{
auto reporter = new ClangCodeModel::HighlightingResultReporter(generateTokenInfos(5));
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(5));
reporter->setChunkSize(2);
auto future = reporter->start();
@@ -158,7 +159,7 @@ TEST_F(HighlightingResultReporter, ReportCompleteLines)
TokenInfoContainer(1, 2, 1, types),
TokenInfoContainer(2, 1, 1, types),
};
auto reporter = new ClangCodeModel::HighlightingResultReporter(tokenInfos);
auto reporter = new ::HighlightingResultReporter(tokenInfos);
reporter->setChunkSize(1);
auto future = reporter->start();

View File

@@ -33,6 +33,7 @@ CONFIG(release, debug|release):QMAKE_LFLAGS += -Wl,--strip-debug
}
gcc:!clang: QMAKE_CXXFLAGS += -Wno-noexcept-type
msvc: QMAKE_CXXFLAGS += /bigobj
# create fake CppTools.json for the mime type definitions
dependencyList = "\"Dependencies\" : []"