• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kate

katebuffer.cpp

Go to the documentation of this file.
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    Copyright (C) 2007 Mirko Stocker <me@misto.ch>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 #include "katebuffer.h"
00022 #include "katebuffer.moc"
00023 
00024 #include <sys/types.h>
00025 #include <sys/stat.h>
00026 #include <unistd.h>
00027 #include <stdlib.h>
00028 
00029 #include "katedocument.h"
00030 #include "katehighlight.h"
00031 #include "kateconfig.h"
00032 #include "kateglobal.h"
00033 #include "kateautoindent.h"
00034 
00035 #include <kdebug.h>
00036 #include <kglobal.h>
00037 #include <kcharsets.h>
00038 #include <kencodingprober.h>
00039 #include <kde_file.h>
00040 
00041 #include <QtCore/QFile>
00042 #include <QtCore/QTextStream>
00043 #include <QtCore/QTimer>
00044 #include <QtCore/QTextCodec>
00045 #include <QtCore/QDate>
00046 
00047 #include <limits.h>
00048 
00054 static const qint64 KATE_FILE_LOADER_BS  = 256 * 1024;
00055 
00061 static const int KATE_HL_LOOKAHEAD = 64;
00062 
00066 static const int KATE_MAX_DYNAMIC_CONTEXTS = 512;
00067 
00071 static const int KATE_AVERAGE_LINES_PER_BLOCK = 4 * 1024;
00072 
00073 class KateFileLoader
00074 {
00075   enum MIB
00076   {
00077     MibLatin1  = 4,
00078     Mib8859_8  = 85,
00079     MibUtf8    = 106,
00080     MibUcs2    = 1000,
00081     MibUtf16   = 1015,
00082     MibUtf16BE = 1013,
00083     MibUtf16LE = 1014
00084   };  
00085   public:
00086     KateFileLoader (const QString &filename, QTextCodec *codec, bool removeTrailingSpaces, KEncodingProber::ProberType proberType)
00087       : m_codec(codec)
00088       , m_prober(new KEncodingProber(proberType))
00089       , m_multiByte(0)
00090       , m_eof (false) // default to not eof
00091       , m_lastWasEndOfLine (true) // at start of file, we had a virtual newline
00092       , m_lastWasR (false) // we have not found a \r as last char
00093       , m_binary (false)
00094       , m_removeTrailingSpaces (removeTrailingSpaces)
00095       , m_utf8Borked (false)
00096       , m_position (0)
00097       , m_lastLineStart (0)
00098       , m_eol (-1) // no eol type detected atm
00099       , m_file (filename)
00100       , m_buffer (qMin (m_file.size() == 0 ? KATE_FILE_LOADER_BS : m_file.size(), KATE_FILE_LOADER_BS), 0) // handle zero sized files special, like in /proc
00101     {
00102     }
00103 
00104     ~KateFileLoader ()
00105     {
00106       //delete m_decoder;
00107     }
00108 
00112     bool open ()
00113     {
00114       if (m_file.open (QIODevice::ReadOnly))
00115       {
00116         int c = m_file.read (m_buffer.data(), m_buffer.size());
00117 
00118         if (c > 0)
00119         {
00120           // fixes utf16 LE
00121           //may change codec if autodetection was set or BOM was found
00122           kDebug (13020) << "PROBER TYPE: " << KEncodingProber::nameForProberType(m_prober->proberType());
00123           m_prober->feed(m_buffer.data(), c);
00124           if (m_prober->confidence() > 0.5)
00125             m_codec = QTextCodec::codecForName(m_prober->encodingName());
00126           m_utf8Borked=errorsIfUtf8(m_buffer.data(), c);
00127           m_binary=processNull(m_buffer.data(), c);
00128           m_text = decoder()->toUnicode(m_buffer, c);
00129           kDebug (13020) << "OPEN USES ENCODING: " << m_codec->name();
00130         }
00131 
00132         m_eof = (c == -1) || (c == 0);
00133 
00134         for (int i=0; i < m_text.length(); i++)
00135         {
00136           if (m_text[i] == '\n')
00137           {
00138             m_eol = KateDocumentConfig::eolUnix;
00139             break;
00140           }
00141           else if ((m_text[i] == '\r'))
00142           {
00143             if (((i+1) < m_text.length()) && (m_text[i+1] == '\n'))
00144             {
00145               m_eol = KateDocumentConfig::eolDos;
00146               break;
00147             }
00148             else
00149             {
00150               m_eol = KateDocumentConfig::eolMac;
00151               break;
00152             }
00153           }
00154         }
00155 
00156         return true;
00157       }
00158 
00159       return false;
00160     }
00161 
00162     inline QByteArray actualEncoding () const { return m_codec->name(); }
00163 
00164     // no new lines around ?
00165     inline bool eof () const { return m_eof && !m_lastWasEndOfLine && (m_lastLineStart == m_text.length()); }
00166 
00167     // eol mode ? autodetected on open(), -1 for no eol found in the first block!
00168     inline int eol () const { return m_eol; }
00169 
00170     // binary ?
00171     inline bool binary () const { return m_binary; }
00172 
00173     // broken utf8?
00174     inline bool brokenUTF8 () const { return m_utf8Borked; }
00175     
00176     inline QTextDecoder* decoder() const { return m_codec->makeDecoder(); }
00177 
00178     bool errorsIfUtf8 (const char* data, int length)
00179     {
00180         if (m_codec->mibEnum()!=MibUtf8)
00181             return false; //means no errors
00182         // #define highest1Bits (unsigned char)0x80
00183         // #define highest2Bits (unsigned char)0xC0
00184         // #define highest3Bits (unsigned char)0xE0
00185         // #define highest4Bits (unsigned char)0xF0
00186         // #define highest5Bits (unsigned char)0xF8
00187         static const unsigned char highest1Bits = 0x80;
00188         static const unsigned char highest2Bits = 0xC0;
00189         static const unsigned char highest3Bits = 0xE0;
00190         static const unsigned char highest4Bits = 0xF0;
00191         static const unsigned char highest5Bits = 0xF8;
00192 
00193         for (int i=0; i<length; ++i)
00194         {
00195             unsigned char c = data[i];
00196 
00197             if (m_multiByte>0)
00198             {
00199                 if ((c & highest2Bits) == 0x80)
00200                 {
00201                     --(m_multiByte);
00202                     continue;
00203                 }
00204                 return true;
00205             }
00206 
00207             // most significant bit zero, single char
00208             if ((c & highest1Bits) == 0x00)
00209                 continue;
00210 
00211             // 110xxxxx => init 1 following bytes
00212             if ((c & highest3Bits) == 0xC0)
00213             {
00214                 m_multiByte = 1;
00215                 continue;
00216             }
00217 
00218             // 1110xxxx => init 2 following bytes
00219             if ((c & highest4Bits) == 0xE0)
00220             {
00221                 m_multiByte = 2;
00222                 continue;
00223             }
00224 
00225             // 11110xxx => init 3 following bytes
00226             if ((c & highest5Bits) == 0xF0)
00227             {
00228                 m_multiByte = 3;
00229                 continue;
00230             }
00231             return true;
00232         }
00233         return false;
00234     }
00235     
00236     bool processNull(char *data, int len)
00237     {
00238       bool bin=false;
00239       if(is16Bit(m_codec))
00240       {
00241         for (int i=1; i < len; i+=2)
00242         {
00243           if ((data[i]=='\0') && (data[i-1]=='\0'))
00244           {
00245             bin=true;
00246             data[i]=' ';
00247           }
00248         }
00249         return bin;
00250       }
00251       // replace '\0' by spaces, for buggy pages
00252       int i = len-1;
00253       while(--i>=0)
00254       {
00255         if(data[i]==0)
00256         {
00257           bin=true;
00258           data[i]=' ';
00259         }
00260       }
00261       return bin;
00262     }
00263 
00264     // should spaces be ignored at end of line?
00265     inline bool removeTrailingSpaces () const { return m_removeTrailingSpaces; }
00266 
00267     // internal unicode data array
00268     inline const QChar *unicode () const { return m_text.unicode(); }
00269 
00270     // read a line, return length + offset in unicode data
00271     void readLine (int &offset, int &length)
00272     {
00273       length = 0;
00274       offset = 0;
00275 
00276       while (m_position <= m_text.length())
00277       {
00278         if (m_position == m_text.length())
00279         {
00280           // try to load more text if something is around
00281           if (!m_eof)
00282           {
00283             int c = m_file.read (m_buffer.data(), m_buffer.size());
00284 
00285             // kill the old lines...
00286             m_text.remove (0, m_lastLineStart);
00287 
00288             // if any text is there, append it....
00289             if (c > 0)
00290             {
00291               m_binary=processNull(m_buffer.data(), c)||m_binary;
00292               m_utf8Borked=m_utf8Borked||errorsIfUtf8(m_buffer.data(), c);
00293               m_text.append (decoder()->toUnicode (m_buffer.data(), c));
00294             }
00295 
00296             // is file completely read ?
00297             m_eof = (c == -1) || (c == 0);
00298 
00299             // recalc current pos and last pos
00300             m_position -= m_lastLineStart;
00301             m_lastLineStart = 0;
00302           }
00303 
00304           // oh oh, end of file, escape !
00305           if (m_eof && (m_position == m_text.length()))
00306           {
00307             m_lastWasEndOfLine = false;
00308 
00309             // line data
00310             offset = m_lastLineStart;
00311             length = m_position-m_lastLineStart;
00312 
00313             m_lastLineStart = m_position;
00314 
00315             return;
00316           }
00317         }
00318 
00319         if (m_text[m_position] == '\n')
00320         {
00321           m_lastWasEndOfLine = true;
00322 
00323           if (m_lastWasR)
00324           {
00325             m_lastLineStart++;
00326             m_lastWasR = false;
00327           }
00328           else
00329           {
00330             // line data
00331             offset = m_lastLineStart;
00332             length = m_position-m_lastLineStart;
00333 
00334             m_lastLineStart = m_position+1;
00335             m_position++;
00336 
00337             return;
00338           }
00339         }
00340         else if (m_text[m_position] == '\r')
00341         {
00342           m_lastWasEndOfLine = true;
00343           m_lastWasR = true;
00344 
00345           // line data
00346           offset = m_lastLineStart;
00347           length = m_position-m_lastLineStart;
00348 
00349           m_lastLineStart = m_position+1;
00350           m_position++;
00351 
00352           return;
00353         }
00354         else
00355         {
00356           m_lastWasEndOfLine = false;
00357           m_lastWasR = false;
00358         }
00359 
00360         m_position++;
00361       }
00362     }
00363 
00364   bool is16Bit(QTextCodec* codec)
00365   {
00366     switch (codec->mibEnum())
00367     {
00368       case MibUtf16:
00369       case MibUtf16BE:
00370       case MibUtf16LE:
00371       case MibUcs2:
00372         return true;
00373       default:
00374         return false;
00375     }
00376   }
00377 
00378   private:
00379     QTextCodec *m_codec;
00380     KEncodingProber *m_prober;
00381     int m_multiByte;
00382     bool m_eof;
00383     bool m_lastWasEndOfLine;
00384     bool m_lastWasR;
00385     bool m_binary;
00386     bool m_removeTrailingSpaces;
00387     bool m_utf8Borked;
00388     int m_position;
00389     int m_lastLineStart;
00390     int m_eol;
00391     QFile m_file;
00392     QByteArray m_buffer;
00393     QString m_text;
00394 };
00395 
00399 KateBuffer::KateBuffer(KateDocument *doc)
00400  : QObject (doc),
00401    editSessionNumber (0),
00402    editIsRunning (false),
00403    editTagLineStart (0xffffffff),
00404    editTagLineEnd (0),
00405    editTagLineFrom (false),
00406    editChangesDone (false),
00407    m_doc (doc),
00408    m_lastUsedBlock (0),
00409    m_lines (0),
00410    m_binary (false),
00411    m_brokenUTF8 (false),
00412    m_highlight (0),
00413    m_regionTree (this),
00414    m_tabWidth (8),
00415    m_lineHighlightedMax (0),
00416    m_lineHighlighted (0),
00417    m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS)
00418 {
00419   clear();
00420 }
00421 
00425 KateBuffer::~KateBuffer()
00426 {
00427   // release HL
00428   if (m_highlight)
00429     m_highlight->release();
00430 
00431   // kill all lines
00432   qDeleteAll (m_blocks);
00433 }
00434 
00435 void KateBuffer::editStart ()
00436 {
00437   editSessionNumber++;
00438 
00439   if (editSessionNumber > 1)
00440     return;
00441 
00442   editIsRunning = true;
00443 
00444   editTagLineStart = INT_MAX;
00445   editTagLineEnd = 0;
00446   editTagLineFrom = false;
00447 
00448   editChangesDone = false;
00449 }
00450 
00451 void KateBuffer::editEnd ()
00452 {
00453   if (editSessionNumber == 0)
00454     return;
00455 
00456   editSessionNumber--;
00457 
00458   if (editSessionNumber > 0)
00459     return;
00460 
00461   if (editChangesDone)
00462   {
00463     // hl update !!!
00464     if (m_highlight && editTagLineStart <= editTagLineEnd && editTagLineEnd <= m_lineHighlighted)
00465     {
00466       // look one line too far, needed for linecontinue stuff
00467       ++editTagLineEnd;
00468 
00469       // look one line before, needed nearly 100% only for indentation based folding !
00470       if (editTagLineStart > 0)
00471         --editTagLineStart;
00472 
00473       bool needContinue = doHighlight (
00474           editTagLineStart,
00475           editTagLineEnd,
00476           true);
00477 
00478       editTagLineStart = editTagLineEnd;
00479 
00480       if (needContinue)
00481         m_lineHighlighted = editTagLineStart;
00482 
00483       if (editTagLineStart > m_lineHighlightedMax)
00484         m_lineHighlightedMax = editTagLineStart;
00485     }
00486     else if (editTagLineStart < m_lineHighlightedMax)
00487       m_lineHighlightedMax = editTagLineStart;
00488   }
00489 
00490   editIsRunning = false;
00491 }
00492 
00493 void KateBuffer::clear()
00494 {
00495   m_regionTree.clear();
00496 
00497   // kill all blocks
00498   qDeleteAll (m_blocks);
00499   m_lastUsedBlock = 0;
00500   m_blocks.clear ();
00501   
00502   // one block
00503   m_blocks.append (new KateBufferBlock(0));
00504 
00505   // one line
00506   KateTextLine::Ptr textLine (new KateTextLine ());
00507   m_blocks[0]->lines.append (textLine);
00508   m_lines = 1;
00509 
00510   // reset the state
00511   m_binary = false;
00512   m_brokenUTF8 = false;
00513 
00514   m_lineHighlightedMax = 0;
00515   m_lineHighlighted = 0;
00516 }
00517 
00518 bool KateBuffer::openFile (const QString &m_file)
00519 {
00520    QTime t;
00521    t.start();
00522 
00523   KateFileLoader file (m_file, m_doc->config()->codec(), m_doc->config()->configFlags() & KateDocumentConfig::cfRemoveSpaces, m_doc->proberTypeForEncodingAutoDetection());
00524 
00525   bool ok = false;
00526   KDE_struct_stat sbuf;
00527   if (KDE_stat(QFile::encodeName(m_file), &sbuf) == 0)
00528   {
00529     if (S_ISREG(sbuf.st_mode) && file.open())
00530       ok = true;
00531   }
00532 
00533   if (!ok)
00534   {
00535     clear();
00536     return false; // Error
00537   }
00538 
00539   m_doc->config()->setEncoding(file.actualEncoding());
00540 
00541   // set eol mode, if a eol char was found in the first 256kb block and we allow this at all!
00542   if (m_doc->config()->allowEolDetection() && (file.eol() != -1))
00543     m_doc->config()->setEol (file.eol());
00544 
00545   // flush current content, one line stays, therefor, remove that
00546   clear ();
00547 
00548   // clear first block
00549   m_blocks[0]->lines.clear ();
00550   m_lines = 0;
00551 
00552   // read in all lines...
00553   while ( !file.eof() )
00554   {
00555     int offset = 0, length = 0;
00556     file.readLine(offset, length);
00557     const QChar *unicodeData = file.unicode () + offset;
00558 
00559     // strip spaces at end of line
00560     if ( file.removeTrailingSpaces() )
00561     {
00562       while (length > 0)
00563       {
00564         if (unicodeData[length-1].isSpace())
00565           --length;
00566         else
00567           break;
00568       }
00569     }
00570 
00571     KateTextLine::Ptr textLine (new KateTextLine (unicodeData, length));
00572     
00573     if (m_blocks.last()->lines.size() >= KATE_AVERAGE_LINES_PER_BLOCK)
00574       m_blocks.append (new KateBufferBlock (m_lines));
00575 
00576     m_blocks.last()->lines.append (textLine);    
00577     m_lines++;
00578   }
00579 
00580   // file was really empty, but we need ONE LINE!!!
00581   if (m_lines == 0)
00582   {
00583     KateTextLine::Ptr textLine (new KateTextLine ());
00584     m_blocks[0]->lines.append (textLine);
00585     m_lines = 1;
00586   }
00587 
00588   // fix region tree
00589   m_regionTree.fixRoot (m_lines);
00590 
00591   // binary?
00592   m_binary = file.binary ();
00593 
00594   // broken utf-8?
00595   m_brokenUTF8 = file.brokenUTF8();
00596   
00597   kDebug (13020) << "Broken UTF-8: " << m_brokenUTF8;
00598 
00599   kDebug (13020) << "LOADING DONE " << t.elapsed();
00600 
00601   return true;
00602 }
00603 
00604 bool KateBuffer::canEncode ()
00605 {
00606   QTextCodec *codec = m_doc->config()->codec();
00607 
00608   kDebug(13020) << "ENC NAME: " << codec->name();
00609 
00610   // hardcode some unicode encodings which can encode all chars
00611   if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2"))
00612     return true;
00613 
00614   for (int i=0; i < m_lines; i++)
00615   {
00616     if (!codec->canEncode (plainLine(i)->string()))
00617     {
00618       kDebug(13020) << "STRING LINE: " << plainLine(i)->string();
00619       kDebug(13020) << "ENC WORKING: FALSE";
00620 
00621       return false;
00622     }
00623   }
00624 
00625   return true;
00626 }
00627 
00628 bool KateBuffer::saveFile (const QString &m_file)
00629 {
00630   QFile file (m_file);
00631   QTextStream stream (&file);
00632 
00633   if ( !file.open( QIODevice::WriteOnly ) )
00634   {
00635     return false; // Error
00636   }
00637 
00638   QTextCodec *codec = m_doc->config()->codec();
00639 
00640   // disable Unicode headers
00641   stream.setCodec(QTextCodec::codecForName("UTF-16"));
00642 
00643   // this line sets the mapper to the correct codec
00644   stream.setCodec(codec);
00645 
00646   // our loved eol string ;)
00647   QString eol = m_doc->config()->eolString ();
00648 
00649   // should we strip spaces?
00650   bool removeTrailingSpaces = m_doc->config()->configFlags() & KateDocumentConfig::cfRemoveSpaces;
00651 
00652   // just dump the lines out ;)
00653   for (int i=0; i < m_lines; i++)
00654   {
00655     KateTextLine::Ptr textline = plainLine(i);
00656 
00657     // strip spaces
00658     if (removeTrailingSpaces)
00659     {
00660       int lastChar = textline->lastChar();
00661 
00662       if (lastChar > -1)
00663       {
00664         stream << textline->string().left(lastChar+1);
00665       }
00666     }
00667     else // simple, dump the line
00668       stream << textline->string();
00669 
00670     if ((i+1) < m_lines)
00671       stream << eol;
00672   }
00673 
00674   file.close ();
00675 
00676   return (file.error() == QFile::NoError);
00677 }
00678 
00679 int KateBuffer::findBlock (int line)
00680 {
00681   // invalid line!
00682   if (line < 0 || line >= m_lines)
00683     return -1;
00684 
00685   // reset invalid last blocks
00686   if (m_lastUsedBlock < 0 || m_lastUsedBlock >= m_blocks.size())
00687     m_lastUsedBlock = 0;
00688 
00689   forever
00690   {
00691     int start = m_blocks[m_lastUsedBlock]->start;
00692     int lines = m_blocks[m_lastUsedBlock]->lines.size ();
00693 
00694     if (start <= line && line < (start + lines))
00695       return m_lastUsedBlock;
00696 
00697     if (line < start)
00698       m_lastUsedBlock--;
00699     else
00700       m_lastUsedBlock++;
00701   }
00702 
00703   return -1;
00704 }
00705 
00706 void KateBuffer::fixBlocksFrom (int lastValidBlock)
00707 {
00712   // last block with valid start
00713   KateBufferBlock *block = m_blocks[lastValidBlock];
00714 
00715   // lines in block
00716   int blockLines = block->lines.size();
00717 
00718   // remember last correct lastline
00719   int lastLine = block->start + blockLines;
00720  
00721   // kill empty blocks
00722   if (blockLines == 0 && m_blocks.size() > 0)
00723   {
00724     delete block;
00725     m_blocks.remove (lastValidBlock);
00726 
00727     // set new last valid block, it's even ok to have here -1!
00728     lastValidBlock--;
00729 
00730     // adjust last used block in all cases
00731     m_lastUsedBlock--;
00732   }
00733   else if (blockLines > (2*KATE_AVERAGE_LINES_PER_BLOCK)) // try to balance blocks
00734   {
00735     int linesToStay = blockLines - KATE_AVERAGE_LINES_PER_BLOCK;
00736 
00737     // construct new block
00738     KateBufferBlock *newBlock = new KateBufferBlock (lastLine - KATE_AVERAGE_LINES_PER_BLOCK);
00739     m_blocks.insert (lastValidBlock+1, newBlock);
00740 
00741     // move lines
00742     newBlock->lines.resize (KATE_AVERAGE_LINES_PER_BLOCK);
00743     for (int i = 0; i < KATE_AVERAGE_LINES_PER_BLOCK; ++i)
00744       newBlock->lines[i] = block->lines[linesToStay + i];
00745 
00746     // resize old block
00747     block->lines.resize (linesToStay);
00748 
00749     // new block is current
00750     block = newBlock;
00751     lastValidBlock++;
00752   }
00753   
00754   // loop over all blocks behind last correct to fix start line
00755   for (int i = lastValidBlock + 1; i < m_blocks.size(); ++i)
00756   {
00757     m_blocks[i]->start = lastLine;
00758     lastLine += m_blocks[i]->lines.size();
00759   }
00760 }
00761 
00762 void KateBuffer::ensureHighlighted (int line)
00763 {
00764   // valid line at all?
00765   if (line < 0 || line >= m_lines)
00766     return;
00767 
00768   // already hl up-to-date for this line?
00769   if (line < m_lineHighlighted)
00770     return;
00771 
00772   // update hl until this line + max KATE_HL_LOOKAHEAD
00773   int end = qMin(line + KATE_HL_LOOKAHEAD, m_lines-1);
00774 
00775   doHighlight ( m_lineHighlighted, end, false );
00776 
00777   m_lineHighlighted = end;
00778 
00779   // update hl max
00780   if (m_lineHighlighted > m_lineHighlightedMax)
00781     m_lineHighlightedMax = m_lineHighlighted;
00782 }
00783 
00784 void KateBuffer::changeLine(int i)
00785 {
00786   if (i < 0 || i >= m_lines)
00787     return;
00788 
00789   // mark buffer changed
00790   editChangesDone = true;
00791 
00792   // tag this line as changed
00793   if (i < editTagLineStart)
00794     editTagLineStart = i;
00795 
00796   if (i > editTagLineEnd)
00797     editTagLineEnd = i;
00798 }
00799 
00800 void KateBuffer::insertLine(int i, KateTextLine::Ptr line)
00801 {
00802   if (i < 0 || i > m_lines)
00803     return;
00804 
00805   // get block
00806   int block = findBlock (i);
00807   if (block == -1)
00808     block = m_blocks.size() - 1;
00809 
00810   // insert line
00811   m_blocks[block]->lines.insert (i - m_blocks[block]->start, line);
00812   m_lines++;
00813   fixBlocksFrom (block);
00814 
00815   if (m_lineHighlightedMax > i)
00816     m_lineHighlightedMax++;
00817 
00818   if (m_lineHighlighted > i)
00819     m_lineHighlighted++;
00820 
00821   // mark buffer changed
00822   editChangesDone = true;
00823 
00824   // tag this line as inserted
00825   if (i < editTagLineStart)
00826     editTagLineStart = i;
00827 
00828   if (i <= editTagLineEnd)
00829     editTagLineEnd++;
00830 
00831   if (i > editTagLineEnd)
00832     editTagLineEnd = i;
00833 
00834   // line inserted
00835   editTagLineFrom = true;
00836 
00837   m_regionTree.lineHasBeenInserted (i);
00838 }
00839 
00840 void KateBuffer::removeLine(int i)
00841 {
00842   int block = findBlock (i);
00843 
00844   if (block == -1)
00845     return;
00846 
00847   // remove line
00848   m_blocks[block]->lines.remove (i - m_blocks[block]->start);
00849   m_lines--;
00850   fixBlocksFrom (block);
00851 
00852   if (m_lineHighlightedMax > i)
00853     m_lineHighlightedMax--;
00854 
00855   if (m_lineHighlighted > i)
00856     m_lineHighlighted--;
00857 
00858   // mark buffer changed
00859   editChangesDone = true;
00860 
00861   // tag this line as removed
00862    if (i < editTagLineStart)
00863     editTagLineStart = i;
00864 
00865   if (i < editTagLineEnd)
00866     editTagLineEnd--;
00867 
00868   if (i > editTagLineEnd)
00869     editTagLineEnd = i;
00870 
00871   // make sure tags do not reach past the last line
00872   // see https://bugs.kde.org/show_bug.cgi?id=152497
00873   if (editTagLineEnd >= m_lines)
00874     editTagLineEnd = m_lines - 1;
00875 
00876   if (editTagLineStart > editTagLineEnd)
00877     editTagLineStart = editTagLineEnd;
00878 
00879   // line removed
00880   editTagLineFrom = true;
00881 
00882   m_regionTree.lineHasBeenRemoved (i);
00883 }
00884 
00885 void KateBuffer::setTabWidth (int w)
00886 {
00887   if ((m_tabWidth != w) && (m_tabWidth > 0))
00888   {
00889     m_tabWidth = w;
00890 
00891     if (m_highlight && m_highlight->foldingIndentationSensitive())
00892       invalidateHighlighting();
00893   }
00894 }
00895 
00896 void KateBuffer::setHighlight(int hlMode)
00897 {
00898   KateHighlighting *h = KateHlManager::self()->getHl(hlMode);
00899 
00900    // aha, hl will change
00901   if (h != m_highlight)
00902   {
00903     bool invalidate = !h->noHighlighting();
00904 
00905     if (m_highlight)
00906     {
00907       m_highlight->release();
00908       invalidate = true;
00909     }
00910 
00911     h->use();
00912 
00913     // Clear code folding tree (see bug #124102)
00914     m_regionTree.clear();
00915     m_regionTree.fixRoot(m_lines);
00916 
00917     // try to set indentation
00918     if (!h->indentation().isEmpty())
00919       m_doc->config()->setIndentationMode (h->indentation());
00920 
00921     m_highlight = h;
00922 
00923     if (invalidate)
00924       invalidateHighlighting();
00925 
00926     // inform the document that the hl was really changed
00927     // needed to update attributes and more ;)
00928     m_doc->bufferHlChanged ();
00929   }
00930 }
00931 
00932 void KateBuffer::invalidateHighlighting()
00933 {
00934   m_lineHighlightedMax = 0;
00935   m_lineHighlighted = 0;
00936 }
00937 
00938 
00939 void KateBuffer::updatePreviousNotEmptyLine(int current_line,bool addindent,int deindent)
00940 {
00941   KateTextLine::Ptr textLine;
00942   do {
00943     if (current_line == 0) return;
00944 
00945     --current_line;
00946 
00947     textLine = plainLine (current_line);
00948   } while (textLine->firstChar()==-1);
00949 
00950   kDebug(13020)<<"updatePreviousNotEmptyLine: updating line:"<<current_line;
00951   QVector<int> foldingList=textLine->foldingListArray();
00952   while ( (foldingList.size()>0)  && ( abs(foldingList[foldingList.size()-2])==1)) {
00953     foldingList.resize(foldingList.size()-2);
00954   }
00955   addIndentBasedFoldingInformation(foldingList,textLine->length(),addindent,deindent);
00956   textLine->setFoldingList(foldingList);
00957 
00958   bool retVal_folding = false;
00959   m_regionTree.updateLine (current_line, &foldingList, &retVal_folding, true,false);
00960 
00961   // tagLines() is emitted from KatBuffer::doHighlight()!
00962 }
00963 
00964 void KateBuffer::addIndentBasedFoldingInformation(QVector<int> &foldingList,int linelength,bool addindent,int deindent)
00965 {
00966   if (addindent) {
00967     //kDebug(13020)<<"adding indent for line :"<<current_line + buf->startLine()<<"  textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart();
00968     kDebug(13020)<<"adding ident";
00969     foldingList.resize (foldingList.size() + 2);
00970     foldingList[foldingList.size()-2] = 1;
00971     foldingList[foldingList.size()-1] = 0;
00972   }
00973   kDebug(13020)<<"DEINDENT: "<<deindent;
00974   if (deindent > 0)
00975   {
00976     //foldingList.resize (foldingList.size() + (deindent*2));
00977 
00978     //Make the whole last line marked as still belonging to the block
00979     for (int z=0;z<deindent;z++) {
00980       //FIXME: Not sure if this is really a performance problem
00981       foldingList.prepend(linelength+1);
00982       foldingList.prepend(-1);
00983     }
00984 
00985 /*    for (int z= foldingList.size()-(deindent*2); z < foldingList.size(); z=z+2)
00986     {
00987       foldingList[z] = -1;
00988       foldingList[z+1] = 0;
00989     }*/
00990   }
00991 }
00992 
00993 
00994 bool KateBuffer::isEmptyLine(KateTextLine::Ptr textline)
00995 {
00996   QLinkedList<QRegExp> l;
00997   l=m_highlight->emptyLines(textline->attribute(0));
00998   kDebug(13020)<<"trying to find empty line data";
00999   if (l.isEmpty()) return false;
01000   QString txt=textline->string();
01001   kDebug(13020)<<"checking empty line regexp";
01002   foreach(const QRegExp &re,l) {
01003     if (re.exactMatch(txt)) return true;
01004   }
01005   kDebug(13020)<<"no matches";
01006   return false;
01007 }
01008 
01009 bool KateBuffer::doHighlight (int startLine, int endLine, bool invalidate)
01010 {
01011   // no hl around, no stuff to do
01012   if (!m_highlight)
01013     return false;
01014 
01015   /*if (m_highlight->foldingIndentationSensitive())
01016   {
01017     startLine=0;
01018     endLine=50;
01019   }*/
01020 
01021   //QTime t;
01022   //t.start();
01023   //kDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine;
01024   //kDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax;
01025   //kDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts;
01026 
01027   // see if there are too many dynamic contexts; if yes, invalidate HL of all documents
01028   if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts)
01029   {
01030     {
01031       if (KateHlManager::self()->resetDynamicCtxs())
01032       {
01033         kDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")";
01034 
01035         // avoid recursive invalidation
01036         KateHlManager::self()->setForceNoDCReset(true);
01037 
01038         for (int i=0; i < KateGlobal::self()->kateDocuments().size(); ++i)
01039           (KateGlobal::self()->kateDocuments())[i]->makeAttribs();
01040 
01041         // doHighlight *shall* do his work. After invalidation, some highlight has
01042         // been recalculated, but *maybe not* until endLine ! So we shall force it manually...
01043         doHighlight ( m_lineHighlighted, endLine, false );
01044         m_lineHighlighted = endLine;
01045 
01046         KateHlManager::self()->setForceNoDCReset(false);
01047 
01048         return false;
01049       }
01050       else
01051       {
01052         m_maxDynamicContexts *= 2;
01053         kDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts;
01054       }
01055     }
01056   }
01057 
01058   // get previous line, if any
01059   KateTextLine::Ptr prevLine;
01060 
01061   if (startLine >= 1)
01062     prevLine = plainLine (startLine-1);
01063   else
01064     prevLine = new KateTextLine ();
01065 
01066   // does we need to emit a signal for the folding changes ?
01067   bool codeFoldingUpdate = false;
01068 
01069   // here we are atm, start at start line in the block
01070   int current_line = startLine;
01071 
01072   // do we need to continue
01073   bool stillcontinue=false;
01074   bool indentContinueWhitespace=false;
01075   bool indentContinueNextWhitespace=false;
01076   // loop over the lines of the block, from startline to endline or end of block
01077   // if stillcontinue forces us to do so
01078   while ( (current_line < m_lines) && (stillcontinue || (current_line <= endLine)) )
01079   {
01080     // current line
01081     KateTextLine::Ptr textLine = plainLine (current_line);
01082 
01083     QVector<int> foldingList;
01084     bool ctxChanged = false;
01085 
01086     m_highlight->doHighlight (prevLine.data(), textLine.data(), foldingList, ctxChanged);
01087 
01088     // debug stuff
01089     //kDebug( 13020 ) << "current line to hl: " << current_line + buf->startLine();
01090     //kDebug( 13020 ) << "text length: " << textLine->length() << " attribute list size: " << textLine->attributesList().size();
01091     /*
01092     const QVector<int> &ml (textLine->attributesList());
01093     for (int i=2; i < ml.size(); i+=3)
01094     {
01095       kDebug( 13020 ) << "start: " << ml[i-2] << " len: " << ml[i-1] << " at: " << ml[i] << " ";
01096     }
01097     kDebug( 13020 );
01098 */
01099     //
01100     // indentation sensitive folding
01101     //
01102     bool indentChanged = false;
01103     if (m_highlight->foldingIndentationSensitive())
01104     {
01105       // get the indentation array of the previous line to start with !
01106       QVector<unsigned short> indentDepth (prevLine->indentationDepthArray());
01107 
01108       // current indentation of this line
01109       int iDepth = textLine->indentDepth(m_tabWidth);
01110       if (current_line==0)
01111       {
01112           indentDepth.resize (1);
01113           indentDepth[0] = iDepth;
01114       }
01115 
01116       textLine->setNoIndentBasedFoldingAtStart(prevLine->noIndentBasedFolding());
01117       // this line is empty, beside spaces, or has indentaion based folding disabled, use indentation depth of the previous line !
01118       kDebug(13020)<<"current_line:"<<current_line<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart();
01119       if ( (textLine->firstChar() == -1) || textLine->noIndentBasedFoldingAtStart() || isEmptyLine(textLine) )
01120       {
01121         // do this to get skipped empty lines indent right, which was given in the indenation array
01122         if (!prevLine->indentationDepthArray().isEmpty())
01123         {
01124           iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1];
01125           kDebug(13020)<<"reusing old depth as current";
01126         }
01127         else
01128         {
01129           iDepth = prevLine->indentDepth(m_tabWidth);
01130           kDebug(13020)<<"creating indentdepth for previous line";
01131         }
01132       }
01133 
01134       kDebug(13020)<<"iDepth:"<<iDepth;
01135 
01136       // query the next line indentation, if we are at the end of the block
01137       // use the first line of the next buf block
01138       int nextLineIndentation = 0;
01139       bool nextLineIndentationValid=true;
01140       indentContinueNextWhitespace=false;
01141       if ((current_line+1) < m_lines)
01142       {
01143         if ( (plainLine (current_line+1)->firstChar() == -1) || isEmptyLine(plainLine (current_line+1)) )
01144         {
01145           nextLineIndentation = iDepth;
01146           indentContinueNextWhitespace=true;
01147         }
01148         else
01149           nextLineIndentation = plainLine (current_line+1)->indentDepth(m_tabWidth);
01150       }
01151       else
01152       {
01153         nextLineIndentationValid=false;
01154       }
01155 
01156       if  (!textLine->noIndentBasedFoldingAtStart()) {
01157 
01158         if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
01159         {
01160           kDebug(13020)<<"adding depth to \"stack\":"<<iDepth;
01161           indentDepth.append (iDepth);
01162         } else {
01163           if (!indentDepth.isEmpty())
01164           {
01165             for (int z=indentDepth.size()-1; z > -1; z--)
01166               if (indentDepth[z]>iDepth)
01167                 indentDepth.resize(z);
01168             if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
01169             {
01170               kDebug(13020)<<"adding depth to \"stack\":"<<iDepth;
01171               indentDepth.append (iDepth);
01172               if (prevLine->firstChar()==-1) {
01173 
01174               }
01175             }
01176           }
01177         }
01178       }
01179 
01180       if (!textLine->noIndentBasedFolding())
01181       {
01182         if (nextLineIndentationValid)
01183         {
01184           //if (textLine->firstChar()!=-1)
01185           {
01186             kDebug(13020)<<"nextLineIndentation:"<<nextLineIndentation;
01187             bool addindent=false;
01188             int deindent=0;
01189             if (!indentDepth.isEmpty())
01190               kDebug(13020)<<"indentDepth[indentDepth.size()-1]:"<<indentDepth[indentDepth.size()-1];
01191             if ((nextLineIndentation>0) && ( indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1]<nextLineIndentation)))
01192             {
01193               kDebug(13020)<<"addindent==true";
01194               addindent=true;
01195             } else {
01196             if ((!indentDepth.isEmpty()) && (indentDepth[indentDepth.size()-1]>nextLineIndentation))
01197               {
01198                 kDebug(13020)<<"....";
01199                 for (int z=indentDepth.size()-1; z > -1; z--)
01200                 {
01201                   kDebug(13020)<<indentDepth[z]<<"  "<<nextLineIndentation;
01202                   if (indentDepth[z]>nextLineIndentation)
01203                     deindent++;
01204                 }
01205               }
01206             }
01207 /*        }
01208         if (textLine->noIndentBasedFolding()) kDebug(13020)<<"=============================indentation based folding disabled======================";
01209         if (!textLine->noIndentBasedFolding()) {*/
01210             if ((textLine->firstChar()==-1)) {
01211               updatePreviousNotEmptyLine(current_line,addindent,deindent);
01212               codeFoldingUpdate=true;
01213             }
01214             else
01215             {
01216               addIndentBasedFoldingInformation(foldingList,textLine->length(),addindent,deindent);
01217             }
01218           }
01219         }
01220       }
01221       indentChanged = !(indentDepth == textLine->indentationDepthArray());
01222 
01223       // assign the new array to the textline !
01224       if (indentChanged)
01225         textLine->setIndentationDepth (indentDepth);
01226 
01227       indentContinueWhitespace=textLine->firstChar()==-1;
01228     }
01229     bool foldingColChanged=false;
01230     bool foldingChanged = false; 
01231     if (foldingList.size()!=textLine->foldingListArray().size()) {
01232       foldingChanged=true;
01233     } else {
01234       QVector<int>::ConstIterator it=foldingList.constBegin();
01235       QVector<int>::ConstIterator it1=textLine->foldingListArray().constBegin();
01236       bool markerType=true;
01237       for(;it!=foldingList.constEnd();++it,++it1) {
01238         if  (markerType) {
01239           if ( ((*it)!=(*it1))) {
01240             foldingChanged=true;
01241             foldingColChanged=false;
01242             break;
01243           }
01244         } else {
01245             if ((*it)!=(*it1)) {
01246               foldingColChanged=true;
01247             }
01248         }
01249         markerType=!markerType;
01250       }
01251     }
01252 
01253     if (foldingChanged || foldingColChanged) {
01254       textLine->setFoldingList(foldingList);
01255       if (foldingChanged==false){
01256         textLine->setFoldingColumnsOutdated(textLine->foldingColumnsOutdated() | foldingColChanged);
01257       } else textLine->setFoldingColumnsOutdated(false);
01258     }
01259     bool retVal_folding = false;
01260     //perhaps make en enums out of the change flags
01261     m_regionTree.updateLine (current_line, &foldingList, &retVal_folding, foldingChanged,foldingColChanged);
01262 
01263     codeFoldingUpdate = codeFoldingUpdate | retVal_folding;
01264 
01265     // need we to continue ?
01266     stillcontinue = ctxChanged || indentChanged || indentContinueWhitespace || indentContinueNextWhitespace;
01267 
01268     // move around the lines
01269     prevLine = textLine;
01270 
01271     // increment line
01272     current_line++;
01273   }
01274 
01275   // tag the changed lines !
01276   if (invalidate)
01277     emit tagLines (startLine, current_line);
01278 
01279   // emit that we have changed the folding
01280   if (codeFoldingUpdate)
01281     emit codeFoldingUpdated();
01282 
01283   kDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine;
01284   kDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax;
01285   kDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts;
01286   //kDebug (13020) << "TIME TAKEN: " << t.elapsed();
01287 
01288   // if we are at the last line of the block + we still need to continue
01289   // return the need of that !
01290   return stillcontinue;
01291 }
01292 
01293 void KateBuffer::codeFoldingColumnUpdate(int lineNr) {
01294   KateTextLine::Ptr line=plainLine(lineNr);
01295   if (!line) return;
01296   if (line->foldingColumnsOutdated()) {
01297     line->setFoldingColumnsOutdated(false);
01298     bool tmp;
01299     QVector<int> folding=line->foldingListArray();
01300     m_regionTree.updateLine(lineNr,&folding,&tmp,true,false);
01301   }
01302 }
01303 
01304 // kate: space-indent on; indent-width 2; replace-tabs on;

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal