/* * RangeMap.java * :tabSize=4:indentSize=4:noTabs=false: * :folding=explicit:collapseFolds=1: * * Copyright (C) 2001, 2005 Slava Pestov * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package org.gjt.sp.jedit.textarea; import org.gjt.sp.jedit.Debug; import org.gjt.sp.util.Log; /** * The fold visibility map. * * All lines from fvm[2*n] to fvm[2*n+1]-1 inclusive are visible. * All lines from position fvm[2*n+1] to fvm[2*n+2]-1 inclusive are * invisible. * * Examples: * --------- * All lines visible: { 0, buffer.getLineCount() } * Narrow from a to b: { a, b + 1 } * Collapsed fold from a to b: { 0, a + 1, b, buffer.getLineCount() } * * Note: length is always even. */ class RangeMap { //{{{ RangeMap constructor RangeMap() { fvm = new int[2]; lastfvmget = -1; } //}}} //{{{ RangeMap constructor RangeMap(RangeMap copy) { this.fvm = copy.fvm.clone(); this.fvmcount = copy.fvmcount; } //}}} //{{{ reset() method void reset(int lines) { lastfvmget = -1; fvmcount = 2; fvm[0] = 0; fvm[1] = lines; } //}}} //{{{ first() method int first() { return fvm[0]; } //}}} //{{{ last() method int last() { return fvm[fvmcount - 1] - 1; } //}}} //{{{ lookup() method int lookup(int index) { return fvm[index]; } //}}} //{{{ search() method /** * Returns the fold visibility map index for the given line. */ int search(int line) { if(line < fvm[0]) return -1; if(line >= fvm[fvmcount - 1]) return fvmcount - 1; if(lastfvmget != -1) { if(line >= fvm[lastfvmget]) { if(lastfvmget == fvmcount - 1 || line < fvm[lastfvmget + 1]) { return lastfvmget; } } } int start = 0; int end = fvmcount - 1; loop: for(;;) { switch(end - start) { case 0: lastfvmget = start; break loop; case 1: int value = fvm[end]; if(value <= line) lastfvmget = end; else lastfvmget = start; break loop; default: int pivot = (end + start) / 2; value = fvm[pivot]; if(value == line) { lastfvmget = pivot; break loop; } else if(value < line) start = pivot; else end = pivot - 1; break; } } return lastfvmget; } //}}} //{{{ put() method /** * Replaces from <code>start</code> to <code>end-1</code> inclusive with * <code>put</code>. Update <code>fvmcount</code>. */ void put(int start, int end, int[] put) { if(Debug.FOLD_VIS_DEBUG) { StringBuilder buf = new StringBuilder(50); buf.append("fvmput(").append(start).append(','); buf.append(end).append(','); buf.append('{'); if(put != null) { for(int i = 0; i < put.length; i++) { if(i != 0) buf.append(','); buf.append(put[i]); } } buf.append("})"); Log.log(Log.DEBUG,this,buf.toString()); } int putl = put == null ? 0 : put.length; int delta = putl - (end - start); if(fvmcount + delta > fvm.length) { int[] newfvm = new int[(fvm.length << 1) + 1]; System.arraycopy(fvm,0,newfvm,0,fvmcount); fvm = newfvm; } if(delta != 0) { System.arraycopy(fvm,end,fvm,start + putl, fvmcount - end); } if(putl != 0) { System.arraycopy(put,0,fvm,start,put.length); } fvmcount += delta; dump(); if(fvmcount == 0) throw new InternalError(); } //}}} //{{{ put2() method /** * Merge previous and next entry if necessary. */ void put2(int starti, int endi, int start, int end) { if(Debug.FOLD_VIS_DEBUG) { Log.log(Log.DEBUG,this,"*fvmput2(" + starti + "," + endi + "," + start + "," + end + ")"); } if(starti != -1 && fvm[starti] == start) { if(endi <= fvmcount - 2 && fvm[endi + 1] == end + 1) { put(starti,endi + 2,null); } else { put(starti,endi + 1, new int[] { end + 1 }); } } else { if(endi != fvmcount - 1 && fvm[endi + 1] == end + 1) { put(starti + 1,endi + 2, new int[] { start }); } else { put(starti + 1,endi + 1, new int[] { start, end + 1 }); } } } //}}} //{{{ next() method int next(int line) { int index = search(line); /* in collapsed range */ if(index % 2 != 0) { /* beyond last visible line */ if(fvmcount == index + 1) return - 1; /* start of next expanded range */ else return fvm[index + 1]; } /* last in expanded range */ else if(line == fvm[index + 1] - 1) { /* equal to last visible line */ if(fvmcount == index + 2) return -1; /* start of next expanded range */ else return fvm[index + 2]; } /* next in expanded range */ else return line + 1; } //}}} //{{{ prev() method int prev(int line) { int index = search(line); /* before first visible line */ if(index == -1) return -1; /* in collapsed range */ else if(index % 2 == 1) { /* end of prev expanded range */ return fvm[index] - 1; } /* first in expanded range */ else if(line == fvm[index]) { /* equal to first visible line */ if(index == 0) return -1; /* end of prev expanded range */ else return fvm[index - 1] - 1; } /* prev in expanded range */ else return line - 1; } //}}} //{{{ show() method void show(int start, int end) { int starti = search(start); int endi = search(end); if(starti % 2 == 0) { if(endi % 2 == 0) put(starti + 1,endi + 1,null); else { if(endi != fvmcount - 1 && fvm[endi + 1] == end + 1) put(starti + 1,endi + 2,null); else { put(starti + 1,endi,null); fvm[starti + 1] = end + 1; } } } else { if(endi % 2 == 0) { if(starti != -1 && fvm[starti] == start) put(starti,endi + 1,null); else { put(starti + 1,endi,null); fvm[starti + 1] = start; } } else put2(starti,endi,start,end); } lastfvmget = -1; } //}}} //{{{ hide() method void hide(int start, int end) { int starti = search(start); int endi = search(end); if(starti % 2 == 0) { if(endi % 2 == 0) put2(starti,endi,start,end); else { if(start == fvm[0]) put(starti,endi + 1,null); else { put(starti + 1,endi,null); fvm[starti + 1] = start; } } } else { if(endi % 2 == 0) { if(end + 1 == fvm[fvmcount - 1]) put(starti + 1,endi + 2,null); else { put(starti + 1,endi,null); fvm[starti + 1] = end + 1; } } else put(starti + 1,endi + 1,null); } lastfvmget = -1; } //}}} //{{{ count() method int count() { return fvmcount; } //}}} //{{{ dump() method void dump() { if(Debug.FOLD_VIS_DEBUG) { StringBuilder buf = new StringBuilder("{"); for(int i = 0; i < fvmcount; i++) { if(i != 0) buf.append(','); buf.append(fvm[i]); } buf.append('}'); Log.log(Log.DEBUG,this,"fvm = " + buf); } } //}}} //{{{ contentInserted() method void contentInserted(int startLine, int numLines) { if(numLines != 0) { int index = search(startLine); int start = index + 1; for(int i = start; i < fvmcount; i++) fvm[i] += numLines; lastfvmget = -1; dump(); } } //}}} //{{{ preContentRemoved() method /** * @return If the anchors should be reset. */ boolean preContentRemoved(int startLine, int numLines) { boolean returnValue = false; int endLine = startLine + numLines; /* update fold visibility map. */ int starti = search(startLine); int endi = search(endLine); /* both have same visibility; just remove * anything in between. */ if(Math.abs(starti % 2) == Math.abs(endi % 2)) { if(endi - starti == fvmcount) { // we're removing from before // the first visible to after // the last visible returnValue = true; starti = 1; } else { put(starti + 1,endi + 1,null); starti++; } } /* collapse 2 */ else if(starti != -1 && fvm[starti] == startLine) { if(endi - starti == fvmcount - 1) { // we're removing from // the first visible to after // the last visible returnValue = true; starti = 1; } else put(starti,endi + 1,null); } /* shift */ else { put(starti + 1,endi,null); fvm[starti + 1] = startLine; starti += 2; } /* update */ for(int i = starti; i < fvmcount; i++) fvm[i] -= numLines; lastfvmget = -1; dump(); return returnValue; } //}}} //{{{ Private members private int[] fvm; private int fvmcount; private int lastfvmget; //}}} }