Results 1 to 2 of 2

Thread: Strange behavior of QSyntaxHighlighter in Qt3

  1. #1
    Join Date
    Nov 2008
    Posts
    33
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Strange behavior of QSyntaxHighlighter in Qt3

    The Qt3 implementation of QSyntaxHighlighter seems to be a little bit buggy or at least the semantics of highlightParagraph, in particular, the semantics of the states of the paragraph is not completely clear.

    As an example consider the code below, which highlights some keywords (in blue) and handles two types of comments: standard /* */ comments (in red) and /** */ doxyfile comments in
    green; for simplicity we assume that comment delimiters appear on a single line.

    Qt Code:
    1. #include <qapplication.h>
    2. #include <qsyntaxhighlighter.h>
    3. #include <qtextedit.h>
    4. #include <iostream>
    5.  
    6. #define NORMAL_STATE 0
    7. #define COMMENT_STATE 1
    8. #define DOXY_STATE 2
    9.  
    10. class SyntaxHighlighter: public QSyntaxHighlighter {
    11. QStringList keywordList;
    12. QColor keyColor;
    13. QFont keyFont;
    14. QColor commentColor;
    15. QFont commentFont;
    16. QColor commentDoxyColor;
    17.  
    18. public:
    19. SyntaxHighlighter(QTextEdit* textEdit) :
    20. QSyntaxHighlighter(textEdit) {
    21. keyFont.setBold(true);
    22. keyFont.setItalic(true);
    23. keyFont.setUnderline(true);
    24. keyColor = Qt::blue;
    25.  
    26. commentColor = Qt::red;
    27. commentDoxyColor = Qt::green;
    28.  
    29. commentFont.setItalic(true);
    30. }
    31.  
    32. ~SyntaxHighlighter() {
    33. }
    34.  
    35. void addKeyword(const QString &str) {
    36. keywordList << str;
    37. }
    38.  
    39. int highlightParagraph(const QString & text, int endStateOfLastPara) {
    40. int index = 0;
    41. int count = 0;
    42. QStringList::iterator it = keywordList.begin();
    43.  
    44. std::cout << "prev par state: " << endStateOfLastPara << std::endl;
    45. std::cout << "text: " << text << std::endl;
    46.  
    47. // Assume the text to not contain keywords
    48. setFormat(0, text.length(), textEdit()->font(), Qt::black);
    49.  
    50. int currentState = endStateOfLastPara;
    51.  
    52. if (endStateOfLastPara == NORMAL_STATE || endStateOfLastPara < 0) {
    53. // keep it simple and assume that comment delimiters appear on a single line
    54. if (text.contains("/**")) {
    55. setFormat(0, text.length(), commentFont, commentDoxyColor);
    56. currentState = DOXY_STATE;
    57. } else if (text.contains("/*")) {
    58. setFormat(0, text.length(), commentFont, commentColor);
    59. currentState = COMMENT_STATE;
    60. } else {
    61. // Look for keywords
    62. while (it != keywordList.end()) {
    63. count = text.contains(*it);
    64. if (count) {
    65. for (int i = 0; i < count; i++) {
    66. index = text.find(*it);
    67. setFormat(index, (*it).length(), keyFont, keyColor);
    68. ++index;
    69. }
    70. }
    71. ++it;
    72. }
    73. }
    74. } else if (endStateOfLastPara == COMMENT_STATE) {
    75. setFormat(0, text.length(), commentFont, commentColor);
    76. if (text.contains("*/"))
    77. currentState = NORMAL_STATE;
    78. } else if (endStateOfLastPara == DOXY_STATE) {
    79. setFormat(0, text.length(), commentFont, commentDoxyColor);
    80. if (text.contains("*/"))
    81. currentState = NORMAL_STATE;
    82. }
    83.  
    84. std::cout << "current state: " << currentState << "\n" << std::endl;
    85.  
    86. return currentState;
    87. }
    88. };
    89.  
    90. include <qapplication.h>
    91. #include <qtextedit.h>
    92.  
    93. int main(int argc, char **argv)
    94. {
    95. QApplication a(argc, argv);
    96.  
    97. SyntaxHighlighter* sh = new SyntaxHighlighter(&te);
    98. sh->addKeyword("class");
    99. sh->addKeyword("if");
    100. sh->addKeyword("while");
    101. sh->addKeyword("for");
    102.  
    103. te.setText("/*\nfoo\n*/\nclass\nwhile");
    104. te.show();
    105. a.setMainWidget(&te);
    106.  
    107. return a.exec();
    108. }
    To copy to clipboard, switch view to plain text mode 

    The code contains some debug statements that are helpful:
    - the state of the previous par (i.e. endStateOfLastPara)
    - the text to highlight
    - the state value that we're about to return

    Assume this text for the contents of QTextEdit (automatically inserted in the main):

    /*
    foo
    */
    class
    while

    The documentation of highlightParagraph, concerning the returned value says:

    int QSyntaxHighlighter::highlightParagraph ( const QString & text, int endStateOfLastPara )

    "The value you return is up to you. We recommend only returning 0
    (to signify that this paragraph's syntax highlighting does not affect the following paragraph), or a positive integer (to signify that this paragraph has ended in the middle of a paragraph spanning construct)."
    if you modify the line with "foo" (e.g., type a char), you'll see that the previous state is COMMENT_STATE, and that we return COMMENT_STATE (i.e., non 0), however, only this paragraph is highlighted, and this is correct, thus a non 0 returned value does not mean: highlight also the next paragraph; from the documentation above one might have the impression that this is what's
    expected, as a negation of "returning 0 (to signify that this paragraph's syntax highlighting does not affect the following paragraph)".
    However, the behavior is correct (there's no need to highlight the following paragraphs), since the state of the paragraph did not change.

    Now, try to change "/*" into "/**", the comment is correctly highlighted in green (doxy comment), however, one would expect that only the parts containing the comments are highlighted, since nothing has changed for the last two lines.
    Moreover, when highlighting "*/" we return 0 (NORMAL_STATE) "to signify that this paragraph's syntax highlighting does not
    affect the following paragraph".

    However, this does not happen: all the lines down to the end of the document are highlighted again (see the debug info). Thus, the semantics of the 0 seems not to be respected.

    Note that in a medium size document, this causes a lot of overhead.

    Indeed the semantics that is probably used is

    1. if the state of a paragraph did not change (w.r.t. to its own previous state) then don't highlight the paragraphs that follow (and this perfectly makes sense)
    2. if such state changes highlight everything else up to the end of the document

    the second point is completely wrong, and as said above, causes a lot
    of overhead.
    In particular, the semantics "We recommend only returning 0
    (to signify that this paragraph's syntax highlighting does not
    affect the following paragraph)" should be always respected.

    This is also made worse when you insert a new line within the comment environment (try to insert a new line after foo): again, from that line on, the whole document will be highlighted again. This is tragic.

    Is this a known issue in Qt3?

    thanks in advance

  2. #2
    Join Date
    Nov 2008
    Posts
    33
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Strange behavior of QSyntaxHighlighter in Qt3

    Note that qt4 version of QSyntaxHighlighter does this right (relying on
    getCurrentBlockState and setCurrentBlockState). For instance, if you try this code in Qt4 you'll see that only the parts that really need to be highlighted are processed:

    Qt Code:
    1. #include <QApplication>
    2. #include <QSyntaxHighlighter>
    3. #include <QTextEdit>
    4. #include <QTextCharFormat>
    5. #include <iostream>
    6.  
    7. #define NORMAL_STATE 0
    8. #define COMMENT_STATE 1
    9. #define DOXY_STATE 2
    10.  
    11. class SyntaxHighlighter: public QSyntaxHighlighter {
    12. QStringList keywordList;
    13.  
    14. QTextCharFormat keyFormat;
    15. QTextCharFormat commentFormat;
    16. QTextCharFormat commentDoxyFormat;
    17.  
    18. public:
    19. SyntaxHighlighter(QTextEdit* textEdit) :
    20. QSyntaxHighlighter(textEdit) {
    21. QColor keyColor;
    22. QFont keyFont;
    23. QColor commentColor;
    24. QFont commentFont;
    25. QColor commentDoxyColor;
    26. keyFont.setBold(true);
    27. keyFont.setItalic(true);
    28. keyFont.setUnderline(true);
    29. keyColor = Qt::blue;
    30.  
    31. commentColor = Qt::red;
    32. commentDoxyColor = Qt::green;
    33.  
    34. commentFont.setItalic(true);
    35.  
    36. keyFormat.setFont(keyFont);
    37. keyFormat.setForeground(keyColor);
    38. commentFormat.setFont(commentFont);
    39. commentFormat.setForeground(commentColor);
    40. commentDoxyFormat.setFont(commentFont);
    41. commentDoxyFormat.setForeground(commentDoxyColor);
    42. }
    43.  
    44. ~SyntaxHighlighter() {
    45. }
    46.  
    47. void addKeyword(const QString &str) {
    48. keywordList << str;
    49. }
    50.  
    51. void highlightBlock(const QString & text) {
    52. int index = 0;
    53. int count = 0;
    54. QStringList::iterator it = keywordList.begin();
    55.  
    56. int endStateOfLastPara = previousBlockState();
    57. int currentState = endStateOfLastPara;
    58.  
    59. std::cout << "prev par state: " << endStateOfLastPara << std::endl;
    60. std::cout << "text: " << text.toStdString() << std::endl;
    61.  
    62. // Assume the text to not contain keywords
    63. setFormat(0, text.length(), Qt::black);
    64.  
    65. if (endStateOfLastPara == NORMAL_STATE || endStateOfLastPara < 0) {
    66. // keep it simple and assume that comment delimiters appear on a single line
    67. if (text.contains("/**")) {
    68. setFormat(0, text.length(), commentDoxyFormat);
    69. currentState = DOXY_STATE;
    70. } else if (text.contains("/*")) {
    71. setFormat(0, text.length(), commentFormat);
    72. currentState = COMMENT_STATE;
    73. } else {
    74. // Look for keywords
    75. while (it != keywordList.end()) {
    76. count = text.count(*it);
    77. if (count) {
    78. for (int i = 0; i < count; i++) {
    79. index = text.indexOf(*it);
    80. setFormat(index, (*it).length(), keyFormat);
    81. ++index;
    82. }
    83. }
    84. ++it;
    85. }
    86. }
    87. } else if (endStateOfLastPara == COMMENT_STATE) {
    88. setFormat(0, text.length(), commentFormat);
    89. if (text.contains("*/"))
    90. currentState = NORMAL_STATE;
    91. } else if (endStateOfLastPara == DOXY_STATE) {
    92. setFormat(0, text.length(), commentDoxyFormat);
    93. if (text.contains("*/"))
    94. currentState = NORMAL_STATE;
    95. }
    96.  
    97. std::cout << "current state: " << currentState << "\n" << std::endl;
    98.  
    99. setCurrentBlockState(currentState);
    100. }
    101. };
    102.  
    103. #include <QApplication>
    104. #include <QTextEdit>
    105. #include <QMainWindow>
    106.  
    107. int main(int argc, char **argv)
    108. {
    109. QApplication a(argc, argv);
    110. QMainWindow window;
    111. SyntaxHighlighter* sh = new SyntaxHighlighter(&te);
    112. sh->addKeyword("class");
    113. sh->addKeyword("if");
    114. sh->addKeyword("while");
    115. sh->addKeyword("for");
    116.  
    117. window.setCentralWidget(&te);
    118. te.setText("/*\nfoo\n*/\nclass\nwhile");
    119. window.show();
    120.  
    121. return a.exec();
    122. }
    To copy to clipboard, switch view to plain text mode 

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.