New code assist API

This is a re-work of our completion engine. Primary goals are:

- Allow the computation to run in a separate thread so the GUI is not locked.
- Support a model-based approach. QStrings are still needed (filtering, etc), but
internal structures are free to use more efficient representations.
- Unifiy all kinds of *assist* into a more reusable and extensible framework.
- Remove unnecessary dependencies on the text editor so we have more generic
and easily "plugable" components (still things to be resolved).
This commit is contained in:
Leandro Melo
2011-04-15 16:19:23 +02:00
parent d835b769c7
commit bec4f02495
119 changed files with 9347 additions and 6595 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,114 +0,0 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJSCODECOMPLETION_H
#define QMLJSCODECOMPLETION_H
#include <qmljs/qmljsdocument.h>
#include <texteditor/icompletioncollector.h>
#include <texteditor/snippets/snippetcollector.h>
#include <QtCore/QDateTime>
#include <QtCore/QPointer>
namespace TextEditor {
class ITextEditor;
}
namespace QmlJS {
class ModelManagerInterface;
namespace Interpreter {
class Value;
}
}
namespace QmlJSEditor {
namespace Internal {
class FunctionArgumentWidget;
class CodeCompletion: public TextEditor::ICompletionCollector
{
Q_OBJECT
public:
explicit CodeCompletion(QmlJS::ModelManagerInterface *modelManager, QObject *parent = 0);
virtual ~CodeCompletion();
virtual TextEditor::ITextEditor *editor() const;
virtual int startPosition() const;
virtual bool shouldRestartCompletion();
virtual bool supportsEditor(TextEditor::ITextEditor *editor) const;
virtual bool supportsPolicy(TextEditor::CompletionPolicy policy) const;
virtual bool triggersCompletion(TextEditor::ITextEditor *editor);
virtual int startCompletion(TextEditor::ITextEditor *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 QList<TextEditor::CompletionItem> getCompletions();
virtual void sortCompletion(QList<TextEditor::CompletionItem> &completionItems);
virtual void cleanup();
private:
bool maybeTriggersCompletion(TextEditor::ITextEditor *editor);
bool isDelimiter(QChar ch) const;
bool completeUrl(const QString &relativeBasePath, const QString &urlString);
bool completeFileName(const QString &relativeBasePath, const QString &fileName,
const QStringList &patterns = QStringList());
void addCompletions(const QHash<QString, const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon, int relevance);
void addCompletions(const QStringList &newCompletions,
const QIcon &icon, int relevance);
void addCompletionsPropertyLhs(
const QHash<QString, const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon, int relevance, bool afterOn);
QmlJS::ModelManagerInterface *m_modelManager;
TextEditor::ITextEditor *m_editor;
int m_startPosition;
bool m_restartCompletion;
TextEditor::SnippetCollector m_snippetProvider;
QList<TextEditor::CompletionItem> m_completions;
QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLJSCODECOMPLETION_H

View File

@@ -0,0 +1,854 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljscompletionassist.h"
#include "qmljseditorconstants.h"
#include "qmljsreuse.h"
#include "qmlexpressionundercursor.h"
#include <coreplugin/ifile.h>
#include <texteditor/codeassist/iassistinterface.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/functionhintproposal.h>
#include <texteditor/codeassist/ifunctionhintproposalmodel.h>
#include <utils/qtcassert.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
#include <qmljs/parser/qmljsast_p.h>
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljslookupcontext.h>
#include <qmljs/qmljsscanner.h>
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljscompletioncontextfinder.h>
#include <qmljs/qmljsscopebuilder.h>
#include <QtCore/QFile>
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <QtCore/QtAlgorithms>
#include <QtCore/QDirIterator>
#include <QtCore/QStringList>
#include <QtGui/QIcon>
using namespace QmlJS;
using namespace QmlJSEditor;
using namespace Internal;
using namespace TextEditor;
namespace {
enum CompletionOrder {
EnumValueOrder = -5,
SnippetOrder = -15,
PropertyOrder = -10,
SymbolOrder = -20,
KeywordOrder = -25,
TypeOrder = -30
};
class EnumerateProperties: private Interpreter::MemberProcessor
{
QSet<const Interpreter::ObjectValue *> _processed;
QHash<QString, const Interpreter::Value *> _properties;
bool _globalCompletion;
bool _enumerateGeneratedSlots;
const Interpreter::Context *_context;
const Interpreter::ObjectValue *_currentObject;
public:
EnumerateProperties(const Interpreter::Context *context)
: _globalCompletion(false),
_enumerateGeneratedSlots(false),
_context(context),
_currentObject(0)
{
}
void setGlobalCompletion(bool globalCompletion)
{
_globalCompletion = globalCompletion;
}
void setEnumerateGeneratedSlots(bool enumerate)
{
_enumerateGeneratedSlots = enumerate;
}
QHash<QString, const Interpreter::Value *> operator ()(const Interpreter::Value *value)
{
_processed.clear();
_properties.clear();
_currentObject = Interpreter::value_cast<const Interpreter::ObjectValue *>(value);
enumerateProperties(value);
return _properties;
}
QHash<QString, const Interpreter::Value *> operator ()()
{
_processed.clear();
_properties.clear();
_currentObject = 0;
foreach (const Interpreter::ObjectValue *scope, _context->scopeChain().all())
enumerateProperties(scope);
return _properties;
}
private:
void insertProperty(const QString &name, const Interpreter::Value *value)
{
_properties.insert(name, value);
}
virtual bool processProperty(const QString &name, const Interpreter::Value *value)
{
insertProperty(name, value);
return true;
}
virtual bool processEnumerator(const QString &name, const Interpreter::Value *value)
{
if (! _globalCompletion)
insertProperty(name, value);
return true;
}
virtual bool processSignal(const QString &, const Interpreter::Value *)
{
return true;
}
virtual bool processSlot(const QString &name, const Interpreter::Value *value)
{
insertProperty(name, value);
return true;
}
virtual bool processGeneratedSlot(const QString &name, const Interpreter::Value *value)
{
if (_enumerateGeneratedSlots || (_currentObject && _currentObject->className().endsWith(QLatin1String("Keys")))) {
// ### FIXME: add support for attached properties.
insertProperty(name, value);
}
return true;
}
void enumerateProperties(const Interpreter::Value *value)
{
if (! value)
return;
else if (const Interpreter::ObjectValue *object = value->asObjectValue()) {
enumerateProperties(object);
}
}
void enumerateProperties(const Interpreter::ObjectValue *object)
{
if (! object || _processed.contains(object))
return;
_processed.insert(object);
enumerateProperties(object->prototype(_context));
object->processMembers(this);
}
};
const Interpreter::Value *getPropertyValue(const Interpreter::ObjectValue *object,
const QStringList &propertyNames,
const Interpreter::Context *context)
{
if (propertyNames.isEmpty() || !object)
return 0;
const Interpreter::Value *value = object;
foreach (const QString &name, propertyNames) {
if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
value = objectValue->property(name, context);
if (!value)
return 0;
} else {
return 0;
}
}
return value;
}
bool isLiteral(AST::Node *ast)
{
if (AST::cast<AST::StringLiteral *>(ast))
return true;
else if (AST::cast<AST::NumericLiteral *>(ast))
return true;
else
return false;
}
} // Anonymous
// -----------------------
// QmlJSAssistProposalItem
// -----------------------
bool QmlJSAssistProposalItem::prematurelyApplies(const QChar &c) const
{
if (data().canConvert<QString>()) // snippet
return false;
return (text().endsWith(QLatin1String(": ")) && c == QLatin1Char(':'))
|| (text().endsWith(QLatin1Char('.')) && c == QLatin1Char('.'));
}
void QmlJSAssistProposalItem::applyContextualContent(TextEditor::BaseTextEditor *editor,
int basePosition) const
{
const int currentPosition = editor->position();
editor->setCursorPosition(basePosition);
editor->remove(currentPosition - basePosition);
QString replaceable;
const QString &content = text();
if (content.endsWith(QLatin1String(": ")))
replaceable = QLatin1String(": ");
else if (content.endsWith(QLatin1Char('.')))
replaceable = QLatin1String(".");
int replacedLength = 0;
for (int i = 0; i < replaceable.length(); ++i) {
const QChar a = replaceable.at(i);
const QChar b = editor->characterAt(editor->position() + i);
if (a == b)
++replacedLength;
else
break;
}
const int length = editor->position() - basePosition + replacedLength;
editor->replace(length, content);
}
// -------------------------
// FunctionHintProposalModel
// -------------------------
class FunctionHintProposalModel : public TextEditor::IFunctionHintProposalModel
{
public:
FunctionHintProposalModel(const QString &functionName, const QStringList &signature)
: m_functionName(functionName)
, m_signature(signature)
, m_minimumArgumentCount(signature.size())
{}
virtual void reset() {}
virtual int size() const { return 1; }
virtual QString text(int index) const;
virtual int activeArgument(const QString &prefix) const;
private:
QString m_functionName;
QStringList m_signature;
int m_minimumArgumentCount;
};
QString FunctionHintProposalModel::text(int index) const
{
Q_UNUSED(index)
QString prettyMethod;
prettyMethod += QString::fromLatin1("function ");
prettyMethod += m_functionName;
prettyMethod += QLatin1Char('(');
for (int i = 0; i < m_minimumArgumentCount; ++i) {
if (i != 0)
prettyMethod += QLatin1String(", ");
QString arg = m_signature.at(i);
if (arg.isEmpty()) {
arg = QLatin1String("arg");
arg += QString::number(i + 1);
}
prettyMethod += arg;
}
prettyMethod += QLatin1Char(')');
return prettyMethod;
}
int FunctionHintProposalModel::activeArgument(const QString &prefix) const
{
int argnr = 0;
int parcount = 0;
Scanner tokenize;
const QList<Token> tokens = tokenize(prefix);
for (int i = 0; i < tokens.count(); ++i) {
const Token &tk = tokens.at(i);
if (tk.is(Token::LeftParenthesis))
++parcount;
else if (tk.is(Token::RightParenthesis))
--parcount;
else if (! parcount && tk.is(Token::Colon))
++argnr;
}
if (parcount < 0)
return -1;
return argnr;
}
// -----------------------------
// QmlJSCompletionAssistProvider
// -----------------------------
bool QmlJSCompletionAssistProvider::supportsEditor(const QString &editorId) const
{
return editorId == QLatin1String(Constants::C_QMLJSEDITOR_ID);
}
int QmlJSCompletionAssistProvider::activationCharSequenceLength() const
{
return 1;
}
bool QmlJSCompletionAssistProvider::isActivationCharSequence(const QString &sequence) const
{
return isActivationChar(sequence.at(0));
}
bool QmlJSCompletionAssistProvider::isContinuationChar(const QChar &c) const
{
return isIdentifierChar(c, false);
}
IAssistProcessor *QmlJSCompletionAssistProvider::createProcessor() const
{
return new QmlJSCompletionAssistProcessor;
}
// ------------------------------
// QmlJSCompletionAssistProcessor
// ------------------------------
QmlJSCompletionAssistProcessor::QmlJSCompletionAssistProcessor()
: m_startPosition(0)
, m_snippetCollector(Constants::QML_SNIPPETS_GROUP_ID, iconForColor(Qt::red), SnippetOrder)
{}
QmlJSCompletionAssistProcessor::~QmlJSCompletionAssistProcessor()
{}
IAssistProposal *QmlJSCompletionAssistProcessor::createContentProposal() const
{
IGenericProposalModel *model = new QmlJSAssistProposalModel(m_completions);
IAssistProposal *proposal = new GenericProposal(m_startPosition, model);
return proposal;
}
IAssistProposal *QmlJSCompletionAssistProcessor::createHintProposal(const QString &functionName,
const QStringList &signature) const
{
IFunctionHintProposalModel *model = new FunctionHintProposalModel(functionName, signature);
IAssistProposal *proposal = new FunctionHintProposal(m_startPosition, model);
return proposal;
}
IAssistProposal *QmlJSCompletionAssistProcessor::perform(const IAssistInterface *assistInterface)
{
m_interface.reset(static_cast<const QmlJSCompletionAssistInterface *>(assistInterface));
if (assistInterface->reason() == IdleEditor && !acceptsIdleEditor())
return 0;
const QString &fileName = m_interface->file()->fileName();
m_startPosition = assistInterface->position();
while (isIdentifierChar(m_interface->document()->characterAt(m_startPosition - 1), false, false))
--m_startPosition;
m_completions.clear();
const QmlJSCompletionAssistInterface *qmlInterface =
static_cast<const QmlJSCompletionAssistInterface *>(assistInterface);
const SemanticInfo &semanticInfo = qmlInterface->semanticInfo();
if (!semanticInfo.isValid())
return 0;
const Document::Ptr document = semanticInfo.document;
const QFileInfo currentFileInfo(fileName);
bool isQmlFile = false;
if (currentFileInfo.suffix() == QLatin1String("qml"))
isQmlFile = true;
const QList<AST::Node *> path = semanticInfo.astPath(m_interface->position());
LookupContext::Ptr lookupContext = semanticInfo.lookupContext(path);
const Interpreter::Context *context = lookupContext->context();
// Search for the operator that triggered the completion.
QChar completionOperator;
if (m_startPosition > 0)
completionOperator = m_interface->document()->characterAt(m_startPosition - 1);
QTextCursor startPositionCursor(qmlInterface->document());
startPositionCursor.setPosition(m_startPosition);
CompletionContextFinder contextFinder(startPositionCursor);
const Interpreter::ObjectValue *qmlScopeType = 0;
if (contextFinder.isInQmlContext()) {
// ### this should use semanticInfo.declaringMember instead, but that may also return functions
for (int i = path.size() - 1; i >= 0; --i) {
AST::Node *node = path[i];
if (AST::cast<AST::UiObjectDefinition *>(node) || AST::cast<AST::UiObjectBinding *>(node)) {
qmlScopeType = document->bind()->findQmlObject(node);
if (qmlScopeType)
break;
}
}
// fallback to getting the base type object
if (!qmlScopeType)
qmlScopeType = context->lookupType(document.data(), contextFinder.qmlObjectTypeName());
}
if (contextFinder.isInStringLiteral()) {
// get the text of the literal up to the cursor position
//QTextCursor tc = textWidget->textCursor();
QTextCursor tc(qmlInterface->document());
tc.setPosition(qmlInterface->position());
QmlExpressionUnderCursor expressionUnderCursor;
expressionUnderCursor(tc);
QString literalText = expressionUnderCursor.text();
QTC_ASSERT(!literalText.isEmpty() && (
literalText.at(0) == QLatin1Char('"')
|| literalText.at(0) == QLatin1Char('\'')), return 0);
literalText = literalText.mid(1);
if (contextFinder.isInImport()) {
QStringList patterns;
patterns << QLatin1String("*.qml") << QLatin1String("*.js");
if (completeFileName(document->path(), literalText, patterns))
return createContentProposal();
return 0;
}
const Interpreter::Value *value =
getPropertyValue(qmlScopeType, contextFinder.bindingPropertyName(), context);
if (!value) {
// do nothing
} else if (value->asUrlValue()) {
if (completeUrl(document->path(), literalText))
return createContentProposal();
}
// ### enum completion?
// completion gets triggered for / in string literals, if we don't
// return here, this will mean the snippet completion pops up for
// each / in a string literal that is not triggering file completion
return 0;
} else if (completionOperator.isSpace()
|| completionOperator.isNull()
|| isDelimiterChar(completionOperator)
|| (completionOperator == QLatin1Char('(')
&& m_startPosition != m_interface->position())) {
bool doGlobalCompletion = true;
bool doQmlKeywordCompletion = true;
bool doJsKeywordCompletion = true;
bool doQmlTypeCompletion = false;
if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
doGlobalCompletion = false;
doJsKeywordCompletion = false;
doQmlTypeCompletion = true;
EnumerateProperties enumerateProperties(context);
enumerateProperties.setGlobalCompletion(true);
enumerateProperties.setEnumerateGeneratedSlots(true);
// id: is special
BasicProposalItem *idProposalItem = new QmlJSAssistProposalItem;
idProposalItem->setText(QLatin1String("id: "));
idProposalItem->setIcon(m_interface->symbolIcon());
idProposalItem->setOrder(PropertyOrder);
m_completions.append(idProposalItem);
addCompletionsPropertyLhs(enumerateProperties(qmlScopeType),
m_interface->symbolIcon(),
PropertyOrder,
contextFinder.isAfterOnInLhsOfBinding());
if (ScopeBuilder::isPropertyChangesObject(context, qmlScopeType)
&& context->scopeChain().qmlScopeObjects.size() == 2) {
addCompletions(enumerateProperties(context->scopeChain().qmlScopeObjects.first()),
m_interface->symbolIcon(),
SymbolOrder);
}
}
if (contextFinder.isInRhsOfBinding() && qmlScopeType) {
doQmlKeywordCompletion = false;
// complete enum values for enum properties
const Interpreter::Value *value =
getPropertyValue(qmlScopeType, contextFinder.bindingPropertyName(), context);
if (const Interpreter::QmlEnumValue *enumValue =
dynamic_cast<const Interpreter::QmlEnumValue *>(value)) {
foreach (const QString &key, enumValue->keys())
addCompletion(key, m_interface->symbolIcon(),
EnumValueOrder, QString("\"%1\"").arg(key));
}
}
if (!contextFinder.isInImport() && !contextFinder.isInQmlContext())
doQmlTypeCompletion = true;
if (doQmlTypeCompletion) {
if (const Interpreter::ObjectValue *qmlTypes = context->scopeChain().qmlTypes) {
EnumerateProperties enumerateProperties(context);
addCompletions(enumerateProperties(qmlTypes), m_interface->symbolIcon(), TypeOrder);
}
}
if (doGlobalCompletion) {
// It's a global completion.
EnumerateProperties enumerateProperties(context);
enumerateProperties.setGlobalCompletion(true);
addCompletions(enumerateProperties(), m_interface->symbolIcon(), SymbolOrder);
}
if (doJsKeywordCompletion) {
// add js keywords
addCompletions(Scanner::keywords(), m_interface->keywordIcon(), KeywordOrder);
}
// add qml extra words
if (doQmlKeywordCompletion && isQmlFile) {
static QStringList qmlWords;
static QStringList qmlWordsAlsoInJs;
if (qmlWords.isEmpty()) {
qmlWords << QLatin1String("property")
//<< QLatin1String("readonly")
<< QLatin1String("signal")
<< QLatin1String("import");
}
if (qmlWordsAlsoInJs.isEmpty())
qmlWordsAlsoInJs << QLatin1String("default") << QLatin1String("function");
addCompletions(qmlWords, m_interface->keywordIcon(), KeywordOrder);
if (!doJsKeywordCompletion)
addCompletions(qmlWordsAlsoInJs, m_interface->keywordIcon(), KeywordOrder);
}
}
else if (completionOperator == QLatin1Char('.') || completionOperator == QLatin1Char('(')) {
// Look at the expression under cursor.
//QTextCursor tc = textWidget->textCursor();
QTextCursor tc(qmlInterface->document());
tc.setPosition(m_startPosition - 1);
QmlExpressionUnderCursor expressionUnderCursor;
QmlJS::AST::ExpressionNode *expression = expressionUnderCursor(tc);
if (expression != 0 && ! isLiteral(expression)) {
// Evaluate the expression under cursor.
Interpreter::Engine *interp = lookupContext->engine();
const Interpreter::Value *value =
interp->convertToObject(lookupContext->evaluate(expression));
//qDebug() << "type:" << interp.typeId(value);
if (value && completionOperator == QLatin1Char('.')) { // member completion
EnumerateProperties enumerateProperties(context);
if (contextFinder.isInLhsOfBinding() && qmlScopeType) {
enumerateProperties.setEnumerateGeneratedSlots(true);
addCompletionsPropertyLhs(enumerateProperties(value),
m_interface->symbolIcon(),
PropertyOrder,
contextFinder.isAfterOnInLhsOfBinding());
} else
addCompletions(enumerateProperties(value), m_interface->symbolIcon(), SymbolOrder);
} else if (value
&& completionOperator == QLatin1Char('(')
&& m_startPosition == m_interface->position()) {
// function completion
if (const Interpreter::FunctionValue *f = value->asFunctionValue()) {
QString functionName = expressionUnderCursor.text();
int indexOfDot = functionName.lastIndexOf(QLatin1Char('.'));
if (indexOfDot != -1)
functionName = functionName.mid(indexOfDot + 1);
QStringList signature;
for (int i = 0; i < f->argumentCount(); ++i)
signature.append(f->argumentName(i));
return createHintProposal(functionName.trimmed(), signature);
}
}
}
if (! m_completions.isEmpty())
return createContentProposal();
return 0;
}
if (isQmlFile
&& (completionOperator.isNull()
|| completionOperator.isSpace()
|| isDelimiterChar(completionOperator))) {
m_completions.append(m_snippetCollector.collect());
}
if (! m_completions.isEmpty())
return createContentProposal();
return 0;
}
bool QmlJSCompletionAssistProcessor::acceptsIdleEditor() const
{
const int cursorPos = m_interface->position();
bool maybeAccept = false;
const QChar &charBeforeCursor = m_interface->document()->characterAt(cursorPos - 1);
if (isActivationChar(charBeforeCursor)) {
maybeAccept = true;
} else {
const QChar &charUnderCursor = m_interface->document()->characterAt(cursorPos);
if (isIdentifierChar(charBeforeCursor)
&& ((charUnderCursor.isSpace()
|| charUnderCursor.isNull()
|| isDelimiterChar(charUnderCursor))
|| isIdentifierChar(charUnderCursor))) {
int startPos = cursorPos - 1;
for (; startPos != -1; --startPos) {
if (!isIdentifierChar(m_interface->document()->characterAt(startPos)))
break;
}
++startPos;
const QString &word = m_interface->textAt(startPos, cursorPos - startPos);
if (word.length() > 2 && isIdentifierChar(word.at(0), true)) {
for (int i = 1; i < word.length(); ++i) {
if (!isIdentifierChar(word.at(i)))
return false;
}
maybeAccept = true;
}
}
}
if (maybeAccept) {
QTextCursor tc(m_interface->document());
tc.setPosition(m_interface->position());
const QTextBlock &block = tc.block();
const QString &blockText = block.text();
const int blockState = qMax(0, block.previous().userState()) & 0xff;
Scanner scanner;
const QList<Token> tokens = scanner(blockText, blockState);
const int column = block.position() - m_interface->position();
foreach (const Token &tk, tokens) {
if (column >= tk.begin() && column <= tk.end()) {
if (charBeforeCursor == QLatin1Char('/') && tk.is(Token::String))
return true; // path completion inside string literals
if (tk.is(Token::Comment) || tk.is(Token::String))
return false;
break;
}
}
if (charBeforeCursor != QLatin1Char('/'))
return true;
}
return false;
}
bool QmlJSCompletionAssistProcessor::completeFileName(const QString &relativeBasePath,
const QString &fileName,
const QStringList &patterns)
{
const QFileInfo fileInfo(fileName);
QString directoryPrefix;
if (fileInfo.isRelative()) {
directoryPrefix = relativeBasePath;
directoryPrefix += QDir::separator();
directoryPrefix += fileInfo.path();
} else {
directoryPrefix = fileInfo.path();
}
if (!QFileInfo(directoryPrefix).exists())
return false;
QDirIterator dirIterator(directoryPrefix,
patterns,
QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot);
while (dirIterator.hasNext()) {
dirIterator.next();
const QString fileName = dirIterator.fileName();
BasicProposalItem *item = new QmlJSAssistProposalItem;
item->setText(fileName);
item->setIcon(m_interface->fileNameIcon());
m_completions.append(item);
}
return !m_completions.isEmpty();
}
bool QmlJSCompletionAssistProcessor::completeUrl(const QString &relativeBasePath, const QString &urlString)
{
const QUrl url(urlString);
QString fileName = url.toLocalFile();
if (fileName.isEmpty())
return false;
return completeFileName(relativeBasePath, fileName);
}
void QmlJSCompletionAssistProcessor::addCompletionsPropertyLhs(const QHash<QString,
const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon,
int order,
bool afterOn)
{
QHashIterator<QString, const Interpreter::Value *> it(newCompletions);
while (it.hasNext()) {
it.next();
QString itemText = it.key();
QLatin1String postfix(": ");
if (afterOn)
postfix = QLatin1String(" {");
if (const Interpreter::QmlObjectValue *qmlValue =
dynamic_cast<const Interpreter::QmlObjectValue *>(it.value())) {
// to distinguish "anchors." from "gradient:" we check if the right hand side
// type is instantiatable or is the prototype of an instantiatable object
if (qmlValue->hasChildInPackage())
itemText.append(postfix);
else
itemText.append(QLatin1Char('.'));
} else {
itemText.append(postfix);
}
addCompletion(itemText, icon, order);
}
}
void QmlJSCompletionAssistProcessor::addCompletion(const QString &text,
const QIcon &icon,
int order,
const QVariant &data)
{
if (text.isEmpty())
return;
BasicProposalItem *item = new QmlJSAssistProposalItem;
item->setText(text);
item->setIcon(icon);
item->setOrder(order);
item->setData(data);
m_completions.append(item);
}
void QmlJSCompletionAssistProcessor::addCompletions(const QHash<QString,
const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon,
int order)
{
QHashIterator<QString, const Interpreter::Value *> it(newCompletions);
while (it.hasNext()) {
it.next();
addCompletion(it.key(), icon, order);
}
}
void QmlJSCompletionAssistProcessor::addCompletions(const QStringList &newCompletions,
const QIcon &icon,
int order)
{
foreach (const QString &text, newCompletions)
addCompletion(text, icon, order);
}
// ------------------------------
// QmlJSCompletionAssistInterface
// ------------------------------
QmlJSCompletionAssistInterface::QmlJSCompletionAssistInterface(QTextDocument *document,
int position,
Core::IFile *file,
TextEditor::AssistReason reason,
const SemanticInfo &info)
: DefaultAssistInterface(document, position, file, reason)
, m_semanticInfo(info)
, m_darkBlueIcon(iconForColor(Qt::darkBlue))
, m_darkYellowIcon(iconForColor(Qt::darkYellow))
, m_darkCyanIcon(iconForColor(Qt::darkCyan))
{}
const SemanticInfo &QmlJSCompletionAssistInterface::semanticInfo() const
{
return m_semanticInfo;
}
namespace {
struct QmlJSLessThan
{
bool operator() (const BasicProposalItem *a, const BasicProposalItem *b)
{
if (a->order() != b->order())
return a->order() > b->order();
else if (a->text().isEmpty())
return true;
else if (b->text().isEmpty())
return false;
else if (a->data().isValid() != b->data().isValid())
return a->data().isValid();
else if (a->text().at(0).isUpper() && b->text().at(0).isLower())
return false;
else if (a->text().at(0).isLower() && b->text().at(0).isUpper())
return true;
return a->text() < b->text();
}
};
} // Anonymous
// -------------------------
// QmlJSAssistProposalModel
// -------------------------
void QmlJSAssistProposalModel::sort()
{
qSort(currentItems().first, currentItems().second, QmlJSLessThan());
}

View File

@@ -0,0 +1,158 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJSCOMPLETIONASSIST_H
#define QMLJSCOMPLETIONASSIST_H
#include "qmljseditor.h"
#include <texteditor/codeassist/basicproposalitem.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <texteditor/codeassist/completionassistprovider.h>
#include <texteditor/codeassist/iassistprocessor.h>
#include <texteditor/snippets/snippetassistcollector.h>
#include <texteditor/codeassist/defaultassistinterface.h>
#include <QtCore/QStringList>
#include <QtCore/QScopedPointer>
#include <QtCore/QVariant>
#include <QtGui/QIcon>
namespace QmlJS {
namespace Interpreter {
class Value;
}
}
namespace QmlJSEditor {
namespace Internal {
class QmlJSCompletionAssistInterface;
class QmlJSAssistProposalItem : public TextEditor::BasicProposalItem
{
public:
virtual bool prematurelyApplies(const QChar &c) const;
virtual void applyContextualContent(TextEditor::BaseTextEditor *editor,
int basePosition) const;
};
class QmlJSAssistProposalModel : public TextEditor::BasicProposalItemListModel
{
public:
QmlJSAssistProposalModel(const QList<TextEditor::BasicProposalItem *> &items)
: TextEditor::BasicProposalItemListModel(items)
{}
virtual void sort();
};
class QmlJSCompletionAssistProvider : public TextEditor::CompletionAssistProvider
{
public:
virtual bool supportsEditor(const QString &editorId) const;
virtual TextEditor::IAssistProcessor *createProcessor() const;
virtual int activationCharSequenceLength() const;
virtual bool isActivationCharSequence(const QString &sequence) const;
virtual bool isContinuationChar(const QChar &c) const;
};
class QmlJSCompletionAssistProcessor : public TextEditor::IAssistProcessor
{
public:
QmlJSCompletionAssistProcessor();
virtual ~QmlJSCompletionAssistProcessor();
virtual TextEditor::IAssistProposal *perform(const TextEditor::IAssistInterface *interface);
private:
TextEditor::IAssistProposal *createContentProposal() const;
TextEditor::IAssistProposal *createHintProposal(const QString &functionName,
const QStringList &signature) const;
bool acceptsIdleEditor() const;
bool completeUrl(const QString &relativeBasePath, const QString &urlString);
bool completeFileName(const QString &relativeBasePath,
const QString &fileName,
const QStringList &patterns = QStringList());
void addCompletion(const QString &text,
const QIcon &icon,
int order,
const QVariant &data = QVariant());
void addCompletions(const QHash<QString, const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon,
int order);
void addCompletions(const QStringList &newCompletions, const QIcon &icon, int order);
void addCompletionsPropertyLhs(const QHash<QString,
const QmlJS::Interpreter::Value *> &newCompletions,
const QIcon &icon,
int order,
bool afterOn);
int m_startPosition;
QScopedPointer<const QmlJSCompletionAssistInterface> m_interface;
QList<TextEditor::BasicProposalItem *> m_completions;
TextEditor::SnippetAssistCollector m_snippetCollector;
const TextEditor::IAssistProvider *m_provider;
};
class QmlJSCompletionAssistInterface : public TextEditor::DefaultAssistInterface
{
public:
QmlJSCompletionAssistInterface(QTextDocument *document,
int position,
Core::IFile *file,
TextEditor::AssistReason reason,
const SemanticInfo &info);
const SemanticInfo &semanticInfo() const;
const QIcon &fileNameIcon() const { return m_darkBlueIcon; }
const QIcon &keywordIcon() const { return m_darkYellowIcon; }
const QIcon &symbolIcon() const { return m_darkCyanIcon; }
private:
SemanticInfo m_semanticInfo;
QIcon m_darkBlueIcon;
QIcon m_darkYellowIcon;
QIcon m_darkCyanIcon;
};
} // Internal
} // QmlJSEditor
#endif // QMLJSCOMPLETIONASSIST_H

View File

@@ -32,6 +32,7 @@
#include "qmljscomponentfromobjectdef.h"
#include "qmljscomponentnamedialog.h"
#include "qmljsquickfixassist.h"
#include <coreplugin/ifile.h>
@@ -93,8 +94,9 @@ class Operation: public QmlJSQuickFixOperation
QString m_idName, m_componentName;
public:
Operation(const QmlJSQuickFixState &state, UiObjectDefinition *objDef)
: QmlJSQuickFixOperation(state, 0)
Operation(const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface,
UiObjectDefinition *objDef)
: QmlJSQuickFixOperation(interface, 0)
, m_objDef(objDef)
{
Q_ASSERT(m_objDef != 0);
@@ -117,7 +119,7 @@ public:
QString componentName = m_componentName;
QString path = QFileInfo(fileName()).path();
if (componentName.isEmpty()) {
ComponentNameDialog::go(&componentName, &path, state().editor());
ComponentNameDialog::go(&componentName, &path, assistInterface()->widget());
}
if (componentName.isEmpty() || path.isEmpty())
@@ -157,19 +159,21 @@ public:
} // end of anonymous namespace
QList<QmlJSQuickFixOperation::Ptr> ComponentFromObjectDef::match(const QmlJSQuickFixState &state)
{
const int pos = state.currentFile().cursor().position();
QList<Node *> path = state.semanticInfo().astPath(pos);
QList<QmlJSQuickFixOperation::Ptr> ComponentFromObjectDef::match(
const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface)
{
const int pos = interface->currentFile().cursor().position();
QList<Node *> path = interface->semanticInfo().astPath(pos);
for (int i = path.size() - 1; i >= 0; --i) {
Node *node = path.at(i);
if (UiObjectDefinition *objDef = cast<UiObjectDefinition *>(node)) {
if (!state.currentFile().isCursorOn(objDef->qualifiedTypeNameId))
if (!interface->currentFile().isCursorOn(objDef->qualifiedTypeNameId))
return noResult();
// check that the node is not the root node
if (i > 0 && !cast<UiProgram*>(path.at(i - 1))) {
return singleResult(new Operation(state, objDef));
return singleResult(new Operation(interface, objDef));
}
}
}

View File

@@ -41,7 +41,8 @@ namespace Internal {
class ComponentFromObjectDef: public QmlJSQuickFixFactory
{
public:
virtual QList<QmlJSQuickFixOperation::Ptr> match(const QmlJSQuickFixState &state);
virtual QList<QmlJSQuickFixOperation::Ptr> match(
const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface);
};
} // namespace Internal

View File

@@ -35,12 +35,13 @@
#include "qmljseditorconstants.h"
#include "qmljshighlighter.h"
#include "qmljseditorplugin.h"
#include "qmljsquickfix.h"
#include "qmloutlinemodel.h"
#include "qmljsfindreferences.h"
#include "qmljssemantichighlighter.h"
#include "qmljsindenter.h"
#include "qmljsautocompleter.h"
#include "qmljscompletionassist.h"
#include "qmljsquickfixassist.h"
#include <qmljs/qmljsbind.h>
#include <qmljs/qmljsevaluate.h>
@@ -70,6 +71,8 @@
#include <texteditor/syntaxhighlighter.h>
#include <texteditor/refactoroverlay.h>
#include <texteditor/tooltip/tooltip.h>
#include <texteditor/codeassist/genericproposal.h>
#include <texteditor/codeassist/basicproposalitemlistmodel.h>
#include <qmldesigner/qmldesignerconstants.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <utils/changeset.h>
@@ -78,6 +81,7 @@
#include <QtCore/QFileInfo>
#include <QtCore/QSignalMapper>
#include <QtCore/QTimer>
#include <QtCore/QScopedPointer>
#include <QtGui/QMenu>
#include <QtGui/QComboBox>
@@ -1347,20 +1351,29 @@ void QmlJSTextEditorWidget::contextMenuEvent(QContextMenuEvent *e)
connect(a, SIGNAL(triggered()), this, SLOT(renameIdUnderCursor()));
}
// Add other refactoring actions:
QmlJSQuickFixCollector *quickFixCollector = QmlJSEditorPlugin::instance()->quickFixCollector();
QSignalMapper mapper;
connect(&mapper, SIGNAL(mapped(int)), this, SLOT(performQuickFix(int)));
if (! isOutdated()) {
if (quickFixCollector->startCompletion(editor()) != -1) {
m_quickFixes = quickFixCollector->quickFixes();
for (int index = 0; index < m_quickFixes.size(); ++index) {
TextEditor::QuickFixOperation::Ptr op = m_quickFixes.at(index);
QAction *action = refactoringMenu->addAction(op->description());
mapper.setMapping(action, index);
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
TextEditor::IAssistInterface *interface =
createAssistInterface(TextEditor::QuickFix, TextEditor::ExplicitlyInvoked);
if (interface) {
QScopedPointer<TextEditor::IAssistProcessor> processor(
QmlJSEditorPlugin::instance()->quickFixAssistProvider()->createProcessor());
QScopedPointer<TextEditor::IAssistProposal> proposal(processor->perform(interface));
if (!proposal.isNull()) {
TextEditor::BasicProposalItemListModel *model =
static_cast<TextEditor::BasicProposalItemListModel *>(proposal->model());
for (int index = 0; index < model->size(); ++index) {
TextEditor::BasicProposalItem *item =
static_cast<TextEditor::BasicProposalItem *>(model->proposalItem(index));
TextEditor::QuickFixOperation::Ptr op =
item->data().value<TextEditor::QuickFixOperation::Ptr>();
m_quickFixes.append(op);
QAction *action = refactoringMenu->addAction(op->description());
mapper.setMapping(action, index);
connect(action, SIGNAL(triggered()), &mapper, SLOT(map()));
}
delete model;
}
}
}
@@ -1380,7 +1393,6 @@ void QmlJSTextEditorWidget::contextMenuEvent(QContextMenuEvent *e)
menu->exec(e->globalPos());
menu->deleteLater();
quickFixCollector->cleanup();
m_quickFixes.clear();
}
@@ -1578,3 +1590,19 @@ SemanticHighlighterSource QmlJSTextEditorWidget::currentSource(bool force)
source.force = force;
return source;
}
TextEditor::IAssistInterface *QmlJSTextEditorWidget::createAssistInterface(
TextEditor::AssistKind assistKind,
TextEditor::AssistReason reason) const
{
if (assistKind == TextEditor::Completion) {
return new QmlJSCompletionAssistInterface(document(),
position(),
editor()->file(),
reason,
m_semanticInfo);
} else if (assistKind == TextEditor::QuickFix) {
return new QmlJSQuickFixAssistInterface(const_cast<QmlJSTextEditorWidget *>(this), reason);
}
return 0;
}

View File

@@ -160,6 +160,9 @@ public:
static QVector<QString> highlighterFormatCategories();
TextEditor::IAssistInterface *createAssistInterface(TextEditor::AssistKind assistKind,
TextEditor::AssistReason reason) const;
public slots:
void forceSemanticRehighlight();
void followSymbolUnderCursor();

View File

@@ -8,7 +8,6 @@ DEFINES += \
QT_CREATOR
HEADERS += \
qmljscodecompletion.h \
qmljseditor.h \
qmljseditor_global.h \
qmljseditoractionhandler.h \
@@ -20,7 +19,6 @@ HEADERS += \
qmljshighlighter.h \
qmljshoverhandler.h \
qmljspreviewrunner.h \
qmljsquickfix.h \
qmljscomponentfromobjectdef.h \
qmljsoutline.h \
qmloutlinemodel.h \
@@ -35,10 +33,13 @@ HEADERS += \
qmljsindenter.h \
qmljsautocompleter.h \
jsfilewizard.h \
qmljssnippetprovider.h
qmljssnippetprovider.h \
qmljsreuse.h \
qmljsquickfixassist.h \
qmljscompletionassist.h \
qmljsquickfix.h
SOURCES += \
qmljscodecompletion.cpp \
qmljseditor.cpp \
qmljseditoractionhandler.cpp \
qmljseditorfactory.cpp \
@@ -48,7 +49,6 @@ SOURCES += \
qmljshighlighter.cpp \
qmljshoverhandler.cpp \
qmljspreviewrunner.cpp \
qmljsquickfix.cpp \
qmljscomponentfromobjectdef.cpp \
qmljsoutline.cpp \
qmloutlinemodel.cpp \
@@ -64,7 +64,11 @@ SOURCES += \
qmljsindenter.cpp \
qmljsautocompleter.cpp \
jsfilewizard.cpp \
qmljssnippetprovider.cpp
qmljssnippetprovider.cpp \
qmljsreuse.cpp \
qmljsquickfixassist.cpp \
qmljscompletionassist.cpp \
qmljsquickfix.cpp
RESOURCES += qmljseditor.qrc
OTHER_FILES += QmlJSEditor.mimetypes.xml

View File

@@ -35,17 +35,17 @@
#include "qmljseditor.h"
#include "qmljseditorconstants.h"
#include "qmljseditorfactory.h"
#include "qmljscodecompletion.h"
#include "qmljshoverhandler.h"
#include "qmlfilewizard.h"
#include "jsfilewizard.h"
#include "qmljsoutline.h"
#include "qmljspreviewrunner.h"
#include "qmljsquickfix.h"
#include "qmljssnippetprovider.h"
#include "qmltaskmanager.h"
#include "quicktoolbar.h"
#include "quicktoolbarsettingspage.h"
#include "qmljscompletionassist.h"
#include "qmljsquickfixassist.h"
#include <qmljs/qmljsicons.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
@@ -69,7 +69,6 @@
#include <texteditor/texteditorsettings.h>
#include <texteditor/textfilewizard.h>
#include <texteditor/texteditoractionhandler.h>
#include <texteditor/completionsupport.h>
#include <utils/qtcassert.h>
#include <QtCore/QtPlugin>
@@ -89,21 +88,18 @@ enum {
QUICKFIX_INTERVAL = 20
};
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn);
QmlJSEditorPlugin *QmlJSEditorPlugin::m_instance = 0;
QmlJSEditorPlugin::QmlJSEditorPlugin() :
m_modelManager(0),
m_wizard(0),
m_editor(0),
m_actionHandler(0)
m_actionHandler(0),
m_quickFixAssistProvider(0)
{
m_instance = this;
m_quickFixCollector = 0;
m_quickFixTimer = new QTimer(this);
m_quickFixTimer->setInterval(20);
m_quickFixTimer->setSingleShot(true);
connect(m_quickFixTimer, SIGNAL(timeout()), this, SLOT(quickFixNow()));
}
QmlJSEditorPlugin::~QmlJSEditorPlugin()
@@ -211,25 +207,18 @@ bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *e
cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
contextMenu->addAction(cmd);
CodeCompletion *completion = new CodeCompletion(m_modelManager);
addAutoReleasedObject(completion);
m_quickFixAssistProvider = new QmlJSQuickFixAssistProvider;
addAutoReleasedObject(m_quickFixAssistProvider);
addAutoReleasedObject(new QmlJSCompletionAssistProvider);
addAutoReleasedObject(new HoverHandler);
// 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)));
error_message->clear();
Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance();
iconProvider->registerIconOverlayForSuffix(QIcon(QLatin1String(":/qmljseditor/images/qmlfile.png")), "qml");
m_quickFixCollector = new QmlJSQuickFixCollector;
addAutoReleasedObject(m_quickFixCollector);
QmlJSQuickFixCollector::registerQuickFixes(this);
registerQuickFixes(this);
addAutoReleasedObject(new QmlJSOutlineWidgetFactory);
@@ -313,35 +302,9 @@ Core::Command *QmlJSEditorPlugin::addToolAction(QAction *a, Core::ActionManager
return command;
}
QmlJSQuickFixCollector *QmlJSEditorPlugin::quickFixCollector() const
{ return m_quickFixCollector; }
void QmlJSEditorPlugin::quickFix(TextEditor::ITextEditor *editable)
QmlJSQuickFixAssistProvider *QmlJSEditorPlugin::quickFixAssistProvider() const
{
m_currentTextEditable = editable;
quickFixNow();
}
void QmlJSEditorPlugin::quickFixNow()
{
if (! m_currentTextEditable)
return;
Core::EditorManager *em = Core::EditorManager::instance();
QmlJSTextEditorWidget *currentEditor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget());
if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(m_currentTextEditable->widget())) {
if (currentEditor == editor) {
if (editor->isOutdated()) {
// qDebug() << "TODO: outdated document" << editor->editorRevision() << editor->semanticInfo().revision();
// ### FIXME: m_quickFixTimer->start(QUICKFIX_INTERVAL);
m_quickFixTimer->stop();
} else {
TextEditor::CompletionSupport::instance()
->complete(m_currentTextEditable, TextEditor::QuickFixCompletion, true);
}
}
}
return m_quickFixAssistProvider;
}
void QmlJSEditorPlugin::currentEditorChanged(Core::IEditor *editor)

