2022-08-19 15:59:36 +02:00
|
|
|
// Copyright (C) 2016 The Qt Company Ltd.
|
2022-12-21 10:12:09 +01:00
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only 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>
|
2022-11-30 18:40:05 +01:00
|
|
|
#include <projectexplorer/project.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
2022-11-30 18:40:05 +01:00
|
|
|
#include <projectexplorer/projectnodes.h>
|
|
|
|
|
#include <projectexplorer/projecttree.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>
|
2023-02-17 23:21:31 +01:00
|
|
|
#include <coreplugin/coreplugintr.h>
|
2014-08-20 01:47:42 +02:00
|
|
|
#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>
|
2024-01-16 12:44:19 +01:00
|
|
|
#include <texteditor/snippets/snippetprovider.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>
|
2009-12-02 17:59:27 +01:00
|
|
|
#include <utils/changeset.h>
|
2023-10-19 17:17:37 +02:00
|
|
|
#include <utils/delegates.h>
|
|
|
|
|
#include <utils/mimeconstants.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
|
|
|
|
2023-09-01 11:40:37 +02:00
|
|
|
#include <languageclient/languageclientmanager.h>
|
|
|
|
|
#include <languageclient/locatorfilter.h>
|
|
|
|
|
#include <languageclient/languageclientsymbolsupport.h>
|
|
|
|
|
|
2014-08-20 01:47:42 +02:00
|
|
|
#include <QComboBox>
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
#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 {
|
|
|
|
|
|
2023-09-04 13:46:04 +02:00
|
|
|
static LanguageClient::Client *getQmllsClient(const Utils::FilePath &fileName)
|
|
|
|
|
{
|
|
|
|
|
// the value in disableBuiltinCodemodel is only valid when useQmlls is enabled
|
2024-06-19 17:55:01 +02:00
|
|
|
if (QmlJsEditingSettings::get().useQmlls()
|
|
|
|
|
&& !QmlJsEditingSettings::get().disableBuiltinCodemodel())
|
2023-09-04 13:46:04 +02:00
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
|
|
auto client = LanguageClient::LanguageClientManager::clientForFilePath(fileName);
|
|
|
|
|
return client;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
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();
|
2023-09-01 15:10:50 +02:00
|
|
|
m_contextPane = QuickToolBar::instance();
|
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));
|
2023-09-01 15:18:36 +02:00
|
|
|
connect(m_contextPane, &QuickToolBar::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
|
|
|
{
|
2023-10-19 17:17:37 +02:00
|
|
|
using namespace Utils::Constants;
|
|
|
|
|
QStringList qmlTypes = {QML_MIMETYPE, QBS_MIMETYPE, QMLTYPES_MIMETYPE, QMLUI_MIMETYPE};
|
2020-02-20 15:19:12 +01:00
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2024-06-06 09:30:17 +02:00
|
|
|
if (!m_outlineCombo)
|
|
|
|
|
return;
|
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
|
|
|
{
|
2024-06-06 09:30:17 +02:00
|
|
|
if (!m_outlineCombo)
|
|
|
|
|
return;
|
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
|
|
|
|
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()) {
|
2023-06-09 13:15:20 +02:00
|
|
|
RefactorMarkers markers;
|
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
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-09 13:15:20 +02:00
|
|
|
setRefactorMarkers(markers, QT_QUICK_TOOLBAR_MARKER_ID);
|
2010-09-01 11:54:35 +02:00
|
|
|
} else if (oldNode != newNode) {
|
2023-06-09 13:15:20 +02:00
|
|
|
clearRefactorMarkers(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));
|
2024-06-06 09:30:17 +02:00
|
|
|
connect(this, &QmlJSEditorWidget::toolbarOutlineChanged,
|
|
|
|
|
this, &QmlJSEditorWidget::updateOutline);
|
|
|
|
|
|
|
|
|
|
setToolbarOutline(m_outlineCombo);
|
|
|
|
|
}
|
2009-04-22 15:21:04 +02:00
|
|
|
|
2024-06-06 09:30:17 +02:00
|
|
|
void QmlJSEditorWidget::updateOutline(QWidget *newOutline)
|
|
|
|
|
{
|
|
|
|
|
if (!newOutline) {
|
|
|
|
|
createToolBar();
|
|
|
|
|
} else if (newOutline != m_outlineCombo){
|
|
|
|
|
m_outlineCombo = nullptr;
|
|
|
|
|
}
|
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);
|
2023-05-08 17:11:54 +02:00
|
|
|
widget->textDocument()->resetSyntaxHighlighter([] { return new QmlJSHighlighter(); });
|
2015-01-08 22:43:39 +03:00
|
|
|
|
|
|
|
|
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,
|
2023-09-04 13:46:04 +02:00
|
|
|
bool resolveTarget,
|
2018-02-21 11:58:16 +01:00
|
|
|
bool /*inNextSplit*/)
|
2009-09-11 14:42:50 +02:00
|
|
|
{
|
2023-09-04 13:46:04 +02:00
|
|
|
if (auto client = getQmllsClient(textDocument()->filePath())) {
|
2023-09-05 11:14:10 +02:00
|
|
|
client->findLinkAt(textDocument(),
|
|
|
|
|
cursor,
|
|
|
|
|
processLinkCallback,
|
|
|
|
|
resolveTarget,
|
|
|
|
|
LanguageClient::LinkTarget::SymbolDef);
|
2023-09-04 13:46:04 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2022-12-07 23:19:07 +01:00
|
|
|
const ProjectExplorer::Project * const project = ProjectExplorer::ProjectTree::currentProject();
|
|
|
|
|
ProjectExplorer::ProjectNode* projectRootNode = nullptr;
|
|
|
|
|
if (project) {
|
|
|
|
|
projectRootNode = project->rootProjectNode();
|
|
|
|
|
}
|
|
|
|
|
|
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();
|
2022-11-30 18:40:05 +01:00
|
|
|
if (text.startsWith("qrc:/")) {
|
2022-12-07 23:19:07 +01:00
|
|
|
if (projectRootNode) {
|
|
|
|
|
const ProjectExplorer::Node * const nodeForPath = projectRootNode->findNode(
|
2022-11-30 18:40:05 +01:00
|
|
|
[qrcPath = text.mid(text.indexOf(':') + 1)](ProjectExplorer::Node *n) {
|
|
|
|
|
if (!n->asFileNode())
|
|
|
|
|
return false;
|
|
|
|
|
const auto qrcNode = dynamic_cast<ProjectExplorer::ResourceFileNode *>(n);
|
|
|
|
|
return qrcNode && qrcNode->qrcPath() == qrcPath;
|
|
|
|
|
});
|
|
|
|
|
if (nodeForPath) {
|
|
|
|
|
Link link(nodeForPath->filePath());
|
|
|
|
|
link.linkTextStart = literal->firstSourceLocation().begin();
|
|
|
|
|
link.linkTextEnd = literal->lastSourceLocation().end();
|
|
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-11-30 18:32:04 +01:00
|
|
|
|
|
|
|
|
if (text.startsWith("https:/") || text.startsWith("http:/")) {
|
2023-01-13 11:36:24 +01:00
|
|
|
Link link = Link::fromString(text);
|
2022-11-30 18:32:04 +01:00
|
|
|
link.linkTextStart = literal->literalToken.begin();
|
|
|
|
|
link.linkTextEnd = literal->literalToken.end();
|
|
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
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) {
|
2022-12-07 23:19:07 +01:00
|
|
|
if (tail->next || !(cursorPosition <= tail->identifierToken.end())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
link.linkTextStart = tail->identifierToken.begin();
|
|
|
|
|
link.linkTextEnd = tail->identifierToken.end();
|
|
|
|
|
|
|
|
|
|
if (!value->asCppComponentValue() || !projectRootNode) {
|
2018-02-21 11:58:16 +01:00
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
2010-02-01 16:14:34 +01:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 23:19:07 +01:00
|
|
|
const ProjectExplorer::Node * const nodeForPath = projectRootNode->findNode(
|
|
|
|
|
[&fileName](ProjectExplorer::Node *n) {
|
|
|
|
|
const auto fileNode = n->asFileNode();
|
|
|
|
|
if (!fileNode)
|
|
|
|
|
return false;
|
|
|
|
|
Utils::FilePath filePath = n->filePath();
|
|
|
|
|
return filePath.endsWith(fileName.toUserOutput());
|
|
|
|
|
});
|
|
|
|
|
if (nodeForPath) {
|
|
|
|
|
link.targetFilePath = nodeForPath->filePath();
|
|
|
|
|
processLinkCallback(link);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// else we will process an empty link below to avoid an error dialog
|
|
|
|
|
}
|
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
|
|
|
{
|
2023-09-01 11:40:37 +02:00
|
|
|
const Utils::FilePath fileName = textDocument()->filePath();
|
|
|
|
|
|
|
|
|
|
if (auto client = getQmllsClient(fileName)) {
|
|
|
|
|
client->symbolSupport().findUsages(textDocument(), textCursor());
|
|
|
|
|
} else {
|
|
|
|
|
const int offset = textCursor().position();
|
|
|
|
|
m_findReferences->findUsages(fileName, offset);
|
|
|
|
|
}
|
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
|
|
|
{
|
2023-09-01 11:40:37 +02:00
|
|
|
const Utils::FilePath fileName = textDocument()->filePath();
|
|
|
|
|
|
|
|
|
|
if (auto client = getQmllsClient(fileName)) {
|
|
|
|
|
client->symbolSupport().renameSymbol(textDocument(), textCursor(), QString());
|
|
|
|
|
} else {
|
|
|
|
|
const int offset = textCursor().position();
|
|
|
|
|
m_findReferences->renameUsages(fileName, offset);
|
|
|
|
|
}
|
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();
|
2023-06-09 13:15:20 +02:00
|
|
|
clearRefactorMarkers(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(
|
2024-01-16 09:28:44 +01:00
|
|
|
Internal::quickFixAssistProvider()->createProcessor(interface.get()));
|
2022-11-15 14:19:06 +01:00
|
|
|
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
|
|
|
}
|
2023-04-28 14:04:50 +02:00
|
|
|
return TextEditorWidget::createAssistInterface(assistKind, reason);
|
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);
|
2023-02-17 23:21:31 +01:00
|
|
|
setDisplayName(::Core::Tr::tr("QMLJS Editor"));
|
2014-08-20 01:47:42 +02:00
|
|
|
|
2023-10-19 17:17:37 +02:00
|
|
|
using namespace Utils::Constants;
|
|
|
|
|
addMimeType(QML_MIMETYPE);
|
|
|
|
|
addMimeType(QMLPROJECT_MIMETYPE);
|
|
|
|
|
addMimeType(QMLTYPES_MIMETYPE);
|
|
|
|
|
addMimeType(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
|
|
|
|
2024-03-27 14:00:21 +01:00
|
|
|
setOptionalActionMask(OptionalActions::Format
|
|
|
|
|
| OptionalActions::UnCommentSelection
|
|
|
|
|
| OptionalActions::UnCollapseAll
|
|
|
|
|
| OptionalActions::FollowSymbolUnderCursor
|
|
|
|
|
| OptionalActions::RenameSymbol
|
|
|
|
|
| OptionalActions::FindUsage);
|
2017-04-24 15:52:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-16 12:44:19 +01:00
|
|
|
static void decorateEditor(TextEditorWidget *editor)
|
2017-04-24 15:52:04 +02:00
|
|
|
{
|
2023-05-08 17:11:54 +02:00
|
|
|
editor->textDocument()->resetSyntaxHighlighter([] { return new QmlJSHighlighter(); });
|
2023-12-21 16:47:22 +01:00
|
|
|
editor->textDocument()->setIndenter(createQmlJsIndenter(editor->textDocument()->document()));
|
2019-08-08 17:15:16 +02:00
|
|
|
editor->setAutoCompleter(new AutoCompleter);
|
2014-08-20 01:47:42 +02:00
|
|
|
}
|
|
|
|
|
|
2024-01-16 12:44:19 +01:00
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
void inspectElement()
|
|
|
|
|
{
|
|
|
|
|
if (auto widget = qobject_cast<QmlJSEditorWidget *>(EditorManager::currentEditor()->widget()))
|
|
|
|
|
widget->inspectElementUnderCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void showContextPane()
|
|
|
|
|
{
|
|
|
|
|
if (auto editor = qobject_cast<QmlJSEditorWidget*>(EditorManager::currentEditor()->widget()))
|
|
|
|
|
editor->showContextPane();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setupQmlJSEditor()
|
|
|
|
|
{
|
|
|
|
|
static QmlJSEditorFactory theQmlJSEditorFactory;
|
|
|
|
|
|
|
|
|
|
TextEditor::SnippetProvider::registerGroup(Constants::QML_SNIPPETS_GROUP_ID,
|
|
|
|
|
Tr::tr("QML", "SnippetProvider"),
|
|
|
|
|
&decorateEditor);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace Internal
|
|
|
|
|
|
2013-06-03 19:31:32 +02:00
|
|
|
} // namespace QmlJSEditor
|