diff options
author | Roberto Raggi <roberto.raggi@nokia.com> | 2010-11-26 10:59:33 +0100 |
---|---|---|
committer | Roberto Raggi <roberto.raggi@nokia.com> | 2010-11-26 11:00:21 +0100 |
commit | a794154dd766d6be56f164178b35ed50643ca1cd (patch) | |
tree | 5a6545706427fa130ea1d7d52a9a39d666455391 /src/plugins | |
parent | 20b58e4e6e1fbf1aa22fd597d33d6c7bca52fe45 (diff) | |
download | qt-creator-a794154dd766d6be56f164178b35ed50643ca1cd.tar.gz |
Added indenter, code folding and automagically brace insertion to the GLSL editor.
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/glsleditor/GLSLEditor.mimetypes.xml | 1 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslautocompleter.cpp | 137 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslautocompleter.h | 59 | ||||
-rw-r--r-- | src/plugins/glsleditor/glsleditor.cpp | 5 | ||||
-rw-r--r-- | src/plugins/glsleditor/glsleditor.pro | 12 | ||||
-rw-r--r-- | src/plugins/glsleditor/glsleditor_dependencies.pri | 2 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslhighlighter.cpp | 330 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslhighlighter.h | 2 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslindenter.cpp | 116 | ||||
-rw-r--r-- | src/plugins/glsleditor/glslindenter.h | 60 | ||||
-rw-r--r-- | src/plugins/plugins.pro | 3 |
11 files changed, 719 insertions, 8 deletions
diff --git a/src/plugins/glsleditor/GLSLEditor.mimetypes.xml b/src/plugins/glsleditor/GLSLEditor.mimetypes.xml index c0fbcb84b4..d9fa822b01 100644 --- a/src/plugins/glsleditor/GLSLEditor.mimetypes.xml +++ b/src/plugins/glsleditor/GLSLEditor.mimetypes.xml @@ -5,6 +5,7 @@ <sub-class-of type="text/plain"/> <comment>GLSL file</comment> <glob pattern="*.glsl"/> + <glob pattern="*.shader"/> <glob pattern="*.frag"/> <glob pattern="*.vert"/> <glob pattern="*.fsh"/> diff --git a/src/plugins/glsleditor/glslautocompleter.cpp b/src/plugins/glsleditor/glslautocompleter.cpp new file mode 100644 index 0000000000..acb3044641 --- /dev/null +++ b/src/plugins/glsleditor/glslautocompleter.cpp @@ -0,0 +1,137 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "glslautocompleter.h" + +#include <Token.h> +#include <cplusplus/SimpleLexer.h> +#include <cplusplus/MatchingText.h> +#include <cplusplus/BackwardsScanner.h> + +#include <QtCore/QLatin1Char> +#include <QtGui/QTextCursor> + +using namespace GLSLEditor; +using namespace Internal; +using namespace CPlusPlus; + +GLSLCompleter::GLSLCompleter() +{} + +GLSLCompleter::~GLSLCompleter() +{} + +bool GLSLCompleter::doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert) const +{ + QChar ch; + + if (! textToInsert.isEmpty()) + ch = textToInsert.at(0); + + if (! (MatchingText::shouldInsertMatchingText(cursor) + || ch == QLatin1Char('\'') + || ch == QLatin1Char('"'))) + return false; + else if (isInComment(cursor)) + return false; + + return true; +} + +bool GLSLCompleter::doContextAllowsElectricCharacters(const QTextCursor &cursor) const +{ + const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), + BackwardsScanner::previousBlockState(cursor.block())); + + // XXX Duplicated from CPPEditor::isInComment to avoid tokenizing twice + if (tk.isComment()) { + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + + if (pos == tk.end()) { + if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) + return false; + + const int state = cursor.block().userState() & 0xFF; + if (state > 0) + return false; + } + + if (pos < tk.end()) + return false; + } + else if (tk.is(T_STRING_LITERAL) || tk.is(T_WIDE_STRING_LITERAL) + || tk.is(T_CHAR_LITERAL) || tk.is(T_WIDE_CHAR_LITERAL)) { + + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + if (pos <= tk.end()) + return false; + } + + return true; +} + +bool GLSLCompleter::doIsInComment(const QTextCursor &cursor) const +{ + const Token tk = SimpleLexer::tokenAt(cursor.block().text(), cursor.positionInBlock(), + BackwardsScanner::previousBlockState(cursor.block())); + + if (tk.isComment()) { + const unsigned pos = cursor.selectionEnd() - cursor.block().position(); + + if (pos == tk.end()) { + if (tk.is(T_CPP_COMMENT) || tk.is(T_CPP_DOXY_COMMENT)) + return true; + + const int state = cursor.block().userState() & 0xFF; + if (state > 0) + return true; + } + + if (pos < tk.end()) + return true; + } + + return false; +} + +QString GLSLCompleter::doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const +{ + MatchingText m; + return m.insertMatchingBrace(cursor, text, la, skippedChars); +} + +QString GLSLCompleter::doInsertParagraphSeparator(const QTextCursor &cursor) const +{ + MatchingText m; + return m.insertParagraphSeparator(cursor); +} diff --git a/src/plugins/glsleditor/glslautocompleter.h b/src/plugins/glsleditor/glslautocompleter.h new file mode 100644 index 0000000000..1fb01991ca --- /dev/null +++ b/src/plugins/glsleditor/glslautocompleter.h @@ -0,0 +1,59 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GLSLAUTOCOMPLETER_H +#define GLSLAUTOCOMPLETER_H + +#include <texteditor/autocompleter.h> + +namespace GLSLEditor { +namespace Internal { + +class GLSLCompleter : public TextEditor::AutoCompleter +{ +public: + GLSLCompleter(); + virtual ~GLSLCompleter(); + +private: + virtual bool doContextAllowsAutoParentheses(const QTextCursor &cursor, + const QString &textToInsert = QString()) const; + virtual bool doContextAllowsElectricCharacters(const QTextCursor &cursor) const; + virtual bool doIsInComment(const QTextCursor &cursor) const; + virtual QString doInsertMatchingBrace(const QTextCursor &cursor, + const QString &text, + QChar la, + int *skippedChars) const; + virtual QString doInsertParagraphSeparator(const QTextCursor &cursor) const; +}; + +} // Internal +} // GLSLEditor + +#endif // GLSLAUTOCOMPLETER_H diff --git a/src/plugins/glsleditor/glsleditor.cpp b/src/plugins/glsleditor/glsleditor.cpp index f256ee50fb..a420c1ce48 100644 --- a/src/plugins/glsleditor/glsleditor.cpp +++ b/src/plugins/glsleditor/glsleditor.cpp @@ -32,6 +32,8 @@ #include "glsleditorconstants.h" #include "glsleditorplugin.h" #include "glslhighlighter.h" +#include "glslautocompleter.h" +#include "glslindenter.h" #include <glsl/glsllexer.h> #include <glsl/glslparser.h> @@ -85,7 +87,8 @@ GLSLTextEditor::GLSLTextEditor(QWidget *parent) : setParenthesesMatchingEnabled(true); setMarksVisible(true); setCodeFoldingSupported(true); - //setIndenter(new Indenter); + setIndenter(new GLSLIndenter()); + setAutoCompleter(new GLSLCompleter()); m_updateDocumentTimer = new QTimer(this); m_updateDocumentTimer->setInterval(UPDATE_DOCUMENT_DEFAULT_INTERVAL); diff --git a/src/plugins/glsleditor/glsleditor.pro b/src/plugins/glsleditor/glsleditor.pro index ea3650c9f3..424c0d0c33 100644 --- a/src/plugins/glsleditor/glsleditor.pro +++ b/src/plugins/glsleditor/glsleditor.pro @@ -16,8 +16,10 @@ glsleditoreditable.h \ glsleditorfactory.h \ glsleditorplugin.h \ glslfilewizard.h \ - glslhighlighter.h \ - glslcodecompletion.h +glslhighlighter.h \ +glslcodecompletion.h \ +glslautocompleter.h \ +glslindenter.h SOURCES += \ glsleditor.cpp \ @@ -26,8 +28,10 @@ glsleditoreditable.cpp \ glsleditorfactory.cpp \ glsleditorplugin.cpp \ glslfilewizard.cpp \ - glslhighlighter.cpp \ - glslcodecompletion.cpp +glslhighlighter.cpp \ +glslcodecompletion.cpp \ +glslautocompleter.cpp \ +glslindenter.cpp OTHER_FILES += GLSLEditor.mimetypes.xml RESOURCES += glsleditor.qrc diff --git a/src/plugins/glsleditor/glsleditor_dependencies.pri b/src/plugins/glsleditor/glsleditor_dependencies.pri index dd92bec33c..163216eac4 100644 --- a/src/plugins/glsleditor/glsleditor_dependencies.pri +++ b/src/plugins/glsleditor/glsleditor_dependencies.pri @@ -1,5 +1,7 @@ include(../../plugins/coreplugin/coreplugin.pri) include(../../plugins/texteditor/texteditor.pri) include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/cpptools/cpptools.pri) include(../../libs/glsl/glsl.pri) include(../../libs/utils/utils.pri) +include(../../libs/cplusplus/cplusplus.pri) diff --git a/src/plugins/glsleditor/glslhighlighter.cpp b/src/plugins/glsleditor/glslhighlighter.cpp index 72b58460d0..bf7bc57a9e 100644 --- a/src/plugins/glsleditor/glslhighlighter.cpp +++ b/src/plugins/glsleditor/glslhighlighter.cpp @@ -29,11 +29,13 @@ #include "glslhighlighter.h" #include <glsl/glsllexer.h> #include <glsl/glslparser.h> +#include <texteditor/basetextdocumentlayout.h> #include <QtCore/QDebug> using namespace GLSLEditor; using namespace GLSLEditor::Internal; +using namespace TextEditor; Highlighter::Highlighter(QTextDocument *parent) : TextEditor::SyntaxHighlighter(parent) @@ -52,18 +54,314 @@ void Highlighter::setFormats(const QVector<QTextCharFormat> &formats) void Highlighter::highlightBlock(const QString &text) { + const int previousState = previousBlockState(); + int state = 0, initialBraceDepth = 0; + if (previousState != -1) { + state = previousState & 0xff; + initialBraceDepth = previousState >> 8; + } + + int braceDepth = initialBraceDepth; + + const QByteArray data = text.toLatin1(); + GLSL::Lexer lex(/*engine=*/ 0, data.constData(), data.size()); + lex.setState(state); + lex.setScanKeywords(false); + lex.setScanComments(true); + const int variant = GLSL::Lexer::Variant_GLSL_Qt | // ### FIXME: hardcoded + GLSL::Lexer::Variant_VertexShader | + GLSL::Lexer::Variant_FragmentShader; + lex.setVariant(variant); + + int initialState = state; + + QList<GLSL::Token> tokens; + GLSL::Token tk; + do { + lex.yylex(&tk); + tokens.append(tk); + } while (tk.isNot(GLSL::Parser::EOF_SYMBOL)); + + state = lex.state(); // refresh the state + + int foldingIndent = initialBraceDepth; + if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) { + userData->setFoldingIndent(0); + userData->setFoldingStartIncluded(false); + userData->setFoldingEndIncluded(false); + } + + if (tokens.isEmpty()) { + setCurrentBlockState(previousState); + BaseTextDocumentLayout::clearParentheses(currentBlock()); + if (text.length()) // the empty line can still contain whitespace + setFormat(0, text.length(), m_formats[GLSLVisualWhitespace]); + BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent); + return; + } + + const int firstNonSpace = tokens.first().begin(); + + Parentheses parentheses; + parentheses.reserve(20); // assume wizard level ;-) + + bool highlightAsPreprocessor = false; + + for (int i = 0; i < tokens.size(); ++i) { + const GLSL::Token &tk = tokens.at(i); + + int previousTokenEnd = 0; + if (i != 0) { + // mark the whitespaces + previousTokenEnd = tokens.at(i - 1).begin() + + tokens.at(i - 1).length; + } + + if (previousTokenEnd != tk.begin()) { + setFormat(previousTokenEnd, tk.begin() - previousTokenEnd, + m_formats[GLSLVisualWhitespace]); + } + + if (tk.is(GLSL::Parser::T_LEFT_PAREN) || tk.is(GLSL::Parser::T_LEFT_BRACE) || tk.is(GLSL::Parser::T_LEFT_BRACKET)) { + const QChar c = text.at(tk.begin()); + parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.begin())); + if (tk.is(GLSL::Parser::T_LEFT_BRACE)) { + ++braceDepth; + + // if a folding block opens at the beginning of a line, treat the entire line + // as if it were inside the folding block + if (tk.begin() == firstNonSpace) { + ++foldingIndent; + BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true); + } + } + } else if (tk.is(GLSL::Parser::T_RIGHT_PAREN) || tk.is(GLSL::Parser::T_RIGHT_BRACE) || tk.is(GLSL::Parser::T_RIGHT_BRACKET)) { + const QChar c = text.at(tk.begin()); + parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.begin())); + if (tk.is(GLSL::Parser::T_RIGHT_BRACE)) { + --braceDepth; + if (braceDepth < foldingIndent) { + // unless we are at the end of the block, we reduce the folding indent + if (i == tokens.size()-1 || tokens.at(i+1).is(GLSL::Parser::T_SEMICOLON)) + BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true); + else + foldingIndent = qMin(braceDepth, foldingIndent); + } + } + } + + bool highlightCurrentWordAsPreprocessor = highlightAsPreprocessor; + + if (highlightAsPreprocessor) + highlightAsPreprocessor = false; + + if (false /* && i == 0 && tk.is(GLSL::Parser::T_POUND)*/) { + highlightLine(text, tk.begin(), tk.length, m_formats[GLSLPreprocessorFormat]); + highlightAsPreprocessor = true; + + } else if (highlightCurrentWordAsPreprocessor && isPPKeyword(text.midRef(tk.begin(), tk.length))) + setFormat(tk.begin(), tk.length, m_formats[GLSLPreprocessorFormat]); + + else if (tk.is(GLSL::Parser::T_NUMBER)) + setFormat(tk.begin(), tk.length, m_formats[GLSLNumberFormat]); + + else if (tk.is(GLSL::Parser::T_COMMENT)) { + highlightLine(text, tk.begin(), tk.length, m_formats[GLSLCommentFormat]); + + // we need to insert a close comment parenthesis, if + // - the line starts in a C Comment (initalState != 0) + // - the first token of the line is a T_COMMENT (i == 0 && tk.is(T_COMMENT)) + // - is not a continuation line (tokens.size() > 1 || ! state) + if (initialState && i == 0 && (tokens.size() > 1 || ! state)) { + --braceDepth; + // unless we are at the end of the block, we reduce the folding indent + if (i == tokens.size()-1) + BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true); + else + foldingIndent = qMin(braceDepth, foldingIndent); + const int tokenEnd = tk.begin() + tk.length - 1; + parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd)); + + // clear the initial state. + initialState = 0; + } + + } else if (tk.is(GLSL::Parser::T_IDENTIFIER)) { + int kind = lex.findKeyword(data.constData() + tk.position, tk.length); + if (kind == GLSL::Parser::T_RESERVED) + setFormat(tk.position, tk.length, m_formats[GLSLReservedKeyword]); + else if (kind != GLSL::Parser::T_IDENTIFIER) + setFormat(tk.position, tk.length, m_formats[GLSLKeywordFormat]); + } + } + + // mark the trailing white spaces + { + const GLSL::Token tk = tokens.last(); + const int lastTokenEnd = tk.begin() + tk.length; + if (text.length() > lastTokenEnd) + highlightLine(text, lastTokenEnd, text.length() - lastTokenEnd, QTextCharFormat()); + } + + if (! initialState && state && ! tokens.isEmpty()) { + parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'), + tokens.last().begin())); + ++braceDepth; + } + + BaseTextDocumentLayout::setParentheses(currentBlock(), parentheses); + + // if the block is ifdefed out, we only store the parentheses, but + + // do not adjust the brace depth. + if (BaseTextDocumentLayout::ifdefedOut(currentBlock())) { + braceDepth = initialBraceDepth; + foldingIndent = initialBraceDepth; + } + + BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent); + + // optimization: if only the brace depth changes, we adjust subsequent blocks + // to have QSyntaxHighlighter stop the rehighlighting + int currentState = currentBlockState(); + if (currentState != -1) { + int oldState = currentState & 0xff; + int oldBraceDepth = currentState >> 8; + if (oldState == lex.state() && oldBraceDepth != braceDepth) { + int delta = braceDepth - oldBraceDepth; + QTextBlock block = currentBlock().next(); + while (block.isValid() && block.userState() != -1) { + BaseTextDocumentLayout::changeBraceDepth(block, delta); + BaseTextDocumentLayout::changeFoldingIndent(block, delta); + block = block.next(); + } + } + } + + setCurrentBlockState((braceDepth << 8) | lex.state()); +} + +void Highlighter::highlightLine(const QString &text, int position, int length, + const QTextCharFormat &format) +{ + const QTextCharFormat visualSpaceFormat = m_formats[GLSLVisualWhitespace]; + + const int end = position + length; + int index = position; + + while (index != end) { + const bool isSpace = text.at(index).isSpace(); + const int start = index; + + do { ++index; } + while (index != end && text.at(index).isSpace() == isSpace); + + const int tokenLength = index - start; + if (isSpace) + setFormat(start, tokenLength, visualSpaceFormat); + else if (format.isValid()) + setFormat(start, tokenLength, format); + } +} + +bool Highlighter::isPPKeyword(const QStringRef &text) const +{ + switch (text.length()) + { + case 2: + if (text.at(0) == 'i' && text.at(1) == 'f') + return true; + break; + + case 4: + if (text.at(0) == 'e' && text == QLatin1String("elif")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("else")) + return true; + break; + + case 5: + if (text.at(0) == 'i' && text == QLatin1String("ifdef")) + return true; + else if (text.at(0) == 'u' && text == QLatin1String("undef")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("endif")) + return true; + else if (text.at(0) == 'e' && text == QLatin1String("error")) + return true; + break; + + case 6: + if (text.at(0) == 'i' && text == QLatin1String("ifndef")) + return true; + if (text.at(0) == 'i' && text == QLatin1String("import")) + return true; + else if (text.at(0) == 'd' && text == QLatin1String("define")) + return true; + else if (text.at(0) == 'p' && text == QLatin1String("pragma")) + return true; + break; + + case 7: + if (text.at(0) == 'i' && text == QLatin1String("include")) + return true; + else if (text.at(0) == 'w' && text == QLatin1String("warning")) + return true; + break; + + case 12: + if (text.at(0) == 'i' && text == QLatin1String("include_next")) + return true; + break; + + default: + break; + } + + return false; +} + +#if 0 +void Highlighter::highlightBlock(const QString &text) +{ + const int previousState = previousBlockState(); + int state = 0, initialBraceDepth = 0; + if (previousState != -1) { + state = previousState & 0xff; + initialBraceDepth = previousState >> 8; + } + + int braceDepth = initialBraceDepth; + + Parentheses parentheses; + parentheses.reserve(20); // assume wizard level ;-) + const QByteArray data = text.toLatin1(); GLSL::Lexer lex(/*engine=*/ 0, data.constData(), data.size()); - lex.setState(qMax(0, previousBlockState())); + lex.setState(qMax(0, previousState)); lex.setScanKeywords(false); lex.setScanComments(true); const int variant = GLSL::Lexer::Variant_GLSL_Qt | // ### FIXME: hardcoded GLSL::Lexer::Variant_VertexShader | GLSL::Lexer::Variant_FragmentShader; lex.setVariant(variant); + + int foldingIndent = initialBraceDepth; + if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) { + userData->setFoldingIndent(0); + userData->setFoldingStartIncluded(false); + userData->setFoldingEndIncluded(false); + } + + QList<GLSL::Token> tokens; GLSL::Token tk; do { lex.yylex(&tk); + tokens.append(tk); + } while (tk.isNot(GLSL::Parser::EOF_SYMBOL)); + + for (int i = 0; i < tokens.size(); ++i) { + const GLSL::Token &tk = tokens.at(i); if (tk.is(GLSL::Parser::T_NUMBER)) setFormat(tk.position, tk.length, m_formats[GLSLNumberFormat]); @@ -75,7 +373,35 @@ void Highlighter::highlightBlock(const QString &text) setFormat(tk.position, tk.length, m_formats[GLSLReservedKeyword]); else if (kind != GLSL::Parser::T_IDENTIFIER) setFormat(tk.position, tk.length, m_formats[GLSLKeywordFormat]); + } else if (tk.is(GLSL::Parser::T_LEFT_PAREN) || tk.is(GLSL::Parser::T_LEFT_BRACE) || tk.is(GLSL::Parser::T_LEFT_BRACKET)) { + const QChar c = text.at(tk.begin()); + parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.begin())); + if (tk.is(GLSL::Parser::T_LEFT_BRACE)) { + ++braceDepth; + + // if a folding block opens at the beginning of a line, treat the entire line + // as if it were inside the folding block +// if (tk.begin() == firstNonSpace) { +// ++foldingIndent; +// BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true); +// } + } + } else if (tk.is(GLSL::Parser::T_RIGHT_PAREN) || tk.is(GLSL::Parser::T_RIGHT_BRACE) || tk.is(GLSL::Parser::T_RIGHT_BRACKET)) { + const QChar c = text.at(tk.begin()); + parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.begin())); + if (tk.is(GLSL::Parser::T_RIGHT_BRACE)) { + --braceDepth; + if (braceDepth < foldingIndent) { + // unless we are at the end of the block, we reduce the folding indent + if (i == tokens.size()-1 || tokens.at(i+1).is(GLSL::Parser::T_SEMICOLON)) + BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true); + else + foldingIndent = qMin(braceDepth, foldingIndent); + } + } } + } while (tk.isNot(GLSL::Parser::EOF_SYMBOL)); - setCurrentBlockState(lex.state()); + setCurrentBlockState((braceDepth << 8) | lex.state()); } +#endif diff --git a/src/plugins/glsleditor/glslhighlighter.h b/src/plugins/glsleditor/glslhighlighter.h index b92763bc6b..8b31121d49 100644 --- a/src/plugins/glsleditor/glslhighlighter.h +++ b/src/plugins/glsleditor/glslhighlighter.h @@ -62,6 +62,8 @@ public: protected: void highlightBlock(const QString &text); + void highlightLine(const QString &text, int position, int length, const QTextCharFormat &format); + bool isPPKeyword(const QStringRef &text) const; private: QTextCharFormat m_formats[NumGLSLFormats]; diff --git a/src/plugins/glsleditor/glslindenter.cpp b/src/plugins/glsleditor/glslindenter.cpp new file mode 100644 index 0000000000..8fecc7e2d3 --- /dev/null +++ b/src/plugins/glsleditor/glslindenter.cpp @@ -0,0 +1,116 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "glslindenter.h" + +#include <cpptools/cppcodeformatter.h> +#include <texteditor/basetexteditor.h> +#include <texteditor/tabsettings.h> + +#include <QtCore/QChar> +#include <QtGui/QTextDocument> +#include <QtGui/QTextBlock> +#include <QtGui/QTextCursor> + +using namespace GLSLEditor; +using namespace Internal; + +GLSLIndenter::GLSLIndenter() +{} + +GLSLIndenter::~GLSLIndenter() +{} + +bool GLSLIndenter::doIsElectricalCharacter(const QChar &ch) const +{ + if (ch == QLatin1Char('{') || + ch == QLatin1Char('}') || + ch == QLatin1Char(':') || + ch == QLatin1Char('#')) { + return true; + } + return false; +} + +void GLSLIndenter::doIndentBlock(QTextDocument *doc, + const QTextBlock &block, + const QChar &typedChar, + TextEditor::BaseTextEditor *editor) +{ + Q_UNUSED(doc) + + const TextEditor::TabSettings &ts = editor->tabSettings(); + CppTools::QtStyleCodeFormatter codeFormatter(ts); + + codeFormatter.updateStateUntil(block); + int indent; + int padding; + codeFormatter.indentFor(block, &indent, &padding); + + // only reindent the current line when typing electric characters if the + // indent is the same it would be if the line were empty + if (isElectricCharacter(typedChar)) { + int newlineIndent; + int newlinePadding; + codeFormatter.indentForNewLineAfter(block.previous(), &newlineIndent, &newlinePadding); + if (ts.indentationColumn(block.text()) != newlineIndent + newlinePadding) + return; + } + + ts.indentLine(block, indent + padding, padding); +} + +void GLSLIndenter::doIndent(QTextDocument *doc, + const QTextCursor &cursor, + const QChar &typedChar, + TextEditor::BaseTextEditor *editor) +{ + if (cursor.hasSelection()) { + QTextBlock block = doc->findBlock(cursor.selectionStart()); + const QTextBlock end = doc->findBlock(cursor.selectionEnd()).next(); + + const TextEditor::TabSettings &ts = editor->tabSettings(); + CppTools::QtStyleCodeFormatter codeFormatter(ts); + codeFormatter.updateStateUntil(block); + + QTextCursor tc = editor->textCursor(); + tc.beginEditBlock(); + do { + int indent; + int padding; + codeFormatter.indentFor(block, &indent, &padding); + ts.indentLine(block, indent + padding, padding); + codeFormatter.updateLineStateChange(block); + block = block.next(); + } while (block.isValid() && block != end); + tc.endEditBlock(); + } else { + indentBlock(doc, cursor.block(), typedChar, editor); + } +} diff --git a/src/plugins/glsleditor/glslindenter.h b/src/plugins/glsleditor/glslindenter.h new file mode 100644 index 0000000000..03af9f378c --- /dev/null +++ b/src/plugins/glsleditor/glslindenter.h @@ -0,0 +1,60 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef GLSLINDENTER_H +#define GLSLINDENTER_H + +#include <texteditor/indenter.h> + +namespace GLSLEditor { +namespace Internal { + +class GLSLIndenter : public TextEditor::Indenter +{ +public: + GLSLIndenter(); + virtual ~GLSLIndenter(); + +private: + virtual bool doIsElectricalCharacter(const QChar &ch) const; + virtual void doIndentBlock(QTextDocument *doc, + const QTextBlock &block, + const QChar &typedChar, + TextEditor::BaseTextEditor *editor); + + virtual void doIndent(QTextDocument *doc, + const QTextCursor &cursor, + const QChar &typedChar, + TextEditor::BaseTextEditor *editor); +}; + +} // Internal +} // GLSLEditor + +#endif // GLSLINDENTER_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 8a2aaa961a..ed90b1e75d 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -50,7 +50,7 @@ contains(QT_CONFIG, declarative) { exists($${QT_PRIVATE_HEADERS}/QtDeclarative/private/qdeclarativecontext_p.h) { minQtVersion(4, 7, 1) { - SUBDIRS += plugin_qmldesigner + SUBDIRS += plugin_qmldesigner } else { warning() warning("QmlDesigner plugin has been disabled.") @@ -201,6 +201,7 @@ plugin_glsleditor.subdir = glsleditor plugin_glsleditor.depends = plugin_texteditor plugin_glsleditor.depends += plugin_coreplugin plugin_glsleditor.depends += plugin_projectexplorer +plugin_glsleditor.depends += plugin_cpptools plugin_qmlprojectmanager.subdir = qmlprojectmanager plugin_qmlprojectmanager.depends = plugin_texteditor |