View File

@@ -68,7 +68,7 @@ namespace Internal {
class QmlJSEditorFactory;
class QmlJSPreviewRunner;
class QmlJSQuickFixCollector;
class QmlJSQuickFixAssistProvider;
class QmlTaskManager;
class QmlJSEditorPlugin : public ExtensionSystem::IPlugin
@@ -87,7 +87,7 @@ public:
static QmlJSEditorPlugin *instance()
{ return m_instance; }
QmlJSQuickFixCollector *quickFixCollector() const;
QmlJSQuickFixAssistProvider *quickFixAssistProvider() const;
void initializeEditor(QmlJSEditor::QmlJSTextEditorWidget *editor);
@@ -97,8 +97,6 @@ public Q_SLOTS:
void showContextPane();
private Q_SLOTS:
void quickFix(TextEditor::ITextEditor *editable);
void quickFixNow();
void currentEditorChanged(Core::IEditor *editor);
private:
@@ -115,9 +113,8 @@ private:
QmlJSEditorFactory *m_editor;
TextEditor::TextEditorActionHandler *m_actionHandler;
QmlJSQuickFixCollector *m_quickFixCollector;
QmlJSQuickFixAssistProvider *m_quickFixAssistProvider;
QTimer *m_quickFixTimer;
QPointer<TextEditor::ITextEditor> m_currentTextEditable;
QmlTaskManager *m_qmlTaskManager;
};

