/* Copyright (c) 2012 Montel Laurent based on code from qt-labs-graphics-dojo/htmleditor/highlighter.* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "htmlhighlighter.h" namespace KPIMTextEdit { class HtmlHighlighterPrivate { public: enum Construct { DocType, Entity, Tag, Comment, AttributeName, AttributeValue }; enum State { State_Text = -1, State_DocType, State_Comment, State_TagStart, State_TagName, State_InsideTag, State_AttributeName, State_SingleQuote, State_DoubleQuote, State_AttributeValue }; HtmlHighlighterPrivate() { colors[DocType] = QColor( 192, 192, 192 ); colors[Entity] = QColor( 128, 128, 128 ); colors[Tag] = QColor( 136, 18, 128 ); colors[Comment] = QColor( 35, 110, 37 ); colors[AttributeName] = QColor( 153, 69, 0 ); colors[AttributeValue] = QColor( 36, 36, 170 ); } QHash colors; }; HtmlHighlighter::HtmlHighlighter( QTextDocument *document ) : QSyntaxHighlighter( document ), d( new HtmlHighlighterPrivate ) { } HtmlHighlighter::~HtmlHighlighter() { delete d; } void HtmlHighlighter::highlightBlock( const QString &text ) { int state = previousBlockState(); int len = text.length(); int start = 0; int pos = 0; while ( pos < len ) { switch ( state ) { case HtmlHighlighterPrivate::State_Text: default: while ( pos < len ) { QChar ch = text.at( pos ); if ( ch == QLatin1Char( '<' ) ) { if ( text.mid( pos, 4 ) == QLatin1String( "" ) ) { pos += 3; state = HtmlHighlighterPrivate::State_Text; break; } else { ++pos; } } setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::Comment] ); break; case HtmlHighlighterPrivate::State_DocType: start = pos; while ( pos < len ) { QChar ch = text.at( pos ); ++pos; if ( ch == QLatin1Char( '>' ) ) { state = HtmlHighlighterPrivate::State_Text; break; } } setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::DocType] ); break; // at '<' in e.g. "foo" case HtmlHighlighterPrivate::State_TagStart: start = pos + 1; while ( pos < len ) { QChar ch = text.at( pos ); ++pos; if ( ch == QLatin1Char( '>' ) ) { state = HtmlHighlighterPrivate::State_Text; break; } if ( !ch.isSpace() ) { --pos; state = HtmlHighlighterPrivate::State_TagName; break; } } break; // at 'b' in e.g "
foo
" case HtmlHighlighterPrivate::State_TagName: start = pos; while ( pos < len ) { QChar ch = text.at( pos ); ++pos; if ( ch.isSpace() ) { --pos; state = HtmlHighlighterPrivate::State_InsideTag; break; } if ( ch == QLatin1Char( '>' ) ) { state = HtmlHighlighterPrivate::State_Text; break; } } setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::Tag] ); break; // anywhere after tag name and before tag closing ('>') case HtmlHighlighterPrivate::State_InsideTag: start = pos; while ( pos < len ) { QChar ch = text.at( pos ); ++pos; if ( ch == QLatin1Char( '/' ) ) { continue; } if ( ch == QLatin1Char( '>' ) ) { state = HtmlHighlighterPrivate::State_Text; break; } if ( !ch.isSpace() ) { --pos; state = HtmlHighlighterPrivate::State_AttributeName; break; } } break; // at 's' in e.g. case HtmlHighlighterPrivate::State_AttributeName: start = pos; while ( pos < len ) { QChar ch = text.at( pos ); ++pos; if ( ch == QLatin1Char( '=' ) ) { state = HtmlHighlighterPrivate::State_AttributeValue; break; } if ( ch == QLatin1Char( '>' ) || ch == QLatin1Char( '/' ) ) { state = HtmlHighlighterPrivate::State_InsideTag; break; } } setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeName] ); break; // after '=' in e.g. case HtmlHighlighterPrivate::State_AttributeValue: start = pos; // find first non-space character while ( pos < len ) { QChar ch = text.at( pos ); ++pos; // handle opening single quote if ( ch == QLatin1Char( '\'' ) ) { state = HtmlHighlighterPrivate::State_SingleQuote; break; } // handle opening double quote if ( ch == QLatin1Char( '"' ) ) { state = HtmlHighlighterPrivate::State_DoubleQuote; break; } if ( !ch.isSpace() ) { break; } } if ( state == HtmlHighlighterPrivate::State_AttributeValue ) { // attribute value without quote // just stop at non-space or tag delimiter start = pos; while ( pos < len ) { QChar ch = text.at( pos ); if ( ch.isSpace() ) { break; } if ( ch == QLatin1Char( '>' ) || ch == QLatin1Char( '/' ) ) { break; } ++pos; } state = HtmlHighlighterPrivate::State_InsideTag; setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); } break; // after the opening single quote in an attribute value case HtmlHighlighterPrivate::State_SingleQuote: start = pos; while ( pos < len ) { QChar ch = text.at(pos); ++pos; if ( ch == QLatin1Char( '\'' ) ) { break; } } state = HtmlHighlighterPrivate::State_InsideTag; setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); break; // after the opening double quote in an attribute value case HtmlHighlighterPrivate::State_DoubleQuote: start = pos; while ( pos < len ) { QChar ch = text.at(pos); ++pos; if ( ch == QLatin1Char( '"' ) ) { break; } } state = HtmlHighlighterPrivate::State_InsideTag; setFormat( start, pos - start, d->colors[HtmlHighlighterPrivate::AttributeValue] ); break; } } setCurrentBlockState( state ); } }