forked from qt-creator/qt-creator
Introduced ranges and versioning of QML/JS documents.
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user