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