kate Library API Documentation

katebuffer.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (c) 2000 Waldo Bastian <bastian@kde.org> 00003 Copyright (C) 2002-2004 Christoph Cullmann <cullmann@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00017 Boston, MA 02111-1307, USA. 00018 */ 00019 00020 #include <sys/types.h> 00021 #include <sys/stat.h> 00022 #include <unistd.h> 00023 00024 #include "katebuffer.h" 00025 #include "katebuffer.moc" 00026 00027 #include "katedocument.h" 00028 #include "katehighlight.h" 00029 #include "kateconfig.h" 00030 #include "katefactory.h" 00031 00032 #include <kdebug.h> 00033 #include <kglobal.h> 00034 #include <kcharsets.h> 00035 00036 #include <qpopupmenu.h> 00037 #include <qfile.h> 00038 #include <qtextstream.h> 00039 #include <qtimer.h> 00040 #include <qtextcodec.h> 00041 #include <qcstring.h> 00042 00047 static const Q_ULONG KATE_FILE_LOADER_BS = 256 * 1024; 00048 00055 static const Q_ULONG KATE_AVG_BLOCK_SIZE = 2048 * 80; 00056 static const Q_ULONG KATE_MAX_BLOCK_LINES = 2048; 00057 00063 static const uint KATE_HL_LOOKAHEAD = 64; 00064 00070 uint KateBuffer::m_maxLoadedBlocks = 16; 00071 00075 static const uint KATE_MAX_DYNAMIC_CONTEXTS = 512; 00076 00077 void KateBuffer::setMaxLoadedBlocks (uint count) 00078 { 00079 m_maxLoadedBlocks = KMAX ((uint)4, count); 00080 } 00081 00082 class KateFileLoader 00083 { 00084 public: 00085 KateFileLoader (const QString &filename, QTextCodec *codec) 00086 : m_file (filename) 00087 , m_buffer (KMIN (m_file.size(), KATE_FILE_LOADER_BS)) 00088 , m_decoder (codec->makeDecoder()) 00089 , m_position (0) 00090 , m_lastLineStart (0) 00091 , m_eof (false) // default to not eof 00092 , lastWasEndOfLine (true) // at start of file, we had a virtual newline 00093 , lastWasR (false) // we have not found a \r as last char 00094 , m_eol (-1) // no eol type detected atm 00095 { 00096 } 00097 00098 ~KateFileLoader () 00099 { 00100 delete m_decoder; 00101 } 00102 00106 bool open () 00107 { 00108 if (m_file.open (IO_ReadOnly)) 00109 { 00110 int c = m_file.readBlock (m_buffer.data(), m_buffer.size()); 00111 00112 if (c > 0) 00113 m_text = m_decoder->toUnicode (m_buffer, c); 00114 00115 m_eol = m_file.atEnd(); 00116 00117 for (uint i=0; i < m_text.length(); i++) 00118 { 00119 if (m_text[i] == '\n') 00120 { 00121 m_eol = KateDocumentConfig::eolUnix; 00122 break; 00123 } 00124 else if ((m_text[i] == '\r')) 00125 { 00126 if (((i+1) < m_text.length()) && (m_text[i+1] == '\n')) 00127 { 00128 m_eol = KateDocumentConfig::eolDos; 00129 break; 00130 } 00131 else 00132 { 00133 m_eol = KateDocumentConfig::eolMac; 00134 break; 00135 } 00136 } 00137 } 00138 00139 return true; 00140 } 00141 00142 return false; 00143 } 00144 00145 // no new lines around ? 00146 inline bool eof () const { return m_eof && !lastWasEndOfLine && (m_lastLineStart == m_text.length()); } 00147 00148 // eol mode ? autodetected on open(), -1 for no eol found in the first block! 00149 inline int eol () const { return m_eol; } 00150 00151 // read a line, return per reference, only valid until the next readLine call 00152 // or until this object goes to trash !!! 00153 QConstString readLine () 00154 { 00155 while (m_position <= m_text.length()) 00156 { 00157 if (m_position == m_text.length()) 00158 { 00159 // try to load more text if something is around 00160 if (!m_eof) 00161 { 00162 int c = m_file.readBlock (m_buffer.data(), m_buffer.size()); 00163 00164 if (c > 0) 00165 m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart) 00166 + m_decoder->toUnicode (m_buffer, c); 00167 else 00168 m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart); 00169 00170 // is file completly read ? 00171 m_eof = m_file.atEnd(); 00172 00173 // recalc current pos and last pos 00174 m_position -= m_lastLineStart; 00175 m_lastLineStart = 0; 00176 } 00177 00178 // oh oh, end of file, escape ! 00179 if (m_eof && (m_position == m_text.length())) 00180 { 00181 lastWasEndOfLine = false; 00182 00183 QConstString line = QConstString (m_text.unicode()+m_lastLineStart, m_position-m_lastLineStart); 00184 m_lastLineStart = m_position; 00185 00186 return line; 00187 } 00188 } 00189 00190 if (m_text[m_position] == '\n') 00191 { 00192 lastWasEndOfLine = true; 00193 00194 if (lastWasR) 00195 { 00196 m_lastLineStart++; 00197 lastWasR = false; 00198 } 00199 else 00200 { 00201 QConstString line = QConstString (m_text.unicode()+m_lastLineStart, m_position-m_lastLineStart); 00202 m_lastLineStart = m_position+1; 00203 m_position++; 00204 00205 return line; 00206 } 00207 } 00208 else if (m_text[m_position] == '\r') 00209 { 00210 lastWasEndOfLine = true; 00211 lastWasR = true; 00212 00213 QConstString line = QConstString (m_text.unicode()+m_lastLineStart, m_position-m_lastLineStart); 00214 m_lastLineStart = m_position+1; 00215 m_position++; 00216 00217 return line; 00218 } 00219 else 00220 { 00221 lastWasEndOfLine = false; 00222 lastWasR = false; 00223 } 00224 00225 m_position++; 00226 } 00227 00228 return QConstString (m_text.unicode(), 0); 00229 } 00230 00231 private: 00232 QFile m_file; 00233 QByteArray m_buffer; 00234 QTextDecoder *m_decoder; 00235 QString m_text; 00236 uint m_position; 00237 uint m_lastLineStart; 00238 bool m_eof; 00239 bool lastWasEndOfLine; 00240 bool lastWasR; 00241 int m_eol; 00242 }; 00243 00247 KateBuffer::KateBuffer(KateDocument *doc) 00248 : QObject (doc), 00249 editSessionNumber (0), 00250 editIsRunning (false), 00251 editTagLineStart (0xffffffff), 00252 editTagLineEnd (0), 00253 m_doc (doc), 00254 m_lines (0), 00255 m_lastInSyncBlock (0), 00256 m_lastFoundBlock (0), 00257 m_cacheReadError(false), 00258 m_cacheWriteError(false), 00259 m_loadingBorked (false), 00260 m_highlight (0), 00261 m_regionTree (this), 00262 m_tabWidth (8), 00263 m_lineHighlightedMax (0), 00264 m_lineHighlighted (0), 00265 m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS) 00266 { 00267 connect( &m_regionTree,SIGNAL(setLineVisible(unsigned int, bool)), this,SLOT(setLineVisible(unsigned int,bool))); 00268 00269 clear(); 00270 } 00271 00275 KateBuffer::~KateBuffer() 00276 { 00277 // DELETE ALL BLOCKS, will free mem 00278 for (uint i=0; i < m_blocks.size(); i++) 00279 delete m_blocks[i]; 00280 } 00281 00282 void KateBuffer::editStart () 00283 { 00284 editSessionNumber++; 00285 00286 if (editSessionNumber > 1) 00287 return; 00288 00289 editIsRunning = true; 00290 00291 editTagLineStart = 0xffffffff; 00292 editTagLineEnd = 0; 00293 } 00294 00295 void KateBuffer::editEnd () 00296 { 00297 if (editSessionNumber == 0) 00298 return; 00299 00300 editSessionNumber--; 00301 00302 if (editSessionNumber > 0) 00303 return; 00304 00305 // hl update !!! 00306 if ((editTagLineStart <= editTagLineEnd) && (editTagLineEnd <= m_lineHighlighted)) 00307 { 00308 // look one line too far, needed for linecontinue stuff 00309 editTagLineEnd++; 00310 00311 // look one line before, needed nearly 100% only for indentation based folding ! 00312 if (editTagLineStart > 0) 00313 editTagLineStart--; 00314 00315 KateBufBlock *buf2 = 0; 00316 bool needContinue = false; 00317 while ((buf2 = findBlock(editTagLineStart))) 00318 { 00319 needContinue = doHighlight (buf2, 00320 (editTagLineStart > buf2->startLine()) ? editTagLineStart : buf2->startLine(), 00321 (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd, 00322 true); 00323 00324 editTagLineStart = (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd; 00325 00326 if ((editTagLineStart >= m_lines) || (editTagLineStart >= editTagLineEnd)) 00327 break; 00328 } 00329 00330 if (needContinue) 00331 m_lineHighlighted = editTagLineStart; 00332 00333 if (editTagLineStart > m_lineHighlightedMax) 00334 m_lineHighlightedMax = editTagLineStart; 00335 } 00336 else if (editTagLineStart < m_lineHighlightedMax) 00337 m_lineHighlightedMax = editTagLineStart; 00338 00339 editIsRunning = false; 00340 } 00341 00342 void KateBuffer::editTagLine (uint line) 00343 { 00344 if (line < editTagLineStart) 00345 editTagLineStart = line; 00346 00347 if (line > editTagLineEnd) 00348 editTagLineEnd = line; 00349 } 00350 00351 void KateBuffer::editInsertTagLine (uint line) 00352 { 00353 if (line < editTagLineStart) 00354 editTagLineStart = line; 00355 00356 if (line <= editTagLineEnd) 00357 editTagLineEnd++; 00358 00359 if (line > editTagLineEnd) 00360 editTagLineEnd = line; 00361 } 00362 00363 void KateBuffer::editRemoveTagLine (uint line) 00364 { 00365 if (line < editTagLineStart) 00366 editTagLineStart = line; 00367 00368 if (line < editTagLineEnd) 00369 editTagLineEnd--; 00370 00371 if (line > editTagLineEnd) 00372 editTagLineEnd = line; 00373 } 00374 00375 void KateBuffer::clear() 00376 { 00377 m_regionTree.clear(); 00378 00379 // cleanup the blocks 00380 for (uint i=0; i < m_blocks.size(); i++) 00381 delete m_blocks[i]; 00382 00383 m_blocks.clear (); 00384 00385 // create a bufblock with one line, we need that, only in openFile we won't have that 00386 KateBufBlock *block = new KateBufBlock(this, 0, 0); 00387 m_blocks.append (block); 00388 00389 // reset the state 00390 m_lines = block->lines(); 00391 m_lastInSyncBlock = 0; 00392 m_lastFoundBlock = 0; 00393 m_cacheWriteError = false; 00394 m_cacheReadError = false; 00395 m_loadingBorked = false; 00396 00397 m_lineHighlightedMax = 0; 00398 m_lineHighlighted = 0; 00399 } 00400 00401 bool KateBuffer::openFile (const QString &m_file) 00402 { 00403 KateFileLoader file (m_file, m_doc->config()->codec()); 00404 00405 bool ok = false; 00406 struct stat sbuf; 00407 if (stat(QFile::encodeName(m_file), &sbuf) == 0) 00408 { 00409 if (S_ISREG(sbuf.st_mode) && file.open()) 00410 ok = true; 00411 } 00412 00413 if (!ok) 00414 { 00415 clear(); 00416 return false; // Error 00417 } 00418 00419 // set eol mode, if a eol char was found in the first 256kb block! 00420 if (file.eol() != -1) 00421 m_doc->config()->setEol (file.eol()); 00422 00423 // flush current content 00424 clear (); 00425 00426 // cleanup the blocks 00427 for (uint i=0; i < m_blocks.size(); i++) 00428 delete m_blocks[i]; 00429 00430 m_blocks.clear (); 00431 00432 // do the real work 00433 KateBufBlock *block = 0; 00434 m_lines = 0; 00435 while (!file.eof() && !m_cacheWriteError) 00436 { 00437 block = new KateBufBlock (this, block, 0, &file); 00438 00439 m_lines = block->endLine (); 00440 00441 if (m_cacheWriteError || (block->lines() == 0)) 00442 { 00443 delete block; 00444 break; 00445 } 00446 else 00447 m_blocks.append (block); 00448 } 00449 00450 // we had a cache write error, this load is really borked ! 00451 if (m_cacheWriteError) 00452 m_loadingBorked = true; 00453 00454 if (m_blocks.isEmpty() || (m_lines == 0)) 00455 { 00456 // file was really empty, clean the buffers + emit the line changed 00457 // loadingBorked will be false for such files, not matter what happened 00458 // before 00459 clear (); 00460 } 00461 else 00462 { 00463 // fix region tree 00464 m_regionTree.fixRoot (m_lines); 00465 } 00466 00467 // if we have no hl or the "None" hl activated, whole file is correct highlighted 00468 // after loading, which wonder ;) 00469 if (!m_highlight || m_highlight->noHighlighting()) 00470 { 00471 m_lineHighlighted = m_lines; 00472 m_lineHighlightedMax = m_lines; 00473 } 00474 00475 return !m_loadingBorked; 00476 } 00477 00478 bool KateBuffer::canEncode () 00479 { 00480 QTextCodec *codec = m_doc->config()->codec(); 00481 00482 kdDebug(13020) << "ENC NAME: " << codec->name() << endl; 00483 00484 // hardcode some unicode encodings which can encode all chars 00485 if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2")) 00486 return true; 00487 00488 for (uint i=0; i < m_lines; i++) 00489 { 00490 if (!codec->canEncode (plainLine(i)->string())) 00491 { 00492 kdDebug(13020) << "STRING LINE: " << plainLine(i)->string() << endl; 00493 kdDebug(13020) << "ENC WORKING: FALSE" << endl; 00494 00495 return false; 00496 } 00497 } 00498 00499 return true; 00500 } 00501 00502 bool KateBuffer::saveFile (const QString &m_file) 00503 { 00504 QFile file (m_file); 00505 QTextStream stream (&file); 00506 00507 if ( !file.open( IO_WriteOnly ) ) 00508 { 00509 return false; // Error 00510 } 00511 00512 QTextCodec *codec = m_doc->config()->codec(); 00513 00514 // disable Unicode headers 00515 stream.setEncoding(QTextStream::RawUnicode); 00516 00517 // this line sets the mapper to the correct codec 00518 stream.setCodec(codec); 00519 00520 QString eol = m_doc->config()->eolString (); 00521 00522 // for tab replacement, initialize only once 00523 uint pos, found, ml, l; 00524 QChar onespace(' '); 00525 QString onetab("\t"); 00526 uint tw = m_doc->config()->tabWidth(); 00527 00528 // Use the document methods 00529 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs || 00530 m_doc->configFlags() & KateDocument::cfRemoveSpaces ) 00531 m_doc->editStart(); 00532 00533 for (uint i=0; i < m_lines; i++) 00534 { 00535 KateTextLine::Ptr textLine = plainLine(i); 00536 00537 if (textLine) 00538 { 00539 // replace tabs if required 00540 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs ) 00541 { 00542 pos = 0; 00543 while ( textLine->searchText( pos, onetab, &found, &ml ) ) 00544 { 00545 l = tw - ( found%tw ); 00546 if ( l ) 00547 { 00548 QString t; 00549 m_doc->editRemoveText( i, found, 1 ); 00550 m_doc->editInsertText( i, found, t.fill(onespace, l) ); // ### anything more efficient? 00551 pos += l-1; 00552 } 00553 } 00554 } 00555 00556 // remove trailing spaces if required 00557 if ( (m_doc->configFlags() & KateDocument::cfRemoveSpaces) && textLine->length() ) 00558 { 00559 pos = textLine->length() - 1; 00560 uint lns = textLine->lastChar(); 00561 if ( lns != pos ) 00562 m_doc->editRemoveText( i, lns + 1, pos - lns ); 00563 } 00564 00565 stream << textLine->string(); 00566 00567 if ((i+1) < m_lines) 00568 stream << eol; 00569 } 00570 } 00571 00572 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs || 00573 m_doc->configFlags() & KateDocument::cfRemoveSpaces ) 00574 m_doc->editEnd(); 00575 00576 file.close (); 00577 00578 m_loadingBorked = false; 00579 00580 return (file.status() == IO_Ok); 00581 } 00582 00583 KateTextLine::Ptr KateBuffer::line_internal (KateBufBlock *buf, uint i) 00584 { 00585 // update hl until this line + max KATE_HL_LOOKAHEAD 00586 KateBufBlock *buf2 = 0; 00587 while ((i >= m_lineHighlighted) && (buf2 = findBlock(m_lineHighlighted))) 00588 { 00589 uint end = kMin(i + KATE_HL_LOOKAHEAD, buf2->endLine()); 00590 00591 doHighlight ( buf2, 00592 kMax(m_lineHighlighted, buf2->startLine()), 00593 end, 00594 false ); 00595 00596 m_lineHighlighted = end; 00597 } 00598 00599 // update hl max 00600 if (m_lineHighlighted > m_lineHighlightedMax) 00601 m_lineHighlightedMax = m_lineHighlighted; 00602 00603 return buf->line (i - buf->startLine()); 00604 } 00605 00606 KateBufBlock *KateBuffer::findBlock_internal (uint i, uint *index) 00607 { 00608 uint lastLine = m_blocks[m_lastInSyncBlock]->endLine (); 00609 00610 if (lastLine > i) // we are in a allready known area ! 00611 { 00612 while (true) 00613 { 00614 KateBufBlock *buf = m_blocks[m_lastFoundBlock]; 00615 00616 if ( (buf->startLine() <= i) 00617 && (buf->endLine() > i) ) 00618 { 00619 if (index) 00620 (*index) = m_lastFoundBlock; 00621 00622 return m_blocks[m_lastFoundBlock]; 00623 } 00624 00625 if (i < buf->startLine()) 00626 m_lastFoundBlock--; 00627 else 00628 m_lastFoundBlock++; 00629 } 00630 } 00631 else // we need first to resync the startLines ! 00632 { 00633 if ((m_lastInSyncBlock+1) < m_blocks.size()) 00634 m_lastInSyncBlock++; 00635 else 00636 return 0; 00637 00638 for (; m_lastInSyncBlock < m_blocks.size(); m_lastInSyncBlock++) 00639 { 00640 // get next block 00641 KateBufBlock *buf = m_blocks[m_lastInSyncBlock]; 00642 00643 // sync startLine ! 00644 buf->setStartLine (lastLine); 00645 00646 // is it allready the searched block ? 00647 if ((i >= lastLine) && (i < buf->endLine())) 00648 { 00649 // remember this block as last found ! 00650 m_lastFoundBlock = m_lastInSyncBlock; 00651 00652 if (index) 00653 (*index) = m_lastFoundBlock; 00654 00655 return buf; 00656 } 00657 00658 // increase lastLine with blocklinecount 00659 lastLine += buf->lines (); 00660 } 00661 } 00662 00663 // no block found ! 00664 // index will not be set to any useful value in this case ! 00665 return 0; 00666 } 00667 00668 void KateBuffer::changeLine(uint i) 00669 { 00670 KateBufBlock *buf = findBlock(i); 00671 00672 editTagLine (i); 00673 00674 if (buf) 00675 buf->markDirty (); 00676 } 00677 00678 void KateBuffer::insertLine(uint i, KateTextLine::Ptr line) 00679 { 00680 uint index = 0; 00681 KateBufBlock *buf; 00682 if (i == m_lines) 00683 buf = findBlock(i-1, &index); 00684 else 00685 buf = findBlock(i, &index); 00686 00687 if (!buf) 00688 return; 00689 00690 buf->insertLine(i - buf->startLine(), line); 00691 00692 if (m_lineHighlightedMax > i) 00693 m_lineHighlightedMax++; 00694 00695 if (m_lineHighlighted > i) 00696 m_lineHighlighted++; 00697 00698 m_lines++; 00699 00700 // last sync block adjust 00701 if (m_lastInSyncBlock > index) 00702 m_lastInSyncBlock = index; 00703 00704 // last found 00705 if (m_lastInSyncBlock < m_lastFoundBlock) 00706 m_lastFoundBlock = m_lastInSyncBlock; 00707 00708 editInsertTagLine (i); 00709 00710 m_regionTree.lineHasBeenInserted (i); 00711 } 00712 00713 void KateBuffer::removeLine(uint i) 00714 { 00715 uint index = 0; 00716 KateBufBlock *buf = findBlock(i, &index); 00717 00718 if (!buf) 00719 return; 00720 00721 buf->removeLine(i - buf->startLine()); 00722 00723 if (m_lineHighlightedMax > i) 00724 m_lineHighlightedMax--; 00725 00726 if (m_lineHighlighted > i) 00727 m_lineHighlighted--; 00728 00729 m_lines--; 00730 00731 // trash away a empty block 00732 if (buf->lines() == 0) 00733 { 00734 // we need to change which block is last in sync 00735 if (m_lastInSyncBlock >= index) 00736 { 00737 m_lastInSyncBlock = index; 00738 00739 if (buf->next()) 00740 { 00741 if (buf->prev()) 00742 buf->next()->setStartLine (buf->prev()->endLine()); 00743 else 00744 buf->next()->setStartLine (0); 00745 } 00746 } 00747 00748 // cu block ! 00749 delete buf; 00750 m_blocks.erase (m_blocks.begin()+index); 00751 } 00752 else 00753 { 00754 // last sync block adjust 00755 if (m_lastInSyncBlock > index) 00756 m_lastInSyncBlock = index; 00757 } 00758 00759 // last found 00760 if (m_lastInSyncBlock < m_lastFoundBlock) 00761 m_lastFoundBlock = m_lastInSyncBlock; 00762 00763 editRemoveTagLine (i); 00764 00765 m_regionTree.lineHasBeenRemoved (i); 00766 } 00767 00768 void KateBuffer::setTabWidth (uint w) 00769 { 00770 if ((m_tabWidth != w) && (m_tabWidth > 0)) 00771 { 00772 m_tabWidth = w; 00773 00774 if (m_highlight && m_highlight->foldingIndentationSensitive()) 00775 invalidateHighlighting(); 00776 } 00777 } 00778 00779 void KateBuffer::setHighlight(KateHighlighting *highlight) 00780 { 00781 m_highlight = highlight; 00782 invalidateHighlighting(); 00783 } 00784 00785 void KateBuffer::invalidateHighlighting() 00786 { 00787 m_lineHighlightedMax = 0; 00788 m_lineHighlighted = 0; 00789 } 00790 00791 bool KateBuffer::doHighlight(KateBufBlock *buf, uint startLine, uint endLine, bool invalidate) 00792 { 00793 // no hl around, no stuff to do 00794 if (!m_highlight) 00795 return false; 00796 00797 // we tried to start in a line behind this buf block ! 00798 if (startLine >= (buf->startLine()+buf->lines())) 00799 return false; 00800 00801 kdDebug (13020) << "NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl; 00802 kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl; 00803 kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl; 00804 00805 // see if there are too many dynamic contexts; if yes, invalidate HL of all documents 00806 if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts) 00807 { 00808 { 00809 if (KateHlManager::self()->resetDynamicCtxs()) 00810 { 00811 kdDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")" << endl; 00812 00813 // avoid recursive invalidation 00814 KateHlManager::self()->setForceNoDCReset(true); 00815 00816 for (KateDocument *doc = KateFactory::self()->documents()->first(); doc; doc = KateFactory::self()->documents()->next()) 00817 doc->makeAttribs(); 00818 00819 // doHighlight *shall* do his work. After invalidation, some highlight has 00820 // been recalculated, but *maybe not* until endLine ! So we shall force it manually... 00821 KateBufBlock *buf = 0; 00822 while ((endLine > m_lineHighlighted) && (buf = findBlock(m_lineHighlighted))) 00823 { 00824 uint end = kMin(endLine, buf->endLine()); 00825 00826 doHighlight ( buf, 00827 kMax(m_lineHighlighted, buf->startLine()), 00828 end, 00829 false ); 00830 00831 m_lineHighlighted = end; 00832 } 00833 00834 KateHlManager::self()->setForceNoDCReset(false); 00835 00836 return false; 00837 } 00838 else 00839 { 00840 m_maxDynamicContexts *= 2; 00841 kdDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts << endl; 00842 } 00843 } 00844 } 00845 00846 // get the previous line, if we start at the beginning of this block 00847 // take the last line of the previous block 00848 KateTextLine::Ptr prevLine = 0; 00849 00850 if ((startLine == buf->startLine()) && buf->prev() && (buf->prev()->lines() > 0)) 00851 prevLine = buf->prev()->line (buf->prev()->lines() - 1); 00852 else if ((startLine > buf->startLine()) && (startLine <= buf->endLine())) 00853 prevLine = buf->line(startLine - buf->startLine() - 1); 00854 else 00855 prevLine = new KateTextLine (); 00856 00857 // does we need to emit a signal for the folding changes ? 00858 bool codeFoldingUpdate = false; 00859 00860 // here we are atm, start at start line in the block 00861 uint current_line = startLine - buf->startLine(); 00862 00863 // does we need to continue 00864 bool stillcontinue=false; 00865 00866 // loop over the lines of the block, from startline to endline or end of block 00867 // if stillcontinue forces us to do so 00868 while ( (current_line < buf->lines()) 00869 && (stillcontinue || ((current_line + buf->startLine()) <= endLine)) ) 00870 { 00871 // current line 00872 KateTextLine::Ptr textLine = buf->line(current_line); 00873 00874 QMemArray<signed char> foldingList; 00875 bool ctxChanged = false; 00876 00877 m_highlight->doHighlight (prevLine, textLine, &foldingList, &ctxChanged); 00878 00879 // 00880 // indentation sensitive folding 00881 // 00882 bool indentChanged = false; 00883 if (m_highlight->foldingIndentationSensitive()) 00884 { 00885 // get the indentation array of the previous line to start with ! 00886 QMemArray<unsigned short> indentDepth; 00887 indentDepth.duplicate (prevLine->indentationDepthArray()); 00888 00889 // current indentation of this line 00890 uint iDepth = textLine->indentDepth(m_tabWidth); 00891 00892 // this line is empty, beside spaces, use indentation depth of the previous line ! 00893 if (textLine->firstChar() == -1) 00894 { 00895 // do this to get skipped empty lines indent right, which was given in the indenation array 00896 if (!prevLine->indentationDepthArray().isEmpty()) 00897 iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1]; 00898 else 00899 iDepth = prevLine->indentDepth(m_tabWidth); 00900 } 00901 00902 // query the next line indentation, if we are at the end of the block 00903 // use the first line of the next buf block 00904 uint nextLineIndentation = 0; 00905 00906 if ((current_line+1) < buf->lines()) 00907 { 00908 if (buf->line(current_line+1)->firstChar() == -1) 00909 nextLineIndentation = iDepth; 00910 else 00911 nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth); 00912 } 00913 else 00914 { 00915 KateBufBlock *blk = buf->next(); 00916 00917 if (blk && (blk->lines() > 0)) 00918 { 00919 if (blk->line (0)->firstChar() == -1) 00920 nextLineIndentation = iDepth; 00921 else 00922 nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth); 00923 } 00924 } 00925 00926 // recalculate the indentation array for this line, query if we have to add 00927 // a new folding start, this means newIn == true ! 00928 bool newIn = false; 00929 if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth))) 00930 { 00931 indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim); 00932 indentDepth[indentDepth.size()-1] = iDepth; 00933 newIn = true; 00934 } 00935 else 00936 { 00937 for (int z=indentDepth.size()-1; z > -1; z--) 00938 { 00939 if (indentDepth[z] > iDepth) 00940 indentDepth.resize (z, QGArray::SpeedOptim); 00941 else if (indentDepth[z] == iDepth) 00942 break; 00943 else if (indentDepth[z] < iDepth) 00944 { 00945 indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim); 00946 indentDepth[indentDepth.size()-1] = iDepth; 00947 newIn = true; 00948 break; 00949 } 00950 } 00951 } 00952 00953 // just for debugging always true to start with ! 00954 indentChanged = !(indentDepth == textLine->indentationDepthArray()); 00955 00956 // assign the new array to the textline ! 00957 if (indentChanged) 00958 textLine->setIndentationDepth (indentDepth); 00959 00960 // add folding start to the list ! 00961 if (newIn) 00962 { 00963 foldingList.resize (foldingList.size() + 1, QGArray::SpeedOptim); 00964 foldingList[foldingList.size()-1] = 1; 00965 } 00966 00967 // calculate how much end folding symbols must be added to the list ! 00968 // remIn gives you the count of them 00969 uint remIn = 0; 00970 00971 for (int z=indentDepth.size()-1; z > -1; z--) 00972 { 00973 if (indentDepth[z] > nextLineIndentation) 00974 remIn++; 00975 else 00976 break; 00977 } 00978 00979 if (remIn > 0) 00980 { 00981 foldingList.resize (foldingList.size() + remIn, QGArray::SpeedOptim); 00982 00983 for (uint z= foldingList.size()-remIn; z < foldingList.size(); z++) 00984 foldingList[z] = -1; 00985 } 00986 } 00987 00988 bool foldingChanged = !(foldingList == textLine->foldingListArray()); 00989 00990 if (foldingChanged) 00991 textLine->setFoldingList(foldingList); 00992 00993 bool retVal_folding = false; 00994 m_regionTree.updateLine (current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged); 00995 00996 codeFoldingUpdate = codeFoldingUpdate | retVal_folding; 00997 00998 // need we to continue ? 00999 stillcontinue = ctxChanged || indentChanged; 01000 01001 // move around the lines 01002 prevLine = textLine; 01003 01004 // increment line 01005 current_line++; 01006 } 01007 01008 buf->markDirty (); 01009 01010 // tag the changed lines ! 01011 if (invalidate) 01012 emit tagLines (startLine, current_line + buf->startLine()); 01013 01014 // emit that we have changed the folding 01015 if (codeFoldingUpdate) 01016 emit codeFoldingUpdated(); 01017 01018 // if we are at the last line of the block + we still need to continue 01019 // return the need of that ! 01020 return stillcontinue && ((current_line+1) == buf->lines()); 01021 } 01022 01023 void KateBuffer::setLineVisible(unsigned int lineNr, bool visible) 01024 { 01025 KateBufBlock *buf = findBlock(lineNr); 01026 01027 if (!buf) 01028 return; 01029 01030 KateTextLine::Ptr l = buf->line(lineNr - buf->startLine()); 01031 01032 if (l && (l->isVisible () != visible)) 01033 { 01034 l->setVisible(visible); 01035 01036 buf->markDirty (); 01037 } 01038 } 01039 01040 // BEGIN KateBufBlock 01041 01042 KateBufBlock::KateBufBlock ( KateBuffer *parent, KateBufBlock *prev, KateBufBlock *next, 01043 KateFileLoader *stream ) 01044 : m_state (KateBufBlock::stateDirty), 01045 m_startLine (0), 01046 m_lines (0), 01047 m_vmblock (0), 01048 m_vmblockSize (0), 01049 m_parent (parent), 01050 m_prev (prev), 01051 m_next (next), 01052 list (0), 01053 listPrev (0), 01054 listNext (0) 01055 { 01056 // init startline + the next pointers of the neighbour blocks 01057 if (m_prev) 01058 { 01059 m_startLine = m_prev->endLine (); 01060 m_prev->m_next = this; 01061 } 01062 01063 if (m_next) 01064 m_next->m_prev = this; 01065 01066 // we have a stream, use it to fill the block ! 01067 // this can lead to 0 line blocks which are invalid ! 01068 if (stream) 01069 { 01070 // this we lead to either dirty or swapped state 01071 fillBlock (stream); 01072 } 01073 else // init the block if no stream given ! 01074 { 01075 // fill in one empty line ! 01076 KateTextLine::Ptr textLine = new KateTextLine (); 01077 m_stringList.push_back (textLine); 01078 m_lines++; 01079 01080 // if we have allready enough blocks around, swap one 01081 if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks()) 01082 m_parent->m_loadedBlocks.first()->swapOut(); 01083 01084 // we are a new nearly empty dirty block 01085 m_state = KateBufBlock::stateDirty; 01086 m_parent->m_loadedBlocks.append (this); 01087 } 01088 } 01089 01090 KateBufBlock::~KateBufBlock () 01091 { 01092 // sync prev/next pointers 01093 if (m_prev) 01094 m_prev->m_next = m_next; 01095 01096 if (m_next) 01097 m_next->m_prev = m_prev; 01098 01099 // if we have some swapped data allocated, free it now or never 01100 if (m_vmblock) 01101 m_parent->vm()->free(m_vmblock); 01102 01103 // remove me from the list I belong 01104 KateBufBlockList::remove (this); 01105 } 01106 01107 void KateBufBlock::fillBlock (KateFileLoader *stream) 01108 { 01109 // is allready too much stuff around in mem ? 01110 bool swap = m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks(); 01111 01112 QByteArray rawData; 01113 01114 // calcs the approx size for KATE_AVG_BLOCK_SIZE chars ! 01115 if (swap) 01116 rawData.resize ((KATE_AVG_BLOCK_SIZE * sizeof(QChar)) + ((KATE_AVG_BLOCK_SIZE/80) * 8)); 01117 01118 char *buf = rawData.data (); 01119 uint size = 0; 01120 uint blockSize = 0; 01121 while (!stream->eof() && (blockSize < KATE_AVG_BLOCK_SIZE) && (m_lines < KATE_MAX_BLOCK_LINES)) 01122 { 01123 QConstString line = stream->readLine(); 01124 uint length = line.string().length (); 01125 blockSize += length; 01126 01127 if (swap) 01128 { 01129 // create the swapped data on the fly, no need to waste time 01130 // via going over the textline classes and dump them ! 01131 char attr = KateTextLine::flagNoOtherData; 01132 uint pos = size; 01133 01134 // calc new size 01135 size = size + 1 + sizeof(uint) + (sizeof(QChar)*length); 01136 01137 if (size > rawData.size ()) 01138 { 01139 rawData.resize (size); 01140 buf = rawData.data (); 01141 } 01142 01143 memcpy(buf+pos, (char *) &attr, 1); 01144 pos += 1; 01145 01146 memcpy(buf+pos, (char *) &length, sizeof(uint)); 01147 pos += sizeof(uint); 01148 01149 memcpy(buf+pos, (char *) line.string().unicode(), sizeof(QChar)*length); 01150 pos += sizeof(QChar)*length; 01151 } 01152 else 01153 { 01154 KateTextLine::Ptr textLine = new KateTextLine (); 01155 textLine->insertText (0, length, line.string().unicode ()); 01156 m_stringList.push_back (textLine); 01157 } 01158 01159 m_lines++; 01160 } 01161 01162 if (swap) 01163 { 01164 m_vmblock = m_parent->vm()->allocate(size); 01165 m_vmblockSize = size; 01166 01167 if (!rawData.isEmpty()) 01168 { 01169 if (!m_parent->vm()->copyBlock(m_vmblock, rawData.data(), 0, size)) 01170 { 01171 if (m_vmblock) 01172 m_parent->vm()->free(m_vmblock); 01173 01174 m_vmblock = 0; 01175 m_vmblockSize = 0; 01176 01177 m_parent->m_cacheWriteError = true; 01178 } 01179 } 01180 01181 // fine, we are swapped ! 01182 m_state = KateBufBlock::stateSwapped; 01183 } 01184 else 01185 { 01186 // we are a new dirty block without any swap data 01187 m_state = KateBufBlock::stateDirty; 01188 m_parent->m_loadedBlocks.append (this); 01189 } 01190 01191 kdDebug (13020) << "A BLOCK LOADED WITH LINES: " << m_lines << endl; 01192 } 01193 01194 KateTextLine::Ptr KateBufBlock::line(uint i) 01195 { 01196 // take care that the string list is around !!! 01197 if (m_state == KateBufBlock::stateSwapped) 01198 swapIn (); 01199 01200 // LRU 01201 if (!m_parent->m_loadedBlocks.isLast(this)) 01202 m_parent->m_loadedBlocks.append (this); 01203 01204 return m_stringList[i]; 01205 } 01206 01207 void KateBufBlock::insertLine(uint i, KateTextLine::Ptr line) 01208 { 01209 // take care that the string list is around !!! 01210 if (m_state == KateBufBlock::stateSwapped) 01211 swapIn (); 01212 01213 m_stringList.insert (m_stringList.begin()+i, line); 01214 m_lines++; 01215 01216 markDirty (); 01217 } 01218 01219 void KateBufBlock::removeLine(uint i) 01220 { 01221 // take care that the string list is around !!! 01222 if (m_state == KateBufBlock::stateSwapped) 01223 swapIn (); 01224 01225 m_stringList.erase (m_stringList.begin()+i); 01226 m_lines--; 01227 01228 markDirty (); 01229 } 01230 01231 void KateBufBlock::markDirty () 01232 { 01233 if (m_state != KateBufBlock::stateSwapped) 01234 { 01235 // LRU 01236 if (!m_parent->m_loadedBlocks.isLast(this)) 01237 m_parent->m_loadedBlocks.append (this); 01238 01239 if (m_state == KateBufBlock::stateClean) 01240 { 01241 // if we have some swapped data allocated which is dirty, free it now 01242 if (m_vmblock) 01243 m_parent->vm()->free(m_vmblock); 01244 01245 m_vmblock = 0; 01246 m_vmblockSize = 0; 01247 01248 // we are dirty 01249 m_state = KateBufBlock::stateDirty; 01250 } 01251 } 01252 } 01253 01254 void KateBufBlock::swapIn () 01255 { 01256 if (m_state != KateBufBlock::stateSwapped) 01257 return; 01258 01259 QByteArray rawData (m_vmblockSize); 01260 01261 // what to do if that fails ? 01262 if (!m_parent->vm()->copyBlock(rawData.data(), m_vmblock, 0, rawData.size())) 01263 m_parent->m_cacheReadError = true; 01264 01265 // reserve mem, keep realloc away on push_back 01266 m_stringList.reserve (m_lines); 01267 01268 char *buf = rawData.data(); 01269 for (uint i=0; i < m_lines; i++) 01270 { 01271 KateTextLine::Ptr textLine = new KateTextLine (); 01272 buf = textLine->restore (buf); 01273 m_stringList.push_back (textLine); 01274 } 01275 01276 // if we have allready enough blocks around, swap one 01277 if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks()) 01278 m_parent->m_loadedBlocks.first()->swapOut(); 01279 01280 // fine, we are now clean again, save state + append to clean list 01281 m_state = KateBufBlock::stateClean; 01282 m_parent->m_loadedBlocks.append (this); 01283 } 01284 01285 void KateBufBlock::swapOut () 01286 { 01287 if (m_state == KateBufBlock::stateSwapped) 01288 return; 01289 01290 if (m_state == KateBufBlock::stateDirty) 01291 { 01292 bool haveHl = m_parent->m_highlight && !m_parent->m_highlight->noHighlighting(); 01293 01294 // Calculate size. 01295 uint size = 0; 01296 for (uint i=0; i < m_lines; i++) 01297 size += m_stringList[i]->dumpSize (haveHl); 01298 01299 QByteArray rawData (size); 01300 char *buf = rawData.data(); 01301 01302 // Dump textlines 01303 for (uint i=0; i < m_lines; i++) 01304 buf = m_stringList[i]->dump (buf, haveHl); 01305 01306 m_vmblock = m_parent->vm()->allocate(rawData.size()); 01307 m_vmblockSize = rawData.size(); 01308 01309 if (!rawData.isEmpty()) 01310 { 01311 if (!m_parent->vm()->copyBlock(m_vmblock, rawData.data(), 0, rawData.size())) 01312 { 01313 if (m_vmblock) 01314 m_parent->vm()->free(m_vmblock); 01315 01316 m_vmblock = 0; 01317 m_vmblockSize = 0; 01318 01319 m_parent->m_cacheWriteError = true; 01320 01321 return; 01322 } 01323 } 01324 } 01325 01326 m_stringList.clear(); 01327 01328 // we are now swapped out, set state + remove us out of the lists ! 01329 m_state = KateBufBlock::stateSwapped; 01330 KateBufBlockList::remove (this); 01331 } 01332 01333 // END KateBufBlock 01334 01335 // BEGIN KateBufBlockList 01336 01337 KateBufBlockList::KateBufBlockList () 01338 : m_count (0), 01339 m_first (0), 01340 m_last (0) 01341 { 01342 } 01343 01344 void KateBufBlockList::append (KateBufBlock *buf) 01345 { 01346 if (buf->list) 01347 buf->list->removeInternal (buf); 01348 01349 m_count++; 01350 01351 // append a element 01352 if (m_last) 01353 { 01354 m_last->listNext = buf; 01355 01356 buf->listPrev = m_last; 01357 buf->listNext = 0; 01358 01359 m_last = buf; 01360 01361 buf->list = this; 01362 01363 return; 01364 } 01365 01366 // insert the first element 01367 m_last = buf; 01368 m_first = buf; 01369 01370 buf->listPrev = 0; 01371 buf->listNext = 0; 01372 01373 buf->list = this; 01374 } 01375 01376 void KateBufBlockList::removeInternal (KateBufBlock *buf) 01377 { 01378 if (buf->list != this) 01379 return; 01380 01381 m_count--; 01382 01383 if ((buf == m_first) && (buf == m_last)) 01384 { 01385 // last element removed ! 01386 m_first = 0; 01387 m_last = 0; 01388 } 01389 else if (buf == m_first) 01390 { 01391 // first element removed 01392 m_first = buf->listNext; 01393 m_first->listPrev = 0; 01394 } 01395 else if (buf == m_last) 01396 { 01397 // last element removed 01398 m_last = buf->listPrev; 01399 m_last->listNext = 0; 01400 } 01401 else 01402 { 01403 buf->listPrev->listNext = buf->listNext; 01404 buf->listNext->listPrev = buf->listPrev; 01405 } 01406 01407 buf->listPrev = 0; 01408 buf->listNext = 0; 01409 01410 buf->list = 0; 01411 } 01412 01413 // END KateBufBlockList 01414 01415 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:42:42 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003