diff --git a/src/plugins/texteditor/generichighlighter/highlighter.cpp b/src/plugins/texteditor/generichighlighter/highlighter.cpp
index fc2fa35bd6ca6ea35b89e29d2598a61273c6402a..aba84b8c4f80c91b3ee0f7c17fd658a9e6ee769e 100644
--- a/src/plugins/texteditor/generichighlighter/highlighter.cpp
+++ b/src/plugins/texteditor/generichighlighter/highlighter.cpp
@@ -53,7 +53,8 @@ const Highlighter::KateFormatMap Highlighter::m_kateFormats;
 
 Highlighter::Highlighter(QTextDocument *parent) :
     QSyntaxHighlighter(parent),
-    m_persistentStatesCounter(PersistentsStart),
+    m_regionDepth(0),
+    m_persistentObservableStatesCounter(PersistentsStart),
     m_dynamicContextsCounter(0),
     m_isBroken(false)
 {}
@@ -61,7 +62,7 @@ Highlighter::Highlighter(QTextDocument *parent) :
 Highlighter::~Highlighter()
 {}
 
-Highlighter::BlockData::BlockData()
+Highlighter::BlockData::BlockData() : m_foldingIndentDelta(0)
 {}
 
 Highlighter::BlockData::~BlockData()
@@ -93,13 +94,15 @@ void Highlighter::configureFormat(TextFormatId id, const QTextCharFormat &format
 void  Highlighter::setDefaultContext(const QSharedPointer<Context> &defaultContext)
 {
     m_defaultContext = defaultContext;
-    m_persistentStates.insert(m_defaultContext->name(), Default);
+    m_persistentObservableStates.insert(m_defaultContext->name(), Default);
 }
 
 void Highlighter::highlightBlock(const QString &text)
 {
     if (!m_defaultContext.isNull() && !m_isBroken) {
         try {
+            if (!currentBlockUserData())
+                initializeBlockData();
             setupDataForBlock(text);
 
             handleContextChange(m_currentContext->lineBeginContext(),
@@ -107,20 +110,19 @@ void Highlighter::highlightBlock(const QString &text)
 
             ProgressData progress;
             const int length = text.length();
-            while (progress.offset() < length) {
-                if (progress.offset() > 0 &&
-                    progress.onlySpacesSoFar() &&
-                    !text.at(progress.offset()).isSpace()) {
-                    progress.setOnlySpacesSoFar(false);
-                }
-
+            while (progress.offset() < length)
                 iterateThroughRules(text, length, &progress, false, m_currentContext->rules());
-            }
 
             handleContextChange(m_currentContext->lineEndContext(),
                                 m_currentContext->definition(),
                                 false);
             m_contexts.clear();
+
+            applyFolding();
+
+            // Takes into the account any change that might have affected the region depth since
+            // the last time the state was set.
+            setCurrentBlockState(computeState(extractObservableState(currentBlockState())));
         } catch (const HighlighterException &) {
             m_isBroken = true;
         }
@@ -131,60 +133,70 @@ void Highlighter::highlightBlock(const QString &text)
 
 void Highlighter::setupDataForBlock(const QString &text)
 {
-    if (currentBlockState() == WillContinue)
+    if (extractObservableState(currentBlockState()) == WillContinue)
         analyseConsistencyOfWillContinueBlock(text);
 
-    if (previousBlockState() == Default || previousBlockState() == -1)
+    if (previousBlockState() == -1) {
+        m_regionDepth = 0;
         setupDefault();
-    else if (previousBlockState() == WillContinue)
-        setupFromWillContinue();
-    else if (previousBlockState() == Continued)
-        setupFromContinued();
-    else
-        setupFromPersistent();
-
-    setCurrentContext();
+    } else {
+        m_regionDepth = extractRegionDepth(previousBlockState());
+        const int observablePreviousState = extractObservableState(previousBlockState());
+        if (observablePreviousState == Default)
+            setupDefault();
+        else if (observablePreviousState == WillContinue)
+            setupFromWillContinue();
+        else if (observablePreviousState == Continued)
+            setupFromContinued();
+        else
+            setupFromPersistent();
+
+        blockData(currentBlockUserData())->m_foldingRegions =
+            blockData(currentBlock().previous().userData())->m_foldingRegions;
+    }
+
+    assignCurrentContext();
 }
 
 void Highlighter::setupDefault()
 {
     m_contexts.push_back(m_defaultContext);
 
-    setCurrentBlockState(Default);
+    setCurrentBlockState(computeState(Default));
 }
 
 void Highlighter::setupFromWillContinue()
 {
-    BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
+    BlockData *previousData = blockData(currentBlock().previous().userData());
     m_contexts.push_back(previousData->m_contextToContinue);
 
-    if (!currentBlockUserData()) {
-        BlockData *data = initializeBlockData();
-        data->m_originalState = previousData->m_originalState;
-    }
+    BlockData *data = blockData(currentBlock().userData());
+    data->m_originalObservableState = previousData->m_originalObservableState;
 
-    if (currentBlockState() == Default || currentBlockState() == -1)
-        setCurrentBlockState(Continued);
+    if (currentBlockState() == -1 || extractObservableState(currentBlockState()) == Default)
+        setCurrentBlockState(computeState(Continued));
 }
 
 void Highlighter::setupFromContinued()
 {
-    BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
+    BlockData *previousData = blockData(currentBlock().previous().userData());
 
-    Q_ASSERT(previousData->m_originalState != WillContinue &&
-             previousData->m_originalState != Continued);
+    Q_ASSERT(previousData->m_originalObservableState != WillContinue &&
+             previousData->m_originalObservableState != Continued);
 
-    if (previousData->m_originalState == Default || previousData->m_originalState == -1)
+    if (previousData->m_originalObservableState == Default ||
+        previousData->m_originalObservableState == -1) {
         m_contexts.push_back(m_defaultContext);
-    else
-        pushContextSequence(previousData->m_originalState);
+    } else {
+        pushContextSequence(previousData->m_originalObservableState);
+    }
 
-    setCurrentBlockState(previousData->m_originalState);
+    setCurrentBlockState(computeState(previousData->m_originalObservableState));
 }
 
 void Highlighter::setupFromPersistent()
 {
-    pushContextSequence(previousBlockState());
+    pushContextSequence(extractObservableState(previousBlockState()));
 
     setCurrentBlockState(previousBlockState());
 }
@@ -204,12 +216,30 @@ void Highlighter::iterateThroughRules(const QString &text,
     RuleIterator endIt = rules.end();
     while (it != endIt && progress->offset() < length) {
         int startOffset = progress->offset();
-
         const QSharedPointer<Rule> &rule = *it;
         if (rule->matchSucceed(text, length, progress)) {
             atLeastOneMatch = true;
 
-            if (progress->willContinueLine()) {
+            // Code folding.
+            if (!rule->beginRegion().isEmpty()) {
+                blockData(currentBlockUserData())->m_foldingRegions.push(rule->beginRegion());
+                ++m_regionDepth;
+                if (progress->isOpeningBraceMatchAtFirstNonSpace())
+                    ++blockData(currentBlockUserData())->m_foldingIndentDelta;
+            }
+            if (!rule->endRegion().isEmpty()) {
+                QStack<QString> *currentRegions =
+                    &blockData(currentBlockUserData())->m_foldingRegions;
+                if (!currentRegions->isEmpty() && rule->endRegion() == currentRegions->top()) {
+                    currentRegions->pop();
+                    --m_regionDepth;
+                    if (progress->isClosingBraceMatchAtNonEnd())
+                        --blockData(currentBlockUserData())->m_foldingIndentDelta;
+                }
+            }
+            progress->clearBracesMatches();
+
+            if (progress->isWillContinueLine()) {
                 createWillContinueBlock();
                 progress->setWillContinueLine(false);
             } else {
@@ -254,6 +284,8 @@ void Highlighter::iterateThroughRules(const QString &text,
         } else {
             applyFormat(progress->offset(), 1, m_currentContext->itemData(),
                         m_currentContext->definition());
+            if (progress->isOnlySpacesSoFar() && !text.at(progress->offset()).isSpace())
+                progress->setOnlySpacesSoFar(false);
             progress->incrementOffset();
         }
     }
@@ -268,20 +300,22 @@ bool Highlighter::contextChangeRequired(const QString &contextName) const
 
 void Highlighter::changeContext(const QString &contextName,
                                 const QSharedPointer<HighlightDefinition> &definition,
-                                const bool setCurrent)
+                                const bool assignCurrent)
 {
     if (contextName.startsWith(kPop)) {
         QStringList list = contextName.split(kHash, QString::SkipEmptyParts);
         for (int i = 0; i < list.size(); ++i)
             m_contexts.pop_back();
 
-        if (currentBlockState() >= PersistentsStart) {
+        if (extractObservableState(currentBlockState()) >= PersistentsStart) {
             // One or more contexts were popped during during a persistent state.
             const QString &currentSequence = currentContextSequence();
-            if (m_persistentStates.contains(currentSequence))
-                setCurrentBlockState(m_persistentStates.value(currentSequence));
+            if (m_persistentObservableStates.contains(currentSequence))
+                setCurrentBlockState(
+                    computeState(m_persistentObservableStates.value(currentSequence)));
             else
-                setCurrentBlockState(m_leadingStates.value(currentSequence));
+                setCurrentBlockState(
+                    computeState(m_leadingObservableStates.value(currentSequence)));
         }
     } else {
         const QSharedPointer<Context> &context = definition->context(contextName);
@@ -292,19 +326,20 @@ void Highlighter::changeContext(const QString &contextName,
             m_contexts.push_back(context);
 
         if (m_contexts.back()->lineEndContext() == kStay ||
-            currentBlockState() >= PersistentsStart) {
+            extractObservableState(currentBlockState()) >= PersistentsStart) {
             const QString &currentSequence = currentContextSequence();
             mapLeadingSequence(currentSequence);
             if (m_contexts.back()->lineEndContext() == kStay) {
                 // A persistent context was pushed.
                 mapPersistentSequence(currentSequence);
-                setCurrentBlockState(m_persistentStates.value(currentSequence));
+                setCurrentBlockState(
+                    computeState(m_persistentObservableStates.value(currentSequence)));
             }
         }
     }
 
-    if (setCurrent)
-        setCurrentContext();
+    if (assignCurrent)
+        assignCurrentContext();
 }
 
 void Highlighter::handleContextChange(const QString &contextName,
@@ -378,50 +413,49 @@ void Highlighter::applyVisualWhitespaceFormat(const QString &text)
 
 void Highlighter::createWillContinueBlock()
 {
-    if (!currentBlockUserData())
-        initializeBlockData();
-
-    BlockData *data = static_cast<BlockData *>(currentBlockUserData());
-    if (currentBlockState() == Continued) {
-        BlockData *previousData = static_cast<BlockData *>(currentBlock().previous().userData());
-        data->m_originalState = previousData->m_originalState;
-    } else if (currentBlockState() != WillContinue) {
-        data->m_originalState = currentBlockState();
+    BlockData *data = blockData(currentBlockUserData());
+    const int currentObservableState = extractObservableState(currentBlockState());
+    if (currentObservableState == Continued) {
+        BlockData *previousData = blockData(currentBlock().previous().userData());
+        data->m_originalObservableState = previousData->m_originalObservableState;
+    } else if (currentObservableState != WillContinue) {
+        data->m_originalObservableState = currentObservableState;
     }
     data->m_contextToContinue = m_currentContext;
 
-    setCurrentBlockState(WillContinue);
+    setCurrentBlockState(computeState(WillContinue));
 }
 
 void Highlighter::analyseConsistencyOfWillContinueBlock(const QString &text)
 {
     if (currentBlock().next().isValid() && (
         text.length() == 0 || text.at(text.length() - 1) != kBackSlash) &&
-        currentBlock().next().userState() != Continued) {
-        currentBlock().next().setUserState(Continued);
+        extractObservableState(currentBlock().next().userState()) != Continued) {
+        currentBlock().next().setUserState(computeState(Continued));
     }
 
     if (text.length() == 0 || text.at(text.length() - 1) != kBackSlash) {
-        BlockData *data = static_cast<BlockData *>(currentBlockUserData());
+        BlockData *data = blockData(currentBlockUserData());
         data->m_contextToContinue.clear();
-        setCurrentBlockState(data->m_originalState);
+        setCurrentBlockState(computeState(data->m_originalObservableState));
     }
 }
 
 void Highlighter::mapPersistentSequence(const QString &contextSequence)
 {
-    if (!m_persistentStates.contains(contextSequence)) {
-        int newState = m_persistentStatesCounter;
-        m_persistentStates.insert(contextSequence, newState);
+    if (!m_persistentObservableStates.contains(contextSequence)) {
+        int newState = m_persistentObservableStatesCounter;
+        m_persistentObservableStates.insert(contextSequence, newState);
         m_persistentContexts.insert(newState, m_contexts);
-        ++m_persistentStatesCounter;
+        ++m_persistentObservableStatesCounter;
     }
 }
 
 void Highlighter::mapLeadingSequence(const QString &contextSequence)
 {
-    if (!m_leadingStates.contains(contextSequence))
-        m_leadingStates.insert(contextSequence, currentBlockState());
+    if (!m_leadingObservableStates.contains(contextSequence))
+        m_leadingObservableStates.insert(contextSequence,
+                                         extractObservableState(currentBlockState()));
 }
 
 void Highlighter::pushContextSequence(int state)
@@ -447,6 +481,11 @@ Highlighter::BlockData *Highlighter::initializeBlockData()
     return data;
 }
 
+Highlighter::BlockData *Highlighter::blockData(QTextBlockUserData *userData)
+{
+    return static_cast<BlockData *>(userData);
+}
+
 void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
 {
     // A dynamic context is created from another context which serves as its basis. Then,
@@ -459,7 +498,7 @@ void Highlighter::pushDynamicContext(const QSharedPointer<Context> &baseContext)
     ++m_dynamicContextsCounter;
 }
 
-void Highlighter::setCurrentContext()
+void Highlighter::assignCurrentContext()
 {
     if (m_contexts.isEmpty()) {
         // This is not supposed to happen. However, there are broken files (for example, php.xml)
@@ -469,3 +508,38 @@ void Highlighter::setCurrentContext()
     }
     m_currentContext = m_contexts.back();
 }
+
+int Highlighter::extractRegionDepth(const int state)
+{
+    return state >> 12;
+}
+
+int Highlighter::extractObservableState(const int state)
+{
+    return state & 0xFFF;
+}
+
+int Highlighter::computeState(const int observableState) const
+{
+    return m_regionDepth << 12 | observableState;
+}
+
+void Highlighter::applyFolding() const
+{
+    int folding = 0;
+    BlockData *data = blockData(currentBlockUserData());
+    BlockData *previousData = blockData(currentBlock().previous().userData());
+    if (previousData) {
+        folding = extractRegionDepth(previousBlockState());
+        if (data->m_foldingIndentDelta != 0) {
+            folding += data->m_foldingIndentDelta;
+            if (data->m_foldingIndentDelta > 0)
+                data->setFoldingStartIncluded(true);
+            else
+                previousData->setFoldingEndIncluded(false);
+            data->m_foldingIndentDelta = 0;
+        }
+    }
+    data->setFoldingEndIncluded(true);
+    data->setFoldingIndent(folding);
+}
diff --git a/src/plugins/texteditor/generichighlighter/highlighter.h b/src/plugins/texteditor/generichighlighter/highlighter.h
index 2aaabbd88c779fb539e47c7817c88ab09b348588..7ca7e9d649d1716ff85a705e17fe2430146fde0d 100644
--- a/src/plugins/texteditor/generichighlighter/highlighter.h
+++ b/src/plugins/texteditor/generichighlighter/highlighter.h
@@ -34,6 +34,7 @@
 
 #include <QtCore/QString>
 #include <QtCore/QVector>
+#include <QtCore/QStack>
 #include <QtCore/QSharedPointer>
 #include <QtCore/QStringList>
 
@@ -92,14 +93,14 @@ private:
                              const bool childRule,
                              const QList<QSharedPointer<Rule> > &rules);
 
-    void setCurrentContext();
+    void assignCurrentContext();
     bool contextChangeRequired(const QString &contextName) const;
     void handleContextChange(const QString &contextName,
                              const QSharedPointer<HighlightDefinition> &definition,
                              const bool setCurrent = true);
     void changeContext(const QString &contextName,
                        const QSharedPointer<HighlightDefinition> &definition,
-                       const bool setCurrent = true);
+                       const bool assignCurrent = true);
 
     QString currentContextSequence() const;
     void mapPersistentSequence(const QString &contextSequence);
@@ -117,6 +118,8 @@ private:
                      const QSharedPointer<HighlightDefinition> &definition);
     void applyVisualWhitespaceFormat(const QString &text);
 
+    void applyFolding() const;
+
     // Mapping from Kate format strings to format ids.
     struct KateFormatMap
     {
@@ -131,27 +134,40 @@ private:
         BlockData();
         virtual ~BlockData();
 
-        int m_originalState;
+        int m_foldingIndentDelta;
+        int m_originalObservableState;
+        QStack<QString> m_foldingRegions;
         QSharedPointer<Context> m_contextToContinue;
     };
     BlockData *initializeBlockData();
+    static BlockData *blockData(QTextBlockUserData *userData);
 
-    // Block states
+    // Block states are composed by the region depth (used for code folding) and what I call
+    // observable states. Observable states occupy the 12 least significant bits. They might have
+    // the following values:
     // - Default [0]: Nothing special.
     // - WillContinue [1]: When there is match of the LineContinue rule (backslash as the last
     //   character).
-    // - Continued [2]: Blocks that happen after a WillContinue block and continued from their
+    // - Continued [2]: Blocks that happen after a WillContinue block and continue from their
     //   context until the next line end.
     // - Persistent(s) [Anything >= 3]: Correspond to persistent contexts which last until a pop
     //   occurs due to a matching rule. Every sequence of persistent contexts seen so far is
     //   associated with a number (incremented by a unit each time).
-    enum BlockState {
+    // Region depths occupy the remaining bits.
+    enum ObservableBlockState {
         Default = 0,
         WillContinue,
         Continued,
         PersistentsStart
     };
-    int m_persistentStatesCounter;
+    int computeState(const int observableState) const;
+
+    static int extractRegionDepth(const int state);
+    static int extractObservableState(const int state);
+
+    int m_regionDepth;
+
+    int m_persistentObservableStatesCounter;
     int m_dynamicContextsCounter;
 
     bool m_isBroken;
@@ -160,11 +176,11 @@ private:
     QSharedPointer<Context> m_currentContext;
     QVector<QSharedPointer<Context> > m_contexts;
 
-    // Mapping from context sequences to the persistent state they represent.
-    QHash<QString, int> m_persistentStates;
-    // Mapping from context sequences to the non-persistent state that led to them.
-    QHash<QString, int> m_leadingStates;
-    // Mapping from persistent states to context sequences (the actual "stack").
+    // Mapping from context sequences to the observable persistent state they represent.
+    QHash<QString, int> m_persistentObservableStates;
+    // Mapping from context sequences to the non-persistent observable state that led to them.
+    QHash<QString, int> m_leadingObservableStates;
+    // Mapping from observable persistent states to context sequences (the actual "stack").
     QHash<int, QVector<QSharedPointer<Context> > > m_persistentContexts;
 
     // Captures used in dynamic rules.
diff --git a/src/plugins/texteditor/generichighlighter/progressdata.cpp b/src/plugins/texteditor/generichighlighter/progressdata.cpp
index 12c76079d84e6be2a564edcb8215988c44f50684..9a7b619eca02756b9be93d26b5c2d442c9c0bd51 100644
--- a/src/plugins/texteditor/generichighlighter/progressdata.cpp
+++ b/src/plugins/texteditor/generichighlighter/progressdata.cpp
@@ -38,7 +38,9 @@ ProgressData::ProgressData() :
     m_offset(0),
     m_savedOffset(-1),
     m_onlySpacesSoFar(true),
-    m_willContinueLine(false)
+    m_willContinueLine(false),
+    m_openingBraceMatchAtFirstNonSpace(false),
+    m_closingBraceMatchAtNonEnd(false)
 {}
 
 void ProgressData::setOffset(const int offset)
@@ -66,13 +68,33 @@ void ProgressData::restoreOffset()
 void ProgressData::setOnlySpacesSoFar(const bool onlySpaces)
 { m_onlySpacesSoFar = onlySpaces; }
 
-bool ProgressData::onlySpacesSoFar() const
+bool ProgressData::isOnlySpacesSoFar() const
 { return m_onlySpacesSoFar; }
 
+void ProgressData::setOpeningBraceMatchAtFirstNonSpace(const bool match)
+{ m_openingBraceMatchAtFirstNonSpace = match; }
+
+bool ProgressData::isOpeningBraceMatchAtFirstNonSpace() const
+{ return m_openingBraceMatchAtFirstNonSpace; }
+
+void ProgressData::setClosingBraceMatchAtNonEnd(const bool match)
+{ m_closingBraceMatchAtNonEnd = match; }
+
+bool ProgressData::isClosingBraceMatchAtNonEnd() const
+{ return m_closingBraceMatchAtNonEnd; }
+
+void ProgressData::clearBracesMatches()
+{
+    if (m_openingBraceMatchAtFirstNonSpace)
+        m_openingBraceMatchAtFirstNonSpace = false;
+    if (m_closingBraceMatchAtNonEnd)
+        m_closingBraceMatchAtNonEnd = false;
+}
+
 void ProgressData::setWillContinueLine(const bool willContinue)
 { m_willContinueLine = willContinue; }
 
-bool ProgressData::willContinueLine() const
+bool ProgressData::isWillContinueLine() const
 { return m_willContinueLine; }
 
 void ProgressData::setCaptures(const QStringList &captures)
diff --git a/src/plugins/texteditor/generichighlighter/progressdata.h b/src/plugins/texteditor/generichighlighter/progressdata.h
index a621c15dc75e2449169ac23e0adc200e78dda09c..d82b4914d53f7ae8f90eea14ed0ee195da2b643d 100644
--- a/src/plugins/texteditor/generichighlighter/progressdata.h
+++ b/src/plugins/texteditor/generichighlighter/progressdata.h
@@ -50,10 +50,18 @@ public:
     void restoreOffset();
 
     void setOnlySpacesSoFar(const bool onlySpaces);
-    bool onlySpacesSoFar() const;
+    bool isOnlySpacesSoFar() const;
+
+    void setOpeningBraceMatchAtFirstNonSpace(const bool match);
+    bool isOpeningBraceMatchAtFirstNonSpace() const;
+
+    void setClosingBraceMatchAtNonEnd(const bool match);
+    bool isClosingBraceMatchAtNonEnd() const;
+
+    void clearBracesMatches();
 
     void setWillContinueLine(const bool willContinue);
-    bool willContinueLine() const;
+    bool isWillContinueLine() const;
 
     void setCaptures(const QStringList &captures);
     const QStringList &captures() const;
@@ -62,6 +70,8 @@ private:
     int m_offset;
     int m_savedOffset;
     bool m_onlySpacesSoFar;
+    bool m_openingBraceMatchAtFirstNonSpace;
+    bool m_closingBraceMatchAtNonEnd;
     bool m_willContinueLine;
     QStringList m_captures;
 };
diff --git a/src/plugins/texteditor/generichighlighter/rule.cpp b/src/plugins/texteditor/generichighlighter/rule.cpp
index 3ad611938e47411ef48b5d550bf9cc8e4f2c09ca..f30e6e8e3c99321636fb9e49de717c00b57a1c14 100644
--- a/src/plugins/texteditor/generichighlighter/rule.cpp
+++ b/src/plugins/texteditor/generichighlighter/rule.cpp
@@ -58,6 +58,8 @@ const QLatin1Char Rule::kN('n');
 const QLatin1Char Rule::kR('r');
 const QLatin1Char Rule::kT('t');
 const QLatin1Char Rule::kV('v');
+const QLatin1Char Rule::kOpeningBrace('{');
+const QLatin1Char Rule::kClosingBrace('}');
 
 Rule::Rule(bool consumesNonSpace) :
     m_lookAhead(false), m_firstNonSpace(false), m_column(-1), m_consumesNonSpace(consumesNonSpace)
@@ -162,7 +164,7 @@ bool Rule::charPredicateMatchSucceed(const QString &text,
 
 bool Rule::matchSucceed(const QString &text, const int length, ProgressData *progress) const
 { 
-    if (m_firstNonSpace && !progress->onlySpacesSoFar())
+    if (m_firstNonSpace && !progress->isOnlySpacesSoFar())
         return false;
 
     if (m_column != -1 && m_column != progress->offset())
@@ -170,7 +172,7 @@ bool Rule::matchSucceed(const QString &text, const int length, ProgressData *pro
 
     int original = progress->offset();
     if (doMatchSucceed(text, length, progress)) {
-        if (progress->onlySpacesSoFar() && !m_lookAhead && m_consumesNonSpace)
+        if (progress->isOnlySpacesSoFar() && !m_lookAhead && m_consumesNonSpace)
             progress->setOnlySpacesSoFar(false);
 
         if (m_lookAhead)
diff --git a/src/plugins/texteditor/generichighlighter/rule.h b/src/plugins/texteditor/generichighlighter/rule.h
index a53b2f8e5c408ee6d632746fc90d13b0a0d9f1a9..77c8397bf198db3484abe6a8865e135d94c52a25 100644
--- a/src/plugins/texteditor/generichighlighter/rule.h
+++ b/src/plugins/texteditor/generichighlighter/rule.h
@@ -123,7 +123,9 @@ protected:
     static const QLatin1Char kN;
     static const QLatin1Char kR;
     static const QLatin1Char kT;
-    static const QLatin1Char kV;    
+    static const QLatin1Char kV;
+    static const QLatin1Char kOpeningBrace;
+    static const QLatin1Char kClosingBrace;
 
 private:
     virtual bool doMatchSucceed(const QString &text,
diff --git a/src/plugins/texteditor/generichighlighter/specificrules.cpp b/src/plugins/texteditor/generichighlighter/specificrules.cpp
index 86e6f4e7ec339e1cb7a5c7033ad952d514f5fe29..8a43f65b703fbb26b66aac1791490fec77544ec9 100644
--- a/src/plugins/texteditor/generichighlighter/specificrules.cpp
+++ b/src/plugins/texteditor/generichighlighter/specificrules.cpp
@@ -86,7 +86,18 @@ bool DetectCharRule::doMatchSucceed(const QString &text,
                                     const int length,
                                     ProgressData *progress) const
 {
-    return matchCharacter(text, length, progress, m_char);
+    if (matchCharacter(text, length, progress, m_char)) {
+        // This is to make code folding have a control flow style look in the case of braces.
+        // Naturally, this assumes that language definitions use braces with this meaning.
+        if (m_char == kOpeningBrace && progress->isOnlySpacesSoFar() && !isLookAhead()) {
+            progress->setOpeningBraceMatchAtFirstNonSpace(true);
+        } else if (m_char == kClosingBrace &&
+                   !text.right(length - progress->offset()).trimmed().isEmpty()) {
+            progress->setClosingBraceMatchAtNonEnd(true);
+        }
+        return true;
+    }
+    return false;
 }
 
 // Detect2Chars
diff --git a/src/plugins/texteditor/plaintexteditor.cpp b/src/plugins/texteditor/plaintexteditor.cpp
index e62e2df035e24d571b21d42f0816206039b78fee..d945b166711d098535419ff6ef9065d981092aec 100644
--- a/src/plugins/texteditor/plaintexteditor.cpp
+++ b/src/plugins/texteditor/plaintexteditor.cpp
@@ -162,7 +162,10 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
 {
     Highlighter *highlighter = new Highlighter();
     baseTextDocument()->setSyntaxHighlighter(highlighter);
+
     m_isMissingSyntaxDefinition = true;
+    setCodeFoldingSupported(false);
+    setCodeFoldingVisible(false);
 
     QString definitionId;
     if (!mimeType.isNull()) {
@@ -185,6 +188,9 @@ void PlainTextEditor::configure(const Core::MimeType &mimeType)
             m_commentDefinition.setMultiLineStart(definition->multiLineCommentStart());
             m_commentDefinition.setMultiLineEnd(definition->multiLineCommentEnd());
 
+            setCodeFoldingSupported(true);
+            setCodeFoldingVisible(true);
+
             m_isMissingSyntaxDefinition = false;
         }
     } else if (file()) {
diff --git a/src/plugins/texteditor/plaintexteditorfactory.cpp b/src/plugins/texteditor/plaintexteditorfactory.cpp
index 87f6c70830152cb8947c52ec448510c8ef79fe52..c06b0f79f95e0b4c35cc2796836c9c17a5b2f550 100644
--- a/src/plugins/texteditor/plaintexteditorfactory.cpp
+++ b/src/plugins/texteditor/plaintexteditorfactory.cpp
@@ -50,7 +50,8 @@ PlainTextEditorFactory::PlainTextEditorFactory(QObject *parent)
     m_actionHandler = new TextEditorActionHandler(
         QLatin1String(TextEditor::Constants::C_TEXTEDITOR),
         TextEditorActionHandler::Format |
-        TextEditorActionHandler::UnCommentSelection);
+        TextEditorActionHandler::UnCommentSelection |
+        TextEditorActionHandler::UnCollapseAll);
     m_mimeTypes << QLatin1String(TextEditor::Constants::C_TEXTEDITOR_MIMETYPE_TEXT);
 
     connect(Core::EditorManager::instance(), SIGNAL(currentEditorChanged(Core::IEditor*)),
diff --git a/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h
index 37320e4361dc9e8e546fa34aad0fe1dbcb763e30..f3433db6327991401234361462b22ed7845de85e 100644
--- a/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h
+++ b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h
@@ -32,13 +32,19 @@
 
 /*
  Since the text editor plugin directory is not included in the search list of the pro file, this
- file replaces the "real" basetextdocumentlayout.h file. The objective is to simply use
- QTextBlockUserData instead of TextEditor::TextBlockUserData to avoid "external"
- dependencies or intrusive defines.
+ file replaces the "real" basetextdocumentlayout.h file. The objective is to provide a simple
+ TextBlockUserData and avoid "external" dependencies or intrusive defines.
  */
 
 #include <QtGui/QTextBlockUserData>
 
-typedef QTextBlockUserData TextBlockUserData;
+struct TextBlockUserData : QTextBlockUserData
+{
+    virtual ~TextBlockUserData(){}
+
+    void setFoldingStartIncluded(const bool) {}
+    void setFoldingEndIncluded(const bool) {}
+    void setFoldingIndent(const int) {}
+};
 
 #endif // BASETEXTDOCUMENTLAYOUT_H
diff --git a/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp b/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp
index d558a9eb418201f28eeaf7305991af45987c53d1..7ed758a5a349c2472eda91473ad8052e6e867f87 100644
--- a/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp
+++ b/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp
@@ -109,7 +109,8 @@ void HighlighterMock::highlightBlock(const QString &text)
 
     if (m_states.size() <= m_statesCounter)
         QFAIL("Expected state for current block not set.");
-    QCOMPARE(currentBlockState(), m_states.at(m_statesCounter++));
+    const int observableState = currentBlockState() & 0xFFF;
+    QCOMPARE(observableState, m_states.at(m_statesCounter++));
 
     if (m_formatSequence.size() <= m_formatsCounter)
         QFAIL("Expected highlight sequence for current block not set.");
diff --git a/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp b/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp
index 8e402f8b8b09b9ed2ac2ac513beca4d003572fe7..6c76fe2c16385e9339293442175ad3e7591920e4 100644
--- a/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp
+++ b/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp
@@ -80,6 +80,9 @@ private slots:
     void testDynamicContexts();
     void testDynamicContexts_data();
 
+    void testFirstNonSpace();
+    void testFirstNonSpace_data();
+
 private:
     void createKeywords();
     void createContexts();
@@ -436,6 +439,13 @@ void tst_HighlighterEngine::createContexts()
     r26->setActive("true");
     r26->setDefinition(m_definition);
     dynamic->addRule(QSharedPointer<Rule>(r26));
+
+    DetectCharRule *r27 = new DetectCharRule;
+    r27->setChar("|");
+    r27->setItemData("Error");
+    r27->setFirstNonSpace("true");
+    r27->setDefinition(m_definition);
+    normal->addRule(QSharedPointer<Rule>(r27));
 }
 
 void tst_HighlighterEngine::createItemDatas()
@@ -1031,6 +1041,47 @@ void tst_HighlighterEngine::testDynamicContexts_data()
     QTest::newRow("case 1") << states << sequences << text;
 }
 
+void tst_HighlighterEngine::testFirstNonSpace()
+{
+    test();
+}
+
+void tst_HighlighterEngine::testFirstNonSpace_data()
+{
+    createColumns();
+
+    QList<int> states;
+    QList<HighlightSequence> sequences;
+    QString text;
+
+    HighlightSequence seqa(0, 1, Formats::instance().errorFormat());
+    HighlightSequence seqb(0, 3);
+    seqb.add(3, 4, Formats::instance().errorFormat());
+    HighlightSequence seqc(0, 1);
+    seqc.add(1, 2, Formats::instance().errorFormat());
+    HighlightSequence seqd(0, 2);
+
+    states << 0;
+    sequences << seqa;
+    text = "|";
+    QTest::newRow("case 0") << states << sequences << text;
+
+    sequences.clear();
+    sequences << seqb;
+    text = "   |";
+    QTest::newRow("case 1") << states << sequences << text;
+
+    sequences.clear();
+    sequences << seqc;
+    text = "\t|";
+    QTest::newRow("case 2") << states << sequences << text;
+
+    sequences.clear();
+    sequences << seqd;
+    text = "a|";
+    QTest::newRow("case 3") << states << sequences << text;
+}
+
 QTEST_MAIN(tst_HighlighterEngine)
 #include "tst_highlighterengine.moc"
 
diff --git a/tests/auto/generichighlighter/specificrules/tst_specificrules.cpp b/tests/auto/generichighlighter/specificrules/tst_specificrules.cpp
index 6e482f38b996aeecfe9558e2d472ee1a16200e36..c06b61fbaf5bcabfb7755bd83a455612a83ce6a7 100644
--- a/tests/auto/generichighlighter/specificrules/tst_specificrules.cpp
+++ b/tests/auto/generichighlighter/specificrules/tst_specificrules.cpp
@@ -149,8 +149,8 @@ void tst_SpecificRules::testMatch(const Rule &rule, ProgressData *progress) cons
 
     QTEST(rule.matchSucceed(s, s.length(), progress), "match");
     QTEST(progress->offset(), "offset");
-    QTEST(progress->onlySpacesSoFar(), "only spaces");
-    QTEST(progress->willContinueLine(), "will continue");
+    QTEST(progress->isOnlySpacesSoFar(), "only spaces");
+    QTEST(progress->isWillContinueLine(), "will continue");
 }
 
 void tst_SpecificRules::testDetectChar()