kate Library API Documentation

katebuffer.cpp

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