Members of the KDE Community are recommended to subscribe to the kde-community mailing list at https://mail.kde.org/mailman/listinfo/kde-community to allow them to participate in important discussions and receive other important announcements

Commit a3222654 authored by Milian Wolff's avatar Milian Wolff

Take unsaved file contents into account during code completion.

This basically reimplements the model <-> worker <-> context API
which is horrendous in KDevplatform currently. This approach here
doesn't require the Foreground lock at all and simplifies things
a lot.
parent 14e4c78a
......@@ -96,53 +96,57 @@ CodeCompletionModel::CompletionProperties SimpleItem::completionProperties() con
return m_properties;
}
}
ClangCodeCompletionContext::ClangCodeCompletionContext(const DUContextPointer& context, const QString& text,
const CursorInRevision& position, int depth)
: CodeCompletionContext(context, text, position, depth)
QByteArray concatenate(const QStringList& contents)
{
QByteArray ret;
int sizeGuess = 0;
foreach(const QString& line, contents) {
sizeGuess += line.size() + 1;
}
ret.reserve(sizeGuess);
foreach(const QString& line, contents) {
ret += line.toUtf8() + '\n';
}
return ret;
}
ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(bool& abort, bool /*fullCompletion*/)
ClangCodeCompletionContext::ClangCodeCompletionContext(const ParseSession* const session,
const SimpleCursor& position,
const QStringList& contents
)
: CodeCompletionContext({}, QString(), {}, 0)
, m_results(nullptr, clang_disposeCodeCompleteResults)
{
// FIXME: ensure we don't access the TU from this thread and the parse thread simultaneously!
DUChainReadLocker lock;
if (abort || !m_duContext) {
return {};
}
const auto session = KSharedPtr<ParseSession>::dynamicCast(m_duContext->topContext()->ast());
if (!session) {
qDebug() << "no session found for file" << m_duContext->url();
return {};
}
ClangString file(clang_getFileName(session->file()));
/* FIXME: m_text only contains the part of the current context which is not enough for the clang use-case
CXUnsavedFile unsaved;
const auto contents = m_text.toUtf8();
unsaved.Contents = contents.constData();
unsaved.Length = contents.size();
const QByteArray fileContents = concatenate(contents);
unsaved.Contents = fileContents.constData();
unsaved.Length = fileContents.size();
unsaved.Filename = file;
*/
const auto results = std::unique_ptr<CXCodeCompleteResults, void(*)(CXCodeCompleteResults*)>(
clang_codeCompleteAt(session->unit(), file, m_position.line + 1, m_position.column + 1, nullptr, 0,
clang_defaultCodeCompleteOptions()),
clang_disposeCodeCompleteResults);
m_results.reset( clang_codeCompleteAt(session->unit(), file,
position.line + 1, position.column + 1,
&unsaved, 1,
clang_defaultCodeCompleteOptions()) );
}
ClangCodeCompletionContext::~ClangCodeCompletionContext()
{
}
QList<CompletionTreeItemPointer> ret;
ret.reserve(results->NumResults);
QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(const TopDUContext* const top,
const CursorInRevision& position)
{
QList<CompletionTreeItemPointer> items;
items.reserve(m_results->NumResults);
QSet<QualifiedIdentifier> handled;
for (uint i = 0; i < results->NumResults; ++i) {
auto result = results->Results[i];
for (uint i = 0; i < m_results->NumResults; ++i) {
auto result = m_results->Results[i];
const uint chunks = clang_getNumCompletionChunks(result.CompletionString);
QString typed;
......@@ -185,12 +189,12 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
}
handled.insert(qid);
Declaration* found = 0;
foreach(Declaration* dec, m_duContext->findDeclarations(qid, m_position)) {
foreach(Declaration* dec, top->findDeclarations(qid, position)) {
found = dec;
break;
}
if (found) {
ret.append(CompletionTreeItemPointer(new NormalDeclarationCompletionItem(DeclarationPointer(found))));
items.append(CompletionTreeItemPointer(new NormalDeclarationCompletionItem(DeclarationPointer(found))));
continue;
} else {
debug() << "Could not find declaration for" << qid;
......@@ -198,8 +202,7 @@ QList<CompletionTreeItemPointer> ClangCodeCompletionContext::completionItems(boo
}
// TODO: grouping of macros and built-in stuff
ret.append(CompletionTreeItemPointer(new SimpleItem(typed, resultType, text, CodeCompletionModel::GlobalScope)));
items.append(CompletionTreeItemPointer(new SimpleItem(typed, resultType, text, CodeCompletionModel::GlobalScope)));
}
return ret;
return items;
}
......@@ -24,14 +24,29 @@
#include <language/codecompletion/codecompletioncontext.h>
#include <clang-c/Index.h>
#include <memory>
class ParseSession;
class ClangCodeCompletionContext : public KDevelop::CodeCompletionContext
{
public:
ClangCodeCompletionContext(const KDevelop::DUContextPointer& context, const QString& text,
const KDevelop::CursorInRevision& position, int depth = 0);
ClangCodeCompletionContext(const ParseSession* const session,
const KDevelop::SimpleCursor& position,
const QStringList& contents);
~ClangCodeCompletionContext();
QList< KDevelop::CompletionTreeItemPointer > completionItems(bool& abort, bool fullCompletion) override;
QList<KDevelop::CompletionTreeItemPointer> completionItems(const KDevelop::TopDUContext* const top,
const KDevelop::CursorInRevision& position);
QList< KDevelop::CompletionTreeItemPointer > completionItems(bool& /*abort*/, bool /*fullCompletion*/ = true) override
{
// not used, see above
return {};
}
private:
std::unique_ptr<CXCodeCompleteResults, void(*)(CXCodeCompleteResults*)> m_results;
};
#endif // CLANGCODECOMPLETIONCONTEXT_H
......@@ -21,8 +21,14 @@
#include "model.h"
#include "context.h"
#include <duchain/parsesession.h>
#include <language/codecompletion/codecompletionworker.h>
#include <language/duchain/topducontext.h>
#include <language/duchain/duchainutils.h>
#include <language/duchain/duchainlock.h>
#include <KTextEditor/View>
#include <KTextEditor/Document>
using namespace KDevelop;
......@@ -34,13 +40,57 @@ public:
ClangCodeCompletionWorker(CodeCompletionModel* model)
: CodeCompletionWorker(model)
{}
virtual ~ClangCodeCompletionWorker()
{}
virtual ~ClangCodeCompletionWorker() = default;
CodeCompletionContext* createCompletionContext(DUContextPointer context, const QString& contextText,
const QString& /*followingText*/, const CursorInRevision& position) const override
public slots:
void completionRequested(const KUrl& url, const KDevelop::SimpleCursor& position, const QStringList& contents)
{
return new ClangCodeCompletionContext(context, contextText, position);
aborting() = false;
DUChainReadLocker lock;
if (aborting()) {
failed();
return;
}
auto top = DUChainUtils::standardContextForUrl(url);
if (!top) {
kWarning() << "No context found for" << url;
return;
}
const auto session = KSharedPtr<ParseSession>::dynamicCast(top->ast());
if (!session) {
// TODO: trigger reparse and re-request code completion
kWarning() << "No parse session / AST attached to context for url" << url;
}
ClangCodeCompletionContext completionContext( session.data(), position, contents );
if (aborting()) {
failed();
return;
}
// NOTE: cursor might be wrong here, but shouldn't matter much I hope...
// when the document changed significantly, then the cache is off anyways and we don't get anything sensible
// the position here is just a "optimization" to only search up to that position
const auto& items = completionContext.completionItems(top, CursorInRevision::castFromSimpleCursor(position));
if (aborting()) {
failed();
return;
}
auto tree = computeGroups( items, {} );
if (aborting()) {
failed();
return;
}
tree += completionContext.ungroupedElements();
foundDeclarations( tree, {} );
}
};
}
......@@ -48,6 +98,7 @@ public:
ClangCodeCompletionModel::ClangCodeCompletionModel(QObject* parent)
: CodeCompletionModel(parent)
{
qRegisterMetaType<KDevelop::SimpleCursor>();
}
ClangCodeCompletionModel::~ClangCodeCompletionModel()
......@@ -57,7 +108,19 @@ ClangCodeCompletionModel::~ClangCodeCompletionModel()
CodeCompletionWorker* ClangCodeCompletionModel::createCompletionWorker()
{
return new ClangCodeCompletionWorker(this);
auto worker = new ClangCodeCompletionWorker(this);
connect(this, SIGNAL(requestCompletion(KUrl,KDevelop::SimpleCursor,QStringList)),
worker, SLOT(completionRequested(KUrl,KDevelop::SimpleCursor,QStringList)));
return worker;
}
void ClangCodeCompletionModel::completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range,
CodeCompletionModel::InvocationType /*invocationType*/, const KUrl& url)
{
// get text before this range so we can parse this version with clang
const QStringList lines = view->document()->textLines({0, 0, range.start().line(), range.start().column()});
emit requestCompletion(url, SimpleCursor(range.start()), lines);
}
#include "model.moc"
......
......@@ -34,8 +34,14 @@ public:
explicit ClangCodeCompletionModel(QObject* parent);
virtual ~ClangCodeCompletionModel();
signals:
void requestCompletion(const KUrl& url, const KDevelop::SimpleCursor& cursor, const QStringList& contents);
protected:
virtual KDevelop::CodeCompletionWorker* createCompletionWorker();
KDevelop::CodeCompletionWorker* createCompletionWorker() override;
void completionInvokedInternal(KTextEditor::View* view, const KTextEditor::Range& range,
InvocationType invocationType, const KUrl& url) override;
};
#endif // CLANGCODECOMPLETIONMODEL_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment