Rework code folding

The new and cleaner foldingIndent in the block user data will
make it easier to support other kinds of indentation for various
other programming languages (like Python).
This commit is contained in:
mae
2010-05-20 15:10:26 +02:00
parent 55b26868e6
commit e07c34017e
11 changed files with 377 additions and 532 deletions

View File

@@ -65,15 +65,19 @@ void CppHighlighter::highlightBlock(const QString &text)
const QList<SimpleToken> 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();
}
}

View File

@@ -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<TextEditorBlockData *>(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));
}

View File

@@ -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;

View File

@@ -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<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
== TextBlockUserData::CollapseThis)
info = block.next();
else
return QTextBlock();
int pos = static_cast<TextBlockUserData*>(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<TextBlockUserData*>(block.userData())->collapseMode() == CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(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<TextBlockUserData*>(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<TextBlockUserData*>(info.userData())->setCollapseIncludesClosure(collapseIncludesClosure);
static_cast<TextBlockUserData*>(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);
}

View File

@@ -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<TextBlockUserData*>(block.userData());
if (!data || data->collapseMode() != CollapseAfter) {
data = static_cast<TextBlockUserData*>(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<TextBlockUserData*>(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<TextBlockUserData*>(block.userData());
return (data && data->hasClosingCollapse());
}
inline static bool hasClosingCollapseAtEnd(const QTextBlock &block) {
TextBlockUserData *data = static_cast<TextBlockUserData*>(block.userData());
return (data && data->hasClosingCollapseAtEnd());
}
inline static bool hasClosingCollapseInside(const QTextBlock &block) {
TextBlockUserData *data = static_cast<TextBlockUserData*>(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<TextBlockUserData*>(block.userData());

View File

@@ -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<BaseTextDocumentLayout*>(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<TextBlockUserData*>(data)->collapseAtPos(&character);
if (character != QLatin1Char('+'))
break; // not a comment
TextBlockUserData::doCollapse(block, false);
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;
}
QString text = block.text();
}
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<int> collapsedBlocks;
QList<int> foldedBlocks;
QTextBlock block = document()->firstBlock();
while (block.isValid()) {
if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->collapsed()) {
if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->folded()) {
int number = block.blockNumber();
if (static_cast<TextBlockUserData*>(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,24 +2715,17 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
QString replacement = QLatin1String("...");
QTextBlock info = block;
if (block.userData()
&& static_cast<TextBlockUserData*>(block.userData())->collapseMode() == TextBlockUserData::CollapseAfter)
;
else if (block.next().userData()
&& static_cast<TextBlockUserData*>(block.next().userData())->collapseMode()
== TextBlockUserData::CollapseThis) {
if (TextBlockUserData *nextBlockUserData = BaseTextDocumentLayout::testUserData(nextBlock)) {
if (nextBlockUserData->foldingStartIncluded())
replacement.prepend(nextBlock.text().trimmed().left(1));
info = nextBlock;
}
block = nextVisibleBlock.previous();
if (!block.isValid())
block = doc->lastBlock();
if (info.userData()
&& static_cast<TextBlockUserData*>(info.userData())->collapseIncludesClosure()) {
if (TextBlockUserData *blockUserData = BaseTextDocumentLayout::testUserData(block)) {
if (blockUserData->foldingEndIncluded()) {
QString right = block.text().trimmed();
if (right.endsWith(QLatin1Char(';'))) {
right.chop(1);
@@ -2753,6 +2736,8 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
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<TextBlockUserData*>(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,8 +4408,35 @@ void BaseTextEditor::_q_highlightBlocks()
{
BaseTextEditorPrivateHighlightBlocks highlightBlocksInfo;
if (d->extraAreaHighlightCollapseBlockNumber >= 0) {
QTextBlock block = document()->findBlockByNumber(d->extraAreaHighlightCollapseBlockNumber);
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)
@@ -4479,8 +4457,7 @@ void BaseTextEditor::_q_highlightBlocks()
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<BaseTextEditor::BlockRange>
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<BaseTextDocumentLayout*>(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;
}
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<BaseTextDocumentLayout*>(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<BaseTextDocumentLayout*>(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();
}

View File

@@ -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 &);

View File

@@ -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();

View File

@@ -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)

View File

@@ -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;

View File

@@ -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";