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