OSDN Git Service

Support completion in profile editor
authorJarek Kobus <jkobus@trolltech.com>
Mon, 8 Nov 2010 13:01:50 +0000 (14:01 +0100)
committerJarek Kobus <jkobus@trolltech.com>
Mon, 8 Nov 2010 13:03:49 +0000 (14:03 +0100)
Task-number: QTCREATORBUG-2110

src/plugins/qt4projectmanager/profilecompletion.cpp [new file with mode: 0644]
src/plugins/qt4projectmanager/profilecompletion.h [new file with mode: 0644]
src/plugins/qt4projectmanager/profileeditor.h
src/plugins/qt4projectmanager/profileeditorfactory.cpp
src/plugins/qt4projectmanager/profilehighlighter.cpp
src/plugins/qt4projectmanager/profilekeywords.cpp [new file with mode: 0644]
src/plugins/qt4projectmanager/profilekeywords.h [new file with mode: 0644]
src/plugins/qt4projectmanager/qt4projectmanager.pro
src/plugins/qt4projectmanager/qt4projectmanagerplugin.cpp

diff --git a/src/plugins/qt4projectmanager/profilecompletion.cpp b/src/plugins/qt4projectmanager/profilecompletion.cpp
new file mode 100644 (file)
index 0000000..9d3f706
--- /dev/null
@@ -0,0 +1,189 @@
+#include "profilecompletion.h"
+#include "profileeditor.h"
+#include "profilekeywords.h"
+#include <texteditor/itexteditor.h>
+#include <texteditor/completionsettings.h>
+#include <cplusplus/Icons.h>
+#include <QDebug>
+
+using namespace Qt4ProjectManager::Internal;
+
+ProFileCompletion::ProFileCompletion(QObject *parent) :
+    TextEditor::ICompletionCollector(parent),
+    m_editor(0),
+    m_startPosition(-1),
+    m_variableIcon(CPlusPlus::Icons().iconForType(CPlusPlus::Icons::VarPublicIconType)),
+    m_functionIcon(CPlusPlus::Icons().iconForType(CPlusPlus::Icons::FuncPublicIconType))
+{
+}
+
+ProFileCompletion::~ProFileCompletion()
+{
+
+}
+
+QList<TextEditor::CompletionItem> ProFileCompletion::getCompletions()
+{
+    QList<TextEditor::CompletionItem> completionItems;
+    completions(&completionItems);
+
+    return completionItems;
+}
+
+bool ProFileCompletion::shouldRestartCompletion()
+{
+    return false;
+}
+
+TextEditor::ITextEditable *ProFileCompletion::editor() const
+{
+    return m_editor;
+}
+
+int ProFileCompletion::startPosition() const
+{
+    return m_startPosition;
+}
+
+bool ProFileCompletion::supportsEditor(TextEditor::ITextEditable *editor)
+{
+    if (qobject_cast<ProFileEditorEditable *>(editor))
+        return true;
+    return false;
+}
+
+bool ProFileCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
+{
+    m_editor = editor;
+    const int pos = editor->position();
+
+    if (completionSettings().m_completionTrigger == TextEditor::AutomaticCompletion) {
+        QChar characterUnderCursor = editor->characterAt(pos);
+        if (!characterUnderCursor.isLetterOrNumber()) {
+            m_startPosition = findStartOfName();
+            if (pos - m_startPosition >= 3 && !isInComment())
+                return true;
+        }
+    }
+    return false;
+}
+
+int ProFileCompletion::findStartOfName(int pos) const
+{
+    if (pos == -1)
+        pos = m_editor->position();
+    QChar chr;
+
+    // Skip to the start of a name
+    do {
+        chr = m_editor->characterAt(--pos);
+    } while (chr.isLetterOrNumber() || chr == QLatin1Char('_'));
+
+    return pos + 1;
+}
+
+bool ProFileCompletion::isInComment() const
+{
+    const int beginOfLinePosition = m_editor->position(TextEditor::ITextEditor::StartOfLine);
+    const QString lineBeginning = m_editor->textAt(beginOfLinePosition,
+                                  m_startPosition - beginOfLinePosition);
+    if (lineBeginning.contains(QLatin1Char('#')))
+        return true;
+    return false;
+}
+
+int ProFileCompletion::startCompletion(TextEditor::ITextEditable *editor)
+{
+    m_editor = editor;
+    m_startPosition = findStartOfName();
+
+    return m_startPosition;
+}
+
+void ProFileCompletion::completions(QList<TextEditor::CompletionItem> *completions)
+{
+    const int length = m_editor->position() - m_startPosition;
+    if (length < 0)
+        return;
+
+    if (isInComment())
+        return;
+
+    const QString key = m_editor->textAt(m_startPosition, length);
+
+    QList<TextEditor::CompletionItem> items;
+    QStringList keywords = ProFileKeywords::variables()
+            + ProFileKeywords::functions();
+//    qSort(keywords);
+    for (int i = 0; i < keywords.count(); i++) {
+        TextEditor::CompletionItem item(this);
+        item.text = keywords[i];
+        item.data = QVariant::fromValue(item.text);
+        item.icon = ProFileKeywords::isFunction(item.text)
+                ? m_functionIcon : m_variableIcon;
+        items.append(item);
+    }
+
+    filter(items, completions, key);
+}
+
+bool ProFileCompletion::typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+    // only '(' in case of a function
+    if (typedChar == QLatin1Char('(') && ProFileKeywords::isFunction(item.text))
+        return true;
+    return false;
+}
+
+void ProFileCompletion::complete(const TextEditor::CompletionItem &item, QChar typedChar)
+{
+    Q_UNUSED(typedChar)
+
+    int replaceLength = m_editor->position() - m_startPosition;
+    if (replaceLength < 0)
+        return;
+
+    QString toInsert = item.text;
+    int cursorOffset = 0;
+    if (ProFileKeywords::isFunction(toInsert)
+            && completionSettings().m_autoInsertBrackets) {
+        if (completionSettings().m_spaceAfterFunctionName) {
+            if (m_editor->textAt(m_editor->position(), 2) == QLatin1String(" (")) {
+                cursorOffset = 2;
+            } else if (m_editor->characterAt(m_editor->position()) == QLatin1Char('(')
+                       || m_editor->characterAt(m_editor->position()) == QLatin1Char(' ')) {
+                replaceLength += 1;
+                toInsert += QLatin1String(" (");
+            } else {
+                toInsert += QLatin1String(" ()");
+                cursorOffset = -1;
+            }
+        } else {
+            if (m_editor->characterAt(m_editor->position()) == QLatin1Char('(')) {
+                cursorOffset = 1;
+            } else {
+                toInsert += QLatin1String("()");
+                cursorOffset = -1;
+            }
+        }
+    }
+
+    m_editor->setCurPos(m_startPosition);
+    m_editor->replace(replaceLength, toInsert);
+    if (cursorOffset)
+        m_editor->setCurPos(m_editor->position() + cursorOffset);
+}
+
+bool ProFileCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems)
+{
+    if (completionItems.count() == 1) {
+        complete(completionItems.first(), QChar());
+        return true;
+    }
+
+    return TextEditor::ICompletionCollector::partiallyComplete(completionItems);
+}
+
+void ProFileCompletion::cleanup()
+{
+}
diff --git a/src/plugins/qt4projectmanager/profilecompletion.h b/src/plugins/qt4projectmanager/profilecompletion.h
new file mode 100644 (file)
index 0000000..cdbec85
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef PROFILECOMPLETION_H
+#define PROFILECOMPLETION_H
+
+#include <texteditor/icompletioncollector.h>
+
+namespace Qt4ProjectManager {
+
+namespace Internal {
+
+class ProFileCompletion : public TextEditor::ICompletionCollector
+{
+    Q_OBJECT
+public:
+    ProFileCompletion(QObject *parent = 0);
+
+    virtual ~ProFileCompletion();
+
+    virtual QList<TextEditor::CompletionItem> getCompletions();
+    virtual bool shouldRestartCompletion();
+
+    virtual TextEditor::ITextEditable *editor() const;
+    virtual int startPosition() const;
+
+    virtual bool supportsEditor(TextEditor::ITextEditable *editor);
+    virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
+    virtual int startCompletion(TextEditor::ITextEditable *editor);
+    virtual void completions(QList<TextEditor::CompletionItem> *completions);
+    virtual bool typedCharCompletes(const TextEditor::CompletionItem &item, QChar typedChar);
+    virtual void complete(const TextEditor::CompletionItem &item, QChar typedChar);
+    virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
+    virtual void cleanup();
+private:
+    int findStartOfName(int pos = -1) const;
+    bool isInComment() const;
+
+    TextEditor::ITextEditable *m_editor;
+    int m_startPosition;
+    const QIcon m_variableIcon;
+    const QIcon m_functionIcon;
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // PROFILECOMPLETION_H
index 1dc9733..1ef4beb 100644 (file)
@@ -53,6 +53,8 @@ class ProFileEditor;
 
 class ProFileEditorEditable : public TextEditor::BaseTextEditorEditable
 {
+    Q_OBJECT
+
 public:
     ProFileEditorEditable(ProFileEditor *);
     Core::Context context() const;
index c2e5e02..031adfc 100644 (file)
@@ -37,6 +37,7 @@
 #include <coreplugin/editormanager/editormanager.h>
 #include <texteditor/texteditoractionhandler.h>
 #include <texteditor/texteditorsettings.h>
+#include <texteditor/completionsupport.h>
 
 #include <QtCore/QFileInfo>
 #include <QtGui/QAction>
@@ -83,9 +84,13 @@ Core::IFile *ProFileEditorFactory::open(const QString &fileName)
 
 Core::IEditor *ProFileEditorFactory::createEditor(QWidget *parent)
 {
-    ProFileEditor *rc = new ProFileEditor(parent, this, m_actionHandler);
-    TextEditor::TextEditorSettings::instance()->initializeEditor(rc);
-    return rc->editableInterface();
+    ProFileEditor *editor = new ProFileEditor(parent, this, m_actionHandler);
+    TextEditor::TextEditorSettings::instance()->initializeEditor(editor);
+
+    // auto completion
+    connect(editor, SIGNAL(requestAutoCompletion(TextEditor::ITextEditable*, bool)),
+            TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(TextEditor::ITextEditable*, bool)));
+    return editor->editableInterface();
 }
 
 QStringList ProFileEditorFactory::mimeTypes() const
index 14daeee..55a00f7 100644 (file)
@@ -28,6 +28,7 @@
 **************************************************************************/
 
 #include "profilehighlighter.h"
+#include "profilekeywords.h"
 
 #include <QtCore/QRegExp>
 #include <QtGui/QColor>
 
 using namespace Qt4ProjectManager::Internal;
 
-const char *const variables[] = {
-    "CCFLAG",
-    "CONFIG",
-    "DEFINES",
-    "DEF_FILE",
-    "DEPENDPATH",
-    "DEPLOYMENT",
-    "DESTDIR",
-    "DESTDIR_TARGET",
-    "DISTFILES",
-    "DLLDESTDIR",
-    "FORMS",
-    "HEADERS",
-    "ICON",
-    "INCLUDEPATH",
-    "INSTALLS",
-    "LEXSOURCES",
-    "LIBS",
-    "MAKEFILE",
-    "MOBILITY",
-    "MOC_DIR",
-    "OBJECTIVE_HEADERS",
-    "OBJECTIVE_SOURCES",
-    "OBJECTS",
-    "OBJECTS_DIR",
-    "OBJMOC",
-    "OTHER_FILES",
-    "PKGCONFIG",
-    "POST_TARGETDEPS",
-    "PRECOMPILED_HEADER",
-    "PRE_TARGETDEPS",
-    "QMAKE",
-    "QMAKESPEC",
-    "QT",
-    "RCC_DIR",
-    "RC_FILE",
-    "REQUIRES",
-    "RESOURCES",
-    "RES_FILE",
-    "SOURCES",
-    "SRCMOC",
-    "STATECHARTS",
-    "SUBDIRS",
-    "TARGET",
-    "TARGET.CAPABILITY",
-    "TARGET.EPOCHEAPSIZE",
-    "TARGET.UID3",
-    "TARGET_EXT",
-    "TARGET_x",
-    "TARGET_x.y.z",
-    "TEMPLATE",
-    "TRANSLATIONS",
-    "UI_DIR",
-    "UI_HEADERS_DIR",
-    "UI_SOURCES_DIR",
-    "VER_MAJ",
-    "VER_MIN",
-    "VER_PAT",
-    "VERSION",
-    "VPATH",
-    "YACCSOURCES",
-    0
-};
-
-const char *const functions[] = {
-    "basename",
-    "contains",
-    "count",
-    "dirname",
-    "error",
-    "exists",
-    "find",
-    "for",
-    "include",
-    "infile",
-    "isEmpty",
-    "join",
-    "member",
-    "message",
-    "prompt",
-    "quote",
-    "sprintf",
-    "system",
-    "unique",
-    "warning",
-    0
-};
-
-struct KeywordHelper
-{
-    inline KeywordHelper(const QString &word) : needle(word) {}
-    const QString needle;
-};
-
-static bool operator<(const KeywordHelper &helper, const char *kw)
-{
-    return helper.needle < QLatin1String(kw);
-}
-
-static bool operator<(const char *kw, const KeywordHelper &helper)
-{
-    return QLatin1String(kw) < helper.needle;
-}
-
-static bool isVariable(const QString &word)
-{
-    const char *const *start = &variables[0];
-    const char *const *end = &variables[sizeof variables / sizeof variables[0] - 1];
-    const char *const *kw = qBinaryFind(start, end, KeywordHelper(word));
-    return *kw != 0;
-}
-
-static bool isFunction(const QString &word)
-{
-    const char *const *start = &functions[0];
-    const char *const *end = &functions[sizeof functions / sizeof functions[0] - 1];
-    const char *const *kw = qBinaryFind(start, end, KeywordHelper(word));
-    return *kw != 0;
-}
 
 ProFileHighlighter::ProFileHighlighter(QTextDocument *document) :
     TextEditor::SyntaxHighlighter(document)
@@ -179,12 +61,12 @@ void ProFileHighlighter::highlightBlock(const QString &text)
             if (c.isLetter() || c == '_' || c == '.' || c.isDigit()) {
                 buf += c;
                 setFormat(i - buf.length()+1, buf.length(), emptyFormat);
-                if (!buf.isEmpty() && isFunction(buf))
+                if (!buf.isEmpty() && ProFileKeywords::isFunction(buf))
                     setFormat(i - buf.length()+1, buf.length(), m_formats[ProfileFunctionFormat]);
-                else if (!buf.isEmpty() && isVariable(buf))
+                else if (!buf.isEmpty() && ProFileKeywords::isVariable(buf))
                     setFormat(i - buf.length()+1, buf.length(), m_formats[ProfileVariableFormat]);
             } else if (c == '(') {
-                if (!buf.isEmpty() && isFunction(buf))
+                if (!buf.isEmpty() && ProFileKeywords::isFunction(buf))
                     setFormat(i - buf.length(), buf.length(), m_formats[ProfileFunctionFormat]);
                 buf.clear();
             } else if (c == '#') {
@@ -192,7 +74,7 @@ void ProFileHighlighter::highlightBlock(const QString &text)
                 setFormat(i, 1, m_formats[ProfileCommentFormat]);
                 buf.clear();
             } else {
-                if (!buf.isEmpty() && isVariable(buf))
+                if (!buf.isEmpty() && ProFileKeywords::isVariable(buf))
                     setFormat(i - buf.length(), buf.length(), m_formats[ProfileVariableFormat]);
                 buf.clear();
             }
diff --git a/src/plugins/qt4projectmanager/profilekeywords.cpp b/src/plugins/qt4projectmanager/profilekeywords.cpp
new file mode 100644 (file)
index 0000000..1e0ee9b
--- /dev/null
@@ -0,0 +1,167 @@
+#include "profilekeywords.h"
+
+using namespace Qt4ProjectManager::Internal;
+
+static const char *const variableKeywords[] = {
+    "CCFLAG",
+    "CONFIG",
+    "DEFINES",
+    "DEF_FILE",
+    "DEPENDPATH",
+    "DEPLOYMENT",
+    "DESTDIR",
+    "DESTDIR_TARGET",
+    "DISTFILES",
+    "DLLDESTDIR",
+    "FORMS",
+    "HEADERS",
+    "ICON",
+    "INCLUDEPATH",
+    "INSTALLS",
+    "LEXSOURCES",
+    "LIBS",
+    "MAKEFILE",
+    "MOBILITY",
+    "MOC_DIR",
+    "OBJECTIVE_HEADERS",
+    "OBJECTIVE_SOURCES",
+    "OBJECTS",
+    "OBJECTS_DIR",
+    "OBJMOC",
+    "OTHER_FILES",
+    "PKGCONFIG",
+    "POST_TARGETDEPS",
+    "PRECOMPILED_HEADER",
+    "PRE_TARGETDEPS",
+    "QMAKE",
+    "QMAKESPEC",
+    "QT",
+    "RCC_DIR",
+    "RC_FILE",
+    "REQUIRES",
+    "RESOURCES",
+    "RES_FILE",
+    "SOURCES",
+    "SRCMOC",
+    "STATECHARTS",
+    "SUBDIRS",
+    "TARGET",
+    "TARGET.CAPABILITY",
+    "TARGET.EPOCHEAPSIZE",
+    "TARGET.UID3",
+    "TARGET_EXT",
+    "TARGET_x",
+    "TARGET_x.y.z",
+    "TEMPLATE",
+    "TRANSLATIONS",
+    "UI_DIR",
+    "UI_HEADERS_DIR",
+    "UI_SOURCES_DIR",
+    "VER_MAJ",
+    "VER_MIN",
+    "VER_PAT",
+    "VERSION",
+    "VPATH",
+    "YACCSOURCES",
+    0
+};
+
+static const char *const functionKeywords[] = {
+    "basename",
+    "contains",
+    "count",
+    "dirname",
+    "error",
+    "exists",
+    "find",
+    "for",
+    "include",
+    "infile",
+    "isEmpty",
+    "join",
+    "member",
+    "message",
+    "prompt",
+    "quote",
+    "sprintf",
+    "system",
+    "unique",
+    "warning",
+    0
+};
+
+class ProFileKeywordsImplementation
+{
+public:
+    static ProFileKeywordsImplementation *instance();
+    QStringList variables();
+    QStringList functions();
+    bool isVariable(const QString &word);
+    bool isFunction(const QString &word);
+private:
+    ProFileKeywordsImplementation();
+    static ProFileKeywordsImplementation *m_instance;
+    QStringList m_variables;
+    QStringList m_functions;
+};
+
+ProFileKeywordsImplementation *ProFileKeywordsImplementation::m_instance = 0;
+
+ProFileKeywordsImplementation *ProFileKeywordsImplementation::instance()
+{
+    if (!m_instance)
+        m_instance = new ProFileKeywordsImplementation();
+    return m_instance;
+}
+
+QStringList ProFileKeywordsImplementation::variables()
+{
+    return m_variables;
+}
+
+QStringList ProFileKeywordsImplementation::functions()
+{
+    return m_functions;
+}
+
+bool ProFileKeywordsImplementation::isVariable(const QString &word)
+{
+    return m_variables.contains(word);
+}
+
+bool ProFileKeywordsImplementation::isFunction(const QString &word)
+{
+    return m_functions.contains(word);
+}
+
+ProFileKeywordsImplementation::ProFileKeywordsImplementation()
+{
+    for (uint i = 0; i < sizeof variableKeywords / sizeof variableKeywords[0] - 1; i++)
+        m_variables.append(QLatin1String(variableKeywords[i]));
+    for (uint i = 0; i < sizeof functionKeywords / sizeof functionKeywords[0] - 1; i++)
+        m_functions.append(QLatin1String(functionKeywords[i]));
+}
+
+ProFileKeywords::ProFileKeywords()
+{
+}
+
+QStringList ProFileKeywords::variables()
+{
+    return ProFileKeywordsImplementation::instance()->variables();
+}
+
+QStringList ProFileKeywords::functions()
+{
+    return ProFileKeywordsImplementation::instance()->functions();
+}
+
+bool ProFileKeywords::isVariable(const QString &word)
+{
+    return ProFileKeywordsImplementation::instance()->isVariable(word);
+}
+
+bool ProFileKeywords::isFunction(const QString &word)
+{
+    return ProFileKeywordsImplementation::instance()->isFunction(word);
+}
diff --git a/src/plugins/qt4projectmanager/profilekeywords.h b/src/plugins/qt4projectmanager/profilekeywords.h
new file mode 100644 (file)
index 0000000..411d98d
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef PROFILEKEYWORDS_H
+#define PROFILEKEYWORDS_H
+
+#include <QtCore/QStringList>
+
+namespace Qt4ProjectManager {
+
+namespace Internal {
+
+class ProFileKeywords
+{
+public:
+    static QStringList variables();
+    static QStringList functions();
+    static bool isVariable(const QString &word);
+    static bool isFunction(const QString &word);
+private:
+    ProFileKeywords();
+};
+
+} // namespace Internal
+} // namespace Qt4ProjectManager
+
+#endif // PROFILEKEYWORDS_H
index 78fa9e4..d0a823d 100644 (file)
@@ -63,7 +63,9 @@ HEADERS += qt4deployconfiguration.h \
     findqt4profiles.h \
     qt4projectmanager_global.h \
     qmldumptool.h \
-    qmlobservertool.h
+    qmlobservertool.h \
+    profilecompletion.h \
+    profilekeywords.h
 SOURCES += qt4projectmanagerplugin.cpp \
     qt4deployconfiguration.cpp \
     qtparser.cpp \
@@ -121,7 +123,9 @@ SOURCES += qt4projectmanagerplugin.cpp \
     librarydetailscontroller.cpp \
     findqt4profiles.cpp \
     qmldumptool.cpp \
-    qmlobservertool.cpp
+    qmlobservertool.cpp \
+    profilecompletion.cpp \
+    profilekeywords.cpp
 FORMS += makestep.ui \
     qmakestep.ui \
     qt4projectconfigwidget.ui \
index 1b62988..fd670e6 100644 (file)
@@ -50,6 +50,7 @@
 #include "qtoptionspage.h"
 #include "externaleditors.h"
 #include "gettingstartedwelcomepage.h"
+#include "profilecompletion.h"
 
 #include "qt-maemo/maemomanager.h"
 #include "qt-s60/s60manager.h"
@@ -70,6 +71,7 @@
 #include <coreplugin/editormanager/editormanager.h>
 #include <texteditor/texteditoractionhandler.h>
 #include <texteditor/texteditorconstants.h>
+#include <texteditor/texteditorsettings.h>
 
 #ifdef WITH_TESTS
 #    include <QTest>
@@ -164,6 +166,14 @@ bool Qt4ProjectManagerPlugin::initialize(const QStringList &arguments, QString *
     addAutoReleasedObject(new S60Manager);
     addAutoReleasedObject(new MaemoManager);
 
+    ProFileCompletion *completion = new ProFileCompletion;
+    addAutoReleasedObject(completion);
+    // Set completion settings and keep them up to date
+    TextEditor::TextEditorSettings *textEditorSettings = TextEditor::TextEditorSettings::instance();
+    completion->setCompletionSettings(textEditorSettings->completionSettings());
+    connect(textEditorSettings, SIGNAL(completionSettingsChanged(TextEditor::CompletionSettings)),
+            completion, SLOT(setCompletionSettings(TextEditor::CompletionSettings)));
+
     new ProFileCacheManager(this);
 
     // TODO reenable