Salome HOME
Merge remote branch 'origin/hydro/imps_2015'
[modules/gui.git] / tools / PyEditor / src / PyEditor_PyHighlighter.cxx
1 // Copyright (C) 2015-2016  OPEN CASCADE
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // File   : PyEditor_PyHighlighter.cxx
20 // Author : Maxim GLIBIN, Open CASCADE S.A.S. (maxim.glibin@opencascade.com)
21 //
22
23 #include "PyEditor_PyHighlighter.h"
24
25 #define NORMAL 0
26 #define TRIPLESINGLE 1
27 #define TRIPLEDOUBLE 2
28
29 /*!
30   \class PyEditor_PyHighlighter
31   \brief Python highlighter class defines the syntax highlighting rules.
32 */
33
34 PyEditor_PyHighlighter::TextBlockData::TextBlockData()
35 {
36 }
37
38 QVector<PyEditor_PyHighlighter::ParenthesisInfo*> PyEditor_PyHighlighter::TextBlockData::parentheses()
39 {
40   return myParentheses;
41 }
42
43 void PyEditor_PyHighlighter::TextBlockData::insert( PyEditor_PyHighlighter::ParenthesisInfo* theInfo )
44 {
45   int i = 0;
46   while ( i < myParentheses.size() && theInfo->position > myParentheses.at(i)->position )
47     ++i;
48
49   myParentheses.insert( i, theInfo );
50 }
51
52 /*!
53   \brief Constructor.
54   \param theDocument container for structured rich text documents.
55 */
56 PyEditor_PyHighlighter::PyEditor_PyHighlighter( QTextDocument* theDocument )
57   : QSyntaxHighlighter( theDocument )
58 {
59   initialize();
60 }
61
62 /*!
63   \brief Initialization rules.
64 */
65 void PyEditor_PyHighlighter::initialize()
66 {
67   HighlightingRule aRule;
68
69   // Keywords
70   keywordFormat.setForeground( Qt::blue );
71   QStringList aKeywords = keywords();
72   foreach ( const QString& keyword, aKeywords )
73   {
74     aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
75     aRule.format = keywordFormat;
76     aRule.capture = 0;
77     highlightingRules.append( aRule );
78   }
79
80   // Special keywords
81   specialFromat.setForeground( Qt::magenta );
82   QStringList aSpecialKeywords = specialKeywords();
83   foreach ( const QString& keyword, aSpecialKeywords )
84   {
85     aRule.pattern = QRegExp( QString( "\\b%1\\b" ).arg( keyword ) );
86     aRule.format = specialFromat;
87     aRule.capture = 0;
88     highlightingRules.append( aRule );
89   }
90
91   // Reference to the current instance of the class
92   referenceClassFormat.setForeground( QColor( 179, 143, 0 ) );
93   referenceClassFormat.setFontItalic( true );
94   aRule.pattern = QRegExp( "\\bself\\b" );
95   aRule.format = referenceClassFormat;
96   aRule.capture = 0;
97   highlightingRules.append( aRule );
98
99   // Numbers
100   numberFormat.setForeground( Qt::darkMagenta );
101   aRule.pattern = QRegExp( "\\b([-+])?(\\d+(\\.)?\\d*|\\d*(\\.)?\\d+)(([eE]([-+])?)?\\d+)?\\b" );
102   aRule.format = numberFormat;
103   aRule.capture = 0;
104   highlightingRules.append( aRule );
105
106   // String qoutation
107   quotationFormat.setForeground( Qt::darkGreen );
108   aRule.pattern = QRegExp( "(?:'[^']*'|\"[^\"]*\")" );
109   aRule.pattern.setMinimal( true );
110   aRule.format = quotationFormat;
111   aRule.capture = 0;
112   highlightingRules.append( aRule );
113
114   // Function names
115   functionFormat.setFontWeight( QFont::Bold );
116   aRule.pattern = QRegExp( "(?:def\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
117   aRule.capture = 1;
118   aRule.format = functionFormat;
119   highlightingRules.append( aRule );
120
121   // Class names
122   classFormat.setForeground( Qt::darkBlue );
123   classFormat.setFontWeight( QFont::Bold );
124   aRule.pattern = QRegExp( "(?:class\\s*)(\\b[A-Za-z0-9_]+)(?=[\\W])" );
125   aRule.capture = 1;
126   aRule.format = classFormat;
127   highlightingRules.append( aRule );
128
129   // Multi line comments
130   multiLineCommentFormat.setForeground( Qt::darkRed );
131   tripleQuotesExpression = QRegExp( "(:?\"[\"]\".*\"[\"]\"|'''.*''')" );
132   aRule.pattern = tripleQuotesExpression;
133   aRule.pattern.setMinimal( true );
134   aRule.format = multiLineCommentFormat;
135   aRule.capture = 0;
136   highlightingRules.append( aRule );
137
138   tripleSingleExpression = QRegExp( "'''(?!\")" );
139   tripleDoubleExpression = QRegExp( "\"\"\"(?!')" );
140
141   // Single comments
142   singleLineCommentFormat.setForeground( Qt::darkGray );
143   aRule.pattern = QRegExp( "#[^\n]*" );
144   aRule.format = singleLineCommentFormat;
145   aRule.capture = 0;
146   highlightingRules.append( aRule );
147 }
148
149 /*!
150   \return string list of Python keywords.
151  */
152 QStringList PyEditor_PyHighlighter::keywords()
153 {
154   QStringList aKeywords;
155   aKeywords << "and"
156             << "as"
157             << "assert"
158             << "break"
159             << "class"
160             << "continue"
161             << "def"
162             << "elif"
163             << "else"
164             << "except"
165             << "exec"
166             << "finally"
167             << "False"
168             << "for"
169             << "from"
170             << "global"
171             << "if"
172             << "import"
173             << "in"
174             << "is"
175             << "lambda"
176             << "None"
177             << "not"
178             << "or"
179             << "pass"
180             << "print"
181             << "raise"
182             << "return"
183             << "True"
184             << "try"
185             << "while"
186             << "with"
187             << "yield";
188   return aKeywords;
189 }
190
191 /*!
192   \return string list of special Python keywords.
193 */
194 QStringList PyEditor_PyHighlighter::specialKeywords()
195 {
196   QStringList aSpecialKeywords;
197   aSpecialKeywords << "ArithmeticError"
198                    << "AssertionError"
199                    << "AttributeError"
200                    << "EnvironmentError"
201                    << "EOFError"
202                    << "Exception"
203                    << "FloatingPointError"
204                    << "ImportError"
205                    << "IndentationError"
206                    << "IndexError"
207                    << "IOError"
208                    << "KeyboardInterrupt"
209                    << "KeyError"
210                    << "LookupError"
211                    << "MemoryError"
212                    << "NameError"
213                    << "NotImplementedError"
214                    << "OSError"
215                    << "OverflowError"
216                    << "ReferenceError"
217                    << "RuntimeError"
218                    << "StandardError"
219                    << "StopIteration"
220                    << "SyntaxError"
221                    << "SystemError"
222                    << "SystemExit"
223                    << "TabError"
224                    << "TypeError"
225                    << "UnboundLocalError"
226                    << "UnicodeDecodeError"
227                    << "UnicodeEncodeError"
228                    << "UnicodeError"
229                    << "UnicodeTranslateError"
230                    << "ValueError"
231                    << "WindowsError"
232                    << "ZeroDivisionError"
233                    << "Warning"
234                    << "UserWarning"
235                    << "DeprecationWarning"
236                    << "PendingDeprecationWarning"
237                    << "SyntaxWarning"
238                    << "OverflowWarning"
239                    << "RuntimeWarning"
240                    << "FutureWarning";
241   return aSpecialKeywords;
242 }
243
244 void PyEditor_PyHighlighter::highlightBlock( const QString& theText )
245 {
246   TextBlockData* aData = new TextBlockData;
247   
248   insertBracketsData( RoundBrackets, aData, theText );
249   insertBracketsData( CurlyBrackets, aData, theText );
250   insertBracketsData( SquareBrackets, aData, theText );
251
252   setCurrentBlockUserData( aData );
253
254   foreach ( const HighlightingRule& rule, highlightingRules )
255   {
256     QRegExp expression( rule.pattern );
257     int anIndex = expression.indexIn( theText );
258     while ( anIndex >= 0 )
259     {
260       anIndex = expression.pos( rule.capture );
261       int aLength = expression.cap( rule.capture ).length();
262       setFormat( anIndex, aLength, rule.format );
263       anIndex = expression.indexIn( theText, anIndex + aLength );
264     }
265   }
266
267   setCurrentBlockState( NORMAL );
268
269   if ( theText.indexOf( tripleQuotesExpression ) != -1 )
270     return;
271
272   QList<int> aTripleSingle;
273   aTripleSingle << theText.indexOf( tripleSingleExpression ) << TRIPLESINGLE;
274
275   QList<int> aTripleDouble;
276   aTripleDouble << theText.indexOf( tripleDoubleExpression ) << TRIPLEDOUBLE;
277  
278   QList< QList<int> > aTripleExpressions;
279   aTripleExpressions << aTripleSingle << aTripleDouble;
280
281   for ( int i = 0; i < aTripleExpressions.length(); i++ )
282   {
283     QList<int> aBlock = aTripleExpressions[i];
284     int anIndex = aBlock[0];
285     int aState = aBlock[1];
286     if ( previousBlockState() == aState )
287     {
288       if ( anIndex == -1 )
289       {
290         anIndex = theText.length();
291         setCurrentBlockState( aState );
292       }
293       setFormat( 0, anIndex + 3, multiLineCommentFormat );
294     }
295     else if ( anIndex > -1 )
296     {
297       setCurrentBlockState( aState );
298       setFormat( anIndex, theText.length(), multiLineCommentFormat );
299     }
300   }
301 }
302
303 void PyEditor_PyHighlighter::insertBracketsData( char theLeftSymbol,
304                                                  char theRightSymbol,
305                                                  TextBlockData* theData,
306                                                  const QString& theText )
307 {
308   int leftPosition = theText.indexOf( theLeftSymbol );
309   while( leftPosition != -1 )
310   {
311     ParenthesisInfo* info = new ParenthesisInfo();
312     info->character = theLeftSymbol;
313     info->position = leftPosition;
314
315     theData->insert( info );
316     leftPosition = theText.indexOf( theLeftSymbol, leftPosition + 1 );
317   }
318
319   int rightPosition = theText.indexOf( theRightSymbol );
320   while( rightPosition != -1 )
321   {
322     ParenthesisInfo* info = new ParenthesisInfo();
323     info->character = theRightSymbol;
324     info->position = rightPosition;
325
326     theData->insert( info );
327     rightPosition = theText.indexOf( theRightSymbol, rightPosition + 1 );
328   }
329 }
330
331 void PyEditor_PyHighlighter::insertBracketsData( Brackets theBrackets,
332                                                  TextBlockData* theData,
333                                                  const QString& theText )
334 {
335   char leftChar = '0';
336   char rightChar = '0';
337   
338   switch( theBrackets )
339   {
340   case RoundBrackets:
341     leftChar = '(';
342     rightChar = ')';
343     break;
344   case CurlyBrackets:
345     leftChar = '{';
346     rightChar = '}';
347     break;
348   case SquareBrackets:
349     leftChar = '[';
350     rightChar = ']';
351     break;
352   }
353
354   insertBracketsData( leftChar, rightChar, theData, theText );
355 }