2 * Copyright Johannes Sixt
3 * This file is licensed under the GNU General Public License Version 2.
4 * See the file COPYING in the toplevel directory of the source directory.
10 #include <QTextStream>
14 #include <QFontDatabase>
16 #include <QContextMenuEvent>
18 #include <QMouseEvent>
19 #include <kiconloader.h>
20 #include <kxmlguiwindow.h>
21 #include <kxmlguifactory.h>
26 SourceWindow::SourceWindow(const QString
& fileName
, QWidget
* parent
) :
27 QPlainTextEdit(parent
),
33 m_lineInfoArea(new LineInfoArea(this))
35 // Center the cursor when moving it into view
36 setCenterOnScroll(true);
39 m_pcinner
= UserIcon("pcinner");
40 m_pcup
= UserIcon("pcup");
41 m_brkena
= UserIcon("brkena");
42 m_brkdis
= UserIcon("brkdis");
43 m_brktmp
= UserIcon("brktmp");
44 m_brkcond
= UserIcon("brkcond");
45 m_brkorph
= UserIcon("brkorph");
46 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont
));
48 setViewportMargins(lineInfoAreaWidth(), 0, 0 ,0);
49 setWordWrapMode(QTextOption::NoWrap
);
50 connect(this, SIGNAL(updateRequest(const QRect
&, int)),
51 m_lineInfoArea
, SLOT(update()));
52 connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(cursorChanged()));
54 // add a syntax highlighter
55 if (QRegExp("\\.(c(pp|c|\\+\\+)?|CC?|h(\\+\\+|h|pp)?|HH?)$").indexIn(m_fileName
) >= 0)
57 m_highlighter
= new HighlightCpp(this);
61 SourceWindow::~SourceWindow()
66 int SourceWindow::lineInfoAreaWidth() const
68 return 3 + m_widthItems
+ m_widthPlus
+ m_widthLineNo
;
71 bool SourceWindow::loadFile()
74 if (!f
.open(QIODevice::ReadOnly
)) {
79 setPlainText(t
.readAll());
83 m_sourceCode
.resize(n
);
84 m_rowToLine
.resize(n
);
85 for (int i
= 0; i
< n
; i
++) {
88 m_lineItems
.resize(n
, 0);
90 // set a font for line numbers
91 m_lineNoFont
= currentCharFormat().font();
92 m_lineNoFont
.setPixelSize(11);
97 void SourceWindow::reloadFile()
100 if (!f
.open(QIODevice::ReadOnly
)) {
101 // open failed; leave alone
105 m_sourceCode
.clear(); /* clear old text */
108 setPlainText(t
.readAll());
111 m_sourceCode
.resize(blockCount());
112 // expanded lines are collapsed: move existing line items up
113 for (size_t i
= 0; i
< m_lineItems
.size(); i
++) {
114 if (m_rowToLine
[i
] != int(i
)) {
115 m_lineItems
[m_rowToLine
[i
]] |= m_lineItems
[i
];
119 // allocate line items
120 m_lineItems
.resize(m_sourceCode
.size(), 0);
122 m_rowToLine
.resize(m_sourceCode
.size());
123 for (size_t i
= 0; i
< m_sourceCode
.size(); i
++)
126 // Highlighting was applied above when the text was inserted into widget,
127 // but at that time m_rowToLine was not corrected, yet, so that lines
128 // that previously were assembly were painted incorrectly.
130 m_highlighter
->rehighlight();
133 void SourceWindow::scrollTo(int lineNo
, const DbgAddr
& address
)
135 if (lineNo
< 0 || lineNo
>= int(m_sourceCode
.size()))
138 int row
= lineToRow(lineNo
, address
);
142 void SourceWindow::scrollToRow(int row
)
144 QTextCursor
cursor(document()->findBlockByNumber(row
));
145 setTextCursor(cursor
);
146 ensureCursorVisible();
149 void SourceWindow::resizeEvent(QResizeEvent
* e
)
151 QPlainTextEdit::resizeEvent(e
);
153 QRect cr
= contentsRect();
154 cr
.setRight(lineInfoAreaWidth());
155 m_lineInfoArea
->setGeometry(cr
);
158 void SourceWindow::drawLineInfoArea(QPainter
* p
, QPaintEvent
* event
)
160 QTextBlock block
= firstVisibleBlock();
162 p
->setFont(m_lineNoFont
);
164 for (; block
.isValid(); block
= block
.next())
166 if (!block
.isVisible())
169 QRect r
= blockBoundingGeometry(block
).translated(contentOffset()).toRect();
170 if (r
.bottom() < event
->rect().top())
171 continue; // skip blocks that are higher than the region being updated
172 else if (r
.top() > event
->rect().bottom())
173 break; // all the following blocks are lower then the region being updated
175 int row
= block
.blockNumber();
176 uchar item
= m_lineItems
[row
];
180 p
->translate(0, r
.top());
183 // enabled breakpoint
184 int y
= (h
- m_brkena
.height())/2;
186 p
->drawPixmap(0,y
,m_brkena
);
188 if (item
& liBPdisabled
) {
189 // disabled breakpoint
190 int y
= (h
- m_brkdis
.height())/2;
192 p
->drawPixmap(0,y
,m_brkdis
);
194 if (item
& liBPtemporary
) {
195 // temporary breakpoint marker
196 int y
= (h
- m_brktmp
.height())/2;
198 p
->drawPixmap(0,y
,m_brktmp
);
200 if (item
& liBPconditional
) {
201 // conditional breakpoint marker
202 int y
= (h
- m_brkcond
.height())/2;
204 p
->drawPixmap(0,y
,m_brkcond
);
206 if (item
& liBPorphan
) {
207 // orphaned breakpoint marker
208 int y
= (h
- m_brkcond
.height())/2;
210 p
->drawPixmap(0,y
,m_brkorph
);
213 // program counter in innermost frame
214 int y
= (h
- m_pcinner
.height())/2;
216 p
->drawPixmap(0,y
,m_pcinner
);
219 // program counter somewhere up the stack
220 int y
= (h
- m_pcup
.height())/2;
222 p
->drawPixmap(0,y
,m_pcup
);
224 p
->translate(m_widthItems
, 0);
225 if (!isRowDisassCode(row
) && m_sourceCode
[rowToLine(row
)].canDisass
) {
229 p
->drawLine(x
-2, y
, x
+2, y
);
230 if (!isRowExpanded(row
)) {
231 p
->drawLine(x
, y
-2, x
, y
+2);
234 p
->translate(m_widthPlus
, 0);
235 if (!isRowDisassCode(row
)) {
236 p
->drawText(0, 0, m_widthLineNo
, h
, Qt::AlignRight
|Qt::AlignVCenter
,
237 QString().setNum(rowToLine(row
)+1));
243 void SourceWindow::updateLineItems(const KDebugger
* dbg
)
245 // clear outdated breakpoints
246 for (int i
= m_lineItems
.size()-1; i
>= 0; i
--) {
247 if (m_lineItems
[i
] & liBPany
) {
248 // check if this breakpoint still exists
249 int line
= rowToLine(i
);
250 TRACE(QString().sprintf("checking for bp at %d", line
));
251 KDebugger::BrkptROIterator bp
= dbg
->breakpointsBegin();
252 for (; bp
!= dbg
->breakpointsEnd(); ++bp
)
254 if (bp
->lineNo
== line
&&
255 fileNameMatches(bp
->fileName
) &&
256 lineToRow(line
, bp
->address
) == i
)
258 // yes it exists; mode is changed below
262 if (bp
== dbg
->breakpointsEnd()) {
263 /* doesn't exist anymore, remove it */
264 m_lineItems
[i
] &= ~liBPany
;
269 // add new breakpoints
270 for (KDebugger::BrkptROIterator bp
= dbg
->breakpointsBegin(); bp
!= dbg
->breakpointsEnd(); ++bp
)
272 if (fileNameMatches(bp
->fileName
)) {
273 TRACE(QString("updating %2:%1").arg(bp
->lineNo
).arg(bp
->fileName
));
275 if (i
< 0 || i
>= int(m_sourceCode
.size()))
277 // compute new line item flags for breakpoint
278 uchar flags
= bp
->enabled
? liBP
: liBPdisabled
;
280 flags
|= liBPtemporary
;
281 if (!bp
->condition
.isEmpty() || bp
->ignoreCount
!= 0)
282 flags
|= liBPconditional
;
283 if (bp
->isOrphaned())
286 int row
= lineToRow(i
, bp
->address
);
287 if ((m_lineItems
[row
] & liBPany
) != flags
) {
288 m_lineItems
[row
] &= ~liBPany
;
289 m_lineItems
[row
] |= flags
;
293 m_lineInfoArea
->update();
296 void SourceWindow::setPC(bool set
, int lineNo
, const DbgAddr
& address
, int frameNo
)
298 if (lineNo
< 0 || lineNo
>= int(m_sourceCode
.size())) {
302 int row
= lineToRow(lineNo
, address
);
304 uchar flag
= frameNo
== 0 ? liPC
: liPCup
;
306 // set only if not already set
307 if ((m_lineItems
[row
] & flag
) == 0) {
308 m_lineItems
[row
] |= flag
;
309 m_lineInfoArea
->update();
312 // clear only if not set
313 if ((m_lineItems
[row
] & flag
) != 0) {
314 m_lineItems
[row
] &= ~flag
;
315 m_lineInfoArea
->update();
320 void SourceWindow::find(const QString
& text
, bool caseSensitive
, FindDirection dir
)
322 ASSERT(dir
== 1 || dir
== -1);
323 QTextDocument::FindFlags flags
= 0;
325 flags
|= QTextDocument::FindCaseSensitively
;
327 flags
|= QTextDocument::FindBackward
;
328 if (QPlainTextEdit::find(text
, flags
))
330 // not found; wrap around
331 QTextCursor
cursor(document());
333 cursor
.movePosition(QTextCursor::End
);
334 cursor
= document()->find(text
, cursor
, flags
);
335 if (!cursor
.isNull())
336 setTextCursor(cursor
);
339 void SourceWindow::infoMousePress(QMouseEvent
* ev
)
341 // we handle left and middle button
342 if (ev
->button() != Qt::LeftButton
&& ev
->button() != Qt::MidButton
)
348 int row
= cursorForPosition(QPoint(0, ev
->y())).blockNumber();
352 if (ev
->x() > m_widthItems
)
354 if (isRowExpanded(row
)) {
355 actionCollapseRow(row
);
357 actionExpandRow(row
);
363 int line
= rowToLine(row
, &sourceRow
);
365 // find address if row is disassembled code
367 if (row
> sourceRow
) {
368 // get offset from source code line
369 int off
= row
- sourceRow
;
370 address
= m_sourceCode
[line
].disassAddr
[off
-1];
373 switch (ev
->button()) {
375 TRACE(QString().sprintf("left-clicked line %d", line
));
376 emit
clickedLeft(m_fileName
, line
, address
,
377 (ev
->modifiers() & Qt::ShiftModifier
) != 0);
380 TRACE(QString().sprintf("mid-clicked row %d", line
));
381 emit
clickedMid(m_fileName
, line
, address
);
387 void SourceWindow::keyPressEvent(QKeyEvent
* ev
)
392 actionExpandRow(textCursor().blockNumber());
395 actionCollapseRow(textCursor().blockNumber());
399 moveCursor(QTextCursor::PreviousBlock
);
403 moveCursor(QTextCursor::NextBlock
);
406 moveCursor(QTextCursor::Start
);
409 moveCursor(QTextCursor::End
);
412 case Qt::Key_PageDown
:
413 top1
= firstVisibleBlock().blockNumber();
416 QPlainTextEdit::keyPressEvent(ev
);
420 case Qt::Key_PageDown
:
421 top2
= firstVisibleBlock().blockNumber();
423 QTextCursor cursor
= textCursor();
425 cursor
.movePosition(QTextCursor::NextBlock
,
426 QTextCursor::MoveAnchor
, top2
-top1
);
428 cursor
.movePosition(QTextCursor::PreviousBlock
,
429 QTextCursor::MoveAnchor
, top1
-top2
);
430 setTextCursor(cursor
);
435 QString
SourceWindow::extendExpr(const QString
&plainText
,
439 QString document
= plainText
.left(wordEnd
);
440 QString word
= plainText
.mid(wordStart
, wordEnd
- wordStart
);
441 QRegExp regex
= QRegExp("(::)?([A-Za-z_]{1}\\w*\\s*(->|\\.|::)\\s*)*" + word
+ "$");
443 #define IDENTIFIER_MAX_SIZE 256
444 // cut the document to reduce size of string to scan
445 // because of this only identifiefs of length <= IDENTIFIER_MAX_SIZE are supported
446 if (document
.length() > IDENTIFIER_MAX_SIZE
) {
447 document
.right(IDENTIFIER_MAX_SIZE
);
450 const int index
= regex
.indexIn(document
);
454 TRACE("No match, returning " + word
);
458 const int length
= regex
.matchedLength();
460 word
= plainText
.mid(index
, length
);
461 TRACE("Matched, returning " + word
);
467 bool SourceWindow::wordAtPoint(const QPoint
& p
, QString
& word
, QRect
& r
)
469 QTextCursor cursor
= cursorForPosition(viewport()->mapFrom(this, p
));
473 cursor
.select(QTextCursor::WordUnderCursor
);
474 word
= cursor
.selectedText();
479 // keep only letters and digits
480 QRegExp
w("[A-Za-z_]{1}[\\dA-Za-z_]*");
481 if (w
.indexIn(word
) < 0)
488 // if cpp highlighter is enabled - c/c++ file is being displayed
490 // check that word is not a c/c++ keyword
491 if (m_highlighter
->isCppKeyword(word
))
494 // TODO check that cursor is on top of a string literal
495 // and don't display any tooltips in this case
497 // try to extend selected word under the cursor to get a full variable name
498 word
= extendExpr(cursor
.document()->toPlainText(),
499 cursor
.selectionStart(),
500 cursor
.selectionEnd());
511 void SourceWindow::changeEvent(QEvent
* ev
)
513 switch (ev
->type()) {
514 case QEvent::ApplicationFontChange
:
515 case QEvent::FontChange
:
516 setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont
));
521 QPlainTextEdit::changeEvent(ev
);
525 * Two file names (possibly full paths) match if the last parts - the file
528 bool SourceWindow::fileNameMatches(const QString
& other
)
530 return QFileInfo(other
).fileName() == QFileInfo(m_fileName
).fileName();
533 void SourceWindow::disassembled(int lineNo
, const std::list
<DisassembledCode
>& disass
)
535 TRACE("disassembled line " + QString().setNum(lineNo
));
536 if (lineNo
< 0 || lineNo
>= int(m_sourceCode
.size()))
539 SourceLine
& sl
= m_sourceCode
[lineNo
];
541 // copy disassembled code and its addresses
542 sl
.disass
.resize(disass
.size());
543 sl
.disassAddr
.resize(disass
.size());
544 sl
.canDisass
= !disass
.empty();
546 for (std::list
<DisassembledCode
>::const_iterator c
= disass
.begin(); c
!= disass
.end(); ++c
, ++i
)
548 QString code
= c
->code
;
549 while (code
.endsWith("\n"))
550 code
.truncate(code
.length()-1);
551 sl
.disass
[i
] = c
->address
.asString() + ' ' + code
;
552 sl
.disassAddr
[i
] = c
->address
;
555 int row
= lineToRow(lineNo
);
559 // clear expansion marker
560 m_lineInfoArea
->update();
564 int SourceWindow::rowToLine(int row
, int* sourceRow
)
566 int line
= row
>= 0 ? m_rowToLine
[row
] : -1;
567 if (sourceRow
!= 0) {
568 // search back until we hit the first entry with the current line number
569 while (row
> 0 && m_rowToLine
[row
-1] == line
)
577 * Rows showing diassembled code have the same line number as the
578 * corresponding source code line number. Therefore, the line numbers in
579 * m_rowToLine are monotonically increasing with blocks of equal line
580 * numbers for a source line and its disassembled code that follows it.
582 * Hence, m_rowToLine always obeys the following condition:
584 * m_rowToLine[i] <= i
587 int SourceWindow::lineToRow(int line
)
589 // line is zero-based!
591 assert(line
< int(m_rowToLine
.size()));
593 // quick test for common case
594 if (line
< 0 || m_rowToLine
[line
] == line
)
597 assert(m_rowToLine
[line
] < line
);
600 * Binary search between row == line and end of list. In the loop below
601 * we use the fact that the line numbers m_rowToLine do not contain
605 int h
= m_rowToLine
.size();
606 while (l
< h
&& m_rowToLine
[l
] != line
)
608 assert(h
== int(m_rowToLine
.size()) || m_rowToLine
[l
] < m_rowToLine
[h
]);
611 * We want to round down the midpoint so that we find the
612 * lowest row that belongs to the line we seek.
615 if (m_rowToLine
[mid
] <= line
)
620 // Found! Result is in l:
621 assert(m_rowToLine
[l
] == line
);
624 * We might not have hit the lowest index for the line.
626 while (l
> 0 && m_rowToLine
[l
-1] == line
)
632 int SourceWindow::lineToRow(int line
, const DbgAddr
& address
)
634 int row
= lineToRow(line
);
635 if (isRowExpanded(row
)) {
636 row
+= m_sourceCode
[line
].findAddressRowOffset(address
);
641 bool SourceWindow::isRowExpanded(int row
)
644 return row
< int(m_rowToLine
.size())-1 &&
645 m_rowToLine
[row
] == m_rowToLine
[row
+1];
648 bool SourceWindow::isRowDisassCode(int row
)
650 return row
> 0 && row
< int(m_rowToLine
.size()) &&
651 m_rowToLine
[row
] == m_rowToLine
[row
-1];
654 void SourceWindow::expandRow(int row
)
656 TRACE("expanding row " + QString().setNum(row
));
657 // get disassembled code
658 int line
= rowToLine(row
);
659 const std::vector
<QString
>& disass
= m_sourceCode
[line
].disass
;
661 // remove PC (must be set again in slot of signal expanded())
662 m_lineItems
[row
] &= ~(liPC
|liPCup
);
665 setUpdatesEnabled(false);
667 m_rowToLine
.insert(m_rowToLine
.begin()+row
, disass
.size(), line
);
668 m_lineItems
.insert(m_lineItems
.begin()+row
, disass
.size(), 0);
670 QTextCursor
cursor(document()->findBlockByNumber(row
));
671 for (size_t i
= 0; i
< disass
.size(); i
++) {
672 cursor
.insertText(disass
[i
]);
673 cursor
.insertBlock();
675 setUpdatesEnabled(true);
677 emit
expanded(line
); /* must set PC */
680 void SourceWindow::collapseRow(int row
)
682 TRACE("collapsing row " + QString().setNum(row
));
683 int line
= rowToLine(row
);
685 // find end of this block
687 while (end
< int(m_rowToLine
.size()) && m_rowToLine
[end
] == m_rowToLine
[row
]) {
691 setUpdatesEnabled(false);
692 QTextCursor
cursor(document()->findBlockByNumber(end
-1));
693 while (--end
>= row
) {
694 m_rowToLine
.erase(m_rowToLine
.begin()+end
);
695 m_lineItems
.erase(m_lineItems
.begin()+end
);
696 cursor
.select(QTextCursor::BlockUnderCursor
);
697 cursor
.removeSelectedText();
699 setUpdatesEnabled(true);
701 emit
collapsed(line
);
704 void SourceWindow::activeLine(int& line
, DbgAddr
& address
)
706 int row
= textCursor().blockNumber();
709 line
= rowToLine(row
, &sourceRow
);
710 if (row
> sourceRow
) {
711 int off
= row
- sourceRow
; /* offset from source line */
712 address
= m_sourceCode
[line
].disassAddr
[off
-1];
717 * Returns the offset from the line displaying the source code to
718 * the line containing the specified address. If the address is not
719 * found, 0 is returned.
721 int SourceWindow::SourceLine::findAddressRowOffset(const DbgAddr
& address
) const
723 if (address
.isEmpty())
726 for (size_t i
= 0; i
< disassAddr
.size(); i
++) {
727 if (disassAddr
[i
] == address
) {
728 // found exact address
731 if (disassAddr
[i
] > address
) {
733 * We have already advanced too far; the address is before this
734 * index, but obviously we haven't found an exact match
735 * earlier. address is somewhere between the displayed
736 * addresses. We return the previous line.
745 void SourceWindow::actionExpandRow(int row
)
747 if (row
< 0 || isRowExpanded(row
) || isRowDisassCode(row
))
751 int line
= rowToLine(row
);
752 const SourceLine
& sl
= m_sourceCode
[line
];
755 if (sl
.disass
.size() == 0) {
756 emit
disassemble(m_fileName
, line
);
762 void SourceWindow::actionCollapseRow(int row
)
764 if (row
< 0 || !isRowExpanded(row
) || isRowDisassCode(row
))
770 void SourceWindow::setTabWidth(int numChars
)
774 QFontMetrics
fm(document()->defaultFont());
776 int w
= fm
.width(s
.fill('x', numChars
));
780 void SourceWindow::cursorChanged()
782 QList
<QTextEdit::ExtraSelection
> extraSelections
;
783 QTextEdit::ExtraSelection selection
;
785 selection
.format
.setBackground(QColor("#E7E7E7"));
786 selection
.format
.setProperty(QTextFormat::FullWidthSelection
, true);
787 selection
.cursor
= textCursor();
788 selection
.cursor
.clearSelection();
789 extraSelections
.append(selection
);
790 setExtraSelections(extraSelections
);
794 * Show our own context menu.
796 void SourceWindow::contextMenuEvent(QContextMenuEvent
* e
)
798 // get the context menu from the GUI factory
801 top
= top
->parentWidget();
802 while (!top
->isTopLevel());
803 KXmlGuiWindow
* mw
= static_cast<KXmlGuiWindow
*>(top
);
804 QMenu
* m
= static_cast<QMenu
*>(mw
->factory()->container("popup_files", mw
));
806 m
->exec(e
->globalPos());
809 void LineInfoArea::paintEvent(QPaintEvent
* e
)
812 static_cast<SourceWindow
*>(parent())->drawLineInfoArea(&p
, e
);
815 void LineInfoArea::mousePressEvent(QMouseEvent
* ev
)
817 static_cast<SourceWindow
*>(parent())->infoMousePress(ev
);
820 void LineInfoArea::contextMenuEvent(QContextMenuEvent
* e
)
822 static_cast<SourceWindow
*>(parent())->contextMenuEvent(e
);
825 HighlightCpp::HighlightCpp(SourceWindow
* srcWnd
) :
826 QSyntaxHighlighter(srcWnd
->document()),
838 static const QString ckw
[] =
926 void HighlightCpp::highlightBlock(const QString
& text
)
928 int state
= previousBlockState();
929 state
= highlight(text
, state
);
930 setCurrentBlockState(state
);
933 int HighlightCpp::highlight(const QString
& text
, int state
)
935 // highlight assembly lines
936 if (m_srcWnd
->isRowDisassCode(currentBlock().blockNumber()))
938 setFormat(0, text
.length(), Qt::blue
);
942 if (state
== -2) // initial state
945 // check for preprocessor line
946 if (state
== 0 && text
.trimmed().startsWith("#"))
948 setFormat(0, text
.length(), QColor("darkgreen"));
952 // a font for keywords
953 QTextCharFormat identFont
;
954 identFont
.setFontWeight(QFont::Bold
);
957 while (start
< text
.length())
964 setFormat(start
, end
-start
, QColor("gray"));
967 end
= text
.indexOf("*/", start
);
972 setFormat(start
, end
-start
, QColor("gray"));
975 for (end
= start
+1; end
< int(text
.length()); end
++) {
976 if (text
[end
] == '\\') {
977 if (end
< int(text
.length()))
979 } else if (text
[end
] == text
[start
]) {
985 setFormat(start
, end
-start
, QColor("darkred"));
988 for (end
= start
+1; end
< int(text
.length()); end
++) {
989 if (!text
[end
].isLetterOrNumber() && text
[end
] != '_')
993 if (isCppKeyword(text
.mid(start
, end
-start
)))
995 setFormat(start
, end
-start
, identFont
);
997 setFormat(start
, end
-start
, m_srcWnd
->palette().color(QPalette::WindowText
));
1001 for (end
= start
; end
< int(text
.length()); end
++)
1003 if (text
[end
] == '/')
1005 if (end
+1 < int(text
.length())) {
1006 if (text
[end
+1] == '/') {
1007 state
= hlCommentLine
;
1009 } else if (text
[end
+1] == '*') {
1010 state
= hlCommentBlock
;
1015 else if (text
[end
] == '"' || text
[end
] == '\'')
1020 else if ((text
[end
] >= 'A' && text
[end
] <= 'Z') ||
1021 (text
[end
] >= 'a' && text
[end
] <= 'z') ||
1028 setFormat(start
, end
-start
, m_srcWnd
->palette().color(QPalette::WindowText
));
1035 bool HighlightCpp::isCppKeyword(const QString
& word
)
1038 // std::binary_search requires the search list to be sorted
1039 static bool keyword_order_verified
= false;
1040 if (!keyword_order_verified
) {
1041 for (size_t i
= 1; i
< sizeof(ckw
)/sizeof(ckw
[0]); ++i
) {
1042 if (ckw
[i
-1] > ckw
[i
]) {
1043 qDebug("\"%s\" > \"%s\"",
1044 qPrintable(ckw
[i
-1]), qPrintable(ckw
[i
]));
1048 keyword_order_verified
= true;
1052 return std::binary_search(ckw
, ckw
+ sizeof(ckw
)/sizeof(ckw
[0]), word
);
1055 #include "sourcewnd.moc"