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