2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditor.h"
|
2014-01-23 14:00:21 +01:00
|
|
|
|
2014-08-19 12:12:45 +02:00
|
|
|
#include "qmljsautocompleter.h"
|
2014-08-20 01:47:42 +02:00
|
|
|
#include "qmljscompletionassist.h"
|
2022-08-25 12:46:47 +02:00
|
|
|
#include "qmljseditingsettingspage.h"
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditorconstants.h"
|
2014-01-23 14:00:21 +01:00
|
|
|
#include "qmljseditordocument.h"
|
2010-01-15 17:20:03 +01:00
|
|
|
#include "qmljseditorplugin.h"
|
2022-08-25 12:46:47 +02:00
|
|
|
#include "qmljseditortr.h"
|
2010-09-24 14:05:34 +02:00
|
|
|
#include "qmljsfindreferences.h"
|
2015-01-08 22:43:39 +03:00
|
|
|
#include "qmljshighlighter.h"
|
2014-09-30 13:08:05 +02:00
|
|
|
#include "qmljshoverhandler.h"
|
2011-04-15 16:19:23 +02:00
|
|
|
#include "qmljsquickfixassist.h"
|
2014-08-20 01:47:42 +02:00
|
|
|
#include "qmloutlinemodel.h"
|
2018-02-21 11:47:01 +01:00
|
|
|
#include "quicktoolbar.h"
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-16 11:16:22 +02:00
|
|
|
#include <qmljs/qmljsbind.h>
|
2010-11-16 14:39:54 +01:00
|
|
|
#include <qmljs/qmljsevaluate.h>
|
2010-11-11 10:05:05 +01:00
|
|
|
#include <qmljs/qmljsmodelmanagerinterface.h>
|
2011-10-07 14:04:06 +02:00
|
|
|
#include <qmljs/qmljsutils.h>
|
2009-09-11 14:42:50 +02:00
|
|
|
|
2017-04-24 15:52:04 +02:00
|
|
|
#include <qmljstools/qmljsindenter.h>
|
2013-12-11 10:49:32 +01:00
|
|
|
#include <qmljstools/qmljstoolsconstants.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2010-11-11 10:05:05 +01:00
|
|
|
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/command.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <coreplugin/coreconstants.h>
|
|
|
|
|
#include <coreplugin/designmode.h>
|
2010-07-14 12:21:23 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2010-01-15 17:41:12 +01:00
|
|
|
#include <coreplugin/icore.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <coreplugin/modemanager.h>
|
|
|
|
|
|
2009-09-04 16:51:11 +02:00
|
|
|
#include <extensionsystem/pluginmanager.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
#include <texteditor/textdocument.h>
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <texteditor/fontsettings.h>
|
2010-10-29 15:20:10 +02:00
|
|
|
#include <texteditor/tabsettings.h>
|
2009-04-22 15:21:04 +02:00
|
|
|
#include <texteditor/texteditorconstants.h>
|
2015-12-09 12:24:53 +01:00
|
|
|
#include <texteditor/texteditorsettings.h>
|
2010-07-09 14:47:18 +02:00
|
|
|
#include <texteditor/syntaxhighlighter.h>
|
2010-08-05 10:44:50 +02:00
|
|
|
#include <texteditor/refactoroverlay.h>
|
2011-04-15 16:19:23 +02:00
|
|
|
#include <texteditor/codeassist/genericproposal.h>
|
2014-09-04 00:04:18 +02:00
|
|
|
#include <texteditor/codeassist/genericproposalmodel.h>
|
2022-11-14 10:51:06 +01:00
|
|
|
#include <texteditor/colorpreviewhoverhandler.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <texteditor/texteditoractionhandler.h>
|
2017-11-15 15:01:54 +01:00
|
|
|
#include <texteditor/textmark.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
|
2021-01-06 15:53:10 +01:00
|
|
|
#include <utils/algorithm.h>
|
2018-07-13 11:13:32 +02:00
|
|
|
#include <utils/delegates.h>
|
2009-12-02 17:59:27 +01:00
|
|
|
#include <utils/changeset.h>
|
2011-08-09 12:18:56 +02:00
|
|
|
#include <utils/qtcassert.h>
|
2015-01-08 22:43:39 +03:00
|
|
|
#include <utils/uncommentselection.h>
|
2009-05-14 16:37:17 +02:00
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QCoreApplication>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QFileInfo>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <QHeaderView>
|
|
|
|
|
#include <QMenu>
|
2016-06-27 22:25:11 +03:00
|
|
|
#include <QMetaMethod>
|
2013-02-18 15:12:09 +01:00
|
|
|
#include <QPointer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QScopedPointer>
|
|
|
|
|
#include <QTextCodec>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <QTimer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTreeView>
|
2020-02-28 17:51:32 +01:00
|
|
|
#include <QDebug>
|
2009-04-22 15:21:04 +02:00
|
|
|
|
|
|
|
|
enum {
|
2010-07-12 17:03:49 +02:00
|
|
|
UPDATE_USES_DEFAULT_INTERVAL = 150,
|
2010-12-16 12:05:48 +01:00
|
|
|
UPDATE_OUTLINE_INTERVAL = 500 // msecs after new semantic info has been arrived / cursor has moved
|
2009-04-22 15:21:04 +02:00
|
|
|
};
|
|
|
|
|
|
2019-07-18 14:17:25 +02:00
|
|
|
const char QML_JS_EDITOR_PLUGIN[] = "QmlJSEditorPlugin";
|
|
|
|
|
const char QT_QUICK_TOOLBAR_MARKER_ID[] = "QtQuickToolbarMarkerId";
|
|
|
|
|
|
2013-08-30 16:38:57 +02:00
|
|
|
using namespace Core;
|
2009-06-11 11:22:26 +02:00
|
|
|
using namespace QmlJS;
|
|
|
|
|
using namespace QmlJS::AST;
|
2012-04-12 15:51:56 +02:00
|
|
|
using namespace QmlJSTools;
|
2014-08-20 01:47:42 +02:00
|
|
|
using namespace TextEditor;
|
2020-06-26 13:59:38 +02:00
|
|
|
using namespace Utils;
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2013-06-03 19:31:32 +02:00
|
|
|
namespace QmlJSEditor {
|
|
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
//
|
|
|
|
|
// QmlJSEditorWidget
|
|
|
|
|
//
|
|
|
|
|
|
2014-08-23 00:19:48 +02:00
|
|
|
QmlJSEditorWidget::QmlJSEditorWidget()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2014-01-09 18:04:45 +01:00
|
|
|
m_findReferences = new FindReferences(this);
|
2013-12-11 10:49:32 +01:00
|
|
|
setLanguageSettingsId(QmlJSTools::Constants::QML_JS_SETTINGS_ID);
|
2014-08-23 00:19:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSEditorWidget::finalizeInitialization()
|
|
|
|
|
{
|
|
|
|
|
m_qmlJsEditorDocument = static_cast<QmlJSEditorDocument *>(textDocument());
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2014-08-26 17:15:37 +02:00
|
|
|
m_updateUsesTimer.setInterval(UPDATE_USES_DEFAULT_INTERVAL);
|
|
|
|
|
m_updateUsesTimer.setSingleShot(true);
|
|
|
|
|
connect(&m_updateUsesTimer, &QTimer::timeout, this, &QmlJSEditorWidget::updateUses);
|
|
|
|
|
connect(this, &QPlainTextEdit::cursorPositionChanged,
|
2019-02-26 09:40:49 +01:00
|
|
|
&m_updateUsesTimer, QOverload<>::of(&QTimer::start));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2014-08-26 17:15:37 +02:00
|
|
|
m_updateOutlineIndexTimer.setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
|
m_updateOutlineIndexTimer.setSingleShot(true);
|
|
|
|
|
connect(&m_updateOutlineIndexTimer, &QTimer::timeout,
|
|
|
|
|
this, &QmlJSEditorWidget::updateOutlineIndexNow);
|
2010-07-12 17:03:49 +02:00
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
m_modelManager = ModelManagerInterface::instance();
|
2019-08-07 17:04:47 +02:00
|
|
|
m_contextPane = Internal::QmlJSEditorPlugin::quickToolBar();
|
2010-08-13 13:59:41 +02:00
|
|
|
|
2014-04-29 16:39:16 +02:00
|
|
|
m_modelManager->activateScan();
|
|
|
|
|
|
2014-08-26 17:15:37 +02:00
|
|
|
m_contextPaneTimer.setInterval(UPDATE_OUTLINE_INTERVAL);
|
|
|
|
|
m_contextPaneTimer.setSingleShot(true);
|
|
|
|
|
connect(&m_contextPaneTimer, &QTimer::timeout, this, &QmlJSEditorWidget::updateContextPane);
|
2010-08-13 13:59:41 +02:00
|
|
|
if (m_contextPane) {
|
2014-08-26 17:15:37 +02:00
|
|
|
connect(this, &QmlJSEditorWidget::cursorPositionChanged,
|
2019-02-26 09:40:49 +01:00
|
|
|
&m_contextPaneTimer, QOverload<>::of(&QTimer::start));
|
2014-08-26 17:15:37 +02:00
|
|
|
connect(m_contextPane, &IContextPane::closed, this, &QmlJSEditorWidget::showTextMarker);
|
2010-08-13 13:59:41 +02:00
|
|
|
}
|
2009-09-04 16:51:11 +02:00
|
|
|
|
2014-08-26 17:15:37 +02:00
|
|
|
connect(this->document(), &QTextDocument::modificationChanged,
|
2020-11-17 15:50:11 +01:00
|
|
|
this, &QmlJSEditorWidget::updateModificationChange);
|
2010-02-16 10:36:09 +01:00
|
|
|
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_qmlJsEditorDocument, &QmlJSEditorDocument::updateCodeWarnings,
|
|
|
|
|
this, &QmlJSEditorWidget::updateCodeWarnings);
|
2014-08-26 17:15:37 +02:00
|
|
|
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(m_qmlJsEditorDocument, &QmlJSEditorDocument::semanticInfoUpdated,
|
|
|
|
|
this, &QmlJSEditorWidget::semanticInfoUpdated);
|
2010-03-17 18:41:14 +01:00
|
|
|
|
2010-07-07 13:56:39 +02:00
|
|
|
setRequestMarkEnabled(true);
|
2014-08-19 00:54:45 +02:00
|
|
|
createToolBar();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2020-09-23 16:38:01 +02:00
|
|
|
void QmlJSEditorWidget::restoreState(const QByteArray &state)
|
2020-02-20 15:19:12 +01:00
|
|
|
{
|
|
|
|
|
QStringList qmlTypes { QmlJSTools::Constants::QML_MIMETYPE,
|
|
|
|
|
QmlJSTools::Constants::QBS_MIMETYPE,
|
|
|
|
|
QmlJSTools::Constants::QMLTYPES_MIMETYPE,
|
|
|
|
|
QmlJSTools::Constants::QMLUI_MIMETYPE };
|
|
|
|
|
|
|
|
|
|
if (QmlJsEditingSettings::get().foldAuxData() && qmlTypes.contains(textDocument()->mimeType())) {
|
|
|
|
|
int version = 0;
|
|
|
|
|
QDataStream stream(state);
|
|
|
|
|
stream >> version;
|
|
|
|
|
if (version < 1)
|
|
|
|
|
foldAuxiliaryData();
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-23 16:38:01 +02:00
|
|
|
TextEditorWidget::restoreState(state);
|
2020-02-20 15:19:12 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
QModelIndex QmlJSEditorWidget::outlineModelIndex()
|
2010-07-12 14:45:22 +02:00
|
|
|
{
|
2010-07-12 16:40:15 +02:00
|
|
|
if (!m_outlineModelIndex.isValid()) {
|
|
|
|
|
m_outlineModelIndex = indexForPosition(position());
|
|
|
|
|
}
|
2010-07-12 14:45:22 +02:00
|
|
|
return m_outlineModelIndex;
|
|
|
|
|
}
|
|
|
|
|
|
2010-02-23 16:11:58 +01:00
|
|
|
static void appendExtraSelectionsForMessages(
|
|
|
|
|
QList<QTextEdit::ExtraSelection> *selections,
|
|
|
|
|
const QList<DiagnosticMessage> &messages,
|
|
|
|
|
const QTextDocument *document)
|
|
|
|
|
{
|
2022-05-17 16:11:03 +02:00
|
|
|
for (const DiagnosticMessage &d : messages) {
|
2010-02-23 16:11:58 +01:00
|
|
|
const int line = d.loc.startLine;
|
|
|
|
|
const int column = qMax(1U, d.loc.startColumn);
|
|
|
|
|
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
QTextCursor c(document->findBlockByNumber(line - 1));
|
|
|
|
|
sel.cursor = c;
|
|
|
|
|
|
|
|
|
|
sel.cursor.setPosition(c.position() + column - 1);
|
|
|
|
|
|
|
|
|
|
if (d.loc.length == 0) {
|
|
|
|
|
if (sel.cursor.atBlockEnd())
|
|
|
|
|
sel.cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
else
|
|
|
|
|
sel.cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
sel.cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, d.loc.length);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-27 13:12:23 +01:00
|
|
|
const auto fontSettings = TextEditor::TextEditorSettings::fontSettings();
|
2015-12-09 12:24:53 +01:00
|
|
|
|
2010-02-23 16:11:58 +01:00
|
|
|
if (d.isWarning())
|
2015-12-09 12:24:53 +01:00
|
|
|
sel.format = fontSettings.toTextCharFormat(TextEditor::C_WARNING);
|
2010-02-23 16:11:58 +01:00
|
|
|
else
|
2015-12-09 12:24:53 +01:00
|
|
|
sel.format = fontSettings.toTextCharFormat(TextEditor::C_ERROR);
|
2010-02-23 16:11:58 +01:00
|
|
|
|
|
|
|
|
sel.format.setToolTip(d.message);
|
|
|
|
|
|
|
|
|
|
selections->append(sel);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
void QmlJSEditorWidget::updateCodeWarnings(Document::Ptr doc)
|
2009-09-04 16:51:11 +02:00
|
|
|
{
|
2010-01-26 12:09:07 +01:00
|
|
|
if (doc->ast()) {
|
2013-02-07 00:10:05 +01:00
|
|
|
setExtraSelections(CodeWarningsSelection, QList<QTextEdit::ExtraSelection>());
|
2014-07-22 19:06:44 +02:00
|
|
|
} else if (doc->language().isFullySupportedLanguage()) {
|
2010-03-31 14:55:05 +02:00
|
|
|
// show parsing errors
|
2010-02-23 16:11:58 +01:00
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
|
|
|
|
appendExtraSelectionsForMessages(&selections, doc->diagnosticMessages(), document());
|
|
|
|
|
setExtraSelections(CodeWarningsSelection, selections);
|
2013-03-05 14:35:15 +01:00
|
|
|
} else {
|
|
|
|
|
setExtraSelections(CodeWarningsSelection, QList<QTextEdit::ExtraSelection>());
|
2009-09-04 16:51:11 +02:00
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2020-02-20 15:19:12 +01:00
|
|
|
void QmlJSEditorWidget::foldAuxiliaryData()
|
|
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
|
|
|
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
|
|
|
|
QTC_ASSERT(documentLayout, return);
|
|
|
|
|
QTextBlock block = doc->lastBlock();
|
|
|
|
|
|
|
|
|
|
while (block.isValid() && block.isVisible()) {
|
|
|
|
|
if (TextDocumentLayout::canFold(block) && block.next().isVisible()) {
|
|
|
|
|
const QString trimmedText = block.text().trimmed();
|
|
|
|
|
if (trimmedText.startsWith("/*##^##")) {
|
|
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, false);
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
block = block.previous();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-17 15:50:11 +01:00
|
|
|
void QmlJSEditorWidget::updateModificationChange(bool changed)
|
2010-04-16 12:42:12 +02:00
|
|
|
{
|
|
|
|
|
if (!changed && m_modelManager)
|
2022-06-20 12:35:13 +02:00
|
|
|
m_modelManager->fileChangedOnDisk(textDocument()->filePath());
|
2010-04-16 12:42:12 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-06 17:27:52 +02:00
|
|
|
bool QmlJSEditorWidget::isOutlineCursorChangesBlocked()
|
|
|
|
|
{
|
2017-06-29 17:48:19 +02:00
|
|
|
return hasFocus();
|
2017-04-06 17:27:52 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::jumpToOutlineElement(int /*index*/)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-13 11:24:16 +02:00
|
|
|
QModelIndex index = m_outlineCombo->view()->currentIndex();
|
2020-02-28 17:51:32 +01:00
|
|
|
SourceLocation location = m_qmlJsEditorDocument->outlineModel()->sourceLocation(index);
|
2010-08-25 13:21:38 +02:00
|
|
|
|
|
|
|
|
if (!location.isValid())
|
|
|
|
|
return;
|
2010-07-12 14:45:22 +02:00
|
|
|
|
2013-08-30 16:38:57 +02:00
|
|
|
EditorManager::cutForwardNavigationHistory();
|
|
|
|
|
EditorManager::addCurrentPositionToNavigationHistory();
|
2010-07-14 12:21:23 +02:00
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.setPosition(location.offset);
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
|
|
|
|
|
setFocus();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::updateOutlineIndexNow()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2014-01-30 17:18:35 +01:00
|
|
|
if (!m_qmlJsEditorDocument->outlineModel()->document())
|
2010-07-12 17:03:49 +02:00
|
|
|
return;
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2014-02-07 13:45:51 +01:00
|
|
|
if (m_qmlJsEditorDocument->outlineModel()->document()->editorRevision() != document()->revision()) {
|
2014-08-26 17:15:37 +02:00
|
|
|
m_updateOutlineIndexTimer.start();
|
2010-07-12 17:03:49 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 16:40:15 +02:00
|
|
|
m_outlineModelIndex = QModelIndex(); // invalidate
|
|
|
|
|
QModelIndex comboIndex = outlineModelIndex();
|
2017-04-06 17:22:52 +02:00
|
|
|
emit outlineModelIndexChanged(m_outlineModelIndex);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
if (comboIndex.isValid()) {
|
2017-09-30 07:12:57 +02:00
|
|
|
QSignalBlocker blocker(m_outlineCombo);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
// There is no direct way to select a non-root item
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setRootModelIndex(comboIndex.parent());
|
|
|
|
|
m_outlineCombo->setCurrentIndex(comboIndex.row());
|
|
|
|
|
m_outlineCombo->setRootModelIndex(QModelIndex());
|
2009-09-16 13:56:06 +02:00
|
|
|
}
|
2010-01-14 16:30:48 +01:00
|
|
|
}
|
2019-08-07 17:04:47 +02:00
|
|
|
|
2013-06-03 19:31:32 +02:00
|
|
|
} // namespace QmlJSEditor
|
|
|
|
|
|
2011-08-12 09:25:01 +02:00
|
|
|
|
2013-06-03 19:31:32 +02:00
|
|
|
namespace QmlJSEditor {
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::updateContextPane()
|
2010-08-05 10:44:50 +02:00
|
|
|
{
|
2014-01-24 16:53:16 +01:00
|
|
|
const SemanticInfo info = m_qmlJsEditorDocument->semanticInfo();
|
|
|
|
|
if (m_contextPane && document() && info.isValid()
|
|
|
|
|
&& document()->revision() == info.document->editorRevision())
|
2010-08-27 10:37:15 +02:00
|
|
|
{
|
2014-01-24 16:53:16 +01:00
|
|
|
Node *oldNode = info.declaringMemberNoProperties(m_oldCursorPosition);
|
|
|
|
|
Node *newNode = info.declaringMemberNoProperties(position());
|
2010-08-13 17:20:33 +02:00
|
|
|
if (oldNode != newNode && m_oldCursorPosition != -1)
|
2018-11-24 02:45:30 +01:00
|
|
|
m_contextPane->apply(this, info.document, nullptr, newNode, false);
|
2011-08-12 09:25:01 +02:00
|
|
|
|
2014-09-01 12:54:03 +02:00
|
|
|
if (m_contextPane->isAvailable(this, info.document, newNode) &&
|
2010-08-05 15:35:52 +02:00
|
|
|
!m_contextPane->widget()->isVisible()) {
|
2019-07-18 14:17:25 +02:00
|
|
|
QList<RefactorMarker> markers
|
|
|
|
|
= RefactorMarker::filterOutType(refactorMarkers(), QT_QUICK_TOOLBAR_MARKER_ID);
|
2010-08-05 10:44:50 +02:00
|
|
|
if (UiObjectMember *m = newNode->uiObjectMemberCast()) {
|
2010-09-08 16:34:49 +02:00
|
|
|
const int start = qualifiedTypeNameId(m)->identifierToken.begin();
|
2010-08-05 10:44:50 +02:00
|
|
|
for (UiQualifiedId *q = qualifiedTypeNameId(m); q; q = q->next) {
|
|
|
|
|
if (! q->next) {
|
|
|
|
|
const int end = q->identifierToken.end();
|
2010-09-08 16:34:49 +02:00
|
|
|
if (position() >= start && position() <= end) {
|
2014-08-20 01:47:42 +02:00
|
|
|
RefactorMarker marker;
|
2010-09-08 16:34:49 +02:00
|
|
|
QTextCursor tc(document());
|
|
|
|
|
tc.setPosition(end);
|
|
|
|
|
marker.cursor = tc;
|
2022-08-25 12:46:47 +02:00
|
|
|
marker.tooltip = Tr::tr("Show Qt Quick ToolBar");
|
2019-07-18 14:17:25 +02:00
|
|
|
marker.type = QT_QUICK_TOOLBAR_MARKER_ID;
|
2019-01-24 06:39:20 +01:00
|
|
|
marker.callback = [this](TextEditorWidget *) {
|
|
|
|
|
showContextPane();
|
|
|
|
|
};
|
2010-09-08 16:34:49 +02:00
|
|
|
markers.append(marker);
|
|
|
|
|
}
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
setRefactorMarkers(markers);
|
2010-09-01 11:54:35 +02:00
|
|
|
} else if (oldNode != newNode) {
|
2019-07-18 14:17:25 +02:00
|
|
|
setRefactorMarkers(
|
|
|
|
|
RefactorMarker::filterOutType(refactorMarkers(), QT_QUICK_TOOLBAR_MARKER_ID));
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
|
m_oldCursorPosition = position();
|
2010-08-27 10:37:15 +02:00
|
|
|
|
|
|
|
|
setSelectedElements();
|
2010-08-05 10:44:50 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::showTextMarker()
|
2010-08-13 13:59:41 +02:00
|
|
|
{
|
|
|
|
|
m_oldCursorPosition = -1;
|
2014-01-30 15:48:24 +01:00
|
|
|
updateContextPane();
|
2010-08-13 13:59:41 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::updateUses()
|
2010-01-14 16:30:48 +01:00
|
|
|
{
|
2014-01-29 13:53:08 +01:00
|
|
|
if (m_qmlJsEditorDocument->isSemanticInfoOutdated()) // will be updated when info is updated
|
2010-01-14 16:30:48 +01:00
|
|
|
return;
|
2009-05-04 18:58:36 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
2022-10-21 14:05:12 +02:00
|
|
|
|
2021-01-06 15:53:10 +01:00
|
|
|
// code model may present the locations not in a document order
|
2022-10-21 14:05:12 +02:00
|
|
|
const QList<SourceLocation> locations = Utils::sorted(
|
|
|
|
|
m_qmlJsEditorDocument->semanticInfo().idLocations.value(wordUnderCursor()),
|
|
|
|
|
[](const SourceLocation &lhs, const SourceLocation &rhs) {
|
2021-01-06 15:53:10 +01:00
|
|
|
return lhs.begin() < rhs.begin();
|
|
|
|
|
});
|
2022-10-21 14:05:12 +02:00
|
|
|
for (const SourceLocation &loc : locations) {
|
2009-09-16 13:56:06 +02:00
|
|
|
if (! loc.isValid())
|
|
|
|
|
continue;
|
2009-05-05 10:46:58 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2015-02-03 23:48:57 +02:00
|
|
|
sel.format = textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES);
|
2009-09-16 13:56:06 +02:00
|
|
|
sel.cursor = textCursor();
|
|
|
|
|
sel.cursor.setPosition(loc.begin());
|
|
|
|
|
sel.cursor.setPosition(loc.end(), QTextCursor::KeepAnchor);
|
|
|
|
|
selections.append(sel);
|
|
|
|
|
}
|
2009-05-04 18:58:36 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
setExtraSelections(CodeSemanticsSelection, selections);
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class SelectedElement: protected Visitor
|
|
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
unsigned m_cursorPositionStart = 0;
|
|
|
|
|
unsigned m_cursorPositionEnd = 0;
|
2010-07-27 11:57:06 +02:00
|
|
|
QList<UiObjectMember *> m_selectedMembers;
|
2010-07-08 11:34:51 +02:00
|
|
|
|
|
|
|
|
public:
|
2011-08-08 12:26:22 +02:00
|
|
|
QList<UiObjectMember *> operator()(const Document::Ptr &doc, unsigned startPosition, unsigned endPosition)
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
m_cursorPositionStart = startPosition;
|
|
|
|
|
m_cursorPositionEnd = endPosition;
|
|
|
|
|
m_selectedMembers.clear();
|
2011-08-08 12:26:22 +02:00
|
|
|
Node::accept(doc->qmlProgram(), this);
|
2010-07-27 11:57:06 +02:00
|
|
|
return m_selectedMembers;
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2010-07-13 16:13:26 +02:00
|
|
|
|
2010-07-27 11:57:06 +02:00
|
|
|
bool isSelectable(UiObjectMember *member) const
|
2010-07-13 16:13:26 +02:00
|
|
|
{
|
2011-10-07 14:04:06 +02:00
|
|
|
UiQualifiedId *id = qualifiedTypeNameId(member);
|
2010-07-13 16:13:26 +02:00
|
|
|
if (id) {
|
2021-06-15 20:25:49 +02:00
|
|
|
QStringView name = id->name;
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (!name.isEmpty() && name.at(0).isUpper())
|
2010-07-13 16:13:26 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool isIdBinding(UiObjectMember *member) const
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2018-11-24 02:45:30 +01:00
|
|
|
if (auto script = cast<const UiScriptBinding *>(member)) {
|
2010-07-08 11:34:51 +02:00
|
|
|
if (! script->qualifiedId)
|
|
|
|
|
return false;
|
2011-09-13 09:57:24 +02:00
|
|
|
else if (script->qualifiedId->name.isEmpty())
|
2010-07-08 11:34:51 +02:00
|
|
|
return false;
|
|
|
|
|
else if (script->qualifiedId->next)
|
|
|
|
|
return false;
|
|
|
|
|
|
2021-06-15 20:25:49 +02:00
|
|
|
QStringView propertyName = script->qualifiedId->name;
|
2010-07-08 11:34:51 +02:00
|
|
|
|
|
|
|
|
if (propertyName == QLatin1String("id"))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool containsCursor(unsigned begin, unsigned end)
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return m_cursorPositionStart >= begin && m_cursorPositionEnd <= end;
|
2010-07-12 17:18:05 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool intersectsCursor(unsigned begin, unsigned end)
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return (m_cursorPositionEnd >= begin && m_cursorPositionStart <= end);
|
2010-07-12 17:18:05 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-24 15:13:50 +02:00
|
|
|
inline bool isRangeSelected() const
|
2010-07-15 10:53:49 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
return (m_cursorPositionStart != m_cursorPositionEnd);
|
2010-07-15 10:53:49 +02:00
|
|
|
}
|
|
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
void postVisit(Node *ast) override
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2010-07-27 11:57:06 +02:00
|
|
|
if (!isRangeSelected() && !m_selectedMembers.isEmpty())
|
2010-07-12 17:18:05 +02:00
|
|
|
return; // nothing to do, we already have the results.
|
2010-07-08 11:34:51 +02:00
|
|
|
|
|
|
|
|
if (UiObjectMember *member = ast->uiObjectMemberCast()) {
|
|
|
|
|
unsigned begin = member->firstSourceLocation().begin();
|
|
|
|
|
unsigned end = member->lastSourceLocation().end();
|
|
|
|
|
|
2010-07-15 10:53:49 +02:00
|
|
|
if ((isRangeSelected() && intersectsCursor(begin, end))
|
|
|
|
|
|| (!isRangeSelected() && containsCursor(begin, end)))
|
2010-07-12 17:18:05 +02:00
|
|
|
{
|
2011-10-07 14:04:06 +02:00
|
|
|
if (initializerOfObject(member) && isSelectable(member)) {
|
2010-07-27 11:57:06 +02:00
|
|
|
m_selectedMembers << member;
|
|
|
|
|
// move start towards end; this facilitates multiselection so that root is usually ignored.
|
|
|
|
|
m_cursorPositionStart = qMin(end, m_cursorPositionEnd);
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-28 17:51:32 +01:00
|
|
|
|
|
|
|
|
void throwRecursionDepthError() override
|
|
|
|
|
{
|
|
|
|
|
qWarning("Warning: Hit maximum recursion depth visiting AST in SelectedElement");
|
|
|
|
|
}
|
2010-07-08 11:34:51 +02:00
|
|
|
};
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::setSelectedElements()
|
2010-07-08 11:34:51 +02:00
|
|
|
{
|
2016-06-27 22:25:11 +03:00
|
|
|
static const QMetaMethod selectedChangedSignal =
|
|
|
|
|
QMetaMethod::fromSignal(&QmlJSEditorWidget::selectedElementsChanged);
|
|
|
|
|
if (!isSignalConnected(selectedChangedSignal))
|
2010-08-24 15:13:50 +02:00
|
|
|
return;
|
|
|
|
|
|
2010-07-08 11:34:51 +02:00
|
|
|
QTextCursor tc = textCursor();
|
2010-07-12 17:18:05 +02:00
|
|
|
QString wordAtCursor;
|
2012-05-10 17:06:29 +02:00
|
|
|
QList<UiObjectMember *> offsets;
|
2010-07-12 17:18:05 +02:00
|
|
|
|
|
|
|
|
unsigned startPos;
|
|
|
|
|
unsigned endPos;
|
|
|
|
|
|
|
|
|
|
if (tc.hasSelection()) {
|
|
|
|
|
startPos = tc.selectionStart();
|
|
|
|
|
endPos = tc.selectionEnd();
|
|
|
|
|
} else {
|
|
|
|
|
tc.movePosition(QTextCursor::StartOfWord);
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
|
|
|
|
|
startPos = textCursor().position();
|
|
|
|
|
endPos = textCursor().position();
|
|
|
|
|
}
|
2010-07-08 11:34:51 +02:00
|
|
|
|
2014-01-24 16:53:16 +01:00
|
|
|
if (m_qmlJsEditorDocument->semanticInfo().isValid()) {
|
2010-07-12 17:18:05 +02:00
|
|
|
SelectedElement selectedMembers;
|
2022-05-17 16:11:03 +02:00
|
|
|
const QList<UiObjectMember *> members
|
|
|
|
|
= selectedMembers(m_qmlJsEditorDocument->semanticInfo().document, startPos, endPos);
|
2010-07-12 17:18:05 +02:00
|
|
|
if (!members.isEmpty()) {
|
2022-05-17 16:11:03 +02:00
|
|
|
for (UiObjectMember *m : members) {
|
2012-05-10 17:06:29 +02:00
|
|
|
offsets << m;
|
2010-07-12 17:18:05 +02:00
|
|
|
}
|
2010-07-08 11:34:51 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-07-12 17:18:05 +02:00
|
|
|
wordAtCursor = tc.selectedText();
|
|
|
|
|
|
|
|
|
|
emit selectedElementsChanged(offsets, wordAtCursor);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::applyFontSettings()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::applyFontSettings();
|
2014-02-06 12:59:00 +01:00
|
|
|
if (!m_qmlJsEditorDocument->isSemanticInfoOutdated())
|
2014-01-30 12:48:30 +01:00
|
|
|
updateUses();
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2010-02-15 12:27:25 +01:00
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
QString QmlJSEditorWidget::wordUnderCursor() const
|
2009-05-05 15:33:39 +02:00
|
|
|
{
|
2009-09-16 13:56:06 +02:00
|
|
|
QTextCursor tc = textCursor();
|
2013-04-18 18:21:17 +02:00
|
|
|
const QChar ch = document()->characterAt(tc.position() - 1);
|
2010-03-25 14:47:14 +01:00
|
|
|
// make sure that we're not at the start of the next word.
|
|
|
|
|
if (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
|
|
|
|
|
tc.movePosition(QTextCursor::Left);
|
2009-09-16 13:56:06 +02:00
|
|
|
tc.movePosition(QTextCursor::StartOfWord);
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
2010-03-25 14:47:14 +01:00
|
|
|
const QString word = tc.selectedText();
|
2009-09-16 13:56:06 +02:00
|
|
|
return word;
|
2009-05-05 15:33:39 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::createToolBar()
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo = new QComboBox;
|
|
|
|
|
m_outlineCombo->setMinimumContentsLength(22);
|
2014-01-30 17:18:35 +01:00
|
|
|
m_outlineCombo->setModel(m_qmlJsEditorDocument->outlineModel());
|
2010-07-12 14:45:22 +02:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
auto treeView = new QTreeView;
|
2012-06-04 12:52:49 +02:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
auto itemDelegate = new Utils::AnnotatedItemDelegate(this);
|
2012-06-04 12:52:49 +02:00
|
|
|
itemDelegate->setDelimiter(QLatin1String(" "));
|
2019-08-07 17:04:47 +02:00
|
|
|
itemDelegate->setAnnotationRole(Internal::QmlOutlineModel::AnnotationRole);
|
2012-06-04 12:52:49 +02:00
|
|
|
treeView->setItemDelegateForColumn(0, itemDelegate);
|
|
|
|
|
|
2010-07-12 14:45:22 +02:00
|
|
|
treeView->header()->hide();
|
|
|
|
|
treeView->setItemsExpandable(false);
|
2010-07-12 15:49:03 +02:00
|
|
|
treeView->setRootIsDecorated(false);
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setView(treeView);
|
2010-07-12 14:45:22 +02:00
|
|
|
treeView->expandAll();
|
|
|
|
|
|
2010-07-13 11:24:16 +02:00
|
|
|
//m_outlineCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
// Make the combo box prefer to expand
|
2010-07-13 11:24:16 +02:00
|
|
|
QSizePolicy policy = m_outlineCombo->sizePolicy();
|
2009-09-16 13:56:06 +02:00
|
|
|
policy.setHorizontalPolicy(QSizePolicy::Expanding);
|
2010-07-13 11:24:16 +02:00
|
|
|
m_outlineCombo->setSizePolicy(policy);
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2022-07-20 00:35:01 +02:00
|
|
|
connect(m_outlineCombo, &QComboBox::activated,
|
2016-06-27 22:25:11 +03:00
|
|
|
this, &QmlJSEditorWidget::jumpToOutlineElement);
|
2019-08-07 17:04:47 +02:00
|
|
|
connect(m_qmlJsEditorDocument->outlineModel(), &Internal::QmlOutlineModel::updated,
|
2016-06-27 22:25:11 +03:00
|
|
|
static_cast<QTreeView *>(m_outlineCombo->view()), &QTreeView::expandAll);
|
2014-01-30 17:18:35 +01:00
|
|
|
|
2014-08-26 17:15:37 +02:00
|
|
|
connect(this, &QmlJSEditorWidget::cursorPositionChanged,
|
2019-02-26 09:40:49 +01:00
|
|
|
&m_updateOutlineIndexTimer, QOverload<>::of(&QTimer::start));
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
insertExtraToolBarWidget(TextEditorWidget::Left, m_outlineCombo);
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2015-01-08 22:43:39 +03:00
|
|
|
class CodeModelInspector : public MemberProcessor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit CodeModelInspector(const CppComponentValue *processingValue, QTextStream *stream) :
|
|
|
|
|
m_processingValue(processingValue),
|
|
|
|
|
m_stream(stream),
|
|
|
|
|
m_indent(QLatin1String(" "))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool processProperty(const QString &name, const Value *value,
|
|
|
|
|
const PropertyInfo &propertyInfo) override
|
|
|
|
|
{
|
|
|
|
|
QString type;
|
|
|
|
|
if (const CppComponentValue *cpp = value->asCppComponentValue())
|
|
|
|
|
type = cpp->metaObject()->className();
|
|
|
|
|
else
|
|
|
|
|
type = m_processingValue->propertyType(name);
|
|
|
|
|
|
|
|
|
|
if (propertyInfo.isList())
|
|
|
|
|
type = QStringLiteral("list<%1>").arg(type);
|
|
|
|
|
|
|
|
|
|
*m_stream << m_indent;
|
|
|
|
|
if (!propertyInfo.isWriteable())
|
|
|
|
|
*m_stream << "readonly ";
|
2020-01-17 14:37:08 +01:00
|
|
|
*m_stream << "property " << type << " " << name << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool processSignal(const QString &name, const Value *value) override
|
|
|
|
|
{
|
2020-01-17 14:37:08 +01:00
|
|
|
*m_stream << m_indent << "signal " << name << stringifyFunctionParameters(value) << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool processSlot(const QString &name, const Value *value) override
|
|
|
|
|
{
|
2020-01-17 14:37:08 +01:00
|
|
|
*m_stream << m_indent << "function " << name << stringifyFunctionParameters(value) << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
bool processGeneratedSlot(const QString &name, const Value *value) override
|
|
|
|
|
{
|
|
|
|
|
*m_stream << m_indent << "/*generated*/ function " << name
|
2020-01-17 14:37:08 +01:00
|
|
|
<< stringifyFunctionParameters(value) << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
QString stringifyFunctionParameters(const Value *value) const
|
|
|
|
|
{
|
|
|
|
|
QStringList params;
|
|
|
|
|
const QmlJS::MetaFunction *metaFunction = value->asMetaFunction();
|
|
|
|
|
if (metaFunction) {
|
|
|
|
|
QStringList paramNames = metaFunction->fakeMetaMethod().parameterNames();
|
|
|
|
|
QStringList paramTypes = metaFunction->fakeMetaMethod().parameterTypes();
|
|
|
|
|
for (int i = 0; i < paramTypes.size(); ++i) {
|
|
|
|
|
QString typeAndNamePair = paramTypes.at(i);
|
|
|
|
|
if (paramNames.size() > i) {
|
|
|
|
|
QString paramName = paramNames.at(i);
|
|
|
|
|
if (!paramName.isEmpty())
|
|
|
|
|
typeAndNamePair += QLatin1Char(' ') + paramName;
|
|
|
|
|
}
|
|
|
|
|
params.append(typeAndNamePair);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QLatin1Char('(') + params.join(QLatin1String(", ")) + QLatin1Char(')');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const CppComponentValue *m_processingValue;
|
|
|
|
|
QTextStream *m_stream;
|
|
|
|
|
const QString m_indent;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const CppComponentValue *findCppComponentToInspect(const SemanticInfo &semanticInfo,
|
|
|
|
|
const unsigned cursorPosition)
|
|
|
|
|
{
|
|
|
|
|
AST::Node *node = semanticInfo.astNodeAt(cursorPosition);
|
|
|
|
|
if (!node)
|
2018-11-24 02:45:30 +01:00
|
|
|
return nullptr;
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
const ScopeChain scopeChain = semanticInfo.scopeChain(semanticInfo.rangePath(cursorPosition));
|
|
|
|
|
Evaluate evaluator(&scopeChain);
|
|
|
|
|
const Value *value = evaluator.reference(node);
|
|
|
|
|
if (!value)
|
2018-11-24 02:45:30 +01:00
|
|
|
return nullptr;
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
return value->asCppComponentValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString inspectCppComponent(const CppComponentValue *cppValue)
|
|
|
|
|
{
|
|
|
|
|
QString result;
|
|
|
|
|
QTextStream bufWriter(&result);
|
|
|
|
|
|
|
|
|
|
// for QtObject
|
|
|
|
|
QString superClassName = cppValue->metaObject()->superclassName();
|
|
|
|
|
if (superClassName.isEmpty())
|
|
|
|
|
superClassName = cppValue->metaObject()->className();
|
|
|
|
|
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << "import QtQuick " << cppValue->importVersion().toString() << '\n'
|
2015-01-08 22:43:39 +03:00
|
|
|
<< "// " << cppValue->metaObject()->className()
|
|
|
|
|
<< " imported as " << cppValue->moduleName() << " "
|
2020-01-17 14:37:08 +01:00
|
|
|
<< cppValue->importVersion().toString() << '\n'
|
|
|
|
|
<< '\n'
|
|
|
|
|
<< superClassName << " {" << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
CodeModelInspector insp(cppValue, &bufWriter);
|
|
|
|
|
cppValue->processMembers(&insp);
|
|
|
|
|
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
const int enumeratorCount = cppValue->metaObject()->enumeratorCount();
|
|
|
|
|
for (int index = cppValue->metaObject()->enumeratorOffset(); index < enumeratorCount; ++index) {
|
|
|
|
|
LanguageUtils::FakeMetaEnum enumerator = cppValue->metaObject()->enumerator(index);
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << " enum " << enumerator.name() << " {" << '\n';
|
2017-12-12 19:50:59 +03:00
|
|
|
const QStringList keys = enumerator.keys();
|
|
|
|
|
const int keysCount = keys.size();
|
|
|
|
|
for (int i = 0; i < keysCount; ++i) {
|
|
|
|
|
bufWriter << " " << keys.at(i);
|
|
|
|
|
if (i != keysCount - 1)
|
|
|
|
|
bufWriter << ',';
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << '\n';
|
2017-12-12 19:50:59 +03:00
|
|
|
}
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << " }" << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
}
|
|
|
|
|
|
2020-01-17 14:37:08 +01:00
|
|
|
bufWriter << "}" << '\n';
|
2015-01-08 22:43:39 +03:00
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSEditorWidget::inspectElementUnderCursor() const
|
|
|
|
|
{
|
|
|
|
|
const QTextCursor cursor = textCursor();
|
|
|
|
|
|
|
|
|
|
const unsigned cursorPosition = cursor.position();
|
|
|
|
|
const SemanticInfo semanticInfo = m_qmlJsEditorDocument->semanticInfo();
|
|
|
|
|
if (!semanticInfo.isValid())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const CppComponentValue *cppValue = findCppComponentToInspect(semanticInfo, cursorPosition);
|
|
|
|
|
if (!cppValue) {
|
2022-08-25 12:46:47 +02:00
|
|
|
QString title = Tr::tr("Code Model Not Available");
|
2019-07-18 14:17:25 +02:00
|
|
|
const QString documentId = QML_JS_EDITOR_PLUGIN + QStringLiteral(".NothingToShow");
|
2015-01-08 22:43:39 +03:00
|
|
|
EditorManager::openEditorWithContents(Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, &title,
|
2022-08-25 12:46:47 +02:00
|
|
|
Tr::tr("Code model not available.").toUtf8(), documentId,
|
2015-01-08 22:43:39 +03:00
|
|
|
EditorManager::IgnoreNavigationHistory);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-25 12:46:47 +02:00
|
|
|
QString title = Tr::tr("Code Model of %1").arg(cppValue->metaObject()->className());
|
2019-07-18 14:17:25 +02:00
|
|
|
const QString documentId = QML_JS_EDITOR_PLUGIN + QStringLiteral(".Class.")
|
|
|
|
|
+ cppValue->metaObject()->className();
|
2015-01-08 22:43:39 +03:00
|
|
|
IEditor *outputEditor = EditorManager::openEditorWithContents(
|
|
|
|
|
Core::Constants::K_DEFAULT_TEXT_EDITOR_ID, &title, QByteArray(),
|
2016-11-24 09:58:11 +01:00
|
|
|
documentId, EditorManager::IgnoreNavigationHistory);
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
if (!outputEditor)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto widget = qobject_cast<TextEditor::TextEditorWidget *>(outputEditor->widget());
|
|
|
|
|
if (!widget)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
widget->setReadOnly(true);
|
|
|
|
|
widget->textDocument()->setTemporary(true);
|
|
|
|
|
widget->textDocument()->setSyntaxHighlighter(new QmlJSHighlighter(widget->document()));
|
|
|
|
|
|
|
|
|
|
const QString buf = inspectCppComponent(cppValue);
|
|
|
|
|
widget->textDocument()->setPlainText(buf);
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-21 11:58:16 +01:00
|
|
|
void QmlJSEditorWidget::findLinkAt(const QTextCursor &cursor,
|
2022-06-03 15:17:33 +02:00
|
|
|
const Utils::LinkHandler &processLinkCallback,
|
2018-02-21 11:58:16 +01:00
|
|
|
bool /*resolveTarget*/,
|
|
|
|
|
bool /*inNextSplit*/)
|
2009-09-11 14:42:50 +02:00
|
|
|
{
|
2014-01-24 16:53:16 +01:00
|
|
|
const SemanticInfo semanticInfo = m_qmlJsEditorDocument->semanticInfo();
|
2010-09-08 10:11:44 +02:00
|
|
|
if (! semanticInfo.isValid())
|
2018-02-21 11:58:16 +01:00
|
|
|
return processLinkCallback(Utils::Link());
|
2010-09-08 10:11:44 +02:00
|
|
|
|
2010-02-02 13:18:56 +01:00
|
|
|
const unsigned cursorPosition = cursor.position();
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2011-08-09 12:18:56 +02:00
|
|
|
AST::Node *node = semanticInfo.astNodeAt(cursorPosition);
|
2018-02-21 11:58:16 +01:00
|
|
|
QTC_ASSERT(node, return;);
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
if (auto importAst = cast<const AST::UiImport *>(node)) {
|
2010-07-16 11:16:22 +02:00
|
|
|
// if it's a file import, link to the file
|
2022-05-17 16:11:03 +02:00
|
|
|
const QList<ImportInfo> imports = semanticInfo.document->bind()->imports();
|
|
|
|
|
for (const ImportInfo &import : imports) {
|
2013-10-16 14:59:28 +02:00
|
|
|
if (import.ast() == importAst && import.type() == ImportType::File) {
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
Utils::Link link(
|
|
|
|
|
m_modelManager->fileToSource(FilePath::fromString(import.path())));
|
2013-02-06 14:23:18 +01:00
|
|
|
link.linkTextStart = importAst->firstSourceLocation().begin();
|
|
|
|
|
link.linkTextEnd = importAst->lastSourceLocation().end();
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(Utils::Link());
|
|
|
|
|
return;
|
2010-07-16 11:16:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(Utils::Link());
|
|
|
|
|
return;
|
2010-07-16 11:16:22 +02:00
|
|
|
}
|
|
|
|
|
|
2011-08-09 11:10:53 +02:00
|
|
|
// string literals that could refer to a file link to them
|
2018-11-24 02:45:30 +01:00
|
|
|
if (auto literal = cast<const StringLiteral *>(node)) {
|
2011-09-13 09:57:24 +02:00
|
|
|
const QString &text = literal->value.toString();
|
2017-10-05 09:45:33 +02:00
|
|
|
Utils::Link link;
|
2013-02-06 14:23:18 +01:00
|
|
|
link.linkTextStart = literal->literalToken.begin();
|
|
|
|
|
link.linkTextEnd = literal->literalToken.end();
|
2022-06-20 12:35:13 +02:00
|
|
|
Utils::FilePath targetFilePath = Utils::FilePath::fromUserInput(text);
|
|
|
|
|
if (semanticInfo.snapshot.document(targetFilePath)) {
|
|
|
|
|
link.targetFilePath = targetFilePath;
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2011-08-09 11:10:53 +02:00
|
|
|
}
|
2022-06-20 12:35:13 +02:00
|
|
|
const Utils::FilePath relative = semanticInfo.document->path().pathAppended(text);
|
qmljs: avoid linking to files in the build directory
cmake creates a consistent uri structure in the build directory.
We use that as import path, but when we find a type in them we should
refer back to the original file, as editing those is dangerous because
any edit are lost with the next build.
To find the original file we use the qrc, as the qrc path is mostly
the same of as the uri path.
It is possible to add prefixes which would make an exact match fail,
so we compare the paths from the right to the left and find the
longest match.
To acheive this:
* QrcParser keeps a reversedResources, so the match from right can be
done efficiently, and provides a longestReverseMatches method
* the model manager keeps a list of all common prefixes of the
application paths (build directories), and identify all files in
build directories
* the method fileToSource identifies the files in the build directory
and tries to find the corresponding source file, warning if he
cannot find it
* fileToSource is used for follow Symbol and find usages
We could use fileToSource much more aggressively, to use to in editor
content for the files in the build directory, increasing the
consistency, but that is a more dangerous change for later.
Fixes: QTCREATORBUG-27173
Change-Id: Iea61b9825e5f6e433a7390cf2de9564b792458a5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2022-06-17 15:46:52 +02:00
|
|
|
if (relative.exists()) {
|
|
|
|
|
link.targetFilePath = m_modelManager->fileToSource(relative);
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2011-08-09 11:10:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-08 12:47:49 +02:00
|
|
|
const ScopeChain scopeChain = semanticInfo.scopeChain(semanticInfo.rangePath(cursorPosition));
|
2011-08-08 12:26:22 +02:00
|
|
|
Evaluate evaluator(&scopeChain);
|
2011-08-08 12:47:49 +02:00
|
|
|
const Value *value = evaluator.reference(node);
|
2010-02-08 12:50:10 +01:00
|
|
|
|
2022-06-20 12:35:13 +02:00
|
|
|
Utils::FilePath fileName;
|
2010-02-08 12:50:10 +01:00
|
|
|
int line = 0, column = 0;
|
|
|
|
|
|
|
|
|
|
if (! (value && value->getSourceLocation(&fileName, &line, &column)))
|
2018-02-21 11:58:16 +01:00
|
|
|
return processLinkCallback(Utils::Link());
|
2010-02-08 12:50:10 +01:00
|
|
|
|
2017-10-05 09:45:33 +02:00
|
|
|
Utils::Link link;
|
2022-06-20 12:35:13 +02:00
|
|
|
link.targetFilePath = m_modelManager->fileToSource(fileName);
|
2013-02-06 14:23:18 +01:00
|
|
|
link.targetLine = line;
|
|
|
|
|
link.targetColumn = column - 1; // adjust the column
|
2010-02-08 12:50:10 +01:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
if (auto q = AST::cast<const AST::UiQualifiedId *>(node)) {
|
|
|
|
|
for (const AST::UiQualifiedId *tail = q; tail; tail = tail->next) {
|
2010-02-02 13:18:56 +01:00
|
|
|
if (! tail->next && cursorPosition <= tail->identifierToken.end()) {
|
2013-02-06 14:23:18 +01:00
|
|
|
link.linkTextStart = tail->identifierToken.begin();
|
|
|
|
|
link.linkTextEnd = tail->identifierToken.end();
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2010-02-01 16:14:34 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
} else if (auto id = AST::cast<const AST::IdentifierExpression *>(node)) {
|
2013-02-06 14:23:18 +01:00
|
|
|
link.linkTextStart = id->firstSourceLocation().begin();
|
|
|
|
|
link.linkTextEnd = id->lastSourceLocation().end();
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2010-02-01 16:14:34 +01:00
|
|
|
|
2018-11-24 02:45:30 +01:00
|
|
|
} else if (auto mem = AST::cast<const AST::FieldMemberExpression *>(node)) {
|
2013-02-06 14:23:18 +01:00
|
|
|
link.linkTextStart = mem->lastSourceLocation().begin();
|
|
|
|
|
link.linkTextEnd = mem->lastSourceLocation().end();
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2010-02-01 16:14:34 +01:00
|
|
|
}
|
|
|
|
|
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(Utils::Link());
|
2009-09-11 14:42:50 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::findUsages()
|
2010-09-24 14:05:34 +02:00
|
|
|
{
|
2022-06-20 12:35:13 +02:00
|
|
|
m_findReferences->findUsages(textDocument()->filePath(), textCursor().position());
|
2010-09-24 14:05:34 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-06 07:30:33 +02:00
|
|
|
void QmlJSEditorWidget::renameSymbolUnderCursor()
|
2011-07-11 12:53:05 +02:00
|
|
|
{
|
2022-06-20 12:35:13 +02:00
|
|
|
m_findReferences->renameUsages(textDocument()->filePath(), textCursor().position());
|
2011-07-11 12:53:05 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::showContextPane()
|
2010-08-04 13:50:15 +02:00
|
|
|
{
|
2014-01-24 16:53:16 +01:00
|
|
|
const SemanticInfo info = m_qmlJsEditorDocument->semanticInfo();
|
|
|
|
|
if (m_contextPane && info.isValid()) {
|
|
|
|
|
Node *newNode = info.declaringMemberNoProperties(position());
|
|
|
|
|
ScopeChain scopeChain = info.scopeChain(info.rangePath(position()));
|
2014-09-01 12:54:03 +02:00
|
|
|
m_contextPane->apply(this, info.document,
|
2011-08-08 12:26:22 +02:00
|
|
|
&scopeChain,
|
|
|
|
|
newNode, false, true);
|
2010-08-04 13:50:15 +02:00
|
|
|
m_oldCursorPosition = position();
|
2019-07-18 14:17:25 +02:00
|
|
|
setRefactorMarkers(
|
|
|
|
|
RefactorMarker::filterOutType(refactorMarkers(), QT_QUICK_TOOLBAR_MARKER_ID));
|
2010-08-04 13:50:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::contextMenuEvent(QContextMenuEvent *e)
|
2009-04-22 15:21:04 +02:00
|
|
|
{
|
2013-02-18 15:12:09 +01:00
|
|
|
QPointer<QMenu> menu(new QMenu(this));
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2022-08-25 12:46:47 +02:00
|
|
|
QMenu *refactoringMenu = new QMenu(Tr::tr("Refactoring"), menu);
|
2009-09-16 13:56:06 +02:00
|
|
|
|
2014-01-24 16:53:16 +01:00
|
|
|
if (!m_qmlJsEditorDocument->isSemanticInfoOutdated()) {
|
2022-11-15 14:19:06 +01:00
|
|
|
std::unique_ptr<AssistInterface> interface = createAssistInterface(QuickFix, ExplicitlyInvoked);
|
2011-04-15 16:19:23 +02:00
|
|
|
if (interface) {
|
2014-08-20 01:47:42 +02:00
|
|
|
QScopedPointer<IAssistProcessor> processor(
|
2022-11-15 14:19:06 +01:00
|
|
|
Internal::QmlJSEditorPlugin::quickFixAssistProvider()->createProcessor(interface.get()));
|
|
|
|
|
QScopedPointer<IAssistProposal> proposal(processor->start(std::move(interface)));
|
2011-04-15 16:19:23 +02:00
|
|
|
if (!proposal.isNull()) {
|
2018-02-14 14:32:51 +01:00
|
|
|
GenericProposalModelPtr model = proposal->model().staticCast<GenericProposalModel>();
|
2011-04-15 16:19:23 +02:00
|
|
|
for (int index = 0; index < model->size(); ++index) {
|
2018-11-24 02:45:30 +01:00
|
|
|
auto item = static_cast<const AssistProposalItem *>(model->proposalItem(index));
|
2014-08-20 01:47:42 +02:00
|
|
|
QuickFixOperation::Ptr op = item->data().value<QuickFixOperation::Ptr>();
|
2011-04-15 16:19:23 +02:00
|
|
|
QAction *action = refactoringMenu->addAction(op->description());
|
2016-06-27 22:25:11 +03:00
|
|
|
connect(action, &QAction::triggered, this, [op]() { op->perform(); });
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
2010-09-16 12:57:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
refactoringMenu->setEnabled(!refactoringMenu->isEmpty());
|
|
|
|
|
|
2013-08-30 16:38:57 +02:00
|
|
|
if (ActionContainer *mcontext = ActionManager::actionContainer(Constants::M_CONTEXT)) {
|
2010-09-16 12:57:07 +02:00
|
|
|
QMenu *contextMenu = mcontext->menu();
|
2022-05-17 16:11:03 +02:00
|
|
|
const QList<QAction *> actions = contextMenu->actions();
|
|
|
|
|
for (QAction *action : actions) {
|
2010-09-16 12:57:07 +02:00
|
|
|
menu->addAction(action);
|
2013-06-03 19:31:32 +02:00
|
|
|
if (action->objectName() == QLatin1String(Constants::M_REFACTORING_MENU_INSERTION_POINT))
|
2010-09-16 12:57:07 +02:00
|
|
|
menu->addMenu(refactoringMenu);
|
2013-06-03 19:31:32 +02:00
|
|
|
if (action->objectName() == QLatin1String(Constants::SHOW_QT_QUICK_HELPER)) {
|
2014-01-24 16:53:16 +01:00
|
|
|
bool enabled = m_contextPane->isAvailable(
|
2014-09-01 12:54:03 +02:00
|
|
|
this, m_qmlJsEditorDocument->semanticInfo().document,
|
2014-01-24 16:53:16 +01:00
|
|
|
m_qmlJsEditorDocument->semanticInfo().declaringMemberNoProperties(position()));
|
2011-07-27 11:22:10 +02:00
|
|
|
action->setEnabled(enabled);
|
|
|
|
|
}
|
2010-09-16 12:57:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-09 17:35:20 +01:00
|
|
|
appendStandardContextMenuActions(menu);
|
|
|
|
|
|
2009-09-16 13:56:06 +02:00
|
|
|
menu->exec(e->globalPos());
|
2013-02-18 15:12:09 +01:00
|
|
|
delete menu;
|
2009-04-22 15:21:04 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
bool QmlJSEditorWidget::event(QEvent *e)
|
2010-07-07 13:09:30 +02:00
|
|
|
{
|
|
|
|
|
switch (e->type()) {
|
|
|
|
|
case QEvent::ShortcutOverride:
|
|
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_contextPane) {
|
2010-07-20 15:01:06 +02:00
|
|
|
if (hideContextPane()) {
|
2010-07-15 16:40:19 +02:00
|
|
|
e->accept();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
return TextEditorWidget::event(e);
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::wheelEvent(QWheelEvent *event)
|
2010-07-07 13:09:30 +02:00
|
|
|
{
|
2010-07-20 15:01:06 +02:00
|
|
|
bool visible = false;
|
|
|
|
|
if (m_contextPane && m_contextPane->widget()->isVisible())
|
|
|
|
|
visible = true;
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::wheelEvent(event);
|
2010-07-20 15:01:06 +02:00
|
|
|
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (visible)
|
2018-11-24 02:45:30 +01:00
|
|
|
m_contextPane->apply(this, m_qmlJsEditorDocument->semanticInfo().document, nullptr,
|
2014-01-24 16:53:16 +01:00
|
|
|
m_qmlJsEditorDocument->semanticInfo().declaringMemberNoProperties(m_oldCursorPosition),
|
|
|
|
|
false, true);
|
2010-07-20 15:01:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::resizeEvent(QResizeEvent *event)
|
2010-07-20 15:01:06 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::resizeEvent(event);
|
2010-07-20 15:01:06 +02:00
|
|
|
hideContextPane();
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::scrollContentsBy(int dx, int dy)
|
2010-07-20 15:01:06 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::scrollContentsBy(dx, dy);
|
2010-07-20 15:01:06 +02:00
|
|
|
hideContextPane();
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
QmlJSEditorDocument *QmlJSEditorWidget::qmlJsEditorDocument() const
|
2014-02-07 13:45:51 +01:00
|
|
|
{
|
|
|
|
|
return m_qmlJsEditorDocument;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
void QmlJSEditorWidget::semanticInfoUpdated(const SemanticInfo &semanticInfo)
|
2010-02-16 10:36:09 +01:00
|
|
|
{
|
2014-01-30 17:18:35 +01:00
|
|
|
if (isVisible()) {
|
|
|
|
|
// trigger semantic highlighting and model update if necessary
|
2014-08-01 23:31:56 +02:00
|
|
|
textDocument()->triggerPendingUpdates();
|
2014-01-30 17:18:35 +01:00
|
|
|
}
|
2014-02-06 12:59:00 +01:00
|
|
|
|
2010-08-05 15:35:52 +02:00
|
|
|
if (m_contextPane) {
|
2014-01-24 16:53:16 +01:00
|
|
|
Node *newNode = semanticInfo.declaringMemberNoProperties(position());
|
2010-08-05 15:35:52 +02:00
|
|
|
if (newNode) {
|
2018-11-24 02:45:30 +01:00
|
|
|
m_contextPane->apply(this, semanticInfo.document, nullptr, newNode, true);
|
2014-08-26 17:15:37 +02:00
|
|
|
m_contextPaneTimer.start(); //update text marker
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-29 13:53:08 +01:00
|
|
|
updateUses();
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
QModelIndex QmlJSEditorWidget::indexForPosition(unsigned cursorPosition, const QModelIndex &rootIndex) const
|
2010-07-12 14:45:22 +02:00
|
|
|
{
|
|
|
|
|
QModelIndex lastIndex = rootIndex;
|
|
|
|
|
|
2019-08-07 17:04:47 +02:00
|
|
|
Internal::QmlOutlineModel *model = m_qmlJsEditorDocument->outlineModel();
|
2014-01-30 17:18:35 +01:00
|
|
|
const int rowCount = model->rowCount(rootIndex);
|
2010-07-12 14:45:22 +02:00
|
|
|
for (int i = 0; i < rowCount; ++i) {
|
2014-01-30 17:18:35 +01:00
|
|
|
QModelIndex childIndex = model->index(i, 0, rootIndex);
|
2020-02-28 17:51:32 +01:00
|
|
|
SourceLocation location = model->sourceLocation(childIndex);
|
2010-07-12 14:45:22 +02:00
|
|
|
|
|
|
|
|
if ((cursorPosition >= location.offset)
|
|
|
|
|
&& (cursorPosition <= location.offset + location.length)) {
|
|
|
|
|
lastIndex = childIndex;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2010-07-07 13:09:30 +02:00
|
|
|
}
|
2010-07-12 14:45:22 +02:00
|
|
|
|
|
|
|
|
if (lastIndex != rootIndex) {
|
|
|
|
|
// recurse
|
|
|
|
|
lastIndex = indexForPosition(cursorPosition, lastIndex);
|
|
|
|
|
}
|
|
|
|
|
return lastIndex;
|
2010-02-16 10:36:09 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
bool QmlJSEditorWidget::hideContextPane()
|
2010-07-20 15:01:06 +02:00
|
|
|
{
|
|
|
|
|
bool b = (m_contextPane) && m_contextPane->widget()->isVisible();
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (b)
|
2018-11-24 02:45:30 +01:00
|
|
|
m_contextPane->apply(this, m_qmlJsEditorDocument->semanticInfo().document,
|
|
|
|
|
nullptr, nullptr, false);
|
2010-07-20 15:01:06 +02:00
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-15 14:19:06 +01:00
|
|
|
std::unique_ptr<AssistInterface> QmlJSEditorWidget::createAssistInterface(
|
2015-02-03 23:48:57 +02:00
|
|
|
AssistKind assistKind,
|
|
|
|
|
AssistReason reason) const
|
2011-04-15 16:19:23 +02:00
|
|
|
{
|
2015-02-03 23:48:57 +02:00
|
|
|
if (assistKind == Completion) {
|
2022-11-15 14:19:06 +01:00
|
|
|
return std::make_unique<QmlJSCompletionAssistInterface>(
|
|
|
|
|
textCursor(), textDocument()->filePath(), reason, m_qmlJsEditorDocument->semanticInfo());
|
2015-02-03 23:48:57 +02:00
|
|
|
} else if (assistKind == QuickFix) {
|
2022-11-15 14:19:06 +01:00
|
|
|
return std::make_unique<Internal::QmlJSQuickFixAssistInterface>(
|
|
|
|
|
const_cast<QmlJSEditorWidget *>(this), reason);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
2018-11-24 02:45:30 +01:00
|
|
|
return nullptr;
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
2011-09-14 22:16:28 +02:00
|
|
|
|
2014-08-25 20:01:46 +02:00
|
|
|
QString QmlJSEditorWidget::foldReplacementText(const QTextBlock &block) const
|
2011-09-14 22:16:28 +02:00
|
|
|
{
|
2011-10-24 09:28:02 +02:00
|
|
|
const int curlyIndex = block.text().indexOf(QLatin1Char('{'));
|
|
|
|
|
|
2014-01-24 16:53:16 +01:00
|
|
|
if (curlyIndex != -1 && m_qmlJsEditorDocument->semanticInfo().isValid()) {
|
2011-10-24 09:28:02 +02:00
|
|
|
const int pos = block.position() + curlyIndex;
|
2014-01-24 16:53:16 +01:00
|
|
|
Node *node = m_qmlJsEditorDocument->semanticInfo().rangeAt(pos);
|
2011-10-24 09:28:02 +02:00
|
|
|
|
|
|
|
|
const QString objectId = idOfObject(node);
|
|
|
|
|
if (!objectId.isEmpty())
|
|
|
|
|
return QLatin1String("id: ") + objectId + QLatin1String("...");
|
2011-09-14 22:16:28 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:48:57 +02:00
|
|
|
return TextEditorWidget::foldReplacementText(block);
|
2011-09-14 22:16:28 +02:00
|
|
|
}
|
2013-06-03 19:31:32 +02:00
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// QmlJSEditor
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
QmlJSEditor::QmlJSEditor()
|
|
|
|
|
{
|
2017-01-30 14:59:10 +01:00
|
|
|
addContext(ProjectExplorer::Constants::QMLJS_LANGUAGE_ID);
|
2014-08-20 01:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
2019-07-18 16:19:33 +02:00
|
|
|
QmlJSEditorDocument *QmlJSEditor::qmlJSDocument() const
|
2014-08-20 01:47:42 +02:00
|
|
|
{
|
2019-07-18 16:19:33 +02:00
|
|
|
return qobject_cast<QmlJSEditorDocument *>(document());
|
|
|
|
|
}
|
2014-10-14 10:10:58 +02:00
|
|
|
|
2019-07-18 16:19:33 +02:00
|
|
|
bool QmlJSEditor::isDesignModePreferred() const
|
|
|
|
|
{
|
2014-10-14 10:10:58 +02:00
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
// stay in design mode if we are there
|
2019-07-18 16:19:33 +02:00
|
|
|
const Id mode = ModeManager::currentModeId();
|
|
|
|
|
return qmlJSDocument()->isDesignModePreferred() || mode == Core::Constants::MODE_DESIGN;
|
2014-08-20 01:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// QmlJSEditorFactory
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
QmlJSEditorFactory::QmlJSEditorFactory()
|
2019-07-18 16:19:33 +02:00
|
|
|
: QmlJSEditorFactory(Constants::C_QMLJSEDITOR_ID)
|
|
|
|
|
{}
|
|
|
|
|
|
2020-06-26 13:59:38 +02:00
|
|
|
QmlJSEditorFactory::QmlJSEditorFactory(Utils::Id _id)
|
2014-08-20 01:47:42 +02:00
|
|
|
{
|
2019-07-18 16:19:33 +02:00
|
|
|
setId(_id);
|
2019-07-18 14:17:25 +02:00
|
|
|
setDisplayName(QCoreApplication::translate("OpenWith::Editors", "QMLJS Editor"));
|
2014-08-20 01:47:42 +02:00
|
|
|
|
|
|
|
|
addMimeType(QmlJSTools::Constants::QML_MIMETYPE);
|
|
|
|
|
addMimeType(QmlJSTools::Constants::QMLPROJECT_MIMETYPE);
|
|
|
|
|
addMimeType(QmlJSTools::Constants::QBS_MIMETYPE);
|
|
|
|
|
addMimeType(QmlJSTools::Constants::QMLTYPES_MIMETYPE);
|
|
|
|
|
addMimeType(QmlJSTools::Constants::JS_MIMETYPE);
|
2014-08-23 00:19:48 +02:00
|
|
|
|
2019-07-18 16:19:33 +02:00
|
|
|
setDocumentCreator([this]() { return new QmlJSEditorDocument(id()); });
|
2014-08-23 00:19:48 +02:00
|
|
|
setEditorWidgetCreator([]() { return new QmlJSEditorWidget; });
|
|
|
|
|
setEditorCreator([]() { return new QmlJSEditor; });
|
2019-08-08 17:15:16 +02:00
|
|
|
setAutoCompleterCreator([]() { return new AutoCompleter; });
|
2017-04-24 16:01:14 +02:00
|
|
|
setCommentDefinition(Utils::CommentDefinition::CppStyle);
|
2014-10-15 00:36:39 +02:00
|
|
|
setParenthesesMatchingEnabled(true);
|
|
|
|
|
setCodeFoldingSupported(true);
|
2014-08-23 00:19:48 +02:00
|
|
|
|
2014-09-30 13:08:05 +02:00
|
|
|
addHoverHandler(new QmlJSHoverHandler);
|
2022-11-14 10:51:06 +01:00
|
|
|
addHoverHandler(new ColorPreviewHoverHandler);
|
2014-10-01 22:39:47 +02:00
|
|
|
setCompletionAssistProvider(new QmlJSCompletionAssistProvider);
|
2014-09-30 13:08:05 +02:00
|
|
|
|
2014-09-02 12:25:20 +02:00
|
|
|
setEditorActionHandlers(TextEditorActionHandler::Format
|
2020-05-06 07:30:33 +02:00
|
|
|
| TextEditorActionHandler::UnCommentSelection
|
|
|
|
|
| TextEditorActionHandler::UnCollapseAll
|
|
|
|
|
| TextEditorActionHandler::FollowSymbolUnderCursor
|
2022-09-23 11:23:17 +02:00
|
|
|
| TextEditorActionHandler::RenameSymbol
|
|
|
|
|
| TextEditorActionHandler::FindUsage);
|
2017-04-24 15:52:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void QmlJSEditorFactory::decorateEditor(TextEditorWidget *editor)
|
|
|
|
|
{
|
|
|
|
|
editor->textDocument()->setSyntaxHighlighter(new QmlJSHighlighter);
|
2019-08-07 17:04:47 +02:00
|
|
|
editor->textDocument()->setIndenter(new Internal::Indenter(editor->textDocument()->document()));
|
2019-08-08 17:15:16 +02:00
|
|
|
editor->setAutoCompleter(new AutoCompleter);
|
2014-08-20 01:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-03 19:31:32 +02:00
|
|
|
} // namespace QmlJSEditor
|