Commit ad5476ff authored by Roberto Raggi's avatar Roberto Raggi
Browse files

Show the signatures in a tooltip when completing functions.

parent 20e749df
......@@ -38,6 +38,8 @@ class GLSL_EXPORT Type
public:
virtual ~Type();
virtual QString toString() const = 0;
virtual const UndefinedType *asUndefinedType() const { return 0; }
virtual const VoidType *asVoidType() const { return 0; }
virtual const BoolType *asBoolType() const { return 0; }
......
......@@ -30,6 +30,7 @@
#include "glsltypes.h"
#include "glslsymbols.h"
#include "glslengine.h"
#include "glslparser.h"
using namespace GLSL;
......@@ -138,6 +139,20 @@ bool DoubleType::isLessThan(const Type *other) const
return false;
}
QString VectorType::toString() const
{
QChar prefix;
if (elementType()->asBoolType() != 0)
prefix = QLatin1Char('b');
else if (elementType()->asIntType() != 0)
prefix = QLatin1Char('i');
else if (elementType()->asUIntType() != 0)
prefix = QLatin1Char('u');
else if (elementType()->asDoubleType() != 0)
prefix = QLatin1Char('d');
return QString("%1vec%2").arg(prefix).arg(_dimension);
}
void VectorType::add(Symbol *symbol)
{
_members.insert(symbol->name(), symbol);
......@@ -248,6 +263,20 @@ bool VectorType::isLessThan(const Type *other) const
return false;
}
QString MatrixType::toString() const
{
QChar prefix;
if (elementType()->asBoolType() != 0)
prefix = QLatin1Char('b');
else if (elementType()->asIntType() != 0)
prefix = QLatin1Char('i');
else if (elementType()->asUIntType() != 0)
prefix = QLatin1Char('u');
else if (elementType()->asDoubleType() != 0)
prefix = QLatin1Char('d');
return QString("%1mat%2x%3").arg(prefix, _columns, _rows);
}
bool MatrixType::isEqualTo(const Type *other) const
{
if (other) {
......@@ -333,6 +362,25 @@ bool Struct::isLessThan(const Type *other) const
return false;
}
QString Function::toString() const
{
QString proto;
proto += _returnType->toString();
proto += QLatin1Char(' ');
proto += name();
proto += QLatin1Char('(');
for (int i = 0; i < _arguments.size(); ++i) {
if (i != 0)
proto += QLatin1String(", ");
Argument *arg = _arguments.at(i);
proto += arg->type()->toString();
proto += QLatin1Char(' ');
proto += arg->name();
}
proto += QLatin1Char(')');
return proto;
}
const Type *Function::returnType() const
{
return _returnType;
......@@ -394,6 +442,11 @@ Symbol *Function::find(const QString &name) const
return 0;
}
QString SamplerType::toString() const
{
return QLatin1String(Parser::spell[_kind]);
}
bool SamplerType::isEqualTo(const Type *other) const
{
if (other) {
......
......@@ -47,6 +47,7 @@ public:
class GLSL_EXPORT UndefinedType: public Type
{
public:
virtual QString toString() const { return QLatin1String("undefined"); }
virtual const UndefinedType *asUndefinedType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -55,6 +56,7 @@ public:
class GLSL_EXPORT VoidType: public Type
{
public:
virtual QString toString() const { return QLatin1String("void"); }
virtual const VoidType *asVoidType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -63,6 +65,7 @@ public:
class GLSL_EXPORT BoolType: public Type
{
public:
virtual QString toString() const { return QLatin1String("bool"); }
virtual const BoolType *asBoolType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -71,6 +74,7 @@ public:
class GLSL_EXPORT IntType: public Type
{
public:
virtual QString toString() const { return QLatin1String("int"); }
virtual const IntType *asIntType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -79,6 +83,7 @@ public:
class GLSL_EXPORT UIntType: public Type
{
public:
virtual QString toString() const { return QLatin1String("uint"); }
virtual const UIntType *asUIntType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -87,6 +92,7 @@ public:
class GLSL_EXPORT FloatType: public Type
{
public:
virtual QString toString() const { return QLatin1String("float"); }
virtual const FloatType *asFloatType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -95,6 +101,7 @@ public:
class GLSL_EXPORT DoubleType: public Type
{
public:
virtual QString toString() const { return QLatin1String("double"); }
virtual const DoubleType *asDoubleType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -120,6 +127,7 @@ public:
VectorType(const Type *elementType, int dimension)
: IndexType(elementType), _dimension(dimension) {}
virtual QString toString() const;
const Type *elementType() const { return indexElementType(); }
int dimension() const { return _dimension; }
......@@ -154,6 +162,7 @@ public:
int columns() const { return _columns; }
int rows() const { return _rows; }
virtual QString toString() const;
virtual const MatrixType *asMatrixType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -188,6 +197,7 @@ public:
virtual Symbol *find(const QString &name) const;
// as Type
virtual QString toString() const { return name(); }
virtual const Struct *asStructType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -215,6 +225,7 @@ public:
Argument *argumentAt(int index) const;
// as Type
virtual QString toString() const;
virtual const Function *asFunctionType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -246,6 +257,7 @@ public:
// Kind of sampler as a token code; e.g. T_SAMPLER2D.
int kind() const { return _kind; }
virtual QString toString() const;
virtual const SamplerType *asSamplerType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......@@ -269,6 +281,7 @@ public:
virtual void add(Symbol *symbol);
// as type
virtual QString toString() const { return QLatin1String("overload"); }
virtual const OverloadSet *asOverloadSetType() const { return this; }
virtual bool isEqualTo(const Type *other) const;
virtual bool isLessThan(const Type *other) const;
......
......@@ -31,13 +31,21 @@
#include "glsleditorplugin.h"
#include <glsl/glslengine.h>
#include <glsl/glslengine.h>
#include <glsl/glsllexer.h>
#include <glsl/glslparser.h>
#include <glsl/glslsemantic.h>
#include <glsl/glslsymbols.h>
#include <glsl/glslastdump.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <texteditor/completionsettings.h>
#include <utils/faketooltip.h>
#include <QtGui/QIcon>
#include <QtGui/QPainter>
#include <QtGui/QLabel>
#include <QtGui/QToolButton>
#include <QtGui/QHBoxLayout>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtCore/QDebug>
using namespace GLSLEditor;
......@@ -243,6 +251,270 @@ static const char *glsl_keywords[] =
0
};
namespace GLSLEditor {
namespace Internal {
class FunctionArgumentWidget : public QLabel
{
Q_OBJECT
public:
FunctionArgumentWidget();
void showFunctionHint(QVector<GLSL::Function *> functionSymbols,
int startPosition);
protected:
bool eventFilter(QObject *obj, QEvent *e);
private slots:
void nextPage();
void previousPage();
private:
void updateArgumentHighlight();
void updateHintText();
void placeInsideScreen();
GLSL::Function *currentFunction() const
{ return m_items.at(m_current); }
int m_startpos;
int m_currentarg;
int m_current;
bool m_escapePressed;
TextEditor::ITextEditor *m_editor;
QWidget *m_pager;
QLabel *m_numberLabel;
Utils::FakeToolTip *m_popupFrame;
QVector<GLSL::Function *> m_items;
};
FunctionArgumentWidget::FunctionArgumentWidget():
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 Utils::FakeToolTip(m_editor->widget());
QToolButton *downArrow = new QToolButton;
downArrow->setArrowType(Qt::DownArrow);
downArrow->setFixedSize(16, 16);
downArrow->setAutoRaise(true);
QToolButton *upArrow = new QToolButton;
upArrow->setArrowType(Qt::UpArrow);
upArrow->setFixedSize(16, 16);
upArrow->setAutoRaise(true);
setParent(m_popupFrame);
setFocusPolicy(Qt::NoFocus);
m_pager = new QWidget;
QHBoxLayout *hbox = new QHBoxLayout(m_pager);
hbox->setMargin(0);
hbox->setSpacing(0);
hbox->addWidget(upArrow);
m_numberLabel = new QLabel;
hbox->addWidget(m_numberLabel);
hbox->addWidget(downArrow);
QHBoxLayout *layout = new QHBoxLayout;
layout->setMargin(0);
layout->setSpacing(0);
layout->addWidget(m_pager);
layout->addWidget(this);
m_popupFrame->setLayout(layout);
connect(upArrow, SIGNAL(clicked()), SLOT(previousPage()));
connect(downArrow, SIGNAL(clicked()), SLOT(nextPage()));
setTextFormat(Qt::RichText);
qApp->installEventFilter(this);
}
void FunctionArgumentWidget::showFunctionHint(QVector<GLSL::Function *> functionSymbols,
int startPosition)
{
Q_ASSERT(!functionSymbols.isEmpty());
if (m_startpos == startPosition)
return;
m_pager->setVisible(functionSymbols.size() > 1);
m_items = functionSymbols;
m_startpos = startPosition;
m_current = 0;
m_escapePressed = false;
// update the text
m_currentarg = -1;
updateArgumentHighlight();
m_popupFrame->show();
}
void FunctionArgumentWidget::nextPage()
{
m_current = (m_current + 1) % m_items.size();
updateHintText();
}
void FunctionArgumentWidget::previousPage()
{
if (m_current == 0)
m_current = m_items.size() - 1;
else
--m_current;
updateHintText();
}
void FunctionArgumentWidget::updateArgumentHighlight()
{
int curpos = m_editor->position();
if (curpos < m_startpos) {
m_popupFrame->close();
return;
}
const QByteArray str = m_editor->textAt(m_startpos, curpos - m_startpos).toLatin1();
int argnr = 0;
int parcount = 0;
GLSL::Lexer lexer(0, str.constData(), str.length());
GLSL::Token tk;
QList<GLSL::Token> tokens;
do {
lexer.yylex(&tk);
tokens.append(tk);
} while (tk.isNot(GLSL::Parser::EOF_SYMBOL));
for (int i = 0; i < tokens.count(); ++i) {
const GLSL::Token &tk = tokens.at(i);
if (tk.is(GLSL::Parser::T_LEFT_PAREN))
++parcount;
else if (tk.is(GLSL::Parser::T_RIGHT_PAREN))
--parcount;
else if (! parcount && tk.is(GLSL::Parser::T_COMMA))
++argnr;
}
if (m_currentarg != argnr) {
m_currentarg = argnr;
updateHintText();
}
if (parcount < 0)
m_popupFrame->close();
}
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;
}
if (m_items.size() > 1) {
QKeyEvent *ke = static_cast<QKeyEvent*>(e);
if (ke->key() == Qt::Key_Up) {
previousPage();
return true;
} else if (ke->key() == Qt::Key_Down) {
nextPage();
return true;
}
return false;
}
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()
{
setText(currentFunction()->toString());
m_numberLabel->setText(tr("%1 of %2").arg(m_current + 1).arg(m_items.size()));
placeInsideScreen();
}
void FunctionArgumentWidget::placeInsideScreen()
{
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
m_pager->setFixedWidth(m_pager->minimumSizeHint().width());
setWordWrap(false);
const int maxDesiredWidth = screen.width() - 10;
const QSize minHint = m_popupFrame->minimumSizeHint();
if (minHint.width() > maxDesiredWidth) {
setWordWrap(true);
m_popupFrame->setFixedWidth(maxDesiredWidth);
const int extra =
m_popupFrame->contentsMargins().bottom() + m_popupFrame->contentsMargins().top();
m_popupFrame->setFixedHeight(heightForWidth(maxDesiredWidth - m_pager->width()) + extra);
} else {
m_popupFrame->setFixedSize(minHint);
}
const QSize sz = m_popupFrame->size();
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);
}
} // Internal
} // GLSLEditor
CodeCompletion::CodeCompletion(QObject *parent)
: ICompletionCollector(parent),
m_editor(0),
......@@ -332,7 +604,8 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
QStringList members;
QStringList specialMembers;
if (ch == QLatin1Char('.')) {
if (ch == QLatin1Char('.') || ch == QLatin1Char('(')) {
const bool memberCompletion = (ch == QLatin1Char('.'));
QTextCursor tc(edit->document());
tc.setPosition(pos);
......@@ -353,32 +626,46 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
#endif
if (Document::Ptr doc = edit->glslDocument()) {
// TODO: ### find the enclosing scope in the previously parsed `doc'.
// let's use the global scope for now. This should be good enough
// to get some basic completion for the global variables.
GLSL::Scope *currentScope = doc->scopeAt(pos);
GLSL::Semantic sem;
GLSL::Semantic::ExprResult exprTy = sem.expression(expr, currentScope, doc->engine());
if (exprTy.type) {
if (const GLSL::VectorType *vecTy = exprTy.type->asVectorType()) {
members = vecTy->members();
// Sort the most relevant swizzle orderings to the top.
specialMembers += QLatin1String("xy");
specialMembers += QLatin1String("xyz");
specialMembers += QLatin1String("xyzw");
specialMembers += QLatin1String("rgb");
specialMembers += QLatin1String("rgba");
specialMembers += QLatin1String("st");
specialMembers += QLatin1String("stp");
specialMembers += QLatin1String("stpq");
} else if (const GLSL::Struct *structTy = exprTy.type->asStructType()) {
members = structTy->members();
} else {
// some other type
if (memberCompletion) {
if (const GLSL::VectorType *vecTy = exprTy.type->asVectorType()) {
members = vecTy->members();
// Sort the most relevant swizzle orderings to the top.
specialMembers += QLatin1String("xy");
specialMembers += QLatin1String("xyz");
specialMembers += QLatin1String("xyzw");
specialMembers += QLatin1String("rgb");
specialMembers += QLatin1String("rgba");
specialMembers += QLatin1String("st");
specialMembers += QLatin1String("stp");
specialMembers += QLatin1String("stpq");
} else if (const GLSL::Struct *structTy = exprTy.type->asStructType()) {
members = structTy->members();
} else {
// some other type
}
} else { // function completion
QVector<GLSL::Function *> signatures;
if (const GLSL::Function *funTy = exprTy.type->asFunctionType())
signatures.append(const_cast<GLSL::Function *>(funTy)); // ### get rid of the const_cast
else if (const GLSL::OverloadSet *overload = exprTy.type->asOverloadSetType())
signatures = overload->functions();
if (! signatures.isEmpty()) {
// Recreate if necessary
if (!m_functionArgumentWidget)
m_functionArgumentWidget = new FunctionArgumentWidget;
m_functionArgumentWidget->showFunctionHint(signatures, pos + 1);
return false;
}
}
} else {
// undefined
......@@ -503,3 +790,4 @@ void CodeCompletion::cleanup()
m_startPosition = -1;
}
#include "glslcodecompletion.moc"
......@@ -30,10 +30,13 @@
#define GLSLCODECOMPLETION_H
#include <texteditor/icompletioncollector.h>
#include <QtCore/QPointer>
namespace GLSLEditor {
namespace Internal {
class FunctionArgumentWidget;
class CodeCompletion: public TextEditor::ICompletionCollector
{
Q_OBJECT
......@@ -100,6 +103,7 @@ private:
TextEditor::ITextEditable *m_editor;
int m_startPosition;
bool m_restartCompletion;
QPointer<FunctionArgumentWidget> m_functionArgumentWidget;
static bool glslCompletionItemLessThan(const TextEditor::CompletionItem &l, const TextEditor::CompletionItem &r);
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment