Commit 7449d982 authored by Kevin Funk's avatar Kevin Funk
Browse files

Merge remote-tracking branch 'origin/5.0'

parents 18c0fb17 1b14c3c5
......@@ -78,8 +78,6 @@ Signature getDeclarationSignature(const Declaration *functionDecl, const DUConte
AdaptSignatureAssistant::AdaptSignatureAssistant(ILanguageSupport* supportedLanguage)
: StaticAssistant(supportedLanguage)
{
connect(DUChain::self(), &DUChain::updateReady,
this, &AdaptSignatureAssistant::updateReady);
}
QString AdaptSignatureAssistant::title() const
......@@ -98,21 +96,21 @@ void AdaptSignatureAssistant::reset()
m_otherSideTopContext = {};
m_otherSideContext = {};
m_oldSignature = {};
m_document = {};
m_document = nullptr;
m_view.clear();
}
void AdaptSignatureAssistant::textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText)
void AdaptSignatureAssistant::textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange, const QString& removedText)
{
reset();
m_view = view;
m_document = doc;
m_lastEditPosition = invocationRange.end();
KTextEditor::Range sigAssistRange = invocationRange;
if (!removedText.isEmpty()) {
sigAssistRange.setRange(sigAssistRange.start(), sigAssistRange.start());
}
m_document = view->document()->url();
DUChainReadLocker lock(DUChain::lock(), 300);
if (!lock.locked()) {
......@@ -120,7 +118,7 @@ void AdaptSignatureAssistant::textChanged(KTextEditor::View* view, const KTextEd
return;
}
KTextEditor::Range simpleInvocationRange = KTextEditor::Range(sigAssistRange);
Declaration* funDecl = getDeclarationAtCursor(simpleInvocationRange.start(), m_document);
Declaration* funDecl = getDeclarationAtCursor(simpleInvocationRange.start(), m_document->url());
if (!funDecl || !funDecl->type<FunctionType>()) {
clangDebug() << "No function at cursor";
return;
......@@ -164,7 +162,7 @@ void AdaptSignatureAssistant::textChanged(KTextEditor::View* view, const KTextEd
bool AdaptSignatureAssistant::isUseful() const
{
return !m_declarationName.isEmpty() && m_otherSideId.isValid();
return !m_declarationName.isEmpty() && m_otherSideId.isValid() && !actions().isEmpty();
}
bool AdaptSignatureAssistant::getSignatureChanges(const Signature& newSignature, QList<int>& oldPositions) const
......@@ -254,18 +252,18 @@ QList<RenameAction*> AdaptSignatureAssistant::getRenameActions(const Signature &
return renameActions;
}
void AdaptSignatureAssistant::updateReady(const KDevelop::IndexedString& document, const KDevelop::ReferencedTopDUContext& /*context*/)
void AdaptSignatureAssistant::updateReady(const KDevelop::IndexedString& document, const KDevelop::ReferencedTopDUContext& top)
{
if (document.toUrl() != m_document || !m_view) {
if (!top || !m_document || document.toUrl() != m_document->url() || top->url() != IndexedString(m_document->url())) {
return;
}
clearActions();
DUChainReadLocker lock;
Declaration *functionDecl = getDeclarationAtCursor(KTextEditor::Cursor(m_view.data()->cursorPosition()), m_document);
Declaration *functionDecl = getDeclarationAtCursor(m_lastEditPosition, m_document->url());
if (!functionDecl || functionDecl->identifier() != m_declarationName) {
clangDebug() << "No function found at" << m_document << m_view.data()->cursorPosition();
clangDebug() << "No function found at" << m_document->url() << m_lastEditPosition;
return;
}
DUContext *functionCtxt = DUChainUtils::getFunctionContext(functionDecl);
......@@ -307,4 +305,16 @@ void AdaptSignatureAssistant::updateReady(const KDevelop::IndexedString& documen
emit actionsChanged();
}
KTextEditor::Range AdaptSignatureAssistant::displayRange() const
{
if (!m_document) {
return {};
}
auto s = m_lastEditPosition;
KTextEditor::Range ran = {s.line(), 0, s.line(), m_document->lineLength(s.line())};
qDebug() << "display range:" << ran;
return ran;
}
#include "moc_adaptsignatureassistant.cpp"
......@@ -42,8 +42,9 @@ public:
AdaptSignatureAssistant(KDevelop::ILanguageSupport* supportedLanguage);
QString title() const override;
void textChanged(KTextEditor::View* view, const KTextEditor::Range& invocationRange, const QString& removedText = QString()) override;
void textChanged(KTextEditor::Document* doc, const KTextEditor::Range& invocationRange, const QString& removedText = QString()) override;
bool isUseful() const override;
KTextEditor::Range displayRange() const override;
private:
///Compare @param newSignature to m_oldSignature and put differences in oldPositions
......@@ -61,13 +62,14 @@ private:
KDevelop::DeclarationId m_otherSideId;
KDevelop::ReferencedTopDUContext m_otherSideTopContext;
KDevelop::DUContextPointer m_otherSideContext;
KTextEditor::Cursor m_lastEditPosition;
//old signature of the _other_side
Signature m_oldSignature;
QUrl m_document;
QPointer<KTextEditor::Document> m_document;
QPointer<KTextEditor::View> m_view;
private slots:
void updateReady(const KDevelop::IndexedString& document, const KDevelop::ReferencedTopDUContext& context);
void updateReady(const KDevelop::IndexedString& document, const KDevelop::ReferencedTopDUContext& context) override;
void reset();
};
......
......@@ -996,9 +996,9 @@ void Visitor::setDeclData(CXCursor cursor, ClassMemberDeclaration *decl) const
const QString offsetStr = bitOffset ? i18nc("%1: bytes, %2: bits", "%1, %2", byteOffsetStr, bitOffsetStr) : byteOffsetStr;
decl->setComment(decl->comment()
+ i18n("<br/>offset in parent: %1<br/>"
"size: %2 Bytes<br/>"
"aligned to: %3 Bytes", offsetStr, sizeOf, alignedTo).toUtf8());
+ i18n("<p>offset in parent: %1; "
"size: %2 Bytes; "
"aligned to: %3 Bytes</p>", offsetStr, sizeOf, alignedTo).toUtf8());
}
}
#endif
......@@ -1036,8 +1036,8 @@ void Visitor::setDeclData(CXCursor cursor, ClassDeclaration* decl) const
auto alignOf = clang_Type_getAlignOf(type);
if (sizeOf >= 0 && alignOf >= 0) {
decl->setComment(decl->comment()
+ i18n("<br/>size: %1 Bytes<br/>"
"aligned to: %2 Bytes", sizeOf, alignOf).toUtf8());
+ i18n("<p>size: %1 Bytes; "
"aligned to: %2 Bytes</p>", sizeOf, alignOf).toUtf8());
}
}
}
......
......@@ -38,68 +38,13 @@
using namespace KDevelop;
namespace {
KTextEditor::View* createDocAndView(const QString& data, KTextEditor::Document** docPtr)
{
if (data.isEmpty()) {
return 0;
}
KTextEditor::Document* doc = ICore::self()->partController()->editorPart()->createDocument(0);
*docPtr = doc;
doc->setText(data);
doc->setMode(QStringLiteral("C++"));
doc->setReadWrite(false);
KTextEditor::View* view = doc->createView(0);
view->setStatusBarEnabled(false);
if (KTextEditor::ConfigInterface* config = qobject_cast<KTextEditor::ConfigInterface*>(view)) {
config->setConfigValue(QStringLiteral("icon-bar"), false);
config->setConfigValue(QStringLiteral("folding-bar"), false);
config->setConfigValue(QStringLiteral("line-numbers"), false);
config->setConfigValue(QStringLiteral("dynamic-word-wrap"), true);
}
return view;
}
}
MacroNavigationContext::MacroNavigationContext(const MacroDefinition::Ptr& macro, const KDevelop::DocumentCursor& expansionLocation)
: m_macro(macro)
, m_preprocessed(nullptr)
, m_definition(nullptr)
, m_widget(new QWidget)
{
QVBoxLayout* layout = new QVBoxLayout(m_widget.data());
if (expansionLocation.isValid()) {
const QString preprocessedBody = retrievePreprocessedBody(expansionLocation);
KTextEditor::View* preprocessedView = createDocAndView(preprocessedBody, &m_preprocessed);
if (m_preprocessed) {
layout->addWidget(new QLabel(i18n("Preprocessed Body:")));
layout->addWidget(preprocessedView);
} else {
layout->addWidget(new QLabel(i18n("Preprocessed Body: (empty)")));
}
}
const QString definitionText = m_macro->definition().str();
KTextEditor::View* definitionView = createDocAndView(definitionText, &m_definition);
if (m_definition) {
layout->addWidget(new QLabel(i18n("Body:")));
layout->addWidget(definitionView);
} else {
layout->addWidget(new QLabel(i18n("Body: (empty)")));
}
m_widget->setLayout(layout);
}
MacroNavigationContext::~MacroNavigationContext()
{
delete m_preprocessed;
delete m_definition;
delete m_widget;
}
QString MacroNavigationContext::name() const
......@@ -107,11 +52,6 @@ QString MacroNavigationContext::name() const
return m_macro->identifier().toString();
}
QWidget* MacroNavigationContext::widget() const
{
return m_widget.data();
}
QString MacroNavigationContext::html(bool shorten)
{
clear();
......@@ -143,6 +83,11 @@ QString MacroNavigationContext::html(bool shorten)
modifyHtml() += QStringLiteral(" "); //The action name _must_ stay "show_uses", since that is also used from outside
makeLink(i18n("Show uses"), QStringLiteral("show_uses"), NavigationAction(m_macro.dynamicCast<Declaration>(), NavigationAction::NavigateUses));
auto code = m_macro->definition().str().replace(QStringLiteral("\n"), QStringLiteral("<br/>"));
modifyHtml() += QLatin1String("<p>") + i18n("Body: ");
modifyHtml() += QLatin1String("<tt>") + code + QLatin1String("</tt>");
modifyHtml() += QLatin1String("</p>");
modifyHtml() += fontSizeSuffix(shorten) + QLatin1String("</p></body></html>");
return currentHtml();
}
......
......@@ -36,7 +36,6 @@ public:
const KDevelop::DocumentCursor& expansionLocation = KDevelop::DocumentCursor::invalid());
~MacroNavigationContext();
virtual QWidget* widget() const override;
virtual QString html(bool shorten) override;
virtual QString name() const override;
......@@ -45,8 +44,6 @@ private:
const MacroDefinition::Ptr m_macro;
QString m_body;
KTextEditor::Document* m_preprocessed;
KTextEditor::Document* m_definition;
QPointer<QWidget> m_widget;
};
......
......@@ -52,7 +52,7 @@
using namespace KDevelop;
namespace {
/* Under some conditions, such as when looking up suggestions
/** Under some conditions, such as when looking up suggestions
* for the undeclared namespace 'std' we will get an awful lot
* of suggestions. This parameter limits how many suggestions
* will pop up, as rarely more than a few will be relevant anyways
......@@ -91,7 +91,8 @@ QStringList scanIncludePaths( const QString& identifier, const QDir& dir, int ma
return {};
}
for (const auto& file : dir.entryList({identifier + QLatin1Char('*')}, QDir::Files)) {
const QStringList nameFilters = {identifier, identifier + QLatin1String(".*")};
for (const auto& file : dir.entryList(nameFilters, QDir::Files)) {
if (identifier.compare(file, Qt::CaseInsensitive) == 0 || ClangHelpers::isHeader(file)) {
const QString filePath = path + QLatin1Char('/') + file;
clangDebug() << "Found candidate file" << filePath;
......@@ -106,7 +107,7 @@ QStringList scanIncludePaths( const QString& identifier, const QDir& dir, int ma
return candidates;
}
/*
/**
* Find files in dir that match the given identifier. Matches common C++ header file extensions only.
*/
QStringList scanIncludePaths( const QualifiedIdentifier& identifier, const KDevelop::Path::List& includes )
......@@ -122,7 +123,7 @@ QStringList scanIncludePaths( const QualifiedIdentifier& identifier, const KDeve
return candidates;
}
/*
/**
* Determine how much path is shared between two includes.
* boost/tr1/unordered_map
* boost/tr1/unordered_set
......@@ -212,7 +213,28 @@ KDevelop::DocumentRange forwardDeclarationPosition(const QualifiedIdentifier& id
return {IndexedString(source.pathOrUrl()), {line, 0, line, 0}};
}
QVector<KDevelop::QualifiedIdentifier> possibleDeclarations( const QualifiedIdentifier& identifier, const KDevelop::Path& file, const KDevelop::CursorInRevision& cursor )
/**
* Iteratively build all levels of the current scope. A (missing) type anywhere
* can be aribtrarily namespaced, so we create the permutations of possible
* nestings of namespaces it can currently be in,
*
* TODO: add detection of namespace aliases, such as 'using namespace KDevelop;'
*
* namespace foo {
* namespace bar {
* function baz() {
* type var;
* }
* }
* }
*
* Would give:
* foo::bar::baz::type
* foo::bar::type
* foo::type
* type
*/
QVector<KDevelop::QualifiedIdentifier> findPossibleQualifiedIdentifiers( const QualifiedIdentifier& identifier, const KDevelop::Path& file, const KDevelop::CursorInRevision& cursor )
{
DUChainReadLocker lock;
const TopDUContext* top = DUChainUtils::standardContextForUrl( file.toUrl() );
......@@ -229,29 +251,6 @@ QVector<KDevelop::QualifiedIdentifier> possibleDeclarations( const QualifiedIden
}
QVector<KDevelop::QualifiedIdentifier> declarations{ identifier };
auto scopes = context->scopeIdentifier();
/*
* Iteratively build all levels of the current scope. A (missing) type anywhere
* can be aribtrarily namespaced, so we create the permutations of possible
* nestings of namespaces it can currently be in,
*
* TODO: add detection of namespace aliases, such as 'using namespace KDevelop;'
*
* namespace foo {
* namespace bar {
* function baz() {
* type var;
* }
* }
* }
*
* Would give:
* foo::bar::baz::type
* foo::bar::type
* foo::type
* type
*/
for( auto scopes = context->scopeIdentifier(); !scopes.isEmpty(); scopes.pop() ) {
declarations.append( scopes + identifier );
}
......@@ -260,71 +259,50 @@ QVector<KDevelop::QualifiedIdentifier> possibleDeclarations( const QualifiedIden
return declarations;
}
QStringList duchainCandidates( const QualifiedIdentifier& identifier, const KDevelop::Path& file, const KDevelop::CursorInRevision& cursor )
QStringList findMatchingIncludeFiles(const QVector<Declaration*> declarations)
{
DUChainReadLocker lock;
/*
* Search the persistent symbol table for the declaration. If it is known from before,
* determine which file it came from and suggest that
*/
QStringList candidates;
for( const auto& declaration : possibleDeclarations( identifier, file, cursor ) ) {
clangDebug() << "Considering candidate declaration" << declaration;
const IndexedDeclaration* declarations;
uint declarationCount;
PersistentSymbolTable::self().declarations( declaration , declarationCount, declarations );
for (const auto decl: declarations) {
// skip declarations that don't belong to us
const auto& file = decl->topContext()->parsingEnvironmentFile();
if (!file || file->language() != ParseSession::languageString()) {
continue;
}
for( uint i = 0; i < declarationCount; ++i ) {
auto* decl = declarations[ i ].declaration();
if( dynamic_cast<KDevelop::AliasDeclaration*>( decl ) ) {
continue;
}
/* Skip if the declaration is invalid or if it is an alias declaration -
* we want the actual declaration (and its file)
*/
if( !decl ) {
continue;
}
if( decl->isForwardDeclaration() ) {
continue;
}
// skip declarations that don't belong to us
const auto& file = decl->topContext()->parsingEnvironmentFile();
if (!file || file->language() != ParseSession::languageString()) {
continue;
}
const auto filepath = decl->url().toUrl().toLocalFile();
if( !isBlacklisted( filepath ) ) {
candidates << filepath;
clangDebug() << "Adding" << filepath << "determined from candidate" << decl->toString();
}
if( dynamic_cast<KDevelop::AliasDeclaration*>( decl ) ) {
for( const auto importer : file->importers() ) {
if( importer->imports().count() != 1 && !isBlacklisted( filepath ) ) {
continue;
}
if( decl->isForwardDeclaration() ) {
if( importer->topContext()->localDeclarations().count() ) {
continue;
}
const auto filepath = decl->url().toUrl().toLocalFile();
if( !isBlacklisted( filepath ) ) {
candidates << filepath;
clangDebug() << "Adding" << filepath << "determined from candidate" << declaration;
const auto filePath = importer->url().toUrl().toLocalFile();
if( isBlacklisted( filePath ) ) {
continue;
}
for( const auto importer : file->importers() ) {
if( importer->imports().count() != 1 && !isBlacklisted( filepath ) ) {
continue;
}
if( importer->topContext()->localDeclarations().count() ) {
continue;
}
const auto filePath = importer->url().toUrl().toLocalFile();
if( isBlacklisted( filePath ) ) {
continue;
}
/* This file is a forwarder, such as <vector>
* <vector> does not actually implement the functions, but include other headers that do
* we prefer this to other headers
*/
candidates << filePath;
clangDebug() << "Adding forwarder file" << filePath << "to the result set";
}
/* This file is a forwarder, such as <vector>
* <vector> does not actually implement the functions, but include other headers that do
* we prefer this to other headers
*/
candidates << filePath;
clangDebug() << "Adding forwarder file" << filePath << "to the result set";
}
}
......@@ -334,7 +312,7 @@ QStringList duchainCandidates( const QualifiedIdentifier& identifier, const KDev
return candidates;
}
/*
/**
* Takes a filepath and the include paths and determines what directive to use.
*/
ClangFixit directiveForFile( const QString& includefile, const KDevelop::Path::List& includepaths, const KDevelop::Path& source )
......@@ -385,63 +363,98 @@ ClangFixit directiveForFile( const QString& includefile, const KDevelop::Path::L
KDevelop::Path::List includePaths( const KDevelop::Path& file )
{
/*
* Find project's custom include paths
*/
// Find project's custom include paths
const auto source = file.toLocalFile();
const auto item = ICore::self()->projectController()->projectModel()->itemForPath( KDevelop::IndexedString( source ) );
return IDefinesAndIncludesManager::manager()->includes(item);
}
/*
/**
* Return a list of header files viable for inclusions. All elements will be unique
*/
QStringList includeFiles( const QualifiedIdentifier& identifier, const KDevelop::Path& file, const KDevelop::DocumentRange& range )
QStringList includeFiles(const QualifiedIdentifier& identifier, const QVector<Declaration*> declarations, const KDevelop::Path& file)
{
const CursorInRevision cursor{ range.start().line(), range.start().column() };
const auto includes = includePaths( file );
if( includes.isEmpty() ) {
clangDebug() << "Include path is empty";
return {};
}
const auto candidates = duchainCandidates( identifier, file, cursor );
const auto candidates = findMatchingIncludeFiles(declarations);
if( !candidates.isEmpty() ) {
// If we find a candidate from the duchain we don't bother scanning the include paths
return candidates;
}
return scanIncludePaths( identifier, includes );
return scanIncludePaths(identifier, includes);
}
/*
/**
* Construct viable forward declarations for the type name.
*
* Currently we're not able to determine what is namespaces, class names etc
* and makes a suitable forward declaration, so just suggest "vanilla" declarations.
*/
ClangFixits forwardDeclarations(const QualifiedIdentifier& identifier, const Path& source)
ClangFixits forwardDeclarations(const QVector<Declaration*>& matchingDeclarations, const Path& source)
{
const auto range = forwardDeclarationPosition(identifier, source);
if (!range.isValid()) {
return {};
ClangFixits fixits;
for (const auto decl : matchingDeclarations) {
const auto qid = decl->qualifiedIdentifier();
if (qid.count() > 1) {
// TODO: Currently we're not able to determine what is namespaces, class names etc
// and makes a suitable forward declaration, so just suggest "vanilla" declarations.
continue;
}
const auto range = forwardDeclarationPosition(qid, source);
if (!range.isValid()) {
continue; // do not know where to insert
}
const auto name = qid.last().toString();
fixits += {
{QLatin1String("class ") + name + QLatin1String(";\n"), range, QObject::tr("Forward declare as 'class'")},
{QLatin1String("struct ") + name + QLatin1String(";\n"), range, QObject::tr("Forward declare as 'struct'")}
};
}
return fixits;
}
/**
* Search the persistent symbol table for matching declarations for identifiers @p identifiers
*/
QVector<Declaration*> findMatchingDeclarations(const QVector<QualifiedIdentifier>& identifiers)
{
DUChainReadLocker lock;
QVector<Declaration*> matchingDeclarations;
matchingDeclarations.reserve(identifiers.size());
for (const auto& declaration : identifiers) {
clangDebug() << "Considering candidate declaration" << declaration;
const IndexedDeclaration* declarations;
uint declarationCount;
PersistentSymbolTable::self().declarations( declaration , declarationCount, declarations );
const auto name = identifier.last().toString();
return {
{QLatin1String("class ") + name + QLatin1String(";\n"), range, QObject::tr("Forward declare as 'class'")},
{QLatin1String("struct ") + name + QLatin1String(";\n"), range, QObject::tr("Forward declare as 'struct'")}
};
for (uint i = 0; i < declarationCount; ++i) {
// Skip if the declaration is invalid or if it is an alias declaration -
// we want the actual declaration (and its file)
if (auto decl = declarations[i].declaration()) {
matchingDeclarations << decl;
}
}
}
return matchingDeclarations;
}
ClangFixits fixUnknownDeclaration( const QualifiedIdentifier& identifier, const KDevelop::Path& file, const KDevelop::DocumentRange& docrange )
{
ClangFixits fixits;
const CursorInRevision cursor{docrange.start().line(), docrange.start().column()};
const auto possibleIdentifiers = findPossibleQualifiedIdentifiers(identifier, file, cursor);
const auto matchingDeclarations = findMatchingDeclarations(possibleIdentifiers);
if (ClangSettingsManager::self()->assistantsSettings().forwardDeclare) {
for (const auto& fixit : forwardDeclarations(identifier, file)) {
for (const auto& fixit : forwardDeclarations(matchingDeclarations, file)) {
fixits << fixit;
if (fixits.size() == maxSuggestions) {
return fixits;
......@@ -449,7 +462,7 @@ ClangFixits fixUnknownDeclaration( const QualifiedIdentifier& identifier, const
}
}
const auto includefiles = includeFiles( identifier, file, docrange );
const auto includefiles = includeFiles(identifier, matchingDeclarations, file);
if (includefiles.isEmpty()) {
// return early as the computation of the include paths is quite expensive
retu