- enhancement: position caret at end of the line of folded code block

- enhancement: copy the whole folded code block
  - enhancement: delete the whole folded code block
  - fix: correctly update the folding state of code block, when deleted
This commit is contained in:
Roy Qu 2022-06-28 21:46:20 +08:00
parent 2008437342
commit a6b24290c9
6 changed files with 148 additions and 36 deletions

View File

@ -1,6 +1,10 @@
Red Panda C++ Version 1.1.3 Red Panda C++ Version 1.1.3
- fix: wrong auto indent calculation for comments - fix: wrong auto indent calculation for comments
- enhancement: position caret at end of the line of folded code block
- enhancement: copy the whole folded code block
- enhancement: delete the whole folded code block
- fix: correctly update the folding state of code block, when deleted
Red Panda C++ Version 1.1.2 Red Panda C++ Version 1.1.2
- enhancement: use different color to differenciate folder and headers in completion popup window - enhancement: use different color to differenciate folder and headers in completion popup window

View File

@ -10,7 +10,7 @@ isEmpty(APP_NAME) {
} }
isEmpty(APP_VERSION) { isEmpty(APP_VERSION) {
APP_VERSION=1.1.2 APP_VERSION=1.1.3
} }
macos: { macos: {

View File

@ -246,10 +246,10 @@ void SynEdit::setCaretXYEx(bool CallEnsureCursorPosVisible, BufferCoord value)
if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) { if (!mOptions.testFlag(SynEditorOption::eoScrollPastEol)) {
nMaxX = 1; nMaxX = 1;
} else { } else {
nMaxX = mDocument->getString(value.Line-1).length()+1; nMaxX = getDisplayStringAtLine(value.Line).length()+1;
} }
} else { } else {
nMaxX = mDocument->getString(value.Line-1).length()+1; nMaxX = getDisplayStringAtLine(value.Line).length()+1;
} }
value.Char = std::min(value.Char,nMaxX); value.Char = std::min(value.Char,nMaxX);
} }
@ -359,10 +359,13 @@ bool SynEdit::canRedo() const
int SynEdit::maxScrollWidth() const int SynEdit::maxScrollWidth() const
{ {
int maxLen = mDocument->lengthOfLongestLine();
if (highlighter())
maxLen = maxLen+stringColumns(highlighter()->foldString(),maxLen);
if (mOptions.testFlag(eoScrollPastEol)) if (mOptions.testFlag(eoScrollPastEol))
return std::max(mDocument->lengthOfLongestLine(),1); return std::max(maxLen ,1);
else else
return std::max(mDocument->lengthOfLongestLine()-mCharsInWindow+1, 1); return std::max(maxLen-mCharsInWindow+1, 1);
} }
bool SynEdit::getHighlighterAttriAtRowCol(const BufferCoord &XY, QString &Token, PSynHighlighterAttribute &Attri) bool SynEdit::getHighlighterAttriAtRowCol(const BufferCoord &XY, QString &Token, PSynHighlighterAttribute &Attri)
@ -826,7 +829,7 @@ QString SynEdit::GetLeftSpacing(int charCount, bool wantTabs) const
int SynEdit::charToColumn(int aLine, int aChar) const int SynEdit::charToColumn(int aLine, int aChar) const
{ {
if (aLine>=1 && aLine <= mDocument->count()) { if (aLine>=1 && aLine <= mDocument->count()) {
QString s = mDocument->getString(aLine - 1); QString s = getDisplayStringAtLine(aLine);
return charToColumn(s,aChar); return charToColumn(s,aChar);
} }
return aChar; return aChar;
@ -849,7 +852,7 @@ int SynEdit::columnToChar(int aLine, int aColumn) const
{ {
Q_ASSERT( (aLine <= mDocument->count()) && (aLine >= 1)); Q_ASSERT( (aLine <= mDocument->count()) && (aLine >= 1));
if (aLine <= mDocument->count()) { if (aLine <= mDocument->count()) {
QString s = mDocument->getString(aLine - 1); QString s = getDisplayStringAtLine(aLine);
int x = 0; int x = 0;
int len = s.length(); int len = s.length();
int i; int i;
@ -1211,8 +1214,8 @@ void SynEdit::processGutterClick(QMouseEvent *event)
// Check if we clicked on a folding thing // Check if we clicked on a folding thing
if (mUseCodeFolding) { if (mUseCodeFolding) {
PSynEditFoldRange FoldRange = foldStartAtLine(Line); PSynEditFoldRange foldRange = foldStartAtLine(Line);
if (FoldRange) { if (foldRange) {
// See if we actually clicked on the rectangle... // See if we actually clicked on the rectangle...
//rect.Left := Gutter.RealGutterWidth(CharWidth) - Gutter.RightOffset; //rect.Left := Gutter.RealGutterWidth(CharWidth) - Gutter.RightOffset;
QRect rect; QRect rect;
@ -1221,10 +1224,10 @@ void SynEdit::processGutterClick(QMouseEvent *event)
rect.setTop((RowColumn.Row - mTopLine) * mTextHeight); rect.setTop((RowColumn.Row - mTopLine) * mTextHeight);
rect.setBottom(rect.top() + mTextHeight - 1); rect.setBottom(rect.top() + mTextHeight - 1);
if (rect.contains(QPoint(X, Y))) { if (rect.contains(QPoint(X, Y))) {
if (FoldRange->collapsed) if (foldRange->collapsed)
uncollapse(FoldRange); uncollapse(foldRange);
else else
collapse(FoldRange); collapse(foldRange);
return; return;
} }
} }
@ -1972,6 +1975,16 @@ void SynEdit::doMouseScroll(bool isDragging)
computeScroll(isDragging); computeScroll(isDragging);
} }
QString SynEdit::getDisplayStringAtLine(int line) const
{
QString s = mDocument->getString(line-1);
PSynEditFoldRange foldRange = foldStartAtLine(line);
if ((foldRange) && foldRange->collapsed) {
return s+highlighter()->foldString();
}
return s;
}
void SynEdit::doDeleteLastChar() void SynEdit::doDeleteLastChar()
{ {
if (mReadOnly) if (mReadOnly)
@ -2369,6 +2382,20 @@ void SynEdit::insertLine(bool moveCaret)
} }
QString Temp = lineText(); QString Temp = lineText();
if (mCaretX>lineText().length()+1) {
PSynEditFoldRange foldRange = foldStartAtLine(mCaretY);
if ((foldRange) && foldRange->collapsed) {
QString s = Temp+highlighter()->foldString();
if (mCaretX > s.length()) {
mCaretY=foldRange->toLine;
if (mCaretY>mDocument->count()) {
mCaretY=mDocument->count();
}
Temp = lineText();
mCaretX=Temp.length()+1;
}
}
}
QString Temp2 = Temp; QString Temp2 = Temp;
QString Temp3; QString Temp3;
PSynHighlighterAttribute Attr; PSynHighlighterAttribute Attr;
@ -2615,7 +2642,7 @@ QRect SynEdit::calculateCaretRect() const
QPoint caretPos = rowColumnToPixels(coord); QPoint caretPos = rowColumnToPixels(coord);
int caretWidth=mCharWidth; int caretWidth=mCharWidth;
if (mCaretY <= mDocument->count() && mCaretX <= mDocument->getString(mCaretY-1).length()) { if (mCaretY <= mDocument->count() && mCaretX <= mDocument->getString(mCaretY-1).length()) {
caretWidth = charColumns(mDocument->getString(mCaretY-1)[mCaretX-1])*mCharWidth; caretWidth = charColumns(getDisplayStringAtLine(mCaretY)[mCaretX-1])*mCharWidth;
} }
if (mActiveSelectionMode == SynSelectionMode::smColumn) { if (mActiveSelectionMode == SynSelectionMode::smColumn) {
return QRect(caretPos.x(),caretPos.y(),caretWidth, return QRect(caretPos.x(),caretPos.y(),caretWidth,
@ -3615,19 +3642,22 @@ void SynEdit::rescanForFoldRanges()
// Add folds to a separate list // Add folds to a separate list
PSynEditFoldRanges TemporaryAllFoldRanges = std::make_shared<SynEditFoldRanges>(); PSynEditFoldRanges TemporaryAllFoldRanges = std::make_shared<SynEditFoldRanges>();
scanForFoldRanges(TemporaryAllFoldRanges); scanForFoldRanges(TemporaryAllFoldRanges);
SynEditFoldRanges ranges=mAllFoldRanges;
mAllFoldRanges.clear();
// Combine new with old folds, preserve parent order // Combine new with old folds, preserve parent order
for (int i = 0; i< TemporaryAllFoldRanges->count();i++) { for (int i = 0; i< TemporaryAllFoldRanges->count();i++) {
int j=0; int j=0;
while (j < mAllFoldRanges.count()) { while (j <ranges.count()) {
if (TemporaryAllFoldRanges->range(i)->fromLine < mAllFoldRanges[j]->fromLine) { if (TemporaryAllFoldRanges->range(i)->fromLine == ranges[j]->fromLine
mAllFoldRanges.insert(j, TemporaryAllFoldRanges->range(i)); && TemporaryAllFoldRanges->range(i)->toLine == ranges[j]->toLine
&& ranges[j]->collapsed) {
mAllFoldRanges.add(ranges[j]);
break; break;
} }
j++; j++;
} }
// If we can't prepend #i anywhere, just dump it at the end if (j>=ranges.count())
if (j >= mAllFoldRanges.count())
mAllFoldRanges.add(TemporaryAllFoldRanges->range(i)); mAllFoldRanges.add(TemporaryAllFoldRanges->range(i));
} }
@ -3806,7 +3836,7 @@ void SynEdit::initializeCaret()
//showCaret(); //showCaret();
} }
PSynEditFoldRange SynEdit::foldStartAtLine(int Line) PSynEditFoldRange SynEdit::foldStartAtLine(int Line) const
{ {
for (int i = 0; i<mAllFoldRanges.count();i++) { for (int i = 0; i<mAllFoldRanges.count();i++) {
PSynEditFoldRange range = mAllFoldRanges[i]; PSynEditFoldRange range = mAllFoldRanges[i];
@ -3818,6 +3848,19 @@ PSynEditFoldRange SynEdit::foldStartAtLine(int Line)
return PSynEditFoldRange(); return PSynEditFoldRange();
} }
bool SynEdit::foldCollapsedBetween(int startLine, int endLine) const
{
for (int i = 0; i<mAllFoldRanges.count();i++) {
PSynEditFoldRange range = mAllFoldRanges[i];
if (startLine >=range->fromLine && range->fromLine<=endLine
&& (range->collapsed || range->parentCollapsed())){
return true;
} else if (range->fromLine>endLine)
break; // sorted by line. don't bother scanning further
}
return false;
}
QString SynEdit::substringByColumns(const QString &s, int startColumn, int &colLen) QString SynEdit::substringByColumns(const QString &s, int startColumn, int &colLen)
{ {
@ -4852,8 +4895,18 @@ QString SynEdit::selText()
// //
int ColTo = blockEnd().Char; int ColTo = blockEnd().Char;
int Last = blockEnd().Line - 1; int Last = blockEnd().Line - 1;
switch(mActiveSelectionMode) { switch(mActiveSelectionMode) {
case SynSelectionMode::smNormal: case SynSelectionMode::smNormal:{
PSynEditFoldRange foldRange = foldStartAtLine(blockEnd().Line);
QString s = mDocument->getString(Last);
if ((foldRange) && foldRange->collapsed && ColTo>s.length()) {
s=s+highlighter()->foldString();
if (ColTo>s.length()) {
Last = foldRange->toLine-1;
ColTo = mDocument->getString(Last).length()+1;
}
}
if (First == Last) if (First == Last)
return mDocument->getString(First).mid(ColFrom-1, ColTo - ColFrom); return mDocument->getString(First).mid(ColFrom-1, ColTo - ColFrom);
else { else {
@ -4866,6 +4919,7 @@ QString SynEdit::selText()
result += mDocument->getString(Last).leftRef(ColTo-1); result += mDocument->getString(Last).leftRef(ColTo-1);
return result; return result;
} }
}
case SynSelectionMode::smColumn: case SynSelectionMode::smColumn:
{ {
First = blockBegin().Line; First = blockBegin().Line;
@ -4928,6 +4982,19 @@ SynEditCodeFolding &SynEdit::codeFolding()
return mCodeFolding; return mCodeFolding;
} }
QString SynEdit::displayLineText()
{
if (mCaretY >= 1 && mCaretY <= mDocument->count()) {
QString s= mDocument->getString(mCaretY - 1);
PSynEditFoldRange foldRange = foldStartAtLine(mCaretY);
if ((foldRange) && foldRange->collapsed) {
return s+highlighter()->foldString();
}
return s;
}
return QString();
}
QString SynEdit::lineText() const QString SynEdit::lineText() const
{ {
if (mCaretY >= 1 && mCaretY <= mDocument->count()) if (mCaretY >= 1 && mCaretY <= mDocument->count())
@ -4988,19 +5055,30 @@ void SynEdit::moveCaretHorz(int DX, bool isSelection)
{ {
BufferCoord ptO = caretXY(); BufferCoord ptO = caretXY();
BufferCoord ptDst = ptO; BufferCoord ptDst = ptO;
QString s = lineText(); QString s = displayLineText();
int nLineLen = s.length(); int nLineLen = s.length();
// only moving or selecting one char can change the line // only moving or selecting one char can change the line
//bool bChangeY = !mOptions.testFlag(SynEditorOption::eoScrollPastEol); //bool bChangeY = !mOptions.testFlag(SynEditorOption::eoScrollPastEol);
bool bChangeY=true; bool bChangeY=true;
if (bChangeY && (DX == -1) && (ptO.Char == 1) && (ptO.Line > 1)) { if (bChangeY && (DX == -1) && (ptO.Char == 1) && (ptO.Line > 1)) {
// end of previous line // end of previous line
ptDst.Line--; int row = lineToRow(ptDst.Line);
ptDst.Char = mDocument->getString(ptDst.Line - 1).length() + 1; row--;
int line = rowToLine(row);
if (line!=ptDst.Line && line>=1) {
ptDst.Line = line;
ptDst.Char = getDisplayStringAtLine(ptDst.Line).length() + 1;
}
} else if (bChangeY && (DX == 1) && (ptO.Char > nLineLen) && (ptO.Line < mDocument->count())) { } else if (bChangeY && (DX == 1) && (ptO.Char > nLineLen) && (ptO.Line < mDocument->count())) {
// start of next line // start of next line
ptDst.Line++; int row = lineToRow(ptDst.Line);
ptDst.Char=1; row++;
int line = rowToLine(row);
qDebug()<<line<<ptDst.Line;
if (line!=ptDst.Line && line<=mDocument->count()) {
ptDst.Line = line;
ptDst.Char = 1;
}
} else { } else {
ptDst.Char = std::max(1, ptDst.Char + DX); ptDst.Char = std::max(1, ptDst.Char + DX);
// don't go past last char when ScrollPastEol option not set // don't go past last char when ScrollPastEol option not set
@ -5104,7 +5182,7 @@ void SynEdit::moveCaretToLineEnd(bool isSelection)
{ {
int vNewX; int vNewX;
if (mOptions.testFlag(SynEditorOption::eoEnhanceEndKey)) { if (mOptions.testFlag(SynEditorOption::eoEnhanceEndKey)) {
QString vText = lineText(); QString vText = displayLineText();
int vLastNonBlank = vText.length()-1; int vLastNonBlank = vText.length()-1;
int vMinX = 0; int vMinX = 0;
while ((vLastNonBlank >= vMinX) && (vText[vLastNonBlank] == ' ' || vText[vLastNonBlank] =='\t')) while ((vLastNonBlank >= vMinX) && (vText[vLastNonBlank] == ' ' || vText[vLastNonBlank] =='\t'))
@ -5116,7 +5194,7 @@ void SynEdit::moveCaretToLineEnd(bool isSelection)
else else
vNewX = vText.length() + 1; vNewX = vText.length() + 1;
} else } else
vNewX = lineText().length() + 1; vNewX = displayLineText().length() + 1;
moveCaretAndSelection(caretXY(), BufferCoord{vNewX, mCaretY}, isSelection); moveCaretAndSelection(caretXY(), BufferCoord{vNewX, mCaretY}, isSelection);
} }
@ -5152,6 +5230,17 @@ void SynEdit::setSelTextPrimitiveEx(SynSelectionMode PasteMode, const QString &V
}); });
BufferCoord BB = blockBegin(); BufferCoord BB = blockBegin();
BufferCoord BE = blockEnd(); BufferCoord BE = blockEnd();
if (mActiveSelectionMode==SynSelectionMode::smNormal) {
PSynEditFoldRange foldRange = foldStartAtLine(BE.Line);
QString s = mDocument->getString(BE.Line-1);
if ((foldRange) && foldRange->collapsed && BE.Char>s.length()) {
s=s+highlighter()->foldString();
if (BE.Char>s.length()) {
BE.Line = foldRange->toLine;
BE.Char = mDocument->getString(BE.Line-1).length()+1;
}
}
}
if (selAvail()) { if (selAvail()) {
deleteSelection(BB,BE); deleteSelection(BB,BE);
if (mActiveSelectionMode == SynSelectionMode::smColumn) { if (mActiveSelectionMode == SynSelectionMode::smColumn) {
@ -5440,11 +5529,18 @@ void SynEdit::deleteSelection(const BufferCoord &BB, const BufferCoord &BE)
// the selection mark. // the selection mark.
QString TempString = mDocument->getString(BB.Line - 1).mid(0, BB.Char - 1) QString TempString = mDocument->getString(BB.Line - 1).mid(0, BB.Char - 1)
+ mDocument->getString(BE.Line - 1).mid(BE.Char-1); + mDocument->getString(BE.Line - 1).mid(BE.Char-1);
// bool collapsed=foldCollapsedBetween(BB.Line,BE.Line);
// Delete all lines in the selection range. // Delete all lines in the selection range.
mDocument->deleteLines(BB.Line, BE.Line - BB.Line); mDocument->deleteLines(BB.Line, BE.Line - BB.Line);
properSetLine(BB.Line-1,TempString); properSetLine(BB.Line-1,TempString);
UpdateMarks = true; UpdateMarks = true;
internalSetCaretXY(BB); internalSetCaretXY(BB);
// if (collapsed) {
// PSynEditFoldRange foldRange = foldStartAtLine(BB.Line);
// if (!foldRange
// || (!foldRange->collapsed))
// uncollapseAroundLine(BB.Line);
// }
} }
break; break;
case SynSelectionMode::smColumn: case SynSelectionMode::smColumn:
@ -6707,10 +6803,10 @@ void SynEdit::onLinesCleared()
void SynEdit::onLinesDeleted(int index, int count) void SynEdit::onLinesDeleted(int index, int count)
{ {
if (mUseCodeFolding)
foldOnListDeleted(index + 1, count);
if (mHighlighter && mDocument->count() > 0) if (mHighlighter && mDocument->count() > 0)
scanFrom(index, index+1); scanFrom(index, index+1);
if (mUseCodeFolding)
foldOnListDeleted(index + 1, count);
invalidateLines(index + 1, INT_MAX); invalidateLines(index + 1, INT_MAX);
invalidateGutterLines(index + 1, INT_MAX); invalidateGutterLines(index + 1, INT_MAX);
} }
@ -6784,12 +6880,16 @@ void SynEdit::setBlockEnd(BufferCoord Value)
{ {
//setActiveSelectionMode(mSelectionMode); //setActiveSelectionMode(mSelectionMode);
Value.Line = minMax(Value.Line, 1, mDocument->count()); Value.Line = minMax(Value.Line, 1, mDocument->count());
Value.Char = minMax(Value.Char, 1, mDocument->lengthOfLongestLine()+1);
if (mActiveSelectionMode == SynSelectionMode::smNormal) { if (mActiveSelectionMode == SynSelectionMode::smNormal) {
if (Value.Line >= 1 && Value.Line <= mDocument->count()) if (Value.Line >= 1 && Value.Line <= mDocument->count())
Value.Char = std::min(Value.Char, mDocument->getString(Value.Line - 1).length() + 1); Value.Char = std::min(Value.Char, getDisplayStringAtLine(Value.Line).length() + 1);
else else
Value.Char = 1; Value.Char = 1;
} else {
int maxLen = mDocument->lengthOfLongestLine();
if (highlighter())
maxLen = maxLen+stringColumns(highlighter()->foldString(),maxLen);
Value.Char = minMax(Value.Char, 1, maxLen+1);
} }
if (Value.Char != mBlockEnd.Char || Value.Line != mBlockEnd.Line) { if (Value.Char != mBlockEnd.Char || Value.Line != mBlockEnd.Line) {
if (mActiveSelectionMode == SynSelectionMode::smColumn && Value.Char != mBlockEnd.Char) { if (mActiveSelectionMode == SynSelectionMode::smColumn && Value.Char != mBlockEnd.Char) {
@ -6878,13 +6978,17 @@ void SynEdit::setBlockBegin(BufferCoord value)
int nInval1, nInval2; int nInval1, nInval2;
bool SelChanged; bool SelChanged;
//setActiveSelectionMode(mSelectionMode); //setActiveSelectionMode(mSelectionMode);
value.Char = minMax(value.Char, 1, mDocument->lengthOfLongestLine()+1);
value.Line = minMax(value.Line, 1, mDocument->count()); value.Line = minMax(value.Line, 1, mDocument->count());
if (mActiveSelectionMode == SynSelectionMode::smNormal) { if (mActiveSelectionMode == SynSelectionMode::smNormal) {
if (value.Line >= 1 && value.Line <= mDocument->count()) if (value.Line >= 1 && value.Line <= mDocument->count())
value.Char = std::min(value.Char, mDocument->getString(value.Line - 1).length() + 1); value.Char = std::min(value.Char, getDisplayStringAtLine(value.Line).length() + 1);
else else
value.Char = 1; value.Char = 1;
} else {
int maxLen = mDocument->lengthOfLongestLine();
if (highlighter())
maxLen = maxLen+stringColumns(highlighter()->foldString(),maxLen);
value.Char = minMax(value.Char, 1, maxLen+1);
} }
if (selAvail()) { if (selAvail()) {
if (mBlockBegin.Line < mBlockEnd.Line) { if (mBlockBegin.Line < mBlockEnd.Line) {

View File

@ -356,6 +356,7 @@ public:
SynEditCodeFolding & codeFolding(); SynEditCodeFolding & codeFolding();
QString displayLineText();
QString lineText() const; QString lineText() const;
void setLineText(const QString s); void setLineText(const QString s);
@ -526,7 +527,8 @@ private:
PSynEditFoldRange collapsedFoldStartAtLine(int Line); PSynEditFoldRange collapsedFoldStartAtLine(int Line);
void doOnPaintTransientEx(SynTransientType TransientType, bool Lock); void doOnPaintTransientEx(SynTransientType TransientType, bool Lock);
void initializeCaret(); void initializeCaret();
PSynEditFoldRange foldStartAtLine(int Line); PSynEditFoldRange foldStartAtLine(int Line) const;
bool foldCollapsedBetween(int startLine, int endLine) const;
QString substringByColumns(const QString& s, int startColumn, int& colLen); QString substringByColumns(const QString& s, int startColumn, int& colLen);
PSynEditFoldRange foldAroundLine(int Line); PSynEditFoldRange foldAroundLine(int Line);
PSynEditFoldRange foldAroundLineEx(int Line, bool WantCollapsed, bool AcceptFromLine, bool AcceptToLine); PSynEditFoldRange foldAroundLineEx(int Line, bool WantCollapsed, bool AcceptFromLine, bool AcceptToLine);
@ -615,6 +617,8 @@ private:
void doToggleBlockComment(); void doToggleBlockComment();
void doMouseScroll(bool isDragging); void doMouseScroll(bool isDragging);
QString getDisplayStringAtLine(int line) const;
private slots: private slots:
void onBookMarkOptionsChanged(); void onBookMarkOptionsChanged();

View File

@ -1046,7 +1046,7 @@ void SynEditTextPainter::PaintLines()
nFold = edit->stringColumns(sFold,edit->mDocument->lineColumns(vLine-1)); nFold = edit->stringColumns(sFold,edit->mDocument->lineColumns(vLine-1));
attr = edit->mHighlighter->symbolAttribute(); attr = edit->mHighlighter->symbolAttribute();
GetBraceColorAttr(edit->mHighlighter->getRangeState().braceLevel,attr); GetBraceColorAttr(edit->mHighlighter->getRangeState().braceLevel,attr);
AddHighlightToken(sFold,edit->mDocument->lineColumns(vLine-1)+1 - (vFirstChar - FirstCol) AddHighlightToken(sFold,edit->mDocument->lineColumns(vLine-1) - (vFirstChar - FirstCol)
, nFold, vLine, attr); , nFold, vLine, attr);
} }

View File

@ -23,7 +23,7 @@ SUBDIRS += \
APP_NAME = RedPandaCPP APP_NAME = RedPandaCPP
APP_VERSION = 1.1.2 APP_VERSION = 1.1.3
linux: { linux: {
isEmpty(PREFIX) { isEmpty(PREFIX) {