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

Introduced ranges and versioning of QML/JS documents.

parent fbdd4fbf
......@@ -43,8 +43,9 @@ Document::Document(const QString &fileName)
: _engine(0)
, _pool(0)
, _ast(0)
, _fileName(fileName)
, _documentRevision(0)
, _parsedCorrectly(false)
, _fileName(fileName)
{
const int slashIdx = fileName.lastIndexOf('/');
if (slashIdx != -1)
......@@ -101,6 +102,16 @@ void Document::setSource(const QString &source)
_source = source;
}
int Document::documentRevision() const
{
return _documentRevision;
}
void Document::setDocumentRevision(int revision)
{
_documentRevision = revision;
}
bool Document::parseQml()
{
Q_ASSERT(! _engine);
......
......@@ -72,6 +72,9 @@ public:
bool isParsedCorrectly() const
{ return _parsedCorrectly; }
int documentRevision() const;
void setDocumentRevision(int documentRevision);
IdTable ids() const { return _ids; }
QString fileName() const { return _fileName; }
......@@ -86,12 +89,13 @@ private:
QmlJS::Engine *_engine;
QmlJS::NodePool *_pool;
QmlJS::AST::Node *_ast;
int _documentRevision;
bool _parsedCorrectly;
QList<QmlJS::DiagnosticMessage> _diagnosticMessages;
QString _fileName;
QString _path;
QString _componentName;
QString _source;
bool _parsedCorrectly;
IdTable _ids;
QmlJS::Symbol::List _symbols;
};
......
......@@ -94,41 +94,6 @@ static QIcon iconForColor(const QColor &color)
namespace {
class FindMembers: protected AST::Visitor
{
QList<AST::UiObjectMember *> _members;
public:
QList<AST::UiObjectMember *> operator()(Document::Ptr doc)
{
_members.clear();
if (doc && doc->qmlProgram())
doc->qmlProgram()->accept(this);
return _members;
}
protected:
using AST::Visitor::visit;
virtual bool visit(AST::UiArrayBinding *ast)
{
_members.append(ast);
return true;
}
virtual bool visit(AST::UiObjectBinding *ast)
{
_members.append(ast);
return true;
}
virtual bool visit(AST::UiObjectDefinition *ast)
{
_members.append(ast);
return true;
}
};
class ExpressionUnderCursor
{
QTextCursor _cursor;
......@@ -692,9 +657,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
m_completions.clear();
QmlJS::Snapshot snapshot = m_modelManager->snapshot();
Document::Ptr qmlDocument = snapshot.document(fileName);
const QFileInfo currentFileInfo(qmlDocument->fileName());
SemanticInfo semanticInfo = edit->semanticInfo();
Document::Ptr qmlDocument = semanticInfo.document;
const QFileInfo currentFileInfo(fileName);
const QString currentFilePath = currentFileInfo.absolutePath();
const QIcon componentIcon = iconForColor(Qt::yellow);
......@@ -737,27 +704,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
AST::UiObjectMember *declaringMember = 0;
AST::UiObjectMember *parentMember = 0;
const int cursorLine = edit->textCursor().blockNumber() + 1;
const int cursorColumn = edit->textCursor().columnNumber() + 1;
FindMembers findMembers;
const QList<AST::UiObjectMember *> members = findMembers(qmlDocument);
for (int index = 0; index < members.size(); ++index) {
AST::UiObjectMember *member = members.at(index);
AST::SourceLocation pos = member->firstSourceLocation();
const int startLine = pos.startLine;
const int startColumn = pos.startColumn;
if (startLine < cursorLine || (startLine == cursorLine && cursorColumn >= startColumn)) {
AST::SourceLocation endPos = member->lastSourceLocation();
const int endLine = endPos.startLine;
const int endColumn = endPos.startColumn + endPos.length;
if (cursorLine < endLine || (cursorLine == endLine && cursorColumn <= endColumn)) {
parentMember = declaringMember;
declaringMember = member;
}
const int cursorPosition = editor->position();
foreach (const Range &range, semanticInfo.ranges) {
if (cursorPosition >= range.begin.position() && cursorPosition <= range.end.position()) {
parentMember = declaringMember;
declaringMember = range.ast;
}
}
......
......@@ -76,8 +76,10 @@ enum {
using namespace QmlJS;
using namespace QmlJS::AST;
using namespace QmlJSEditor::Internal;
namespace {
int blockBraceDepth(const QTextBlock &block)
{
int state = block.userState();
......@@ -120,11 +122,6 @@ bool shouldInsertMatchingText(const QTextCursor &tc)
return shouldInsertMatchingText(doc->characterAt(tc.selectionEnd()));
}
} // end of anonymous namespace
namespace QmlJSEditor {
namespace Internal {
class FindIdDeclarations: protected Visitor
{
public:
......@@ -383,6 +380,54 @@ protected:
}
};
class CreateRanges: protected AST::Visitor
{
QTextDocument *_textDocument;
QList<Range> _ranges;
public:
QList<Range> operator()(QTextDocument *textDocument, Document::Ptr doc)
{
_textDocument = textDocument;
_ranges.clear();
if (doc && doc->qmlProgram() != 0)
doc->qmlProgram()->accept(this);
return _ranges;
}
protected:
using AST::Visitor::visit;
virtual bool visit(AST::UiObjectBinding *ast)
{
_ranges.append(createRange(ast));
return true;
}
virtual bool visit(AST::UiObjectDefinition *ast)
{
_ranges.append(createRange(ast));
return true;
}
Range createRange(AST::UiObjectMember *ast)
{
Range range;
range.ast = ast;
range.begin = QTextCursor(_textDocument);
range.begin.setPosition(ast->firstSourceLocation().begin());
range.end = QTextCursor(_textDocument);
range.end.setPosition(ast->lastSourceLocation().end());
return range;
}
};
} // end of anonymous namespace
QmlJSEditorEditable::QmlJSEditorEditable(QmlJSTextEditor *editor)
: BaseTextEditorEditable(editor)
{
......@@ -481,13 +526,23 @@ void QmlJSTextEditor::onDocumentUpdated(QmlJS::Document::Ptr doc)
if (file()->fileName() != doc->fileName())
return;
m_document = doc;
if (doc->documentRevision() != document()->revision()) {
// got an outdated document.
return;
}
FindIdDeclarations updateIds;
m_idsRevision = document()->revision();
m_ids = updateIds(doc->qmlProgram());
if (doc->isParsedCorrectly()) {
// create the ranges
CreateRanges createRanges;
SemanticInfo sem;
sem.document = doc;
sem.ranges = createRanges(document(), doc);
m_semanticInfo = sem;
FindDeclarations findDeclarations;
m_declarations = findDeclarations(doc->ast());
......@@ -956,6 +1011,3 @@ QString QmlJSTextEditor::insertParagraphSeparator(const QTextCursor &) const
{
return QLatin1String("}\n");
}
} // namespace Internal
} // namespace QmlJSEditor
......@@ -91,6 +91,27 @@ struct Declaration
{ }
};
class Range
{
public:
Range(): ast(0) {}
public: // attributes
QmlJS::AST::UiObjectMember *ast;
QTextCursor begin;
QTextCursor end;
};
class SemanticInfo
{
public:
SemanticInfo() {}
public: // attributes
QmlJS::Document::Ptr document;
QList<Range> ranges;
};
class QmlJSTextEditor : public TextEditor::BaseTextEditor
{
Q_OBJECT
......@@ -109,7 +130,7 @@ public:
virtual void unCommentSelection();
QmlJS::Document::Ptr qmlDocument() const { return m_document; }
SemanticInfo semanticInfo() const { return m_semanticInfo; }
public slots:
virtual void setFontSettings(const TextEditor::FontSettings &);
......@@ -154,14 +175,15 @@ private:
QTimer *m_updateDocumentTimer;
QTimer *m_updateUsesTimer;
QComboBox *m_methodCombo;
QList<Declaration> m_declarations;
QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### use QMultiMap
int m_idsRevision;
QList<QmlJS::DiagnosticMessage> m_diagnosticMessages;
QmlJS::Document::Ptr m_document;
QList<Declaration> m_declarations; // ### remove me
QMap<QString, QList<QmlJS::AST::SourceLocation> > m_ids; // ### remove me
int m_idsRevision; // ### remove me
QList<QmlJS::DiagnosticMessage> m_diagnosticMessages; // ### remove me
QmlModelManagerInterface *m_modelManager;
QmlJS::TypeSystem *m_typeSystem;
QTextCharFormat m_occurrencesFormat;
SemanticInfo m_semanticInfo;
};
} // namespace Internal
......
......@@ -29,6 +29,7 @@
#include "qmljseditorconstants.h"
#include "qmlmodelmanager.h"
#include "qmljseditor.h"
#include <coreplugin/icore.h>
#include <coreplugin/editormanager/editormanager.h>
......@@ -42,6 +43,7 @@
#include <qtconcurrent/runextensions.h>
#include <QTextStream>
using namespace QmlJS;
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
......@@ -57,7 +59,7 @@ QmlModelManager::QmlModelManager(QObject *parent):
this, SLOT(onDocumentUpdated(QmlJS::Document::Ptr)));
}
QmlJS::Snapshot QmlModelManager::snapshot() const
Snapshot QmlModelManager::snapshot() const
{
QMutexLocker locker(&m_mutex);
......@@ -75,7 +77,7 @@ QFuture<void> QmlModelManager::refreshSourceFiles(const QStringList &sourceFiles
return QFuture<void>();
}
const QMap<QString, QString> workingCopy = buildWorkingCopyList();
const QMap<QString, WorkingCopy> workingCopy = buildWorkingCopyList();
QFuture<void> result = QtConcurrent::run(&QmlModelManager::parse,
workingCopy, sourceFiles,
......@@ -102,26 +104,29 @@ QFuture<void> QmlModelManager::refreshSourceFiles(const QStringList &sourceFiles
return result;
}
QMap<QString, QString> QmlModelManager::buildWorkingCopyList()
QMap<QString, QmlModelManager::WorkingCopy> QmlModelManager::buildWorkingCopyList()
{
QMap<QString, QString> workingCopy;
QMap<QString, WorkingCopy> workingCopy;
Core::EditorManager *editorManager = m_core->editorManager();
foreach (Core::IEditor *editor, editorManager->openedEditors()) {
const QString key = editor->file()->fileName();
if (TextEditor::ITextEditor *textEditor = qobject_cast<TextEditor::ITextEditor*>(editor)) {
workingCopy[key] = textEditor->contents();
if (QmlJSTextEditor *ed = qobject_cast<QmlJSTextEditor *>(textEditor->widget())) {
workingCopy[key].contents = ed->toPlainText();
workingCopy[key].documentRevision = ed->document()->revision();
}
}
}
return workingCopy;
}
void QmlModelManager::emitDocumentUpdated(QmlJS::Document::Ptr doc)
void QmlModelManager::emitDocumentUpdated(Document::Ptr doc)
{ emit documentUpdated(doc); }
void QmlModelManager::onDocumentUpdated(QmlJS::Document::Ptr doc)
void QmlModelManager::onDocumentUpdated(Document::Ptr doc)
{
QMutexLocker locker(&m_mutex);
......@@ -129,10 +134,14 @@ void QmlModelManager::onDocumentUpdated(QmlJS::Document::Ptr doc)
}
void QmlModelManager::parse(QFutureInterface<void> &future,
QMap<QString, QString> workingCopy,
QMap<QString, WorkingCopy> workingCopy,
QStringList files,
QmlModelManager *modelManager)
{
Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase();
Core::MimeType jsSourceTy = db->findByType(QLatin1String("application/javascript"));
Core::MimeType qmlSourceTy = db->findByType(QLatin1String("application/x-qml"));
future.setProgressRange(0, files.size());
for (int i = 0; i < files.size(); ++i) {
......@@ -140,9 +149,12 @@ void QmlModelManager::parse(QFutureInterface<void> &future,
const QString fileName = files.at(i);
QString contents;
int documentRevision = 0;
if (workingCopy.contains(fileName)) {
contents = workingCopy.value(fileName);
WorkingCopy wc = workingCopy.value(fileName);
contents = wc.contents;
documentRevision = wc.documentRevision;
} else {
QFile inFile(fileName);
......@@ -153,23 +165,18 @@ void QmlModelManager::parse(QFutureInterface<void> &future,
}
}
QmlJS::Document::Ptr doc = QmlJS::Document::create(fileName);
Document::Ptr doc = Document::create(fileName);
doc->setDocumentRevision(documentRevision);
doc->setSource(contents);
{
Core::MimeDatabase *db = Core::ICore::instance()->mimeDatabase();
Core::MimeType jsSourceTy = db->findByType(QLatin1String("application/javascript"));
Core::MimeType qmlSourceTy = db->findByType(QLatin1String("application/x-qml"));
const QFileInfo fileInfo(fileName);
const QFileInfo fileInfo(fileName);
if (jsSourceTy.matchesFile(fileInfo))
doc->parseJavaScript();
else if (qmlSourceTy.matchesFile(fileInfo))
doc->parseQml();
else
qWarning() << "Don't know how to treat" << fileName;
}
if (jsSourceTy.matchesFile(fileInfo))
doc->parseJavaScript();
else if (qmlSourceTy.matchesFile(fileInfo))
doc->parseQml();
else
qWarning() << "Don't know how to treat" << fileName;
modelManager->emitDocumentUpdated(doc);
}
......
......@@ -66,11 +66,18 @@ private Q_SLOTS:
void onDocumentUpdated(QmlJS::Document::Ptr doc);
protected:
struct WorkingCopy
{
WorkingCopy(int revision = 0): documentRevision(revision) {}
int documentRevision;
QString contents;
};
QFuture<void> refreshSourceFiles(const QStringList &sourceFiles);
QMap<QString, QString> buildWorkingCopyList();
QMap<QString, WorkingCopy> buildWorkingCopyList();
static void parse(QFutureInterface<void> &future,
QMap<QString, QString> workingCopy,
QMap<QString, WorkingCopy> workingCopy,
QStringList files,
QmlModelManager *modelManager);
......
Supports Markdown
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