View File

@@ -56,8 +56,6 @@ class QmlJSTextEditorWidget;
namespace Internal {
class SemanticInfo;
class HoverHandler : public TextEditor::BaseHoverHandler
{
Q_OBJECT

View File

@@ -34,6 +34,7 @@
#include "qmljscomponentfromobjectdef.h"
#include "qmljseditor.h"
#include "qmljs/parser/qmljsast_p.h"
#include "qmljsquickfixassist.h"
#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginmanager.h>
@@ -50,34 +51,11 @@ using namespace QmlJSTools;
using namespace TextEditor;
using TextEditor::RefactoringChanges;
QmlJSQuickFixState::QmlJSQuickFixState(TextEditor::BaseTextEditorWidget *editor)
: QuickFixState(editor)
{
}
SemanticInfo QmlJSQuickFixState::semanticInfo() const
{
return _semanticInfo;
}
Snapshot QmlJSQuickFixState::snapshot() const
{
return _semanticInfo.snapshot;
}
Document::Ptr QmlJSQuickFixState::document() const
{
return _semanticInfo.document;
}
const QmlJSRefactoringFile QmlJSQuickFixState::currentFile() const
{
return QmlJSRefactoringFile(editor(), document());
}
QmlJSQuickFixOperation::QmlJSQuickFixOperation(const QmlJSQuickFixState &state, int priority)
QmlJSQuickFixOperation::QmlJSQuickFixOperation(
const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface,
int priority)
: QuickFixOperation(priority)
, _state(state)
, m_interface(interface)
{
}
@@ -88,20 +66,21 @@ QmlJSQuickFixOperation::~QmlJSQuickFixOperation()
void QmlJSQuickFixOperation::perform()
{
QmlJSRefactoringChanges refactoring(ExtensionSystem::PluginManager::instance()->getObject<QmlJS::ModelManagerInterface>(),
_state.snapshot());
//_state.snapshot());
m_interface->semanticInfo().snapshot);
QmlJSRefactoringFile current = refactoring.file(fileName());
performChanges(&current, &refactoring);
}
const QmlJSQuickFixState &QmlJSQuickFixOperation::state() const
const QmlJSQuickFixAssistInterface *QmlJSQuickFixOperation::assistInterface() const
{
return _state;
return m_interface.data();
}
QString QmlJSQuickFixOperation::fileName() const
{
return state().document()->fileName();
return m_interface->semanticInfo().document->fileName();
}
QmlJSQuickFixFactory::QmlJSQuickFixFactory()
@@ -112,12 +91,10 @@ QmlJSQuickFixFactory::~QmlJSQuickFixFactory()
{
}
QList<QuickFixOperation::Ptr> QmlJSQuickFixFactory::matchingOperations(QuickFixState *state)
QList<QuickFixOperation::Ptr> QmlJSQuickFixFactory::matchingOperations(
const QSharedPointer<const TextEditor::IAssistInterface> &interface)
{
if (QmlJSQuickFixState *qmljsState = static_cast<QmlJSQuickFixState *>(state))
return match(*qmljsState);
else
return QList<TextEditor::QuickFixOperation::Ptr>();
return match(interface.staticCast<const QmlJSQuickFixAssistInterface>());
}
QList<QmlJSQuickFixOperation::Ptr> QmlJSQuickFixFactory::noResult()
@@ -131,49 +108,3 @@ QList<QmlJSQuickFixOperation::Ptr> QmlJSQuickFixFactory::singleResult(QmlJSQuick
result.append(QmlJSQuickFixOperation::Ptr(operation));
return result;
}
QmlJSQuickFixCollector::QmlJSQuickFixCollector()
{
}
QmlJSQuickFixCollector::~QmlJSQuickFixCollector()
{
}
bool QmlJSQuickFixCollector::supportsEditor(TextEditor::ITextEditor *editable) const
{
return qobject_cast<QmlJSTextEditorWidget *>(editable->widget()) != 0;
}
bool QmlJSQuickFixCollector::supportsPolicy(TextEditor::CompletionPolicy policy) const
{
return policy == TextEditor::QuickFixCompletion;
}
TextEditor::QuickFixState *QmlJSQuickFixCollector::initializeCompletion(TextEditor::BaseTextEditorWidget *editor)
{
if (QmlJSTextEditorWidget *qmljsEditor = qobject_cast<QmlJSTextEditorWidget *>(editor)) {
const SemanticInfo info = qmljsEditor->semanticInfo();
if (! info.isValid() || qmljsEditor->isOutdated()) {
// outdated
qWarning() << "TODO: outdated semantic info, force a reparse.";
return 0;
}
QmlJSQuickFixState *state = new QmlJSQuickFixState(editor);
state->_semanticInfo = info;
return state;
}
return 0;
}
QList<TextEditor::QuickFixFactory *> QmlJSQuickFixCollector::quickFixFactories() const
{
QList<TextEditor::QuickFixFactory *> results;
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
foreach (QmlJSQuickFixFactory *f, pm->getObjects<QmlJSEditor::QmlJSQuickFixFactory>())
results.append(f);
return results;
}

View File

@@ -40,6 +40,8 @@
#include <qmljs/qmljsdocument.h>
#include <qmljstools/qmljsrefactoringchanges.h>
#include <QtCore/QSharedPointer>
namespace ExtensionSystem {
class IPlugin;
}
@@ -51,40 +53,12 @@ namespace QmlJS {
namespace QmlJSEditor {
namespace Internal {
class QmlJSQuickFixCollector;
class QmlJSQuickFixAssistInterface;
} // namespace Internal
/*!
Specialized QuickFixState for QML/JavaScript quick-fixes.
This specialized state for QML/JavaScript quick-fixes also holds the
QmlJSEditor::Internal::SemanticInfo for the document in the editor.
*/
class QmlJSQuickFixState: public TextEditor::QuickFixState
{
friend class Internal::QmlJSQuickFixCollector;
public:
/// Creates a new state for the given editor.
QmlJSQuickFixState(TextEditor::BaseTextEditorWidget *editor);
SemanticInfo semanticInfo() const;
/// \returns the snapshot holding the document of the editor.
QmlJS::Snapshot snapshot() const;
/// \returns the document of the editor
QmlJS::Document::Ptr document() const;
const QmlJSTools::QmlJSRefactoringFile currentFile() const;
private:
SemanticInfo _semanticInfo;
};
/*!
A quick-fix operation for the QML/JavaScript editor, which works on a
QmlJSQuickFixState .
A quick-fix operation for the QML/JavaScript editor.
*/
class QmlJSQuickFixOperation: public TextEditor::QuickFixOperation
{
@@ -94,13 +68,12 @@ public:
/*!
Creates a new QmlJSQuickFixOperation.
This operation will copy the complete state, in order to be able to perform
its changes later on.
\param state The state for which this operation was created.
\param interface The interface on which the operation is performed.
\param priority The priority for this operation.
*/
explicit QmlJSQuickFixOperation(const QmlJSQuickFixState &state, int priority = -1);
explicit QmlJSQuickFixOperation(
const QSharedPointer<const Internal::QmlJSQuickFixAssistInterface> &interface,
int priority = -1);
virtual ~QmlJSQuickFixOperation();
virtual void perform();
@@ -111,14 +84,13 @@ protected:
virtual void performChanges(QmlJSTools::QmlJSRefactoringFile *currentFile,
QmlJSTools::QmlJSRefactoringChanges *refactoring) = 0;
/// \returns A const-reference to the state of the operation.
const QmlJSQuickFixState &state() const;
const Internal::QmlJSQuickFixAssistInterface *assistInterface() const;
/// \returns The name of the file for for which this operation is invoked.
QString fileName() const;
private:
QmlJSQuickFixState _state;
QSharedPointer<const Internal::QmlJSQuickFixAssistInterface> m_interface;
};
class QmlJSQuickFixFactory: public TextEditor::QuickFixFactory
@@ -129,39 +101,20 @@ public:
QmlJSQuickFixFactory();
virtual ~QmlJSQuickFixFactory();
virtual QList<TextEditor::QuickFixOperation::Ptr> matchingOperations(TextEditor::QuickFixState *state);
virtual QList<TextEditor::QuickFixOperation::Ptr>
matchingOperations(const QSharedPointer<const TextEditor::IAssistInterface> &interface);
/*!
Implement this method to match and create the appropriate
QmlJSQuickFixOperation objects.
*/
virtual QList<QmlJSQuickFixOperation::Ptr> match(const QmlJSQuickFixState &state) = 0;
virtual QList<QmlJSQuickFixOperation::Ptr> match(
const QSharedPointer<const Internal::QmlJSQuickFixAssistInterface> &interface) = 0;
static QList<QmlJSQuickFixOperation::Ptr> noResult();
static QList<QmlJSQuickFixOperation::Ptr> singleResult(QmlJSQuickFixOperation *operation);
};
namespace Internal {
class QmlJSQuickFixCollector: public TextEditor::QuickFixCollector
{
Q_OBJECT
public:
QmlJSQuickFixCollector();
virtual ~QmlJSQuickFixCollector();
virtual bool supportsEditor(TextEditor::ITextEditor *editor) const;
virtual bool supportsPolicy(TextEditor::CompletionPolicy policy) const;
virtual TextEditor::QuickFixState *initializeCompletion(TextEditor::BaseTextEditorWidget *editor);
virtual QList<TextEditor::QuickFixFactory *> quickFixFactories() const;
/// Registers all quick-fixes in this plug-in as auto-released objects.
static void registerQuickFixes(ExtensionSystem::IPlugin *plugIn);
};
} // namespace Internal
} // namespace QmlJSEditor
#endif // QMLJSQUICKFIX_H

View File

@@ -0,0 +1,115 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljsquickfixassist.h"
#include "qmljseditorconstants.h"
//temp
#include "qmljsquickfix.h"
#include <extensionsystem/pluginmanager.h>
using namespace QmlJSEditor;
using namespace Internal;
using namespace QmlJSTools;
using namespace TextEditor;
// -----------------------
// QuickFixAssistInterface
// -----------------------
QmlJSQuickFixAssistInterface::QmlJSQuickFixAssistInterface(QmlJSTextEditorWidget *editor,
TextEditor::AssistReason reason)
: DefaultAssistInterface(editor->document(), editor->position(), editor->file(), reason)
, m_editor(editor)
, m_semanticInfo(editor->semanticInfo())
{}
QmlJSQuickFixAssistInterface::~QmlJSQuickFixAssistInterface()
{}
const SemanticInfo &QmlJSQuickFixAssistInterface::semanticInfo() const
{
return m_semanticInfo;
}
const QmlJSTools::QmlJSRefactoringFile QmlJSQuickFixAssistInterface::currentFile() const
{
return QmlJSRefactoringFile(m_editor, m_semanticInfo.document);
}
QWidget *QmlJSQuickFixAssistInterface::widget() const
{
return m_editor;
}
// ----------------------
// QmlJSQuickFixProcessor
// ----------------------
QmlJSQuickFixProcessor::QmlJSQuickFixProcessor(const TextEditor::IAssistProvider *provider)
: m_provider(provider)
{}
QmlJSQuickFixProcessor::~QmlJSQuickFixProcessor()
{}
const IAssistProvider *QmlJSQuickFixProcessor::provider() const
{
return m_provider;
}
// ---------------------------
// QmlJSQuickFixAssistProvider
// ---------------------------
QmlJSQuickFixAssistProvider::QmlJSQuickFixAssistProvider()
{}
QmlJSQuickFixAssistProvider::~QmlJSQuickFixAssistProvider()
{}
bool QmlJSQuickFixAssistProvider::supportsEditor(const QString &editorId) const
{
return editorId == QLatin1String(Constants::C_QMLJSEDITOR_ID);
}
IAssistProcessor *QmlJSQuickFixAssistProvider::createProcessor() const
{
return new QmlJSQuickFixProcessor(this);
}
QList<QuickFixFactory *> QmlJSQuickFixAssistProvider::quickFixFactories() const
{
QList<TextEditor::QuickFixFactory *> results;
ExtensionSystem::PluginManager *pm = ExtensionSystem::PluginManager::instance();
foreach (QmlJSQuickFixFactory *f, pm->getObjects<QmlJSEditor::QmlJSQuickFixFactory>())
results.append(f);
return results;
}

View File

@@ -0,0 +1,91 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJSQUICKFIXASSIST_H
#define QMLJSQUICKFIXASSIST_H
#include "qmljseditor.h"
#include <qmljstools/qmljsrefactoringchanges.h>
#include <texteditor/codeassist/defaultassistinterface.h>
#include <texteditor/codeassist/quickfixassistprovider.h>
#include <texteditor/codeassist/quickfixassistprocessor.h>
namespace QmlJSEditor {
namespace Internal {
class QmlJSQuickFixAssistInterface : public TextEditor::DefaultAssistInterface
{
public:
QmlJSQuickFixAssistInterface(QmlJSTextEditorWidget *editor, TextEditor::AssistReason reason);
virtual ~QmlJSQuickFixAssistInterface();
const SemanticInfo &semanticInfo() const;
const QmlJSTools::QmlJSRefactoringFile currentFile() const;
QWidget *widget() const;
private:
QmlJSTextEditorWidget *m_editor;
SemanticInfo m_semanticInfo;
};
class QmlJSQuickFixProcessor : public TextEditor::QuickFixAssistProcessor
{
public:
QmlJSQuickFixProcessor(const TextEditor::IAssistProvider *provider);
virtual ~QmlJSQuickFixProcessor();
virtual const TextEditor::IAssistProvider *provider() const;
private:
const TextEditor::IAssistProvider *m_provider;
};
class QmlJSQuickFixAssistProvider : public TextEditor::QuickFixAssistProvider
{
public:
QmlJSQuickFixAssistProvider();
virtual ~QmlJSQuickFixAssistProvider();
virtual bool supportsEditor(const QString &editorId) const;
virtual TextEditor::IAssistProcessor *createProcessor() const;
virtual QList<TextEditor::QuickFixFactory *> quickFixFactories() const;
};
} // Internal
} // QmlJSEditor
#endif // QMLJSQUICKFIXASSIST_H

View File

@@ -33,6 +33,7 @@
#include "qmljsquickfix.h"
#include "qmljscomponentfromobjectdef.h"
#include "qmljseditor.h"
#include "qmljsquickfixassist.h"
#include <extensionsystem/iplugin.h>
#include <extensionsystem/pluginmanager.h>
@@ -64,13 +65,14 @@ namespace {
class SplitInitializerOp: public QmlJSQuickFixFactory
{
public:
virtual QList<QmlJSQuickFixOperation::Ptr> match(const QmlJSQuickFixState &state)
virtual QList<QmlJSQuickFixOperation::Ptr> match(
const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface)
{
UiObjectInitializer *objectInitializer = 0;
const int pos = state.currentFile().cursor().position();
const int pos = interface->currentFile().cursor().position();
if (QmlJS::AST::Node *member = state.semanticInfo().declaringMember(pos)) {
if (QmlJS::AST::Node *member = interface->semanticInfo().declaringMember(pos)) {
if (QmlJS::AST::UiObjectBinding *b = QmlJS::AST::cast<QmlJS::AST::UiObjectBinding *>(member)) {
if (b->initializer->lbraceToken.startLine == b->initializer->rbraceToken.startLine)
objectInitializer = b->initializer;
@@ -82,7 +84,7 @@ public:
}
if (objectInitializer)
return singleResult(new Operation(state, objectInitializer));
return singleResult(new Operation(interface, objectInitializer));
else
return noResult();
}
@@ -93,8 +95,9 @@ private:
UiObjectInitializer *_objectInitializer;
public:
Operation(const QmlJSQuickFixState &state, UiObjectInitializer *objectInitializer)
: QmlJSQuickFixOperation(state, 0)
Operation(const QSharedPointer<const QmlJSQuickFixAssistInterface> &interface,
UiObjectInitializer *objectInitializer)
: QmlJSQuickFixOperation(interface, 0)
, _objectInitializer(objectInitializer)
{
setDescription(QApplication::translate("QmlJSEditor::QuickFix",
@@ -129,7 +132,7 @@ private:
} // end of anonymous namespace
void QmlJSQuickFixCollector::registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn)
{
plugIn->addAutoReleasedObject(new SplitInitializerOp);
plugIn->addAutoReleasedObject(new ComponentFromObjectDef);

View File

@@ -0,0 +1,122 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#include "qmljsreuse.h"
#include <QtCore/QChar>
#include <QtGui/QPainter>
namespace QmlJSEditor {
namespace Internal {
bool isIdentifierChar(const QChar &c, bool atStart, bool acceptDollar)
{
switch (c.unicode()) {
case '_':
return true;
case '$':
if (acceptDollar)
return true;
return false;
default:
if (atStart)
return c.isLetter();
else
return c.isLetterOrNumber();
}
}
bool isDelimiterChar(const QChar &c)
{
switch (c.unicode()) {
case '{':
case '}':
case '[':
case ']':
case ')':
case '?':
case '!':
case ':':
case ';':
case ',':
case '+':
case '-':
case '*':
case '/':
return true;
default:
return false;
}
}
bool isActivationChar(const QChar &c)
{
if (c == QLatin1Char('(') || c == QLatin1Char('.') || c == QLatin1Char('/'))
return true;
return false;
}
QIcon iconForColor(const QColor &color)
{
QPixmap pix(6, 6);
int pixSize = 20;
QBrush br(color);
QPixmap pm(2 * pixSize, 2 * pixSize);
QPainter pmp(&pm);
pmp.fillRect(0, 0, pixSize, pixSize, Qt::lightGray);
pmp.fillRect(pixSize, pixSize, pixSize, pixSize, Qt::lightGray);
pmp.fillRect(0, pixSize, pixSize, pixSize, Qt::darkGray);
pmp.fillRect(pixSize, 0, pixSize, pixSize, Qt::darkGray);
pmp.fillRect(0, 0, 2 * pixSize, 2 * pixSize, color);
br = QBrush(pm);
QPainter p(&pix);
int corr = 1;
QRect r = pix.rect().adjusted(corr, corr, -corr, -corr);
p.setBrushOrigin((r.width() % pixSize + pixSize) / 2 + corr, (r.height() % pixSize + pixSize) / 2 + corr);
p.fillRect(r, br);
p.fillRect(r.width() / 4 + corr, r.height() / 4 + corr,
r.width() / 2, r.height() / 2,
QColor(color.rgb()));
p.drawRect(pix.rect().adjusted(0, 0, -1, -1));
return pix;
}
} // Internal
} // QmlJSEditor

View File

@@ -0,0 +1,55 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/
#ifndef QMLJSREUSE_H
#define QMLJSREUSE_H
#include <QtCore/QtGlobal>
#include <QtGui/QIcon>
QT_BEGIN_NAMESPACE
class QChar;
QT_END_NAMESPACE
namespace QmlJSEditor {
namespace Internal {
bool isIdentifierChar(const QChar &c, bool atStart = false, bool acceptDollar = true);
bool isDelimiterChar(const QChar &c);
bool isActivationChar(const QChar &c);
QIcon iconForColor(const QColor &color);
} // Internal
} // QmlJSEditor
#endif // QMLJSREUSE_H