* Constructor.
*/
ReLines::ReLines() :
- QStringList(),
- m_empty() {
+ QStringList(),
+ m_empty() {
}
/**
* Destructor.
}
/**
- * Inserts a text into a given position of the file.
+ * Checks that the summary size of the undo information remains under the maximum.
*
- * Note: the line separators will not be stored.
+ * @param stringSize the additional string size
+ * @return <code>true</code>: undo info should be stored
+ * <code>false</code>: too large undo info, all info has
+ * been freed
+ */
+bool ReUndoList::checkSummarySize(qint64 stringSize) {
+ static qint64 sizeStruct = (qint64) sizeof(UndoItem);
+ while (m_currentUndoSize + stringSize + sizeStruct > m_maxUndoSize) {
+ if (m_list.length() == 0)
+ m_currentUndoSize = 0;
+ else {
+ UndoItem* item = m_list.at(0);
+ m_currentUndoSize -= sizeof *item - item->m_string.length();
+ m_list.removeAt(0);
+ delete item;
+ }
+ }
+ return stringSize + sizeStruct < m_maxUndoSize;
+}
+
+/**
+ * Removes all lines.
+ */
+void ReLines::clear() {
+ QStringList::clear();
+}
+
+/**
+ * Inserts one or more lines into the lines.
*
- * @param line the line number (0..N-1) of the insert position
- * @param col the column number (0..M-1) of the insert position
- * @param text the text to insert. May contain line separators ('\n' or "\r\n").
- * In this case the number of lines grows
- */
-void ReLines::insertText(int line, int col, const QString& text) {
- if (line >= 0) {
- if (line == length()) {
- int beginOfLine = 0;
- int endOfLine = text.indexOf('\n');
- while (endOfLine >= 0) {
- int nextBOL = endOfLine + 1;
- if (endOfLine > 0 && text.at(endOfLine - 1) == '\r')
- endOfLine--;
- append(text.mid(beginOfLine, endOfLine - beginOfLine));
- beginOfLine = nextBOL;
- endOfLine = text.indexOf('\n', beginOfLine);
+ * @param lineNo the line number (0..N-1) of the new line
+ * @param text the text to insert. May not contain newlines!
+ * @param withUndo <code>true</code>: prepares undo operation<br>
+ * <code>false</code>: undo is impossible
+ */
+void ReLines::insertLines(int lineNo, const QString& text, bool withUndo) {
+ if (lineNo >= 0) {
+ int count = 0;
+ if (text.isEmpty())
+ count = 1;
+ else {
+ count = ReQStringUtil::countOf(text, '\n');
+ if (text.at(text.length() - 1) != '\n')
+ count++;
+ }
+ if (withUndo)
+ storeInsertLines(lineNo, count);
+ int start = 0;
+ int end;
+ if (lineNo >= length()) {
+ while ((end = text.indexOf('\n', start)) >= 0) {
+ append(text.mid(start, end - start));
+ start = end + 1;
}
- if (beginOfLine < text.length())
- append(text.mid(beginOfLine));
- } else if (line < length()) {
- QString oldLine = at(line);
- int length = oldLine.length();
- if (col < 0)
- col = 0;
- else if (col >= length)
- col = length - 1;
- QString tail;
- if (col < length - 1)
- tail = oldLine.mid(col);
- int endOfLine = text.indexOf('\n');
- if (endOfLine < 0) {
- replace(line, oldLine.mid(0, col) + text + tail);
- } else {
- int beginOfLine = endOfLine + 1;
- if (endOfLine > 0 && text[endOfLine - 1] == '\r')
- endOfLine--;
- replace(line++, oldLine.mid(0, col) + text.mid(0, endOfLine));
- do {
- if ((endOfLine = text.indexOf('\n', beginOfLine)) < 0)
- break;
- int nextBOL = endOfLine + 1;
- if (endOfLine > 0 && text.at(endOfLine - 1) == '\r')
- endOfLine--;
- insert(line++,
- text.mid(beginOfLine, endOfLine - beginOfLine));
- beginOfLine = nextBOL;
- } while (true);
- if (tail.length() == 0) {
- if (beginOfLine < text.length())
- insert(line, text.mid(beginOfLine));
- } else {
- if (beginOfLine >= text.length())
- insert(line, tail);
- else
- insert(line, text.mid(beginOfLine) + tail);
- }
+ if (start < text.length())
+ append(text.mid(start));
+ } else {
+ while ((end = text.indexOf('\n', start)) >= 0) {
+ insert(lineNo++, text.mid(start, end - start));
+ start = end + 1;
}
+ if (start < text.length())
+ insert(lineNo, text.mid(start));
+ }
+ }
+}
+/**
+ * Inserts a text into a given position of the file.
+ *
+ * @param lineNo the line number (0..N-1) of the insert position
+ * @param col the column number (0..M-1) of the insert position<br>
+ * col < 0: no insertion<br>
+ * col > length_of_the_line: the text will be appended to the line
+ * @param text the text to insert. May not contain newlines!
+ * @param withUndo <code>true</code>: prepares undo operation<br>
+ * <code>false</code>: undo is impossible
+ */
+void ReLines::insertPart(int lineNo, int col, const QString& text,
+ bool withUndo) {
+ if (lineNo >= 0 && lineNo < lineCount() && col >= 0) {
+ if (withUndo)
+ storeInsertPart(lineNo, col, text.length());
+ QString current = lineAt(lineNo);
+ if (col == 0)
+ replace(lineNo, text + current);
+ else if (col < current.length())
+ replace(lineNo, current.mid(0, col) + text + current.mid(col));
+ else
+ replace(lineNo, current.mid(0, col) + text);
+ }
+}
+
+/**
+ * Inserts a text into a given position of the file, with or without newlines.
+ *
+ * Note: the line separators will not be stored.
+ *
+ * @param lineNo the line number (0..N-1) of the insert position
+ * @param col the column number (0..M-1) of the insert position
+ * @param text the text to insert. May contain line separators ('\n' or "\r\n").
+ * In this case the number of lines grows
+ */
+void ReLines::insertText(int lineNo, int col, const QString& text) {
+ if (length() == 0)
+ insertLines(0, "", true);
+ int endOfLine = text.indexOf(QChar('\n'));
+ if (endOfLine < 0)
+ insertPart(lineNo, col, text, true);
+ else {
+ splitLine(lineNo, col, true);
+ int newLines = 0;
+ if (lineNo < length())
+ insertPart(lineNo, col, text.mid(0, endOfLine), true);
+ else
+ insertLines(lineNo, text.mid(0, endOfLine), true);
+ int lastEoLn = text.lastIndexOf('\n');
+ if (lastEoLn != endOfLine) {
+ int oldCount = lineCount();
+ insertLines(lineNo + 1,
+ text.mid(endOfLine + 1, lastEoLn - endOfLine), true);
+ newLines = lineCount() - oldCount;
+ }
+ if (lastEoLn != text.length() - 1) {
+ int nextLine = lineNo + newLines + 1;
+ if (nextLine < length())
+ insertPart(nextLine, 0, text.mid(lastEoLn + 1), true);
+ else
+ insertLines(nextLine, text.mid(lastEoLn + 1), true);
}
}
}
/**
* Removes a part of a line.
*
- * @param line the line number (0..N-1) of the first position to delete
- * @param col -1: join the current line with the previous line.
- * <line_length>: join the current line with the following
- * the column number (0..M-1) of the first position to delete
- * @param count the number of character to delete
- * @return <code>true</code>: a join of 2 lines has been done
+ * @param lineNo the line number (0..N-1) of the first position to delete
+ * @param col -1: join the current line with the previous line.
+ * <line_length>: join the current line with the following
+ * the column number (0..M-1) of the first position to delete
+ * @param count the number of character to delete
+ * @param withUndo <code>true</code>: prepares undo operation<br>
+ * <code>false</code>: undo is impossible
+ * @return <code>true</code>: a join of 2 lines has been done
*/
-bool ReLines::remove(int line, int pos, int count) {
+bool ReLines::removePart(int lineNo, int col, int count, bool withUndo) {
bool rc = false;
- if (line >= 0 && line < lineCount()){
- const QString& current = at(line);
- if (pos == -1) {
- rc = joinLines(line - 1);
- } else if (pos == current.length()) {
- rc = joinLines(line);
- } else if (pos >= 0) {
+ if (lineNo >= 0 && lineNo < lineCount()) {
+ const QString& current = at(lineNo);
+ if (col == -1) {
+ if (lineNo > 0) {
+ if (withUndo)
+ storeJoin(lineNo - 1, at(lineNo - 1).length());
+ rc = joinLines(lineNo - 1);
+ }
+ } else if (col == current.length()) {
+ if (withUndo)
+ storeJoin(lineNo, current.length());
+ rc = joinLines(lineNo);
+ } else if (col >= 0) {
int length = current.length();
- if (pos < length - 1) {
- if (pos + count > length)
- count = length - pos;
- if (pos == 0)
- replace(line, current.mid(count));
- else if (pos + count >= length)
- replace(line, current.mid(0, pos));
+ if (col < length - 1) {
+ if (col + count > length)
+ count = length - col;
+ if (withUndo)
+ storeRemovePart(lineNo, col, current.mid(col, count));
+ if (col == 0)
+ replace(lineNo, current.mid(count));
+ else if (col + count >= length)
+ replace(lineNo, current.mid(0, col));
else
- replace(line, current.mid(0, pos) + current.mid(pos + count));
+ replace(lineNo,
+ current.mid(0, col) + current.mid(col + count));
}
}
}
/**
* Removes a given number of lines.
*
- * @param start the line number (0..N-1) of the first line to remove
- * @param count the number of lines to delete
+ * @param start the line number (0..N-1) of the first line to remove
+ * @param count the number of lines to delete
+ * @param withUndo <code>true</code>: prepares undo operation<br>
+ * <code>false</code>: undo is impossible
*/
-
-void ReLines::removeLines(int start, int count) {
+void ReLines::removeLines(int start, int count, bool withUndo) {
if (start >= 0 && start < length()) {
if (start + count > length())
count = length() - start;
+ if (withUndo)
+ storeRemoveLines(start, count, *this);
for (int ix = start + count - 1; ix >= 0; ix--)
removeAt(ix);
}
}
+/**
+ * Splits a line at a given position into two lines.
+ *
+ * @param lineNo the line number (0..N-1) of line to split
+ * @param col the column number (0..M-1) of the split point. The character
+ * of this position will be the first in the next line
+ * @param withUndo <code>true</code>: prepares undo operation<br>
+ * <code>false</code>: undo is impossible
+ */
+void ReLines::splitLine(int lineNo, int col, bool withUndo) {
+ if (lineNo >= 0 && lineNo < length() && col >= 0) {
+ QString current = at(lineNo);
+ if (withUndo)
+ storeSplit(lineNo, col);
+ int count = length();
+ if (lineNo >= count - 1) {
+ if (col >= current.length())
+ append("");
+ else
+ append(current.mid(col));
+ } else {
+ if (col >= current.length())
+ insert(lineNo, "");
+ else
+ insert(lineNo + 1, current.mid(col));
+ }
+ replace(lineNo, current.mid(0, col));
+ }
+}
+/**
+ * Rewinds the last change operation (insertion/deletion).
+ *
+ * @param lineNo OUT: the line number of the restored operation
+ * @param col OUT: the column of the restored operation
+ */
+void ReLines::undo(int& lineNo, int& col) {
+ if (m_list.length() > 0) {
+ UndoItem* item = pop();
+ lineNo = item->m_lineNo;
+ col = item->m_position;
+ switch (item->m_type) {
+ case UIT_INSERT_PART:
+ removePart(item->m_lineNo, item->m_position, item->m_length, false);
+ break;
+ case UIT_INSERT_LINES:
+ removeLines(item->m_lineNo, item->m_length, false);
+ break;
+ case UIT_SPLIT:
+ joinLines(item->m_lineNo);
+ break;
+ case UIT_JOIN:
+ splitLine(item->m_lineNo, item->m_position, false);
+ break;
+ case UIT_REMOVE_LINES:
+ insertLines(item->m_lineNo, item->m_string, false);
+ break;
+ case UIT_REMOVE_PART:
+ insertPart(item->m_lineNo, item->m_position, item->m_string, false);
+ break;
+ default:
+ break;
+ }
+ delete item;
+ }
+}
+
/**
* Constructor.
*
* @param filename name of the file
*/
ReFile::ReFile(const QString& filename, bool readOnly, ReLogger* logger) :
- ReLineSource(),
- ReLines(),
- m_endOfLine(),
- m_filename(filename),
- m_file(filename),
- m_block(NULL),
- // in 32-bit address space we allocate only 10 MByte, in 64-bit environments 100 GByte
- m_blocksize(
- sizeof(void*) <= 4 ?
- 10 * 1024 * 1024ll : 0x100ll * 0x10000 * 0x10000),
- m_blockOffset(0),
- m_filesize(0),
- m_startOfLine(NULL),
- m_lineLength(0),
- m_lineOffset(0),
- m_currentLineNo(0),
- m_maxLineLength(0x10000),
- m_content(),
- m_readOnly(readOnly),
- m_logger(logger) {
+ ReLineSource(),
+ ReLines(),
+ m_endOfLine(),
+ m_filename(filename),
+ m_file(filename),
+ m_block(NULL),
+ // in 32-bit address space we allocate only 10 MByte, in 64-bit environments 100 GByte
+ m_blocksize(
+ sizeof(void*) <= 4 ?
+ 10 * 1024 * 1024ll : 0x100ll * 0x10000 * 0x10000),
+ m_blockOffset(0),
+ m_filesize(0),
+ m_startOfLine(NULL),
+ m_lineLength(0),
+ m_lineOffset(0),
+ m_currentLineNo(0),
+ m_maxLineLength(0x10000),
+ m_content(),
+ m_readOnly(readOnly),
+ m_logger(logger) {
#if defined __linux__
setEndOfLine("\n");
#elif defined __WIN32__
* Frees the resources.
*/
void ReFile::close() {
- clear();
+ ReFile::clearUndo();
m_file.close();
}
/**
* Finds the next line with the string.
*
- * @param toFind the string to find "" or the pattern matching the result
- * @param ignoreCase true: the search is case insensitive
- * @param lineNo OUT: 0 or the line number
- * @param line OUT: "" or the found line
- * @return true: a line has been found<br>
- * false: a line has not been found
+ * @param toFind the string to find "" or the pattern matching the result
+ * @param ignoreCase true: the search is case insensitive
+ * @param lineNo OUT: 0 or the line number
+ * @param line OUT: "" or the found line
+ * @return true: a line has been found<br>
+ * false: a line has not been found
*/
bool ReFile::findLine(const char* toFind, bool ignoreCase, int& lineNo,
- QString* line) {
+ QString* line) {
bool rc = false;
int length;
int sourceLength = strlen(toFind);
const char* ptr = start;
int restLength = length - sourceLength + 1;
while (restLength > 0
- && (ptr = reinterpret_cast<const char*>(
- ignoreCase ?
- memchr(start, first, restLength) :
- memichr(start, first, restLength))) != NULL) {
+ && (ptr = reinterpret_cast<const char*>(
+ ignoreCase ?
+ memchr(start, first, restLength) :
+ memichr(start, first, restLength))) != NULL) {
if ((
- ignoreCase ?
- _memicmp(ptr, toFind, sourceLength) :
- memcmp(ptr, toFind, sourceLength)) == 0) {
+ ignoreCase ?
+ _memicmp(ptr, toFind, sourceLength) :
+ memcmp(ptr, toFind, sourceLength)) == 0) {
rc = true;
lineNo = m_currentLineNo;
QByteArray buffer(m_startOfLine, m_lineLength);
* false: a line has not been found
*/
bool ReFile::findLine(const QString& includePattern, bool includeIsRegExpr,
- bool includeIgnoreCase, const QString& excludePattern,
- bool excludeIsRegExpr, bool excludeIgnoreCase, int& lineNo, QString* line) {
+ bool includeIgnoreCase, const QString& excludePattern,
+ bool excludeIsRegExpr, bool excludeIgnoreCase, int& lineNo, QString* line) {
bool rc = false;
if (line != NULL)
*line = "";
if (m_currentLineNo == 65639)
m_currentLineNo += 0;
rc = m_startOfLine = remap(m_lineOffset += m_lineLength,
- m_maxLineLength, lineLength);
+ m_maxLineLength, lineLength);
const char* ptr = reinterpret_cast<const char*>(memchr(rc, '\n',
- lineLength));
+ lineLength));
if (ptr != NULL)
lineLength = ptr - rc + 1;
length = m_lineLength = lineLength;
size = m_filesize - offset;
// Note: size <= m_blocksize
if (m_block != NULL && offset >= m_blockOffset
- && offset + size <= m_blockOffset + m_blocksize) {
+ && offset + size <= m_blockOffset + m_blocksize) {
// new block is inside the internal block:
// no remapping needed
rc = m_block + (offset - m_blockOffset);
if (m_block != NULL)
m_file.unmap(reinterpret_cast<uchar*>(m_block));
m_block = reinterpret_cast<char*>(m_file.map(m_blockOffset,
- m_blocksize));
+ m_blocksize));
rc = m_block + (offset - m_blockOffset);
length = m_blocksize - (rc - m_block);
if (length > size)
* @return the name of an existing directory
*/
QByteArray ReFile::tempDir(const char* node, const char* parent,
- bool withSeparator) {
+ bool withSeparator) {
#if defined __linux__
QByteArray temp("/tmp");
static const char* firstVar = "TMP";
* @return the full name of a temporary file
*/
QByteArray ReFile::tempFile(const char* node, const char* parent,
- bool deleteIfExists) {
+ bool deleteIfExists) {
QByteArray rc(tempDir(parent));
if (!rc.endsWith('/'))
rc += '/';
/**
* Reads the content of the file into the line list.
*
- * @param filename the full name of the file. If "" the internal name will be used
- * @return <code>true</code>success<br>
- * <code>false</code>file not readable
+ * @param filename the full name of the file. If "" the internal name will be used
+ * @return <code>true</code>success<br>
+ * <code>false</code>file not readable
*/
bool ReFile::write(const QString& filename) {
bool rc = false;
* @param mode file write mode: "w" (write) or "a" (append)
*/
void ReFile::writeToFile(const char* filename, const char* content,
- size_t contentLength, const char* mode) {
+ size_t contentLength, const char* mode) {
FILE* fp = fopen(filename, mode);
if (fp != NULL) {
if (contentLength == (size_t) - 1)
}
}
+/**
+ * Constructor.
+ */
+ReUndoList::ReUndoList() :
+ m_list(),
+ m_current(NULL),
+ m_lastLine(-1),
+ m_lastPosition(-1),
+ m_maxUndoSize(10 * 1024 * 1024),
+ m_currentUndoSize(0) {
+}
+
+/**
+ * Destructor.
+ */
+ReUndoList::~ReUndoList() {
+ clearUndo();
+}
+
+/**
+ * Frees the resources.
+ */
+void ReUndoList::clearUndo() {
+ for (int ii = 0; ii < m_list.length(); ii++) {
+ delete m_list.at(ii);
+ }
+ m_list.clear();
+}
+
+/**
+ * Returns the current maximum size of the undo information.
+ *
+ * @return the maximum size of the undo information
+ */
+qint64 ReUndoList::maxUndoSize() const {
+ return m_maxUndoSize;
+}
+/**
+ * Prepares the undo operation of an insertion in a given lineNo.
+ *
+ * @param line the line number of the the first line to insert
+ * @param count the number of lines to insert
+ */
+void ReUndoList::storeInsertLines(int lineNo, int count) {
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_INSERT_LINES;
+ item->m_lineNo = lineNo;
+ item->m_position = 0;
+ item->m_length = count;
+ item->m_isPart = false;
+ m_list.append(item);
+ checkSummarySize(0);
+ m_currentUndoSize += (qint64) sizeof(*item);
+}
+
+/**
+ * Prepares the undo operation of an insertion in a given line.
+ *
+ * @param lineNo the line number of the insertion point
+ * @param col the index of the insertion point in the line
+ * @param count the number of chars has been inserted
+ */
+void ReUndoList::storeInsertPart(int lineNo, int col, int count) {
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_INSERT_PART;
+ item->m_lineNo = lineNo;
+ item->m_position = col;
+ item->m_length = count;
+ item->m_isPart = false;
+ m_list.append(item);
+ checkSummarySize(0);
+ m_currentUndoSize += (qint64) sizeof(*item);
+}
+
+/**
+ * Prepares the undo operation of a join of two lines.
+ *
+ * @param lineNo the number of the first line to join. The second is line+1
+ * @param length the length of the first line
+ */
+void ReUndoList::storeJoin(int lineNo, int length) {
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_JOIN;
+ item->m_lineNo = lineNo;
+ item->m_position = length;
+ item->m_length = 0;
+ item->m_isPart = false;
+ m_list.append(item);
+ checkSummarySize(0);
+ m_currentUndoSize += (qint64) sizeof(*item);
+}
+
+/**
+ * Prepares the undo operation of the deletion of a part of a line.
+ *
+ * @param lineNo the line number of the deletion point
+ * @param col the index of the deletion point in the line
+ * @param string the text which is deleted
+ */
+void ReUndoList::storeRemovePart(int lineNo, int col, const QString& string) {
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_REMOVE_PART;
+ item->m_lineNo = lineNo;
+ item->m_position = col;
+ item->m_length = string.length();
+ item->m_string = string;
+ item->m_isPart = false;
+ m_list.append(item);
+}
+
+/**
+ * Sets the maximum size of the undo information.
+ *
+ * If the undo info exceeds this value the oldest infos will be removed.
+ *
+ * @param maxUndoSize the new maximum size of undo information
+ */
+void ReUndoList::setMaxUndoSize(qint64 maxUndoSize) {
+ if (maxUndoSize < (qint64) sizeof(UndoItem) + 1)
+ maxUndoSize = (qint64) sizeof(UndoItem) + 1;
+ m_maxUndoSize = maxUndoSize;
+}
+
+/**
+ * Prepares the undo operation of a line split.
+ *
+ * @param lineNo the line number of line to split
+ * @param col the index of the split point. The line will be split behind
+ */
+void ReUndoList::storeSplit(int lineNo, int col) {
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_SPLIT;
+ item->m_lineNo = lineNo;
+ item->m_position = col;
+ item->m_length = 0;
+ item->m_isPart = false;
+ m_list.append(item);
+ checkSummarySize(0);
+ m_currentUndoSize += (qint64) sizeof(*item);
+}
+
+/**
+ * Prepares the undo operation of some lines.
+ * @param lineNo the number of the first line to remove
+ * @param count the number of lines to remove
+ * @param list the list containing the lines for extracting the line content
+ */
+void ReUndoList::storeRemoveLines(int lineNo, int count,
+ const QStringList& list) {
+ qint64 size = 0;
+ // Calculate the additional space
+ for (int ii = lineNo + count - 1; ii >= lineNo; ii--)
+ // +1: the newline
+ size += list.at(ii).length() + 1;
+ //@ToDo: handle more than 2**31 summary length
+ if (checkSummarySize(size)) {
+ assert(size <= INT_MAX);
+ UndoItem* item = new UndoItem();
+ item->m_type = UIT_REMOVE_LINES;
+ item->m_lineNo = lineNo;
+ item->m_position = 0;
+ item->m_length = count;
+ item->m_string.reserve(size);
+ for (int ii = lineNo; ii < lineNo + count; ii++)
+ item->m_string.append(list.at(ii)).append('\n');
+ m_list.append(item);
+ }
+}
+
+/**
+ * Returns the last element of the undo item and remove it from the list.
+ *
+ * Note: Do not forget to free the returned item!
+ *
+ * @return the last element of the list
+ */
+ReUndoList::UndoItem* ReUndoList::pop() {
+ ReUndoList::UndoItem* item = m_list.last();
+ m_list.removeLast();
+ return item;
+}
QByteArray fn2(ReFile::tempFile("test2.txt", "cuReFile", true));
file.write(fn2);
file.close();
+ file.clear();
checkEqu(0, file.lineCount());
ReFile file2(fn2, false);
checkEqu(3, file2.lineCount());
checkEqu("ABCDE", lines.lineAt(2));
// at line start:
- lines.remove(0, 0, 2);
+ lines.removePart(0, 0, 2, true);
checkEqu(3, lines.lineCount());
checkEqu("3", lines.lineAt(0));
checkEqu("abcdefg", lines.lineAt(1));
checkEqu("ABCDE", lines.lineAt(2));
// at line end (precisely):
- lines.remove(1, 5, 2);
+ lines.removePart(1, 5, 2, true);
checkEqu(3, lines.lineCount());
checkEqu("3", lines.lineAt(0));
checkEqu("abcde", lines.lineAt(1));
checkEqu("ABCDE", lines.lineAt(2));
// at line end (too many chars):
- lines.remove(1, 3, 99);
+ lines.removePart(1, 3, 99, true);
checkEqu(3, lines.lineCount());
checkEqu("3", lines.lineAt(0));
checkEqu("abc", lines.lineAt(1));
checkEqu("ABCDE", lines.lineAt(2));
// no remove because of wrong arguments:
- lines.remove(-1, 3, 1);
- lines.remove(0, 1, 1);
- lines.remove(3, 1, 1);
+ lines.removePart(-1, 3, 1, true);
+ checkEqu(3, lines.lineCount());
+ lines.removePart(0, 2, 1, true);
+ checkEqu(3, lines.lineCount());
+ lines.removePart(3, 1, 1, true);
checkEqu(3, lines.lineCount());
checkEqu("3", lines.lineAt(0));
checkEqu("abc", lines.lineAt(1));
checkEqu("ABCDE", lines.lineAt(2));
}
+ void testRelLinesInsertLines() {
+ ReLines lines;
+ // inserts into an empty instance:
+ lines.insertLines(0, QString("123\nline2-abc\n"), true);
+ checkEqu(2, lines.lineCount());
+ checkEqu("123", lines.lineAt(0));
+ checkEqu("line2-abc", lines.lineAt(1));
+ // inserts at first line, one line:
+ lines.insertLines(0, QString("line-0"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("line-0", lines.lineAt(0));
+ checkEqu("123", lines.lineAt(1));
+ checkEqu("line2-abc", lines.lineAt(2));
+ // inserts in the middle, 2 lines, no '\n' at the end
+ lines.insertLines(1, QString("BCDE\nCDEF"), true);
+ checkEqu(5, lines.lineCount());
+ checkEqu("line-0", lines.lineAt(0));
+ checkEqu("BCDE", lines.lineAt(1));
+ checkEqu("CDEF", lines.lineAt(2));
+ checkEqu("123", lines.lineAt(3));
+ checkEqu("line2-abc", lines.lineAt(4));
+ // appends at the end, one line, ending with '\n':
+ lines.insertLines(6, QString("xyz\n"), true);
+ checkEqu(6, lines.lineCount());
+ checkEqu("line-0", lines.lineAt(0));
+ checkEqu("BCDE", lines.lineAt(1));
+ checkEqu("CDEF", lines.lineAt(2));
+ checkEqu("123", lines.lineAt(3));
+ checkEqu("line2-abc", lines.lineAt(4));
+ checkEqu("xyz", lines.lineAt(5));
+
+ // lineno outside:
+ lines.insertLines(-1, QString("bad\n"), true);
+ checkEqu(6, lines.lineCount());
+ lines.insertLines(9999, QString("last\n"), true);
+ checkEqu(7, lines.lineCount());
+ checkEqu("last", lines.lineAt(6));
+ }
+ void testRelLinesInsertPart() {
+ ReLines lines;
+ // inserts into an empty instance:
+ lines.insertLines(0, QString("123\nabc\nA"), true);
+ // first position:
+ lines.insertPart(0, 0, QString("x"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abc", lines.lineAt(1));
+ checkEqu("A", lines.lineAt(2));
+ // in the middle of the lines, in the middle of the line
+ lines.insertPart(1, 2, QString("YY"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc", lines.lineAt(1));
+ checkEqu("A", lines.lineAt(2));
+ // in the middle of the lines, at the end of the line
+ lines.insertPart(1, 5, QString("!?!"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!", lines.lineAt(1));
+ checkEqu("A", lines.lineAt(2));
+ // last line:
+ lines.insertPart(2, 0, QString("xyz"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!", lines.lineAt(1));
+ checkEqu("xyzA", lines.lineAt(2));
+
+ // outside of the line range:
+ lines.insertPart(-1, 0, QString("wrong"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!", lines.lineAt(1));
+ checkEqu("xyzA", lines.lineAt(2));
+ lines.insertPart(3, 0, QString("wrong"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!", lines.lineAt(1));
+ checkEqu("xyzA", lines.lineAt(2));
+
+ // outside of the column range:
+ lines.insertPart(0, -1, QString("wrong"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!", lines.lineAt(1));
+ checkEqu("xyzA", lines.lineAt(2));
+ lines.insertPart(1, 99, QString("appended"), true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("x123", lines.lineAt(0));
+ checkEqu("abYYc!?!appended", lines.lineAt(1));
+ checkEqu("xyzA", lines.lineAt(2));
+ }
+ void testRelLinesjoinLines() {
+ ReLines lines;
+ // inserts into an empty instance:
+ lines.insertLines(0, QString("123\nabc\nA\nB"), true);
+ checkEqu(4, lines.lineCount());
+
+ // inside the lines:
+ lines.joinLines(1);
+ checkEqu(3, lines.lineCount());
+ checkEqu("123", lines.lineAt(0));
+ checkEqu("abcA", lines.lineAt(1));
+ checkEqu("B", lines.lineAt(2));
+ // the last two lines:
+ lines.joinLines(1);
+ checkEqu(2, lines.lineCount());
+ checkEqu("123", lines.lineAt(0));
+ checkEqu("abcAB", lines.lineAt(1));
+ // the first two lines:
+ lines.joinLines(0);
+ checkEqu(1, lines.lineCount());
+ checkEqu("123abcAB", lines.lineAt(0));
+ }
+ void testReLinesSplitLine() {
+ ReLines lines;
+ // inserts into an empty instance:
+ lines.insertLines(0, QString("123\nabcdefg"), true);
+
+ // in the middle of the line:
+ lines.splitLine(0, 1, true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("1", lines.lineAt(0));
+ checkEqu("23", lines.lineAt(1));
+ checkEqu("abcdefg", lines.lineAt(2));
+ // at the end of the line:
+ lines.splitLine(1, 2, true);
+ checkEqu(4, lines.lineCount());
+ checkEqu("1", lines.lineAt(0));
+ checkEqu("", lines.lineAt(1));
+ checkEqu("23", lines.lineAt(2));
+ checkEqu("abcdefg", lines.lineAt(3));
+
+ lines.clear();
+ lines.insertLines(0, QString("12"), true);
+ // in the middle of the last line:
+ lines.splitLine(0, 1, true);
+ checkEqu(2, lines.lineCount());
+ checkEqu("1", lines.lineAt(0));
+ checkEqu("2", lines.lineAt(1));
+
+ // in the end of the last line:
+ lines.splitLine(1, 99, true);
+ checkEqu(3, lines.lineCount());
+ checkEqu("1", lines.lineAt(0));
+ checkEqu("2", lines.lineAt(1));
+ checkEqu("", lines.lineAt(2));
+ }
virtual void run() {
testReLinesInsert();
+ testReLinesSplitLine();
+ testRelLinesjoinLines();
+ testRelLinesInsertPart();
+ testRelLinesInsertLines();
testReLinesRemove();
testWritableFile();
testPerformance();