RedPanda-CPP/RedPandaIDE/qsynedit/TextBuffer.cpp

1153 lines
29 KiB
C++
Raw Normal View History

2021-12-26 23:18:28 +08:00
/*
* Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2021-05-14 23:56:43 +08:00
#include "TextBuffer.h"
#include <QDataStream>
#include <QFile>
#include <QTextCodec>
#include <QTextStream>
#include <QMutexLocker>
2021-05-14 23:56:43 +08:00
#include <stdexcept>
2021-05-18 15:49:58 +08:00
#include "SynEdit.h"
2021-05-14 23:56:43 +08:00
#include "../utils.h"
#include "../platform.h"
#include <QMessageBox>
#include <cmath>
2021-05-14 23:56:43 +08:00
SynDocument::SynDocument(const QFont& font, QObject *parent):
2021-05-18 15:49:58 +08:00
QObject(parent),
mTabWidth(4),
mFontMetrics(font),
2022-01-04 16:50:54 +08:00
mMutex(QMutex::Recursive)
2021-05-14 23:56:43 +08:00
{
2021-05-14 23:56:43 +08:00
mAppendNewLineAtEOF = true;
mFileEndingType = FileEndingType::Windows;
mIndexOfLongestLine = -1;
mUpdateCount = 0;
mCharWidth = mFontMetrics.horizontalAdvance("M");
2021-05-14 23:56:43 +08:00
}
static void ListIndexOutOfBounds(int index) {
throw IndexOutOfRange(index);
}
2021-05-18 15:49:58 +08:00
int SynDocument::parenthesisLevels(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange.parenthesisLevel;
2021-05-14 23:56:43 +08:00
} else
return 0;
}
int SynDocument::bracketLevels(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange.bracketLevel;
2021-05-14 23:56:43 +08:00
} else
return 0;
}
int SynDocument::braceLevels(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange.braceLevel;
2021-05-14 23:56:43 +08:00
} else
return 0;
}
2021-05-18 15:49:58 +08:00
//QString SynEditStringList::expandedStrings(int Index)
//{
// if (Index>=0 && Index < mList.size()) {
// if (mList[Index]->fFlags & SynEditStringFlag::sfHasNoTabs)
// return mList[Index]->fString;
// else
// return ExpandString(Index);
// } else
// return QString();
//}
2021-05-14 23:56:43 +08:00
int SynDocument::lineColumns(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
if (mLines[Index]->fColumns == -1) {
2021-05-18 15:49:58 +08:00
return calculateLineColumns(Index);
} else
return mLines[Index]->fColumns;
2021-05-14 23:56:43 +08:00
} else
return 0;
}
int SynDocument::leftBraces(int Index)
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange.leftBraces;
} else
return 0;
}
int SynDocument::rightBraces(int Index)
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange.rightBraces;
} else
return 0;
}
int SynDocument::lengthOfLongestLine() {
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
if (mIndexOfLongestLine < 0) {
int MaxLen = -1;
mIndexOfLongestLine = -1;
if (mLines.count() > 0 ) {
for (int i=0;i<mLines.size();i++) {
2021-05-18 15:49:58 +08:00
int len = lineColumns(i);
2021-05-14 23:56:43 +08:00
if (len > MaxLen) {
MaxLen = len;
mIndexOfLongestLine = i;
}
}
}
}
if (mIndexOfLongestLine >= 0)
return mLines[mIndexOfLongestLine]->fColumns;
2021-05-14 23:56:43 +08:00
else
return 0;
}
QString SynDocument::lineBreak() const
2021-05-29 21:35:46 +08:00
{
switch(mFileEndingType) {
case FileEndingType::Linux:
return "\n";
case FileEndingType::Windows:
return "\r\n";
case FileEndingType::Mac:
return "\r";
}
2021-08-16 23:17:48 +08:00
return "\n";
2021-05-29 21:35:46 +08:00
}
SynRangeState SynDocument::ranges(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index>=0 && Index < mLines.size()) {
return mLines[Index]->fRange;
} else {
ListIndexOutOfBounds(Index);
}
return {0};
2021-05-14 23:56:43 +08:00
}
void SynDocument::insertItem(int Index, const QString &s)
2021-05-14 23:56:43 +08:00
{
beginUpdate();
PSynDocumentLine line = std::make_shared<SynDocumentLine>();
2021-05-14 23:56:43 +08:00
line->fString = s;
mIndexOfLongestLine = -1;
mLines.insert(Index,line);
2021-05-14 23:56:43 +08:00
endUpdate();
}
void SynDocument::addItem(const QString &s)
2021-05-24 00:41:00 +08:00
{
beginUpdate();
PSynDocumentLine line = std::make_shared<SynDocumentLine>();
2021-05-24 00:41:00 +08:00
line->fString = s;
mIndexOfLongestLine = -1;
mLines.append(line);
2021-05-24 00:41:00 +08:00
endUpdate();
}
bool SynDocument::getAppendNewLineAtEOF()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
return mAppendNewLineAtEOF;
}
void SynDocument::setAppendNewLineAtEOF(bool appendNewLineAtEOF)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
mAppendNewLineAtEOF = appendNewLineAtEOF;
}
void SynDocument::setRange(int Index, const SynRangeState& ARange)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
mLines[Index]->fRange = ARange;
2021-05-14 23:56:43 +08:00
endUpdate();
}
QString SynDocument::getString(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
return QString();
}
return mLines[Index]->fString;
2021-05-14 23:56:43 +08:00
}
int SynDocument::count()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
return mLines.count();
2021-05-14 23:56:43 +08:00
}
void *SynDocument::getObject(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
return nullptr;
}
return mLines[Index]->fObject;
2021-05-14 23:56:43 +08:00
}
QString SynDocument::text()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-08-08 17:22:37 +08:00
return getTextStr();
2021-05-14 23:56:43 +08:00
}
void SynDocument::setText(const QString &text)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
putTextStr(text);
2021-05-14 23:56:43 +08:00
}
void SynDocument::setContents(const QStringList &text)
{
QMutexLocker locker(&mMutex);
beginUpdate();
auto action = finally([this]{
endUpdate();
});
internalClear();
if (text.count() > 0) {
mIndexOfLongestLine = -1;
int FirstAdded = mLines.count();
foreach (const QString& s,text) {
addItem(s);
}
emit inserted(FirstAdded,text.count());
}
}
QStringList SynDocument::contents()
2021-08-23 10:16:06 +08:00
{
QMutexLocker locker(&mMutex);
2021-08-23 10:16:06 +08:00
QStringList Result;
SynDocumentLines list = mLines;
foreach (const PSynDocumentLine& line, list) {
2021-08-23 10:16:06 +08:00
Result.append(line->fString);
}
return Result;
}
void SynDocument::beginUpdate()
2021-05-14 23:56:43 +08:00
{
if (mUpdateCount == 0) {
setUpdateState(true);
2021-05-14 23:56:43 +08:00
}
mUpdateCount++;
}
void SynDocument::endUpdate()
2021-05-14 23:56:43 +08:00
{
mUpdateCount--;
if (mUpdateCount == 0) {
setUpdateState(false);
2021-05-14 23:56:43 +08:00
}
}
int SynDocument::add(const QString &s)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
beginUpdate();
int Result = mLines.count();
insertItem(Result, s);
2021-05-14 23:56:43 +08:00
emit inserted(Result,1);
endUpdate();
return Result;
}
void SynDocument::addStrings(const QStringList &Strings)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
if (Strings.count() > 0) {
mIndexOfLongestLine = -1;
beginUpdate();
auto action = finally([this]{
endUpdate();
});
int FirstAdded = mLines.count();
2021-05-14 23:56:43 +08:00
for (const QString& s:Strings) {
2021-05-24 00:41:00 +08:00
addItem(s);
2021-05-14 23:56:43 +08:00
}
emit inserted(FirstAdded,Strings.count());
}
}
int SynDocument::getTextLength()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
int Result = 0;
foreach (const PSynDocumentLine& line, mLines ) {
2021-05-14 23:56:43 +08:00
Result += line->fString.length();
if (mFileEndingType == FileEndingType::Windows) {
Result += 2;
} else {
Result += 1;
}
}
return Result;
2021-05-14 23:56:43 +08:00
}
void SynDocument::clear()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
internalClear();
2021-05-14 23:56:43 +08:00
}
void SynDocument::deleteLines(int Index, int NumLines)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
if (NumLines<=0)
return;
if ((Index < 0) || (Index >= mLines.count())) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
auto action = finally([this]{
endUpdate();
});
if (mIndexOfLongestLine>=Index) {
if (mIndexOfLongestLine <Index+NumLines) {
mIndexOfLongestLine = -1;
} else {
mIndexOfLongestLine -= NumLines;
}
2021-05-14 23:56:43 +08:00
}
int LinesAfter = mLines.count() - (Index + NumLines);
2021-05-14 23:56:43 +08:00
if (LinesAfter < 0) {
NumLines = mLines.count() - Index;
2021-05-14 23:56:43 +08:00
}
mLines.remove(Index,NumLines);
2021-05-14 23:56:43 +08:00
emit deleted(Index,NumLines);
}
void SynDocument::exchange(int Index1, int Index2)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if ((Index1 < 0) || (Index1 >= mLines.count())) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index1);
}
if ((Index2 < 0) || (Index2 >= mLines.count())) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index2);
}
beginUpdate();
PSynDocumentLine temp = mLines[Index1];
mLines[Index1]=mLines[Index2];
mLines[Index2]=temp;
2022-01-04 16:50:54 +08:00
//mList.swapItemsAt(Index1,Index2);
2021-05-14 23:56:43 +08:00
if (mIndexOfLongestLine == Index1) {
mIndexOfLongestLine = Index2;
} else if (mIndexOfLongestLine == Index2) {
mIndexOfLongestLine = Index1;
}
endUpdate();
}
void SynDocument::insert(int Index, const QString &s)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if ((Index < 0) || (Index > mLines.count())) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
insertItem(Index, s);
2021-05-14 23:56:43 +08:00
emit inserted(Index,1);
endUpdate();
}
void SynDocument::deleteAt(int Index)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if ((Index < 0) || (Index >= mLines.count())) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
if (mIndexOfLongestLine == Index)
mIndexOfLongestLine = -1;
else if (mIndexOfLongestLine>Index)
mIndexOfLongestLine -= 1;
mLines.removeAt(Index);
2021-05-14 23:56:43 +08:00
emit deleted(Index,1);
endUpdate();
}
QString SynDocument::getTextStr() const
2021-05-14 23:56:43 +08:00
{
2021-09-02 12:14:02 +08:00
QString result;
for (int i=0;i<mLines.count()-1;i++) {
const PSynDocumentLine& line = mLines[i];
2021-09-02 12:14:02 +08:00
result.append(line->fString);
result.append(lineBreak());
2021-05-14 23:56:43 +08:00
}
if (mLines.length()>0) {
result.append(mLines.back()->fString);
2021-09-02 12:14:02 +08:00
}
return result;
2021-05-14 23:56:43 +08:00
}
void SynDocument::putString(int Index, const QString &s, bool notify) {
QMutexLocker locker(&mMutex);
if (Index == mLines.count()) {
2021-05-14 23:56:43 +08:00
add(s);
} else {
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
int oldColumns = mLines[Index]->fColumns;
mLines[Index]->fString = s;
calculateLineColumns(Index);
if (mIndexOfLongestLine == Index && oldColumns>mLines[Index]->fColumns )
mIndexOfLongestLine = -1;
else if (mIndexOfLongestLine>=0
&& mIndexOfLongestLine<mLines.count()
&& mLines[Index]->fColumns > mLines[mIndexOfLongestLine]->fColumns)
mIndexOfLongestLine = Index;
2021-11-13 13:03:42 +08:00
if (notify)
emit putted(Index,1);
2021-05-14 23:56:43 +08:00
endUpdate();
}
}
void SynDocument::putObject(int Index, void *AObject)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
beginUpdate();
mLines[Index]->fObject = AObject;
2021-05-14 23:56:43 +08:00
endUpdate();
}
void SynDocument::setUpdateState(bool Updating)
2021-05-14 23:56:43 +08:00
{
if (Updating)
emit changing();
else
emit changed();
}
int SynDocument::calculateLineColumns(int Index)
2021-05-14 23:56:43 +08:00
{
PSynDocumentLine line = mLines[Index];
2021-05-18 15:49:58 +08:00
line->fColumns = stringColumns(line->fString,0);
2021-05-18 15:49:58 +08:00
return line->fColumns;
2021-05-14 23:56:43 +08:00
}
void SynDocument::insertLines(int Index, int NumLines)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
if (NumLines<=0)
return;
beginUpdate();
auto action = finally([this]{
endUpdate();
});
mIndexOfLongestLine = -1;
PSynDocumentLine line;
mLines.insert(Index,NumLines,line);
2021-05-14 23:56:43 +08:00
for (int i=Index;i<Index+NumLines;i++) {
line = std::make_shared<SynDocumentLine>();
mLines[i]=line;
2021-05-14 23:56:43 +08:00
}
emit inserted(Index,NumLines);
}
void SynDocument::insertStrings(int Index, const QStringList &NewStrings)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
if (NewStrings.isEmpty())
return;
beginUpdate();
auto action = finally([this]{
endUpdate();
});
mIndexOfLongestLine = -1;
PSynDocumentLine line;
mLines.insert(Index,NewStrings.length(),line);
2021-05-14 23:56:43 +08:00
for (int i=0;i<NewStrings.length();i++) {
line = std::make_shared<SynDocumentLine>();
2021-05-14 23:56:43 +08:00
line->fString = NewStrings[i];
mLines[i+Index]=line;
2021-05-14 23:56:43 +08:00
}
emit inserted(Index,NewStrings.length());
}
void SynDocument::insertText(int Index, const QString &NewText)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
if (Index<0 || Index>=mLines.count()) {
2021-05-14 23:56:43 +08:00
ListIndexOutOfBounds(Index);
}
if (NewText.isEmpty())
return;
2021-11-12 10:51:00 +08:00
QStringList lines = textToLines(NewText);
insertStrings(Index,lines);
2021-05-14 23:56:43 +08:00
}
bool SynDocument::tryLoadFileByEncoding(QByteArray encodingName, QFile& file) {
QTextCodec* codec = QTextCodec::codecForName(encodingName);
if (!codec)
return false;
file.reset();
internalClear();
QTextCodec::ConverterState state;
while (true) {
if (file.atEnd()){
break;
}
QByteArray line = file.readLine();
if (line.endsWith("\r\n")) {
line.remove(line.length()-2,2);
} else if (line.endsWith("\r")) {
line.remove(line.length()-1,1);
} else if (line.endsWith("\n")){
line.remove(line.length()-1,1);
}
QString newLine = codec->toUnicode(line.constData(),line.length(),&state);
if (state.invalidChars>0) {
return false;
break;
}
addItem(newLine);
}
return true;
}
const QFontMetrics &SynDocument::fontMetrics() const
{
return mFontMetrics;
}
void SynDocument::setFontMetrics(const QFont &newFont)
{
mFontMetrics = QFontMetrics(newFont);
mCharWidth = mFontMetrics.horizontalAdvance("M");
}
void SynDocument::setTabWidth(int newTabWidth)
{
if (mTabWidth!=newTabWidth) {
mTabWidth = newTabWidth;
resetColumns();
}
}
void SynDocument::loadFromFile(const QString& filename, const QByteArray& encoding, QByteArray& realEncoding)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
QFile file(filename);
if (!file.open(QFile::ReadOnly ))
2021-05-14 23:56:43 +08:00
throw FileError(tr("Can't open file '%1' for read!").arg(file.fileName()));
beginUpdate();
internalClear();
2021-05-14 23:56:43 +08:00
auto action = finally([this]{
if (mLines.count()>0)
emit inserted(0,mLines.count());
2021-05-14 23:56:43 +08:00
endUpdate();
});
mIndexOfLongestLine = -1;
2021-05-14 23:56:43 +08:00
//test for utf8 / utf 8 bom
if (encoding == ENCODING_AUTO_DETECT) {
2021-05-24 00:41:00 +08:00
if (file.atEnd()) {
realEncoding = ENCODING_ASCII;
return;
}
2021-05-14 23:56:43 +08:00
QByteArray line = file.readLine();
QTextCodec* codec;
2021-05-24 00:41:00 +08:00
QTextCodec::ConverterState state;
2021-05-14 23:56:43 +08:00
bool needReread = false;
bool allAscii = true;
//test for BOM
if ((line.length()>=3) && ((unsigned char)line[0]==0xEF) && ((unsigned char)line[1]==0xBB) && ((unsigned char)line[2]==0xBF) ) {
realEncoding = ENCODING_UTF8_BOM;
line = line.mid(3);
codec = QTextCodec::codecForName(ENCODING_UTF8);
} else {
realEncoding = ENCODING_UTF8;
codec = QTextCodec::codecForName(ENCODING_UTF8);
}
2021-05-24 22:57:01 +08:00
if (line.endsWith("\r\n")) {
mFileEndingType = FileEndingType::Windows;
} else if (line.endsWith("\n")) {
mFileEndingType = FileEndingType::Linux;
} else if (line.endsWith("\r")) {
mFileEndingType = FileEndingType::Mac;
}
internalClear();
while (true) {
if (line.endsWith("\r\n")) {
line.remove(line.length()-2,2);
} else if (line.endsWith("\r")) {
line.remove(line.length()-1,1);
} else if (line.endsWith("\n")){
line.remove(line.length()-1,1);
}
2021-05-14 23:56:43 +08:00
if (allAscii) {
allAscii = isTextAllAscii(line);
}
if (allAscii) {
addItem(QString::fromLatin1(line));
2021-05-14 23:56:43 +08:00
} else {
2021-05-24 00:41:00 +08:00
QString newLine = codec->toUnicode(line.constData(),line.length(),&state);
if (state.invalidChars>0) {
2021-05-14 23:56:43 +08:00
needReread = true;
break;
}
addItem(newLine);
2021-05-14 23:56:43 +08:00
}
if (file.atEnd()){
break;
}
2021-05-14 23:56:43 +08:00
line = file.readLine();
}
2021-05-14 23:56:43 +08:00
if (!needReread) {
if (allAscii)
realEncoding = ENCODING_ASCII;
return;
}
realEncoding = pCharsetInfoManager->getDefaultSystemEncoding();
QList<PCharsetInfo> charsets = pCharsetInfoManager->findCharsetByLocale(pCharsetInfoManager->localeName());
if (!charsets.isEmpty()) {
if (tryLoadFileByEncoding(realEncoding,file)) {
return;
}
QSet<QByteArray> encodingSet;
for (int i=0;i<charsets.size();i++) {
encodingSet.insert(charsets[i]->name);
}
encodingSet.remove(realEncoding);
foreach (const QByteArray& encodingName,encodingSet) {
if (encodingName == ENCODING_UTF8)
continue;
if (tryLoadFileByEncoding(encodingName,file)) {
//qDebug()<<encodingName;
realEncoding = encodingName;
return;
}
}
}
2021-05-14 23:56:43 +08:00
} else {
realEncoding = encoding;
}
if (realEncoding == ENCODING_SYSTEM_DEFAULT) {
realEncoding = pCharsetInfoManager->getDefaultSystemEncoding();
2021-05-14 23:56:43 +08:00
}
file.reset();
QTextStream textStream(&file);
if (realEncoding == ENCODING_UTF8_BOM) {
textStream.setAutoDetectUnicode(true);
textStream.setCodec(ENCODING_UTF8);
} else {
textStream.setAutoDetectUnicode(false);
textStream.setCodec(realEncoding);
}
QString line;
internalClear();
2021-05-14 23:56:43 +08:00
while (textStream.readLineInto(&line)) {
if (line.endsWith("\r\n")) {
line.remove(line.length()-2,2);
} else if (line.endsWith("\r")) {
line.remove(line.length()-1,1);
} else if (line.endsWith("\n")){
line.remove(line.length()-1,1);
}
addItem(line);
2021-05-14 23:56:43 +08:00
}
}
void SynDocument::saveToFile(QFile &file, const QByteArray& encoding,
const QByteArray& defaultEncoding, QByteArray& realEncoding)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-24 22:57:01 +08:00
if (!file.open(QFile::WriteOnly | QFile::Truncate))
2021-05-14 23:56:43 +08:00
throw FileError(tr("Can't open file '%1' for save!").arg(file.fileName()));
if (mLines.isEmpty())
2021-05-14 23:56:43 +08:00
return;
bool allAscii = true;
QTextCodec* codec;
realEncoding = encoding;
2021-05-14 23:56:43 +08:00
if (realEncoding == ENCODING_UTF8_BOM) {
codec = QTextCodec::codecForName(ENCODING_UTF8);
file.putChar(0xEF);
file.putChar(0xBB);
file.putChar(0xBF);
2021-05-24 00:41:00 +08:00
} else if (realEncoding == ENCODING_SYSTEM_DEFAULT) {
2021-05-14 23:56:43 +08:00
codec = QTextCodec::codecForLocale();
} else if (realEncoding == ENCODING_AUTO_DETECT) {
codec = QTextCodec::codecForName(defaultEncoding);
if (!codec)
codec = QTextCodec::codecForLocale();
2021-05-24 22:57:01 +08:00
} else {
codec = QTextCodec::codecForName(realEncoding);
2021-05-14 23:56:43 +08:00
}
for (PSynDocumentLine& line:mLines) {
2021-05-14 23:56:43 +08:00
if (allAscii) {
allAscii = isTextAllAscii(line->fString);
}
if (!allAscii) {
file.write(codec->fromUnicode(line->fString));
} else {
file.write(line->fString.toLatin1());
}
file.write(lineBreak().toLatin1());
2021-05-14 23:56:43 +08:00
}
if (encoding == ENCODING_AUTO_DETECT) {
if (allAscii)
realEncoding = ENCODING_ASCII;
else if (codec->name() == "System") {
realEncoding = pCharsetInfoManager->getDefaultSystemEncoding();
} else {
realEncoding = codec->name();
}
2021-05-14 23:56:43 +08:00
}
}
int SynDocument::stringColumns(const QString &line, int colsBefore) const
{
int columns = std::max(0,colsBefore);
int charCols;
for (int i=0;i<line.length();i++) {
QChar ch = line[i];
if (ch == '\t') {
charCols = mTabWidth - columns % mTabWidth;
} else {
charCols = charColumns(ch);
}
columns+=charCols;
}
return columns-colsBefore;
}
int SynDocument::charColumns(QChar ch) const
{
if (ch.unicode()<=32)
return 1;
//return std::ceil((int)(fontMetrics().horizontalAdvance(ch) * dpiFactor()) / (double)mCharWidth);
return std::ceil((int)(fontMetrics().horizontalAdvance(ch)) / (double)mCharWidth);
}
void SynDocument::putTextStr(const QString &text)
2021-05-14 23:56:43 +08:00
{
beginUpdate();
auto action = finally([this]{
endUpdate();
});
internalClear();
2021-05-14 23:56:43 +08:00
int pos = 0;
int start;
while (pos < text.length()) {
start = pos;
while (pos<text.length()) {
if (text[pos] == '\r' || text[pos] == '\n') {
break;
}
pos++;
}
add(text.mid(start,pos-start));
if (pos>=text.length())
break;
if (text[pos] == '\r')
pos++;
if (text[pos] == '\n')
pos++;
}
}
void SynDocument::internalClear()
{
if (!mLines.isEmpty()) {
beginUpdate();
int oldCount = mLines.count();
mIndexOfLongestLine = -1;
mLines.clear();
emit deleted(0,oldCount);
endUpdate();
}
}
FileEndingType SynDocument::getFileEndingType()
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
return mFileEndingType;
}
void SynDocument::setFileEndingType(const FileEndingType &fileEndingType)
2021-05-14 23:56:43 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-14 23:56:43 +08:00
mFileEndingType = fileEndingType;
}
bool SynDocument::empty()
2021-05-24 00:41:00 +08:00
{
QMutexLocker locker(&mMutex);
return mLines.count()==0;
2021-05-24 00:41:00 +08:00
}
void SynDocument::resetColumns()
2021-06-07 11:02:03 +08:00
{
QMutexLocker locker(&mMutex);
2021-06-07 11:02:03 +08:00
mIndexOfLongestLine = -1;
if (mLines.count() > 0 ) {
for (int i=0;i<mLines.size();i++) {
mLines[i]->fColumns = -1;
2021-06-07 11:02:03 +08:00
}
}
}
void SynDocument::invalidAllLineColumns()
2021-05-18 15:49:58 +08:00
{
QMutexLocker locker(&mMutex);
2021-05-18 15:49:58 +08:00
mIndexOfLongestLine = -1;
for (PSynDocumentLine& line:mLines) {
2021-05-18 15:49:58 +08:00
line->fColumns = -1;
}
}
SynDocumentLine::SynDocumentLine():
2021-05-14 23:56:43 +08:00
fString(),
fObject(nullptr),
fRange{0,0,0,0,0},
fColumns(-1)
2021-05-14 23:56:43 +08:00
{
}
SynEditUndoList::SynEditUndoList():QObject()
{
mMaxUndoActions = 1024;
mNextChangeNumber = 1;
mInsideRedo = false;
mBlockChangeNumber=0;
mBlockCount=0;
mFullUndoImposible=false;
mLockCount = 0;
mInitialChangeNumber = 0;
}
void SynEditUndoList::AddChange(SynChangeReason AReason, const BufferCoord &AStart,
const BufferCoord &AEnd, const QString &ChangeText,
SynSelectionMode SelMode)
{
if (mLockCount != 0)
return;
int changeNumber;
if (mBlockChangeNumber != 0) {
changeNumber = mBlockChangeNumber;
} else {
changeNumber = mNextChangeNumber;
if (mBlockCount == 0) {
mNextChangeNumber++;
if (mNextChangeNumber == 0) {
mNextChangeNumber++;
}
}
}
PSynEditUndoItem NewItem = std::make_shared<SynEditUndoItem>(AReason,
SelMode,AStart,AEnd,ChangeText,
changeNumber);
PushItem(NewItem);
}
void SynEditUndoList::AddGroupBreak()
{
//Add the GroupBreak even if ItemCount = 0. Since items are stored in
//reverse order in TCustomSynEdit.fRedoList, a GroupBreak could be lost.
if (LastChangeReason() != SynChangeReason::crGroupBreak) {
AddChange(SynChangeReason::crGroupBreak, {0,0}, {0,0}, "", SynSelectionMode::smNormal);
}
}
void SynEditUndoList::BeginBlock()
{
mBlockCount++;
mBlockChangeNumber = mNextChangeNumber;
}
void SynEditUndoList::Clear()
{
mItems.clear();
mFullUndoImposible = false;
}
void SynEditUndoList::DeleteItem(int index)
{
if (index <0 || index>=mItems.count()) {
ListIndexOutOfBounds(index);
}
mItems.removeAt(index);
}
void SynEditUndoList::EndBlock()
{
if (mBlockCount > 0) {
mBlockCount--;
if (mBlockCount == 0) {
int iBlockID = mBlockChangeNumber;
mBlockChangeNumber = 0;
mNextChangeNumber++;
if (mNextChangeNumber == 0)
mNextChangeNumber++;
if (mItems.count() > 0 && PeekItem()->changeNumber() == iBlockID)
emit addedUndo();
}
}
}
SynChangeReason SynEditUndoList::LastChangeReason()
{
if (mItems.count() == 0)
return SynChangeReason::crNothing;
else
return mItems.last()->changeReason();
2021-05-14 23:56:43 +08:00
}
bool SynEditUndoList::isEmpty()
{
return mItems.count()==0;
}
2021-05-14 23:56:43 +08:00
void SynEditUndoList::Lock()
{
mLockCount++;
}
PSynEditUndoItem SynEditUndoList::PeekItem()
{
if (mItems.count() == 0)
return PSynEditUndoItem();
else
return mItems.last();
}
PSynEditUndoItem SynEditUndoList::PopItem()
{
if (mItems.count() == 0)
return PSynEditUndoItem();
else {
PSynEditUndoItem item = mItems.last();
mItems.removeLast();
return item;
}
}
void SynEditUndoList::PushItem(PSynEditUndoItem Item)
{
if (!Item)
return;
mItems.append(Item);
ensureMaxEntries();
2021-05-14 23:56:43 +08:00
if (Item->changeReason()!= SynChangeReason::crGroupBreak)
emit addedUndo();
2021-05-14 23:56:43 +08:00
}
void SynEditUndoList::Unlock()
{
if (mLockCount > 0)
mLockCount--;
}
bool SynEditUndoList::CanUndo()
{
return mItems.count()>0;
}
int SynEditUndoList::ItemCount()
{
return mItems.count();
}
int SynEditUndoList::maxUndoActions() const
{
return mMaxUndoActions;
}
void SynEditUndoList::setMaxUndoActions(int maxUndoActions)
{
if (maxUndoActions!=mMaxUndoActions) {
mMaxUndoActions = maxUndoActions;
ensureMaxEntries();
}
2021-05-14 23:56:43 +08:00
}
bool SynEditUndoList::initialState()
{
if (ItemCount() == 0) {
return mInitialChangeNumber == 0;
} else {
return PeekItem()->changeNumber() == mInitialChangeNumber;
}
}
PSynEditUndoItem SynEditUndoList::item(int index)
{
if (index <0 || index>=mItems.count()) {
ListIndexOutOfBounds(index);
}
return mItems[index];
}
void SynEditUndoList::setInitialState(const bool Value)
{
if (Value) {
if (ItemCount() == 0)
mInitialChangeNumber = 0;
else
mInitialChangeNumber = PeekItem()->changeNumber();
} else if (ItemCount() == 0) {
if (mInitialChangeNumber == 0) {
mInitialChangeNumber = -1;
}
} else if (PeekItem()->changeNumber() == mInitialChangeNumber) {
mInitialChangeNumber = -1;
}
}
void SynEditUndoList::setItem(int index, PSynEditUndoItem Value)
{
if (index <0 || index>=mItems.count()) {
ListIndexOutOfBounds(index);
}
mItems[index]=Value;
}
int SynEditUndoList::blockChangeNumber() const
{
return mBlockChangeNumber;
}
void SynEditUndoList::setBlockChangeNumber(int blockChangeNumber)
{
mBlockChangeNumber = blockChangeNumber;
}
int SynEditUndoList::blockCount() const
{
return mBlockCount;
}
bool SynEditUndoList::insideRedo() const
{
return mInsideRedo;
}
void SynEditUndoList::setInsideRedo(bool insideRedo)
{
mInsideRedo = insideRedo;
}
bool SynEditUndoList::fullUndoImposible() const
{
return mFullUndoImposible;
}
void SynEditUndoList::ensureMaxEntries()
2021-05-14 23:56:43 +08:00
{
if (mItems.count() > mMaxUndoActions){
mFullUndoImposible = true;
while (mItems.count() > mMaxUndoActions) {
//remove all undo item in block
int changeNumber = mItems.front()->changeNumber();
while (mItems.count()>0 && mItems.front()->changeNumber() == changeNumber)
mItems.removeFirst();
2021-05-14 23:56:43 +08:00
}
}
}
SynSelectionMode SynEditUndoItem::changeSelMode() const
{
return mChangeSelMode;
}
BufferCoord SynEditUndoItem::changeStartPos() const
{
return mChangeStartPos;
}
BufferCoord SynEditUndoItem::changeEndPos() const
{
return mChangeEndPos;
}
QString SynEditUndoItem::changeStr() const
{
return mChangeStr;
}
int SynEditUndoItem::changeNumber() const
{
return mChangeNumber;
}
SynEditUndoItem::SynEditUndoItem(SynChangeReason reason, SynSelectionMode selMode,
BufferCoord startPos, BufferCoord endPos,
const QString &str, int number)
{
mChangeReason = reason;
mChangeSelMode = selMode;
mChangeStartPos = startPos;
mChangeEndPos = endPos;
mChangeStr = str;
mChangeNumber = number;
}
SynChangeReason SynEditUndoItem::changeReason() const
{
return mChangeReason;
}