forked from qt-creator/qt-creator
Try to get the type of the qualified name id under cursor
This commit is contained in:
@@ -42,8 +42,7 @@ using namespace QmlJS::AST;
|
||||
Document::Document(const QString &fileName)
|
||||
: _engine(0)
|
||||
, _pool(0)
|
||||
, _uiProgram(0)
|
||||
, _jsProgram(0)
|
||||
, _ast(0)
|
||||
, _fileName(fileName)
|
||||
, _parsedCorrectly(false)
|
||||
{
|
||||
@@ -74,20 +73,17 @@ Document::Ptr Document::create(const QString &fileName)
|
||||
|
||||
AST::UiProgram *Document::qmlProgram() const
|
||||
{
|
||||
return _uiProgram;
|
||||
return cast<UiProgram *>(_ast);
|
||||
}
|
||||
|
||||
AST::Program *Document::jsProgram() const
|
||||
{
|
||||
return _jsProgram;
|
||||
return cast<Program *>(_ast);
|
||||
}
|
||||
|
||||
AST::Node *Document::ast() const
|
||||
{
|
||||
Q_ASSERT(!_uiProgram || !_jsProgram);
|
||||
if (_uiProgram)
|
||||
return _uiProgram;
|
||||
return _jsProgram;
|
||||
return _ast;
|
||||
}
|
||||
|
||||
QList<DiagnosticMessage> Document::diagnosticMessages() const
|
||||
@@ -109,8 +105,7 @@ bool Document::parseQml()
|
||||
{
|
||||
Q_ASSERT(! _engine);
|
||||
Q_ASSERT(! _pool);
|
||||
Q_ASSERT(! _uiProgram);
|
||||
Q_ASSERT(! _jsProgram);
|
||||
Q_ASSERT(! _ast);
|
||||
|
||||
_engine = new Engine();
|
||||
_pool = new NodePool(_fileName, _engine);
|
||||
@@ -122,11 +117,11 @@ bool Document::parseQml()
|
||||
lexer.setCode(_source, /*line = */ 1);
|
||||
|
||||
_parsedCorrectly = parser.parse();
|
||||
_uiProgram = parser.ast();
|
||||
_ast = parser.ast();
|
||||
_diagnosticMessages = parser.diagnosticMessages();
|
||||
|
||||
if (_uiProgram) {
|
||||
for (QmlJS::AST::UiObjectMemberList *iter = _uiProgram->members; iter; iter = iter->next)
|
||||
if (qmlProgram()) {
|
||||
for (QmlJS::AST::UiObjectMemberList *iter = qmlProgram()->members; iter; iter = iter->next)
|
||||
if (iter->member)
|
||||
_symbols.append(new SymbolFromFile(_fileName, iter->member));
|
||||
|
||||
@@ -143,8 +138,7 @@ bool Document::parseJavaScript()
|
||||
{
|
||||
Q_ASSERT(! _engine);
|
||||
Q_ASSERT(! _pool);
|
||||
Q_ASSERT(! _uiProgram);
|
||||
Q_ASSERT(! _jsProgram);
|
||||
Q_ASSERT(! _ast);
|
||||
|
||||
_engine = new Engine();
|
||||
_pool = new NodePool(_fileName, _engine);
|
||||
@@ -156,7 +150,31 @@ bool Document::parseJavaScript()
|
||||
lexer.setCode(_source, /*line = */ 1);
|
||||
|
||||
_parsedCorrectly = parser.parseProgram();
|
||||
_jsProgram = cast<Program*>(parser.rootNode());
|
||||
_ast = cast<Program*>(parser.rootNode());
|
||||
_diagnosticMessages = parser.diagnosticMessages();
|
||||
|
||||
return _parsedCorrectly;
|
||||
}
|
||||
|
||||
bool Document::parseExpression()
|
||||
{
|
||||
Q_ASSERT(! _engine);
|
||||
Q_ASSERT(! _pool);
|
||||
Q_ASSERT(! _ast);
|
||||
|
||||
_engine = new Engine();
|
||||
_pool = new NodePool(_fileName, _engine);
|
||||
_ids.clear();
|
||||
|
||||
Lexer lexer(_engine);
|
||||
Parser parser(_engine);
|
||||
|
||||
lexer.setCode(_source, /*line = */ 1);
|
||||
|
||||
_parsedCorrectly = parser.parseExpression();
|
||||
_ast = parser.rootNode();
|
||||
if (_ast)
|
||||
_ast = _ast->expressionCast();
|
||||
_diagnosticMessages = parser.diagnosticMessages();
|
||||
|
||||
return _parsedCorrectly;
|
||||
|
||||
@@ -67,6 +67,7 @@ public:
|
||||
|
||||
bool parseQml();
|
||||
bool parseJavaScript();
|
||||
bool parseExpression();
|
||||
|
||||
bool isParsedCorrectly() const
|
||||
{ return _parsedCorrectly; }
|
||||
@@ -84,8 +85,7 @@ public:
|
||||
private:
|
||||
QmlJS::Engine *_engine;
|
||||
QmlJS::NodePool *_pool;
|
||||
QmlJS::AST::UiProgram *_uiProgram;
|
||||
QmlJS::AST::Program *_jsProgram;
|
||||
QmlJS::AST::Node *_ast;
|
||||
QList<QmlJS::DiagnosticMessage> _diagnosticMessages;
|
||||
QString _fileName;
|
||||
QString _path;
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
|
||||
using namespace QmlJSEditor;
|
||||
using namespace QmlJSEditor::Internal;
|
||||
using namespace QmlJS;
|
||||
|
||||
|
||||
// Temporary workaround until we have proper icons for QML completion items
|
||||
@@ -83,6 +84,7 @@ static QIcon iconForColor(const QColor &color)
|
||||
return pix;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class Evaluate: public QmlJS::AST::Visitor
|
||||
{
|
||||
@@ -181,6 +183,46 @@ protected:
|
||||
|
||||
};
|
||||
|
||||
class EnumerateProperties
|
||||
{
|
||||
QSet<const Interpreter::ObjectValue *> _processed;
|
||||
QHash<QString, const Interpreter::Value *> _properties;
|
||||
|
||||
public:
|
||||
QHash<QString, const Interpreter::Value *> operator()(const Interpreter::Value *value)
|
||||
{
|
||||
_processed.clear();
|
||||
_properties.clear();
|
||||
enumerateProperties(value);
|
||||
return _properties;
|
||||
}
|
||||
|
||||
private:
|
||||
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());
|
||||
|
||||
for (Interpreter::ObjectValue::MemberIterator it = object->firstMember(); it != object->lastMember(); ++it) {
|
||||
_properties.insert(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // end of anonymous namespace
|
||||
|
||||
QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, QmlJS::TypeSystem *typeSystem, QObject *parent)
|
||||
: TextEditor::ICompletionCollector(parent),
|
||||
m_modelManager(modelManager),
|
||||
@@ -248,32 +290,33 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
||||
|
||||
const QIcon typeIcon = iconForColor(Qt::yellow);
|
||||
|
||||
foreach (QmlJS::Document::Ptr doc, snapshot) {
|
||||
const QFileInfo fileInfo(doc->fileName());
|
||||
|
||||
if (fileInfo.suffix() != QLatin1String("qml"))
|
||||
continue;
|
||||
else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME includ `imported' components
|
||||
continue;
|
||||
|
||||
const QString typeName = fileInfo.baseName();
|
||||
if (typeName.isEmpty())
|
||||
continue;
|
||||
|
||||
if (typeName.at(0).isUpper()) {
|
||||
TextEditor::CompletionItem item(this);
|
||||
item.text = typeName;
|
||||
item.icon = typeIcon;
|
||||
m_completions.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
QChar previousChar;
|
||||
if (m_startPosition > 0)
|
||||
previousChar = editor->characterAt(m_startPosition - 1);
|
||||
|
||||
if (previousChar.isSpace() || previousChar.isNull()) {
|
||||
// ### FIXME
|
||||
// Add the visible components to the completion box.
|
||||
foreach (QmlJS::Document::Ptr doc, snapshot) {
|
||||
const QFileInfo fileInfo(doc->fileName());
|
||||
|
||||
if (fileInfo.suffix() != QLatin1String("qml"))
|
||||
continue;
|
||||
else if (fileInfo.absolutePath() != currentFilePath) // ### FIXME includ `imported' components
|
||||
continue;
|
||||
|
||||
const QString typeName = fileInfo.baseName();
|
||||
if (typeName.isEmpty())
|
||||
continue;
|
||||
|
||||
if (typeName.at(0).isUpper()) {
|
||||
TextEditor::CompletionItem item(this);
|
||||
item.text = typeName;
|
||||
item.icon = typeIcon;
|
||||
m_completions.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the visible IDs to the completion box
|
||||
const QIcon idIcon = iconForColor(Qt::darkGray);
|
||||
QStringList ids = qmlDocument->ids().keys();
|
||||
foreach (const QString &id, ids) {
|
||||
@@ -287,54 +330,53 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// FIXME: this completion strategy is not going to work when the document was never parsed correctly.
|
||||
if (qmlDocument->qmlProgram() != 0) {
|
||||
const QIcon otherIcon = iconForColor(Qt::darkCyan);
|
||||
if (previousChar == QLatin1Char('.')) {
|
||||
const int endOfExpression = m_startPosition - 1;
|
||||
int startOfExpression = endOfExpression - 2;
|
||||
|
||||
// qDebug() << "*** program:" << program;
|
||||
QmlExpressionUnderCursor expressionUnderCursor;
|
||||
QTextCursor cursor(edit->document());
|
||||
cursor.setPosition(pos);
|
||||
expressionUnderCursor(cursor, qmlDocument);
|
||||
while (startOfExpression >= 0) {
|
||||
const QChar ch = editor->characterAt(startOfExpression);
|
||||
|
||||
QmlLookupContext context(expressionUnderCursor.expressionScopes(), qmlDocument, m_modelManager->snapshot(), m_typeSystem);
|
||||
QmlResolveExpression resolver(context);
|
||||
|
||||
QmlJS::AST::Node *expr = expressionUnderCursor.expressionNode();
|
||||
|
||||
QmlJS::Interpreter::Engine engine;
|
||||
Evaluate evaluate(&engine);
|
||||
if (const QmlJS::Interpreter::Value *value = evaluate(expr)) {
|
||||
if (const QmlJS::Interpreter::ObjectValue *object = value->asObjectValue()) {
|
||||
for (QmlJS::Interpreter::ObjectValue::MemberIterator it = object->firstMember(); it != object->lastMember(); ++it) {
|
||||
TextEditor::CompletionItem item(this);
|
||||
item.text = it.key();
|
||||
item.icon = otherIcon;
|
||||
m_completions.append(item);
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
if (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.'))
|
||||
--startOfExpression;
|
||||
else
|
||||
break;
|
||||
}
|
||||
++startOfExpression;
|
||||
|
||||
// qDebug()<<"*** expression under cursor:"<<expressionUnderCursor.expressionNode();
|
||||
const QList<QmlJS::Symbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
|
||||
// qDebug()<<"***"<<symbols.size()<<"visible symbols";
|
||||
const QString expression = m_editor->textAt(startOfExpression, endOfExpression - startOfExpression);
|
||||
//qDebug() << "expression:" << expression;
|
||||
|
||||
foreach (QmlJS::Symbol *symbol, symbols) {
|
||||
if (symbol->isIdSymbol())
|
||||
continue; // nothing to do here.
|
||||
QmlJS::Document::Ptr exprDoc = QmlJS::Document::create(QLatin1String("<expression>"));
|
||||
exprDoc->setSource(expression);
|
||||
exprDoc->parseExpression();
|
||||
|
||||
if (exprDoc->ast()) {
|
||||
Interpreter::Engine interp;
|
||||
Evaluate evaluate(&interp);
|
||||
|
||||
const Interpreter::Value *value = interp.convertToObject(evaluate(exprDoc->ast()));
|
||||
//qDebug() << "type:" << interp.typeId(value);
|
||||
|
||||
const QIcon symbolIcon = iconForColor(Qt::darkCyan);
|
||||
|
||||
EnumerateProperties enumerateProperties;
|
||||
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
|
||||
const QString word = symbol->name();
|
||||
if (! word.isEmpty()) {
|
||||
TextEditor::CompletionItem item(this);
|
||||
item.text = word;
|
||||
item.icon = otherIcon;
|
||||
item.text = it.key();
|
||||
item.icon = symbolIcon;
|
||||
m_completions.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_completions.isEmpty())
|
||||
return m_startPosition;
|
||||
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (previousChar.isNull()
|
||||
|| previousChar.isSpace()
|
||||
|
||||
Reference in New Issue
Block a user