Skip to content

Commit

Permalink
Merge branch 'extendedFolding' into '1.3.0-release'
Browse files Browse the repository at this point in the history
Extended folding

See merge request devel/studio!554
  • Loading branch information
ClemensGAMS committed Jul 14, 2020
2 parents ba616a4 + 91d133f commit cbc59b7
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 87 deletions.
102 changes: 49 additions & 53 deletions src/editors/codeedit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ void CodeEdit::undoCommandAdded()

void CodeEdit::switchCurrentFolding()
{
switchFolding(textCursor().block());
toggleFolding(textCursor().block());
}

void CodeEdit::extendedRedo()
Expand Down Expand Up @@ -407,7 +407,7 @@ void CodeEdit::keyPressEvent(QKeyEvent* e)
}
if (e == Hotkey::ToggleBlockFolding) {
QTextBlock block = textCursor().block();
switchFolding(block);
toggleFolding(block);
return;
}
if (e == Hotkey::Indent) {
Expand Down Expand Up @@ -534,17 +534,19 @@ bool CodeEdit::allowClosing(int chIndex)
return allowAutoClose && (!prior.isLetterOrNumber() || chIndex < 3);
}

bool CodeEdit::switchFolding(QTextBlock block)
bool CodeEdit::toggleFolding(QTextBlock block)
{
bool folded;
block = findFoldStart(block);
if (!block.isValid()) return false;
int foldPos = foldStart(block.blockNumber(), folded);
if (foldPos < 0) return false;
QTextCursor cursor(block);
cursor.setPosition(cursor.position() + foldPos+1);
int foldCount = 0;
PositionPair pp = matchParentheses(cursor, true, &foldCount);
if (!pp.isNull() && pp.valid && block.userData()) {
BlockData *startDat = static_cast<BlockData*>(block.userData());
syntax::BlockData *startDat = syntax::BlockData::fromTextBlock(block);
int foldSkip = 0;
startDat->setFoldCount(folded ? 0 : foldCount);
while (foldCount--) {
Expand All @@ -554,10 +556,8 @@ bool CodeEdit::switchFolding(QTextBlock block)
--foldSkip;
} else {
block.setVisible(folded);
if (block.userData()) {
BlockData *dat = static_cast<BlockData*>(block.userData());
if (dat->isFolded()) foldSkip = dat->foldCount();
}
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat && dat->isFolded()) foldSkip = dat->foldCount();
}
}
}
Expand Down Expand Up @@ -586,8 +586,8 @@ void CodeEdit::foldAll()
cursor.setPosition(cursor.position() + foldPos+1);
int foldCount;
PositionPair pp = matchParentheses(cursor, true, &foldCount);
if (!pp.isNull() && pp.valid && block.userData()) {
BlockData *dat = static_cast<BlockData*>(block.userData());
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (!pp.isNull() && pp.valid && dat) {
dat->setFoldCount(foldCount);
if (foldRemain < foldCount) foldRemain = foldCount;
}
Expand All @@ -606,10 +606,8 @@ void CodeEdit::unfoldAll()
QTextBlock block = document()->firstBlock();
while (block.isValid()) {
if (!block.isVisible()) block.setVisible(true);
if (block.userData()) {
BlockData *dat = static_cast<BlockData*>(block.userData());
dat->setFoldCount(0);
}
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat) dat->setFoldCount(0);
block = block.next();
}
mFoldMark = LinePair();
Expand Down Expand Up @@ -645,6 +643,27 @@ LinePair CodeEdit::findFoldBlock(int line, bool onlyThisLine) const
return res;
}

QTextBlock CodeEdit::findFoldStart(QTextBlock block) const
{
int count = 0;
int depth = 0;
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat) {
if (dat->nestingImpact().rightOpen()) return block;
if (dat->nestingImpact().leftOpen())
depth = dat->nestingImpact().leftOpen() + 1;
}
while (block.isValid() && count < 1000) {
block = block.previous();
++count;
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat) depth += dat->nestingImpact().rightOpen();
if (depth > 0) return block;
if (dat) depth += dat->nestingImpact().leftOpen();
}
return QTextBlock();
}

