kate Library API Documentation

katebuffer.cpp

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