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, 2003 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 "katecodefoldinghelpers.h" 00031 00032 #include <kvmallocator.h> 00033 #include <kdebug.h> 00034 #include <kglobal.h> 00035 #include <kcharsets.h> 00036 00037 #include <qfile.h> 00038 #include <qtextstream.h> 00039 #include <qtimer.h> 00040 #include <qtextcodec.h> 00041 00042 #include <assert.h> 00043 00044 // SOME LIMITS, may need testing which values are clever 00045 #define AVG_BLOCK_SIZE 32000 00046 00050 class KateBufFileLoader 00051 { 00052 public: 00053 KateBufFileLoader (const QString &m_file) : 00054 file (m_file), stream (&file), codec (0), prev (0), lastCharEOL (false) 00055 { 00056 } 00057 00058 ~KateBufFileLoader () 00059 { 00060 } 00061 00062 public: 00063 QFile file; 00064 QTextStream stream; 00065 QTextCodec *codec; 00066 KateBufBlock *prev; 00067 bool lastCharEOL; 00068 }; 00069 00074 class KateBufBlock 00075 { 00076 friend class KateBuffer; 00077 00078 public: 00079 /* 00080 * Create an empty block. 00081 */ 00082 KateBufBlock (KateBuffer *parent, KateBufBlock *prev, KVMAllocator *vm); 00083 00084 ~KateBufBlock (); 00085 00092 bool fillBlock (QTextStream *stream, bool lastCharEOL); 00093 00098 void buildStringList(); 00099 00104 void flushStringList(); 00105 00110 void disposeStringList(); 00111 00116 void disposeRawData(); 00117 00121 bool swapOut (); 00122 00127 bool swapIn (); 00128 00133 void disposeSwap (); 00134 00139 TextLine::Ptr line(uint i); 00140 00144 void insertLine(uint i, TextLine::Ptr line); 00145 00149 void removeLine(uint i); 00150 00154 inline uint startLine () { return m_startLine; }; 00155 00156 inline void setStartLine (uint line) 00157 { 00158 m_startLine = line; 00159 } 00160 00164 inline uint endLine () { return m_startLine + m_lines; } 00165 00169 inline uint lines () { return m_lines; } 00170 00171 inline uint firstLineIndentation () { return m_firstLineIndentation; } 00172 inline bool firstLineOnlySpaces () { return m_firstLineOnlySpaces; } 00173 00174 inline TextLine::Ptr lastLine () { return m_lastLine; } 00175 00176 private: 00177 // IMPORTANT, start line + lines in block 00178 uint m_startLine; 00179 uint m_lines; 00180 00181 // Used for context & hlContinue flag if this bufblock has no stringlist 00182 uint m_firstLineIndentation; 00183 bool m_firstLineOnlySpaces; 00184 TextLine::Ptr m_lastLine; 00185 00186 // here we swap our stuff 00187 KVMAllocator *m_vm; 00188 KVMAllocator::Block *m_vmblock; 00189 uint m_vmblockSize; 00190 bool b_vmDataValid; 00191 00196 QByteArray m_rawData; 00197 bool b_rawDataValid; 00198 00202 TextLine::List m_stringList; 00203 bool b_stringListValid; 00204 00205 // Buffer requires highlighting. 00206 bool b_needHighlight; 00207 00208 // Parent buffer. 00209 KateBuffer* m_parent; 00210 }; 00211 00215 KateBuffer::KateBuffer(KateDocument *doc) 00216 : QObject (doc), 00217 m_hlUpdate (true), 00218 m_lines (0), 00219 m_highlightedTo (0), 00220 m_highlightedRequested (0), 00221 m_lastInSyncBlock (0), 00222 m_highlight (0), 00223 m_doc (doc), 00224 m_loader (0), 00225 m_vm (0), 00226 m_regionTree (0), 00227 m_highlightedTill (0), 00228 m_highlightedEnd (0), 00229 m_highlightedSteps (0), 00230 m_cacheReadError(false), 00231 m_cacheWriteError(false), 00232 m_loadingBorked (false), 00233 m_tabWidth (0) 00234 { 00235 m_blocks.setAutoDelete(true); 00236 00237 connect( &m_highlightTimer, SIGNAL(timeout()), this, SLOT(pleaseHighlight())); 00238 00239 clear(); 00240 } 00241 00245 KateBuffer::~KateBuffer() 00246 { 00247 m_blocks.clear (); 00248 00249 delete m_vm; 00250 delete m_loader; 00251 } 00252 00253 void KateBuffer::setTabWidth (uint w) 00254 { 00255 if (m_tabWidth != w) 00256 { 00257 m_tabWidth = w; 00258 00259 if (m_highlight && m_highlight->foldingIndentationSensitive()) 00260 invalidateHighlighting(); 00261 } 00262 } 00263 00267 void KateBuffer::checkLoadedMax () 00268 { 00269 if (m_loadedBlocks.count() > 40) 00270 { 00271 KateBufBlock *buf2 = m_loadedBlocks.take(2); 00272 bool ok = buf2->swapOut (); 00273 if (!ok) 00274 { 00275 m_cacheWriteError = true; 00276 m_loadedBlocks.append(buf2); 00277 } 00278 } 00279 } 00280 00284 void KateBuffer::checkCleanMax () 00285 { 00286 if (m_cleanBlocks.count() > 10) 00287 { 00288 checkLoadedMax (); 00289 00290 KateBufBlock *buf2 = m_cleanBlocks.take(2); 00291 buf2->disposeStringList(); 00292 m_loadedBlocks.append(buf2); 00293 } 00294 } 00295 00299 void KateBuffer::checkDirtyMax () 00300 { 00301 if (m_dirtyBlocks.count() > 10) 00302 { 00303 checkLoadedMax (); 00304 00305 KateBufBlock *buf2 = m_dirtyBlocks.take(2); 00306 buf2->flushStringList(); // Copy stringlist to raw 00307 buf2->disposeStringList(); // dispose stringlist. 00308 m_loadedBlocks.append(buf2); 00309 } 00310 } 00311 00312 uint KateBuffer::countVisible () 00313 { 00314 return m_lines - m_regionTree->getHiddenLinesCount(m_lines); 00315 } 00316 00317 uint KateBuffer::lineNumber (uint visibleLine) 00318 { 00319 return m_regionTree->getRealLine (visibleLine); 00320 } 00321 00322 uint KateBuffer::lineVisibleNumber (uint line) 00323 { 00324 return m_regionTree->getVirtualLine (line); 00325 } 00326 00327 void KateBuffer::lineInfo (KateLineInfo *info, unsigned int line) 00328 { 00329 m_regionTree->getLineInfo(info,line); 00330 } 00331 00332 KateCodeFoldingTree *KateBuffer::foldingTree () 00333 { 00334 return m_regionTree; 00335 } 00336 00340 void KateBuffer::loadBlock(KateBufBlock *buf) 00341 { 00342 if (m_loadedBlocks.findRef (buf) > -1) 00343 return; 00344 00345 // does we have already to much loaded blocks ? 00346 checkLoadedMax (); 00347 00348 // swap the data in 00349 if (!buf->swapIn ()) 00350 { 00351 m_cacheReadError = true; 00352 return; // This is bad! 00353 } 00354 00355 m_loadedBlocks.append(buf); 00356 } 00357 00361 void KateBuffer::parseBlock(KateBufBlock *buf) 00362 { 00363 if (m_cleanBlocks.findRef (buf) > -1) 00364 return; 00365 00366 // uh, not even loaded :( 00367 if (!buf->b_rawDataValid) 00368 loadBlock(buf); 00369 00370 // does we have already too much clean blocks ? 00371 checkCleanMax (); 00372 00373 // now you are clean my little block 00374 buf->buildStringList(); 00375 00376 m_loadedBlocks.removeRef(buf); 00377 m_cleanBlocks.append(buf); 00378 } 00379 00383 void KateBuffer::dirtyBlock(KateBufBlock *buf) 00384 { 00385 if (m_dirtyBlocks.findRef (buf) > -1) 00386 return; 00387 00388 // does we have already to much dirty blocks ? 00389 checkDirtyMax (); 00390 00391 // dispose the dirty raw data 00392 buf->disposeRawData(); 00393 00394 // dispose the swap stuff 00395 if (buf->b_vmDataValid) 00396 buf->disposeSwap(); 00397 00398 m_cleanBlocks.removeRef(buf); 00399 m_dirtyBlocks.append(buf); 00400 } 00401 00405 KateBufBlock *KateBuffer::findBlock(uint i) 00406 { 00407 if ((i >= m_lines)) 00408 return 0; 00409 00410 KateBufBlock *buf = 0; 00411 00412 if (m_blocks.current() && (int(m_lastInSyncBlock) >= m_blocks.at())) 00413 { 00414 buf = m_blocks.current(); 00415 } 00416 else 00417 { 00418 buf = m_blocks.at (m_lastInSyncBlock); 00419 } 00420 00421 int lastLine = 0; 00422 while (buf != 0) 00423 { 00424 lastLine = buf->endLine (); 00425 00426 if (i < buf->startLine()) 00427 { 00428 // Search backwards 00429 buf = m_blocks.prev (); 00430 } 00431 else if (i >= buf->endLine()) 00432 { 00433 // Search forwards 00434 buf = m_blocks.next(); 00435 } 00436 else 00437 { 00438 // We found the block. 00439 return buf; 00440 } 00441 00442 if (buf && (m_blocks.at () > int(m_lastInSyncBlock)) && (int(buf->startLine()) != lastLine)) 00443 { 00444 buf->setStartLine (lastLine); 00445 m_lastInSyncBlock = m_blocks.at (); 00446 } 00447 } 00448 00449 return 0; 00450 } 00451 00452 void KateBuffer::clear() 00453 { 00454 // reset the folding tree hard ! 00455 // delete m_regionTree; 00456 // trying to reset the region tree softly 00457 if (m_regionTree) m_regionTree->clear(); 00458 else 00459 { 00460 m_regionTree=new KateCodeFoldingTree(this); 00461 connect(m_regionTree,SIGNAL(setLineVisible(unsigned int, bool)), this,SLOT(setLineVisible(unsigned int,bool))); 00462 } 00463 00464 // cleanup the blocks 00465 m_cleanBlocks.clear(); 00466 m_dirtyBlocks.clear(); 00467 m_loadedBlocks.clear(); 00468 m_blocks.clear(); 00469 delete m_vm; 00470 m_vm = new KVMAllocator; 00471 m_highlight = 0; 00472 00473 // create a bufblock with one line, we need that, only in openFile we won't have that 00474 KateBufBlock *block = new KateBufBlock(this, 0, m_vm); 00475 block->b_rawDataValid = true; 00476 block->m_rawData.resize (sizeof(uint) + 1); 00477 char* buf = block->m_rawData.data (); 00478 uint length = 0; 00479 memcpy(buf, (char *) &length, sizeof(uint)); 00480 char attr = TextLine::flagNoOtherData; 00481 memcpy(buf+sizeof(uint), (char *) &attr, 1); 00482 block->m_lines++; 00483 00484 m_blocks.append (block); 00485 m_loadedBlocks.append (block); 00486 00487 m_lines = block->m_lines; 00488 00489 m_highlightedTo = 0; 00490 m_highlightedRequested = 0; 00491 m_lastInSyncBlock = 0; 00492 00493 emit linesChanged(m_lines); 00494 } 00495 00496 void KateBuffer::setHighlight(Highlight *highlight) 00497 { 00498 m_highlight = highlight; 00499 invalidateHighlighting(); 00500 } 00501 00505 bool KateBuffer::openFile (const QString &m_file) 00506 { 00507 clear(); 00508 00509 // here we feed the loader with info 00510 KateBufFileLoader loader (m_file); 00511 00512 bool ok = false; 00513 struct stat sbuf; 00514 if (stat(QFile::encodeName(m_file), &sbuf) == 0) 00515 { 00516 if (S_ISREG(sbuf.st_mode) && loader.file.open( IO_ReadOnly )) 00517 ok = true; 00518 } 00519 00520 if (!ok) 00521 { 00522 clear(); 00523 return false; // Error 00524 } 00525 00526 if (loader.file.isDirectAccess()) 00527 { 00528 // detect eol 00529 while (true) 00530 { 00531 int ch = loader.file.getch(); 00532 00533 if (ch == -1) 00534 break; 00535 00536 if ((ch == '\r')) 00537 { 00538 ch = loader.file.getch (); 00539 00540 if (ch == '\n') 00541 { 00542 m_doc->config()->setEol (KateDocumentConfig::eolDos); 00543 break; 00544 } 00545 else 00546 { 00547 m_doc->config()->setEol (KateDocumentConfig::eolMac); 00548 break; 00549 } 00550 } 00551 else if (ch == '\n') 00552 { 00553 m_doc->config()->setEol (KateDocumentConfig::eolUnix); 00554 break; 00555 } 00556 } 00557 00558 if (loader.file.size () > 0) 00559 { 00560 loader.file.at (loader.file.size () - 1); 00561 00562 int ch = loader.file.getch(); 00563 00564 if ((ch == '\n') || (ch == '\r')) 00565 loader.lastCharEOL = true; 00566 } 00567 00568 loader.file.reset (); 00569 } 00570 else 00571 { 00572 loader.lastCharEOL = true; 00573 m_doc->config()->setEol (KateDocumentConfig::eolUnix); 00574 } 00575 00576 QTextCodec *codec = m_doc->config()->codec(); 00577 loader.stream.setEncoding(QTextStream::RawUnicode); // disable Unicode headers 00578 loader.stream.setCodec(codec); // this line sets the mapper to the correct codec 00579 loader.codec = codec; 00580 loader.prev = 0; 00581 00582 // trash away the one unneeded already existing block 00583 m_loadedBlocks.clear(); 00584 m_blocks.clear(); 00585 m_lines = 0; 00586 00587 // start with not borked 00588 m_loadingBorked = false; 00589 00590 // do the real work 00591 00592 bool eof = false; 00593 while (true) 00594 { 00595 if (loader.stream.atEnd()) 00596 eof = true; 00597 00598 if (eof) 00599 break; 00600 00601 checkLoadedMax (); 00602 if (m_cacheWriteError) 00603 break; 00604 00605 KateBufBlock *block = new KateBufBlock(this, loader.prev, m_vm); 00606 eof = block->fillBlock (&loader.stream, loader.lastCharEOL); 00607 00608 m_blocks.append (block); 00609 m_loadedBlocks.append (block); 00610 00611 loader.prev = block; 00612 m_lines = block->endLine (); 00613 } 00614 00615 if (m_cacheWriteError) 00616 { 00617 m_loadingBorked = true; 00618 } 00619 00620 if (m_cacheWriteError) 00621 kdDebug(13020)<<"Loading failed, no room for temp-file.\n"; 00622 else 00623 kdDebug(13020)<<"Loading finished.\n"; 00624 00625 // trigger the creation of a block with one line if there is no data in the buffer now 00626 // THIS IS IMPORTANT, OR CRASH WITH EMPTY FILE 00627 if (m_blocks.isEmpty() || (count () == 0)) 00628 clear (); 00629 else 00630 m_regionTree->fixRoot (m_lines); 00631 00632 emit linesChanged(m_lines); 00633 emit loadingFinished (); 00634 00635 return !m_loadingBorked; 00636 } 00637 00638 bool KateBuffer::canEncode () 00639 { 00640 QTextCodec *codec = m_doc->config()->codec(); 00641 00642 kdDebug(13020) << "ENC NAME: " << codec->name() << endl; 00643 00644 // hardcode some unicode encodings which can encode all chars 00645 if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2")) 00646 return true; 00647 00648 for (uint i=0; i < m_lines; i++) 00649 { 00650 if (!codec->canEncode (plainLine(i)->string())) 00651 { 00652 kdDebug(13020) << "STRING LINE: " << plainLine(i)->string() << endl; 00653 kdDebug(13020) << "ENC WORKING: FALSE" << endl; 00654 00655 return false; 00656 } 00657 } 00658 00659 return true; 00660 } 00661 00662 bool KateBuffer::saveFile (const QString &m_file) 00663 { 00664 QFile file (m_file); 00665 QTextStream stream (&file); 00666 00667 if ( !file.open( IO_WriteOnly ) ) 00668 { 00669 return false; // Error 00670 } 00671 00672 QTextCodec *codec = m_doc->config()->codec(); 00673 00674 // disable Unicode headers 00675 stream.setEncoding(QTextStream::RawUnicode); 00676 00677 // this line sets the mapper to the correct codec 00678 stream.setCodec(codec); 00679 00680 QString eol = m_doc->config()->eolString (); 00681 00682 // QString tabs; 00683 // if (m_doc->configFlags() & KateDocument::cfReplaceTabs) 00684 // tabs.fill (QChar(' '), m_doc->config()->tabWidth()); 00685 00686 // for tab replacement, initialize only once 00687 uint pos, found, ml, l; 00688 QChar onespace(' '); 00689 QString onetab("\t"); 00690 uint tw = m_doc->config()->tabWidth(); 00691 00692 // Use the document methods 00693 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs || 00694 m_doc->configFlags() & KateDocument::cfRemoveSpaces ) 00695 m_doc->editStart(); 00696 00697 for (uint i=0; i < m_lines; i++) 00698 { 00699 TextLine::Ptr textLine = plainLine(i); 00700 00701 if (textLine) 00702 { 00703 // replace tabs if required 00704 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs ) 00705 { 00706 pos = 0; 00707 while ( textLine->searchText( pos, onetab, &found, &ml ) ) 00708 { 00709 l = tw - ( found%tw ); 00710 if ( l ) 00711 { 00712 QString t; 00713 m_doc->editRemoveText( i, found, 1 ); 00714 m_doc->editInsertText( i, found, t.fill(onespace, l) ); // ### anything more eficient? 00715 pos += l-1; 00716 } 00717 } 00718 } 00719 00720 // remove trailing spaces if required 00721 if ( (m_doc->configFlags() & KateDocument::cfRemoveSpaces) && textLine->length() ) 00722 { 00723 pos = textLine->length() - 1; 00724 uint lns = textLine->lastChar(); 00725 if ( lns != pos ) 00726 m_doc->editRemoveText( i, lns + 1, pos - lns ); 00727 } 00728 00729 stream << textLine->string(); 00730 00731 if ((i+1) < m_lines) 00732 stream << eol; 00733 } 00734 } 00735 00736 if ( m_doc->configFlags() & KateDocument::cfReplaceTabs || 00737 m_doc->configFlags() & KateDocument::cfRemoveSpaces ) 00738 m_doc->editEnd(); 00739 00740 file.close (); 00741 00742 m_loadingBorked = false; 00743 00744 return (file.status() == IO_Ok); 00745 } 00746 00750 TextLine::Ptr KateBuffer::line(uint i) 00751 { 00752 KateBufBlock *buf = findBlock(i); 00753 00754 if (!buf) 00755 return 0; 00756 00757 if (!buf->b_stringListValid) 00758 parseBlock(buf); 00759 00760 if (buf->b_needHighlight) 00761 { 00762 buf->b_needHighlight = false; 00763 00764 if (m_highlightedTo > buf->startLine()) 00765 { 00766 needHighlight (buf, buf->startLine(), buf->endLine()); 00767 } 00768 } 00769 00770 if ((m_highlightedRequested <= i) && (m_highlightedTo <= i)) 00771 { 00772 m_highlightedRequested = buf->endLine(); 00773 00774 pleaseHighlight (m_highlightedTo, buf->endLine()); 00775 00776 // Check again... 00777 if (!buf->b_stringListValid) 00778 parseBlock(buf); 00779 } 00780 00781 return buf->line (i - buf->m_startLine); 00782 } 00783 00784 bool KateBuffer::needHighlight(KateBufBlock *buf, uint startLine, uint endLine) 00785 { 00786 // no hl around, no stuff to do 00787 if (!m_highlight) 00788 return false; 00789 00790 // nothing to update, still up to date ;) 00791 if (!m_hlUpdate) 00792 return false; 00793 00794 // we tried to start in a line behind this buf block ! 00795 if (startLine >= (buf->startLine()+buf->lines())) 00796 return false; 00797 00798 // parse this block if needed, very important ! 00799 if (!buf->b_stringListValid) 00800 parseBlock(buf); 00801 00802 // get the previous line, if we start at the beginning of this block 00803 // take the last line of the previous block 00804 TextLine::Ptr prevLine = 0; 00805 00806 if (startLine == buf->startLine()) 00807 { 00808 int pos = m_blocks.findRef (buf); 00809 if (pos > 0) 00810 { 00811 KateBufBlock *blk = m_blocks.at (pos-1); 00812 00813 if (blk->b_stringListValid && (blk->lines() > 0)) 00814 prevLine = blk->line (blk->lines() - 1); 00815 else 00816 prevLine = blk->lastLine(); 00817 } 00818 } 00819 else if ((startLine > buf->startLine()) && (startLine <= buf->endLine())) 00820 { 00821 prevLine = buf->line(startLine - buf->startLine() - 1); 00822 } 00823 00824 if (!prevLine) 00825 prevLine = new TextLine (); 00826 00827 bool line_continue = prevLine->hlLineContinue(); 00828 00829 QMemArray<short> ctxNum, endCtx; 00830 ctxNum.duplicate (prevLine->ctxArray ()); 00831 00832 // does we need to emit a signal for the folding changes ? 00833 bool codeFoldingUpdate = false; 00834 00835 // here we are atm, start at start line in the block 00836 uint current_line = startLine - buf->startLine(); 00837 00838 // does we need to continue 00839 bool stillcontinue=false; 00840 00841 // loop over the lines of the block, from startline to endline or end of block 00842 // if stillcontinue forces us to do so 00843 while ( (current_line < buf->lines()) 00844 && (stillcontinue || ((current_line + buf->startLine()) <= endLine)) ) 00845 { 00846 // current line 00847 TextLine::Ptr textLine = buf->line(current_line); 00848 00849 endCtx.duplicate (textLine->ctxArray ()); 00850 00851 QMemArray<signed char> foldingList; 00852 m_highlight->doHighlight(ctxNum, textLine, line_continue, &foldingList); 00853 00854 // 00855 // indentation sensitive folding 00856 // 00857 bool indentChanged = false; 00858 if (m_highlight->foldingIndentationSensitive()) 00859 { 00860 // get the indentation array of the previous line to start with ! 00861 QMemArray<unsigned short> indentDepth; 00862 indentDepth.duplicate (prevLine->indentationDepthArray()); 00863 00864 // current indentation of this line 00865 uint iDepth = textLine->indentDepth(m_tabWidth); 00866 00867 // this line is empty, beside spaces, use indentation depth of the previous line ! 00868 if (textLine->firstChar() == -1) 00869 { 00870 // do this to get skipped empty lines indent right, which was given in the indenation array 00871 if (!prevLine->indentationDepthArray().isEmpty()) 00872 iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1]; 00873 else 00874 iDepth = prevLine->indentDepth(m_tabWidth); 00875 00876 // run over empty lines ;) 00877 indentChanged = true; 00878 } 00879 00880 // query the next line indentation, if we are at the end of the block 00881 // use the first line of the next buf block 00882 uint nextLineIndentation = 0; 00883 00884 if ((current_line+1) < buf->lines()) 00885 { 00886 if (buf->line(current_line+1)->firstChar() == -1) 00887 nextLineIndentation = iDepth; 00888 else 00889 nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth); 00890 } 00891 else 00892 { 00893 int pos = m_blocks.findRef (buf); 00894 if (uint(pos + 1) < m_blocks.count()) 00895 { 00896 KateBufBlock *blk = m_blocks.at (pos+1); 00897 00898 if (blk->b_stringListValid && (blk->lines() > 0)) 00899 { 00900 if (blk->line (0)->firstChar() == -1) 00901 nextLineIndentation = iDepth; 00902 else 00903 nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth); 00904 } 00905 else 00906 { 00907 if (blk->firstLineOnlySpaces()) 00908 nextLineIndentation = iDepth; 00909 else 00910 nextLineIndentation = blk->firstLineIndentation(); 00911 } 00912 } 00913 } 00914 00915 // recalculate the indentation array for this line, query if we have to add 00916 // a new folding start, this means newIn == true ! 00917 bool newIn = false; 00918 if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth))) 00919 { 00920 indentDepth.resize (indentDepth.size()+1); 00921 indentDepth[indentDepth.size()-1] = iDepth; 00922 newIn = true; 00923 } 00924 else 00925 { 00926 for (int z=indentDepth.size()-1; z > -1; z--) 00927 { 00928 if (indentDepth[z] > iDepth) 00929 indentDepth.resize (z); 00930 else if (indentDepth[z] == iDepth) 00931 break; 00932 else if (indentDepth[z] < iDepth) 00933 { 00934 indentDepth.resize (indentDepth.size()+1); 00935 indentDepth[indentDepth.size()-1] = iDepth; 00936 newIn = true; 00937 break; 00938 } 00939 } 00940 } 00941 00942 // just for debugging always true to start with ! 00943 indentChanged = indentChanged || (indentDepth.size() != textLine->indentationDepthArray().size()) 00944 || (indentDepth != textLine->indentationDepthArray()); 00945 00946 // set the new array in the textline ! 00947 textLine->setIndentationDepth (indentDepth); 00948 00949 // add folding start to the list ! 00950 if (newIn) 00951 { 00952 foldingList.resize (foldingList.size() + 1); 00953 foldingList[foldingList.size()-1] = 1; 00954 } 00955 00956 // calculate how much end folding symbols must be added to the list ! 00957 // remIn gives you the count of them 00958 uint remIn = 0; 00959 00960 for (int z=indentDepth.size()-1; z > -1; z--) 00961 { 00962 if (indentDepth[z] > nextLineIndentation) 00963 remIn++; 00964 else 00965 break; 00966 } 00967 00968 if (remIn > 0) 00969 { 00970 foldingList.resize (foldingList.size() + remIn); 00971 00972 for (uint z= foldingList.size()-remIn; z < foldingList.size(); z++) 00973 foldingList[z] = -1; 00974 } 00975 } 00976 00977 bool foldingChanged = (foldingList.size() != textLine->foldingListArray().size()) 00978 || (foldingList != textLine->foldingListArray()); 00979 00980 if (foldingChanged) 00981 textLine->setFoldingList(foldingList); 00982 00983 bool retVal_folding = false; 00984 m_regionTree->updateLine(current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged); 00985 00986 codeFoldingUpdate = codeFoldingUpdate | retVal_folding; 00987 00988 line_continue=textLine->hlLineContinue(); 00989 00990 ctxNum.duplicate (textLine->ctxArray()); 00991 00992 if ( indentChanged || (endCtx.size() != ctxNum.size()) ) 00993 { 00994 stillcontinue = true; 00995 } 00996 else 00997 { 00998 stillcontinue = false; 00999 01000 if ((ctxNum != endCtx)) 01001 stillcontinue = true; 01002 } 01003 01004 // move around the lines 01005 prevLine = textLine; 01006 01007 // increment line 01008 current_line++; 01009 } 01010 01011 // tag the changed lines ! 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::updateHighlighting(uint from, uint to, bool invalidate) 01024 { 01025 //kdDebug()<<"KateBuffer::updateHighlight("<<from<<","<<to<<","<<invalidate<<")"<<endl; 01026 if (!m_hlUpdate) 01027 return; 01028 01029 //kdDebug()<<"passed the no update check"<<endl; 01030 01031 if (from > m_highlightedTo ) 01032 from = m_highlightedTo; 01033 01034 uint done = 0; 01035 bool endStateChanged = true; 01036 01037 while (done < to) 01038 { 01039 KateBufBlock *buf = findBlock(from); 01040 if (!buf) 01041 return; 01042 01043 if (!buf->b_stringListValid) 01044 { 01045 parseBlock(buf); 01046 } 01047 01048 if (buf->b_needHighlight || invalidate || m_highlightedTo < buf->endLine()) 01049 { 01050 uint fromLine = buf->startLine(); 01051 uint tillLine = buf->endLine(); 01052 01053 if (!buf->b_needHighlight && invalidate) 01054 { 01055 if (to < tillLine) 01056 tillLine = to; 01057 01058 if (from > fromLine) 01059 { 01060 if (m_highlightedTo > from) 01061 fromLine = from; 01062 else if (m_highlightedTo > fromLine) 01063 fromLine = m_highlightedTo; 01064 } 01065 } 01066 01067 buf->b_needHighlight = false; 01068 01069 //kdDebug()<<"Calling need highlight: "<<fromLine<<","<<tillLine<<endl; 01070 endStateChanged = needHighlight (buf, fromLine, tillLine); 01071 01072 if (buf->b_rawDataValid) 01073 { 01074 dirtyBlock(buf); 01075 } 01076 } 01077 01078 done = buf->endLine(); 01079 from = done; 01080 } 01081 if (invalidate) 01082 { 01083 if (endStateChanged) 01084 m_highlightedTo = done; 01085 m_highlightedRequested = done; 01086 } 01087 else 01088 { 01089 if (done > m_highlightedTo) 01090 m_highlightedTo = done; 01091 } 01092 } 01093 01094 void KateBuffer::invalidateHighlighting() 01095 { 01096 m_highlightedTo = 0; 01097 m_highlightedRequested = 0; 01098 } 01099 01100 void KateBuffer::pleaseHighlight(uint from, uint to) 01101 { 01102 if (to > m_highlightedEnd) 01103 m_highlightedEnd = to; 01104 01105 if (m_highlightedEnd < from) 01106 return; 01107 01108 // 01109 // this calc makes much of the responsiveness 01110 // 01111 m_highlightedSteps = ((m_highlightedEnd-from) / 5) + 1; 01112 if (m_highlightedSteps < 100) 01113 m_highlightedSteps = 100; 01114 else if (m_highlightedSteps > 2000) 01115 m_highlightedSteps = 2000; 01116 01117 uint till = from + m_highlightedSteps; 01118 if (till > m_highlightedEnd) 01119 till = m_highlightedEnd; 01120 01121 updateHighlighting(from, till, false); 01122 01123 m_highlightedTill = till; 01124 if (m_highlightedTill >= m_highlightedEnd) 01125 { 01126 m_highlightedTill = 0; 01127 m_highlightedEnd = 0; 01128 m_highlightTimer.stop(); 01129 } 01130 else 01131 { 01132 m_highlightTimer.start(100, true); 01133 } 01134 } 01135 01136 void KateBuffer::pleaseHighlight() 01137 { 01138 uint till = m_highlightedTill + m_highlightedSteps; 01139 01140 if (m_highlightedSteps == 0) 01141 till += 100; 01142 01143 if (m_highlightedEnd > m_lines) 01144 m_highlightedEnd = m_lines; 01145 01146 if (till > m_highlightedEnd) 01147 till = m_highlightedEnd; 01148 01149 updateHighlighting(m_highlightedTill, till, false); 01150 01151 m_highlightedTill = till; 01152 if (m_highlightedTill >= m_highlightedEnd) 01153 { 01154 m_highlightedTill = 0; 01155 m_highlightedEnd = 0; 01156 m_highlightedSteps = 0; 01157 m_highlightTimer.stop(); 01158 } 01159 else 01160 { 01161 m_highlightTimer.start(100, true); 01162 } 01163 } 01164 01168 TextLine::Ptr KateBuffer::plainLine(uint i) 01169 { 01170 KateBufBlock *buf = findBlock(i); 01171 if (!buf) 01172 return 0; 01173 01174 if (!buf->b_stringListValid) 01175 { 01176 parseBlock(buf); 01177 } 01178 01179 return buf->line(i - buf->m_startLine); 01180 } 01181 01185 QString KateBuffer::textLine(uint i, bool withoutTrailingSpaces) 01186 { 01187 KateBufBlock *buf = findBlock(i); 01188 if (!buf) 01189 return QString(); 01190 01191 if (!buf->b_stringListValid) 01192 { 01193 parseBlock(buf); 01194 } 01195 01196 if (withoutTrailingSpaces) 01197 return buf->line(i - buf->startLine())->withoutTrailingSpaces(); 01198 01199 return buf->line(i - buf->startLine())->string(); 01200 } 01201 01202 void KateBuffer::insertLine(uint i, TextLine::Ptr line) 01203 { 01204 //kdDebug()<<"bit debugging"<<endl; 01205 //kdDebug()<<"bufferblock count: "<<m_blocks.count()<<endl; 01206 01207 KateBufBlock *buf; 01208 if (i == m_lines) 01209 buf = findBlock(i-1); 01210 else 01211 buf = findBlock(i); 01212 01213 if (!buf) 01214 return; 01215 01216 if (!buf->b_stringListValid) 01217 parseBlock(buf); 01218 01219 if (buf->b_rawDataValid) 01220 dirtyBlock(buf); 01221 01222 buf->insertLine(i - buf->startLine(), line); 01223 01224 if (m_highlightedTo > i) 01225 m_highlightedTo++; 01226 m_lines++; 01227 01228 if (int(m_lastInSyncBlock) > m_blocks.findRef (buf)) 01229 m_lastInSyncBlock = m_blocks.findRef (buf); 01230 01231 m_regionTree->lineHasBeenInserted (i); 01232 } 01233 01234 void 01235 KateBuffer::removeLine(uint i) 01236 { 01237 KateBufBlock *buf = findBlock(i); 01238 assert(buf); 01239 if (!buf->b_stringListValid) 01240 { 01241 parseBlock(buf); 01242 } 01243 if (buf->b_rawDataValid) 01244 { 01245 dirtyBlock(buf); 01246 } 01247 01248 buf->removeLine(i - buf->startLine()); 01249 01250 if (m_highlightedTo > i) 01251 m_highlightedTo--; 01252 01253 m_lines--; 01254 01255 // trash away a empty block 01256 if (buf->lines() == 0) 01257 { 01258 if ((m_lastInSyncBlock > 0) && (int(m_lastInSyncBlock) >= m_blocks.findRef (buf))) 01259 m_lastInSyncBlock = m_blocks.findRef (buf) -1; 01260 01261 m_cleanBlocks.removeRef(buf); 01262 m_dirtyBlocks.removeRef(buf); 01263 m_loadedBlocks.removeRef(buf); 01264 m_blocks.removeRef(buf); 01265 } 01266 else 01267 { 01268 if (int(m_lastInSyncBlock) > m_blocks.findRef (buf)) 01269 m_lastInSyncBlock = m_blocks.findRef (buf); 01270 } 01271 01272 m_regionTree->lineHasBeenRemoved (i); 01273 } 01274 01275 void KateBuffer::changeLine(uint i) 01276 { 01278 KateBufBlock *buf = findBlock(i); 01279 assert(buf); 01280 assert(buf->b_stringListValid); 01281 01282 if (buf->b_rawDataValid) 01283 { 01284 dirtyBlock(buf); 01285 } 01286 } 01287 01288 void KateBuffer::setLineVisible(unsigned int lineNr, bool visible) 01289 { 01290 kdDebug(13000)<<"void KateBuffer::setLineVisible(unsigned int lineNr, bool visible)"<<endl; 01291 TextLine::Ptr l=plainLine(lineNr); 01292 if (l) 01293 { 01294 l->setVisible(visible); 01295 changeLine (lineNr); 01296 } 01297 else 01298 kdDebug(13000)<<QString("Invalid line %1").arg(lineNr)<<endl; 01299 } 01300 01301 uint KateBuffer::length () 01302 { 01303 uint l = 0; 01304 01305 for (uint i = 0; i < count(); i++) 01306 { 01307 l += plainLine(i)->length(); 01308 } 01309 01310 return l; 01311 } 01312 01313 int KateBuffer::lineLength ( uint i ) 01314 { 01315 TextLine::Ptr l = plainLine(i); 01316 Q_ASSERT(l); 01317 if (!l) return 0; 01318 return l->length(); 01319 } 01320 01321 QString KateBuffer::text() 01322 { 01323 QString s; 01324 01325 for (uint i = 0; i < count(); i++) 01326 { 01327 s.append (textLine(i)); 01328 if ( (i < (count()-1)) ) 01329 s.append('\n'); 01330 } 01331 01332 return s; 01333 } 01334 01335 QString KateBuffer::text ( uint startLine, uint startCol, uint endLine, uint endCol, bool blockwise ) 01336 { 01337 if ( blockwise && (startCol > endCol) ) 01338 return QString (); 01339 01340 QString s; 01341 01342 if (startLine == endLine) 01343 { 01344 if (startCol > endCol) 01345 return QString (); 01346 01347 TextLine::Ptr textLine = plainLine(startLine); 01348 01349 if ( !textLine ) 01350 return QString (); 01351 01352 return textLine->string(startCol, endCol-startCol); 01353 } 01354 else 01355 { 01356 for (uint i = startLine; (i <= endLine) && (i < count()); i++) 01357 { 01358 TextLine::Ptr textLine = plainLine(i); 01359 01360 if ( !blockwise ) 01361 { 01362 if (i == startLine) 01363 s.append (textLine->string(startCol, textLine->length()-startCol)); 01364 else if (i == endLine) 01365 s.append (textLine->string(0, endCol)); 01366 else 01367 s.append (textLine->string()); 01368 } 01369 else 01370 { 01371 s.append (textLine->string (startCol, endCol - startCol)); 01372 } 01373 01374 if ( i < endLine ) 01375 s.append('\n'); 01376 } 01377 } 01378 01379 return s; 01380 } 01381 01382 void KateBuffer::dumpRegionTree() 01383 { 01384 m_regionTree->debugDump(); 01385 } 01386 01387 //----------------------------------------------------------------- 01388 01397 KateBufBlock::KateBufBlock(KateBuffer *parent, KateBufBlock *prev, KVMAllocator *vm) 01398 : m_firstLineIndentation (0), 01399 m_firstLineOnlySpaces (true), 01400 m_lastLine (0), 01401 m_vm (vm), 01402 m_vmblock (0), 01403 m_vmblockSize (0), 01404 b_vmDataValid (false), 01405 b_rawDataValid (false), 01406 b_stringListValid (false), 01407 b_needHighlight (true), 01408 m_parent (parent) 01409 { 01410 if (prev) 01411 m_startLine = prev->endLine (); 01412 else 01413 m_startLine = 0; 01414 01415 m_lines = 0; 01416 } 01417 01421 KateBufBlock::~KateBufBlock () 01422 { 01423 if (b_vmDataValid) 01424 disposeSwap (); 01425 } 01426 01430 bool KateBufBlock::fillBlock (QTextStream *stream, bool lastCharEOL) 01431 { 01432 bool eof = false; 01433 uint lines = 0; 01434 01435 m_rawData.resize (AVG_BLOCK_SIZE); 01436 char *buf = m_rawData.data (); 01437 uint pos = 0; 01438 char attr = TextLine::flagNoOtherData; 01439 01440 uint size = 0; 01441 while (size < AVG_BLOCK_SIZE) 01442 { 01443 QString line = stream->readLine(); 01444 01445 if (!(!lastCharEOL && stream->atEnd() && line.isNull())) 01446 { 01447 uint length = line.length (); 01448 size = pos + sizeof(uint) + (sizeof(QChar)*length) + 1; 01449 01450 if (size > m_rawData.size ()) 01451 { 01452 m_rawData.resize (size); 01453 buf = m_rawData.data (); 01454 } 01455 01456 memcpy(buf+pos, (char *) &length, sizeof(uint)); 01457 pos += sizeof(uint); 01458 01459 if (!line.isNull()) 01460 { 01461 memcpy(buf+pos, (char *) line.unicode(), sizeof(QChar)*length); 01462 pos += sizeof(QChar)*length; 01463 } 01464 01465 memcpy(buf+pos, (char *) &attr, 1); 01466 pos += 1; 01467 01468 lines++; 01469 } 01470 01471 if (stream->atEnd() && line.isNull()) 01472 { 01473 eof = true; 01474 break; 01475 } 01476 } 01477 01478 if (pos < m_rawData.size()) 01479 { 01480 m_rawData.resize (size); 01481 } 01482 01483 m_lines = lines; 01484 b_rawDataValid = true; 01485 01486 return eof; 01487 } 01488 01494 bool KateBufBlock::swapOut () 01495 { 01496 //kdDebug(13020)<<"KateBufBlock: swapout this ="<< this<<endl; 01497 assert(b_rawDataValid); 01498 01499 if (!b_vmDataValid) 01500 { 01501 m_vmblock = m_vm->allocate(m_rawData.count()); 01502 m_vmblockSize = m_rawData.count(); 01503 01504 if (!m_rawData.isEmpty()) 01505 { 01506 bool ok = m_vm->copyBlock(m_vmblock, m_rawData.data(), 0, m_rawData.count()); 01507 if (!ok) 01508 return false; 01509 } 01510 01511 b_vmDataValid = true; 01512 } 01513 disposeRawData(); 01514 return true; 01515 } 01516 01521 bool KateBufBlock::swapIn () 01522 { 01523 //kdDebug(13020)<<"KateBufBlock: swapin this ="<< this<<endl; 01524 assert(b_vmDataValid); 01525 assert(!b_rawDataValid); 01526 assert(m_vmblock); 01527 m_rawData.resize(m_vmblockSize); 01528 bool ok = m_vm->copyBlock(m_rawData.data(), m_vmblock, 0, m_vmblockSize); 01529 if (!ok) 01530 return false; 01531 b_rawDataValid = true; 01532 return true; 01533 } 01534 01538 void KateBufBlock::buildStringList() 01539 { 01540 //kdDebug(13020)<<"KateBufBlock: buildStringList this ="<< this<<endl; 01541 assert(m_stringList.empty()); 01542 01543 char *buf = m_rawData.data(); 01544 char *end = buf + m_rawData.count(); 01545 01546 while(buf < end) 01547 { 01548 TextLine::Ptr textLine = new TextLine (); 01549 buf = textLine->restore (buf); 01550 m_stringList.push_back (textLine); 01551 } 01552 01553 //kdDebug(13020)<<"stringList.count = "<< m_stringList.size()<<" should be "<< (m_endState.lineNr - m_beginState.lineNr) <<endl; 01554 01555 if (m_lines > 0) 01556 { 01557 m_lastLine = m_stringList[m_lines - 1]; 01558 } 01559 else 01560 { 01561 m_lastLine = 0; 01562 } 01563 01564 m_firstLineIndentation = 0; 01565 m_firstLineOnlySpaces = true; 01566 01567 assert(m_stringList.size() == m_lines); 01568 b_stringListValid = true; 01569 //kdDebug(13020)<<"END: KateBufBlock: buildStringList LINES: "<<m_endState.lineNr - m_beginState.lineNr<<endl; 01570 } 01571 01576 void KateBufBlock::flushStringList() 01577 { 01578 //kdDebug(13020)<<"KateBufBlock: flushStringList this ="<< this<<endl; 01579 assert(b_stringListValid); 01580 assert(!b_rawDataValid); 01581 01582 // Calculate size. 01583 uint size = 0; 01584 for(TextLine::List::const_iterator it = m_stringList.begin(); it != m_stringList.end(); ++it) 01585 size += (*it)->dumpSize (); 01586 01587 m_rawData.resize (size); 01588 char *buf = m_rawData.data(); 01589 01590 // Dump textlines 01591 for(TextLine::List::iterator it = m_stringList.begin(); it != m_stringList.end(); ++it) 01592 buf = (*it)->dump (buf); 01593 01594 assert(buf-m_rawData.data() == (int)size); 01595 b_rawDataValid = true; 01596 } 01597 01601 void KateBufBlock::disposeStringList() 01602 { 01603 //kdDebug(13020)<<"KateBufBlock: disposeStringList this = "<< this<<endl; 01604 assert(b_rawDataValid || b_vmDataValid); 01605 01606 if (m_lines > 0) 01607 { 01608 m_firstLineIndentation = m_stringList[0]->indentDepth (m_parent->tabWidth()); 01609 m_firstLineOnlySpaces = (m_stringList[0]->firstChar() == -1); 01610 m_lastLine = m_stringList[m_lines - 1]; 01611 } 01612 else 01613 { 01614 m_firstLineIndentation = 0; 01615 m_firstLineOnlySpaces = true; 01616 m_lastLine = 0; 01617 } 01618 01619 m_stringList.clear(); 01620 b_stringListValid = false; 01621 } 01622 01626 void KateBufBlock::disposeRawData() 01627 { 01628 //kdDebug(13020)<< "KateBufBlock: disposeRawData this = "<< this<<endl; 01629 assert(b_stringListValid || b_vmDataValid); 01630 b_rawDataValid = false; 01631 m_rawData.resize (0); 01632 } 01633 01637 void KateBufBlock::disposeSwap() 01638 { 01639 if (m_vmblock) 01640 m_vm->free(m_vmblock); 01641 01642 m_vmblock = 0; 01643 m_vmblockSize = 0; 01644 b_vmDataValid = false; 01645 } 01646 01651 TextLine::Ptr KateBufBlock::line(uint i) 01652 { 01653 assert(b_stringListValid); 01654 assert(i < m_stringList.size()); 01655 01656 return m_stringList[i]; 01657 } 01658 01659 void KateBufBlock::insertLine(uint i, TextLine::Ptr line) 01660 { 01661 assert(b_stringListValid); 01662 assert(i <= m_stringList.size()); 01663 01664 m_stringList.insert (m_stringList.begin()+i, line); 01665 m_lines++; 01666 } 01667 01668 void KateBufBlock::removeLine(uint i) 01669 { 01670 assert(b_stringListValid); 01671 assert(i < m_stringList.size()); 01672 01673 m_stringList.erase (m_stringList.begin()+i); 01674 m_lines--; 01675 } 01676 01677 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Logo
This file is part of the documentation for kate Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:56:22 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003