bool CodeEdit::unfoldBadBlock(QTextBlock block)
{
if (!block.isVisible()) return false;
Expand All @@ -655,10 +674,8 @@ bool CodeEdit::unfoldBadBlock(QTextBlock block)
block.setVisible(true);
else
--skip;
if (block.userData()) {
BlockData *dat = static_cast<BlockData*>(block.userData());
skip = dat->foldCount();
}
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat) skip = dat->foldCount();
block = block.next();
}
return true;
Expand Down Expand Up @@ -694,7 +711,7 @@ bool CodeEdit::ensureUnfolded(int line)
while (block.isValid() && !block.isVisible())
block = block.previous();
if (block.blockNumber() != line) {
bool ok = switchFolding(block);
bool ok = toggleFolding(block);
if (!ok)
return unfoldBadBlock(block);
if (block.blockNumber() == lastUnfoldedNr) {
Expand Down Expand Up @@ -1377,11 +1394,11 @@ int CodeEdit::foldStart(int line, bool &folded, QString *closingSymbol) const
};
static int pSplit = parentheses.length()/2;
QTextBlock block = document()->findBlockByNumber(line);
if (!block.userData()) return -1;
syntax::BlockData* dat = syntax::BlockData::fromTextBlock(block);
if (!dat) return -1;

BlockData* dat = static_cast<BlockData*>(block.userData());
folded = dat->isFolded();
QVector<ParenthesesPos> parList = dat->parentheses();
QVector<syntax::ParenthesesPos> parList = dat->parentheses();
int depth = 0;
// if (parList.count())
// DEB() << "parenthesis " << parList.at(0).character << " at " << parList.at(0).relPos;
Expand Down Expand Up @@ -1417,9 +1434,9 @@ PositionPair CodeEdit::matchParentheses(QTextCursor cursor, bool all, int *foldC
static int pAll = parentheses.indexOf("/");
QTextBlock block = cursor.block();
if (!block.userData()) return PositionPair();
BlockData *startDat = static_cast<BlockData*>(block.userData());
syntax::BlockData *startDat = syntax::BlockData::fromTextBlock(block);
// int state = block.userState();
QVector<ParenthesesPos> parList = startDat->parentheses();
QVector<syntax::ParenthesesPos> parList = startDat->parentheses();
int pos = cursor.positionInBlock();
int start = -1;
for (int i = parList.count()-1; i >= 0; --i) {
Expand Down Expand Up @@ -1448,14 +1465,14 @@ PositionPair CodeEdit::matchParentheses(QTextCursor cursor, bool all, int *foldC
block = back ? block.previous() : block.next();
if (!block.isValid()) break;
if (foldCount) *foldCount = block.blockNumber() - startBlockNr;
if (block.userData()) {
BlockData *dat = static_cast<BlockData*>(block.userData());
syntax::BlockData *dat = syntax::BlockData::fromTextBlock(block);
if (dat) {
parList = dat->parentheses();
if (!parList.isEmpty()) isEmpty = false;
}
}
if (isEmpty) continue;
parList = static_cast<BlockData*>(block.userData())->parentheses();
parList = syntax::BlockData::fromTextBlock(block)->parentheses();
pi = back ? parList.count()-1 : 0;
}

Expand Down Expand Up @@ -1581,8 +1598,8 @@ void CodeEdit::updateExtraSelections()

void CodeEdit::unfold(QTextBlock block)
{
if (block.userData() && static_cast<BlockData*>(block.userData())->foldCount())
switchFolding(block);
if (block.userData() && syntax::BlockData::fromTextBlock(block)->foldCount())
toggleFolding(block);
}

void CodeEdit::extraSelBlockEdit(QList<QTextEdit::ExtraSelection>& selections)
Expand Down Expand Up @@ -2205,29 +2222,6 @@ void CodeEdit::BlockEdit::replaceBlockText(QStringList texts)
cursor.endEditBlock();
}

BlockData::~BlockData()
{ }

QChar BlockData::charForPos(int relPos)
{
for (int i = mParentheses.count()-1; i >= 0; --i) {
if (mParentheses.at(i).relPos == relPos || mParentheses.at(i).relPos-1 == relPos) {
return mParentheses.at(i).character;
}
}
return QChar();
}

QVector<ParenthesesPos> BlockData::parentheses() const
{
return mParentheses;
}

void BlockData::setParentheses(const QVector<ParenthesesPos> &parentheses)
{
mParentheses = parentheses;
}

void LineNumberArea::mousePressEvent(QMouseEvent *event)
{
QPoint pos = event->pos();
Expand All @@ -2236,7 +2230,8 @@ void LineNumberArea::mousePressEvent(QMouseEvent *event)
if (mCodeEditor->showFolding() && e.pos().x() < 0
&& e.pos().x() > -width() + (mCodeEditor->mIconCols * mCodeEditor->iconSize())) {
QTextBlock block = mCodeEditor->cursorForPosition(e.pos()).block();
if (mCodeEditor->switchFolding(block)) {
block = mCodeEditor->findFoldStart(block);
if (mCodeEditor->toggleFolding(block)) {
mNoCursorFocus = true;
event->accept();
return;
Expand All @@ -2252,6 +2247,7 @@ void LineNumberArea::mouseMoveEvent(QMouseEvent *event)
pos.setX(pos.x()-width());
QMouseEvent e(event->type(), pos, event->button(), event->buttons(), event->modifiers());
QTextBlock block = mCodeEditor->cursorForPosition(e.pos()).block();
block = mCodeEditor->findFoldStart(block);
LinePair newFoldMark = mCodeEditor->findFoldBlock(block.blockNumber(), true);
if (newFoldMark != mCodeEditor->mFoldMark) {
mCodeEditor->mFoldMark = newFoldMark;
Expand Down
31 changes: 3 additions & 28 deletions src/editors/codeedit.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <QTimer>
#include "editors/abstractedit.h"
#include "syntax/textmark.h"
#include "syntax/blockdata.h"

class QPaintEvent;
class QResizeEvent;
Expand All @@ -39,33 +40,6 @@ class Settings;
class LineNumberArea;
class SearchWidget;

struct ParenthesesPos
{
ParenthesesPos() : character(QChar()), relPos(-1) {}
ParenthesesPos(QChar _character, int _relPos) : character(_character), relPos(_relPos) {}
QChar character;
int relPos;
};

class BlockData : public QTextBlockUserData
{
public:
BlockData() {}
~BlockData();
QChar charForPos(int relPos);
bool isEmpty() {return mParentheses.isEmpty();}
QVector<ParenthesesPos> parentheses() const;
void setParentheses(const QVector<ParenthesesPos> &parentheses);
int &foldCount() { return mFoldCount; }
bool isFolded() const { return mFoldCount; }
void setFoldCount(int foldCount) { mFoldCount = foldCount; }

private:
// if extending the data remember to enhance isEmpty()
QVector<ParenthesesPos> mParentheses;
int mFoldCount = 0;
};

struct BlockEditPos
{
BlockEditPos(int _startLine, int _endLine, int _column)
Expand Down Expand Up @@ -205,8 +179,9 @@ private slots:
void rawKeyPressEvent(QKeyEvent *e);
void updateBlockEditPos();
bool allowClosing(int chIndex);
bool switchFolding(QTextBlock block);
bool toggleFolding(QTextBlock block);
LinePair findFoldBlock(int line, bool onlyThisLine = false) const override;
QTextBlock findFoldStart(QTextBlock block) const;
bool unfoldBadBlock(QTextBlock block);
void checkCursorAfterFolding();

Expand Down
2 changes: 2 additions & 0 deletions src/studio.pro
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ SOURCES += \
support/updatedialog.cpp \
svgengine.cpp \
syntax/basehighlighter.cpp \
syntax/blockdata.cpp \
syntax/syntaxdeclaration.cpp \
syntax/syntaxformats.cpp \
syntax/syntaxhighlighter.cpp \
Expand Down Expand Up @@ -366,6 +367,7 @@ HEADERS += \
syntax.h \
syntax/basehighlighter.h \
syntax/blockcode.h \
syntax/blockdata.h \
syntax/syntaxdeclaration.h \
syntax/syntaxformats.h \
syntax/syntaxhighlighter.h \
Expand Down
40 changes: 40 additions & 0 deletions src/syntax/blockdata.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "blockdata.h"

namespace gams {
namespace studio {
namespace syntax {

BlockData::~BlockData()
{ }

BlockData *BlockData::fromTextBlock(QTextBlock block)
{
return (block.isValid() && block.userData()) ? static_cast<BlockData*>(block.userData())
: nullptr;
}

QChar BlockData::charForPos(int relPos)
{
for (int i = mParentheses.count()-1; i >= 0; --i) {
if (mParentheses.at(i).relPos == relPos || mParentheses.at(i).relPos-1 == relPos) {
return mParentheses.at(i).character;
}
}
return QChar();
}

QVector<ParenthesesPos> BlockData::parentheses() const
{
return mParentheses;
}

void BlockData::setParentheses(const QVector<ParenthesesPos> &parentheses, const NestingImpact &nestingImpact)
{
mParentheses = parentheses;
mNestingImpact = nestingImpact;
}


} // namespace syntax
} // namespace studio
} // namespace gams
57 changes: 57 additions & 0 deletions src/syntax/blockdata.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#ifndef GAMS_STUDIO_BLOCKDATA_H
#define GAMS_STUDIO_BLOCKDATA_H

#include <QTextBlock>

namespace gams {
namespace studio {
namespace syntax {

struct NestingImpact
{
NestingImpact() {}
void addCloser() { --mImpact; if (mImpact<mMaxDepth) mMaxDepth = mImpact; }
void addOpener() { ++mImpact; }
int impact() { return mImpact; }
int leftOpen() { return mMaxDepth; }
int rightOpen() { return mImpact - mMaxDepth; }
private:
short mImpact = 0;
short mMaxDepth = 0;
};

struct ParenthesesPos
{
ParenthesesPos() : character(QChar()), relPos(-1) {}
ParenthesesPos(QChar _character, int _relPos) : character(_character), relPos(_relPos) {}
QChar character;
int relPos;
};

class BlockData : public QTextBlockUserData
{
public:
BlockData() {}
~BlockData();
static BlockData *fromTextBlock(QTextBlock block);
QChar charForPos(int relPos);
bool isEmpty() {return mParentheses.isEmpty();}
QVector<ParenthesesPos> parentheses() const;
void setParentheses(const QVector<ParenthesesPos> &parentheses, const NestingImpact &nestingImpact);
NestingImpact nestingImpact() const { return mNestingImpact; }
int &foldCount() { return mFoldCount; }
bool isFolded() const { return mFoldCount; }
void setFoldCount(int foldCount) { mFoldCount = foldCount; }

private:
// if extending the data remember to enhance isEmpty()
QVector<ParenthesesPos> mParentheses;
NestingImpact mNestingImpact;
int mFoldCount = 0;
};

} // namespace syntax
} // namespace studio
} // namespace gams

#endif // GAMS_STUDIO_BLOCKDATA_H
Loading

0 comments on commit cbc59b7

Please sign in to comment.