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

Try to get the type of the qualified name id under cursor

parent 4b97bf17
......@@ -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);
if (ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.'))
--startOfExpression;
else
break;
}
++startOfExpression;
QmlJS::AST::Node *expr = expressionUnderCursor.expressionNode();
const QString expression = m_editor->textAt(startOfExpression, endOfExpression - startOfExpression);
//qDebug() << "expression:" << expression;
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;
}
}
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);
// qDebug()<<"*** expression under cursor:"<<expressionUnderCursor.expressionNode();
const QList<QmlJS::Symbol*> symbols = resolver.visibleSymbols(expressionUnderCursor.expressionNode());
// qDebug()<<"***"<<symbols.size()<<"visible symbols";
const QIcon symbolIcon = iconForColor(Qt::darkCyan);
foreach (QmlJS::Symbol *symbol, symbols) {
if (symbol->isIdSymbol())
continue; // nothing to do here.
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()
......
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