Commit 55234b9d authored by Roberto Raggi's avatar Roberto Raggi

Implemented some basic code completion.

parent 31ff319f
......@@ -36,6 +36,9 @@
%token_prefix T_
%expect 1
%token FEED_GLSL "feed GLSL"
%token FEED_EXPRESSION "feed expression"
%token ADD_ASSIGN "+="
%token AMPERSAND "&"
%token AND_ASSIGN "&="
......@@ -209,7 +212,7 @@
%token ERROR "error"
%token RESERVED "reserved word"
%start translation_unit
%start toplevel
/:
/**************************************************************************
......@@ -301,10 +304,23 @@ public:
Parser(Engine *engine, const char *source, unsigned size, int variant);
~Parser();
TranslationUnitAST *parse();
TranslationUnitAST *parse() {
if (AST *u = parse(T_FEED_GLSL))
return u->asTranslationUnit();
return 0;
}
ExpressionAST *parseExpression() {
if (AST *u = parse(T_FEED_EXPRESSION))
return u->asExpression();
return 0;
}
AST *parse(int startToken);
private:
// 1-based
int &location(int n) { return _locationStack[_tos + n - 1]; }
Value &sym(int n) { return _symStack[_tos + n - 1]; }
AST *&ast(int n) { return _symStack[_tos + n - 1].ast; }
const QString *&string(int n) { return _symStack[_tos + n - 1].string; }
......@@ -318,8 +334,16 @@ private:
return _index++;
return _tokens.size() - 1;
}
inline const Token &tokenAt(int index) const { return _tokens.at(index); }
inline int tokenKind(int index) const { return _tokens.at(index).kind; }
inline const Token &tokenAt(int index) const {
if (index == 0)
return _startToken;
return _tokens.at(index);
}
inline int tokenKind(int index) const {
if (index == 0)
return _startToken.kind;
return _tokens.at(index).kind;
}
void reduce(int ruleno);
void warning(int line, const QString &message)
......@@ -387,6 +411,7 @@ private:
int yytoken;
int yyrecovering;
bool _recovered;
Token _startToken;
std::vector<int> _stateStack;
std::vector<int> _locationStack;
std::vector<Value> _symStack;
......@@ -492,14 +517,14 @@ Parser::Parser(Engine *engine, const char *source, unsigned size, int variant)
_tokens.push_back(tk);
} while (tk.isNot(EOF_SYMBOL));
_index = 1;
_index = 0;
}
Parser::~Parser()
{
}
TranslationUnitAST *Parser::parse()
AST *Parser::parse(int startToken)
{
int action = 0;
yytoken = -1;
......@@ -508,6 +533,7 @@ TranslationUnitAST *Parser::parse()
_recovered = false;
_tos = -1;
_startToken.kind = startToken;
do {
again:
......@@ -2641,14 +2667,20 @@ case $rule_number: {
compound_statement ::= LEFT_BRACE RIGHT_BRACE ;
/.
case $rule_number: {
ast(1) = makeAstNode<CompoundStatementAST>();
CompoundStatementAST *stmt = makeAstNode<CompoundStatementAST>();
stmt->start = tokenAt(location(1)).begin();
stmt->end = tokenAt(location(2)).end();
ast(1) = stmt;
} break;
./
compound_statement ::= LEFT_BRACE statement_list RIGHT_BRACE ;
/.
case $rule_number: {
ast(1) = makeAstNode<CompoundStatementAST>(sym(2).statement_list);
CompoundStatementAST *stmt = makeAstNode<CompoundStatementAST>(sym(2).statement_list);
stmt->start = tokenAt(location(1)).begin();
stmt->end = tokenAt(location(3)).end();
ast(1) = stmt;
} break;
./
......@@ -2669,14 +2701,20 @@ case $rule_number: {
compound_statement_no_new_scope ::= LEFT_BRACE RIGHT_BRACE ;
/.
case $rule_number: {
ast(1) = makeAstNode<CompoundStatementAST>();
CompoundStatementAST *stmt = makeAstNode<CompoundStatementAST>();
stmt->start = tokenAt(location(1)).begin();
stmt->end = tokenAt(location(2)).end();
ast(1) = stmt;
} break;
./
compound_statement_no_new_scope ::= LEFT_BRACE statement_list RIGHT_BRACE ;
/.
case $rule_number: {
ast(1) = makeAstNode<CompoundStatementAST>(sym(2).statement_list);
CompoundStatementAST *stmt = makeAstNode<CompoundStatementAST>(sym(2).statement_list);
stmt->start = tokenAt(location(1)).begin();
stmt->end = tokenAt(location(3)).end();
ast(1) = stmt;
} break;
./
......@@ -2953,6 +2991,19 @@ case $rule_number: {
./
toplevel ::= FEED_GLSL translation_unit ;
/.
case $rule_number: {
ast(1) = ast(2);
} break;
./
toplevel ::= FEED_EXPRESSION expression ;
/.
case $rule_number: {
ast(1) = ast(2);
} break;
./
/.
} // end switch
......
......@@ -508,9 +508,11 @@ class GLSL_EXPORT CompoundStatementAST: public StatementAST
{
public:
CompoundStatementAST()
: StatementAST(Kind_CompoundStatement), statements(0) {}
: StatementAST(Kind_CompoundStatement), statements(0)
, start(0), end(0), symbol(0) {}
CompoundStatementAST(List<StatementAST *> *_statements)
: StatementAST(Kind_CompoundStatement), statements(finish(_statements)) {}
: StatementAST(Kind_CompoundStatement), statements(finish(_statements))
, start(0), end(0), symbol(0) {}
virtual CompoundStatementAST *asCompoundStatement() { return this; }
......@@ -518,6 +520,9 @@ public:
public: // attributes
List<StatementAST *> *statements;
int start;
int end;
Block *symbol; // decoration
};
class GLSL_EXPORT IfStatementAST: public StatementAST
......
This diff is collapsed.
#line 214 "glsl.g"
#line 217 "./glsl.g"
/**************************************************************************
**
......@@ -90,10 +90,23 @@ public:
Parser(Engine *engine, const char *source, unsigned size, int variant);
~Parser();
TranslationUnitAST *parse();
TranslationUnitAST *parse() {
if (AST *u = parse(T_FEED_GLSL))
return u->asTranslationUnit();
return 0;
}
ExpressionAST *parseExpression() {
if (AST *u = parse(T_FEED_EXPRESSION))
return u->asExpression();
return 0;
}
AST *parse(int startToken);
private:
// 1-based
int &location(int n) { return _locationStack[_tos + n - 1]; }
Value &sym(int n) { return _symStack[_tos + n - 1]; }
AST *&ast(int n) { return _symStack[_tos + n - 1].ast; }
const QString *&string(int n) { return _symStack[_tos + n - 1].string; }
......@@ -107,8 +120,16 @@ private:
return _index++;
return _tokens.size() - 1;
}
inline const Token &tokenAt(int index) const { return _tokens.at(index); }
inline int tokenKind(int index) const { return _tokens.at(index).kind; }
inline const Token &tokenAt(int index) const {
if (index == 0)
return _startToken;
return _tokens.at(index);
}
inline int tokenKind(int index) const {
if (index == 0)
return _startToken.kind;
return _tokens.at(index).kind;
}
void reduce(int ruleno);
void warning(int line, const QString &message)
......@@ -176,6 +197,7 @@ private:
int yytoken;
int yyrecovering;
bool _recovered;
Token _startToken;
std::vector<int> _stateStack;
std::vector<int> _locationStack;
std::vector<Value> _symStack;
......
This diff is collapsed.
This diff is collapsed.
......@@ -103,6 +103,19 @@ void Semantic::translationUnit(TranslationUnitAST *ast, Scope *globalScope, Engi
(void) switchEngine(previousEngine);
}
Semantic::ExprResult Semantic::expression(ExpressionAST *ast, Scope *scope, Engine *engine)
{
ExprResult result(engine->undefinedType());
if (ast && scope) {
Engine *previousEngine = switchEngine(engine);
Scope *previousScope = switchScope(scope);
result = expression(ast);
(void) switchScope(previousScope);
(void) switchEngine(previousEngine);
}
return result;
}
Semantic::ExprResult Semantic::functionIdentifier(FunctionIdentifierAST *ast)
{
ExprResult result;
......@@ -269,7 +282,9 @@ bool Semantic::visit(ExpressionStatementAST *ast)
bool Semantic::visit(CompoundStatementAST *ast)
{
Scope *previousScope = switchScope(_engine->newBlock(_scope));
Block *block = _engine->newBlock(_scope);
Scope *previousScope = switchScope(block);
ast->symbol = block;
for (List<StatementAST *> *it = ast->statements; it; it = it->next) {
StatementAST *stmt = it->value;
statement(stmt);
......
......@@ -51,6 +51,7 @@ public:
};
void translationUnit(TranslationUnitAST *ast, Scope *globalScope, Engine *engine);
ExprResult expression(ExpressionAST *ast, Scope *scope, Engine *engine);
protected:
Engine *switchEngine(Engine *engine);
......
......@@ -28,6 +28,7 @@
**************************************************************************/
#include "glslsymbol.h"
#include <QtCore/QStringList>
using namespace GLSL;
......@@ -74,3 +75,8 @@ Symbol *Scope::lookup(const QString &name) const
else
return 0;
}
QStringList Scope::members() const
{
return QStringList();
}
......@@ -73,6 +73,7 @@ public:
Symbol *lookup(const QString &name) const;
virtual QStringList members() const;
virtual void add(Symbol *symbol) = 0;
virtual Symbol *find(const QString &name) const = 0;
......
......@@ -54,6 +54,11 @@ Block::Block(Scope *enclosingScope)
{
}
QStringList Block::members() const
{
return _members.keys();
}
void Block::add(Symbol *symbol)
{
_members.insert(symbol->name(), symbol);
......@@ -95,6 +100,11 @@ Namespace::~Namespace()
qDeleteAll(_overloadSets);
}
QStringList Namespace::members() const
{
return _members.keys();
}
void Namespace::add(Symbol *symbol)
{
Symbol *&sym = _members[symbol->name()];
......
......@@ -70,7 +70,8 @@ class GLSL_EXPORT Block: public Scope
public:
Block(Scope *enclosingScope = 0);
void add(Symbol *symbol);
virtual QStringList members() const;
virtual void add(Symbol *symbol);
virtual Block *asBlock() { return this; }
......@@ -91,6 +92,7 @@ public:
virtual Namespace *asNamespace() { return this; }
virtual QStringList members() const;
virtual const Type *type() const;
virtual Symbol *find(const QString &name) const;
......
......@@ -297,6 +297,16 @@ bool ArrayType::isLessThan(const Type *other) const
return elementType() < array->elementType();
}
QStringList Struct::members() const
{
QStringList m;
foreach (Symbol *s, _members) {
if (! s->name().isEmpty())
m.append(s->name());
}
return m;
}
void Struct::add(Symbol *member)
{
_members.append(member);
......@@ -365,6 +375,16 @@ bool Function::isLessThan(const Type *other) const
return false;
}
QStringList Function::members() const
{
QStringList m;
foreach (Argument *arg, _arguments) {
if (! arg->name().isEmpty())
m.append(arg->name());
}
return m;
}
Symbol *Function::find(const QString &name) const
{
foreach (Argument *arg, _arguments) {
......
......@@ -34,6 +34,7 @@
#include <QtCore/QVector>
#include <QtCore/QHash>
#include <QtCore/QString>
#include <QtCore/QStringList>
namespace GLSL {
......@@ -122,6 +123,8 @@ public:
const Type *elementType() const { return indexElementType(); }
int dimension() const { return _dimension; }
QStringList members() const { return _members.keys(); }
virtual void add(Symbol *symbol);
virtual Symbol *find(const QString &name) const;
virtual const Type *type() const { return this; }
......@@ -180,6 +183,7 @@ public:
Struct(Scope *scope = 0)
: Scope(scope) {}
QStringList members() const;
virtual void add(Symbol *member);
virtual Symbol *find(const QString &name) const;
......@@ -221,6 +225,7 @@ public:
virtual Symbol *find(const QString &name) const;
virtual QStringList members() const;
virtual void add(Symbol *symbol) {
if (! symbol)
return;
......
......@@ -30,6 +30,11 @@
#include "glsleditor.h"
#include "glsleditorplugin.h"
#include <glsl/glslengine.h>
#include <glsl/glslengine.h>
#include <glsl/glslparser.h>
#include <glsl/glslsemantic.h>
#include <glsl/glslastdump.h>
#include <cplusplus/ExpressionUnderCursor.h>
#include <texteditor/completionsettings.h>
#include <QtGui/QIcon>
#include <QtGui/QPainter>
......@@ -300,9 +305,8 @@ bool CodeCompletion::triggersCompletion(TextEditor::ITextEditable *editor)
}
}
// if (ch == QLatin1Char('(') || ch == QLatin1Char('.') || ch == QLatin1Char('/'))
// return true;
if (ch == QLatin1Char('(') || ch == QLatin1Char('.'))
return true;
return false;
}
......@@ -316,26 +320,78 @@ int CodeCompletion::startCompletion(TextEditor::ITextEditable *editor)
while (ch.isLetterOrNumber() || ch == QLatin1Char('_'))
ch = editor->characterAt(--pos);
CPlusPlus::ExpressionUnderCursor expressionUnderCursor;
GLSLTextEditor *edit = qobject_cast<GLSLTextEditor *>(editor->widget());
const QIcon symbolIcon = iconForColor(Qt::darkCyan);
m_completions += m_keywordCompletions;
if (GLSLTextEditor *ed = qobject_cast<GLSLTextEditor *>(m_editor->widget())) {
QSet<QString> identifiers = ed->identifiers();
QStringList members;
if (ch == QLatin1Char('.')) {
QTextCursor tc(edit->document());
tc.setPosition(pos);
// get the expression under cursor
const QByteArray code = expressionUnderCursor(tc).toLatin1();
//qDebug() << endl << "expression:" << code;
// parse the expression
GLSL::Engine engine;
GLSL::Parser parser(&engine, code, code.size(), GLSL::Lexer::Variant_GLSL_Qt);
GLSL::ExpressionAST *expr = parser.parseExpression();
#if 0
// dump it!
QTextStream qout(stdout, QIODevice::WriteOnly);
GLSL::ASTDump dump(qout);
dump(expr);
#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();
} else if (const GLSL::Struct *structTy = exprTy.type->asStructType()) {
members = structTy->members();
} else {
// some other type
}
} else {
// undefined
identifiers += GLSLEditorPlugin::instance()->shaderInit()->engine->identifiers();
}
if (ed->isVertexShader())
identifiers += GLSLEditorPlugin::instance()->vertexShaderInit()->engine->identifiers();
} else {
// sorry, there's no document
}
if (ed->isFragmentShader())
identifiers += GLSLEditorPlugin::instance()->fragmentShaderInit()->engine->identifiers();
} else {
// it's a global completion
if (Document::Ptr doc = edit->glslDocument()) {
GLSL::Scope *currentScope = doc->scopeAt(pos);
foreach (const QString &id, identifiers) {
TextEditor::CompletionItem item(this);
item.text = id;
item.icon = symbolIcon;
m_completions.append(item);
// add the members from the scope chain
for (; currentScope; currentScope = currentScope->scope())
members += currentScope->members();
}
m_completions += m_keywordCompletions;
}
foreach (const QString &s, members) {
TextEditor::CompletionItem item(this);
item.icon = symbolIcon;
item.text = s;
m_completions.append(item);
}
m_startPosition = pos + 1;
......
......@@ -82,6 +82,66 @@ enum {
UPDATE_DOCUMENT_DEFAULT_INTERVAL = 150
};
namespace {
class CreateRanges: protected GLSL::Visitor
{
QTextDocument *textDocument;
Document::Ptr glslDocument;
public:
CreateRanges(QTextDocument *textDocument, Document::Ptr glslDocument)
: textDocument(textDocument), glslDocument(glslDocument) {}
void operator()(GLSL::AST *ast) { accept(ast); }
protected:
using GLSL::Visitor::visit;
virtual void endVisit(GLSL::CompoundStatementAST *ast)
{
if (ast->symbol) {
QTextCursor tc(textDocument);
tc.setPosition(ast->start);
tc.setPosition(ast->end, QTextCursor::KeepAnchor);
glslDocument->addRange(tc, ast->symbol);
}
}
};
} // end of anonymous namespace
Document::Document()
: _engine(0)
, _ast(0)
, _globalScope(0)
{
}
Document::~Document()
{
delete _globalScope;
delete _engine;
}
GLSL::Scope *Document::scopeAt(int position) const
{
foreach (const Range &c, _cursors) {
if (position >= c.cursor.selectionStart() && position <= c.cursor.selectionEnd())
return c.scope;
}
return _globalScope;
}
void Document::addRange(const QTextCursor &cursor, GLSL::Scope *scope)
{
Range c;
c.cursor = cursor;
c.scope = scope;
_cursors.append(c);
}
GLSLTextEditor::GLSLTextEditor(QWidget *parent) :
TextEditor::BaseTextEditor(parent),
m_outlineCombo(0)
......@@ -129,11 +189,6 @@ bool GLSLTextEditor::isOutdated() const
return false;
}
QSet<QString> GLSLTextEditor::identifiers() const
{
return m_identifiers;
}
Core::IEditor *GLSLEditorEditable::duplicate(QWidget *parent)
{
GLSLTextEditor *newEditor = new GLSLTextEditor(parent);
......@@ -270,20 +325,26 @@ void GLSLTextEditor::updateDocumentNow()
const QString contents = toPlainText(); // get the code from the editor
const QByteArray preprocessedCode = contents.toLatin1(); // ### use the QtCreator C++ preprocessor.
Engine engine;
Parser parser(&engine, preprocessedCode.constData(), preprocessedCode.size(), variant);
Document::Ptr doc(new Document());
GLSL::Engine *engine = new GLSL::Engine();
doc->_engine = new GLSL::Engine();
Parser parser(doc->_engine, preprocessedCode.constData(), preprocessedCode.size(), variant);
TranslationUnitAST *ast = parser.parse();
if (ast != 0 || extraSelections(CodeWarningsSelection).isEmpty()) {
GLSLEditorPlugin *plugin = GLSLEditorPlugin::instance();
Semantic sem;
Scope *globalScope = engine.newNamespace();
Scope *globalScope = engine->newNamespace();
doc->_globalScope = globalScope;
sem.translationUnit(plugin->shaderInit()->ast, globalScope, plugin->shaderInit()->engine);
if (variant & Lexer::Variant_VertexShader)
sem.translationUnit(plugin->vertexShaderInit()->ast, globalScope, plugin->vertexShaderInit()->engine);
if (variant & Lexer::Variant_FragmentShader)
sem.translationUnit(plugin->fragmentShaderInit()->ast, globalScope, plugin->fragmentShaderInit()->engine);
sem.translationUnit(ast, globalScope, &engine);
sem.translationUnit(ast, globalScope, engine);
CreateRanges createRanges(document(), doc);
createRanges(ast);
QTextCharFormat errorFormat;
errorFormat.setUnderlineStyle(QTextCharFormat::WaveUnderline);
......@@ -296,7 +357,7 @@ void GLSLTextEditor::updateDocumentNow()
QList<QTextEdit::ExtraSelection> sels;
QSet<int> errors;
foreach (const DiagnosticMessage &m, engine.diagnosticMessages()) {