001: /*
002: * RangeMap.java
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2001, 2005 Slava Pestov
007: *
008: * This program is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU General Public License
010: * as published by the Free Software Foundation; either version 2
011: * of the License, or any later version.
012: *
013: * This program is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016: * GNU General Public License for more details.
017: *
018: * You should have received a copy of the GNU General Public License
019: * along with this program; if not, write to the Free Software
020: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
021: */
022:
023: package org.gjt.sp.jedit.textarea;
024:
025: import org.gjt.sp.jedit.Debug;
026: import org.gjt.sp.util.Log;
027:
028: /**
029: * The fold visibility map.
030: *
031: * All lines from fvm[2*n] to fvm[2*n+1]-1 inclusive are visible.
032: * All lines from position fvm[2*n+1] to fvm[2*n+2]-1 inclusive are
033: * invisible.
034: *
035: * Examples:
036: * ---------
037: * All lines visible: { 0, buffer.getLineCount() }
038: * Narrow from a to b: { a, b + 1 }
039: * Collapsed fold from a to b: { 0, a + 1, b, buffer.getLineCount() }
040: *
041: * Note: length is always even.
042: */
043: class RangeMap {
044: //{{{ RangeMap constructor
045: RangeMap() {
046: fvm = new int[2];
047: lastfvmget = -1;
048: } //}}}
049:
050: //{{{ RangeMap constructor
051: RangeMap(RangeMap copy) {
052: this .fvm = copy.fvm.clone();
053: this .fvmcount = copy.fvmcount;
054: } //}}}
055:
056: //{{{ reset() method
057: void reset(int lines) {
058: lastfvmget = -1;
059: fvmcount = 2;
060: fvm[0] = 0;
061: fvm[1] = lines;
062: } //}}}
063:
064: //{{{ first() method
065: int first() {
066: return fvm[0];
067: } //}}}
068:
069: //{{{ last() method
070: int last() {
071: return fvm[fvmcount - 1] - 1;
072: } //}}}
073:
074: //{{{ lookup() method
075: int lookup(int index) {
076: return fvm[index];
077: } //}}}
078:
079: //{{{ search() method
080: /**
081: * Returns the fold visibility map index for the given line.
082: */
083: int search(int line) {
084: if (line < fvm[0])
085: return -1;
086: if (line >= fvm[fvmcount - 1])
087: return fvmcount - 1;
088:
089: if (lastfvmget != -1) {
090: if (line >= fvm[lastfvmget]) {
091: if (lastfvmget == fvmcount - 1
092: || line < fvm[lastfvmget + 1]) {
093: return lastfvmget;
094: }
095: }
096: }
097:
098: int start = 0;
099: int end = fvmcount - 1;
100:
101: loop: for (;;) {
102: switch (end - start) {
103: case 0:
104: lastfvmget = start;
105: break loop;
106: case 1:
107: int value = fvm[end];
108: if (value <= line)
109: lastfvmget = end;
110: else
111: lastfvmget = start;
112: break loop;
113: default:
114: int pivot = (end + start) / 2;
115: value = fvm[pivot];
116: if (value == line) {
117: lastfvmget = pivot;
118: break loop;
119: } else if (value < line)
120: start = pivot;
121: else
122: end = pivot - 1;
123: break;
124: }
125: }
126:
127: return lastfvmget;
128: } //}}}
129:
130: //{{{ put() method
131: /**
132: * Replaces from <code>start</code> to <code>end-1</code> inclusive with
133: * <code>put</code>. Update <code>fvmcount</code>.
134: */
135: void put(int start, int end, int[] put) {
136: if (Debug.FOLD_VIS_DEBUG) {
137: StringBuilder buf = new StringBuilder(50);
138: buf.append("fvmput(").append(start).append(',');
139: buf.append(end).append(',');
140: buf.append('{');
141: if (put != null) {
142: for (int i = 0; i < put.length; i++) {
143: if (i != 0)
144: buf.append(',');
145: buf.append(put[i]);
146: }
147: }
148: buf.append("})");
149: Log.log(Log.DEBUG, this , buf.toString());
150: }
151: int putl = put == null ? 0 : put.length;
152:
153: int delta = putl - (end - start);
154: if (fvmcount + delta > fvm.length) {
155: int[] newfvm = new int[(fvm.length << 1) + 1];
156: System.arraycopy(fvm, 0, newfvm, 0, fvmcount);
157: fvm = newfvm;
158: }
159:
160: if (delta != 0) {
161: System.arraycopy(fvm, end, fvm, start + putl, fvmcount
162: - end);
163: }
164:
165: if (putl != 0) {
166: System.arraycopy(put, 0, fvm, start, put.length);
167: }
168:
169: fvmcount += delta;
170:
171: dump();
172:
173: if (fvmcount == 0)
174: throw new InternalError();
175: } //}}}
176:
177: //{{{ put2() method
178: /**
179: * Merge previous and next entry if necessary.
180: */
181: void put2(int starti, int endi, int start, int end) {
182: if (Debug.FOLD_VIS_DEBUG) {
183: Log.log(Log.DEBUG, this , "*fvmput2(" + starti + "," + endi
184: + "," + start + "," + end + ")");
185: }
186: if (starti != -1 && fvm[starti] == start) {
187: if (endi <= fvmcount - 2 && fvm[endi + 1] == end + 1) {
188: put(starti, endi + 2, null);
189: } else {
190: put(starti, endi + 1, new int[] { end + 1 });
191: }
192: } else {
193: if (endi != fvmcount - 1 && fvm[endi + 1] == end + 1) {
194: put(starti + 1, endi + 2, new int[] { start });
195: } else {
196: put(starti + 1, endi + 1, new int[] { start, end + 1 });
197: }
198: }
199: } //}}}
200:
201: //{{{ next() method
202: int next(int line) {
203: int index = search(line);
204: /* in collapsed range */
205: if (index % 2 != 0) {
206: /* beyond last visible line */
207: if (fvmcount == index + 1)
208: return -1;
209: /* start of next expanded range */
210: else
211: return fvm[index + 1];
212: }
213: /* last in expanded range */
214: else if (line == fvm[index + 1] - 1) {
215: /* equal to last visible line */
216: if (fvmcount == index + 2)
217: return -1;
218: /* start of next expanded range */
219: else
220: return fvm[index + 2];
221: }
222: /* next in expanded range */
223: else
224: return line + 1;
225: } //}}}
226:
227: //{{{ prev() method
228: int prev(int line) {
229: int index = search(line);
230: /* before first visible line */
231: if (index == -1)
232: return -1;
233: /* in collapsed range */
234: else if (index % 2 == 1) {
235: /* end of prev expanded range */
236: return fvm[index] - 1;
237: }
238: /* first in expanded range */
239: else if (line == fvm[index]) {
240: /* equal to first visible line */
241: if (index == 0)
242: return -1;
243: /* end of prev expanded range */
244: else
245: return fvm[index - 1] - 1;
246: }
247: /* prev in expanded range */
248: else
249: return line - 1;
250: } //}}}
251:
252: //{{{ show() method
253: void show(int start, int end) {
254: int starti = search(start);
255: int endi = search(end);
256:
257: if (starti % 2 == 0) {
258: if (endi % 2 == 0)
259: put(starti + 1, endi + 1, null);
260: else {
261: if (endi != fvmcount - 1 && fvm[endi + 1] == end + 1)
262: put(starti + 1, endi + 2, null);
263: else {
264: put(starti + 1, endi, null);
265: fvm[starti + 1] = end + 1;
266: }
267: }
268: } else {
269: if (endi % 2 == 0) {
270: if (starti != -1 && fvm[starti] == start)
271: put(starti, endi + 1, null);
272: else {
273: put(starti + 1, endi, null);
274: fvm[starti + 1] = start;
275: }
276: } else
277: put2(starti, endi, start, end);
278: }
279:
280: lastfvmget = -1;
281: } //}}}
282:
283: //{{{ hide() method
284: void hide(int start, int end) {
285: int starti = search(start);
286: int endi = search(end);
287:
288: if (starti % 2 == 0) {
289: if (endi % 2 == 0)
290: put2(starti, endi, start, end);
291: else {
292: if (start == fvm[0])
293: put(starti, endi + 1, null);
294: else {
295: put(starti + 1, endi, null);
296: fvm[starti + 1] = start;
297: }
298: }
299: } else {
300: if (endi % 2 == 0) {
301: if (end + 1 == fvm[fvmcount - 1])
302: put(starti + 1, endi + 2, null);
303: else {
304: put(starti + 1, endi, null);
305: fvm[starti + 1] = end + 1;
306: }
307: } else
308: put(starti + 1, endi + 1, null);
309: }
310:
311: lastfvmget = -1;
312: } //}}}
313:
314: //{{{ count() method
315: int count() {
316: return fvmcount;
317: } //}}}
318:
319: //{{{ dump() method
320: void dump() {
321: if (Debug.FOLD_VIS_DEBUG) {
322: StringBuilder buf = new StringBuilder("{");
323: for (int i = 0; i < fvmcount; i++) {
324: if (i != 0)
325: buf.append(',');
326: buf.append(fvm[i]);
327: }
328: buf.append('}');
329: Log.log(Log.DEBUG, this , "fvm = " + buf);
330: }
331: } //}}}
332:
333: //{{{ contentInserted() method
334: void contentInserted(int startLine, int numLines) {
335: if (numLines != 0) {
336: int index = search(startLine);
337: int start = index + 1;
338:
339: for (int i = start; i < fvmcount; i++)
340: fvm[i] += numLines;
341:
342: lastfvmget = -1;
343: dump();
344: }
345: } //}}}
346:
347: //{{{ preContentRemoved() method
348: /**
349: * @return If the anchors should be reset.
350: */
351: boolean preContentRemoved(int startLine, int numLines) {
352: boolean returnValue = false;
353:
354: int endLine = startLine + numLines;
355:
356: /* update fold visibility map. */
357: int starti = search(startLine);
358: int endi = search(endLine);
359:
360: /* both have same visibility; just remove
361: * anything in between. */
362: if (Math.abs(starti % 2) == Math.abs(endi % 2)) {
363: if (endi - starti == fvmcount) {
364: // we're removing from before
365: // the first visible to after
366: // the last visible
367: returnValue = true;
368: starti = 1;
369: } else {
370: put(starti + 1, endi + 1, null);
371: starti++;
372: }
373: }
374: /* collapse 2 */
375: else if (starti != -1 && fvm[starti] == startLine) {
376: if (endi - starti == fvmcount - 1) {
377: // we're removing from
378: // the first visible to after
379: // the last visible
380: returnValue = true;
381: starti = 1;
382: } else
383: put(starti, endi + 1, null);
384: }
385: /* shift */
386: else {
387: put(starti + 1, endi, null);
388: fvm[starti + 1] = startLine;
389: starti += 2;
390: }
391:
392: /* update */
393: for (int i = starti; i < fvmcount; i++)
394: fvm[i] -= numLines;
395:
396: lastfvmget = -1;
397: dump();
398:
399: return returnValue;
400: } //}}}
401:
402: //{{{ Private members
403: private int[] fvm;
404: private int fvmcount;
405: private int lastfvmget;
406: //}}}
407: }
|