qmljseditorplugin.cpp 15 KB
Newer Older
1
2
3
4
/**************************************************************************
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6
**
7
** Contact: Nokia Corporation (qt-info@nokia.com)
8
9
10
11
**
**
** GNU Lesser General Public License Usage
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
30
31
32
**
**************************************************************************/

33
#include "qmljseditorplugin.h"
34
#include "qmljshighlighter.h"
35
36
37
#include "qmljseditor.h"
#include "qmljseditorconstants.h"
#include "qmljseditorfactory.h"
Roberto Raggi's avatar
Roberto Raggi committed
38
#include "qmljshoverhandler.h"
Erik Verbruggen's avatar
Erik Verbruggen committed
39
#include "qmlfilewizard.h"
40
#include "jsfilewizard.h"
41
#include "qmljsoutline.h"
42
#include "qmljspreviewrunner.h"
43
#include "qmljssnippetprovider.h"
44
#include "qmltaskmanager.h"
45
46
#include "quicktoolbar.h"
#include "quicktoolbarsettingspage.h"
Leandro Melo's avatar
Leandro Melo committed
47
48
#include "qmljscompletionassist.h"
#include "qmljsquickfixassist.h"
49

50
51
#include <qmljs/qmljsicons.h>
#include <qmljs/qmljsmodelmanagerinterface.h>
52
#include <qmljs/qmljsreformatter.h>
53
#include <qmljstools/qmljstoolsconstants.h>
54

55
#include <qmldesigner/qmldesignerconstants.h>
56
57
58
59

#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/mimedatabase.h>
60
#include <coreplugin/id.h>
61
#include <coreplugin/fileiconprovider.h>
62
#include <coreplugin/actionmanager/actionmanager.h>
63
64
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
65
#include <coreplugin/editormanager/editormanager.h>
66
#include <projectexplorer/taskhub.h>
67
68
69
70
71
72
73
74
75
76
#include <extensionsystem/pluginmanager.h>
#include <texteditor/texteditorconstants.h>
#include <texteditor/texteditorsettings.h>
#include <texteditor/textfilewizard.h>
#include <texteditor/texteditoractionhandler.h>
#include <utils/qtcassert.h>

#include <QtCore/QtPlugin>
#include <QtCore/QDebug>
#include <QtCore/QSettings>
77
78
#include <QtCore/QDir>
#include <QtCore/QCoreApplication>
79
#include <QtCore/QTimer>
80
#include <QtGui/QMenu>
81
82
#include <QtGui/QAction>

83
84
85
using namespace QmlJSEditor;
using namespace QmlJSEditor::Internal;
using namespace QmlJSEditor::Constants;
86

87
88
89
90
enum {
    QUICKFIX_INTERVAL = 20
};

Leandro Melo's avatar
Leandro Melo committed
91
92
void registerQuickFixes(ExtensionSystem::IPlugin *plugIn);

93
QmlJSEditorPlugin *QmlJSEditorPlugin::m_instance = 0;
94

95
QmlJSEditorPlugin::QmlJSEditorPlugin() :
96
        m_modelManager(0),
97
    m_editor(0),
Leandro Melo's avatar
Leandro Melo committed
98
    m_actionHandler(0),
99
100
101
    m_quickFixAssistProvider(0),
    m_reformatFileAction(0),
    m_currentEditor(0)
102
103
104
105
{
    m_instance = this;
}

106
QmlJSEditorPlugin::~QmlJSEditorPlugin()
107
108
109
110
111
112
{
    removeObject(m_editor);
    delete m_actionHandler;
    m_instance = 0;
}

113
114
115
116
117
118
119
120
121
122
123
124
/*! Copied from cppplugin.cpp */
static inline
Core::Command *createSeparator(Core::ActionManager *am,
                               QObject *parent,
                               Core::Context &context,
                               const char *id)
{
    QAction *separator = new QAction(parent);
    separator->setSeparator(true);
    return am->registerAction(separator, Core::Id(id), context);
}

