Commit afa7aec5 authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Simple code completion for QtScript.

parent 6635ddbd
......@@ -77,6 +77,9 @@ public:
: _lexer(0), _nodePool(0), _ast(0)
{ }
QSet<JavaScriptNameIdImpl> literals() const
{ return _literals; }
JavaScriptNameIdImpl *intern(const QChar *u, int s)
{ return const_cast<JavaScriptNameIdImpl *>(&*_literals.insert(JavaScriptNameIdImpl(u, s))); }
......
#include "qtscriptcodecompletion.h"
#include "qtscripteditor.h"
#include <texteditor/basetexteditor.h>
#include <QtDebug>
using namespace QtScriptEditor::Internal;
QtScriptCodeCompletion::QtScriptCodeCompletion(QObject *parent)
: TextEditor::ICompletionCollector(parent),
m_editor(0),
m_startPosition(0),
m_caseSensitivity(Qt::CaseSensitive)
{ }
QtScriptCodeCompletion::~QtScriptCodeCompletion()
{ }
Qt::CaseSensitivity QtScriptCodeCompletion::caseSensitivity() const
{ return m_caseSensitivity; }
void QtScriptCodeCompletion::setCaseSensitivity(Qt::CaseSensitivity caseSensitivity)
{ m_caseSensitivity = caseSensitivity; }
bool QtScriptCodeCompletion::isValid(TextEditor::ITextEditable *editor)
{
if (qobject_cast<ScriptEditor *>(editor->widget()))
return true;
return false;
}
bool QtScriptCodeCompletion::triggersCompletion(TextEditor::ITextEditable *)
{ return false; }
int QtScriptCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
{
m_editor = editor;
ScriptEditor *edit = qobject_cast<ScriptEditor *>(m_editor->widget());
if (! edit)
return -1;
int pos = editor->position();
while (editor->characterAt(pos - 1).isLetterOrNumber() || editor->characterAt(pos - 1) == QLatin1Char('_'))
--pos;
m_startPosition = pos;
m_completions.clear();
foreach (const QString &word, edit->words()) {
TextEditor::CompletionItem item(this);
item.m_text = word;
m_completions.append(item);
}
return pos;
}
void QtScriptCodeCompletion::completions(QList<TextEditor::CompletionItem> *completions)
{
// ### FIXME: this code needs to be generalized.
const int length = m_editor->position() - m_startPosition;
if (length == 0)
*completions = m_completions;
else if (length > 0) {
const QString key = m_editor->textAt(m_startPosition, length);
/*
* This code builds a regular expression in order to more intelligently match
* camel-case style. This means upper-case characters will be rewritten as follows:
*
* A => [a-z0-9_]*A (for any but the first capital letter)
*
* Meaning it allows any sequence of lower-case characters to preceed an
* upper-case character. So for example gAC matches getActionController.
*/
QString keyRegExp;
keyRegExp += QLatin1Char('^');
bool first = true;
foreach (const QChar &c, key) {
if (c.isUpper() && !first) {
keyRegExp += QLatin1String("[a-z0-9_]*");
keyRegExp += c;
} else if (m_caseSensitivity == Qt::CaseInsensitive && c.isLower()) {
keyRegExp += QLatin1Char('[');
keyRegExp += c;
keyRegExp += c.toUpper();
keyRegExp += QLatin1Char(']');
} else {
keyRegExp += QRegExp::escape(c);
}
first = false;
}
const QRegExp regExp(keyRegExp, Qt::CaseSensitive);
foreach (TextEditor::CompletionItem item, m_completions) {
if (regExp.indexIn(item.m_text) == 0) {
item.m_relevance = (key.length() > 0 &&
item.m_text.startsWith(key, Qt::CaseInsensitive)) ? 1 : 0;
(*completions) << item;
}
}
}
}
void QtScriptCodeCompletion::complete(const TextEditor::CompletionItem &item)
{
const QString toInsert = item.m_text;
const int length = m_editor->position() - m_startPosition;
m_editor->setCurPos(m_startPosition);
m_editor->replace(length, toInsert);
}
bool QtScriptCodeCompletion::partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems)
{
if (completionItems.count() == 1) {
complete(completionItems.first());
return true;
} else {
// Compute common prefix
QString firstKey = completionItems.first().m_text;
QString lastKey = completionItems.last().m_text;
const int length = qMin(firstKey.length(), lastKey.length());
firstKey.truncate(length);
lastKey.truncate(length);
while (firstKey != lastKey) {
firstKey.chop(1);
lastKey.chop(1);
}
int typedLength = m_editor->position() - m_startPosition;
if (!firstKey.isEmpty() && firstKey.length() > typedLength) {
m_editor->setCurPos(m_startPosition);
m_editor->replace(typedLength, firstKey);
}
}
return false;
}
void QtScriptCodeCompletion::cleanup()
{
m_editor = 0;
m_startPosition = 0;
m_completions.clear();
}
#ifndef QTSCRIPTCODECOMPLETION_H
#define QTSCRIPTCODECOMPLETION_H
#include <texteditor/icompletioncollector.h>
namespace TextEditor {
class ITextEditable;
}
namespace QtScriptEditor {
namespace Internal {
class QtScriptCodeCompletion: public TextEditor::ICompletionCollector
{
Q_OBJECT
public:
QtScriptCodeCompletion(QObject *parent = 0);
virtual ~QtScriptCodeCompletion();
Qt::CaseSensitivity caseSensitivity() const;
void setCaseSensitivity(Qt::CaseSensitivity caseSensitivity);
virtual bool isValid(TextEditor::ITextEditable *editor);
virtual bool triggersCompletion(TextEditor::ITextEditable *editor);
virtual int startCompletion(TextEditor::ITextEditable *editor);
virtual void completions(QList<TextEditor::CompletionItem> *completions);
virtual void complete(const TextEditor::CompletionItem &item);
virtual bool partiallyComplete(const QList<TextEditor::CompletionItem> &completionItems);
virtual void cleanup();
private:
TextEditor::ITextEditable *m_editor;
int m_startPosition;
QList<TextEditor::CompletionItem> m_completions;
Qt::CaseSensitivity m_caseSensitivity;
};
} // end of namespace Internal
} // end of namespace QtScriptEditor
#endif // QTSCRIPTCODECOMPLETION_H
......@@ -166,6 +166,9 @@ ScriptEditor::~ScriptEditor()
QList<Declaration> ScriptEditor::declarations() const
{ return m_declarations; }
QStringList ScriptEditor::words() const
{ return m_words; }
Core::IEditor *ScriptEditorEditable::duplicate(QWidget *parent)
{
ScriptEditor *newEditor = new ScriptEditor(m_context, parent);
......@@ -215,6 +218,10 @@ void ScriptEditor::updateDocumentNow()
FindDeclarations decls;
m_declarations = decls.accept(driver.ast());
m_words.clear();
foreach (const JavaScriptNameIdImpl &id, driver.literals())
m_words.append(id.asString());
QStringList items;
items.append(tr("<Select Symbol>"));
......
......@@ -91,6 +91,7 @@ public:
~ScriptEditor();
QList<Declaration> declarations() const;
QStringList words() const;
public slots:
virtual void setFontSettings(const TextEditor::FontSettings &);
......@@ -117,6 +118,7 @@ private:
QTimer *m_updateDocumentTimer;
QComboBox *m_methodCombo;
QList<Declaration> m_declarations;
QStringList m_words;
};
} // namespace Internal
......
......@@ -13,12 +13,14 @@ HEADERS += qtscripteditor.h \
qtscripteditorfactory.h \
qtscripteditorplugin.h \
qtscripthighlighter.h \
qtscripteditoractionhandler.h
qtscripteditoractionhandler.h \
qtscriptcodecompletion.h
SOURCES += qtscripteditor.cpp \
qtscripteditorfactory.cpp \
qtscripteditorplugin.cpp \
qtscripthighlighter.cpp \
qtscripteditoractionhandler.cpp
qtscripteditoractionhandler.cpp \
qtscriptcodecompletion.cpp
RESOURCES += qtscripteditor.qrc
......@@ -33,6 +33,7 @@
#include "qtscripteditor.h"
#include "qtscripteditorconstants.h"
#include "qtscripteditorfactory.h"
#include "qtscriptcodecompletion.h"
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
......@@ -46,10 +47,12 @@
#include <texteditor/texteditorsettings.h>
#include <texteditor/textfilewizard.h>
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/completionsupport.h>
#include <utils/qtcassert.h>
#include <QtCore/QtPlugin>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
#include <QtGui/QAction>
using namespace QtScriptEditor::Internal;
......@@ -60,7 +63,8 @@ QtScriptEditorPlugin *QtScriptEditorPlugin::m_instance = 0;
QtScriptEditorPlugin::QtScriptEditorPlugin() :
m_wizard(0),
m_editor(0),
m_actionHandler(0)
m_actionHandler(0),
m_completion(0)
{
m_instance = this;
}
......@@ -105,6 +109,18 @@ bool QtScriptEditorPlugin::initialize(const QStringList & /*arguments*/, QString
| TextEditor::TextEditorActionHandler::UnCommentSelection
| TextEditor::TextEditorActionHandler::UnCollapseAll);
m_completion = new QtScriptCodeCompletion();
addAutoReleasedObject(m_completion);
// Restore settings
QSettings *settings = Core::ICore::instance()->settings();
settings->beginGroup(QLatin1String("CppTools")); // ### FIXME:
settings->beginGroup(QLatin1String("Completion"));
const bool caseSensitive = settings->value(QLatin1String("CaseSensitive"), true).toBool();
m_completion->setCaseSensitivity(caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive);
settings->endGroup();
settings->endGroup();
error_message->clear();
return true;
......@@ -122,6 +138,10 @@ void QtScriptEditorPlugin::initializeEditor(QtScriptEditor::Internal::ScriptEdit
m_actionHandler->setupActions(editor);
TextEditor::TextEditorSettings::instance()->initializeEditor(editor);
// auto completion
connect(editor, SIGNAL(requestAutoCompletion(ITextEditable*, bool)),
TextEditor::Internal::CompletionSupport::instance(), SLOT(autoComplete(ITextEditable*, bool)));
}
void QtScriptEditorPlugin::registerActions()
......
......@@ -41,6 +41,7 @@ namespace QtScriptEditor {
namespace Internal {
class QtScriptEditorFactory;
class QtScriptCodeCompletion;
class ScriptEditor;
class QtScriptEditorPlugin : public ExtensionSystem::IPlugin
......@@ -72,6 +73,7 @@ private:
TextEditor::TextFileWizard *m_wizard;
QtScriptEditorFactory *m_editor;
TextEditor::TextEditorActionHandler *m_actionHandler;
QtScriptCodeCompletion *m_completion;
};
} // namespace Internal
......
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