diff --git a/src/plugins/cppeditor/cpphighlighter.cpp b/src/plugins/cppeditor/cpphighlighter.cpp index 77c9d1e81e2..a8a02fd550c 100644 --- a/src/plugins/cppeditor/cpphighlighter.cpp +++ b/src/plugins/cppeditor/cpphighlighter.cpp @@ -65,15 +65,19 @@ void CppHighlighter::highlightBlock(const QString &text) const QList tokens = tokenize(text, initialState); state = tokenize.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); - if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) { - userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse); - userData->setCollapseMode(TextBlockUserData::NoCollapse); - } BaseTextDocumentLayout::clearParentheses(currentBlock()); if (text.length()) // the empty line can still contain whitespace setFormat(0, text.length(), m_formats[CppVisualWhitespace]); + BaseTextDocumentLayout::setFoldingIndent(currentBlock(), foldingIndent); return; } @@ -102,13 +106,29 @@ void CppHighlighter::highlightBlock(const QString &text) if (tk.is(T_LPAREN) || tk.is(T_LBRACE) || tk.is(T_LBRACKET)) { const QChar c(tk.text().at(0)); parentheses.append(Parenthesis(Parenthesis::Opened, c, tk.position())); - if (tk.is(T_LBRACE)) + if (tk.is(T_LBRACE)) { ++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.position() == firstNonSpace) { + ++foldingIndent; + BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true); + } + } } else if (tk.is(T_RPAREN) || tk.is(T_RBRACE) || tk.is(T_RBRACKET)) { const QChar c(tk.text().at(0)); parentheses.append(Parenthesis(Parenthesis::Closed, c, tk.position())); - if (tk.is(T_RBRACE)) + if (tk.is(T_RBRACE)) { --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(T_SEMICOLON)) + BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true); + else + foldingIndent = qMin(braceDepth, foldingIndent); + } + } } bool highlightCurrentWordAsPreprocessor = highlightAsPreprocessor; @@ -148,7 +168,11 @@ void CppHighlighter::highlightBlock(const QString &text) // - 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.position() + tk.length() - 1; parentheses.append(Parenthesis(Parenthesis::Closed, QLatin1Char('-'), tokenEnd)); @@ -167,6 +191,7 @@ void CppHighlighter::highlightBlock(const QString &text) else if (tk.is(T_IDENTIFIER)) highlightWord(tk.text(), tk.position(), tk.length()); + } // mark the trailing white spaces @@ -177,43 +202,23 @@ void CppHighlighter::highlightBlock(const QString &text) highlightLine(text, lastTokenEnd, text.length() - lastTokenEnd, QTextCharFormat()); } - if (TextBlockUserData *userData = BaseTextDocumentLayout::testUserData(currentBlock())) { - userData->setClosingCollapseMode(TextBlockUserData::NoClosingCollapse); - userData->setCollapseMode(TextBlockUserData::NoCollapse); - } - if (! initialState && state && ! tokens.isEmpty()) { parentheses.append(Parenthesis(Parenthesis::Opened, QLatin1Char('+'), tokens.last().position())); ++braceDepth; } - QChar c; - int collapse = Parenthesis::collapseAtPos(parentheses, &c); - if (collapse >= 0) { - TextBlockUserData::CollapseMode collapseMode = TextBlockUserData::CollapseAfter; - if (collapse == firstNonSpace && c != QLatin1Char('+')) - collapseMode = TextBlockUserData::CollapseThis; - BaseTextDocumentLayout::userData(currentBlock())->setCollapseMode(collapseMode); - } - - - int cc = Parenthesis::closeCollapseAtPos(parentheses); - if (cc >= 0) { - TextBlockUserData *userData = BaseTextDocumentLayout::userData(currentBlock()); - userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapse); - QString trailingText = text.mid(cc+1).simplified(); - if (trailingText.isEmpty() || trailingText == QLatin1String(";")) { - userData->setClosingCollapseMode(TextBlockUserData::ClosingCollapseAtEnd); - } - } - 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())) + 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 @@ -226,6 +231,7 @@ void CppHighlighter::highlightBlock(const QString &text) QTextBlock block = currentBlock().next(); while (block.isValid() && block.userState() != -1) { BaseTextDocumentLayout::changeBraceDepth(block, delta); + BaseTextDocumentLayout::changeFoldingIndent(block, delta); block = block.next(); } } diff --git a/src/plugins/qmljseditor/qmljshighlighter.cpp b/src/plugins/qmljseditor/qmljshighlighter.cpp index 685d7bb80ce..0e4cf833b0d 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.cpp +++ b/src/plugins/qmljseditor/qmljshighlighter.cpp @@ -44,6 +44,7 @@ Highlighter::Highlighter(QTextDocument *parent) { m_currentBlockParentheses.reserve(20); m_braceDepth = 0; + m_foldingIndent = 0; } Highlighter::~Highlighter() @@ -102,27 +103,27 @@ void Highlighter::highlightBlock(const QString &text) break; case Token::LeftParenthesis: - onOpeningParenthesis('(', token.offset); + onOpeningParenthesis('(', token.offset, index == 0); break; case Token::RightParenthesis: - onClosingParenthesis(')', token.offset); + onClosingParenthesis(')', token.offset, index == tokens.size()-1); break; case Token::LeftBrace: - onOpeningParenthesis('{', token.offset); + onOpeningParenthesis('{', token.offset, index == 0); break; case Token::RightBrace: - onClosingParenthesis('}', token.offset); + onClosingParenthesis('}', token.offset, index == tokens.size()-1); break; case Token::LeftBracket: - onOpeningParenthesis('[', token.offset); + onOpeningParenthesis('[', token.offset, index == 0); break; case Token::RightBracket: - onClosingParenthesis(']', token.offset); + onClosingParenthesis(']', token.offset, index == tokens.size()-1); break; case Token::Identifier: { @@ -232,12 +233,8 @@ void Highlighter::highlightBlock(const QString &text) setFormat(previousTokenEnd, text.length() - previousTokenEnd, m_formats[VisualWhitespace]); - int firstNonSpace = 0; - if (! tokens.isEmpty()) - firstNonSpace = tokens.first().offset; - setCurrentBlockState(m_scanner.state()); - onBlockEnd(m_scanner.state(), firstNonSpace); + onBlockEnd(m_scanner.state()); } bool Highlighter::maybeQmlKeyword(const QStringRef &text) const @@ -301,6 +298,12 @@ int Highlighter::onBlockStart() { m_currentBlockParentheses.clear(); m_braceDepth = 0; + m_foldingIndent = 0; + if (TextEditor::TextBlockUserData *userData = TextEditor::BaseTextDocumentLayout::testUserData(currentBlock())) { + userData->setFoldingIndent(0); + userData->setFoldingStartIncluded(false); + userData->setFoldingEndIncluded(false); + } int state = 0; int previousState = previousBlockState(); @@ -308,56 +311,41 @@ int Highlighter::onBlockStart() state = previousState & 0xff; m_braceDepth = previousState >> 8; } + m_foldingIndent = m_braceDepth; return state; } -void Highlighter::onBlockEnd(int state, int firstNonSpace) +void Highlighter::onBlockEnd(int state) { typedef TextEditor::TextBlockUserData TextEditorBlockData; setCurrentBlockState((m_braceDepth << 8) | state); - - // Set block data parentheses. Force creation of block data unless empty - TextEditorBlockData *blockData = 0; - - if (QTextBlockUserData *userData = currentBlockUserData()) - blockData = static_cast(userData); - - if (!blockData && !m_currentBlockParentheses.empty()) { - blockData = new TextEditorBlockData; - setCurrentBlockUserData(blockData); - } - if (blockData) { - blockData->setParentheses(m_currentBlockParentheses); - blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse); - blockData->setCollapseMode(TextEditor::TextBlockUserData::NoCollapse); - } - if (!m_currentBlockParentheses.isEmpty()) { - QTC_ASSERT(blockData, return); - int collapse = Parenthesis::collapseAtPos(m_currentBlockParentheses); - if (collapse >= 0) { - if (collapse == firstNonSpace) - blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseThis); - else - blockData->setCollapseMode(TextEditor::TextBlockUserData::CollapseAfter); - } - if (Parenthesis::hasClosingCollapse(m_currentBlockParentheses)) - blockData->setClosingCollapseMode(TextEditor::TextBlockUserData::NoClosingCollapse); - } + TextEditor::BaseTextDocumentLayout::setParentheses(currentBlock(), m_currentBlockParentheses); + TextEditor::BaseTextDocumentLayout::setFoldingIndent(currentBlock(), m_foldingIndent); } -void Highlighter::onOpeningParenthesis(QChar parenthesis, int pos) +void Highlighter::onOpeningParenthesis(QChar parenthesis, int pos, bool atStart) { - if (parenthesis == QLatin1Char('{') || parenthesis == QLatin1Char('[')) + if (parenthesis == QLatin1Char('{') || parenthesis == QLatin1Char('[')) { ++m_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 (atStart) + TextEditor::BaseTextDocumentLayout::userData(currentBlock())->setFoldingStartIncluded(true); + } m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Opened, parenthesis, pos)); } -void Highlighter::onClosingParenthesis(QChar parenthesis, int pos) +void Highlighter::onClosingParenthesis(QChar parenthesis, int pos, bool atEnd) { - if (parenthesis == QLatin1Char('}') || parenthesis == QLatin1Char(']')) + if (parenthesis == QLatin1Char('}') || parenthesis == QLatin1Char(']')) { --m_braceDepth; + if (atEnd) + TextEditor::BaseTextDocumentLayout::userData(currentBlock())->setFoldingEndIncluded(true); + else + m_foldingIndent = qMin(m_braceDepth, m_foldingIndent); // folding indent is the minimum brace depth of a block + } m_currentBlockParentheses.push_back(Parenthesis(Parenthesis::Closed, parenthesis, pos)); } diff --git a/src/plugins/qmljseditor/qmljshighlighter.h b/src/plugins/qmljseditor/qmljshighlighter.h index 974bbe9012b..cf59f5d6135 100644 --- a/src/plugins/qmljseditor/qmljshighlighter.h +++ b/src/plugins/qmljseditor/qmljshighlighter.h @@ -78,12 +78,12 @@ protected: virtual void highlightBlock(const QString &text); int onBlockStart(); - void onBlockEnd(int state, int firstNonSpace); + void onBlockEnd(int state); // The functions are notified whenever parentheses are encountered. // Custom behaviour can be added, for example storing info for indenting. - void onOpeningParenthesis(QChar parenthesis, int pos); - void onClosingParenthesis(QChar parenthesis, int pos); + void onOpeningParenthesis(QChar parenthesis, int pos, bool atStart); + void onClosingParenthesis(QChar parenthesis, int pos, bool atEnd); bool maybeQmlKeyword(const QStringRef &text) const; bool maybeQmlBuiltinType(const QStringRef &text) const; @@ -94,6 +94,7 @@ private: bool m_qmlEnabled; int m_braceDepth; + int m_foldingIndent; QmlJS::Scanner m_scanner; Parentheses m_currentBlockParentheses; diff --git a/src/plugins/texteditor/basetextdocumentlayout.cpp b/src/plugins/texteditor/basetextdocumentlayout.cpp index 3f4c3ad541a..57bc1e6dfa4 100644 --- a/src/plugins/texteditor/basetextdocumentlayout.cpp +++ b/src/plugins/texteditor/basetextdocumentlayout.cpp @@ -31,60 +31,6 @@ using namespace TextEditor; -bool Parenthesis::hasClosingCollapse(const Parentheses &parentheses) -{ - return closeCollapseAtPos(parentheses) >= 0; -} - -int Parenthesis::closeCollapseAtPos(const Parentheses &parentheses) -{ - int depth = 0; - for (int i = 0; i < parentheses.size(); ++i) { - const Parenthesis &p = parentheses.at(i); - if (p.chr == QLatin1Char('{') - || p.chr == QLatin1Char('+') - || p.chr == QLatin1Char('[')) { - ++depth; - } else if (p.chr == QLatin1Char('}') - || p.chr == QLatin1Char('-') - || p.chr == QLatin1Char(']')) { - if (--depth < 0) - return p.pos; - } - } - return -1; -} - -int Parenthesis::collapseAtPos(const Parentheses &parentheses, QChar *character) -{ - int result = -1; - QChar c; - - int depth = 0; - for (int i = 0; i < parentheses.size(); ++i) { - const Parenthesis &p = parentheses.at(i); - if (p.chr == QLatin1Char('{') - || p.chr == QLatin1Char('+') - || p.chr == QLatin1Char('[')) { - if (depth == 0) { - result = p.pos; - c = p.chr; - } - ++depth; - } else if (p.chr == QLatin1Char('}') - || p.chr == QLatin1Char('-') - || p.chr == QLatin1Char(']')) { - if (--depth < 0) - depth = 0; - result = -1; - } - } - if (result >= 0 && character) - *character = c; - return result; -} - - TextBlockUserData::~TextBlockUserData() { TextMarks marks = m_marks; @@ -94,11 +40,6 @@ TextBlockUserData::~TextBlockUserData() } } -int TextBlockUserData::collapseAtPos(QChar *character) const -{ - return Parenthesis::collapseAtPos(m_parentheses, character); -} - int TextBlockUserData::braceDepthDelta() const { int delta = 0; @@ -112,96 +53,6 @@ int TextBlockUserData::braceDepthDelta() const return delta; } - -QTextBlock TextBlockUserData::testCollapse(const QTextBlock& block) -{ - QTextBlock info = block; - if (block.userData() && static_cast(block.userData())->collapseMode() == CollapseAfter) - ; - else if (block.next().userData() - && static_cast(block.next().userData())->collapseMode() - == TextBlockUserData::CollapseThis) - info = block.next(); - else - return QTextBlock(); - int pos = static_cast(info.userData())->collapseAtPos(); - if (pos < 0) - return QTextBlock(); - QTextCursor cursor(info); - cursor.setPosition(cursor.position() + pos); - matchCursorForward(&cursor); - return cursor.block(); -} - -void TextBlockUserData::doCollapse(const QTextBlock& block, bool visible) -{ - QTextBlock info = block; - if (block.userData() && static_cast(block.userData())->collapseMode() == CollapseAfter) - ; - else if (block.next().userData() - && static_cast(block.next().userData())->collapseMode() - == TextBlockUserData::CollapseThis) - info = block.next(); - else { - if (visible && !block.next().isVisible()) { - // no match, at least unfold! - QTextBlock b = block.next(); - while (b.isValid() && !b.isVisible()) { - b.setVisible(true); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - b = b.next(); - } - } - return; - } - int pos = static_cast(info.userData())->collapseAtPos(); - if (pos < 0) - return; - QTextCursor cursor(info); - cursor.setPosition(cursor.position() + pos); - if (matchCursorForward(&cursor) != Match) { - if (visible) { - // no match, at least unfold! - QTextBlock b = block.next(); - while (b.isValid() && !b.isVisible()) { - b.setVisible(true); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - b = b.next(); - } - } - return; - } - - QTextBlock b = block.next(); - while (b < cursor.block()) { - b.setVisible(visible); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - if (visible) { - TextBlockUserData *data = canCollapse(b); - if (data && data->collapsed()) { - QTextBlock end = testCollapse(b); - if (data->collapseIncludesClosure()) - end = end.next(); - if (end.isValid()) { - b = end; - continue; - } - } - } - b = b.next(); - } - - bool collapseIncludesClosure = hasClosingCollapseAtEnd(b); - if (collapseIncludesClosure) { - b.setVisible(visible); - b.setLineCount(visible ? qMax(1, b.layout()->lineCount()) : 0); - } - static_cast(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure); - static_cast(info.userData())->setCollapsed(!block.next().isVisible()); - -} - - TextBlockUserData::MatchType TextBlockUserData::checkOpenParenthesis(QTextCursor *cursor, QChar c) { QTextBlock block = cursor->block(); @@ -591,3 +442,76 @@ void BaseTextDocumentLayout::changeBraceDepth(QTextBlock &block, int delta) if (delta) setBraceDepth(block, braceDepth(block) + delta); } + +void BaseTextDocumentLayout::setFoldingIndent(const QTextBlock &block, int indent) +{ + if (indent == 0) { + if (TextBlockUserData *userData = testUserData(block)) + userData->setFoldingIndent(0); + } else { + userData(block)->setFoldingIndent(qMax(0,indent)); + } +} + +int BaseTextDocumentLayout::foldingIndent(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->foldingIndent(); + return 0; +} + +void BaseTextDocumentLayout::changeFoldingIndent(QTextBlock &block, int delta) +{ + if (delta) + setFoldingIndent(block, foldingIndent(block) + delta); +} + +bool BaseTextDocumentLayout::canFold(const QTextBlock &block) +{ + return (block.next().isValid() && foldingIndent(block.next()) > foldingIndent(block)); +} + +bool BaseTextDocumentLayout::isFolded(const QTextBlock &block) +{ + if (TextBlockUserData *userData = testUserData(block)) + return userData->folded(); + return false; +} + +void BaseTextDocumentLayout::setFolded(const QTextBlock &block, bool folded) +{ + if (folded) + userData(block)->setFolded(true); + else { + if (TextBlockUserData *userData = testUserData(block)) + return userData->setFolded(false); + } +} + +void BaseTextDocumentLayout::doFoldOrUnfold(const QTextBlock& block, bool unfold) +{ + if (!canFold(block)) + return; + QTextBlock b = block.next(); + if (b.isVisible() == unfold) + return; + + int indent = foldingIndent(block); + while (b.isValid() && foldingIndent(b) > indent && b.next().isValid()) { + b.setVisible(unfold); + b.setLineCount(unfold? qMax(1, b.layout()->lineCount()) : 0); + if (unfold) { // do not unfold folded sub-blocks + if (isFolded(b) && b.next().isValid()) { + int jndent = foldingIndent(b); + b = b.next(); + while (b.isValid() && foldingIndent(b) > jndent) + b = b.next(); + continue; + } + } + b = b.next(); + } + setFolded(block, !unfold); +} + + diff --git a/src/plugins/texteditor/basetextdocumentlayout.h b/src/plugins/texteditor/basetextdocumentlayout.h index c2ba64a50cd..c76a6283399 100644 --- a/src/plugins/texteditor/basetextdocumentlayout.h +++ b/src/plugins/texteditor/basetextdocumentlayout.h @@ -52,9 +52,6 @@ struct TEXTEDITOR_EXPORT Parenthesis Type type; QChar chr; int pos; - static int collapseAtPos(const Parentheses &parentheses, QChar *character = 0); - static int closeCollapseAtPos(const Parentheses &parentheses); - static bool hasClosingCollapse(const Parentheses &parentheses); }; @@ -62,15 +59,12 @@ class TEXTEDITOR_EXPORT TextBlockUserData : public QTextBlockUserData { public: - enum CollapseMode { NoCollapse , CollapseThis, CollapseAfter }; - enum ClosingCollapseMode { NoClosingCollapse, ClosingCollapse, ClosingCollapseAtEnd }; - inline TextBlockUserData() - : m_collapseIncludesClosure(false), - m_collapseMode(NoCollapse), - m_closingCollapseMode(NoClosingCollapse), - m_collapsed(false), - m_ifdefedOut(false) {} + : m_folded(false), + m_ifdefedOut(false), + m_foldingIndent(0), + m_foldingStartIncluded(false), + m_foldingEndIncluded(false){} ~TextBlockUserData(); inline TextMarks marks() const { return m_marks; } @@ -80,21 +74,8 @@ public: inline void clearMarks() { m_marks.clear(); } inline void documentClosing() { Q_FOREACH(ITextMark *tm, m_marks) { tm->documentClosing(); } m_marks.clear();} - inline CollapseMode collapseMode() const { return (CollapseMode)m_collapseMode; } - inline void setCollapseMode(CollapseMode c) { m_collapseMode = c; } - - inline void setClosingCollapseMode(ClosingCollapseMode c) { m_closingCollapseMode = c; } - inline ClosingCollapseMode closingCollapseMode() const { return (ClosingCollapseMode) m_closingCollapseMode; } - - inline bool hasClosingCollapse() const { return closingCollapseMode() != NoClosingCollapse; } - inline bool hasClosingCollapseAtEnd() const { return closingCollapseMode() == ClosingCollapseAtEnd; } - inline bool hasClosingCollapseInside() const { return closingCollapseMode() == ClosingCollapse; } - - inline void setCollapsed(bool b) { m_collapsed = b; } - inline bool collapsed() const { return m_collapsed; } - - inline void setCollapseIncludesClosure(bool b) { m_collapseIncludesClosure = b; } - inline bool collapseIncludesClosure() const { return m_collapseIncludesClosure; } + inline void setFolded(bool b) { m_folded = b; } + inline bool folded() const { return m_folded; } inline void setParentheses(const Parentheses &parentheses) { m_parentheses = parentheses; } inline void clearParentheses() { m_parentheses.clear(); } @@ -106,49 +87,6 @@ public: inline bool clearIfdefedOut() { bool result = m_ifdefedOut; m_ifdefedOut = false; return result;} inline bool ifdefedOut() const { return m_ifdefedOut; } - inline static TextBlockUserData *canCollapse(const QTextBlock &block) { - TextBlockUserData *data = static_cast(block.userData()); - if (!data || data->collapseMode() != CollapseAfter) { - data = static_cast(block.next().userData()); - if (!data || data->collapseMode() != TextBlockUserData::CollapseThis) - data = 0; - } - if (data && data->m_ifdefedOut) - data = 0; - return data; - } - - inline static bool hasCollapseAfter(const QTextBlock & block) - { - if (!block.isValid()) { - return false; - } else if (block.next().isValid()) { - TextBlockUserData *data = static_cast(block.next().userData()); - if (data && data->collapseMode() == TextBlockUserData::CollapseThis && !data->m_ifdefedOut) - return true; - } - return false; - } - - inline static bool hasClosingCollapse(const QTextBlock &block) { - TextBlockUserData *data = static_cast(block.userData()); - return (data && data->hasClosingCollapse()); - } - - inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) { - TextBlockUserData *data = static_cast(block.userData()); - return (data && data->hasClosingCollapseAtEnd()); - } - - inline static bool hasClosingCollapseInside(const QTextBlock &block) { - TextBlockUserData *data = static_cast(block.userData()); - return (data && data->hasClosingCollapseInside()); - } - - static QTextBlock testCollapse(const QTextBlock& block); - static void doCollapse(const QTextBlock& block, bool visible); - - int collapseAtPos(QChar *character = 0) const; enum MatchType { NoMatch, Match, Mismatch }; static MatchType checkOpenParenthesis(QTextCursor *cursor, QChar c); @@ -161,14 +99,21 @@ public: static bool findPreviousBlockOpenParenthesis(QTextCursor *cursor, bool checkStartPosition = false); static bool findNextBlockClosingParenthesis(QTextCursor *cursor); + int foldingIndent() const { return m_foldingIndent; } + void setFoldingIndent(int indent) { m_foldingIndent = indent; } + void setFoldingStartIncluded(bool included) { m_foldingStartIncluded = included; } + bool foldingStartIncluded() const { return m_foldingStartIncluded; } + void setFoldingEndIncluded(bool included) { m_foldingEndIncluded = included; } + bool foldingEndIncluded() const { return m_foldingEndIncluded; } + private: TextMarks m_marks; - uint m_collapseIncludesClosure : 1; - uint m_collapseMode : 4; - uint m_closingCollapseMode : 4; - uint m_collapsed : 1; + uint m_folded : 1; uint m_ifdefedOut : 1; + uint m_foldingIndent : 16; + uint m_foldingStartIncluded : 1; + uint m_foldingEndIncluded : 1; Parentheses m_parentheses; }; @@ -192,6 +137,13 @@ public: static int braceDepth(const QTextBlock &block); static void setBraceDepth(QTextBlock &block, int depth); static void changeBraceDepth(QTextBlock &block, int delta); + static void setFoldingIndent(const QTextBlock &block, int indent); + static int foldingIndent(const QTextBlock &block); + static void changeFoldingIndent(QTextBlock &block, int delta); + static bool canFold(const QTextBlock &block); + static void doFoldOrUnfold(const QTextBlock& block, bool unfold); + static bool isFolded(const QTextBlock &block); + static void setFolded(const QTextBlock &block, bool folded); static TextBlockUserData *testUserData(const QTextBlock &block) { return static_cast(block.userData()); diff --git a/src/plugins/texteditor/basetexteditor.cpp b/src/plugins/texteditor/basetexteditor.cpp index 6b049a9a3b7..b536c15433a 100644 --- a/src/plugins/texteditor/basetexteditor.cpp +++ b/src/plugins/texteditor/basetexteditor.cpp @@ -202,11 +202,10 @@ BaseTextEditor::BaseTextEditor(QWidget *parent) viewport()->setMouseTracking(true); d->extraAreaSelectionAnchorBlockNumber = d->extraAreaToggleMarkBlockNumber - = d->extraAreaHighlightCollapseBlockNumber - = d->extraAreaHighlightCollapseColumn + = d->extraAreaHighlightFoldedBlockNumber = -1; - d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber = -1; + d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber = -1; connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(slotUpdateExtraAreaWidth())); connect(this, SIGNAL(modificationChanged(bool)), this, SLOT(slotModificationChanged(bool))); @@ -280,7 +279,7 @@ void BaseTextEditor::print(QPrinter *printer) delete dlg; } -static int collapseBoxWidth(const QFontMetrics &fm) +static int foldBoxWidth(const QFontMetrics &fm) { const int lineSpacing = fm.lineSpacing(); return lineSpacing + lineSpacing%2 + 1; @@ -585,7 +584,7 @@ bool BaseTextEditor::open(const QString &fileName) /* Collapses the first comment in a file, if there is only whitespace above */ -void BaseTextEditorPrivate::collapseLicenseHeader() +void BaseTextEditorPrivate::foldLicenseHeader() { QTextDocument *doc = q->document(); BaseTextDocumentLayout *documentLayout = qobject_cast(doc->documentLayout()); @@ -593,19 +592,16 @@ void BaseTextEditorPrivate::collapseLicenseHeader() QTextBlock block = doc->firstBlock(); const TabSettings &ts = m_document->tabSettings(); while (block.isValid() && block.isVisible()) { - TextBlockUserData *data = TextBlockUserData::canCollapse(block); - if (data && block.next().isVisible()) { - QChar character; - static_cast(data)->collapseAtPos(&character); - if (character != QLatin1Char('+')) - break; // not a comment - TextBlockUserData::doCollapse(block, false); - moveCursorVisible(); - documentLayout->requestUpdate(); - documentLayout->emitDocumentSizeChanged(); - break; - } QString text = block.text(); + if (BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) { + if (text.trimmed().startsWith(QLatin1String("/*"))) { + BaseTextDocumentLayout::doFoldOrUnfold(block, false); + moveCursorVisible(); + documentLayout->requestUpdate(); + documentLayout->emitDocumentSizeChanged(); + break; + } + } if (ts.firstNonSpace(text) < text.size()) break; block = block.next(); @@ -1001,7 +997,7 @@ void BaseTextEditor::moveLineUpDown(bool up) QTextCursor cursor = textCursor(); QTextCursor move = cursor; - move.setVisualNavigation(false); // this opens collapsed items instead of destroying them + move.setVisualNavigation(false); // this opens folded items instead of destroying them if (d->m_moveLineUndoHack) move.joinPreviousEditBlock(); @@ -1068,7 +1064,7 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e) QToolTip::hideText(); d->m_moveLineUndoHack = false; - d->clearVisibleCollapsedBlock(); + d->clearVisibleFoldedBlock(); QKeyEvent *original_e = e; d->m_lastEventWasBlockSelectionEvent = false; @@ -1629,19 +1625,16 @@ QByteArray BaseTextEditor::saveState() const stream << column; // store code folding state - QList collapsedBlocks; + QList foldedBlocks; QTextBlock block = document()->firstBlock(); while (block.isValid()) { - if (block.userData() && static_cast(block.userData())->collapsed()) { + if (block.userData() && static_cast(block.userData())->folded()) { int number = block.blockNumber(); - if (static_cast(block.userData())->collapseMode() - == TextBlockUserData::CollapseThis) - number--; - collapsedBlocks += number; + foldedBlocks += number; } block = block.next(); } - stream << collapsedBlocks; + stream << foldedBlocks; return state; } @@ -1650,7 +1643,7 @@ bool BaseTextEditor::restoreState(const QByteArray &state) { if (state.isEmpty()) { if (d->m_displaySettings.m_autoFoldFirstComment) - d->collapseLicenseHeader(); + d->foldLicenseHeader(); return false; } int version; @@ -1672,11 +1665,11 @@ bool BaseTextEditor::restoreState(const QByteArray &state) foreach(int blockNumber, collapsedBlocks) { QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber)); if (block.isValid()) - TextBlockUserData::doCollapse(block, false); + BaseTextDocumentLayout::doFoldOrUnfold(block, false); } } else { if (d->m_displaySettings.m_autoFoldFirstComment) - d->collapseLicenseHeader(); + d->foldLicenseHeader(); } d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history @@ -1850,7 +1843,7 @@ BaseTextEditorPrivate::BaseTextEditorPrivate() m_parenthesesMatchingEnabled(false), m_autoParenthesesEnabled(true), m_extraArea(0), - m_mouseOnCollapsedMarker(false), + m_mouseOnFoldedMarker(false), m_marksVisible(false), m_codeFoldingVisible(false), m_codeFoldingSupported(false), @@ -1999,29 +1992,26 @@ void BaseTextEditor::resizeEvent(QResizeEvent *e) QRect(cr.left(), cr.top(), extraAreaWidth(), cr.height()))); } -QRect BaseTextEditor::collapseBox() +QRect BaseTextEditor::foldBox() { - if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightCollapseBlockNumber < 0) + if (d->m_highlightBlocksInfo.isEmpty() || d->extraAreaHighlightFoldedBlockNumber < 0) return QRect(); QTextBlock begin = document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()); - if (TextBlockUserData::hasCollapseAfter(begin.previous())) - begin = begin.previous(); - QTextBlock end = document()->findBlockByNumber(d->m_highlightBlocksInfo.close.first()); if (!begin.isValid() || !end.isValid()) return QRect(); QRectF br = blockBoundingGeometry(begin).translated(contentOffset()); QRectF er = blockBoundingGeometry(end).translated(contentOffset()); - return QRect(d->m_extraArea->width() - collapseBoxWidth(fontMetrics()), + return QRect(d->m_extraArea->width() - foldBoxWidth(fontMetrics()), int(br.top()), - collapseBoxWidth(fontMetrics()), + foldBoxWidth(fontMetrics()), er.bottom() - br.top()); } -QTextBlock BaseTextEditor::collapsedBlockAt(const QPoint &pos, QRect *box) const +QTextBlock BaseTextEditor::foldedBlockAt(const QPoint &pos, QRect *box) const { QPointF offset(contentOffset()); QTextBlock block = firstVisibleBlock(); @@ -2621,7 +2611,7 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) block = block.next(); if (!block.isVisible()) { - if (block.blockNumber() == d->visibleCollapsedBlockNumber) { + if (block.blockNumber() == d->visibleFoldedBlockNumber) { visibleCollapsedBlock = block; visibleCollapsedBlockOffset = offset + QPointF(0,1); } @@ -2725,34 +2715,29 @@ void BaseTextEditor::paintEvent(QPaintEvent *e) QString replacement = QLatin1String("..."); - QTextBlock info = block; - if (block.userData() - && static_cast(block.userData())->collapseMode() == TextBlockUserData::CollapseAfter) - ; - else if (block.next().userData() - && static_cast(block.next().userData())->collapseMode() - == TextBlockUserData::CollapseThis) { - replacement.prepend(nextBlock.text().trimmed().left(1)); - info = nextBlock; + if (TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock)) { + if (nextBlockUserData->foldingStartIncluded()) + replacement.prepend(nextBlock.text().trimmed().left(1)); } - block = nextVisibleBlock.previous(); if (!block.isValid()) block = doc->lastBlock(); - if (info.userData() - && static_cast(info.userData())->collapseIncludesClosure()) { - QString right = block.text().trimmed(); - if (right.endsWith(QLatin1Char(';'))) { - right.chop(1); - right = right.trimmed(); - replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); - replacement.append(QLatin1Char(';')); - } else { - replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); + if (TextBlockUserData *blockUserData = BaseTextDocumentLayout::testUserData(block)) { + if (blockUserData->foldingEndIncluded()) { + QString right = block.text().trimmed(); + if (right.endsWith(QLatin1Char(';'))) { + right.chop(1); + right = right.trimmed(); + replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); + replacement.append(QLatin1Char(';')); + } else { + replacement.append(right.right(right.endsWith(QLatin1Char('/')) ? 2 : 1)); + } } } + if (selectThis) painter.setPen(palette().highlightedText().color()); painter.drawText(collapseRect, Qt::AlignCenter, replacement); @@ -2888,7 +2873,7 @@ int BaseTextEditor::extraAreaWidth(int *markWidthPtr) const space += 4; if (d->m_codeFoldingVisible) - space += collapseBoxWidth(fm); + space += foldBoxWidth(fm); return space; } @@ -2948,7 +2933,7 @@ void BaseTextEditor::extraAreaPaintEvent(QPaintEvent *e) if (d->m_marksVisible) markWidth += fm.lineSpacing(); - const int collapseColumnWidth = d->m_codeFoldingVisible ? collapseBoxWidth(fm): 0; + const int collapseColumnWidth = d->m_codeFoldingVisible ? foldBoxWidth(fm): 0; const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth; painter.fillRect(e->rect(), pal.color(QPalette::Base)); @@ -3020,50 +3005,35 @@ void BaseTextEditor::extraAreaPaintEvent(QPaintEvent *e) if (d->m_codeFoldingVisible) { - bool collapseThis = false; - bool collapseAfter = false; - bool hasClosingCollapse = false; - - if (TextBlockUserData *userData = static_cast(block.userData())) { - if (!userData->ifdefedOut()) { - collapseAfter = (userData->collapseMode() == TextBlockUserData::CollapseAfter); - collapseThis = (userData->collapseMode() == TextBlockUserData::CollapseThis); - hasClosingCollapse = userData->hasClosingCollapse() && (previousBraceDepth > 0); - } - } - - int extraAreaHighlightCollapseBlockNumber = -1; - int extraAreaHighlightCollapseEndBlockNumber = -1; + int extraAreaHighlightFoldBlockNumber = -1; + int extraAreaHighlightFoldEndBlockNumber = -1; bool endIsVisible = false; if (!d->m_highlightBlocksInfo.isEmpty()) { - extraAreaHighlightCollapseBlockNumber = d->m_highlightBlocksInfo.open.last(); - extraAreaHighlightCollapseEndBlockNumber = d->m_highlightBlocksInfo.close.first(); - endIsVisible = doc->findBlockByNumber(extraAreaHighlightCollapseEndBlockNumber).isVisible(); + extraAreaHighlightFoldBlockNumber = d->m_highlightBlocksInfo.open.last(); + extraAreaHighlightFoldEndBlockNumber = d->m_highlightBlocksInfo.close.first(); + endIsVisible = doc->findBlockByNumber(extraAreaHighlightFoldEndBlockNumber).isVisible(); - QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1); - if (TextBlockUserData::hasCollapseAfter(before)) { - extraAreaHighlightCollapseBlockNumber--; - } +// QTextBlock before = doc->findBlockByNumber(extraAreaHighlightCollapseBlockNumber-1); +// if (TextBlockUserData::hasCollapseAfter(before)) { +// extraAreaHighlightCollapseBlockNumber--; +// } } TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock); - bool collapseNext = nextBlockUserData - && nextBlockUserData->collapseMode() == TextBlockUserData::CollapseThis - && !nextBlockUserData->ifdefedOut(); + bool drawBox = nextBlockUserData + && BaseTextDocumentLayout::foldingIndent(block) < nextBlockUserData->foldingIndent(); - bool nextHasClosingCollapse = nextBlockUserData - && nextBlockUserData->hasClosingCollapseInside() - && nextBlockUserData->ifdefedOut(); - bool drawBox = ((collapseAfter || collapseNext) && !nextHasClosingCollapse); - bool active = blockNumber == extraAreaHighlightCollapseBlockNumber; - bool drawStart = drawBox && active; - bool drawEnd = blockNumber == extraAreaHighlightCollapseEndBlockNumber || (drawStart && !endIsVisible); - bool hovered = blockNumber >= extraAreaHighlightCollapseBlockNumber - && blockNumber <= extraAreaHighlightCollapseEndBlockNumber; - int boxWidth = collapseBoxWidth(fm); + bool active = blockNumber == extraAreaHighlightFoldBlockNumber; + + bool drawStart = active; + bool drawEnd = blockNumber == extraAreaHighlightFoldEndBlockNumber || (drawStart && !endIsVisible); + bool hovered = blockNumber >= extraAreaHighlightFoldBlockNumber + && blockNumber <= extraAreaHighlightFoldEndBlockNumber; + + int boxWidth = foldBoxWidth(fm); if (hovered) { int itop = qRound(top); int ibottom = qRound(bottom); @@ -3264,8 +3234,8 @@ void BaseTextEditor::slotCursorPositionChanged() { #if 0 qDebug() << "block" << textCursor().blockNumber()+1 - << "depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block()) - << '/' << BaseTextDocumentLayout::braceDepth(document()->lastBlock()); + << "brace depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block()) + << "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent(); #endif if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) { Core::EditorManager::instance()->addCurrentPositionToNavigationHistory(editableInterface(), d->m_tempNavigationState); @@ -3294,8 +3264,7 @@ void BaseTextEditor::updateHighlights() if (d->m_displaySettings.m_highlightBlocks) { QTextCursor cursor = textCursor(); - d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber(); - d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position(); + d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); d->m_highlightBlocksTimer->start(100); } } @@ -3315,7 +3284,7 @@ void BaseTextEditor::slotUpdateBlockNotify(const QTextBlock &block) /* The syntax highlighting state changes. This opens up for the possibility that the paragraph has braces that support code folding. In this case, do the save thing and also - update the previous block, which might contain a collapse + update the previous block, which might contain a fold box which now is invalid.*/ emit requestBlockUpdate(block.previous()); } @@ -3350,24 +3319,24 @@ void BaseTextEditor::timerEvent(QTimerEvent *e) int timeout = 4900 / (delta * delta); d->autoScrollTimer.start(timeout, this); - } else if (e->timerId() == d->collapsedBlockTimer.timerId()) { - d->visibleCollapsedBlockNumber = d->suggestedVisibleCollapsedBlockNumber; - d->suggestedVisibleCollapsedBlockNumber = -1; - d->collapsedBlockTimer.stop(); + } else if (e->timerId() == d->foldedBlockTimer.timerId()) { + d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber; + d->suggestedVisibleFoldedBlockNumber = -1; + d->foldedBlockTimer.stop(); viewport()->update(); } QPlainTextEdit::timerEvent(e); } -void BaseTextEditorPrivate::clearVisibleCollapsedBlock() +void BaseTextEditorPrivate::clearVisibleFoldedBlock() { - if (suggestedVisibleCollapsedBlockNumber) { - suggestedVisibleCollapsedBlockNumber = -1; - collapsedBlockTimer.stop(); + if (suggestedVisibleFoldedBlockNumber) { + suggestedVisibleFoldedBlockNumber = -1; + foldedBlockTimer.stop(); } - if (visibleCollapsedBlockNumber >= 0) { - visibleCollapsedBlockNumber = -1; + if (visibleFoldedBlockNumber >= 0) { + visibleFoldedBlockNumber = -1; q->viewport()->update(); } } @@ -3379,21 +3348,21 @@ void BaseTextEditor::mouseMoveEvent(QMouseEvent *e) updateLink(e); if (e->buttons() == Qt::NoButton) { - const QTextBlock collapsedBlock = collapsedBlockAt(e->pos()); + const QTextBlock collapsedBlock = foldedBlockAt(e->pos()); const int blockNumber = collapsedBlock.next().blockNumber(); if (blockNumber < 0) { - d->clearVisibleCollapsedBlock(); - } else if (blockNumber != d->visibleCollapsedBlockNumber) { - d->suggestedVisibleCollapsedBlockNumber = blockNumber; - d->collapsedBlockTimer.start(40, this); + d->clearVisibleFoldedBlock(); + } else if (blockNumber != d->visibleFoldedBlockNumber) { + d->suggestedVisibleFoldedBlockNumber = blockNumber; + d->foldedBlockTimer.start(40, this); } // Update the mouse cursor - if (collapsedBlock.isValid() && !d->m_mouseOnCollapsedMarker) { - d->m_mouseOnCollapsedMarker = true; + if (collapsedBlock.isValid() && !d->m_mouseOnFoldedMarker) { + d->m_mouseOnFoldedMarker = true; viewport()->setCursor(Qt::PointingHandCursor); - } else if (!collapsedBlock.isValid() && d->m_mouseOnCollapsedMarker) { - d->m_mouseOnCollapsedMarker = false; + } else if (!collapsedBlock.isValid() && d->m_mouseOnFoldedMarker) { + d->m_mouseOnFoldedMarker = false; viewport()->setCursor(Qt::IBeamCursor); } } else { @@ -3429,9 +3398,9 @@ void BaseTextEditor::mousePressEvent(QMouseEvent *e) if (e->button() == Qt::LeftButton) { d->clearBlockSelection(); // just in case, otherwise we might get strange drag and drop - QTextBlock collapsedBlock = collapsedBlockAt(e->pos()); - if (collapsedBlock.isValid()) { - toggleBlockVisible(collapsedBlock); + QTextBlock foldedBlock = foldedBlockAt(e->pos()); + if (foldedBlock.isValid()) { + toggleBlockVisible(foldedBlock); viewport()->setCursor(Qt::IBeamCursor); } @@ -3509,7 +3478,6 @@ void BaseTextEditor::extraAreaLeaveEvent(QEvent *) void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e) { QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y())); - cursor.setPosition(cursor.block().position()); int markWidth; extraAreaWidth(&markWidth); @@ -3517,34 +3485,19 @@ void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e) if (d->m_codeFoldingVisible && e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking // Update which folder marker is highlighted - const int highlightBlockNumber = d->extraAreaHighlightCollapseBlockNumber; - const int highlightColumn = d->extraAreaHighlightCollapseColumn; - d->extraAreaHighlightCollapseBlockNumber = -1; - d->extraAreaHighlightCollapseColumn = -1; + const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber; + d->extraAreaHighlightFoldedBlockNumber = -1; - if (e->pos().x() > extraArea()->width() - collapseBoxWidth(fontMetrics())) { - d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber(); - if (TextBlockUserData::canCollapse(cursor.block()) - || !TextBlockUserData::hasClosingCollapse(cursor.block())) - d->extraAreaHighlightCollapseColumn = cursor.block().length()-1; - if (TextBlockUserData::hasCollapseAfter(cursor.block())) { - d->extraAreaHighlightCollapseBlockNumber++; - d->extraAreaHighlightCollapseColumn = -1; - if (TextBlockUserData::canCollapse(cursor.block().next()) - || !TextBlockUserData::hasClosingCollapse(cursor.block().next())) - d->extraAreaHighlightCollapseColumn = cursor.block().next().length()-1; - } + if (e->pos().x() > extraArea()->width() - foldBoxWidth(fontMetrics())) { + d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); } else if (d->m_displaySettings.m_highlightBlocks) { QTextCursor cursor = textCursor(); - d->extraAreaHighlightCollapseBlockNumber = cursor.blockNumber(); - d->extraAreaHighlightCollapseColumn = cursor.position() - cursor.block().position(); + d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber(); } - if (highlightBlockNumber != d->extraAreaHighlightCollapseBlockNumber - || highlightColumn != d->extraAreaHighlightCollapseColumn) { + if (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber) d->m_highlightBlocksTimer->start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0); } - } // Set whether the mouse cursor is a hand or normal arrow if (e->type() == QEvent::MouseMove) { @@ -3555,18 +3508,16 @@ void BaseTextEditor::extraAreaMouseEvent(QMouseEvent *e) if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) { if (e->button() == Qt::LeftButton) { - int boxWidth = collapseBoxWidth(fontMetrics()); + int boxWidth = foldBoxWidth(fontMetrics()); if (d->m_codeFoldingVisible && e->pos().x() > extraArea()->width() - boxWidth) { if (!cursor.block().next().isVisible()) { toggleBlockVisible(cursor.block()); d->moveCursorVisible(false); - } else if (collapseBox().contains(e->pos())) { + } else if (foldBox().contains(e->pos())) { cursor.setPosition( document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position() ); QTextBlock c = cursor.block(); - if (TextBlockUserData::hasCollapseAfter(c.previous())) - c = c.previous(); toggleBlockVisible(c); d->moveCursorVisible(false); } @@ -3643,7 +3594,7 @@ void BaseTextEditor::toggleBlockVisible(const QTextBlock &block) QTC_ASSERT(documentLayout, return); bool visible = block.next().isVisible(); - TextBlockUserData::doCollapse(block, !visible); + BaseTextDocumentLayout::doFoldOrUnfold(block, !visible); documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } @@ -3790,7 +3741,7 @@ void BaseTextEditor::handleBackspaceKey() void BaseTextEditor::wheelEvent(QWheelEvent *e) { - d->clearVisibleCollapsedBlock(); + d->clearVisibleFoldedBlock(); if (scrollWheelZoomingEnabled() && e->modifiers() & Qt::ControlModifier) { const int delta = e->delta(); if (delta < 0) @@ -3804,7 +3755,7 @@ void BaseTextEditor::wheelEvent(QWheelEvent *e) void BaseTextEditor::zoomIn(int range) { - d->clearVisibleCollapsedBlock(); + d->clearVisibleFoldedBlock(); emit requestFontZoom(range*10); } @@ -4457,30 +4408,56 @@ void BaseTextEditor::_q_highlightBlocks() { BaseTextEditorPrivateHighlightBlocks highlightBlocksInfo; - if (d->extraAreaHighlightCollapseBlockNumber >= 0) { - QTextBlock block = document()->findBlockByNumber(d->extraAreaHighlightCollapseBlockNumber); - if (block.isValid()) { - QTextCursor cursor(block); - if (d->extraAreaHighlightCollapseColumn >= 0) - cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn, - block.length()-1)); - QTextCursor closeCursor; - bool firstRun = true; - while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) { - firstRun = false; - highlightBlocksInfo.open.prepend(cursor.blockNumber()); - int visualIndent = d->visualIndent(cursor.block()); - if (closeCursor.isNull()) - closeCursor = cursor; - if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) { - highlightBlocksInfo.close.append(closeCursor.blockNumber()); - visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block())); - } - highlightBlocksInfo.visualIndent.prepend(visualIndent); - } - } + QTextBlock block; + if (d->extraAreaHighlightFoldedBlockNumber >= 0) { + block = document()->findBlockByNumber(d->extraAreaHighlightFoldedBlockNumber); + if (block.isValid() + && block.next().isValid() + && BaseTextDocumentLayout::foldingIndent(block.next()) + > BaseTextDocumentLayout::foldingIndent(block)) + block = block.next(); } + QTextBlock closeBlock = block; + while (block.isValid()) { + int foldingIndent = BaseTextDocumentLayout::foldingIndent(block); + + while (block.previous().isValid() && BaseTextDocumentLayout::foldingIndent(block) >= foldingIndent) + block = block.previous(); + int nextIndent = BaseTextDocumentLayout::foldingIndent(block); + if (nextIndent == foldingIndent) + break; + highlightBlocksInfo.open.prepend(block.blockNumber()); + while (closeBlock.next().isValid() + && BaseTextDocumentLayout::foldingIndent(closeBlock.next()) >= foldingIndent ) + closeBlock = closeBlock.next(); + highlightBlocksInfo.close.append(closeBlock.blockNumber()); + int visualIndent = qMin(d->visualIndent(block), d->visualIndent(closeBlock)); + highlightBlocksInfo.visualIndent.prepend(visualIndent); + } + +#if 0 + if (block.isValid()) { + QTextCursor cursor(block); + if (d->extraAreaHighlightCollapseColumn >= 0) + cursor.setPosition(cursor.position() + qMin(d->extraAreaHighlightCollapseColumn, + block.length()-1)); + QTextCursor closeCursor; + bool firstRun = true; + while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) { + firstRun = false; + highlightBlocksInfo.open.prepend(cursor.blockNumber()); + int visualIndent = d->visualIndent(cursor.block()); + if (closeCursor.isNull()) + closeCursor = cursor; + if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) { + highlightBlocksInfo.close.append(closeCursor.blockNumber()); + visualIndent = qMin(visualIndent, d->visualIndent(closeCursor.block())); + } + highlightBlocksInfo.visualIndent.prepend(visualIndent); + } + } +#endif if (d->m_highlightBlocksInfo != highlightBlocksInfo) { d->m_highlightBlocksInfo = highlightBlocksInfo; viewport()->update(); @@ -4651,8 +4628,10 @@ void BaseTextEditor::setIfdefedOutBlocks(const QList braceDepthDelta -= delta; } - if (braceDepthDelta) + if (braceDepthDelta) { BaseTextDocumentLayout::changeBraceDepth(block,braceDepthDelta); + BaseTextDocumentLayout::changeFoldingIndent(block, braceDepthDelta); // ### C++ only, refactor! + } block = block.next(); } @@ -4903,7 +4882,7 @@ void BaseTextEditor::setDisplaySettings(const DisplaySettings &ds) d->m_displaySettings = ds; if (!ds.m_highlightBlocks) { - d->extraAreaHighlightCollapseBlockNumber = d->extraAreaHighlightCollapseColumn = -1; + d->extraAreaHighlightFoldedBlockNumber = -1; d->m_highlightBlocksInfo = BaseTextEditorPrivateHighlightBlocks(); } @@ -4928,31 +4907,27 @@ void BaseTextEditor::setCompletionSettings(const TextEditor::CompletionSettings setAutoParenthesesEnabled(completionSettings.m_autoInsertBrackets); } -void BaseTextEditor::collapse() +void BaseTextEditor::fold() { QTextDocument *doc = document(); BaseTextDocumentLayout *documentLayout = qobject_cast(doc->documentLayout()); QTC_ASSERT(documentLayout, return); QTextBlock block = textCursor().block(); - QTextBlock curBlock = block; - while (block.isValid()) { - if (TextBlockUserData::canCollapse(block) && block.next().isVisible()) { - if (block == curBlock || block.next() == curBlock) - break; - if ((block.next().userState()) >> 8 <= (curBlock.previous().userState() >> 8)) - break; - } - block = block.previous(); + if (!(BaseTextDocumentLayout::canFold(block) && block.next().isVisible())) { + // find the closest previous block which can fold + int indent = BaseTextDocumentLayout::foldingIndent(block); + while (block.isValid() && (BaseTextDocumentLayout::foldingIndent(block) >= indent || !block.isVisible())) + block = block.previous(); } if (block.isValid()) { - TextBlockUserData::doCollapse(block, false); + BaseTextDocumentLayout::doFoldOrUnfold(block, false); d->moveCursorVisible(); documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } } -void BaseTextEditor::expand() +void BaseTextEditor::unfold() { QTextDocument *doc = document(); BaseTextDocumentLayout *documentLayout = qobject_cast(doc->documentLayout()); @@ -4960,13 +4935,13 @@ void BaseTextEditor::expand() QTextBlock block = textCursor().block(); while (block.isValid() && !block.isVisible()) block = block.previous(); - TextBlockUserData::doCollapse(block, true); + BaseTextDocumentLayout::doFoldOrUnfold(block, true); d->moveCursorVisible(); documentLayout->requestUpdate(); documentLayout->emitDocumentSizeChanged(); } -void BaseTextEditor::unCollapseAll() +void BaseTextEditor::unfoldAll() { QTextDocument *doc = document(); BaseTextDocumentLayout *documentLayout = qobject_cast(doc->documentLayout()); @@ -4975,7 +4950,7 @@ void BaseTextEditor::unCollapseAll() QTextBlock block = doc->firstBlock(); bool makeVisible = true; while (block.isValid()) { - if (block.isVisible() && TextBlockUserData::canCollapse(block) && block.next().isVisible()) { + if (block.isVisible() && BaseTextDocumentLayout::canFold(block) && block.next().isVisible()) { makeVisible = false; break; } @@ -4985,8 +4960,8 @@ void BaseTextEditor::unCollapseAll() block = doc->firstBlock(); while (block.isValid()) { - if (TextBlockUserData::canCollapse(block)) - TextBlockUserData::doCollapse(block, makeVisible); + if (BaseTextDocumentLayout::canFold(block)) + BaseTextDocumentLayout::doFoldOrUnfold(block, makeVisible); block = block.next(); } diff --git a/src/plugins/texteditor/basetexteditor.h b/src/plugins/texteditor/basetexteditor.h index 967543a6194..d13a7aea67e 100644 --- a/src/plugins/texteditor/basetexteditor.h +++ b/src/plugins/texteditor/basetexteditor.h @@ -224,9 +224,9 @@ public slots: void cutLine(); void deleteLine(); - void unCollapseAll(); - void collapse(); - void expand(); + void unfoldAll(); + void fold(); + void unfold(); void selectEncoding(); void gotoBlockStart(); @@ -491,9 +491,9 @@ private: bool hovered) const; void toggleBlockVisible(const QTextBlock &block); - QRect collapseBox(); + QRect foldBox(); - QTextBlock collapsedBlockAt(const QPoint &pos, QRect *box = 0) const; + QTextBlock foldedBlockAt(const QPoint &pos, QRect *box = 0) const; void updateLink(QMouseEvent *e); void showLink(const Link &); diff --git a/src/plugins/texteditor/basetexteditor_p.h b/src/plugins/texteditor/basetexteditor_p.h index 784d95fd9e1..9cb7788186f 100644 --- a/src/plugins/texteditor/basetexteditor_p.h +++ b/src/plugins/texteditor/basetexteditor_p.h @@ -189,8 +189,7 @@ public: int extraAreaSelectionAnchorBlockNumber; int extraAreaToggleMarkBlockNumber; - int extraAreaHighlightCollapseBlockNumber; - int extraAreaHighlightCollapseColumn; + int extraAreaHighlightFoldedBlockNumber; TextEditorOverlay *m_overlay; TextEditorOverlay *m_snippetOverlay; @@ -200,12 +199,12 @@ public: QTextCharFormat m_occurrencesFormat; QTextCharFormat m_occurrenceRenameFormat; - QBasicTimer collapsedBlockTimer; - int visibleCollapsedBlockNumber; - int suggestedVisibleCollapsedBlockNumber; - void clearVisibleCollapsedBlock(); - bool m_mouseOnCollapsedMarker; - void collapseLicenseHeader(); + QBasicTimer foldedBlockTimer; + int visibleFoldedBlockNumber; + int suggestedVisibleFoldedBlockNumber; + void clearVisibleFoldedBlock(); + bool m_mouseOnFoldedMarker; + void foldLicenseHeader(); QBasicTimer autoScrollTimer; void updateMarksLineNumber(); diff --git a/src/plugins/texteditor/texteditoractionhandler.cpp b/src/plugins/texteditor/texteditoractionhandler.cpp index aa5e9dedf36..604319149f2 100644 --- a/src/plugins/texteditor/texteditoractionhandler.cpp +++ b/src/plugins/texteditor/texteditoractionhandler.cpp @@ -69,9 +69,9 @@ TextEditorActionHandler::TextEditorActionHandler(const QString &context, m_cleanWhitespaceAction(0), m_textWrappingAction(0), m_unCommentSelectionAction(0), - m_unCollapseAllAction(0), - m_collapseAction(0), - m_expandAction(0), + m_unfoldAllAction(0), + m_foldAction(0), + m_unfoldAction(0), m_cutLineAction(0), m_deleteLineAction(0), m_selectEncodingAction(0), @@ -198,21 +198,21 @@ void TextEditorActionHandler::createActions() command = am->registerAction(m_deleteLineAction, Constants::DELETE_LINE, m_contextId); connect(m_deleteLineAction, SIGNAL(triggered()), this, SLOT(deleteLine())); - m_collapseAction = new QAction(tr("Collapse"), this); - command = am->registerAction(m_collapseAction, Constants::COLLAPSE, m_contextId); + m_foldAction = new QAction(tr("Fold"), this); + command = am->registerAction(m_foldAction, Constants::FOLD, m_contextId); command->setDefaultKeySequence(QKeySequence(tr("Ctrl+<"))); - connect(m_collapseAction, SIGNAL(triggered()), this, SLOT(collapse())); + connect(m_foldAction, SIGNAL(triggered()), this, SLOT(fold())); advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING); - m_expandAction = new QAction(tr("Expand"), this); - command = am->registerAction(m_expandAction, Constants::EXPAND, m_contextId); + m_unfoldAction = new QAction(tr("Unfold"), this); + command = am->registerAction(m_unfoldAction, Constants::UNFOLD, m_contextId); command->setDefaultKeySequence(QKeySequence(tr("Ctrl+>"))); - connect(m_expandAction, SIGNAL(triggered()), this, SLOT(expand())); + connect(m_unfoldAction, SIGNAL(triggered()), this, SLOT(unfold())); advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING); - m_unCollapseAllAction = new QAction(tr("(Un)&Collapse All"), this); - command = am->registerAction(m_unCollapseAllAction, Constants::UN_COLLAPSE_ALL, m_contextId); - connect(m_unCollapseAllAction, SIGNAL(triggered()), this, SLOT(unCollapseAll())); + m_unfoldAllAction = new QAction(tr("(Un)&Collapse All"), this); + command = am->registerAction(m_unfoldAllAction, Constants::UNFOLD_ALL, m_contextId); + connect(m_unfoldAllAction, SIGNAL(triggered()), this, SLOT(unfoldAll())); advancedMenu->addAction(command, Core::Constants::G_EDIT_COLLAPSING); m_increaseFontSizeAction = new QAction(tr("Increase Font Size"), this); @@ -406,7 +406,7 @@ void TextEditorActionHandler::updateActions(UpdateMode um) m_moveLineDownAction->setEnabled(um != ReadOnlyMode); m_formatAction->setEnabled((m_optionalActions & Format)); - m_unCollapseAllAction->setEnabled((m_optionalActions & UnCollapseAll)); + m_unfoldAllAction->setEnabled((m_optionalActions & UnCollapseAll)); m_visualizeWhitespaceAction->setChecked(m_currentEditor->displaySettings().m_visualizeWhitespace); if (m_textWrappingAction) { m_textWrappingAction->setChecked(m_currentEditor->displaySettings().m_textWrapping); @@ -498,9 +498,9 @@ FUNCTION(cleanWhitespace) FUNCTION(unCommentSelection) FUNCTION(cutLine) FUNCTION(deleteLine) -FUNCTION(unCollapseAll) -FUNCTION(collapse) -FUNCTION(expand) +FUNCTION(unfoldAll) +FUNCTION(fold) +FUNCTION(unfold) FUNCTION2(increaseFontSize, zoomIn) FUNCTION2(decreaseFontSize, zoomOut) FUNCTION2(resetFontSize, zoomReset) diff --git a/src/plugins/texteditor/texteditoractionhandler.h b/src/plugins/texteditor/texteditoractionhandler.h index 25f2e9febb3..331c788d31c 100644 --- a/src/plugins/texteditor/texteditoractionhandler.h +++ b/src/plugins/texteditor/texteditoractionhandler.h @@ -96,9 +96,9 @@ private slots: void cleanWhitespace(); void setTextWrapping(bool); void unCommentSelection(); - void unCollapseAll(); - void collapse(); - void expand(); + void unfoldAll(); + void fold(); + void unfold(); void cutLine(); void deleteLine(); void selectEncoding(); @@ -153,9 +153,9 @@ private: QAction *m_cleanWhitespaceAction; QAction *m_textWrappingAction; QAction *m_unCommentSelectionAction; - QAction *m_unCollapseAllAction; - QAction *m_collapseAction; - QAction *m_expandAction; + QAction *m_unfoldAllAction; + QAction *m_foldAction; + QAction *m_unfoldAction; QAction *m_cutLineAction; QAction *m_deleteLineAction; QAction *m_selectEncodingAction; diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index 4a474b6bdbb..d4e46500a94 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -43,9 +43,9 @@ const char * const CLEAN_WHITESPACE = "TextEditor.CleanWhitespace"; const char * const TEXT_WRAPPING = "TextEditor.TextWrapping"; const char * const UN_COMMENT_SELECTION = "TextEditor.UnCommentSelection"; const char * const REFORMAT = "TextEditor.Reformat"; -const char * const COLLAPSE = "TextEditor.Collapse"; -const char * const EXPAND = "TextEditor.Expand"; -const char * const UN_COLLAPSE_ALL = "TextEditor.UnCollapseAll"; +const char * const FOLD = "TextEditor.Fold"; +const char * const UNFOLD = "TextEditor.Unfold"; +const char * const UNFOLD_ALL = "TextEditor.UnCollapseAll"; const char * const AUTO_INDENT_SELECTION = "TextEditor.AutoIndentSelection"; const char * const INCREASE_FONT_SIZE = "TextEditor.IncreaseFontSize"; const char * const DECREASE_FONT_SIZE = "TextEditor.DecreaseFontSize";