From b4a17986187930ee73e2b1cb1e476762b7aa2a56 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 9 Feb 2018 14:02:11 +0100 Subject: [PATCH] TextEditor: add document content completer Add completion based on words of the document. This provides basic assistance for programming languages without a code model. Task-number: QTCREATORBUG-13869 Change-Id: I3a9c59c741dfd6895442fc0524cfd1bd3b2b0111 Reviewed-by: Christian Stenger Reviewed-by: Eike Ziller --- .../texteditor/codeassist/codeassistant.cpp | 1 - .../codeassist/documentcontentcompletion.cpp | 137 ++++++++++++++++++ .../codeassist/documentcontentcompletion.h | 49 +++++++ src/plugins/texteditor/texteditor.cpp | 4 +- src/plugins/texteditor/texteditor.pro | 2 + src/plugins/texteditor/texteditor.qbs | 2 + 6 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/plugins/texteditor/codeassist/documentcontentcompletion.cpp create mode 100644 src/plugins/texteditor/codeassist/documentcontentcompletion.h diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index 2b854abc2bb..de52c961af6 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -252,7 +252,6 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, case IAssistProvider::Asynchronous: { processor->setAsyncCompletionAvailableHandler( [this, reason](IAssistProposal *newProposal){ - QTC_CHECK(newProposal); invalidateCurrentRequestData(); displayProposal(newProposal, reason); diff --git a/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp new file mode 100644 index 00000000000..de5db0f0ba0 --- /dev/null +++ b/src/plugins/texteditor/codeassist/documentcontentcompletion.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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. +** +****************************************************************************/ + +#include "documentcontentcompletion.h" + +#include "assistinterface.h" +#include "assistproposalitem.h" +#include "genericproposal.h" +#include "genericproposalmodel.h" +#include "iassistprocessor.h" +#include "../snippets/snippetassistcollector.h" + +#include "utils/asconst.h" +#include "utils/runextensions.h" + +#include +#include +#include +#include +#include + +using namespace TextEditor; + +class DocumentContentCompletionProcessor : public IAssistProcessor +{ +public: + DocumentContentCompletionProcessor(const QString &snippetGroupId); + + IAssistProposal *perform(const AssistInterface *interface) override; + +private: + TextEditor::SnippetAssistCollector m_snippetCollector; + IAssistProposal *createProposal(const AssistInterface *interface); +}; + +DocumentContentCompletionProvider::DocumentContentCompletionProvider(const QString &snippetGroup) + : m_snippetGroup(snippetGroup) +{ } + +IAssistProvider::RunType DocumentContentCompletionProvider::runType() const +{ + return Asynchronous; +} + +IAssistProcessor *DocumentContentCompletionProvider::createProcessor() const +{ + return new DocumentContentCompletionProcessor(m_snippetGroup); +} + +DocumentContentCompletionProcessor::DocumentContentCompletionProcessor(const QString &snippetGroupId) + : m_snippetCollector(snippetGroupId, QIcon(":/texteditor/images/snippet.png")) +{ } + +IAssistProposal *DocumentContentCompletionProcessor::perform(const AssistInterface *interface) +{ + Utils::onResultReady(Utils::runAsync( + &DocumentContentCompletionProcessor::createProposal, this, interface), + [this](IAssistProposal *proposal){ + setAsyncProposalAvailable(proposal); + }); + setPerformWasApplicable(true); + return nullptr; +} + +static void generateProposalItems(const QString &text, QSet &words, + QList &items) +{ + static const QRegularExpression wordRE("([a-zA-Z_][a-zA-Z0-9_]{2,})"); + + QRegularExpressionMatch match; + int index = text.indexOf(wordRE, 0, &match); + while (index >= 0) { + const QString &word = match.captured(); + if (!words.contains(word)) { + auto item = new AssistProposalItem(); + item->setText(word); + items.append(item); + words.insert(word); + } + index += word.size(); + index = text.indexOf(wordRE, index, &match); + } +} + +IAssistProposal *DocumentContentCompletionProcessor::createProposal( + const AssistInterface *interface) +{ + QScopedPointer assistInterface(interface); + int pos = interface->position(); + + QChar chr; + // Skip to the start of a name + do { + chr = interface->characterAt(--pos); + } while (chr.isLetterOrNumber() || chr == '_'); + + ++pos; + + if (interface->reason() == IdleEditor) { + QChar characterUnderCursor = interface->characterAt(interface->position()); + if (characterUnderCursor.isLetterOrNumber() || interface->position() - pos < 3) + return nullptr; + } + + QSet words; + QList items = m_snippetCollector.collect(); + QTextBlock block = interface->textDocument()->firstBlock(); + + while (block.isValid()) { + generateProposalItems(block.text(), words, items); + block = block.next(); + } + + return new GenericProposal(pos, items); +} diff --git a/src/plugins/texteditor/codeassist/documentcontentcompletion.h b/src/plugins/texteditor/codeassist/documentcontentcompletion.h new file mode 100644 index 00000000000..3d8c048cabd --- /dev/null +++ b/src/plugins/texteditor/codeassist/documentcontentcompletion.h @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2018 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 "completionassistprovider.h" + +#include "texteditor/texteditorconstants.h" + +namespace TextEditor { + +class AssistInterface; + +class TEXTEDITOR_EXPORT DocumentContentCompletionProvider : public CompletionAssistProvider +{ +public: + DocumentContentCompletionProvider( + const QString &snippetGroup = QString(Constants::TEXT_SNIPPET_GROUP_ID)); + + RunType runType() const override; + IAssistProcessor *createProcessor() const override; + +private: + QString m_snippetGroup; +}; + +} // TextEditor diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index c531e6bf30f..cb4bfd12c15 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -56,7 +56,7 @@ #include #include #include -#include +#include #include #include #include @@ -8608,7 +8608,7 @@ void TextEditorFactory::setParenthesesMatchingEnabled(bool on) IEditor *TextEditorFactory::createEditor() { - static KeywordsCompletionAssistProvider basicSnippetProvider; + static DocumentContentCompletionProvider basicSnippetProvider; TextDocumentPtr doc(d->m_documentCreator()); if (d->m_indenterCreator) diff --git a/src/plugins/texteditor/texteditor.pro b/src/plugins/texteditor/texteditor.pro index becb9f51eaf..508acb78a72 100644 --- a/src/plugins/texteditor/texteditor.pro +++ b/src/plugins/texteditor/texteditor.pro @@ -84,6 +84,7 @@ SOURCES += texteditorplugin.cpp \ codeassist/genericproposalwidget.cpp \ codeassist/iassistproposalmodel.cpp \ codeassist/textdocumentmanipulator.cpp \ + codeassist/documentcontentcompletion.cpp\ tabsettingswidget.cpp \ simplecodestylepreferences.cpp \ simplecodestylepreferenceswidget.cpp \ @@ -194,6 +195,7 @@ HEADERS += texteditorplugin.h \ codeassist/iassistproposalmodel.h \ codeassist/textdocumentmanipulator.h \ codeassist/textdocumentmanipulatorinterface.h \ + codeassist/documentcontentcompletion.h \ tabsettingswidget.h \ simplecodestylepreferences.h \ simplecodestylepreferenceswidget.h \ diff --git a/src/plugins/texteditor/texteditor.qbs b/src/plugins/texteditor/texteditor.qbs index 09054d80ddb..81bb85c0a3d 100644 --- a/src/plugins/texteditor/texteditor.qbs +++ b/src/plugins/texteditor/texteditor.qbs @@ -160,6 +160,8 @@ Project { "codeassistant.h", "completionassistprovider.cpp", "completionassistprovider.h", + "documentcontentcompletion.cpp", + "documentcontentcompletion.h", "functionhintproposal.cpp", "functionhintproposal.h", "functionhintproposalwidget.cpp",