#include "glsltypes.h"
#include "glslsymbols.h"
#include "glslengine.h"
+#include "glslparser.h"
using namespace GLSL;
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);
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) {
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;
return 0;
}
+QString SamplerType::toString() const
+{
+ return QLatin1String(Parser::spell[_kind]);
+}
+
bool SamplerType::isEqualTo(const Type *other) const
{
if (other) {
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;
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;
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;
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;
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;
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;
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;
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; }
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;
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;
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;
// 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;
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;
#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;
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),
QStringList members;
QStringList specialMembers;
- if (ch == QLatin1Char('.')) {
+ if (ch == QLatin1Char('.') || ch == QLatin1Char('(')) {
+ const bool memberCompletion = (ch == QLatin1Char('.'));
QTextCursor tc(edit->document());
tc.setPosition(pos);
#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
m_startPosition = -1;
}
+#include "glslcodecompletion.moc"