You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1663 lines
43 KiB
1663 lines
43 KiB
/* 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;
|