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., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, 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 #include "kateautoindent.h" 00032 00033 #include <kdebug.h> 00034 #include <kglobal.h> 00035 #include <kcharsets.h> 00036 00037 #include <qpopupmenu.h> 00038 #include <qfile.h> 00039 #include <qtextstream.h> 00040 #include <qtimer.h> 00041 #include <qtextcodec.h> 00042 #include <qcstring.h> 00043 #include <qdatetime.h> 00044 00049 static const Q_ULONG KATE_FILE_LOADER_BS = 256 * 1024; 00050 00057 static const Q_ULONG KATE_AVG_BLOCK_SIZE = 2048 * 80; 00058 static const Q_ULONG KATE_MAX_BLOCK_LINES = 2048; 00059 00065 static const uint KATE_HL_LOOKAHEAD = 64; 00066 00072 uint KateBuffer::m_maxLoadedBlocks = 16; 00073 00077 static const uint KATE_MAX_DYNAMIC_CONTEXTS = 512; 00078 00079 void KateBuffer::setMaxLoadedBlocks (uint count) 00080 { 00081 m_maxLoadedBlocks = kMax (4U, count); 00082 } 00083 00084 class KateFileLoader 00085 { 00086 public: 00087 KateFileLoader (const QString &filename, QTextCodec *codec, bool removeTrailingSpaces) 00088 : m_file (filename) 00089 , m_buffer (kMin (m_file.size(), KATE_FILE_LOADER_BS)) 00090 , m_codec (codec) 00091 , m_decoder (m_codec->makeDecoder()) 00092 , m_position (0) 00093 , m_lastLineStart (0) 00094 , m_eof (false) // default to not eof 00095 , lastWasEndOfLine (true) // at start of file, we had a virtual newline 00096 , lastWasR (false) // we have not found a \r as last char 00097 , m_eol (-1) // no eol type detected atm 00098 , m_twoByteEncoding (QString(codec->name()) == "ISO-10646-UCS-2") 00099 , m_binary (false) 00100 , m_removeTrailingSpaces (removeTrailingSpaces) 00101 { 00102 kdDebug (13020) << "OPEN USES ENCODING: " << m_codec->name() << endl; 00103 } 00104 00105 ~KateFileLoader () 00106 { 00107 delete m_decoder; 00108 } 00109 00113 bool open () 00114 { 00115 if (m_file.open (IO_ReadOnly)) 00116 { 00117 int c = m_file.readBlock (m_buffer.data(), m_buffer.size()); 00118 00119 if (c > 0) 00120 { 00121 // fix utf16 LE, stolen from khtml ;) 00122 if ((c >= 2) && (m_codec->mibEnum() == 1000) && (m_buffer[1] == 0x00)) 00123 { 00124 // utf16LE, we need to put the decoder in LE mode 00125 char reverseUtf16[3] = {0xFF, 0xFE, 0x00}; 00126 m_decoder->toUnicode(reverseUtf16, 2); 00127 } 00128 00129 processNull (c); 00130 m_text = m_decoder->toUnicode (m_buffer, c); 00131 } 00132 00133 m_eof = (c == -1) || (c == 0) || (m_text.length() == 0) || m_file.atEnd(); 00134 00135 for (uint i=0; i < m_text.length(); i++) 00136 { 00137 if (m_text[i] == '\n') 00138 { 00139 m_eol = KateDocumentConfig::eolUnix; 00140 break; 00141 } 00142 else if ((m_text[i] == '\r')) 00143 { 00144 if (((i+1) < m_text.length()) && (m_text[i+1] == '\n')) 00145 { 00146 m_eol = KateDocumentConfig::eolDos; 00147 break; 00148 } 00149 else 00150 { 00151 m_eol = KateDocumentConfig::eolMac; 00152 break; 00153 } 00154 } 00155 } 00156 00157 return true; 00158 } 00159 00160 return false; 00161 } 00162 00163 // no new lines around ? 00164 inline bool eof () const { return m_eof && !lastWasEndOfLine && (m_lastLineStart == m_text.length()); } 00165 00166 // eol mode ? autodetected on open(), -1 for no eol found in the first block! 00167 inline int eol () const { return m_eol; } 00168 00169 // binary ? 00170 inline bool binary () const { return m_binary; } 00171 00172 // should spaces be ignored at end of line? 00173 inline bool removeTrailingSpaces () const { return m_removeTrailingSpaces; } 00174 00175 // internal unicode data array 00176 inline const QChar *unicode () const { return m_text.unicode(); } 00177 00178 // read a line, return length + offset in unicode data 00179 void readLine (uint &offset, uint &length) 00180 { 00181 length = 0; 00182 offset = 0; 00183 00184 while (m_position <= m_text.length()) 00185 { 00186 if (m_position == m_text.length()) 00187 { 00188 // try to load more text if something is around 00189 if (!m_eof) 00190 { 00191 int c = m_file.readBlock (m_buffer.data(), m_buffer.size()); 00192 00193 uint readString = 0; 00194 if (c > 0) 00195 { 00196 processNull (c); 00197 00198 QString str (m_decoder->toUnicode (m_buffer, c)); 00199 readString = str.length(); 00200 00201 m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart) 00202 + str; 00203 } 00204 else 00205 m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart); 00206 00207 // is file completly read ? 00208 m_eof = (c == -1) || (c == 0) || (readString == 0) || m_file.atEnd(); 00209 00210 // recalc current pos and last pos 00211 m_position -= m_lastLineStart; 00212 m_lastLineStart = 0; 00213 } 00214 00215 // oh oh, end of file, escape ! 00216 if (m_eof && (m_position == m_text.length())) 00217 { 00218 lastWasEndOfLine = false; 00219 00220 // line data 00221 offset = m_lastLineStart; 00222 length = m_position-m_lastLineStart; 00223 00224 m_lastLineStart = m_position; 00225 00226 return; 00227 } 00228 } 00229 00230 if (m_text[m_position] == '\n') 00231 { 00232 lastWasEndOfLine = true; 00233 00234 if (lastWasR) 00235 { 00236 m_lastLineStart++; 00237 lastWasR = false; 00238 } 00239 else 00240 { 00241 // line data 00242 offset = m_lastLineStart; 00243 length = m_position-m_lastLineStart; 00244 00245 m_lastLineStart = m_position+1; 00246 m_position++; 00247 00248 return; 00249 } 00250 } 00251 else if (m_text[m_position] == '\r') 00252 { 00253 lastWasEndOfLine = true; 00254 lastWasR = true; 00255 00256 // line data 00257 offset = m_lastLineStart; 00258 length = m_position-m_lastLineStart; 00259 00260 m_lastLineStart = m_position+1; 00261 m_position++; 00262 00263 return; 00264 } 00265 else 00266 { 00267 lastWasEndOfLine = false; 00268 lastWasR = false; 00269 } 00270 00271 m_position++; 00272 } 00273 } 00274 00275 // this nice methode will kill all 0 bytes (or double bytes) 00276 // and remember if this was a binary or not ;) 00277 void processNull (uint length) 00278 { 00279 if (m_twoByteEncoding) 00280 { 00281 for (uint i=1; i < length; i+=2) 00282 { 00283 if ((m_buffer[i] == 0) && (m_buffer[i-1] == 0)) 00284 { 00285 m_binary = true; 00286 m_buffer[i] = ' '; 00287 } 00288 } 00289 } 00290 else 00291 { 00292 for (uint i=0; i < length; i++) 00293 { 00294 if (m_buffer[i] == 0) 00295 { 00296 m_binary = true; 00297 m_buffer[i] = ' '; 00298 } 00299 } 00300 } 00301 } 00302 00303 private: 00304 QFile m_file; 00305 QByteArray m_buffer; 00306 QTextCodec *m_codec; 00307 QTextDecoder *m_decoder; 00308 QString m_text; 00309 uint m_position; 00310 uint m_lastLineStart; 00311 bool m_eof; 00312 bool lastWasEndOfLine; 00313 bool lastWasR; 00314 int m_eol; 00315 bool m_twoByteEncoding; 00316 bool m_binary; 00317 bool m_removeTrailingSpaces; 00318 }; 00319 00323 KateBuffer::KateBuffer(KateDocument *doc) 00324 : QObject (doc), 00325 editSessionNumber (0), 00326 editIsRunning (false), 00327 editTagLineStart (0xffffffff), 00328 editTagLineEnd (0), 00329 editTagLineFrom (false), 00330 editChangesDone (false), 00331 m_doc (doc), 00332 m_lines (0), 00333 m_lastInSyncBlock (0), 00334 m_lastFoundBlock (0), 00335 m_cacheReadError(false), 00336 m_cacheWriteError(false), 00337 m_loadingBorked (false), 00338 m_binary (false), 00339 m_highlight (0), 00340 m_regionTree (this), 00341 m_tabWidth (8), 00342 m_lineHighlightedMax (0), 00343 m_lineHighlighted (0), 00344 m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS) 00345 { 00346 clear(); 00347 } 00348 00352 KateBuffer::~KateBuffer() 00353 { 00354 // DELETE ALL BLOCKS, will free mem 00355 for (uint i=0; i < m_blocks.size(); i++) 00356 delete m_blocks[i]; 00357 00358 // release HL 00359 if (m_highlight) 00360 m_highlight->release(); 00361 } 00362 00363 void KateBuffer::editStart () 00364 { 00365 editSessionNumber++; 00366 00367 if (editSessionNumber > 1) 00368 return; 00369 00370 editIsRunning = true; 00371 00372 editTagLineStart = 0xffffffff; 00373 editTagLineEnd = 0; 00374 editTagLineFrom = false; 00375 00376 editChangesDone = false; 00377 } 00378 00379 void KateBuffer::editEnd () 00380 { 00381 if (editSessionNumber == 0) 00382 return; 00383 00384 editSessionNumber--; 00385 00386 if (editSessionNumber > 0) 00387 return; 00388 00389 if (editChangesDone) 00390 { 00391 // hl update !!! 00392 if ( m_highlight && !m_highlight->noHighlighting() 00393 && (editTagLineStart <= editTagLineEnd) 00394 && (editTagLineEnd <= m_lineHighlighted)) 00395 { 00396 // look one line too far, needed for linecontinue stuff 00397 editTagLineEnd++; 00398 00399 // look one line before, needed nearly 100% only for indentation based folding ! 00400 if (editTagLineStart > 0) 00401 editTagLineStart--; 00402 00403 KateBufBlock *buf2 = 0; 00404 bool needContinue = false; 00405 while ((buf2 = findBlock(editTagLineStart))) 00406 { 00407 needContinue = doHighlight (buf2, 00408 (editTagLineStart > buf2->startLine()) ? editTagLineStart : buf2->startLine(), 00409 (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd, 00410 true); 00411 00412 editTagLineStart = (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd; 00413 00414 if ((editTagLineStart >= m_lines) || (editTagLineStart >= editTagLineEnd)) 00415 break; 00416 } 00417 00418 if (needContinue) 00419 m_lineHighlighted = editTagLineStart; 00420 00421 if (editTagLineStart > m_lineHighlightedMax) 00422 m_lineHighlightedMax = editTagLineStart; 00423 } 00424 else if (editTagLineStart < m_lineHighlightedMax) 00425 m_lineHighlightedMax = editTagLineStart; 00426 } 00427 00428 editIsRunning = false; 00429 } 00430 00431 void KateBuffer::clear() 00432 { 00433 m_regionTree.clear(); 00434 00435 // cleanup the blocks 00436 for (uint i=0; i < m_blocks.size(); i++) 00437 delete m_blocks[i]; 00438 00439 m_blocks.clear (); 00440 00441 // create a bufblock with one line, we need that, only in openFile we won't have that 00442 KateBufBlock *block = new KateBufBlock(this, 0, 0); 00443 m_blocks.append (block); 00444 00445 // reset the state 00446 m_lines = block->lines(); 00447 m_lastInSyncBlock = 0; 00448 m_lastFoundBlock = 0; 00449 m_cacheWriteError = false; 00450 m_cacheReadError = false; 00451 m_loadingBorked = false; 00452 m_binary = false; 00453 00454 m_lineHighlightedMax = 0; 00455 m_lineHighlighted = 0; 00456 } 00457 00458 bool KateBuffer::openFile (const QString &m_file) 00459 { 00460 KateFileLoader file (m_file, m_doc->config()->codec(), m_doc->configFlags() & KateDocument::cfRemoveSpaces); 00461 00462 bool ok = false; 00463 struct stat sbuf; 00464 if (stat(QFile::encodeName(m_file), &sbuf) == 0) 00465 { 00466 if (S_ISREG(sbuf.st_mode) && file.open()) 00467 ok = true; 00468 } 00469 00470 if (!ok) 00471 { 00472 clear(); 00473 return false; // Error 00474 } 00475 00476 // set eol mode, if a eol char was found in the first 256kb block and we allow this at all! 00477 if (m_doc->config()->allowEolDetection() && (file.eol() != -1)) 00478 m_doc->config()->setEol (file.eol()); 00479 00480 // flush current content 00481 clear (); 00482 00483 // cleanup the blocks 00484 for (uint i=0; i < m_blocks.size(); i++) 00485 delete m_blocks[i]; 00486 00487 m_blocks.clear (); 00488 00489 // do the real work 00490 KateBufBlock *block = 0; 00491 m_lines = 0; 00492 while (!file.eof() && !m_cacheWriteError) 00493 { 00494 block = new KateBufBlock (this, block, 0, &file); 00495 00496 m_lines = block->endLine (); 00497 00498 if (m_cacheWriteError || (block->lines() == 0)) 00499 { 00500 delete block; 00501 break; 00502 } 00503 else 00504 m_blocks.append (block); 00505 } 00506 00507 // we had a cache write error, this load is really borked ! 00508 if (m_cacheWriteError) 00509 m_loadingBorked = true; 00510 00511 if (m_blocks.isEmpty() || (m_lines == 0)) 00512 { 00513 // file was really empty, clean the buffers + emit the line changed 00514 // loadingBorked will be false for such files, not matter what happened 00515 // before 00516 clear (); 00517 } 00518 else 00519 { 00520 // fix region tree 00521 m_regionTree.fixRoot (m_lines); 00522 } 00523 00524 // if we have no hl or the "None" hl activated, whole file is correct highlighted 00525 // after loading, which wonder ;) 00526 if (!m_highlight || m_highlight->noHighlighting()) 00527 { 00528 m_lineHighlighted = m_lines; 00529 m_lineHighlightedMax = m_lines; 00530 } 00531 00532 // binary? 00533 m_binary = file.binary (); 00534 00535 kdDebug (13020) << "LOADING DONE" << endl; 00536 00537 return !m_loadingBorked; 00538 } 00539 00540 bool KateBuffer::canEncode () 00541 { 00542 QTextCodec *codec = m_doc->config()->codec(); 00543 00544 kdDebug(13020) << "ENC NAME: " << codec->name() << endl; 00545 00546 // hardcode some unicode encodings which can encode all chars 00547 if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2")) 00548 return true; 00549 00550 for (uint i=0; i < m_lines; i++) 00551 { 00552 if (!codec->canEncode (plainLine(i)->string())) 00553 { 00554 kdDebug(13020) << "STRING LINE: " << plainLine(i)->string() << endl; 00555 kdDebug(13020) << "ENC WORKING: FALSE" << endl; 00556 00557 return false; 00558 } 00559 } 00560 00561 return true; 00562 } 00563 00564 bool KateBuffer::saveFile (const QString &m_file) 00565 { 00566 QFile file (m_file); 00567 QTextStream stream (&file); 00568 00569 if ( !file.open( IO_WriteOnly ) ) 00570 { 00571 return false; // Error 00572 } 00573 00574 QTextCodec *codec = m_doc->config()->codec(); 00575 00576 // disable Unicode headers 00577 stream.setEncoding(QTextStream::RawUnicode); 00578 00579 // this line sets the mapper to the correct codec 00580 stream.setCodec(codec); 00581 00582 // our loved eol string ;) 00583 QString eol = m_doc->config()->eolString (); 00584 00585 // should we strip spaces? 00586 bool removeTrailingSpaces = m_doc->configFlags() & KateDocument::cfRemoveSpaces; 00587 00588 // just dump the lines out ;) 00589 for (uint i=0; i < m_lines; i++) 00590 { 00591 KateTextLine::Ptr textline = plainLine(i); 00592 00593 // strip spaces 00594 if (removeTrailingSpaces) 00595 { 00596 int lastChar = textline->lastChar(); 00597 00598 if (lastChar > -1) 00599 { 00600 stream << QConstString (textline->text(), lastChar+1).string(); 00601 } 00602 } 00603 else // simple, dump the line 00604 stream << textline->string(); 00605 00606 if ((i+1) < m_lines) 00607 stream << eol; 00608 } 00609 00610 file.close (); 00611 00612 m_loadingBorked = false; 00613 00614 return (file.status() == IO_Ok); 00615 } 00616 00617 KateTextLine::Ptr KateBuffer::line_internal (KateBufBlock *buf, uint i) 00618 { 00619 // update hl until this line + max KATE_HL_LOOKAHEAD 00620 KateBufBlock *buf2 = 0; 00621 while ((i >= m_lineHighlighted) && (buf2 = findBlock(m_lineHighlighted))) 00622 { 00623 uint end = kMin(i + KATE_HL_LOOKAHEAD, buf2->endLine()); 00624 00625 doHighlight ( buf2, 00626 kMax(m_lineHighlighted, buf2->startLine()), 00627 end, 00628 false ); 00629 00630 m_lineHighlighted = end; 00631 } 00632 00633 // update hl max 00634 if (m_lineHighlighted > m_lineHighlightedMax) 00635 m_lineHighlightedMax = m_lineHighlighted; 00636 00637 return buf->line (i - buf->startLine()); 00638 } 00639 00640 KateBufBlock *KateBuffer::findBlock_internal (uint i, uint *index) 00641 { 00642 uint lastLine = m_blocks[m_lastInSyncBlock]->endLine (); 00643 00644 if (lastLine > i) // we are in a allready known area ! 00645 { 00646 while (true) 00647 { 00648 KateBufBlock *buf = m_blocks[m_lastFoundBlock]; 00649 00650 if ( (buf->startLine() <= i) 00651 && (buf->endLine() > i) ) 00652 { 00653 if (index) 00654 (*index) = m_lastFoundBlock; 00655 00656 return m_blocks[m_lastFoundBlock]; 00657 } 00658 00659 if (i < buf->startLine()) 00660 m_lastFoundBlock--; 00661 else 00662 m_lastFoundBlock++; 00663 } 00664 } 00665 else // we need first to resync the startLines ! 00666 { 00667 if ((m_lastInSyncBlock+1) < m_blocks.size()) 00668 m_lastInSyncBlock++; 00669 else 00670 return 0; 00671 00672 for (; m_lastInSyncBlock < m_blocks.size(); m_lastInSyncBlock++) 00673 { 00674 // get next block 00675 KateBufBlock *buf = m_blocks[m_lastInSyncBlock]; 00676 00677 // sync startLine ! 00678 buf->setStartLine (lastLine); 00679 00680 // is it allready the searched block ? 00681 if ((i >= lastLine) && (i < buf->endLine())) 00682 { 00683 // remember this block as last found ! 00684 m_lastFoundBlock = m_lastInSyncBlock; 00685 00686 if (index) 00687 (*index) = m_lastFoundBlock; 00688 00689 return buf; 00690 } 00691 00692 // increase lastLine with blocklinecount 00693 lastLine += buf->lines (); 00694 } 00695 } 00696 00697 // no block found ! 00698 // index will not be set to any useful value in this case ! 00699 return 0; 00700 } 00701 00702 void KateBuffer::changeLine(uint i) 00703 { 00704 KateBufBlock *buf = findBlock(i); 00705 00706 if (!buf) 00707 return; 00708 00709 // mark this block dirty 00710 buf->markDirty (); 00711 00712 // mark buffer changed 00713 editChangesDone = true; 00714 00715 // tag this line as changed 00716 if (i < editTagLineStart) 00717 editTagLineStart = i; 00718 00719 if (i > editTagLineEnd) 00720 editTagLineEnd = i; 00721 } 00722 00723 void KateBuffer::insertLine(uint i, KateTextLine::Ptr line) 00724 { 00725 uint index = 0; 00726 KateBufBlock *buf; 00727 if (i == m_lines) 00728 buf = findBlock(i-1, &index); 00729 else 00730 buf = findBlock(i, &index); 00731 00732 if (!buf) 00733 return; 00734 00735 buf->insertLine(i - buf->startLine(), line); 00736 00737 if (m_lineHighlightedMax > i) 00738 m_lineHighlightedMax++; 00739 00740 if (m_lineHighlighted > i) 00741 m_lineHighlighted++; 00742 00743 m_lines++; 00744 00745 // last sync block adjust 00746 if (m_lastInSyncBlock > index) 00747 m_lastInSyncBlock = index; 00748 00749 // last found 00750 if (m_lastInSyncBlock < m_lastFoundBlock) 00751 m_lastFoundBlock = m_lastInSyncBlock; 00752 00753 // mark buffer changed 00754 editChangesDone = true; 00755 00756 // tag this line as inserted 00757 if (i < editTagLineStart) 00758 editTagLineStart = i; 00759 00760 if (i <= editTagLineEnd) 00761 editTagLineEnd++; 00762 00763 if (i > editTagLineEnd) 00764 editTagLineEnd = i; 00765 00766 // line inserted 00767 editTagLineFrom = true; 00768 00769 m_regionTree.lineHasBeenInserted (i); 00770 } 00771 00772 void KateBuffer::removeLine(uint i) 00773 { 00774 uint index = 0; 00775 KateBufBlock *buf = findBlock(i, &index); 00776 00777 if (!buf) 00778 return; 00779 00780 buf->removeLine(i - buf->startLine()); 00781 00782 if (m_lineHighlightedMax > i) 00783 m_lineHighlightedMax--; 00784 00785 if (m_lineHighlighted > i) 00786 m_lineHighlighted--; 00787 00788 m_lines--; 00789 00790 // trash away a empty block 00791 if (buf->lines() == 0) 00792 { 00793 // we need to change which block is last in sync 00794 if (m_lastInSyncBlock >= index) 00795 { 00796 m_lastInSyncBlock = index; 00797 00798 if (buf->next()) 00799 { 00800 if (buf->prev()) 00801 buf->next()->setStartLine (buf->prev()->endLine()); 00802 else 00803 buf->next()->setStartLine (0); 00804 } 00805 } 00806 00807 // cu block ! 00808 delete buf; 00809 m_blocks.erase (m_blocks.begin()+index); 00810 00811 // make sure we don't keep a pointer to the deleted block 00812 if( m_lastInSyncBlock >= index ) 00813 m_lastInSyncBlock = index - 1; 00814 } 00815 else 00816 { 00817 // last sync block adjust 00818 if (m_lastInSyncBlock > index) 00819 m_lastInSyncBlock = index; 00820 } 00821 00822 // last found 00823 if (m_lastInSyncBlock < m_lastFoundBlock) 00824 m_lastFoundBlock = m_lastInSyncBlock; 00825 00826 // mark buffer changed 00827 editChangesDone = true; 00828 00829 // tag this line as removed 00830 if (i < editTagLineStart) 00831 editTagLineStart = i; 00832 00833 if (i < editTagLineEnd) 00834 editTagLineEnd--; 00835 00836 if (i > editTagLineEnd) 00837 editTagLineEnd = i; 00838 00839 // line removed 00840 editTagLineFrom = true; 00841 00842 m_regionTree.lineHasBeenRemoved (i); 00843 } 00844 00845 void KateBuffer::setTabWidth (uint w) 00846 { 00847 if ((m_tabWidth != w) && (m_tabWidth > 0)) 00848 { 00849 m_tabWidth = w; 00850 00851 if (m_highlight && m_highlight->foldingIndentationSensitive()) 00852 invalidateHighlighting(); 00853 } 00854 } 00855 00856 void KateBuffer::setHighlight(uint hlMode) 00857 { 00858 KateHighlighting *h = KateHlManager::self()->getHl(hlMode); 00859 00860 // aha, hl will change 00861 if (h != m_highlight) 00862 { 00863 bool invalidate = !h->noHighlighting(); 00864 00865 if (m_highlight) 00866 { 00867 m_highlight->release(); 00868 invalidate = true; 00869 } 00870 00871 h->use(); 00872 00873 // Clear code folding tree (see bug #124102) 00874 m_regionTree.clear(); 00875 m_regionTree.fixRoot(m_lines); 00876 00877 // try to set indentation 00878 if (!h->indentation().isEmpty()) 00879 m_doc->config()->setIndentationMode (KateAutoIndent::modeNumber(h->indentation())); 00880 00881 m_highlight = h; 00882 00883 if (invalidate) 00884 invalidateHighlighting(); 00885 00886 // inform the document that the hl was really changed 00887 // needed to update attributes and more ;) 00888 m_doc->bufferHlChanged (); 00889 } 00890 } 00891 00892 void KateBuffer::invalidateHighlighting() 00893 { 00894 m_lineHighlightedMax = 0; 00895 m_lineHighlighted = 0; 00896 } 00897 00898 00899 void KateBuffer::updatePreviousNotEmptyLine(KateBufBlock *blk,uint current_line,bool addindent,uint deindent) 00900 { 00901 KateTextLine::Ptr textLine; 00902 do { 00903 if (current_line>0) current_line--; 00904 else 00905 { 00906 uint line=blk->startLine()+current_line; 00907 if (line==0) return; 00908 line--; 00909 blk=findBlock(line); 00910 if (!blk) { 00911 kdDebug(13020)<<"updatePreviousNotEmptyLine: block not found, this must not happen"<<endl; 00912 return; 00913 } 00914 current_line=line-blk->startLine(); 00915 } 00916 textLine = blk->line(current_line); 00917 } while (textLine->firstChar()==-1); 00918 kdDebug(13020)<<"updatePreviousNotEmptyLine: updating line:"<<(blk->startLine()+current_line)<<endl; 00919 QMemArray<uint> foldingList=textLine->foldingListArray(); 00920 while ( (foldingList.size()>0) && ( abs(foldingList[foldingList.size()-2])==1)) { 00921 foldingList.resize(foldingList.size()-2,QGArray::SpeedOptim); 00922 } 00923 addIndentBasedFoldingInformation(foldingList,addindent,deindent); 00924 textLine->setFoldingList(foldingList); 00925 bool retVal_folding = false; 00926 m_regionTree.updateLine (current_line + blk->startLine(), &foldingList, &retVal_folding, true,false); 00927 emit tagLines (blk->startLine()+current_line, blk->startLine()+current_line); 00928 } 00929 00930 void KateBuffer::addIndentBasedFoldingInformation(QMemArray<uint> &foldingList,bool addindent,uint deindent) 00931 { 00932 if (addindent) { 00933 //kdDebug(13020)<<"adding indent for line :"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl; 00934 kdDebug(13020)<<"adding ident"<<endl; 00935 foldingList.resize (foldingList.size() + 2, QGArray::SpeedOptim); 00936 foldingList[foldingList.size()-2] = 1; 00937 foldingList[foldingList.size()-1] = 0; 00938 } 00939 kdDebug(13020)<<"DEINDENT: "<<deindent<<endl; 00940 if (deindent > 0) 00941 { 00942 foldingList.resize (foldingList.size() + (deindent*2), QGArray::SpeedOptim); 00943 00944 for (uint z= foldingList.size()-(deindent*2); z < foldingList.size(); z=z+2) 00945 { 00946 foldingList[z] = -1; 00947 foldingList[z+1] = 0; 00948 } 00949 } 00950 } 00951 00952 bool KateBuffer::doHighlight (KateBufBlock *buf, uint startLine, uint endLine, bool invalidate) 00953 { 00954 // no hl around, no stuff to do 00955 if (!m_highlight) 00956 return false; 00957 00958 /*if (m_highlight->foldingIndentationSensitive()) 00959 { 00960 startLine=0; 00961 endLine=50; 00962 }*/ 00963 00964 // we tried to start in a line behind this buf block ! 00965 if (startLine >= (buf->startLine()+buf->lines())) 00966 return false; 00967 00968 //QTime t; 00969 //t.start(); 00970 //kdDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl; 00971 //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl; 00972 //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl; 00973 00974 // see if there are too many dynamic contexts; if yes, invalidate HL of all documents 00975 if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts) 00976 { 00977 { 00978 if (KateHlManager::self()->resetDynamicCtxs()) 00979 { 00980 kdDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")" << endl; 00981 00982 // avoid recursive invalidation 00983 KateHlManager::self()->setForceNoDCReset(true); 00984 00985 for (KateDocument *doc = KateFactory::self()->documents()->first(); doc; doc = KateFactory::self()->documents()->next()) 00986 doc->makeAttribs(); 00987 00988 // doHighlight *shall* do his work. After invalidation, some highlight has 00989 // been recalculated, but *maybe not* until endLine ! So we shall force it manually... 00990 KateBufBlock *buf = 0; 00991 while ((endLine > m_lineHighlighted) && (buf = findBlock(m_lineHighlighted))) 00992 { 00993 uint end = kMin(endLine, buf->endLine()); 00994 00995 doHighlight ( buf, 00996 kMax(m_lineHighlighted, buf->startLine()), 00997 end, 00998 false ); 00999 01000 m_lineHighlighted = end; 01001 } 01002 01003 KateHlManager::self()->setForceNoDCReset(false); 01004 01005 return false; 01006 } 01007 else 01008 { 01009 m_maxDynamicContexts *= 2; 01010 kdDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts << endl; 01011 } 01012 } 01013 } 01014 01015 // get the previous line, if we start at the beginning of this block 01016 // take the last line of the previous block 01017 KateTextLine::Ptr prevLine = 0; 01018 01019 if ((startLine == buf->startLine()) && buf->prev() && (buf->prev()->lines() > 0)) 01020 prevLine = buf->prev()->line (buf->prev()->lines() - 1); 01021 else if ((startLine > buf->startLine()) && (startLine <= buf->endLine())) 01022 prevLine = buf->line(startLine - buf->startLine() - 1); 01023 else 01024 prevLine = new KateTextLine (); 01025 01026 // does we need to emit a signal for the folding changes ? 01027 bool codeFoldingUpdate = false; 01028 01029 // here we are atm, start at start line in the block 01030 uint current_line = startLine - buf->startLine(); 01031 01032 // do we need to continue 01033 bool stillcontinue=false; 01034 bool indentContinueWhitespace=false; 01035 bool indentContinueNextWhitespace=false; 01036 // loop over the lines of the block, from startline to endline or end of block 01037 // if stillcontinue forces us to do so 01038 while ( (current_line < buf->lines()) 01039 && (stillcontinue || ((current_line + buf->startLine()) <= endLine)) ) 01040 { 01041 // current line 01042 KateTextLine::Ptr textLine = buf->line(current_line); 01043 01044 QMemArray<uint> foldingList; 01045 bool ctxChanged = false; 01046 01047 m_highlight->doHighlight (prevLine, textLine, &foldingList, &ctxChanged); 01048 01049 // 01050 // indentation sensitive folding 01051 // 01052 bool indentChanged = false; 01053 if (m_highlight->foldingIndentationSensitive()) 01054 { 01055 // get the indentation array of the previous line to start with ! 01056 QMemArray<unsigned short> indentDepth; 01057 indentDepth.duplicate (prevLine->indentationDepthArray()); 01058 01059 // current indentation of this line 01060 uint iDepth = textLine->indentDepth(m_tabWidth); 01061 if ((current_line+buf->startLine())==0) 01062 { 01063 indentDepth.resize (1, QGArray::SpeedOptim); 01064 indentDepth[0] = iDepth; 01065 } 01066 01067 textLine->setNoIndentBasedFoldingAtStart(prevLine->noIndentBasedFolding()); 01068 // this line is empty, beside spaces, or has indentaion based folding disabled, use indentation depth of the previous line ! 01069 kdDebug(13020)<<"current_line:"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl; 01070 if ( (textLine->firstChar() == -1) || textLine->noIndentBasedFoldingAtStart()) 01071 { 01072 // do this to get skipped empty lines indent right, which was given in the indenation array 01073 if (!prevLine->indentationDepthArray().isEmpty()) 01074 { 01075 iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1]; 01076 kdDebug(13020)<<"reusing old depth as current"<<endl; 01077 } 01078 else 01079 { 01080 iDepth = prevLine->indentDepth(m_tabWidth); 01081 kdDebug(13020)<<"creating indentdepth for previous line"<<endl; 01082 } 01083 } 01084 01085 kdDebug(13020)<<"iDepth:"<<iDepth<<endl; 01086 01087 // query the next line indentation, if we are at the end of the block 01088 // use the first line of the next buf block 01089 uint nextLineIndentation = 0; 01090 bool nextLineIndentationValid=true; 01091 indentContinueNextWhitespace=false; 01092 if ((current_line+1) < buf->lines()) 01093 { 01094 if (buf->line(current_line+1)->firstChar() == -1) 01095 { 01096 nextLineIndentation = iDepth; 01097 indentContinueNextWhitespace=true; 01098 } 01099 else 01100 nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth); 01101 } 01102 else 01103 { 01104 KateBufBlock *blk = buf->next(); 01105 01106 if (blk && (blk->lines() > 0)) 01107 { 01108 if (blk->line (0)->firstChar() == -1) 01109 { 01110 nextLineIndentation = iDepth; 01111 indentContinueNextWhitespace=true; 01112 } 01113 else 01114 nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth); 01115 } 01116 else nextLineIndentationValid=false; 01117 } 01118 01119 if (!textLine->noIndentBasedFoldingAtStart()) { 01120 01121 if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth))) 01122 { 01123 kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl; 01124 indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim); 01125 indentDepth[indentDepth.size()-1] = iDepth; 01126 } else { 01127 if (!indentDepth.isEmpty()) 01128 { 01129 for (int z=indentDepth.size()-1; z > -1; z--) 01130 if (indentDepth[z]>iDepth) 01131 indentDepth.resize(z, QGArray::SpeedOptim); 01132 if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth))) 01133 { 01134 kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl; 01135 indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim); 01136 indentDepth[indentDepth.size()-1] = iDepth; 01137 if (prevLine->firstChar()==-1) { 01138 01139 } 01140 } 01141 } 01142 } 01143 } 01144 01145 if (!textLine->noIndentBasedFolding()) 01146 { 01147 if (nextLineIndentationValid) 01148 { 01149 //if (textLine->firstChar()!=-1) 01150 { 01151 kdDebug(13020)<<"nextLineIndentation:"<<nextLineIndentation<<endl; 01152 bool addindent=false; 01153 uint deindent=0; 01154 if (!indentDepth.isEmpty()) 01155 kdDebug()<<"indentDepth[indentDepth.size()-1]:"<<indentDepth[indentDepth.size()-1]<<endl; 01156 if ((nextLineIndentation>0) && ( indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1]<nextLineIndentation))) 01157 { 01158 kdDebug(13020)<<"addindent==true"<<endl; 01159 addindent=true; 01160 } else { 01161 if ((!indentDepth.isEmpty()) && (indentDepth[indentDepth.size()-1]>nextLineIndentation)) 01162 { 01163 kdDebug(13020)<<"...."<<endl; 01164 for (int z=indentDepth.size()-1; z > -1; z--) 01165 { 01166 kdDebug(13020)<<indentDepth[z]<<" "<<nextLineIndentation<<endl; 01167 if (indentDepth[z]>nextLineIndentation) 01168 deindent++; 01169 } 01170 } 01171 } 01172 /* } 01173 if (textLine->noIndentBasedFolding()) kdDebug(13020)<<"=============================indentation based folding disabled======================"<<endl; 01174 if (!textLine->noIndentBasedFolding()) {*/ 01175 if ((textLine->firstChar()==-1)) { 01176 updatePreviousNotEmptyLine(buf,current_line,addindent,deindent); 01177 codeFoldingUpdate=true; 01178 } 01179 else 01180 { 01181 addIndentBasedFoldingInformation(foldingList,addindent,deindent); 01182 } 01183 } 01184 } 01185 } 01186 indentChanged = !(indentDepth == textLine->indentationDepthArray()); 01187 01188 // assign the new array to the textline ! 01189 if (indentChanged) 01190 textLine->setIndentationDepth (indentDepth); 01191 01192 indentContinueWhitespace=textLine->firstChar()==-1; 01193 } 01194 bool foldingColChanged=false; 01195 bool foldingChanged = false; 01196 if (foldingList.size()!=textLine->foldingListArray().size()) { 01197 foldingChanged=true; 01198 } else { 01199 QMemArray<uint>::ConstIterator it=foldingList.begin(); 01200 QMemArray<uint>::ConstIterator it1=textLine->foldingListArray(); 01201 bool markerType=true; 01202 for(;it!=foldingList.end();++it,++it1) { 01203 if (markerType) { 01204 if ( ((*it)!=(*it1))) { 01205 foldingChanged=true; 01206 foldingColChanged=false; 01207 break; 01208 } 01209 } else { 01210 if ((*it)!=(*it1)) { 01211 foldingColChanged=true; 01212 } 01213 } 01214 markerType=!markerType; 01215 } 01216 } 01217 01218 if (foldingChanged || foldingColChanged) { 01219 textLine->setFoldingList(foldingList); 01220 if (foldingChanged==false){ 01221 textLine->setFoldingColumnsOutdated(textLine->foldingColumnsOutdated() | foldingColChanged); 01222 } else textLine->setFoldingColumnsOutdated(false); 01223 } 01224 bool retVal_folding = false; 01225 //perhaps make en enums out of the change flags 01226 m_regionTree.updateLine (current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged,foldingColChanged); 01227 01228 codeFoldingUpdate = codeFoldingUpdate | retVal_folding; 01229 01230 // need we to continue ? 01231 stillcontinue = ctxChanged || indentChanged || indentContinueWhitespace || indentContinueNextWhitespace; 01232 01233 // move around the lines 01234 prevLine = textLine; 01235 01236 // increment line 01237 current_line++; 01238 } 01239 01240 buf->markDirty (); 01241 01242 // tag the changed lines ! 01243 if (invalidate) 01244 emit tagLines (startLine, current_line + buf->startLine()); 01245 01246 // emit that we have changed the folding 01247 if (codeFoldingUpdate) 01248 emit codeFoldingUpdated(); 01249 01250 //kdDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl; 01251 //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl; 01252 //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl; 01253 //kdDebug (13020) << "TIME TAKEN: " << t.elapsed() << endl; 01254 01255 // if we are at the last line of the block + we still need to continue 01256 // return the need of that ! 01257 return stillcontinue && ((current_line+1) == buf->lines()); 01258 } 01259 01260 void KateBuffer::codeFoldingColumnUpdate(unsigned int lineNr) { 01261 KateTextLine::Ptr line=plainLine(lineNr); 01262 if (!line) return; 01263 if (line->foldingColumnsOutdated()) { 01264 line->setFoldingColumnsOutdated(false); 01265 bool tmp; 01266 QMemArray<uint> folding=line->foldingListArray(); 01267 m_regionTree.updateLine(lineNr,&folding,&tmp,true,false); 01268 } 01269 } 01270 01271 //BEGIN KateBufBlock 01272 01273 KateBufBlock::KateBufBlock ( KateBuffer *parent, KateBufBlock *prev, KateBufBlock *next, 01274 KateFileLoader *stream ) 01275 : m_state (KateBufBlock::stateDirty), 01276 m_startLine (0), 01277 m_lines (0), 01278 m_vmblock (0), 01279 m_vmblockSize (0), 01280 m_parent (parent), 01281 m_prev (prev), 01282 m_next (next), 01283 list (0), 01284 listPrev (0), 01285 listNext (0) 01286 { 01287 // init startline + the next pointers of the neighbour blocks 01288 if (m_prev) 01289 { 01290 m_startLine = m_prev->endLine (); 01291 m_prev->m_next = this; 01292 } 01293 01294 if (m_next) 01295 m_next->m_prev = this; 01296 01297 // we have a stream, use it to fill the block ! 01298 // this can lead to 0 line blocks which are invalid ! 01299 if (stream) 01300 { 01301 // this we lead to either dirty or swapped state 01302 fillBlock (stream); 01303 } 01304 else // init the block if no stream given ! 01305 { 01306 // fill in one empty line ! 01307 KateTextLine::Ptr textLine = new KateTextLine (); 01308 m_stringList.push_back (textLine); 01309 m_lines++; 01310 01311 // if we have allready enough blocks around, swap one 01312 if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks()) 01313 m_parent->m_loadedBlocks.first()->swapOut(); 01314 01315 // we are a new nearly empty dirty block 01316 m_state = KateBufBlock::stateDirty; 01317 m_parent->m_loadedBlocks.append (this); 01318 } 01319 } 01320 01321 KateBufBlock::~KateBufBlock () 01322 { 01323 // sync prev/next pointers 01324 if (m_prev) 01325 m_prev->m_next = m_next; 01326 01327 if (m_next) 01328 m_next->m_prev = m_prev; 01329 01330 // if we have some swapped data allocated, free it now or never 01331 if (m_vmblock) 01332 KateFactory::self()->vm()->free(m_vmblock); 01333 01334 // remove me from the list I belong 01335 KateBufBlockList::remove (this); 01336 } 01337 01338 void KateBufBlock::fillBlock (KateFileLoader *stream) 01339 { 01340 // is allready too much stuff around in mem ? 01341 bool swap = m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks(); 01342 01343 QByteArray rawData; 01344 01345 // calcs the approx size for KATE_AVG_BLOCK_SIZE chars ! 01346 if (swap) 01347 rawData.resize ((KATE_AVG_BLOCK_SIZE * sizeof(QChar)) + ((KATE_AVG_BLOCK_SIZE/80) * 8)); 01348 01349 char *buf = rawData.data (); 01350 uint size = 0; 01351 uint blockSize = 0; 01352 while (!stream->eof() && (blockSize < KATE_AVG_BLOCK_SIZE) && (m_lines < KATE_MAX_BLOCK_LINES)) 01353 { 01354 uint offset = 0, length = 0; 01355 stream->readLine(offset, length); 01356 const QChar *unicodeData = stream->unicode () + offset; 01357 01358 // strip spaces at end of line 01359 if ( stream->removeTrailingSpaces() ) 01360 { 01361 while (length > 0) 01362 { 01363 if (unicodeData[length-1].isSpace()) 01364 --length; 01365 else 01366 break; 01367 } 01368 } 01369 01370 blockSize += length; 01371 01372 if (swap) 01373 { 01374 // create the swapped data on the fly, no need to waste time 01375 // via going over the textline classes and dump them ! 01376 char attr = KateTextLine::flagNoOtherData; 01377 uint pos = size; 01378 01379 // calc new size 01380 size = size + 1 + sizeof(uint) + (sizeof(QChar)*length); 01381 01382 if (size > rawData.size ()) 01383 { 01384 rawData.resize (size); 01385 buf = rawData.data (); 01386 } 01387 01388 memcpy(buf+pos, (char *) &attr, 1); 01389 pos += 1; 01390 01391 memcpy(buf+pos, (char *) &length, sizeof(uint)); 01392 pos += sizeof(uint); 01393 01394 memcpy(buf+pos, (char *) unicodeData, sizeof(QChar)*length); 01395 pos += sizeof(QChar)*length; 01396 } 01397 else 01398 { 01399 KateTextLine::Ptr textLine = new KateTextLine (); 01400 textLine->insertText (0, length, unicodeData); 01401 m_stringList.push_back (textLine); 01402 } 01403 01404 m_lines++; 01405 } 01406 01407 if (swap) 01408 { 01409 m_vmblock = KateFactory::self()->vm()->allocate(size); 01410 m_vmblockSize = size; 01411 01412 if (!rawData.isEmpty()) 01413 { 01414 if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, size)) 01415 { 01416 if (m_vmblock) 01417 KateFactory::self()->vm()->free(m_vmblock); 01418 01419 m_vmblock = 0; 01420 m_vmblockSize = 0; 01421 01422 m_parent->m_cacheWriteError = true; 01423 } 01424 } 01425 01426 // fine, we are swapped ! 01427 m_state = KateBufBlock::stateSwapped; 01428 } 01429 else 01430 { 01431 // we are a new dirty block without any swap data 01432 m_state = KateBufBlock::stateDirty; 01433 m_parent->m_loadedBlocks.append (this); 01434 } 01435 01436 kdDebug (13020) << "A BLOCK LOADED WITH LINES: " << m_lines << endl; 01437 } 01438 01439 KateTextLine::Ptr KateBufBlock::line(uint i) 01440 { 01441 // take care that the string list is around !!! 01442 if (m_state == KateBufBlock::stateSwapped) 01443 swapIn (); 01444 01445 // LRU 01446 if (!m_parent->m_loadedBlocks.isLast(this)) 01447 m_parent->m_loadedBlocks.append (this); 01448 01449 return m_stringList[i]; 01450 } 01451 01452 void KateBufBlock::insertLine(uint i, KateTextLine::Ptr line) 01453 { 01454 // take care that the string list is around !!! 01455 if (m_state == KateBufBlock::stateSwapped) 01456 swapIn (); 01457 01458 m_stringList.insert (m_stringList.begin()+i, line); 01459 m_lines++; 01460 01461 markDirty (); 01462 } 01463 01464 void KateBufBlock::removeLine(uint i) 01465 { 01466 // take care that the string list is around !!! 01467 if (m_state == KateBufBlock::stateSwapped) 01468 swapIn (); 01469 01470 m_stringList.erase (m_stringList.begin()+i); 01471 m_lines--; 01472 01473 markDirty (); 01474 } 01475 01476 void KateBufBlock::markDirty () 01477 { 01478 if (m_state != KateBufBlock::stateSwapped) 01479 { 01480 // LRU 01481 if (!m_parent->m_loadedBlocks.isLast(this)) 01482 m_parent->m_loadedBlocks.append (this); 01483 01484 if (m_state == KateBufBlock::stateClean) 01485 { 01486 // if we have some swapped data allocated which is dirty, free it now 01487 if (m_vmblock) 01488 KateFactory::self()->vm()->free(m_vmblock); 01489 01490 m_vmblock = 0; 01491 m_vmblockSize = 0; 01492 01493 // we are dirty 01494 m_state = KateBufBlock::stateDirty; 01495 } 01496 } 01497 } 01498 01499 void KateBufBlock::swapIn () 01500 { 01501 if (m_state != KateBufBlock::stateSwapped) 01502 return; 01503 01504 QByteArray rawData (m_vmblockSize); 01505 01506 // what to do if that fails ? 01507 if (!KateFactory::self()->vm()->copyBlock(rawData.data(), m_vmblock, 0, rawData.size())) 01508 m_parent->m_cacheReadError = true; 01509 01510 // reserve mem, keep realloc away on push_back 01511 m_stringList.reserve (m_lines); 01512 01513 char *buf = rawData.data(); 01514 for (uint i=0; i < m_lines; i++) 01515 { 01516 KateTextLine::Ptr textLine = new KateTextLine (); 01517 buf = textLine->restore (buf); 01518 m_stringList.push_back (textLine); 01519 } 01520 01521 // if we have allready enough blocks around, swap one 01522 if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks()) 01523 m_parent->m_loadedBlocks.first()->swapOut(); 01524 01525 // fine, we are now clean again, save state + append to clean list 01526 m_state = KateBufBlock::stateClean; 01527 m_parent->m_loadedBlocks.append (this); 01528 } 01529 01530 void KateBufBlock::swapOut () 01531 { 01532 if (m_state == KateBufBlock::stateSwapped) 01533 return; 01534 01535 if (m_state == KateBufBlock::stateDirty) 01536 { 01537 bool haveHl = m_parent->m_highlight && !m_parent->m_highlight->noHighlighting(); 01538 01539 // Calculate size. 01540 uint size = 0; 01541 for (uint i=0; i < m_lines; i++) 01542 size += m_stringList[i]->dumpSize (haveHl); 01543 01544 QByteArray rawData (size); 01545 char *buf = rawData.data(); 01546 01547 // Dump textlines 01548 for (uint i=0; i < m_lines; i++) 01549 buf = m_stringList[i]->dump (buf, haveHl); 01550 01551 m_vmblock = KateFactory::self()->vm()->allocate(rawData.size()); 01552 m_vmblockSize = rawData.size(); 01553 01554 if (!rawData.isEmpty()) 01555 { 01556 if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, rawData.size())) 01557 { 01558 if (m_vmblock) 01559 KateFactory::self()->vm()->free(m_vmblock); 01560 01561 m_vmblock = 0; 01562 m_vmblockSize = 0; 01563 01564 m_parent->m_cacheWriteError = true; 01565 01566 return; 01567 } 01568 } 01569 } 01570 01571 m_stringList.clear(); 01572 01573 // we are now swapped out, set state + remove us out of the lists ! 01574 m_state = KateBufBlock::stateSwapped; 01575 KateBufBlockList::remove (this); 01576 } 01577 01578 //END KateBufBlock 01579 01580 //BEGIN KateBufBlockList 01581 01582 KateBufBlockList::KateBufBlockList () 01583 : m_count (0), 01584 m_first (0), 01585 m_last (0) 01586 { 01587 } 01588 01589 void KateBufBlockList::append (KateBufBlock *buf) 01590 { 01591 if (buf->list) 01592 buf->list->removeInternal (buf); 01593 01594 m_count++; 01595 01596 // append a element 01597 if (m_last) 01598 { 01599 m_last->listNext = buf; 01600 01601 buf->listPrev = m_last; 01602 buf->listNext = 0; 01603 01604 m_last = buf; 01605 01606 buf->list = this; 01607 01608 return; 01609 } 01610 01611 // insert the first element 01612 m_last = buf; 01613 m_first = buf; 01614 01615 buf->listPrev = 0; 01616 buf->listNext = 0; 01617 01618 buf->list = this; 01619 } 01620 01621 void KateBufBlockList::removeInternal (KateBufBlock *buf) 01622 { 01623 if (buf->list != this) 01624 return; 01625 01626 m_count--; 01627 01628 if ((buf == m_first) && (buf == m_last)) 01629 { 01630 // last element removed ! 01631 m_first = 0; 01632 m_last = 0; 01633 } 01634 else if (buf == m_first) 01635 { 01636 // first element removed 01637 m_first = buf->listNext; 01638 m_first->listPrev = 0; 01639 } 01640 else if (buf == m_last) 01641 { 01642 // last element removed 01643 m_last = buf->listPrev; 01644 m_last->listNext = 0; 01645 } 01646 else 01647 { 01648 buf->listPrev->listNext = buf->listNext; 01649 buf->listNext->listPrev = buf->listPrev; 01650 } 01651 01652 buf->listPrev = 0; 01653 buf->listNext = 0; 01654 01655 buf->list = 0; 01656 } 01657 01658 //END KateBufBlockList 01659 01660 // kate: space-indent on; indent-width 2; replace-tabs on;