You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1661 lines
43 KiB
1661 lines
43 KiB
/* This file is part of the KDE libraries
|
|
Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
|
|
Copyright (C) 2002-2004 Christoph Cullmann <cullmann@kde.org>
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public
|
|
License version 2 as published by the Free Software Foundation.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include "katebuffer.h"
|
|
#include "katebuffer.moc"
|
|
|
|
#include "katedocument.h"
|
|
#include "katehighlight.h"
|
|
#include "kateconfig.h"
|
|
#include "katefactory.h"
|
|
#include "kateautoindent.h"
|
|
|
|
#include <kdebug.h>
|
|
#include <kglobal.h>
|
|
#include <kcharsets.h>
|
|
|
|
#include <qpopupmenu.h>
|
|
#include <qfile.h>
|
|
#include <qtextstream.h>
|
|
#include <qtimer.h>
|
|
#include <qtextcodec.h>
|
|
#include <qcstring.h>
|
|
#include <qdatetime.h>
|
|
|
|
/**
|
|
* loader block size, load 256 kb at once per default
|
|
* if file size is smaller, fall back to file size
|
|
*/
|
|
static const Q_ULONG KATE_FILE_LOADER_BS = 256 * 1024;
|
|
|
|
/**
|
|
* KATE_AVG_BLOCK_SIZE is in characters !
|
|
* (internaly we calc with approx 80 chars per line !)
|
|
* block will max contain around BLOCK_SIZE chars or
|
|
* BLOCK_LINES lines (after load, later that won't be tracked)
|
|
*/
|
|
static const Q_ULONG KATE_AVG_BLOCK_SIZE = 2048 * 80;
|
|
static const Q_ULONG KATE_MAX_BLOCK_LINES = 2048;
|
|
|
|
/**
|
|
* hl will look at the next KATE_HL_LOOKAHEAD lines
|
|
* or until the current block ends if a line is requested
|
|
* will avoid to run doHighlight too often
|
|
*/
|
|
static const uint KATE_HL_LOOKAHEAD = 64;
|
|
|
|
/**
|
|
* KATE_MAX_BLOCKS_LOADED should be at least 4, as some
|
|
* methodes will cause heavy trashing, if not at least the
|
|
* latest 2-3 used blocks are alive
|
|
*/
|
|
uint KateBuffer::m_maxLoadedBlocks = 16;
|
|
|
|
/**
|
|
* Initial value for m_maxDynamicContexts
|
|
*/
|
|
static const uint KATE_MAX_DYNAMIC_CONTEXTS = 512;
|
|
|
|
void KateBuffer::setMaxLoadedBlocks (uint count)
|
|
{
|
|
m_maxLoadedBlocks = kMax (4U, count);
|
|
}
|
|
|
|
class KateFileLoader
|
|
{
|
|
public:
|
|
KateFileLoader (const QString &filename, QTextCodec *codec, bool removeTrailingSpaces)
|
|
: m_file (filename)
|
|
, m_buffer (kMin (m_file.size(), KATE_FILE_LOADER_BS))
|
|
, m_codec (codec)
|
|
, m_decoder (m_codec->makeDecoder())
|
|
, m_position (0)
|
|
, m_lastLineStart (0)
|
|
, m_eof (false) // default to not eof
|
|
, lastWasEndOfLine (true) // at start of file, we had a virtual newline
|
|
, lastWasR (false) // we have not found a \r as last char
|
|
, m_eol (-1) // no eol type detected atm
|
|
, m_twoByteEncoding (QString(codec->name()) == "ISO-10646-UCS-2")
|
|
, m_binary (false)
|
|
, m_removeTrailingSpaces (removeTrailingSpaces)
|
|
{
|
|
kdDebug (13020) << "OPEN USES ENCODING: " << m_codec->name() << endl;
|
|
}
|
|
|
|
~KateFileLoader ()
|
|
{
|
|
delete m_decoder;
|
|
}
|
|
|
|
/**
|
|
* open file, read first chunk of data, detect eol
|
|
*/
|
|
bool open ()
|
|
{
|
|
if (m_file.open (IO_ReadOnly))
|
|
{
|
|
int c = m_file.readBlock (m_buffer.data(), m_buffer.size());
|
|
|
|
if (c > 0)
|
|
{
|
|
// fix utf16 LE, stolen from khtml ;)
|
|
if ((c >= 2) && (m_codec->mibEnum() == 1000) && (m_buffer[1] == 0x00))
|
|
{
|
|
// utf16LE, we need to put the decoder in LE mode
|
|
char reverseUtf16[3] = {0xFF, 0xFE, 0x00};
|
|
m_decoder->toUnicode(reverseUtf16, 2);
|
|
}
|
|
|
|
processNull (c);
|
|
m_text = m_decoder->toUnicode (m_buffer, c);
|
|
}
|
|
|
|
m_eof = (c == -1) || (c == 0) || (m_text.length() == 0) || m_file.atEnd();
|
|
|
|
for (uint i=0; i < m_text.length(); i++)
|
|
{
|
|
if (m_text[i] == '\n')
|
|
{
|
|
m_eol = KateDocumentConfig::eolUnix;
|
|
break;
|
|
}
|
|
else if ((m_text[i] == '\r'))
|
|
{
|
|
if (((i+1) < m_text.length()) && (m_text[i+1] == '\n'))
|
|
{
|
|
m_eol = KateDocumentConfig::eolDos;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
m_eol = KateDocumentConfig::eolMac;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// no new lines around ?
|
|
inline bool eof () const { return m_eof && !lastWasEndOfLine && (m_lastLineStart == m_text.length()); }
|
|
|
|
// eol mode ? autodetected on open(), -1 for no eol found in the first block!
|
|
inline int eol () const { return m_eol; }
|
|
|
|
// binary ?
|
|
inline bool binary () const { return m_binary; }
|
|
|
|
// should spaces be ignored at end of line?
|
|
inline bool removeTrailingSpaces () const { return m_removeTrailingSpaces; }
|
|
|
|
// internal unicode data array
|
|
inline const QChar *unicode () const { return m_text.unicode(); }
|
|
|
|
// read a line, return length + offset in unicode data
|
|
void readLine (uint &offset, uint &length)
|
|
{
|
|
length = 0;
|
|
offset = 0;
|
|
|
|
while (m_position <= m_text.length())
|
|
{
|
|
if (m_position == m_text.length())
|
|
{
|
|
// try to load more text if something is around
|
|
if (!m_eof)
|
|
{
|
|
int c = m_file.readBlock (m_buffer.data(), m_buffer.size());
|
|
|
|
uint readString = 0;
|
|
if (c > 0)
|
|
{
|
|
processNull (c);
|
|
|
|
QString str (m_decoder->toUnicode (m_buffer, c));
|
|
readString = str.length();
|
|
|
|
m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart)
|
|
+ str;
|
|
}
|
|
else
|
|
m_text = m_text.mid (m_lastLineStart, m_position-m_lastLineStart);
|
|
|
|
// is file completly read ?
|
|
m_eof = (c == -1) || (c == 0) || (readString == 0) || m_file.atEnd();
|
|
|
|
// recalc current pos and last pos
|
|
m_position -= m_lastLineStart;
|
|
m_lastLineStart = 0;
|
|
}
|
|
|
|
// oh oh, end of file, escape !
|
|
if (m_eof && (m_position == m_text.length()))
|
|
{
|
|
lastWasEndOfLine = false;
|
|
|
|
// line data
|
|
offset = m_lastLineStart;
|
|
length = m_position-m_lastLineStart;
|
|
|
|
m_lastLineStart = m_position;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m_text[m_position] == '\n')
|
|
{
|
|
lastWasEndOfLine = true;
|
|
|
|
if (lastWasR)
|
|
{
|
|
m_lastLineStart++;
|
|
lastWasR = false;
|
|
}
|
|
else
|
|
{
|
|
// line data
|
|
offset = m_lastLineStart;
|
|
length = m_position-m_lastLineStart;
|
|
|
|
m_lastLineStart = m_position+1;
|
|
m_position++;
|
|
|
|
return;
|
|
}
|
|
}
|
|
else if (m_text[m_position] == '\r')
|
|
{
|
|
lastWasEndOfLine = true;
|
|
lastWasR = true;
|
|
|
|
// line data
|
|
offset = m_lastLineStart;
|
|
length = m_position-m_lastLineStart;
|
|
|
|
m_lastLineStart = m_position+1;
|
|
m_position++;
|
|
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
lastWasEndOfLine = false;
|
|
lastWasR = false;
|
|
}
|
|
|
|
m_position++;
|
|
}
|
|
}
|
|
|
|
// this nice methode will kill all 0 bytes (or double bytes)
|
|
// and remember if this was a binary or not ;)
|
|
void processNull (uint length)
|
|
{
|
|
if (m_twoByteEncoding)
|
|
{
|
|
for (uint i=1; i < length; i+=2)
|
|
{
|
|
if ((m_buffer[i] == 0) && (m_buffer[i-1] == 0))
|
|
{
|
|
m_binary = true;
|
|
m_buffer[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (uint i=0; i < length; i++)
|
|
{
|
|
if (m_buffer[i] == 0)
|
|
{
|
|
m_binary = true;
|
|
m_buffer[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
QFile m_file;
|
|
QByteArray m_buffer;
|
|
QTextCodec *m_codec;
|
|
QTextDecoder *m_decoder;
|
|
QString m_text;
|
|
uint m_position;
|
|
uint m_lastLineStart;
|
|
bool m_eof;
|
|
bool lastWasEndOfLine;
|
|
bool lastWasR;
|
|
int m_eol;
|
|
bool m_twoByteEncoding;
|
|
bool m_binary;
|
|
bool m_removeTrailingSpaces;
|
|
};
|
|
|
|
/**
|
|
* Create an empty buffer. (with one block with one empty line)
|
|
*/
|
|
KateBuffer::KateBuffer(KateDocument *doc)
|
|
: QObject (doc),
|
|
editSessionNumber (0),
|
|
editIsRunning (false),
|
|
editTagLineStart (0xffffffff),
|
|
editTagLineEnd (0),
|
|
editTagLineFrom (false),
|
|
editChangesDone (false),
|
|
m_doc (doc),
|
|
m_lines (0),
|
|
m_lastInSyncBlock (0),
|
|
m_lastFoundBlock (0),
|
|
m_cacheReadError(false),
|
|
m_cacheWriteError(false),
|
|
m_loadingBorked (false),
|
|
m_binary (false),
|
|
m_highlight (0),
|
|
m_regionTree (this),
|
|
m_tabWidth (8),
|
|
m_lineHighlightedMax (0),
|
|
m_lineHighlighted (0),
|
|
m_maxDynamicContexts (KATE_MAX_DYNAMIC_CONTEXTS)
|
|
{
|
|
clear();
|
|
}
|
|
|
|
/**
|
|
* Cleanup on destruction
|
|
*/
|
|
KateBuffer::~KateBuffer()
|
|
{
|
|
// DELETE ALL BLOCKS, will free mem
|
|
for (uint i=0; i < m_blocks.size(); i++)
|
|
delete m_blocks[i];
|
|
|
|
// release HL
|
|
if (m_highlight)
|
|
m_highlight->release();
|
|
}
|
|
|
|
void KateBuffer::editStart ()
|
|
{
|
|
editSessionNumber++;
|
|
|
|
if (editSessionNumber > 1)
|
|
return;
|
|
|
|
editIsRunning = true;
|
|
|
|
editTagLineStart = 0xffffffff;
|
|
editTagLineEnd = 0;
|
|
editTagLineFrom = false;
|
|
|
|
editChangesDone = false;
|
|
}
|
|
|
|
void KateBuffer::editEnd ()
|
|
{
|
|
if (editSessionNumber == 0)
|
|
return;
|
|
|
|
editSessionNumber--;
|
|
|
|
if (editSessionNumber > 0)
|
|
return;
|
|
|
|
if (editChangesDone)
|
|
{
|
|
// hl update !!!
|
|
if ( m_highlight && !m_highlight->noHighlighting()
|
|
&& (editTagLineStart <= editTagLineEnd)
|
|
&& (editTagLineEnd <= m_lineHighlighted))
|
|
{
|
|
// look one line too far, needed for linecontinue stuff
|
|
editTagLineEnd++;
|
|
|
|
// look one line before, needed nearly 100% only for indentation based folding !
|
|
if (editTagLineStart > 0)
|
|
editTagLineStart--;
|
|
|
|
KateBufBlock *buf2 = 0;
|
|
bool needContinue = false;
|
|
while ((buf2 = findBlock(editTagLineStart)))
|
|
{
|
|
needContinue = doHighlight (buf2,
|
|
(editTagLineStart > buf2->startLine()) ? editTagLineStart : buf2->startLine(),
|
|
(editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd,
|
|
true);
|
|
|
|
editTagLineStart = (editTagLineEnd > buf2->endLine()) ? buf2->endLine() : editTagLineEnd;
|
|
|
|
if ((editTagLineStart >= m_lines) || (editTagLineStart >= editTagLineEnd))
|
|
break;
|
|
}
|
|
|
|
if (needContinue)
|
|
m_lineHighlighted = editTagLineStart;
|
|
|
|
if (editTagLineStart > m_lineHighlightedMax)
|
|
m_lineHighlightedMax = editTagLineStart;
|
|
}
|
|
else if (editTagLineStart < m_lineHighlightedMax)
|
|
m_lineHighlightedMax = editTagLineStart;
|
|
}
|
|
|
|
editIsRunning = false;
|
|
}
|
|
|
|
void KateBuffer::clear()
|
|
{
|
|
m_regionTree.clear();
|
|
|
|
// cleanup the blocks
|
|
for (uint i=0; i < m_blocks.size(); i++)
|
|
delete m_blocks[i];
|
|
|
|
m_blocks.clear ();
|
|
|
|
// create a bufblock with one line, we need that, only in openFile we won't have that
|
|
KateBufBlock *block = new KateBufBlock(this, 0, 0);
|
|
m_blocks.append (block);
|
|
|
|
// reset the state
|
|
m_lines = block->lines();
|
|
m_lastInSyncBlock = 0;
|
|
m_lastFoundBlock = 0;
|
|
m_cacheWriteError = false;
|
|
m_cacheReadError = false;
|
|
m_loadingBorked = false;
|
|
m_binary = false;
|
|
|
|
m_lineHighlightedMax = 0;
|
|
m_lineHighlighted = 0;
|
|
}
|
|
|
|
bool KateBuffer::openFile (const QString &m_file)
|
|
{
|
|
KateFileLoader file (m_file, m_doc->config()->codec(), m_doc->configFlags() & KateDocument::cfRemoveSpaces);
|
|
|
|
bool ok = false;
|
|
struct stat sbuf;
|
|
if (stat(QFile::encodeName(m_file), &sbuf) == 0)
|
|
{
|
|
if (S_ISREG(sbuf.st_mode) && file.open())
|
|
ok = true;
|
|
}
|
|
|
|
if (!ok)
|
|
{
|
|
clear();
|
|
return false; // Error
|
|
}
|
|
|
|
// set eol mode, if a eol char was found in the first 256kb block and we allow this at all!
|
|
if (m_doc->config()->allowEolDetection() && (file.eol() != -1))
|
|
m_doc->config()->setEol (file.eol());
|
|
|
|
// flush current content
|
|
clear ();
|
|
|
|
// cleanup the blocks
|
|
for (uint i=0; i < m_blocks.size(); i++)
|
|
delete m_blocks[i];
|
|
|
|
m_blocks.clear ();
|
|
|
|
// do the real work
|
|
KateBufBlock *block = 0;
|
|
m_lines = 0;
|
|
while (!file.eof() && !m_cacheWriteError)
|
|
{
|
|
block = new KateBufBlock (this, block, 0, &file);
|
|
|
|
m_lines = block->endLine ();
|
|
|
|
if (m_cacheWriteError || (block->lines() == 0))
|
|
{
|
|
delete block;
|
|
break;
|
|
}
|
|
else
|
|
m_blocks.append (block);
|
|
}
|
|
|
|
// we had a cache write error, this load is really borked !
|
|
if (m_cacheWriteError)
|
|
m_loadingBorked = true;
|
|
|
|
if (m_blocks.isEmpty() || (m_lines == 0))
|
|
{
|
|
// file was really empty, clean the buffers + emit the line changed
|
|
// loadingBorked will be false for such files, not matter what happened
|
|
// before
|
|
clear ();
|
|
}
|
|
else
|
|
{
|
|
// fix region tree
|
|
m_regionTree.fixRoot (m_lines);
|
|
}
|
|
|
|
// if we have no hl or the "None" hl activated, whole file is correct highlighted
|
|
// after loading, which wonder ;)
|
|
if (!m_highlight || m_highlight->noHighlighting())
|
|
{
|
|
m_lineHighlighted = m_lines;
|
|
m_lineHighlightedMax = m_lines;
|
|
}
|
|
|
|
// binary?
|
|
m_binary = file.binary ();
|
|
|
|
kdDebug (13020) << "LOADING DONE" << endl;
|
|
|
|
return !m_loadingBorked;
|
|
}
|
|
|
|
bool KateBuffer::canEncode ()
|
|
{
|
|
QTextCodec *codec = m_doc->config()->codec();
|
|
|
|
kdDebug(13020) << "ENC NAME: " << codec->name() << endl;
|
|
|
|
// hardcode some unicode encodings which can encode all chars
|
|
if ((QString(codec->name()) == "UTF-8") || (QString(codec->name()) == "ISO-10646-UCS-2"))
|
|
return true;
|
|
|
|
for (uint i=0; i < m_lines; i++)
|
|
{
|
|
if (!codec->canEncode (plainLine(i)->string()))
|
|
{
|
|
kdDebug(13020) << "STRING LINE: " << plainLine(i)->string() << endl;
|
|
kdDebug(13020) << "ENC WORKING: FALSE" << endl;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool KateBuffer::saveFile (const QString &m_file)
|
|
{
|
|
QFile file (m_file);
|
|
QTextStream stream (&file);
|
|
|
|
if ( !file.open( IO_WriteOnly ) )
|
|
{
|
|
return false; // Error
|
|
}
|
|
|
|
QTextCodec *codec = m_doc->config()->codec();
|
|
|
|
// disable Unicode headers
|
|
stream.setEncoding(QTextStream::RawUnicode);
|
|
|
|
// this line sets the mapper to the correct codec
|
|
stream.setCodec(codec);
|
|
|
|
// our loved eol string ;)
|
|
QString eol = m_doc->config()->eolString ();
|
|
|
|
// should we strip spaces?
|
|
bool removeTrailingSpaces = m_doc->configFlags() & KateDocument::cfRemoveSpaces;
|
|
|
|
// just dump the lines out ;)
|
|
for (uint i=0; i < m_lines; i++)
|
|
{
|
|
KateTextLine::Ptr textline = plainLine(i);
|
|
|
|
// strip spaces
|
|
if (removeTrailingSpaces)
|
|
{
|
|
int lastChar = textline->lastChar();
|
|
|
|
if (lastChar > -1)
|
|
{
|
|
stream << QConstString (textline->text(), lastChar+1).string();
|
|
}
|
|
}
|
|
else // simple, dump the line
|
|
stream << textline->string();
|
|
|
|
if ((i+1) < m_lines)
|
|
stream << eol;
|
|
}
|
|
|
|
file.close ();
|
|
|
|
m_loadingBorked = false;
|
|
|
|
return (file.status() == IO_Ok);
|
|
}
|
|
|
|
KateTextLine::Ptr KateBuffer::line_internal (KateBufBlock *buf, uint i)
|
|
{
|
|
// update hl until this line + max KATE_HL_LOOKAHEAD
|
|
KateBufBlock *buf2 = 0;
|
|
while ((i >= m_lineHighlighted) && (buf2 = findBlock(m_lineHighlighted)))
|
|
{
|
|
uint end = kMin(i + KATE_HL_LOOKAHEAD, buf2->endLine());
|
|
|
|
doHighlight ( buf2,
|
|
kMax(m_lineHighlighted, buf2->startLine()),
|
|
end,
|
|
false );
|
|
|
|
m_lineHighlighted = end;
|
|
}
|
|
|
|
// update hl max
|
|
if (m_lineHighlighted > m_lineHighlightedMax)
|
|
m_lineHighlightedMax = m_lineHighlighted;
|
|
|
|
return buf->line (i - buf->startLine());
|
|
}
|
|
|
|
KateBufBlock *KateBuffer::findBlock_internal (uint i, uint *index)
|
|
{
|
|
uint lastLine = m_blocks[m_lastInSyncBlock]->endLine ();
|
|
|
|
if (lastLine > i) // we are in a allready known area !
|
|
{
|
|
while (true)
|
|
{
|
|
KateBufBlock *buf = m_blocks[m_lastFoundBlock];
|
|
|
|
if ( (buf->startLine() <= i)
|
|
&& (buf->endLine() > i) )
|
|
{
|
|
if (index)
|
|
(*index) = m_lastFoundBlock;
|
|
|
|
return m_blocks[m_lastFoundBlock];
|
|
}
|
|
|
|
if (i < buf->startLine())
|
|
m_lastFoundBlock--;
|
|
else
|
|
m_lastFoundBlock++;
|
|
}
|
|
}
|
|
else // we need first to resync the startLines !
|
|
{
|
|
if ((m_lastInSyncBlock+1) < m_blocks.size())
|
|
m_lastInSyncBlock++;
|
|
else
|
|
return 0;
|
|
|
|
for (; m_lastInSyncBlock < m_blocks.size(); m_lastInSyncBlock++)
|
|
{
|
|
// get next block
|
|
KateBufBlock *buf = m_blocks[m_lastInSyncBlock];
|
|
|
|
// sync startLine !
|
|
buf->setStartLine (lastLine);
|
|
|
|
// is it allready the searched block ?
|
|
if ((i >= lastLine) && (i < buf->endLine()))
|
|
{
|
|
// remember this block as last found !
|
|
m_lastFoundBlock = m_lastInSyncBlock;
|
|
|
|
if (index)
|
|
(*index) = m_lastFoundBlock;
|
|
|
|
return buf;
|
|
}
|
|
|
|
// increase lastLine with blocklinecount
|
|
lastLine += buf->lines ();
|
|
}
|
|
}
|
|
|
|
// no block found !
|
|
// index will not be set to any useful value in this case !
|
|
return 0;
|
|
}
|
|
|
|
void KateBuffer::changeLine(uint i)
|
|
{
|
|
KateBufBlock *buf = findBlock(i);
|
|
|
|
if (!buf)
|
|
return;
|
|
|
|
// mark this block dirty
|
|
buf->markDirty ();
|
|
|
|
// mark buffer changed
|
|
editChangesDone = true;
|
|
|
|
// tag this line as changed
|
|
if (i < editTagLineStart)
|
|
editTagLineStart = i;
|
|
|
|
if (i > editTagLineEnd)
|
|
editTagLineEnd = i;
|
|
}
|
|
|
|
void KateBuffer::insertLine(uint i, KateTextLine::Ptr line)
|
|
{
|
|
uint index = 0;
|
|
KateBufBlock *buf;
|
|
if (i == m_lines)
|
|
buf = findBlock(i-1, &index);
|
|
else
|
|
buf = findBlock(i, &index);
|
|
|
|
if (!buf)
|
|
return;
|
|
|
|
buf->insertLine(i - buf->startLine(), line);
|
|
|
|
if (m_lineHighlightedMax > i)
|
|
m_lineHighlightedMax++;
|
|
|
|
if (m_lineHighlighted > i)
|
|
m_lineHighlighted++;
|
|
|
|
m_lines++;
|
|
|
|
// last sync block adjust
|
|
if (m_lastInSyncBlock > index)
|
|
m_lastInSyncBlock = index;
|
|
|
|
// last found
|
|
if (m_lastInSyncBlock < m_lastFoundBlock)
|
|
m_lastFoundBlock = m_lastInSyncBlock;
|
|
|
|
// mark buffer changed
|
|
editChangesDone = true;
|
|
|
|
// tag this line as inserted
|
|
if (i < editTagLineStart)
|
|
editTagLineStart = i;
|
|
|
|
if (i <= editTagLineEnd)
|
|
editTagLineEnd++;
|
|
|
|
if (i > editTagLineEnd)
|
|
editTagLineEnd = i;
|
|
|
|
// line inserted
|
|
editTagLineFrom = true;
|
|
|
|
m_regionTree.lineHasBeenInserted (i);
|
|
}
|
|
|
|
void KateBuffer::removeLine(uint i)
|
|
{
|
|
uint index = 0;
|
|
KateBufBlock *buf = findBlock(i, &index);
|
|
|
|
if (!buf)
|
|
return;
|
|
|
|
buf->removeLine(i - buf->startLine());
|
|
|
|
if (m_lineHighlightedMax > i)
|
|
m_lineHighlightedMax--;
|
|
|
|
if (m_lineHighlighted > i)
|
|
m_lineHighlighted--;
|
|
|
|
m_lines--;
|
|
|
|
// trash away a empty block
|
|
if (buf->lines() == 0)
|
|
{
|
|
// we need to change which block is last in sync
|
|
if (m_lastInSyncBlock >= index)
|
|
{
|
|
m_lastInSyncBlock = index;
|
|
|
|
if (buf->next())
|
|
{
|
|
if (buf->prev())
|
|
buf->next()->setStartLine (buf->prev()->endLine());
|
|
else
|
|
buf->next()->setStartLine (0);
|
|
}
|
|
}
|
|
|
|
// cu block !
|
|
delete buf;
|
|
m_blocks.erase (m_blocks.begin()+index);
|
|
|
|
// make sure we don't keep a pointer to the deleted block
|
|
if( m_lastInSyncBlock >= index )
|
|
m_lastInSyncBlock = index - 1;
|
|
}
|
|
else
|
|
{
|
|
// last sync block adjust
|
|
if (m_lastInSyncBlock > index)
|
|
m_lastInSyncBlock = index;
|
|
}
|
|
|
|
// last found
|
|
if (m_lastInSyncBlock < m_lastFoundBlock)
|
|
m_lastFoundBlock = m_lastInSyncBlock;
|
|
|
|
// mark buffer changed
|
|
editChangesDone = true;
|
|
|
|
// tag this line as removed
|
|
if (i < editTagLineStart)
|
|
editTagLineStart = i;
|
|
|
|
if (i < editTagLineEnd)
|
|
editTagLineEnd--;
|
|
|
|
if (i > editTagLineEnd)
|
|
editTagLineEnd = i;
|
|
|
|
// line removed
|
|
editTagLineFrom = true;
|
|
|
|
m_regionTree.lineHasBeenRemoved (i);
|
|
}
|
|
|
|
void KateBuffer::setTabWidth (uint w)
|
|
{
|
|
if ((m_tabWidth != w) && (m_tabWidth > 0))
|
|
{
|
|
m_tabWidth = w;
|
|
|
|
if (m_highlight && m_highlight->foldingIndentationSensitive())
|
|
invalidateHighlighting();
|
|
}
|
|
}
|
|
|
|
void KateBuffer::setHighlight(uint hlMode)
|
|
{
|
|
KateHighlighting *h = KateHlManager::self()->getHl(hlMode);
|
|
|
|
// aha, hl will change
|
|
if (h != m_highlight)
|
|
{
|
|
bool invalidate = !h->noHighlighting();
|
|
|
|
if (m_highlight)
|
|
{
|
|
m_highlight->release();
|
|
invalidate = true;
|
|
}
|
|
|
|
h->use();
|
|
|
|
// Clear code folding tree (see bug #124102)
|
|
m_regionTree.clear();
|
|
m_regionTree.fixRoot(m_lines);
|
|
|
|
// try to set indentation
|
|
if (!h->indentation().isEmpty())
|
|
m_doc->config()->setIndentationMode (KateAutoIndent::modeNumber(h->indentation()));
|
|
|
|
m_highlight = h;
|
|
|
|
if (invalidate)
|
|
invalidateHighlighting();
|
|
|
|
// inform the document that the hl was really changed
|
|
// needed to update attributes and more ;)
|
|
m_doc->bufferHlChanged ();
|
|
}
|
|
}
|
|
|
|
void KateBuffer::invalidateHighlighting()
|
|
{
|
|
m_lineHighlightedMax = 0;
|
|
m_lineHighlighted = 0;
|
|
}
|
|
|
|
|
|
void KateBuffer::updatePreviousNotEmptyLine(KateBufBlock *blk,uint current_line,bool addindent,uint deindent)
|
|
{
|
|
KateTextLine::Ptr textLine;
|
|
do {
|
|
if (current_line>0) current_line--;
|
|
else
|
|
{
|
|
uint line=blk->startLine()+current_line;
|
|
if (line==0) return;
|
|
line--;
|
|
blk=findBlock(line);
|
|
if (!blk) {
|
|
kdDebug(13020)<<"updatePreviousNotEmptyLine: block not found, this must not happen"<<endl;
|
|
return;
|
|
}
|
|
current_line=line-blk->startLine();
|
|
}
|
|
textLine = blk->line(current_line);
|
|
} while (textLine->firstChar()==-1);
|
|
kdDebug(13020)<<"updatePreviousNotEmptyLine: updating line:"<<(blk->startLine()+current_line)<<endl;
|
|
QMemArray<uint> foldingList=textLine->foldingListArray();
|
|
while ( (foldingList.size()>0) && ( abs(foldingList[foldingList.size()-2])==1)) {
|
|
foldingList.resize(foldingList.size()-2,QGArray::SpeedOptim);
|
|
}
|
|
addIndentBasedFoldingInformation(foldingList,addindent,deindent);
|
|
textLine->setFoldingList(foldingList);
|
|
bool retVal_folding = false;
|
|
m_regionTree.updateLine (current_line + blk->startLine(), &foldingList, &retVal_folding, true,false);
|
|
emit tagLines (blk->startLine()+current_line, blk->startLine()+current_line);
|
|
}
|
|
|
|
void KateBuffer::addIndentBasedFoldingInformation(QMemArray<uint> &foldingList,bool addindent,uint deindent)
|
|
{
|
|
if (addindent) {
|
|
//kdDebug(13020)<<"adding indent for line :"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
|
|
kdDebug(13020)<<"adding ident"<<endl;
|
|
foldingList.resize (foldingList.size() + 2, QGArray::SpeedOptim);
|
|
foldingList[foldingList.size()-2] = 1;
|
|
foldingList[foldingList.size()-1] = 0;
|
|
}
|
|
kdDebug(13020)<<"DEINDENT: "<<deindent<<endl;
|
|
if (deindent > 0)
|
|
{
|
|
foldingList.resize (foldingList.size() + (deindent*2), QGArray::SpeedOptim);
|
|
|
|
for (uint z= foldingList.size()-(deindent*2); z < foldingList.size(); z=z+2)
|
|
{
|
|
foldingList[z] = -1;
|
|
foldingList[z+1] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool KateBuffer::doHighlight (KateBufBlock *buf, uint startLine, uint endLine, bool invalidate)
|
|
{
|
|
// no hl around, no stuff to do
|
|
if (!m_highlight)
|
|
return false;
|
|
|
|
/*if (m_highlight->foldingIndentationSensitive())
|
|
{
|
|
startLine=0;
|
|
endLine=50;
|
|
}*/
|
|
|
|
// we tried to start in a line behind this buf block !
|
|
if (startLine >= (buf->startLine()+buf->lines()))
|
|
return false;
|
|
|
|
//QTime t;
|
|
//t.start();
|
|
//kdDebug (13020) << "HIGHLIGHTED START --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
|
|
//kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
|
|
//kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
|
|
|
|
// see if there are too many dynamic contexts; if yes, invalidate HL of all documents
|
|
if (KateHlManager::self()->countDynamicCtxs() >= m_maxDynamicContexts)
|
|
{
|
|
{
|
|
if (KateHlManager::self()->resetDynamicCtxs())
|
|
{
|
|
kdDebug (13020) << "HL invalidated - too many dynamic contexts ( >= " << m_maxDynamicContexts << ")" << endl;
|
|
|
|
// avoid recursive invalidation
|
|
KateHlManager::self()->setForceNoDCReset(true);
|
|
|
|
for (KateDocument *doc = KateFactory::self()->documents()->first(); doc; doc = KateFactory::self()->documents()->next())
|
|
doc->makeAttribs();
|
|
|
|
// doHighlight *shall* do his work. After invalidation, some highlight has
|
|
// been recalculated, but *maybe not* until endLine ! So we shall force it manually...
|
|
KateBufBlock *buf = 0;
|
|
while ((endLine > m_lineHighlighted) && (buf = findBlock(m_lineHighlighted)))
|
|
{
|
|
uint end = kMin(endLine, buf->endLine());
|
|
|
|
doHighlight ( buf,
|
|
kMax(m_lineHighlighted, buf->startLine()),
|
|
end,
|
|
false );
|
|
|
|
m_lineHighlighted = end;
|
|
}
|
|
|
|
KateHlManager::self()->setForceNoDCReset(false);
|
|
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_maxDynamicContexts *= 2;
|
|
kdDebug (13020) << "New dynamic contexts limit: " << m_maxDynamicContexts << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the previous line, if we start at the beginning of this block
|
|
// take the last line of the previous block
|
|
KateTextLine::Ptr prevLine = 0;
|
|
|
|
if ((startLine == buf->startLine()) && buf->prev() && (buf->prev()->lines() > 0))
|
|
prevLine = buf->prev()->line (buf->prev()->lines() - 1);
|
|
else if ((startLine > buf->startLine()) && (startLine <= buf->endLine()))
|
|
prevLine = buf->line(startLine - buf->startLine() - 1);
|
|
else
|
|
prevLine = new KateTextLine ();
|
|
|
|
// does we need to emit a signal for the folding changes ?
|
|
bool codeFoldingUpdate = false;
|
|
|
|
// here we are atm, start at start line in the block
|
|
uint current_line = startLine - buf->startLine();
|
|
|
|
// do we need to continue
|
|
bool stillcontinue=false;
|
|
bool indentContinueWhitespace=false;
|
|
bool indentContinueNextWhitespace=false;
|
|
// loop over the lines of the block, from startline to endline or end of block
|
|
// if stillcontinue forces us to do so
|
|
while ( (current_line < buf->lines())
|
|
&& (stillcontinue || ((current_line + buf->startLine()) <= endLine)) )
|
|
{
|
|
// current line
|
|
KateTextLine::Ptr textLine = buf->line(current_line);
|
|
|
|
QMemArray<uint> foldingList;
|
|
bool ctxChanged = false;
|
|
|
|
m_highlight->doHighlight (prevLine, textLine, &foldingList, &ctxChanged);
|
|
|
|
//
|
|
// indentation sensitive folding
|
|
//
|
|
bool indentChanged = false;
|
|
if (m_highlight->foldingIndentationSensitive())
|
|
{
|
|
// get the indentation array of the previous line to start with !
|
|
QMemArray<unsigned short> indentDepth;
|
|
indentDepth.duplicate (prevLine->indentationDepthArray());
|
|
|
|
// current indentation of this line
|
|
uint iDepth = textLine->indentDepth(m_tabWidth);
|
|
if ((current_line+buf->startLine())==0)
|
|
{
|
|
indentDepth.resize (1, QGArray::SpeedOptim);
|
|
indentDepth[0] = iDepth;
|
|
}
|
|
|
|
textLine->setNoIndentBasedFoldingAtStart(prevLine->noIndentBasedFolding());
|
|
// this line is empty, beside spaces, or has indentaion based folding disabled, use indentation depth of the previous line !
|
|
kdDebug(13020)<<"current_line:"<<current_line + buf->startLine()<<" textLine->noIndentBasedFoldingAtStart"<<textLine->noIndentBasedFoldingAtStart()<<endl;
|
|
if ( (textLine->firstChar() == -1) || textLine->noIndentBasedFoldingAtStart())
|
|
{
|
|
// do this to get skipped empty lines indent right, which was given in the indenation array
|
|
if (!prevLine->indentationDepthArray().isEmpty())
|
|
{
|
|
iDepth = (prevLine->indentationDepthArray())[prevLine->indentationDepthArray().size()-1];
|
|
kdDebug(13020)<<"reusing old depth as current"<<endl;
|
|
}
|
|
else
|
|
{
|
|
iDepth = prevLine->indentDepth(m_tabWidth);
|
|
kdDebug(13020)<<"creating indentdepth for previous line"<<endl;
|
|
}
|
|
}
|
|
|
|
kdDebug(13020)<<"iDepth:"<<iDepth<<endl;
|
|
|
|
// query the next line indentation, if we are at the end of the block
|
|
// use the first line of the next buf block
|
|
uint nextLineIndentation = 0;
|
|
bool nextLineIndentationValid=true;
|
|
indentContinueNextWhitespace=false;
|
|
if ((current_line+1) < buf->lines())
|
|
{
|
|
if (buf->line(current_line+1)->firstChar() == -1)
|
|
{
|
|
nextLineIndentation = iDepth;
|
|
indentContinueNextWhitespace=true;
|
|
}
|
|
else
|
|
nextLineIndentation = buf->line(current_line+1)->indentDepth(m_tabWidth);
|
|
}
|
|
else
|
|
{
|
|
KateBufBlock *blk = buf->next();
|
|
|
|
if (blk && (blk->lines() > 0))
|
|
{
|
|
if (blk->line (0)->firstChar() == -1)
|
|
{
|
|
nextLineIndentation = iDepth;
|
|
indentContinueNextWhitespace=true;
|
|
}
|
|
else
|
|
nextLineIndentation = blk->line (0)->indentDepth(m_tabWidth);
|
|
}
|
|
else nextLineIndentationValid=false;
|
|
}
|
|
|
|
if (!textLine->noIndentBasedFoldingAtStart()) {
|
|
|
|
if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
|
|
{
|
|
kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
|
|
indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
|
|
indentDepth[indentDepth.size()-1] = iDepth;
|
|
} else {
|
|
if (!indentDepth.isEmpty())
|
|
{
|
|
for (int z=indentDepth.size()-1; z > -1; z--)
|
|
if (indentDepth[z]>iDepth)
|
|
indentDepth.resize(z, QGArray::SpeedOptim);
|
|
if ((iDepth > 0) && (indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1] < iDepth)))
|
|
{
|
|
kdDebug(13020)<<"adding depth to \"stack\":"<<iDepth<<endl;
|
|
indentDepth.resize (indentDepth.size()+1, QGArray::SpeedOptim);
|
|
indentDepth[indentDepth.size()-1] = iDepth;
|
|
if (prevLine->firstChar()==-1) {
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!textLine->noIndentBasedFolding())
|
|
{
|
|
if (nextLineIndentationValid)
|
|
{
|
|
//if (textLine->firstChar()!=-1)
|
|
{
|
|
kdDebug(13020)<<"nextLineIndentation:"<<nextLineIndentation<<endl;
|
|
bool addindent=false;
|
|
uint deindent=0;
|
|
if (!indentDepth.isEmpty())
|
|
kdDebug()<<"indentDepth[indentDepth.size()-1]:"<<indentDepth[indentDepth.size()-1]<<endl;
|
|
if ((nextLineIndentation>0) && ( indentDepth.isEmpty() || (indentDepth[indentDepth.size()-1]<nextLineIndentation)))
|
|
{
|
|
kdDebug(13020)<<"addindent==true"<<endl;
|
|
addindent=true;
|
|
} else {
|
|
if ((!indentDepth.isEmpty()) && (indentDepth[indentDepth.size()-1]>nextLineIndentation))
|
|
{
|
|
kdDebug(13020)<<"...."<<endl;
|
|
for (int z=indentDepth.size()-1; z > -1; z--)
|
|
{
|
|
kdDebug(13020)<<indentDepth[z]<<" "<<nextLineIndentation<<endl;
|
|
if (indentDepth[z]>nextLineIndentation)
|
|
deindent++;
|
|
}
|
|
}
|
|
}
|
|
/* }
|
|
if (textLine->noIndentBasedFolding()) kdDebug(13020)<<"=============================indentation based folding disabled======================"<<endl;
|
|
if (!textLine->noIndentBasedFolding()) {*/
|
|
if ((textLine->firstChar()==-1)) {
|
|
updatePreviousNotEmptyLine(buf,current_line,addindent,deindent);
|
|
codeFoldingUpdate=true;
|
|
}
|
|
else
|
|
{
|
|
addIndentBasedFoldingInformation(foldingList,addindent,deindent);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
indentChanged = !(indentDepth == textLine->indentationDepthArray());
|
|
|
|
// assign the new array to the textline !
|
|
if (indentChanged)
|
|
textLine->setIndentationDepth (indentDepth);
|
|
|
|
indentContinueWhitespace=textLine->firstChar()==-1;
|
|
}
|
|
bool foldingColChanged=false;
|
|
bool foldingChanged = false; //!(foldingList == textLine->foldingListArray());
|
|
if (foldingList.size()!=textLine->foldingListArray().size()) {
|
|
foldingChanged=true;
|
|
} else {
|
|
QMemArray<uint>::ConstIterator it=foldingList.begin();
|
|
QMemArray<uint>::ConstIterator it1=textLine->foldingListArray();
|
|
bool markerType=true;
|
|
for(;it!=foldingList.end();++it,++it1) {
|
|
if (markerType) {
|
|
if ( ((*it)!=(*it1))) {
|
|
foldingChanged=true;
|
|
foldingColChanged=false;
|
|
break;
|
|
}
|
|
} else {
|
|
if ((*it)!=(*it1)) {
|
|
foldingColChanged=true;
|
|
}
|
|
}
|
|
markerType=!markerType;
|
|
}
|
|
}
|
|
|
|
if (foldingChanged || foldingColChanged) {
|
|
textLine->setFoldingList(foldingList);
|
|
if (foldingChanged==false){
|
|
textLine->setFoldingColumnsOutdated(textLine->foldingColumnsOutdated() | foldingColChanged);
|
|
} else textLine->setFoldingColumnsOutdated(false);
|
|
}
|
|
bool retVal_folding = false;
|
|
//perhaps make en enums out of the change flags
|
|
m_regionTree.updateLine (current_line + buf->startLine(), &foldingList, &retVal_folding, foldingChanged,foldingColChanged);
|
|
|
|
codeFoldingUpdate = codeFoldingUpdate | retVal_folding;
|
|
|
|
// need we to continue ?
|
|
stillcontinue = ctxChanged || indentChanged || indentContinueWhitespace || indentContinueNextWhitespace;
|
|
|
|
// move around the lines
|
|
prevLine = textLine;
|
|
|
|
// increment line
|
|
current_line++;
|
|
}
|
|
|
|
buf->markDirty ();
|
|
|
|
// tag the changed lines !
|
|
if (invalidate)
|
|
emit tagLines (startLine, current_line + buf->startLine());
|
|
|
|
// emit that we have changed the folding
|
|
if (codeFoldingUpdate)
|
|
emit codeFoldingUpdated();
|
|
|
|
//kdDebug (13020) << "HIGHLIGHTED END --- NEED HL, LINESTART: " << startLine << " LINEEND: " << endLine << endl;
|
|
//kdDebug (13020) << "HL UNTIL LINE: " << m_lineHighlighted << " MAX: " << m_lineHighlightedMax << endl;
|
|
//kdDebug (13020) << "HL DYN COUNT: " << KateHlManager::self()->countDynamicCtxs() << " MAX: " << m_maxDynamicContexts << endl;
|
|
//kdDebug (13020) << "TIME TAKEN: " << t.elapsed() << endl;
|
|
|
|
// if we are at the last line of the block + we still need to continue
|
|
// return the need of that !
|
|
return stillcontinue && ((current_line+1) == buf->lines());
|
|
}
|
|
|
|
void KateBuffer::codeFoldingColumnUpdate(unsigned int lineNr) {
|
|
KateTextLine::Ptr line=plainLine(lineNr);
|
|
if (!line) return;
|
|
if (line->foldingColumnsOutdated()) {
|
|
line->setFoldingColumnsOutdated(false);
|
|
bool tmp;
|
|
QMemArray<uint> folding=line->foldingListArray();
|
|
m_regionTree.updateLine(lineNr,&folding,&tmp,true,false);
|
|
}
|
|
}
|
|
|
|
//BEGIN KateBufBlock
|
|
|
|
KateBufBlock::KateBufBlock ( KateBuffer *parent, KateBufBlock *prev, KateBufBlock *next,
|
|
KateFileLoader *stream )
|
|
: m_state (KateBufBlock::stateDirty),
|
|
m_startLine (0),
|
|
m_lines (0),
|
|
m_vmblock (0),
|
|
m_vmblockSize (0),
|
|
m_parent (parent),
|
|
m_prev (prev),
|
|
m_next (next),
|
|
list (0),
|
|
listPrev (0),
|
|
listNext (0)
|
|
{
|
|
// init startline + the next pointers of the neighbour blocks
|
|
if (m_prev)
|
|
{
|
|
m_startLine = m_prev->endLine ();
|
|
m_prev->m_next = this;
|
|
}
|
|
|
|
if (m_next)
|
|
m_next->m_prev = this;
|
|
|
|
// we have a stream, use it to fill the block !
|
|
// this can lead to 0 line blocks which are invalid !
|
|
if (stream)
|
|
{
|
|
// this we lead to either dirty or swapped state
|
|
fillBlock (stream);
|
|
}
|
|
else // init the block if no stream given !
|
|
{
|
|
// fill in one empty line !
|
|
KateTextLine::Ptr textLine = new KateTextLine ();
|
|
m_stringList.push_back (textLine);
|
|
m_lines++;
|
|
|
|
// if we have allready enough blocks around, swap one
|
|
if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
|
|
m_parent->m_loadedBlocks.first()->swapOut();
|
|
|
|
// we are a new nearly empty dirty block
|
|
m_state = KateBufBlock::stateDirty;
|
|
m_parent->m_loadedBlocks.append (this);
|
|
}
|
|
}
|
|
|
|
KateBufBlock::~KateBufBlock ()
|
|
{
|
|
// sync prev/next pointers
|
|
if (m_prev)
|
|
m_prev->m_next = m_next;
|
|
|
|
if (m_next)
|
|
m_next->m_prev = m_prev;
|
|
|
|
// if we have some swapped data allocated, free it now or never
|
|
if (m_vmblock)
|
|
KateFactory::self()->vm()->free(m_vmblock);
|
|
|
|
// remove me from the list I belong
|
|
KateBufBlockList::remove (this);
|
|
}
|
|
|
|
void KateBufBlock::fillBlock (KateFileLoader *stream)
|
|
{
|
|
// is allready too much stuff around in mem ?
|
|
bool swap = m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks();
|
|
|
|
QByteArray rawData;
|
|
|
|
// calcs the approx size for KATE_AVG_BLOCK_SIZE chars !
|
|
if (swap)
|
|
rawData.resize ((KATE_AVG_BLOCK_SIZE * sizeof(QChar)) + ((KATE_AVG_BLOCK_SIZE/80) * 8));
|
|
|
|
char *buf = rawData.data ();
|
|
uint size = 0;
|
|
uint blockSize = 0;
|
|
while (!stream->eof() && (blockSize < KATE_AVG_BLOCK_SIZE) && (m_lines < KATE_MAX_BLOCK_LINES))
|
|
{
|
|
uint offset = 0, length = 0;
|
|
stream->readLine(offset, length);
|
|
const QChar *unicodeData = stream->unicode () + offset;
|
|
|
|
// strip spaces at end of line
|
|
if ( stream->removeTrailingSpaces() )
|
|
{
|
|
while (length > 0)
|
|
{
|
|
if (unicodeData[length-1].isSpace())
|
|
--length;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
blockSize += length;
|
|
|
|
if (swap)
|
|
{
|
|
// create the swapped data on the fly, no need to waste time
|
|
// via going over the textline classes and dump them !
|
|
char attr = KateTextLine::flagNoOtherData;
|
|
uint pos = size;
|
|
|
|
// calc new size
|
|
size = size + 1 + sizeof(uint) + (sizeof(QChar)*length);
|
|
|
|
if (size > rawData.size ())
|
|
{
|
|
rawData.resize (size);
|
|
buf = rawData.data ();
|
|
}
|
|
|
|
memcpy(buf+pos, (char *) &attr, 1);
|
|
pos += 1;
|
|
|
|
memcpy(buf+pos, (char *) &length, sizeof(uint));
|
|
pos += sizeof(uint);
|
|
|
|
memcpy(buf+pos, (char *) unicodeData, sizeof(QChar)*length);
|
|
pos += sizeof(QChar)*length;
|
|
}
|
|
else
|
|
{
|
|
KateTextLine::Ptr textLine = new KateTextLine ();
|
|
textLine->insertText (0, length, unicodeData);
|
|
m_stringList.push_back (textLine);
|
|
}
|
|
|
|
m_lines++;
|
|
}
|
|
|
|
if (swap)
|
|
{
|
|
m_vmblock = KateFactory::self()->vm()->allocate(size);
|
|
m_vmblockSize = size;
|
|
|
|
if (!rawData.isEmpty())
|
|
{
|
|
if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, size))
|
|
{
|
|
if (m_vmblock)
|
|
KateFactory::self()->vm()->free(m_vmblock);
|
|
|
|
m_vmblock = 0;
|
|
m_vmblockSize = 0;
|
|
|
|
m_parent->m_cacheWriteError = true;
|
|
}
|
|
}
|
|
|
|
// fine, we are swapped !
|
|
m_state = KateBufBlock::stateSwapped;
|
|
}
|
|
else
|
|
{
|
|
// we are a new dirty block without any swap data
|
|
m_state = KateBufBlock::stateDirty;
|
|
m_parent->m_loadedBlocks.append (this);
|
|
}
|
|
|
|
kdDebug (13020) << "A BLOCK LOADED WITH LINES: " << m_lines << endl;
|
|
}
|
|
|
|
KateTextLine::Ptr KateBufBlock::line(uint i)
|
|
{
|
|
// take care that the string list is around !!!
|
|
if (m_state == KateBufBlock::stateSwapped)
|
|
swapIn ();
|
|
|
|
// LRU
|
|
if (!m_parent->m_loadedBlocks.isLast(this))
|
|
m_parent->m_loadedBlocks.append (this);
|
|
|
|
return m_stringList[i];
|
|
}
|
|
|
|
void KateBufBlock::insertLine(uint i, KateTextLine::Ptr line)
|
|
{
|
|
// take care that the string list is around !!!
|
|
if (m_state == KateBufBlock::stateSwapped)
|
|
swapIn ();
|
|
|
|
m_stringList.insert (m_stringList.begin()+i, line);
|
|
m_lines++;
|
|
|
|
markDirty ();
|
|
}
|
|
|
|
void KateBufBlock::removeLine(uint i)
|
|
{
|
|
// take care that the string list is around !!!
|
|
if (m_state == KateBufBlock::stateSwapped)
|
|
swapIn ();
|
|
|
|
m_stringList.erase (m_stringList.begin()+i);
|
|
m_lines--;
|
|
|
|
markDirty ();
|
|
}
|
|
|
|
void KateBufBlock::markDirty ()
|
|
{
|
|
if (m_state != KateBufBlock::stateSwapped)
|
|
{
|
|
// LRU
|
|
if (!m_parent->m_loadedBlocks.isLast(this))
|
|
m_parent->m_loadedBlocks.append (this);
|
|
|
|
if (m_state == KateBufBlock::stateClean)
|
|
{
|
|
// if we have some swapped data allocated which is dirty, free it now
|
|
if (m_vmblock)
|
|
KateFactory::self()->vm()->free(m_vmblock);
|
|
|
|
m_vmblock = 0;
|
|
m_vmblockSize = 0;
|
|
|
|
// we are dirty
|
|
m_state = KateBufBlock::stateDirty;
|
|
}
|
|
}
|
|
}
|
|
|
|
void KateBufBlock::swapIn ()
|
|
{
|
|
if (m_state != KateBufBlock::stateSwapped)
|
|
return;
|
|
|
|
QByteArray rawData (m_vmblockSize);
|
|
|
|
// what to do if that fails ?
|
|
if (!KateFactory::self()->vm()->copyBlock(rawData.data(), m_vmblock, 0, rawData.size()))
|
|
m_parent->m_cacheReadError = true;
|
|
|
|
// reserve mem, keep realloc away on push_back
|
|
m_stringList.reserve (m_lines);
|
|
|
|
char *buf = rawData.data();
|
|
for (uint i=0; i < m_lines; i++)
|
|
{
|
|
KateTextLine::Ptr textLine = new KateTextLine ();
|
|
buf = textLine->restore (buf);
|
|
m_stringList.push_back (textLine);
|
|
}
|
|
|
|
// if we have allready enough blocks around, swap one
|
|
if (m_parent->m_loadedBlocks.count() >= KateBuffer::maxLoadedBlocks())
|
|
m_parent->m_loadedBlocks.first()->swapOut();
|
|
|
|
// fine, we are now clean again, save state + append to clean list
|
|
m_state = KateBufBlock::stateClean;
|
|
m_parent->m_loadedBlocks.append (this);
|
|
}
|
|
|
|
void KateBufBlock::swapOut ()
|
|
{
|
|
if (m_state == KateBufBlock::stateSwapped)
|
|
return;
|
|
|
|
if (m_state == KateBufBlock::stateDirty)
|
|
{
|
|
bool haveHl = m_parent->m_highlight && !m_parent->m_highlight->noHighlighting();
|
|
|
|
// Calculate size.
|
|
uint size = 0;
|
|
for (uint i=0; i < m_lines; i++)
|
|
size += m_stringList[i]->dumpSize (haveHl);
|
|
|
|
QByteArray rawData (size);
|
|
char *buf = rawData.data();
|
|
|
|
// Dump textlines
|
|
for (uint i=0; i < m_lines; i++)
|
|
buf = m_stringList[i]->dump (buf, haveHl);
|
|
|
|
m_vmblock = KateFactory::self()->vm()->allocate(rawData.size());
|
|
m_vmblockSize = rawData.size();
|
|
|
|
if (!rawData.isEmpty())
|
|
{
|
|
if (!KateFactory::self()->vm()->copyBlock(m_vmblock, rawData.data(), 0, rawData.size()))
|
|
{
|
|
if (m_vmblock)
|
|
KateFactory::self()->vm()->free(m_vmblock);
|
|
|
|
m_vmblock = 0;
|
|
m_vmblockSize = 0;
|
|
|
|
m_parent->m_cacheWriteError = true;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_stringList.clear();
|
|
|
|
// we are now swapped out, set state + remove us out of the lists !
|
|
m_state = KateBufBlock::stateSwapped;
|
|
KateBufBlockList::remove (this);
|
|
}
|
|
|
|
//END KateBufBlock
|
|
|
|
//BEGIN KateBufBlockList
|
|
|
|
KateBufBlockList::KateBufBlockList ()
|
|
: m_count (0),
|
|
m_first (0),
|
|
m_last (0)
|
|
{
|
|
}
|
|
|
|
void KateBufBlockList::append (KateBufBlock *buf)
|
|
{
|
|
if (buf->list)
|
|
buf->list->removeInternal (buf);
|
|
|
|
m_count++;
|
|
|
|
// append a element
|
|
if (m_last)
|
|
{
|
|
m_last->listNext = buf;
|
|
|
|
buf->listPrev = m_last;
|
|
buf->listNext = 0;
|
|
|
|
m_last = buf;
|
|
|
|
buf->list = this;
|
|
|
|
return;
|
|
}
|
|
|
|
// insert the first element
|
|
m_last = buf;
|
|
m_first = buf;
|
|
|
|
buf->listPrev = 0;
|
|
buf->listNext = 0;
|
|
|
|
buf->list = this;
|
|
}
|
|
|
|
void KateBufBlockList::removeInternal (KateBufBlock *buf)
|
|
{
|
|
if (buf->list != this)
|
|
return;
|
|
|
|
m_count--;
|
|
|
|
if ((buf == m_first) && (buf == m_last))
|
|
{
|
|
// last element removed !
|
|
m_first = 0;
|
|
m_last = 0;
|
|
}
|
|
else if (buf == m_first)
|
|
{
|
|
// first element removed
|
|
m_first = buf->listNext;
|
|
m_first->listPrev = 0;
|
|
}
|
|
else if (buf == m_last)
|
|
{
|
|
// last element removed
|
|
m_last = buf->listPrev;
|
|
m_last->listNext = 0;
|
|
}
|
|
else
|
|
{
|
|
buf->listPrev->listNext = buf->listNext;
|
|
buf->listNext->listPrev = buf->listPrev;
|
|
}
|
|
|
|
buf->listPrev = 0;
|
|
buf->listNext = 0;
|
|
|
|
buf->list = 0;
|
|
}
|
|
|
|
//END KateBufBlockList
|
|
|
|
// kate: space-indent on; indent-width 2; replace-tabs on;
|