forked from qt-creator/qt-creator
Show the function arguments hint.
This commit is contained in:
@@ -40,13 +40,21 @@
|
|||||||
#include <texteditor/basetexteditor.h>
|
#include <texteditor/basetexteditor.h>
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
#include <QtCore/QFileInfo>
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QXmlStreamReader>
|
#include <QtCore/QXmlStreamReader>
|
||||||
#include <QtGui/QPainter>
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
#include <QtDebug>
|
#include <QtGui/QPainter>
|
||||||
|
#include <QtGui/QLabel>
|
||||||
|
#include <QtGui/QStylePainter>
|
||||||
|
#include <QtGui/QStyleOption>
|
||||||
|
#include <QtGui/QToolButton>
|
||||||
|
#include <QtGui/QHBoxLayout>
|
||||||
|
#include <QtGui/QApplication>
|
||||||
|
#include <QtGui/QDesktopWidget>
|
||||||
|
|
||||||
using namespace QmlJSEditor;
|
using namespace QmlJSEditor;
|
||||||
using namespace QmlJSEditor::Internal;
|
using namespace QmlJSEditor::Internal;
|
||||||
@@ -223,6 +231,239 @@ private:
|
|||||||
|
|
||||||
} // end of anonymous namespace
|
} // end of anonymous namespace
|
||||||
|
|
||||||
|
namespace QmlJSEditor {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class FakeToolTipFrame : public QWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FakeToolTipFrame(QWidget *parent = 0) :
|
||||||
|
QWidget(parent, Qt::ToolTip | Qt::WindowStaysOnTopHint)
|
||||||
|
{
|
||||||
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
|
||||||
|
// Set the window and button text to the tooltip text color, since this
|
||||||
|
// widget draws the background as a tooltip.
|
||||||
|
QPalette p = palette();
|
||||||
|
const QColor toolTipTextColor = p.color(QPalette::Inactive, QPalette::ToolTipText);
|
||||||
|
p.setColor(QPalette::Inactive, QPalette::WindowText, toolTipTextColor);
|
||||||
|
p.setColor(QPalette::Inactive, QPalette::ButtonText, toolTipTextColor);
|
||||||
|
setPalette(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e);
|
||||||
|
void resizeEvent(QResizeEvent *e);
|
||||||
|
};
|
||||||
|
|
||||||
|
class FunctionArgumentWidget : public QLabel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FunctionArgumentWidget();
|
||||||
|
void showFunctionHint(const QString &functionName, int minimumArgumentCount, int startPosition);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool eventFilter(QObject *obj, QEvent *e);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateArgumentHighlight();
|
||||||
|
void updateHintText();
|
||||||
|
|
||||||
|
QString m_functionName;
|
||||||
|
int m_minimumArgumentCount;
|
||||||
|
int m_startpos;
|
||||||
|
int m_currentarg;
|
||||||
|
int m_current;
|
||||||
|
bool m_escapePressed;
|
||||||
|
|
||||||
|
TextEditor::ITextEditor *m_editor;
|
||||||
|
|
||||||
|
QWidget *m_pager;
|
||||||
|
QLabel *m_numberLabel;
|
||||||
|
FakeToolTipFrame *m_popupFrame;
|
||||||
|
};
|
||||||
|
|
||||||
|
void FakeToolTipFrame::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QStylePainter p(this);
|
||||||
|
QStyleOptionFrame opt;
|
||||||
|
opt.init(this);
|
||||||
|
p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
|
||||||
|
p.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FakeToolTipFrame::resizeEvent(QResizeEvent *)
|
||||||
|
{
|
||||||
|
QStyleHintReturnMask frameMask;
|
||||||
|
QStyleOption option;
|
||||||
|
option.init(this);
|
||||||
|
if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
|
||||||
|
setMask(frameMask.region);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FunctionArgumentWidget::FunctionArgumentWidget():
|
||||||
|
m_minimumArgumentCount(0),
|
||||||
|
m_startpos(-1),
|
||||||
|
m_current(0),
|
||||||
|
m_escapePressed(false)
|
||||||
|
{
|
||||||
|
QObject *editorObject = Core::EditorManager::instance()->currentEditor();
|
||||||
|
m_editor = qobject_cast<TextEditor::ITextEditor *>(editorObject);
|
||||||
|
|
||||||
|
m_popupFrame = new FakeToolTipFrame(m_editor->widget());
|
||||||
|
|
||||||
|
setParent(m_popupFrame);
|
||||||
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
|
||||||
|
m_pager = new QWidget;
|
||||||
|
QHBoxLayout *hbox = new QHBoxLayout(m_pager);
|
||||||
|
hbox->setMargin(0);
|
||||||
|
hbox->setSpacing(0);
|
||||||
|
m_numberLabel = new QLabel;
|
||||||
|
hbox->addWidget(m_numberLabel);
|
||||||
|
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout;
|
||||||
|
layout->setMargin(0);
|
||||||
|
layout->setSpacing(0);
|
||||||
|
layout->addWidget(m_pager);
|
||||||
|
layout->addWidget(this);
|
||||||
|
m_popupFrame->setLayout(layout);
|
||||||
|
|
||||||
|
setTextFormat(Qt::RichText);
|
||||||
|
setMargin(1);
|
||||||
|
|
||||||
|
qApp->installEventFilter(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionArgumentWidget::showFunctionHint(const QString &functionName, int mininumArgumentCount, int startPosition)
|
||||||
|
{
|
||||||
|
if (m_startpos == startPosition)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_functionName = functionName;
|
||||||
|
m_minimumArgumentCount = mininumArgumentCount;
|
||||||
|
m_startpos = startPosition;
|
||||||
|
m_current = 0;
|
||||||
|
m_escapePressed = false;
|
||||||
|
|
||||||
|
// update the text
|
||||||
|
m_currentarg = -1;
|
||||||
|
updateArgumentHighlight();
|
||||||
|
|
||||||
|
m_popupFrame->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionArgumentWidget::updateArgumentHighlight()
|
||||||
|
{
|
||||||
|
int curpos = m_editor->position();
|
||||||
|
if (curpos < m_startpos) {
|
||||||
|
m_popupFrame->close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHintText();
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
QString str = m_editor->textAt(m_startpos, curpos - m_startpos);
|
||||||
|
int argnr = 0;
|
||||||
|
int parcount = 0;
|
||||||
|
SimpleLexer tokenize;
|
||||||
|
QList<SimpleToken> tokens = tokenize(str);
|
||||||
|
for (int i = 0; i < tokens.count(); ++i) {
|
||||||
|
const SimpleToken &tk = tokens.at(i);
|
||||||
|
if (tk.is(T_LPAREN))
|
||||||
|
++parcount;
|
||||||
|
else if (tk.is(T_RPAREN))
|
||||||
|
--parcount;
|
||||||
|
else if (! parcount && tk.is(T_COMMA))
|
||||||
|
++argnr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentarg != argnr) {
|
||||||
|
m_currentarg = argnr;
|
||||||
|
updateHintText();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parcount < 0)
|
||||||
|
m_popupFrame->close();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FunctionArgumentWidget::eventFilter(QObject *obj, QEvent *e)
|
||||||
|
{
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::ShortcutOverride:
|
||||||
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
||||||
|
m_escapePressed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QEvent::KeyPress:
|
||||||
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape) {
|
||||||
|
m_escapePressed = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QEvent::KeyRelease:
|
||||||
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && m_escapePressed) {
|
||||||
|
m_popupFrame->close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
updateArgumentHighlight();
|
||||||
|
break;
|
||||||
|
case QEvent::WindowDeactivate:
|
||||||
|
case QEvent::FocusOut:
|
||||||
|
if (obj != m_editor->widget())
|
||||||
|
break;
|
||||||
|
m_popupFrame->close();
|
||||||
|
break;
|
||||||
|
case QEvent::MouseButtonPress:
|
||||||
|
case QEvent::MouseButtonRelease:
|
||||||
|
case QEvent::MouseButtonDblClick:
|
||||||
|
case QEvent::Wheel: {
|
||||||
|
QWidget *widget = qobject_cast<QWidget *>(obj);
|
||||||
|
if (! (widget == this || m_popupFrame->isAncestorOf(widget))) {
|
||||||
|
m_popupFrame->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionArgumentWidget::updateHintText()
|
||||||
|
{
|
||||||
|
QString prettyMethod;
|
||||||
|
prettyMethod += QString::fromLatin1("function ");
|
||||||
|
prettyMethod += m_functionName;
|
||||||
|
prettyMethod += QLatin1String("(arguments...)");
|
||||||
|
|
||||||
|
m_numberLabel->setText(prettyMethod);
|
||||||
|
|
||||||
|
m_popupFrame->setFixedWidth(m_popupFrame->minimumSizeHint().width());
|
||||||
|
|
||||||
|
const QDesktopWidget *desktop = QApplication::desktop();
|
||||||
|
#ifdef Q_WS_MAC
|
||||||
|
const QRect screen = desktop->availableGeometry(desktop->screenNumber(m_editor->widget()));
|
||||||
|
#else
|
||||||
|
const QRect screen = desktop->screenGeometry(desktop->screenNumber(m_editor->widget()));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const QSize sz = m_popupFrame->sizeHint();
|
||||||
|
QPoint pos = m_editor->cursorRect(m_startpos).topLeft();
|
||||||
|
pos.setY(pos.y() - sz.height() - 1);
|
||||||
|
|
||||||
|
if (pos.x() + sz.width() > screen.right())
|
||||||
|
pos.setX(screen.right() - sz.width());
|
||||||
|
|
||||||
|
m_popupFrame->move(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
} } // end of namespace QmlJSEditor::Internal
|
||||||
|
|
||||||
QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, QmlJS::TypeSystem *typeSystem, QObject *parent)
|
QmlCodeCompletion::QmlCodeCompletion(QmlModelManagerInterface *modelManager, QmlJS::TypeSystem *typeSystem, QObject *parent)
|
||||||
: TextEditor::ICompletionCollector(parent),
|
: TextEditor::ICompletionCollector(parent),
|
||||||
m_modelManager(modelManager),
|
m_modelManager(modelManager),
|
||||||
@@ -252,8 +493,15 @@ bool QmlCodeCompletion::supportsEditor(TextEditor::ITextEditable *editor)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QmlCodeCompletion::triggersCompletion(TextEditor::ITextEditable *)
|
bool QmlCodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
|
||||||
{ return false; }
|
{
|
||||||
|
const QChar ch = editor->characterAt(editor->position() - 1);
|
||||||
|
|
||||||
|
if (ch == QLatin1Char('(') || ch == QLatin1Char('.'))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
||||||
{
|
{
|
||||||
@@ -290,11 +538,11 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
|||||||
|
|
||||||
const QIcon typeIcon = iconForColor(Qt::yellow);
|
const QIcon typeIcon = iconForColor(Qt::yellow);
|
||||||
|
|
||||||
QChar previousChar;
|
QChar completionOperator;
|
||||||
if (m_startPosition > 0)
|
if (m_startPosition > 0)
|
||||||
previousChar = editor->characterAt(m_startPosition - 1);
|
completionOperator = editor->characterAt(m_startPosition - 1);
|
||||||
|
|
||||||
if (previousChar.isSpace() || previousChar.isNull()) {
|
if (completionOperator.isSpace() || completionOperator.isNull()) {
|
||||||
// Add the visible components to the completion box.
|
// Add the visible components to the completion box.
|
||||||
foreach (QmlJS::Document::Ptr doc, snapshot) {
|
foreach (QmlJS::Document::Ptr doc, snapshot) {
|
||||||
const QFileInfo fileInfo(doc->fileName());
|
const QFileInfo fileInfo(doc->fileName());
|
||||||
@@ -330,7 +578,7 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousChar == QLatin1Char('.')) {
|
if (completionOperator == QLatin1Char('.') || completionOperator == QLatin1Char('(')) {
|
||||||
const int endOfExpression = m_startPosition - 1;
|
const int endOfExpression = m_startPosition - 1;
|
||||||
int startOfExpression = endOfExpression - 2;
|
int startOfExpression = endOfExpression - 2;
|
||||||
|
|
||||||
@@ -360,15 +608,34 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
|||||||
|
|
||||||
const QIcon symbolIcon = iconForColor(Qt::darkCyan);
|
const QIcon symbolIcon = iconForColor(Qt::darkCyan);
|
||||||
|
|
||||||
EnumerateProperties enumerateProperties;
|
if (value && completionOperator == QLatin1Char('.')) { // member completion
|
||||||
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
|
EnumerateProperties enumerateProperties;
|
||||||
while (it.hasNext()) {
|
QHashIterator<QString, const Interpreter::Value *> it(enumerateProperties(value));
|
||||||
it.next();
|
while (it.hasNext()) {
|
||||||
|
it.next();
|
||||||
|
|
||||||
TextEditor::CompletionItem item(this);
|
TextEditor::CompletionItem item(this);
|
||||||
item.text = it.key();
|
item.text = it.key();
|
||||||
item.icon = symbolIcon;
|
item.icon = symbolIcon;
|
||||||
m_completions.append(item);
|
m_completions.append(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (value && completionOperator == QLatin1Char('(')) {
|
||||||
|
if (const Interpreter::FunctionValue *f = value->asFunctionValue()) {
|
||||||
|
QString functionName = expression;
|
||||||
|
int indexOfDot = expression.lastIndexOf(QLatin1Char('.'));
|
||||||
|
if (indexOfDot != -1)
|
||||||
|
functionName = expression.mid(indexOfDot + 1);
|
||||||
|
// Recreate if necessary
|
||||||
|
if (!m_functionArgumentWidget)
|
||||||
|
m_functionArgumentWidget = new QmlJSEditor::Internal::FunctionArgumentWidget;
|
||||||
|
|
||||||
|
m_functionArgumentWidget->showFunctionHint(functionName,
|
||||||
|
f->argumentCount(),
|
||||||
|
m_startPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,12 +645,12 @@ int QmlCodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previousChar.isNull()
|
if (completionOperator.isNull()
|
||||||
|| previousChar.isSpace()
|
|| completionOperator.isSpace()
|
||||||
|| previousChar == QLatin1Char('{')
|
|| completionOperator == QLatin1Char('{')
|
||||||
|| previousChar == QLatin1Char('}')
|
|| completionOperator == QLatin1Char('}')
|
||||||
|| previousChar == QLatin1Char(':')
|
|| completionOperator == QLatin1Char(':')
|
||||||
|| previousChar == QLatin1Char(';')) {
|
|| completionOperator == QLatin1Char(';')) {
|
||||||
updateSnippets();
|
updateSnippets();
|
||||||
m_completions.append(m_snippets);
|
m_completions.append(m_snippets);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include <qmljs/qmljstypesystem.h>
|
#include <qmljs/qmljstypesystem.h>
|
||||||
#include <texteditor/icompletioncollector.h>
|
#include <texteditor/icompletioncollector.h>
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
|
||||||
namespace TextEditor {
|
namespace TextEditor {
|
||||||
class ITextEditable;
|
class ITextEditable;
|
||||||
@@ -44,6 +45,8 @@ class QmlModelManagerInterface;
|
|||||||
|
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class FunctionArgumentWidget;
|
||||||
|
|
||||||
class QmlCodeCompletion: public TextEditor::ICompletionCollector
|
class QmlCodeCompletion: public TextEditor::ICompletionCollector
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -66,6 +69,8 @@ public:
|
|||||||
virtual void cleanup();
|
virtual void cleanup();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void updateSnippets();
|
||||||
|
|
||||||
QmlModelManagerInterface *m_modelManager;
|
QmlModelManagerInterface *m_modelManager;
|
||||||
TextEditor::ITextEditable *m_editor;
|
TextEditor::ITextEditable *m_editor;
|
||||||
int m_startPosition;
|
int m_startPosition;
|
||||||
@@ -75,7 +80,7 @@ private:
|
|||||||
|
|
||||||
QList<TextEditor::CompletionItem> m_snippets;
|
QList<TextEditor::CompletionItem> m_snippets;
|
||||||
QDateTime m_snippetFileLastModified;
|
QDateTime m_snippetFileLastModified;
|
||||||
void updateSnippets();
|
QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user