diff options
Diffstat (limited to 'kate/part/katecodefoldinghelpers.cpp')
-rw-r--r-- | kate/part/katecodefoldinghelpers.cpp | 1662 |
1 files changed, 1662 insertions, 0 deletions
diff --git a/kate/part/katecodefoldinghelpers.cpp b/kate/part/katecodefoldinghelpers.cpp new file mode 100644 index 000000000..49090820b --- /dev/null +++ b/kate/part/katecodefoldinghelpers.cpp @@ -0,0 +1,1662 @@ +/* This file is part of the KDE libraries + Copyright (C) 2002 Joseph Wenninger <jowenn@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 "katecodefoldinghelpers.h" +#include "katecodefoldinghelpers.moc" + +#include "katebuffer.h" +#include "katecursor.h" +#include <kdebug.h> + +#include <qstring.h> + +#define JW_DEBUG 0 + +bool KateCodeFoldingTree::trueVal = true; + +KateCodeFoldingNode::KateCodeFoldingNode() : + parentNode(0), + startLineRel(0), + endLineRel(0), + startCol(0), + endCol(0), + startLineValid(false), + endLineValid(false), + type(0), + visible(true), + deleteOpening(false), + deleteEnding(false) +{ +}//the endline fields should be initialised to not valid + +KateCodeFoldingNode::KateCodeFoldingNode(KateCodeFoldingNode *par, signed char typ, unsigned int sLRel): + parentNode(par), + startLineRel(sLRel), + endLineRel(10000), + startCol(0), + endCol(0), + startLineValid(true), + endLineValid(false), + type(typ), + visible(true), + deleteOpening(false), + deleteEnding(false) +{ +}//the endline fields should be initialised to not valid + +KateCodeFoldingNode::~KateCodeFoldingNode() +{ + // delete all child nodes + clearChildren (); +} + +bool KateCodeFoldingNode::getBegin(KateCodeFoldingTree *tree, KateTextCursor* begin) { + if (!startLineValid) return false; + unsigned int line=startLineRel; + for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode) + line+=n->startLineRel; + + tree->m_buffer->codeFoldingColumnUpdate(line); + begin->setLine(line); + begin->setCol(startCol); + + return true; +} + +bool KateCodeFoldingNode::getEnd(KateCodeFoldingTree *tree, KateTextCursor *end) { + if (!endLineValid) return false; + unsigned int line=startLineRel+endLineRel; + for (KateCodeFoldingNode *n=parentNode;n;n=n->parentNode) + line+=n->startLineRel; + + tree->m_buffer->codeFoldingColumnUpdate(line); + end->setLine(line); + end->setCol(endCol); + + return true; +} + +int KateCodeFoldingNode::cmpPos(KateCodeFoldingTree *tree, uint line,uint col) { + KateTextCursor cur(line,col); + KateTextCursor start,end; + kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (1)"<<endl; + bool startValid=getBegin(tree, &start); + kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (2)"<<endl; + bool endValid=getEnd(tree, &end); + kdDebug(13000)<<"KateCodeFoldingNode::cmpPos (3)"<<endl; + if ((!endValid) && startValid) { + return ((start>cur)?-1:0); + } + if ((!startValid) && endValid) { + return ((cur>end)?1:0); + } + //here both have to be valid, both invalid must not happen + Q_ASSERT(startValid && endValid); + return ( (cur<start)?(-1):( (cur>end) ? 1:0)); +} + +void KateCodeFoldingNode::insertChild (uint index, KateCodeFoldingNode *node) +{ + uint s = m_children.size (); + + if (index > s) + return; + + m_children.resize (++s); + + for (uint i=s-1; i > index; --i) + m_children[i] = m_children[i-1]; + + m_children[index] = node; +} + +KateCodeFoldingNode *KateCodeFoldingNode::takeChild (uint index) +{ + uint s = m_children.size (); + + if (index >= s) + return 0; + + KateCodeFoldingNode *n = m_children[index]; + + for (uint i=index; (i+1) < s; ++i) + m_children[i] = m_children[i+1]; + + m_children.resize (s-1); + + return n; +} + +void KateCodeFoldingNode::clearChildren () +{ + for (uint i=0; i < m_children.size(); ++i) + delete m_children[i]; + + m_children.resize (0); +} + +KateCodeFoldingTree::KateCodeFoldingTree(KateBuffer *buffer): QObject(buffer), m_buffer (buffer) +{ + clear(); +} + +void KateCodeFoldingTree::fixRoot(int endLRel) +{ + m_root.endLineRel = endLRel; +} + +void KateCodeFoldingTree::clear() +{ + m_root.clearChildren(); + + // initialize the root "special" node + m_root.startLineValid=true; + m_root.endLineValid=true; // temporary, should be false; + m_root.endLineRel=1; // temporary; + + hiddenLinesCountCacheValid=false; + lineMapping.setAutoDelete(true); + hiddenLines.clear(); + lineMapping.clear(); + nodesForLine.clear(); + markedForDeleting.clear(); + dontIgnoreUnchangedLines.clear(); +} + +KateCodeFoldingTree::~KateCodeFoldingTree() +{ +} + +bool KateCodeFoldingTree::isTopLevel(unsigned int line) +{ + if (m_root.noChildren()) + return true; // no childs + + // look if a given lines belongs to a sub node + for ( uint i=0; i < m_root.childCount(); ++i ) + { + KateCodeFoldingNode *node = m_root.child(i); + + if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel)) + return false; // the line is within the range of a subnode -> return toplevel=false + } + + return true; // the root node is the only node containing the given line, return toplevel=true +} + +void KateCodeFoldingTree::getLineInfo(KateLineInfo *info, unsigned int line) +{ + // Initialze the returned structure, this will also be returned if the root node has no child nodes + // or the line is not within a childnode's range. + info->topLevel = true; + info->startsVisibleBlock = false; + info->startsInVisibleBlock = false; + info->endsBlock = false; + info->invalidBlockEnd = false; + + if (m_root.noChildren()) + return; + + //let's look for some information + for ( uint i=0; i < m_root.childCount(); ++i ) + { + KateCodeFoldingNode *node = m_root.child(i); + + if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel)) // we found a node, which contains the given line -> do a complete lookup + { + info->topLevel = false; //we are definitly not toplevel + findAllNodesOpenedOrClosedAt(line); //lookup all nodes, which start or and at the given line + + for ( KateCodeFoldingNode *node = nodesForLine.first(); node; node = nodesForLine.next() ) + { + uint startLine = getStartLine(node); + + // type<0 means, that a region has been closed, but not opened + // eg. parantheses missmatch + if (node->type < 0) + info->invalidBlockEnd=true; + else + { + if (startLine != line) // does the region we look at not start at the given line + info->endsBlock = true; // than it has to be an ending + else + { + // The line starts a new region, now determine, if it's a visible or a hidden region + if (node->visible) + info->startsVisibleBlock=true; + else + info->startsInVisibleBlock=true; + } + } + } + + return; + } + } + + return; +} + + +KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLine(unsigned int line) +{ + if (m_root.noChildren()) // does we have child list + nodes ? + return &m_root; + + // lets look, if given line is within a subnode range, and then return the deepest one. + for ( uint i=0; i < m_root.childCount(); ++i ) + { + KateCodeFoldingNode *node = m_root.child(i); + + if ((node->startLineRel<=line) && (line<=node->startLineRel+node->endLineRel)) + { + // a region surounds the line, look in the next deeper hierarchy step + return findNodeForLineDescending(node,line,0); + } + } + + return &m_root; +} + + +KateCodeFoldingNode *KateCodeFoldingTree::findNodeForLineDescending ( KateCodeFoldingNode *node, + unsigned int line, unsigned int offset, bool oneStepOnly ) +{ + if (node->noChildren()) + return node; + + // calculate the offset, between a subnodes real start line and its relative start + offset += node->startLineRel; + + for ( uint i=0; i < node->childCount(); ++i ) + { + KateCodeFoldingNode *subNode = node->child(i); + + if ((subNode->startLineRel+offset<=line) && (line<=subNode->endLineRel+subNode->startLineRel+offset)) //warning fix me for invalid ends + { + // a subnode contains the line. + // if oneStepOnly is true, we don't want to search for the deepest node, just return the found one + + if (oneStepOnly) + return subNode; + else + return findNodeForLineDescending (subNode,line,offset); // look into the next deeper hierarchy step + } + } + + return node; // the current node has no sub nodes, or the line couldn'te be found within a subregion +} + +KateCodeFoldingNode *KateCodeFoldingTree::findNodeForPosition(unsigned int line, unsigned int column) +{ + KateCodeFoldingNode *node=findNodeForLine(line); + + if (node==&m_root) return &m_root; + + kdDebug(13000)<<"initial cmpPos"<<endl; + + KateCodeFoldingNode *tmp; + int leq=node->cmpPos(this, line,column); + while (true) { + switch (leq) { + case 0: { + if (node->noChildren()) + return node; + else + { + tmp=node; + for ( uint i=0; i < node->childCount(); ++i ) + { + KateCodeFoldingNode *subNode = node->child(i); + kdDebug(13000)<<"cmdPos(case0):calling"<<endl; + leq=subNode->cmpPos(this, line,column); + kdDebug(13000)<<"cmdPos(case0):returned"<<endl; + if (leq==0) { + tmp=subNode; + break; + } else if (leq==-1) break; + } + if (tmp!=node) node=tmp; else return node; + } + break; + } + //this could be optimized a littlebit + case -1: + case 1: { + if (!(node->parentNode)) return &m_root; + kdDebug(13000)<<"current node type"<<node->type<<endl; + node=node->parentNode; + kdDebug(13000)<<"cmdPos(case-1/1):calling:"<<node<<endl; + leq=node->cmpPos(this, line,column); + kdDebug(13000)<<"cmdPos(case-1/1):returned"<<endl; + break; + } + } + + } + Q_ASSERT(false); + return &m_root; +} + +void KateCodeFoldingTree::debugDump() +{ + //dump all nodes for debugging + kdDebug(13000)<<"The parsed region/block tree for code folding"<<endl; + dumpNode(&m_root, ""); +} + +void KateCodeFoldingTree::dumpNode(KateCodeFoldingNode *node, const QString &prefix) +{ + //output node properties + kdDebug(13000)<<prefix<<QString("Type: %1, startLineValid %2, startLineRel %3, endLineValid %4, endLineRel %5, visible %6"). + arg(node->type).arg(node->startLineValid).arg(node->startLineRel).arg(node->endLineValid). + arg(node->endLineRel).arg(node->visible)<<endl; + + //output child node properties recursive + if (node->noChildren()) + return; + + QString newprefix(prefix + " "); + for ( uint i=0; i < node->childCount(); ++i ) + dumpNode (node->child(i),newprefix); +} + +/* + That's one of the most important functions ;) +*/ +void KateCodeFoldingTree::updateLine(unsigned int line, + QMemArray<uint> *regionChanges, bool *updated,bool changed,bool colsChanged) +{ + if ( (!changed) || colsChanged) + { + if (dontIgnoreUnchangedLines.isEmpty()) + return; + + if (dontIgnoreUnchangedLines[line]) + dontIgnoreUnchangedLines.remove(line); + else + return; + } + + something_changed = false; + + findAndMarkAllNodesforRemovalOpenedOrClosedAt(line); + + if (regionChanges->isEmpty()) + { + // KateCodeFoldingNode *node=findNodeForLine(line); + // if (node->type!=0) + // if (getStartLine(node)+node->endLineRel==line) removeEnding(node,line); + } + else + { + for (unsigned int i=0;i<regionChanges->size() / 4;i++) + { + signed char tmp=(*regionChanges)[regionChanges->size()-2-i*2]; + uint tmppos=(*regionChanges)[regionChanges->size()-1-i*2]; + (*regionChanges)[regionChanges->size()-2-i*2]=(*regionChanges)[i*2]; + (*regionChanges)[regionChanges->size()-1-i*2]=(*regionChanges)[i*2+1]; + (*regionChanges)[i*2]=tmp; + (*regionChanges)[i*2+1]=tmppos; + } + + + signed char data= (*regionChanges)[regionChanges->size()-2]; + uint charPos=(*regionChanges)[regionChanges->size()-1]; + regionChanges->resize (regionChanges->size()-2); + + int insertPos=-1; + KateCodeFoldingNode *node = findNodeForLine(line); + + if (data<0) + { + // if (insertPos==-1) + { + unsigned int tmpLine=line-getStartLine(node); + + for ( uint i=0; i < node->childCount(); ++i ) + { + if (node->child(i)->startLineRel >= tmpLine) + { + insertPos=i; + break; + } + } + } + } + else + { + for (; (node->parentNode) && (getStartLine(node->parentNode)==line) && (node->parentNode->type!=0); node=node->parentNode); + + if ((getStartLine(node)==line) && (node->type!=0)) + { + insertPos=node->parentNode->findChild(node); + node = node->parentNode; + } + else + { + for ( uint i=0; i < node->childCount(); ++i ) + { + if (getStartLine(node->child(i))>=line) + { + insertPos=i; + break; + } + } + } + } + + do + { + if (data<0) + { + if (correctEndings(data,node,line,charPos,insertPos)) + { + insertPos=node->parentNode->findChild(node)+1; + node=node->parentNode; + } + else + { + if (insertPos!=-1) insertPos++; + } + } + else + { + int startLine=getStartLine(node); + if ((insertPos==-1) || (insertPos>=(int)node->childCount())) + { + KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine); + something_changed = true; + node->appendChild(newNode); + addOpening(newNode, data, regionChanges, line,charPos); + insertPos = node->findChild(newNode)+1; + } + else + { + if (node->child(insertPos)->startLineRel == line-startLine) + { + addOpening(node->child(insertPos), data, regionChanges, line,charPos); + insertPos++; + } + else + { +// kdDebug(13000)<<"ADDING NODE "<<endl; + KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine); + something_changed = true; + node->insertChild(insertPos, newNode); + addOpening(newNode, data, regionChanges, line,charPos); + insertPos++; + } + } + } + + if (regionChanges->isEmpty()) + data = 0; + else + { + data = (*regionChanges)[regionChanges->size()-2]; + charPos=(*regionChanges)[regionChanges->size()-1]; + regionChanges->resize (regionChanges->size()-2); + } + } while (data!=0); + } + + cleanupUnneededNodes(line); +// if (something_changed) emit regionBeginEndAddedRemoved(line); + (*updated) = something_changed; +} + + +bool KateCodeFoldingTree::removeOpening(KateCodeFoldingNode *node,unsigned int line) +{ + signed char type; + if ((type=node->type) == 0) + { + dontDeleteOpening(node); + dontDeleteEnding(node); + return false; + } + + if (!node->visible) + { + toggleRegionVisibility(getStartLine(node)); + } + + KateCodeFoldingNode *parent = node->parentNode; + int mypos = parent->findChild(node); + + if (mypos > -1) + { + //move childnodes() up + for(; node->childCount()>0 ;) + { + KateCodeFoldingNode *tmp; + parent->insertChild(mypos, tmp=node->takeChild(0)); + tmp->parentNode = parent; + tmp->startLineRel += node->startLineRel; + mypos++; + } + + // remove the node + //mypos = parent->findChild(node); + bool endLineValid = node->endLineValid; + int endLineRel = node->endLineRel; + uint endCol=node->endCol; + + // removes + deletes + KateCodeFoldingNode *child = parent->takeChild(mypos); + markedForDeleting.removeRef(child); + delete child; + + if ((type>0) && (endLineValid)) + correctEndings(-type, parent, line+endLineRel/*+1*/,endCol, mypos); // why the hell did I add a +1 here ? + } + + return true; +} + +bool KateCodeFoldingTree::removeEnding(KateCodeFoldingNode *node,unsigned int /* line */) +{ + KateCodeFoldingNode *parent = node->parentNode; + + if (!parent) + return false; + + if (node->type == 0) + return false; + + if (node->type < 0) + { + // removes + deletes + int i = parent->findChild (node); + if (i >= 0) + { + KateCodeFoldingNode *child = parent->takeChild(i); + markedForDeleting.removeRef(child); + delete child; + } + + return true; + } + + int mypos = parent->findChild(node); + int count = parent->childCount(); + + for (int i=mypos+1; i<count; i++) + { + if (parent->child(i)->type == -node->type) + { + node->endLineValid = true; + node->endLineRel = parent->child(i)->startLineRel - node->startLineRel; + + KateCodeFoldingNode *child = parent->takeChild(i); + markedForDeleting.removeRef(child); + delete child; + + count = i-mypos-1; + if (count > 0) + { + for (int i=0; i<count; i++) + { + KateCodeFoldingNode *tmp = parent->takeChild(mypos+1); + tmp->startLineRel -= node->startLineRel; + tmp->parentNode = node; //should help 16.04.2002 + node->appendChild(tmp); + } + } + return false; + } + } + + if ( (parent->type == node->type) || /*temporary fix */ (!parent->parentNode)) + { + for (int i=mypos+1; i<(int)parent->childCount(); i++) + { + KateCodeFoldingNode *tmp = parent->takeChild(mypos+1); + tmp->startLineRel -= node->startLineRel; + tmp->parentNode = node; // SHOULD HELP 16.04.2002 + node->appendChild(tmp); + } + + // this should fix the bug of wrongly closed nodes + if (!parent->parentNode) + node->endLineValid=false; + else + node->endLineValid = parent->endLineValid; + + node->endLineRel = parent->endLineRel-node->startLineRel; + + if (node->endLineValid) + return removeEnding(parent, getStartLine(parent)+parent->endLineRel); + + return false; + } + + node->endLineValid = false; + node->endLineRel = parent->endLineRel - node->startLineRel; + + return false; +} + + +bool KateCodeFoldingTree::correctEndings(signed char data, KateCodeFoldingNode *node,unsigned int line,unsigned int endCol,int insertPos) +{ +// if (node->type==0) {kdError()<<"correct Ending should never be called with the root node"<<endl; return true;} + uint startLine = getStartLine(node); + if (data != -node->type) + { +#if JW_DEBUG + kdDebug(13000)<<"data!=-node->type (correctEndings)"<<endl; +#endif + //invalid close -> add to unopend list + dontDeleteEnding(node); + if (data == node->type) { + node->endCol=endCol; + return false; + } + KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,data,line-startLine); + something_changed = true; + newNode->startLineValid = false; + newNode->endLineValid = true; + newNode->endLineRel = 0; + newNode->endCol=endCol; + + if ((insertPos==-1) || (insertPos==(int)node->childCount())) + node->appendChild(newNode); + else + node->insertChild(insertPos,newNode); + + // find correct position + return false; + } + else + { + something_changed = true; + dontDeleteEnding(node); + + // valid closing region + if (!node->endLineValid) + { + node->endLineValid = true; + node->endLineRel = line - startLine; + node->endCol=endCol; + //moving + + moveSubNodesUp(node); + } + else + { +#if JW_DEBUG + kdDebug(13000)<<"Closing a node which had already a valid end"<<endl; +#endif + // block has already an ending + if (startLine+node->endLineRel == line) + { + node->endCol=endCol; + // we won, just skip +#if JW_DEBUG + kdDebug(13000)<< "We won, just skipping (correctEndings)"<<endl; +#endif + } + else + { + int bakEndLine = node->endLineRel+startLine; + uint bakEndCol = node->endCol; + node->endLineRel = line-startLine; + node->endCol=endCol; + +#if JW_DEBUG + kdDebug(13000)<< "reclosed node had childnodes()"<<endl; + kdDebug(13000)<<"It could be, that childnodes() need to be moved up"<<endl; +#endif + moveSubNodesUp(node); + + if (node->parentNode) + { + correctEndings(data,node->parentNode,bakEndLine, bakEndCol,node->parentNode->findChild(node)+1); // ???? + } + else + { + //add to unopened list (bakEndLine) + } + } + } + } + return true; +} + +void KateCodeFoldingTree::moveSubNodesUp(KateCodeFoldingNode *node) +{ + int mypos = node->parentNode->findChild(node); + int removepos=-1; + int count = node->childCount(); + for (int i=0; i<count; i++) + if (node->child(i)->startLineRel >= node->endLineRel) + { + removepos=i; + break; + } +#if JW_DEBUG + kdDebug(13000)<<QString("remove pos: %1").arg(removepos)<<endl; +#endif + if (removepos>-1) + { +#if JW_DEBUG + kdDebug(13000)<<"Children need to be moved"<<endl; +#endif + KateCodeFoldingNode *moveNode; + if (mypos == (int)node->parentNode->childCount()-1) + { + while (removepos<(int)node->childCount()) + { + node->parentNode->appendChild(moveNode=node->takeChild(removepos)); + moveNode->parentNode = node->parentNode; + moveNode->startLineRel += node->startLineRel; + } + } + else + { + int insertPos=mypos; + while (removepos < (int)node->childCount()) + { + insertPos++; + node->parentNode->insertChild(insertPos, moveNode=node->takeChild(removepos)); + moveNode->parentNode = node->parentNode; // That should solve a crash + moveNode->startLineRel += node->startLineRel; + } + } + } + +} + + + +void KateCodeFoldingTree::addOpening(KateCodeFoldingNode *node,signed char nType, QMemArray<uint>* list,unsigned int line,unsigned int charPos) +{ + uint startLine = getStartLine(node); + if ((startLine==line) && (node->type!=0)) + { +#if JW_DEBUG + kdDebug(13000)<<"startLine equals line"<<endl; +#endif + if (nType == node->type) + { +#if JW_DEBUG + kdDebug(13000)<<"Node exists"<<endl; +#endif + node->deleteOpening = false; + node->startCol=charPos; + KateCodeFoldingNode *parent = node->parentNode; + + if (!node->endLineValid) + { + int current = parent->findChild(node); + int count = parent->childCount()-(current+1); + node->endLineRel = parent->endLineRel - node->startLineRel; + +// EXPERIMENTAL TEST BEGIN +// move this afte the test for unopened, but closed regions within the parent node, or if there are no siblings, bubble up + if (parent) + if (parent->type == node->type) + { + if (parent->endLineValid) + { + removeEnding(parent, line); + node->endLineValid = true; + } + } + +// EXPERIMENTAL TEST BEGIN + + if (current != (int)parent->childCount()-1) + { + //search for an unopened but closed region, even if the parent is of the same type +#ifdef __GNUC__ +#warning "FIXME: why does this seem to work?" +#endif +// if (node->type != parent->type) + { + for (int i=current+1; i<(int)parent->childCount(); i++) + { + if (parent->child(i)->type == -node->type) + { + count = (i-current-1); + node->endLineValid = true; + node->endLineRel = getStartLine(parent->child(i))-line; + node->endCol = parent->child(i)->endCol; + KateCodeFoldingNode *child = parent->takeChild(i); + markedForDeleting.removeRef( child ); + delete child; + break; + } + } + } +// else +// { +// parent->endLineValid = false; +// parent->endLineRel = 20000; +// } + + if (count>0) + { + for (int i=0;i<count;i++) + { + KateCodeFoldingNode *tmp; + node->appendChild(tmp=parent->takeChild(current+1)); + tmp->startLineRel -= node->startLineRel; + tmp->parentNode = node; + } + } + } + + } + + addOpening_further_iterations(node, nType, list, line, 0, startLine,node->startCol); + + } //else ohoh, much work to do same line, but other region type + } + else + { // create a new region + KateCodeFoldingNode *newNode = new KateCodeFoldingNode (node,nType,line-startLine); + something_changed = true; + + int insert_position=-1; + for (int i=0; i<(int)node->childCount(); i++) + { + if (startLine+node->child(i)->startLineRel > line) + { + insert_position=i; + break; + } + } + + int current; + if (insert_position==-1) + { + node->appendChild(newNode); + current = node->childCount()-1; + } + else + { + node->insertChild(insert_position, newNode); + current = insert_position; + } + +// if (node->type==newNode->type) +// { +// newNode->endLineValid=true; +// node->endLineValid=false; +// newNode->endLineRel=node->endLineRel-newNode->startLineRel; +// node->endLineRel=20000; //FIXME + + int count = node->childCount() - (current+1); + newNode->endLineRel -= newNode->startLineRel; + if (current != (int)node->childCount()-1) + { + if (node->type != newNode->type) + { + for (int i=current+1; i<(int)node->childCount(); i++) + { + if (node->child(i)->type == -newNode->type) + { + count = node->childCount() - i - 1; + newNode->endLineValid = true; + newNode->endLineRel = line - getStartLine(node->child(i)); + KateCodeFoldingNode *child = node->takeChild(i); + markedForDeleting.removeRef( child ); + delete child; + break; + } + } + } + else + { + node->endLineValid = false; + node->endLineRel = 10000; + } + if (count > 0) + { + for (int i=0;i<count;i++) + { + KateCodeFoldingNode *tmp; + newNode->appendChild(tmp=node->takeChild(current+1)); + tmp->parentNode=newNode; + } + } +// } + } + + addOpening(newNode, nType, list, line,charPos); + + addOpening_further_iterations(node, node->type, list, line, current, startLine,node->startCol); + } +} + + +void KateCodeFoldingTree::addOpening_further_iterations(KateCodeFoldingNode *node,signed char /* nType */, QMemArray<uint>* + list,unsigned int line,int current, unsigned int startLine,unsigned int charPos) +{ + while (!(list->isEmpty())) + { + if (list->isEmpty()) + return; + else + { + signed char data = (*list)[list->size()-2]; + uint charPos=(*list)[list->size()-1]; + list->resize (list->size()-2); + + if (data<0) + { +#if JW_DEBUG + kdDebug(13000)<<"An ending was found"<<endl; +#endif + + if (correctEndings(data,node,line,charPos,-1)) + return; // -1 ? + +#if 0 + if(data == -nType) + { + if (node->endLineValid) + { + if (node->endLineRel+startLine==line) // We've won again + { + //handle next node; + } + else + { // much moving + node->endLineRel=line-startLine; + node->endLineValid=true; + } + return; // next higher level should do the rest + } + else + { + node->endLineRel=line-startLine; + node->endLineValid=true; + //much moving + } + } //else add to unopened list +#endif + } + else + { + bool needNew = true; + if (current < (int)node->childCount()) + { + if (getStartLine(node->child(current)) == line) + needNew=false; + } + if (needNew) + { + something_changed = true; + KateCodeFoldingNode *newNode = new KateCodeFoldingNode(node, data, line-startLine); + node->insertChild(current, newNode); //find the correct position later + } + + addOpening(node->child(current), data, list, line,charPos); + current++; + //lookup node or create subnode + } + } + } // end while +} + +unsigned int KateCodeFoldingTree::getStartLine(KateCodeFoldingNode *node) +{ + unsigned int lineStart=0; + for (KateCodeFoldingNode *iter=node; iter->type != 0; iter=iter->parentNode) + lineStart += iter->startLineRel; + + return lineStart; +} + + +void KateCodeFoldingTree::lineHasBeenRemoved(unsigned int line) +{ + lineMapping.clear(); + dontIgnoreUnchangedLines.insert(line, &trueVal); + dontIgnoreUnchangedLines.insert(line-1, &trueVal); + dontIgnoreUnchangedLines.insert(line+1, &trueVal); + hiddenLinesCountCacheValid = false; +#if JW_DEBUG + kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenRemoved: %1").arg(line)<<endl; +#endif + +//line ++; + findAndMarkAllNodesforRemovalOpenedOrClosedAt(line); //It's an ugly solution + cleanupUnneededNodes(line); //It's an ugly solution + + KateCodeFoldingNode *node = findNodeForLine(line); +//????? if (node->endLineValid) + { + int startLine = getStartLine(node); + if (startLine == (int)line) + node->startLineRel--; + else + { + if (node->endLineRel == 0) + node->endLineValid = false; + node->endLineRel--; + } + + int count = node->childCount(); + for (int i=0; i<count; i++) + { + if (node->child(i)->startLineRel+startLine >= line) + node->child(i)->startLineRel--; + } + } + + if (node->parentNode) + decrementBy1(node->parentNode, node); + + for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it) + { + if ((*it).start > line) + (*it).start--; + else if ((*it).start+(*it).length > line) + (*it).length--; + } +} + + +void KateCodeFoldingTree::decrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after) +{ + if (node->endLineRel == 0) + node->endLineValid = false; + node->endLineRel--; + + for (uint i=node->findChild(after)+1; i < node->childCount(); ++i) + node->child(i)->startLineRel--; + + if (node->parentNode) + decrementBy1(node->parentNode,node); +} + + +void KateCodeFoldingTree::lineHasBeenInserted(unsigned int line) +{ + lineMapping.clear(); + dontIgnoreUnchangedLines.insert(line, &trueVal); + dontIgnoreUnchangedLines.insert(line-1, &trueVal); + dontIgnoreUnchangedLines.insert(line+1, &trueVal); + hiddenLinesCountCacheValid = false; +//return; +#if JW_DEBUG + kdDebug(13000)<<QString("KateCodeFoldingTree::lineHasBeenInserted: %1").arg(line)<<endl; +#endif + +// findAndMarkAllNodesforRemovalOpenedOrClosedAt(line); +// cleanupUnneededNodes(line); + + KateCodeFoldingNode *node = findNodeForLine(line); +// ???????? if (node->endLineValid) + { + int startLine=getStartLine(node); + if (node->type < 0) + node->startLineRel++; + else + node->endLineRel++; + + for (uint i=0; i < node->childCount(); ++i) + { + KateCodeFoldingNode *iter = node->child(i); + + if (iter->startLineRel+startLine >= line) + iter->startLineRel++; + } + } + + if (node->parentNode) + incrementBy1(node->parentNode, node); + + for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it) + { + if ((*it).start > line) + (*it).start++; + else if ((*it).start+(*it).length > line) + (*it).length++; + } +} + +void KateCodeFoldingTree::incrementBy1(KateCodeFoldingNode *node, KateCodeFoldingNode *after) +{ + node->endLineRel++; + + for (uint i=node->findChild(after)+1; i < node->childCount(); ++i) + node->child(i)->startLineRel++; + + if (node->parentNode) + incrementBy1(node->parentNode,node); +} + + +void KateCodeFoldingTree::findAndMarkAllNodesforRemovalOpenedOrClosedAt(unsigned int line) +{ +#ifdef __GNUC__ +#warning "FIXME: make this multiple region changes per line save"; +#endif +// return; + markedForDeleting.clear(); + KateCodeFoldingNode *node = findNodeForLine(line); + if (node->type == 0) + return; + + addNodeToRemoveList(node, line); + + while (((node->parentNode) && (node->parentNode->type!=0)) && (getStartLine(node->parentNode)==line)) + { + node = node->parentNode; + addNodeToRemoveList(node, line); + } +#if JW_DEBUG + kdDebug(13000)<<" added line to markedForDeleting list"<<endl; +#endif +} + + +void KateCodeFoldingTree::addNodeToRemoveList(KateCodeFoldingNode *node,unsigned int line) +{ + bool add=false; +#ifdef __GNUC__ +#warning "FIXME: make this multiple region changes per line save"; +#endif + unsigned int startLine=getStartLine(node); + if ((startLine==line) && (node->startLineValid)) + { + add=true; + node->deleteOpening = true; + } + if ((startLine+node->endLineRel==line) || ((node->endLineValid==false) && (node->deleteOpening))) + { + int myPos=node->parentNode->findChild(node); // this has to be implemented nicely + if ((int)node->parentNode->childCount()>myPos+1) + addNodeToRemoveList(node->parentNode->child(myPos+1),line); + add=true; + node->deleteEnding = true; + } + + if(add) + markedForDeleting.append(node); + +} + + +void KateCodeFoldingTree::findAllNodesOpenedOrClosedAt(unsigned int line) +{ + nodesForLine.clear(); + KateCodeFoldingNode *node = findNodeForLine(line); + if (node->type == 0) + return; + + unsigned int startLine = getStartLine(node); + if (startLine == line) + nodesForLine.append(node); + else if ((startLine+node->endLineRel == line)) + nodesForLine.append(node); + + while (node->parentNode) + { + addNodeToFoundList(node->parentNode, line, node->parentNode->findChild(node)); + node = node->parentNode; + } +#if JW_DEBUG + kdDebug(13000)<<" added line to nodesForLine list"<<endl; +#endif +} + + +void KateCodeFoldingTree::addNodeToFoundList(KateCodeFoldingNode *node,unsigned int line,int childpos) +{ + unsigned int startLine = getStartLine(node); + + if ((startLine==line) && (node->type!=0)) + nodesForLine.append(node); + else if ((startLine+node->endLineRel==line) && (node->type!=0)) + nodesForLine.append(node); + + for (int i=childpos+1; i<(int)node->childCount(); i++) + { + KateCodeFoldingNode *child = node->child(i); + + if (startLine+child->startLineRel == line) + { + nodesForLine.append(child); + addNodeToFoundList(child, line, 0); + } + else + break; + } +} + + +void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line) +{ +#if JW_DEBUG + kdDebug(13000)<<"void KateCodeFoldingTree::cleanupUnneededNodes(unsigned int line)"<<endl; +#endif + +// return; + if (markedForDeleting.isEmpty()) + return; + + for (int i=0; i<(int)markedForDeleting.count(); i++) + { + KateCodeFoldingNode *node = markedForDeleting.at(i); + if (node->deleteOpening) + kdDebug(13000)<<"DELETE OPENING SET"<<endl; + if (node->deleteEnding) + kdDebug(13000)<<"DELETE ENDING SET"<<endl; + + if ((node->deleteOpening) && (node->deleteEnding)) + { +#if JW_DEBUG + kdDebug(13000)<<"Deleting complete node"<<endl; +#endif + if (node->endLineValid) // just delete it, it has been opened and closed on this line + { + int f = node->parentNode->findChild (node); + + if (f >= 0) + delete node->parentNode->takeChild(f); + } + else + { + removeOpening(node, line); + // the node has subnodes which need to be moved up and this one has to be deleted + } + something_changed = true; + } + else + { + if ((node->deleteOpening) && (node->startLineValid)) + { +#if JW_DEBUG + kdDebug(13000)<<"calling removeOpening"<<endl; +#endif + removeOpening(node, line); + something_changed = true; + } + else + { + dontDeleteOpening(node); + + if ((node->deleteEnding) && (node->endLineValid)) + { + dontDeleteEnding(node); + removeEnding(node, line); + something_changed = true; + } + else + dontDeleteEnding(node); + } + } + } +} + +void KateCodeFoldingTree::dontDeleteEnding(KateCodeFoldingNode* node) +{ + node->deleteEnding = false; +} + + +void KateCodeFoldingTree::dontDeleteOpening(KateCodeFoldingNode* node) +{ + node->deleteOpening = false; +} + + +void KateCodeFoldingTree::toggleRegionVisibility(unsigned int line) +{ + // hl whole file + m_buffer->line (m_buffer->count()-1); + + lineMapping.clear(); + hiddenLinesCountCacheValid = false; + kdDebug(13000)<<QString("KateCodeFoldingTree::toggleRegionVisibility() %1").arg(line)<<endl; + + findAllNodesOpenedOrClosedAt(line); + for (int i=0; i<(int)nodesForLine.count(); i++) + { + KateCodeFoldingNode *node=nodesForLine.at(i); + if ( (!node->startLineValid) || (getStartLine(node) != line) ) + { + nodesForLine.remove(i); + i--; + } + } + + if (nodesForLine.isEmpty()) + return; + + nodesForLine.at(0)->visible = !nodesForLine.at(0)->visible; + + if (!nodesForLine.at(0)->visible) + addHiddenLineBlock(nodesForLine.at(0),line); + else + { + for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end();++it) + if ((*it).start == line+1) + { + hiddenLines.remove(it); + break; + } + + updateHiddenSubNodes(nodesForLine.at(0)); + } + + emit regionVisibilityChangedAt(line); +} + +void KateCodeFoldingTree::updateHiddenSubNodes(KateCodeFoldingNode *node) +{ + for (uint i=0; i < node->childCount(); ++i) + { + KateCodeFoldingNode *iter = node->child(i); + + if (!iter->visible) + addHiddenLineBlock(iter, getStartLine(iter)); + else + updateHiddenSubNodes(iter); + } +} + +void KateCodeFoldingTree::addHiddenLineBlock(KateCodeFoldingNode *node,unsigned int line) +{ + KateHiddenLineBlock data; + data.start = line+1; + data.length = node->endLineRel-(existsOpeningAtLineAfter(line+node->endLineRel,node)?1:0); // without -1; + bool inserted = false; + + for (QValueList<KateHiddenLineBlock>::Iterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it) + { + if (((*it).start>=data.start) && ((*it).start<=data.start+data.length-1)) // another hidden block starting at the within this block already exits -> adapt new block + { + // the existing block can't have lines behind the new one, because a newly hidden + // block has to encapsulate already hidden ones + it=hiddenLines.remove(it); + --it; + } + else + { + if ((*it).start > line) + { + hiddenLines.insert(it, data); + inserted = true; + + break; + } + } + } + + if (!inserted) + hiddenLines.append(data); +} + +bool KateCodeFoldingTree::existsOpeningAtLineAfter(unsigned int line, KateCodeFoldingNode *node) +{ + for(KateCodeFoldingNode *tmp = node->parentNode; tmp; tmp=tmp->parentNode) + { + KateCodeFoldingNode *tmp2; + unsigned int startLine=getStartLine(tmp); + + if ((tmp2 = tmp->child(tmp->findChild(node) + 1)) + && ((tmp2->startLineRel + startLine) == line)) + return true; + + if ((startLine + tmp->endLineRel) > line) + return false; + } + + return false; +} + + +// +// get the real line number for a virtual line +// +unsigned int KateCodeFoldingTree::getRealLine(unsigned int virtualLine) +{ + // he, if nothing is hidden, why look at it ;) + if (hiddenLines.isEmpty()) + return virtualLine; + + // kdDebug(13000)<<QString("VirtualLine %1").arg(virtualLine)<<endl; + + unsigned int *real=lineMapping[virtualLine]; + if (real) + return (*real); + + unsigned int tmp = virtualLine; + for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it) + { + if ((*it).start<=virtualLine) + virtualLine += (*it).length; + else + break; + } + + // kdDebug(13000)<<QString("Real Line %1").arg(virtualLine)<<endl; + + lineMapping.insert(tmp, new unsigned int(virtualLine)); + return virtualLine; +} + +// +// get the virtual line number for a real line +// +unsigned int KateCodeFoldingTree::getVirtualLine(unsigned int realLine) +{ + // he, if nothing is hidden, why look at it ;) + if (hiddenLines.isEmpty()) + return realLine; + + // kdDebug(13000)<<QString("RealLine--> %1").arg(realLine)<<endl; + + for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.fromLast(); it!=hiddenLines.end(); --it) + { + if ((*it).start <= realLine) + realLine -= (*it).length; + // else + // break; + } + + // kdDebug(13000)<<QString("-->virtual Line %1").arg(realLine)<<endl; + + return realLine; +} + +// +// get the number of hidden lines +// +unsigned int KateCodeFoldingTree::getHiddenLinesCount(unsigned int doclen) +{ + // he, if nothing is hidden, why look at it ;) + if (hiddenLines.isEmpty()) + return 0; + + if (hiddenLinesCountCacheValid) + return hiddenLinesCountCache; + + hiddenLinesCountCacheValid = true; + hiddenLinesCountCache = 0; + + for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin(); it!=hiddenLines.end(); ++it) + { + if ((*it).start+(*it).length<=doclen) + hiddenLinesCountCache += (*it).length; + else + { + hiddenLinesCountCache += ((*it).length- ((*it).length + (*it).start - doclen)); + break; + } + } + + return hiddenLinesCountCache; +} + +void KateCodeFoldingTree::collapseToplevelNodes() +{ + // hl whole file + m_buffer->line (m_buffer->count()-1); + + if (m_root.noChildren ()) + return; + + for ( uint i=0; i < m_root.childCount(); ++i ) + { + KateCodeFoldingNode *node = m_root.child(i); + + if (node->visible && node->startLineValid && node->endLineValid) + { + node->visible=false; + lineMapping.clear(); + hiddenLinesCountCacheValid = false; + addHiddenLineBlock(node,node->startLineRel); + emit regionVisibilityChangedAt(node->startLineRel); + } + } +} + +void KateCodeFoldingTree::expandToplevelNodes(int numLines) +{ + // hl whole file + m_buffer->line (m_buffer->count()-1); + + KateLineInfo line; + for (int i = 0; i < numLines; i++) { + getLineInfo(&line, i); + + if (line.startsInVisibleBlock) + toggleRegionVisibility(i); + } +} + +int KateCodeFoldingTree::collapseOne(int realLine) +{ + // hl whole file + m_buffer->line (m_buffer->count()-1); + + KateLineInfo line; + int unrelatedBlocks = 0; + for (int i = realLine; i >= 0; i--) { + getLineInfo(&line, i); + + if (line.topLevel && !line.endsBlock) + // optimisation + break; + + if (line.endsBlock && ( line.invalidBlockEnd ) && (i != realLine)) { + unrelatedBlocks++; + } + + if (line.startsVisibleBlock) { + unrelatedBlocks--; + if (unrelatedBlocks == -1) { + toggleRegionVisibility(i); + return i; + } + } + } + return -1; +} + +void KateCodeFoldingTree::expandOne(int realLine, int numLines) +{ + // hl whole file + m_buffer->line (m_buffer->count()-1); + + KateLineInfo line; + int blockTrack = 0; + for (int i = realLine; i >= 0; i--) { + getLineInfo(&line, i); + + if (line.topLevel) + // done + break; + + if (line.startsInVisibleBlock && i != realLine) { + if (blockTrack == 0) + toggleRegionVisibility(i); + + blockTrack--; + } + + if (line.endsBlock) + blockTrack++; + + if (blockTrack < 0) + // too shallow + break; + } + + blockTrack = 0; + for (int i = realLine; i < numLines; i++) { + getLineInfo(&line, i); + + if (line.topLevel) + // done + break; + + if (line.startsInVisibleBlock) { + if (blockTrack == 0) + toggleRegionVisibility(i); + + blockTrack++; + } + + if (line.endsBlock) + blockTrack--; + + if (blockTrack < 0) + // too shallow + break; + } +} + +void KateCodeFoldingTree::ensureVisible( uint line ) +{ + // first have a look, if the line is really hidden + bool found=false; + for (QValueList<KateHiddenLineBlock>::ConstIterator it=hiddenLines.begin();it!=hiddenLines.end();++it) + { + if ( ((*it).start<=line) && ((*it).start+(*it).length>line) ) + { + found=true; + break; + } + } + + + if (!found) return; + + kdDebug(13000)<<"line "<<line<<" is really hidden ->show block"<<endl; + + // it looks like we really have to ensure visibility + KateCodeFoldingNode *n = findNodeForLine( line ); + do { + if ( ! n->visible ) + toggleRegionVisibility( getStartLine( n ) ); + n = n->parentNode; + } while( n ); + +} + +// kate: space-indent on; indent-width 2; replace-tabs on; |