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 ((uint)4, 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   else
00812   {
00813     // last sync block adjust
00814     if (m_lastInSyncBlock > index)
00815       m_lastInSyncBlock = index;
00816   }
00817 
00818   // last found
00819   if (m_lastInSyncBlock < m_lastFoundBlock)
00820     m_lastFoundBlock = m_lastInSyncBlock;
00821 
00822   // mark buffer changed
00823   editChangesDone = true;
00824 
00825   // tag this line as removed
00826    if (i < editTagLineStart)
00827     editTagLineStart = i;
00828 
00829   if (i < editTagLineEnd)
00830     editTagLineEnd--;
00831 
00832   if (i > editTagLineEnd)
00833     editTagLineEnd = i;
00834 
00835   // line removed
00836   editTagLineFrom = true;
00837 
00838   m_regionTree.lineHasBeenRemoved (i);
00839 }
00840 
00841 void KateBuffer::setTabWidth (uint w)
00842 {
00843   if ((m_tabWidth != w) && (m_tabWidth > 0))
00844   {
00845     m_tabWidth = w;
00846 
00847     if (m_highlight && m_highlight->foldingIndentationSensitive())
00848       invalidateHighlighting();
00849   }
00850 }
00851 
00852 void KateBuffer::setHighlight(uint hlMode)
00853 {
00854   KateHighlighting *h = KateHlManager::self()->getHl(hlMode);
00855 
00856    // aha, hl will change
00857   if (h != m_highlight)
00858   {
00859     bool invalidate = !h->noHighlighting();
00860 
00861     if (m_highlight)
00862     {
00863       m_highlight->release();
00864       invalidate = true;
00865     }
00866 
00867     h->use();
00868 
00869     // try to set indentation
00870     if (!h->indentation().isEmpty())
00871       m_doc->config()->setIndentationMode (KateAutoIndent::modeNumber(h->indentation()));
00872 
00873     m_highlight = h;
00874 
00875     if (invalidate)
00876       invalidateHighlighting();
00877 
00878     // inform the document that the hl was really changed
00879     // needed to update attributes and more ;)
00880     m_doc->bufferHlChanged ();
00881   }
00882 }
00883 
00884 void KateBuffer::invalidateHighlighting()
00885 {
00886   m_lineHighlightedMax = 0;
00887   m_lineHighlighted = 0;
00888 }
00889 
00890 
00891 void KateBuffer::updatePreviousNotEmptyLine(KateBufBlock *blk,uint current_line,bool addindent,uint deindent)
00892 {
00893   KateTextLine::Ptr textLine;
00894   do {
00895     if (current_line>0) current_line--;
00896     else
00897     {
00898       uint line=blk->startLine()+current_line;
00899       if (line==0) return;
00900       line--;
00901       blk=findBlock(line);
00902       if (!blk) {
00903         kdDebug(13020)<<"updatePreviousNotEmptyLine: block not found, this must not happen"<<endl;
00904         return;
00905       }
00906       current_line=line-blk->startLine();
00907     }
00908     textLine = blk->line(current_line);
00909   } while (textLine->firstChar()==-1);
00910   kdDebug(13020)<<"updatePreviousNotEmptyLine: updating line:"<<(blk->startLine()+current_line)<<endl;
00911   QMemArray<uint> foldingList=textLine->foldingListArray();
00912   while ( (foldingList.size()>0)  && ( abs(foldingList[foldingList.size()-2])==1)) {
00913     foldingList.resize(foldingList.size()-2,QGArray::SpeedOptim);
00914   }
00915   addIndentBasedFoldingInformation(foldingList,addindent,deindent);
00916   textLine->setFoldingList(foldingList);
00917   bool retVal_folding = false;
00918   m_regionTree.updateLine (current_line + blk->startLine(), &foldingList, &retVal_folding, true,false);
00919   emit tagLines (blk->startLine()+current_line, blk->startLine()+current_line);
00920 }
00921 
00922 void KateBuffer::addIndentBasedFoldingInformation(QMemArray<uint> &foldingList,bool addindent,uint deindent)
00923 {
00924   if (addindent) {
00925     //kdDebug(13020)<<"adding indent for line :"<<current_line + buf->startLine()<<"  textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
00926     kdDebug(13020)<<"adding ident"<<endl;
00927     foldingList.resize (foldingList.size() + 2, QGArray::SpeedOptim);
00928     foldingList[foldingList.size()-2] = 1;
00929     foldingList[foldingList.size()-1] = 0;
00930   }
00931   kdDebug(13020)<<"DEINDENT: "<<deindent<<endl;
00932   if (deindent > 0)
00933   {
00934     foldingList.resize (foldingList.size() + (deindent*2), QGArray::SpeedOptim);
00935 
00936     for (uint z= foldingList.size()-(deindent*2); z < foldingList.size(); z=z+2)
00937     {
00938       foldingList[z] = -1;
00939       foldingList[z+1] = 0;
00940     }
00941   }
00942 }
00943 
00944 bool KateBuffer::doHighlight (KateBufBlock *buf, uint startLine, uint endLine, bool invalidate)
00945 {
00946   // no hl around, no stuff to do
00947   if (!m_highlight)
00948     return false;
00949 
00950   /*if (m_highlight->foldingIndentationSensitive())
00951   {
00952     startLine=0;
00953     endLine=50;
00954   }*/
00955 
00956   // we tried to start in a line behind this buf block !
00957   if (startLine >= (buf->startLine()+buf->lines()))
00958     return false;
00959 
00960   //QTime t;
00961   //t.start();
00962   //kdDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
00963   //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
00964   //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
00965 
00966   // see if there are too many dynamic contexts; if yes, invalidate HL of all documents
00967   if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts)
00968   {
00969     {
00970       if (KateHlManager::self()->resetDynamicCtxs())
00971       {
00972         kdDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")" << endl;
00973 
00974         // avoid recursive invalidation
00975         KateHlManager::self()->setForceNoDCReset(true);
00976 
00977         for (KateDocument *doc = KateFactory::self()->documents()->first(); doc; doc = KateFactory::self()->documents()->next())
00978           doc->makeAttribs();
00979 
00980         // doHighlight *shall* do his work. After invalidation, some highlight has
00981         // been recalculated, but *maybe not* until endLine ! So we shall force it manually...
00982         KateBufBlock *buf = 0;
00983         while ((endLine > m_lineHighlighted) && (buf = findBlock(m_lineHighlighted)))
00984         {
00985           uint end = kMin(endLine, buf->endLine());
00986 
00987           doHighlight ( buf,
00988                         kMax(m_lineHighlighted, buf->startLine()),
00989                         end,
00990                         false );
00991 
00992           m_lineHighlighted = end;
00993         }
00994 
00995         KateHlManager::self()->setForceNoDCReset(false);
00996 
00997         return false;
00998       }
00999       else
01000       {
01001         m_maxDynamicContexts *= 2;
01002         kdDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts << endl;
01003       }
01004     }
01005   }
01006 
01007   // get the previous line, if we start at the beginning of this block
01008   // take the last line of the previous block
01009   KateTextLine::Ptr prevLine = 0;
01010 
01011   if ((startLine == buf->startLine()) && buf->prev() && (buf->prev()->lines() > 0))
01012     prevLine = buf->prev()->line (buf->prev()->lines() - 1);
01013   else if ((startLine > buf->startLine()) && (startLine <= buf->endLine()))
01014     prevLine = buf->line(startLine - buf->startLine() - 1);
01015   else
01016     prevLine = new KateTextLine ();
01017 
01018   // does we need to emit a signal for the folding changes ?
01019   bool codeFoldingUpdate = false;
01020 
01021   // here we are atm, start at start line in the block
01022   uint current_line = startLine - buf->startLine();
01023 
01024   // do we need to continue
01025   bool stillcontinue=false;
01026   bool indentContinueWhitespace=false;
01027   bool indentContinueNextWhitespace=false;
01028   // loop over the lines of the block, from startline to endline or end of block
01029   // if stillcontinue forces us to do so
01030   while ( (current_line < buf->lines())
01031           && (stillcontinue || ((current_line + buf->startLine()) <= endLine)) )
01032   {
01033     // current line
01034     KateTextLine::Ptr textLine = buf->line(current_line);
01035 
01036     QMemArray<uint> foldingList;
01037     bool ctxChanged = false;
01038 
01039     m_highlight->doHighlight (prevLine, textLine, &foldingList, &ctxChanged);
01040 
01041     //
01042     // indentation sensitive folding
01043     //
01044     bool indentChanged = false;
01045     if (m_highlight->foldingIndentationSensitive())
01046     {
01047       // get the indentation array of the previous line to start with !
01048       QMemArray<unsigned short> indentDepth;
01049       indentDepth.duplicate (prevLine->indentationDepthArray());
01050 
01051       // current indentation of this line
01052       uint iDepth = textLine->indentDepth(m_tabWidth);
01053       if ((current_line+buf->startLine())==0)
01054       {
01055           indentDepth.resize (1, QGArray::SpeedOptim);
01056           indentDepth[0] = iDepth;
01057       }
01058 
01059       textLine->setNoIndentBasedFoldingAtStart(prevLine->noIndentBasedFolding());
01060       // this line is empty, beside spaces, or has indentaion based folding disabled, use indentation depth of the previous line !
01061       kdDebug(13020)<<"current_line:"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
01062       if ( (textLine->firstChar() == -1) || textLine->noIndentBasedFoldingAtStart())
01063       {
01064         // do this to get skipped empty lines indent right, which was given in the indenation array
01065         if (!prevLine->indentationDepthArray().isEmpty())
01066         {
01067           iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1];
01068           kdDebug(13020)<<"reusing old depth as current"<<endl;
01069         }
01070         else
01071         {
01072           iDepth = prevLine->indentDepth(m_tabWidth);
01073           kdDebug(13020)<<"creating indentdepth for previous line"<<endl;
01074         }
01075       }
01076 
01077       kdDebug(13020)<<"iDepth:"<<iDepth<<endl;
01078 
01079       // query the next line indentation, if we are at the end of the block
01080       // use the first line of the next buf block
01081       uint nextLineIndentation = 0;
01082       bool nextLineIndentationValid=true;
01083       indentContinueNextWhitespace=false;
01084       if ((current_line+1) < buf->lines())
01085       {
01086         if (buf->line(current_line+1)->firstChar() == -1)
01087         {
01088           nextLineIndentation = iDepth;
01089           indentContinueNextWhitespace=true;
01090         }
01091         else
01092           nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth);
01093       }
01094       else
01095       {
01096         KateBufBlock *blk = buf->next();
01097 
01098         if (blk && (blk->lines() > 0))
01099         {
01100           if (blk->line (0)->firstChar() == -1)
01101           {
01102             nextLineIndentation = iDepth;
01103             indentContinueNextWhitespace=true;
01104           }
01105           else
01106             nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth);
01107         }
01108         else nextLineIndentationValid=false;
01109       }
01110 
01111       if  (!textLine->noIndentBasedFoldingAtStart()) {
01112 
01113         if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
01114         {
01115           kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
01116           indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
01117           indentDepth[indentDepth.size()-1] = iDepth;
01118         } else {
01119           if (!indentDepth.isEmpty())
01120           {
01121             for (int z=indentDepth.size()-1; z > -1; z--)
01122               if (indentDepth[z]>iDepth)
01123                 indentDepth.resize(z, QGArray::SpeedOptim);
01124             if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
01125             {
01126               kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
01127               indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
01128               indentDepth[indentDepth.size()-1] = iDepth;
01129               if (prevLine->firstChar()==-1) {
01130 
01131               }
01132             }
01133           }
01134         }
01135       }
01136 
01137       if (!textLine->noIndentBasedFolding())
01138       {
01139         if (nextLineIndentationValid)
01140         {
01141           //if (textLine->firstChar()!=-1)
01142           {
01143             kdDebug(13020)<<"nextLineIndentation:"<<nextLineIndentation<<endl;
01144             bool addindent=false;
01145             uint deindent=0;
01146             if (!indentDepth.isEmpty())
01147               kdDebug()<<"indentDepth[indentDepth.size()-1]:"<<indentDepth[indentDepth.size()-1]<<endl;
01148             if ((nextLineIndentation>0) && ( indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1]<nextLineIndentation)))
01149             {
01150               kdDebug(13020)<<"addindent==true"<<endl;
01151               addindent=true;
01152             } else {
01153             if ((!indentDepth.isEmpty()) && (indentDepth[indentDepth.size()-1]>nextLineIndentation))
01154               {
01155                 kdDebug(13020)<<"...."<<endl;
01156                 for (int z=indentDepth.size()-1; z > -1; z--)
01157                 {
01158                   kdDebug(13020)<<indentDepth[z]<<"  "<<nextLineIndentation<<endl;
01159                   if (indentDepth[z]>nextLineIndentation)
01160                     deindent++;
01161                 }
01162               }
01163             }
01164 /*        }
01165         if (textLine->noIndentBasedFolding()) kdDebug(13020)<<"=============================indentation based folding disabled======================"<<endl;
01166         if (!textLine->noIndentBasedFolding()) {*/
01167             if ((textLine->firstChar()==-1)) {
01168               updatePreviousNotEmptyLine(buf,current_line,addindent,deindent);
01169               codeFoldingUpdate=true;
01170             }
01171             else
01172             {
01173               addIndentBasedFoldingInformation(foldingList,addindent,deindent);
01174             }
01175           }
01176         }
01177       }
01178       indentChanged = !(indentDepth == textLine->indentationDepthArray());
01179 
01180       // assign the new array to the textline !
01181       if (indentChanged)
01182         textLine->setIndentationDepth (indentDepth);
01183 
01184       indentContinueWhitespace=textLine->firstChar()==-1;
01185     }
01186     bool foldingColChanged=false;
01187     bool foldingChanged = false; 
01188     if (foldingList.size()!=textLine->foldingListArray().size()) {
01189       foldingChanged=true;
01190     } else {
01191       QMemArray<uint>::ConstIterator it=foldingList.begin();
01192       QMemArray<uint>::ConstIterator it1=textLine->foldingListArray();
01193       bool markerType=true;
01194       for(;it!=foldingList.end();++it,++it1) {
01195         if  (markerType) {
01196           if ( ((*it)!=(*it1))) {
01197             foldingChanged=true;
01198             foldingColChanged=false;
01199             break;
01200           }
01201         } else {
01202             if ((*it)!=(*it1)) {
01203               foldingColChanged=true;
01204             }
01205         }
01206         markerType=!markerType;
01207       }
01208     }
01209 
01210     if (foldingChanged || foldingColChanged) {
01211       textLine->setFoldingList(foldingList);
01212       if (foldingChanged==false){
01213         textLine->setFoldingColumnsOutdated(textLine->foldingColumnsOutdated() | foldingColChanged);
01214       } else textLine->setFoldingColumnsOutdated(false);
01215     }
01216     bool retVal_folding = false;
01217     //perhaps make en enums out of the change flags
01218     m_regionTree.updateLine (current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged,foldingColChanged);
01219 
01220     codeFoldingUpdate = codeFoldingUpdate | retVal_folding;
01221 
01222     // need we to continue ?
01223     stillcontinue = ctxChanged || indentChanged || indentContinueWhitespace || indentContinueNextWhitespace;
01224 
01225     // move around the lines
01226     prevLine = textLine;
01227 
01228     // increment line
01229     current_line++;
01230   }
01231 
01232   buf->markDirty ();
01233 
01234   // tag the changed lines !
01235   if (invalidate)
01236     emit tagLines (startLine, current_line + buf->startLine());
01237 
01238   // emit that we have changed the folding
01239   if (codeFoldingUpdate)
01240     emit codeFoldingUpdated();
01241 
01242   //kdDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
01243   //kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
01244   //kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
01245   //kdDebug (13020) << "TIME TAKEN: " << t.elapsed() << endl;
01246 
01247   // if we are at the last line of the block + we still need to continue
01248   // return the need of that !
01249   return stillcontinue && ((current_line+1) == buf->lines());
01250 }
01251 
01252 void KateBuffer::codeFoldingColumnUpdate(unsigned int lineNr) {
01253   KateTextLine::Ptr line=plainLine(lineNr);
01254   if (!line) return;
01255   if (line->foldingColumnsOutdated()) {
01256     line->setFoldingColumnsOutdated(false);
01257     bool tmp;
01258     QMemArray<uint> folding=line->foldingListArray();
01259     m_regionTree.updateLine(lineNr,&folding,&tmp,true,false);
01260   }
01261 }
01262 
01263 //BEGIN KateBufBlock
01264 
01265 KateBufBlock::KateBufBlock ( KateBuffer *parent, KateBufBlock *prev, KateBufBlock *next,
01266                              KateFileLoader *stream )
01267 : m_state (KateBufBlock::stateDirty),
01268   m_startLine (0),
01269   m_lines (0),
01270   m_vmblock (0),
01271   m_vmblockSize (0),
01272   m_parent (parent),
01273   m_prev (prev),
01274   m_next (next),
01275   list (0),
01276   listPrev (0),
01277   listNext (0)
01278 {
01279   // init startline + the next pointers of the neighbour blocks
01280   if (m_prev)
01281   {
01282     m_startLine = m_prev->endLine ();
01283     m_prev->m_next = this;
01284   }
01285 
01286   if (m_next)
01287     m_next->m_prev = this;
01288 
01289   // we have a stream, use it to fill the block !
01290   // this can lead to 0 line blocks which are invalid !
01291   if (stream)
01292   {
01293     // this we lead to either dirty or swapped state
01294     fillBlock (stream);
01295   }
01296   else // init the block if no stream given !
01297   {
01298     // fill in one empty line !
01299     KateTextLine::Ptr textLine = new KateTextLine ();
01300     m_stringList.push_back (textLine);
01301     m_lines++;
01302 
01303     // if we have allready enough blocks around, swap one
01304     if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
01305       m_parent->m_loadedBlocks.first()->swapOut();
01306 
01307     // we are a new nearly empty dirty block
01308     m_state = KateBufBlock::stateDirty;
01309     m_parent->m_loadedBlocks.append (this);
01310   }
01311 }
01312 
01313 KateBufBlock::~KateBufBlock ()
01314 {
01315   // sync prev/next pointers
01316   if (m_prev)
01317     m_prev->m_next = m_next;
01318 
01319   if (m_next)
01320     m_next->m_prev = m_prev;
01321 
01322   // if we have some swapped data allocated, free it now or never
01323   if (m_vmblock)
01324     KateFactory::self()->vm()->free(m_vmblock);
01325 
01326   // remove me from the list I belong
01327   KateBufBlockList::remove (this);
01328 }
01329 
01330 void KateBufBlock::fillBlock (KateFileLoader *stream)
01331 {
01332   // is allready too much stuff around in mem ?
01333   bool swap = m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks();
01334 
01335   QByteArray rawData;
01336 
01337   // calcs the approx size for KATE_AVG_BLOCK_SIZE chars !
01338   if (swap)
01339     rawData.resize ((KATE_AVG_BLOCK_SIZE * sizeof(QChar)) + ((KATE_AVG_BLOCK_SIZE/80) * 8));
01340 
01341   char *buf = rawData.data ();
01342   uint size = 0;
01343   uint blockSize = 0;
01344   while (!stream->eof() && (blockSize < KATE_AVG_BLOCK_SIZE) && (m_lines < KATE_MAX_BLOCK_LINES))
01345   {
01346     uint offset = 0, length = 0;
01347     stream->readLine(offset, length);
01348     const QChar *unicodeData = stream->unicode () + offset;
01349 
01350     // strip spaces at end of line
01351     if ( stream->removeTrailingSpaces() )
01352     {
01353       while (length > 0)
01354       {
01355         if (unicodeData[length-1].isSpace())
01356           --length;
01357         else
01358           break;
01359       }
01360     }
01361 
01362     blockSize += length;
01363 
01364     if (swap)
01365     {
01366       // create the swapped data on the fly, no need to waste time
01367       // via going over the textline classes and dump them !
01368       char attr = KateTextLine::flagNoOtherData;
01369       uint pos = size;
01370 
01371       // calc new size
01372       size = size + 1 + sizeof(uint) + (sizeof(QChar)*length);
01373 
01374       if (size > rawData.size ())
01375       {
01376         rawData.resize (size);
01377         buf = rawData.data ();
01378       }
01379 
01380       memcpy(buf+pos, (char *) &attr, 1);
01381       pos += 1;
01382 
01383       memcpy(buf+pos, (char *) &length, sizeof(uint));
01384       pos += sizeof(uint);
01385 
01386       memcpy(buf+pos, (char *) unicodeData, sizeof(QChar)*length);
01387       pos += sizeof(QChar)*length;
01388     }
01389     else
01390     {
01391       KateTextLine::Ptr textLine = new KateTextLine ();
01392       textLine->insertText (0, length, unicodeData);
01393       m_stringList.push_back (textLine);
01394     }
01395 
01396     m_lines++;
01397   }
01398 
01399   if (swap)
01400   {
01401     m_vmblock = KateFactory::self()->vm()->allocate(size);
01402     m_vmblockSize = size;
01403 
01404     if (!rawData.isEmpty())
01405     {
01406       if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, size))
01407       {
01408         if (m_vmblock)
01409           KateFactory::self()->vm()->free(m_vmblock);
01410 
01411         m_vmblock = 0;
01412         m_vmblockSize = 0;
01413 
01414         m_parent->m_cacheWriteError = true;
01415       }
01416     }
01417 
01418     // fine, we are swapped !
01419     m_state = KateBufBlock::stateSwapped;
01420   }
01421   else
01422   {
01423     // we are a new dirty block without any swap data
01424     m_state = KateBufBlock::stateDirty;
01425     m_parent->m_loadedBlocks.append (this);
01426   }
01427 
01428   kdDebug (13020) << "A BLOCK LOADED WITH LINES: " << m_lines << endl;
01429 }
01430 
01431 KateTextLine::Ptr KateBufBlock::line(uint i)
01432 {
01433   // take care that the string list is around !!!
01434   if (m_state == KateBufBlock::stateSwapped)
01435     swapIn ();
01436 
01437   // LRU
01438   if (!m_parent->m_loadedBlocks.isLast(this))
01439     m_parent->m_loadedBlocks.append (this);
01440 
01441   return m_stringList[i];
01442 }
01443 
01444 void KateBufBlock::insertLine(uint i, KateTextLine::Ptr line)
01445 {
01446   // take care that the string list is around !!!
01447   if (m_state == KateBufBlock::stateSwapped)
01448     swapIn ();
01449 
01450   m_stringList.insert (m_stringList.begin()+i, line);
01451   m_lines++;
01452 
01453   markDirty ();
01454 }
01455 
01456 void KateBufBlock::removeLine(uint i)
01457 {
01458   // take care that the string list is around !!!
01459   if (m_state == KateBufBlock::stateSwapped)
01460     swapIn ();
01461 
01462   m_stringList.erase (m_stringList.begin()+i);
01463   m_lines--;
01464 
01465   markDirty ();
01466 }
01467 
01468 void KateBufBlock::markDirty ()
01469 {
01470   if (m_state != KateBufBlock::stateSwapped)
01471   {
01472     // LRU
01473     if (!m_parent->m_loadedBlocks.isLast(this))
01474       m_parent->m_loadedBlocks.append (this);
01475 
01476     if (m_state == KateBufBlock::stateClean)
01477     {
01478       // if we have some swapped data allocated which is dirty, free it now
01479       if (m_vmblock)
01480         KateFactory::self()->vm()->free(m_vmblock);
01481 
01482       m_vmblock = 0;
01483       m_vmblockSize = 0;
01484 
01485       // we are dirty
01486       m_state = KateBufBlock::stateDirty;
01487     }
01488   }
01489 }
01490 
01491 void KateBufBlock::swapIn ()
01492 {
01493   if (m_state != KateBufBlock::stateSwapped)
01494     return;
01495 
01496   QByteArray rawData (m_vmblockSize);
01497 
01498   // what to do if that fails ?
01499   if (!KateFactory::self()->vm()->copyBlock(rawData.data(), m_vmblock, 0, rawData.size()))
01500     m_parent->m_cacheReadError = true;
01501 
01502   // reserve mem, keep realloc away on push_back
01503   m_stringList.reserve (m_lines);
01504 
01505   char *buf = rawData.data();
01506   for (uint i=0; i < m_lines; i++)
01507   {
01508     KateTextLine::Ptr textLine = new KateTextLine ();
01509     buf = textLine->restore (buf);
01510     m_stringList.push_back (textLine);
01511   }
01512 
01513   // if we have allready enough blocks around, swap one
01514   if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
01515     m_parent->m_loadedBlocks.first()->swapOut();
01516 
01517   // fine, we are now clean again, save state + append to clean list
01518   m_state = KateBufBlock::stateClean;
01519   m_parent->m_loadedBlocks.append (this);
01520 }
01521 
01522 void KateBufBlock::swapOut ()
01523 {
01524   if (m_state == KateBufBlock::stateSwapped)
01525     return;
01526 
01527   if (m_state == KateBufBlock::stateDirty)
01528   {
01529     bool haveHl = m_parent->m_highlight && !m_parent->m_highlight->noHighlighting();
01530 
01531     // Calculate size.
01532     uint size = 0;
01533     for (uint i=0; i < m_lines; i++)
01534       size += m_stringList[i]->dumpSize (haveHl);
01535 
01536     QByteArray rawData (size);
01537     char *buf = rawData.data();
01538 
01539     // Dump textlines
01540     for (uint i=0; i < m_lines; i++)
01541       buf = m_stringList[i]->dump (buf, haveHl);
01542 
01543     m_vmblock = KateFactory::self()->vm()->allocate(rawData.size());
01544     m_vmblockSize = rawData.size();
01545 
01546     if (!rawData.isEmpty())
01547     {
01548       if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, rawData.size()))
01549       {
01550         if (m_vmblock)
01551           KateFactory::self()->vm()->free(m_vmblock);
01552 
01553         m_vmblock = 0;
01554         m_vmblockSize = 0;
01555 
01556         m_parent->m_cacheWriteError = true;
01557 
01558         return;
01559       }
01560     }
01561   }
01562 
01563   m_stringList.clear();
01564 
01565   // we are now swapped out, set state + remove us out of the lists !
01566   m_state = KateBufBlock::stateSwapped;
01567   KateBufBlockList::remove (this);
01568 }
01569 
01570 //END KateBufBlock
01571 
01572 //BEGIN KateBufBlockList
01573 
01574 KateBufBlockList::KateBufBlockList ()
01575  : m_count (0),
01576    m_first (0),
01577    m_last (0)
01578 {
01579 }
01580 
01581 void KateBufBlockList::append (KateBufBlock *buf)
01582 {
01583   if (buf->list)
01584     buf->list->removeInternal (buf);
01585 
01586   m_count++;
01587 
01588   // append a element
01589   if (m_last)
01590   {
01591     m_last->listNext = buf;
01592 
01593     buf->listPrev = m_last;
01594     buf->listNext = 0;
01595 
01596     m_last = buf;
01597 
01598     buf->list = this;
01599 
01600     return;
01601   }
01602 
01603   // insert the first element
01604   m_last = buf;
01605   m_first = buf;
01606 
01607   buf->listPrev = 0;
01608   buf->listNext = 0;
01609 
01610   buf->list = this;
01611 }
01612 
01613 void KateBufBlockList::removeInternal (KateBufBlock *buf)
01614 {
01615   if (buf->list != this)
01616     return;
01617 
01618   m_count--;
01619 
01620   if ((buf == m_first) && (buf == m_last))
01621   {
01622     // last element removed !
01623     m_first = 0;
01624     m_last = 0;
01625   }
01626   else if (buf == m_first)
01627   {
01628     // first element removed
01629     m_first = buf->listNext;
01630     m_first->listPrev = 0;
01631   }
01632   else if (buf == m_last)
01633   {
01634     // last element removed
01635     m_last = buf->listPrev;
01636     m_last->listNext = 0;
01637   }
01638   else
01639   {
01640     buf->listPrev->listNext = buf->listNext;
01641     buf->listNext->listPrev = buf->listPrev;
01642   }
01643 
01644   buf->listPrev = 0;
01645   buf->listNext = 0;
01646 
01647   buf->list = 0;
01648 }
01649 
01650 //END KateBufBlockList
01651 
01652 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE Home | KDE Accessibility Home | Description of Access Keys