hjk's avatar
hjk committed
125
bool QmlJSEditorPlugin::initialize(const QStringList & /*arguments*/, QString *errorMessage)
126
127
{
    Core::ICore *core = Core::ICore::instance();
hjk's avatar
hjk committed
128
    if (!core->mimeDatabase()->addMimeTypes(QLatin1String(":/qmljseditor/QmlJSEditor.mimetypes.xml"), errorMessage))
129
        return false;
130

131
    m_modelManager = QmlJS::ModelManagerInterface::instance();
132
    addAutoReleasedObject(new QmlJSSnippetProvider);
133

134
135
136
137
138
139
140
141
142
143
144
145
146
147
    // QML task updating manager
    m_qmlTaskManager = new QmlTaskManager;
    addAutoReleasedObject(m_qmlTaskManager);
    connect(m_modelManager, SIGNAL(documentChangedOnDisk(QmlJS::Document::Ptr)),
            m_qmlTaskManager, SLOT(updateMessages()));
    // recompute messages when information about libraries changes
    connect(m_modelManager, SIGNAL(libraryInfoUpdated(QString,QmlJS::LibraryInfo)),
            m_qmlTaskManager, SLOT(updateMessages()));
    // recompute messages when project data changes (files added or removed)
    connect(m_modelManager, SIGNAL(projectInfoUpdated(ProjectInfo)),
            m_qmlTaskManager, SLOT(updateMessages()));
    connect(m_modelManager, SIGNAL(aboutToRemoveFiles(QStringList)),
            m_qmlTaskManager, SLOT(documentsRemoved(QStringList)));

148
    Core::Context context(QmlJSEditor::Constants::C_QMLJSEDITOR_ID);
149

150
    m_editor = new QmlJSEditorFactory(this);
151
152
    addObject(m_editor);

153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
    Core::BaseFileWizardParameters qmlWizardParameters(Core::IWizard::FileWizard);
    qmlWizardParameters.setCategory(QLatin1String(Constants::WIZARD_CATEGORY_QML));
    qmlWizardParameters.setDisplayCategory(QCoreApplication::translate("QmlJsEditor", Constants::WIZARD_TR_CATEGORY_QML));
    qmlWizardParameters.setDescription(tr("Creates a QML file."));
    qmlWizardParameters.setDisplayName(tr("QML File"));
    qmlWizardParameters.setId(QLatin1String("Q.Qml"));
    addAutoReleasedObject(new QmlFileWizard(qmlWizardParameters, core));

    Core::BaseFileWizardParameters jsWizardParameters(Core::IWizard::FileWizard);
    jsWizardParameters.setCategory(QLatin1String(Constants::WIZARD_CATEGORY_QML));
    jsWizardParameters.setDisplayCategory(QCoreApplication::translate("QmlJsEditor", Constants::WIZARD_TR_CATEGORY_QML));
    jsWizardParameters.setDescription(tr("Creates a JavaScript file."));
    jsWizardParameters.setDisplayName(tr("JS File"));
    jsWizardParameters.setId(QLatin1String("Z.Js"));
    addAutoReleasedObject(new JsFileWizard(jsWizardParameters, core));
168

169
    m_actionHandler = new TextEditor::TextEditorActionHandler(QmlJSEditor::Constants::C_QMLJSEDITOR_ID,
170
171
172
          TextEditor::TextEditorActionHandler::Format
        | TextEditor::TextEditorActionHandler::UnCommentSelection
        | TextEditor::TextEditorActionHandler::UnCollapseAll);
173
    m_actionHandler->initializeActions();
174

175
    Core::ActionManager *am =  core->actionManager();
176
    Core::ActionContainer *contextMenu = am->createMenu(QmlJSEditor::Constants::M_CONTEXT);
177
178
179
180
    Core::ActionContainer *qmlToolsMenu = am->actionContainer(Core::Id(QmlJSTools::Constants::M_TOOLS_QMLJS));

    Core::Context globalContext(Core::Constants::C_GLOBAL);
    qmlToolsMenu->addAction(createSeparator(am, this, globalContext, QmlJSEditor::Constants::SEPARATOR3));
181

182
    Core::Command *cmd;
183
184
185
186
    QAction *followSymbolUnderCursorAction = new QAction(tr("Follow Symbol Under Cursor"), this);
    cmd = am->registerAction(followSymbolUnderCursorAction, Constants::FOLLOW_SYMBOL_UNDER_CURSOR, context);
    cmd->setDefaultKeySequence(QKeySequence(Qt::Key_F2));
    connect(followSymbolUnderCursorAction, SIGNAL(triggered()), this, SLOT(followSymbolUnderCursor()));
187
    contextMenu->addAction(cmd);
188
    qmlToolsMenu->addAction(cmd);
189

190
191
192
193
194
195
196
    QAction *findUsagesAction = new QAction(tr("Find Usages"), this);
    cmd = am->registerAction(findUsagesAction, Constants::FIND_USAGES, context);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+U")));
    connect(findUsagesAction, SIGNAL(triggered()), this, SLOT(findUsages()));
    contextMenu->addAction(cmd);
    qmlToolsMenu->addAction(cmd);

197
    QAction *renameUsagesAction = new QAction(tr("Rename Symbol Under Cursor"), this);
198
199
200
201
202
203
    cmd = am->registerAction(renameUsagesAction, Constants::RENAME_USAGES, context);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+R")));
    connect(renameUsagesAction, SIGNAL(triggered()), this, SLOT(renameUsages()));
    contextMenu->addAction(cmd);
    qmlToolsMenu->addAction(cmd);

204
205
206
207
208
209
    QAction *semanticScan = new QAction(tr("Run Checks"), this);
    cmd = am->registerAction(semanticScan, Core::Id(Constants::RUN_SEMANTIC_SCAN), globalContext);
    cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+C")));
    connect(semanticScan, SIGNAL(triggered()), this, SLOT(runSemanticScan()));
    qmlToolsMenu->addAction(cmd);

210
    m_reformatFileAction = new QAction(tr("Reformat File"), this);
211
    cmd = am->registerAction(m_reformatFileAction, Core::Id(Constants::REFORMAT_FILE), context);
212
213
214
    connect(m_reformatFileAction, SIGNAL(triggered()), this, SLOT(reformatFile()));
    qmlToolsMenu->addAction(cmd);

215
    QAction *showQuickToolbar = new QAction(tr("Show Qt Quick Toolbar"), this);
216
    cmd = am->registerAction(showQuickToolbar, Constants::SHOW_QT_QUICK_HELPER, context);
217
218
219
#ifdef Q_WS_MACX
    cmd->setDefaultKeySequence(QKeySequence(Qt::META + Qt::ALT + Qt::Key_Space));
#else
220
    cmd->setDefaultKeySequence(QKeySequence(Qt::CTRL + Qt::ALT + Qt::Key_Space));
221
#endif
222
223
    connect(showQuickToolbar, SIGNAL(triggered()), this, SLOT(showContextPane()));
    contextMenu->addAction(cmd);
224
225
226
227
228
229
230
231
232
    qmlToolsMenu->addAction(cmd);

    // Insert marker for "Refactoring" menu:
    Core::Command *sep = createSeparator(am, this, globalContext,
                                         Constants::SEPARATOR1);
    sep->action()->setObjectName(Constants::M_REFACTORING_MENU_INSERTION_POINT);
    contextMenu->addAction(sep);
    contextMenu->addAction(createSeparator(am, this, globalContext,
                                           Constants::SEPARATOR2));
233

234
235
236
    cmd = am->command(TextEditor::Constants::AUTO_INDENT_SELECTION);
    contextMenu->addAction(cmd);

237
238
239
    cmd = am->command(TextEditor::Constants::UN_COMMENT_SELECTION);
    contextMenu->addAction(cmd);

Leandro Melo's avatar
Leandro Melo committed
240
241
242
    m_quickFixAssistProvider = new QmlJSQuickFixAssistProvider;
    addAutoReleasedObject(m_quickFixAssistProvider);
    addAutoReleasedObject(new QmlJSCompletionAssistProvider);
243

244
    addAutoReleasedObject(new HoverHandler);
245

hjk's avatar
hjk committed
246
    errorMessage->clear();
247

248
    Core::FileIconProvider *iconProvider = Core::FileIconProvider::instance();
249
    iconProvider->registerIconOverlayForSuffix(QIcon(QLatin1String(":/qmljseditor/images/qmlfile.png")), "qml");
250

Leandro Melo's avatar
Leandro Melo committed
251
    registerQuickFixes(this);
252

253
254
    addAutoReleasedObject(new QmlJSOutlineWidgetFactory);

255
256
257
    addAutoReleasedObject(new QuickToolBar);
    addAutoReleasedObject(new Internal::QuickToolBarSettingsPage);

258
259
    connect(core->editorManager(), SIGNAL(currentEditorChanged(Core::IEditor*)), SLOT(currentEditorChanged(Core::IEditor*)));

260
261
262
    return true;
}

263
void QmlJSEditorPlugin::extensionsInitialized()
264
{
265
266
267
    ProjectExplorer::TaskHub *taskHub =
        ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
    taskHub->addCategory(Constants::TASK_CATEGORY_QML, tr("QML"));
268
    taskHub->addCategory(Constants::TASK_CATEGORY_QML_ANALYSIS, tr("QML Analysis"), false);
269
270
}

271
272
273
274
275
276
277
ExtensionSystem::IPlugin::ShutdownFlag QmlJSEditorPlugin::aboutToShutdown()
{
    delete QmlJS::Icons::instance(); // delete object held by singleton

    return IPlugin::aboutToShutdown();
}

278
void QmlJSEditorPlugin::initializeEditor(QmlJSEditor::QmlJSTextEditorWidget *editor)
279
{
280
    QTC_CHECK(m_instance);
281
282
283

    m_actionHandler->setupActions(editor);

284
    editor->setLanguageSettingsId(QmlJSTools::Constants::QML_JS_SETTINGS_ID);
285
286
287
    TextEditor::TextEditorSettings::instance()->initializeEditor(editor);
}

288
289
290
291
void QmlJSEditorPlugin::followSymbolUnderCursor()
{
    Core::EditorManager *em = Core::EditorManager::instance();

292
    if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget()))
293
294
295
        editor->followSymbolUnderCursor();
}

296
297
298
void QmlJSEditorPlugin::findUsages()
{
    Core::EditorManager *em = Core::EditorManager::instance();
299
    if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget()))
300
301
302
        editor->findUsages();
}

303
304
305
306
307
308
309
void QmlJSEditorPlugin::renameUsages()
{
    Core::EditorManager *em = Core::EditorManager::instance();
    if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget()))
        editor->renameUsages();
}

310
311
312
313
void QmlJSEditorPlugin::reformatFile()
{
    Core::EditorManager *em = Core::EditorManager::instance();
    if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget())) {
314
        QTC_ASSERT(!editor->isSemanticInfoOutdated(), return);
315
316
317
318
319
320
321
322
323

        const QString &newText = QmlJS::reformat(editor->semanticInfo().document);
        QTextCursor tc(editor->textCursor());
        tc.movePosition(QTextCursor::Start);
        tc.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
        tc.insertText(newText);
    }
}

324
325
326
327
void QmlJSEditorPlugin::showContextPane()
{
    Core::EditorManager *em = Core::EditorManager::instance();

328
    if (QmlJSTextEditorWidget *editor = qobject_cast<QmlJSTextEditorWidget*>(em->currentEditor()->widget()))
329
330
331
332
        editor->showContextPane();

}

333
Core::Command *QmlJSEditorPlugin::addToolAction(QAction *a, Core::ActionManager *am,
hjk's avatar
hjk committed
334
                                          Core::Context &context, const Core::Id &id,
335
336
                                          Core::ActionContainer *c1, const QString &keySequence)
{
hjk's avatar
hjk committed
337
    Core::Command *command = am->registerAction(a, id, context);
338
339
340
341
342
343
    if (!keySequence.isEmpty())
        command->setDefaultKeySequence(QKeySequence(keySequence));
    c1->addAction(command);
    return command;
}

Leandro Melo's avatar
Leandro Melo committed
344
QmlJSQuickFixAssistProvider *QmlJSEditorPlugin::quickFixAssistProvider() const
345
{
Leandro Melo's avatar
Leandro Melo committed
346
    return m_quickFixAssistProvider;
347
}
348

349
350
void QmlJSEditorPlugin::currentEditorChanged(Core::IEditor *editor)
{
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
    QmlJSTextEditorWidget *newTextEditor = 0;
    if (editor)
        newTextEditor = qobject_cast<QmlJSTextEditorWidget *>(editor->widget());

    if (m_currentEditor) {
        disconnect(m_currentEditor.data(), SIGNAL(contentsChanged()),
                   this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
        disconnect(m_currentEditor.data(), SIGNAL(semanticInfoUpdated()),
                   this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
    }
    m_currentEditor = newTextEditor;
    if (newTextEditor) {
        connect(newTextEditor, SIGNAL(contentsChanged()),
                this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
        connect(newTextEditor, SIGNAL(semanticInfoUpdated()),
                this, SLOT(checkCurrentEditorSemanticInfoUpToDate()));
367
        newTextEditor->reparseDocumentNow();
368
369
370
    }
}

371
372
373
374
375
376
377
378
void QmlJSEditorPlugin::runSemanticScan()
{
    m_qmlTaskManager->updateSemanticMessagesNow();
    ProjectExplorer::TaskHub *hub = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::TaskHub>();
    hub->setCategoryVisibility(Constants::TASK_CATEGORY_QML_ANALYSIS, true);
    hub->popup(false);
}

379
380
void QmlJSEditorPlugin::checkCurrentEditorSemanticInfoUpToDate()
{
381
    const bool semanticInfoUpToDate = m_currentEditor && !m_currentEditor->isSemanticInfoOutdated();
382
383
384
    m_reformatFileAction->setEnabled(semanticInfoUpToDate);
}

385
Q_EXPORT_PLUGIN(QmlJSEditorPlugin)