001: /*
002: * LineManager.java - Manages line info, line start offsets, positions
003: * :tabSize=8:indentSize=8:noTabs=false:
004: * :folding=explicit:collapseFolds=1:
005: *
006: * Copyright (C) 2001, 2004 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.buffer;
024:
025: //{{{ Imports
026: import org.gjt.sp.jedit.syntax.*;
027: import org.gjt.sp.jedit.Debug;
028: import org.gjt.sp.util.IntegerArray;
029: import org.gjt.sp.util.Log;
030:
031: //}}}
032:
033: /**
034: * A class internal to jEdit's document model. You should not use it
035: * directly. To improve performance, none of the methods in this class
036: * check for out of bounds access, nor are they thread-safe. The
037: * <code>Buffer</code> class, through which these methods must be
038: * called through, implements such protection.
039: *
040: * @author Slava Pestov
041: * @version $Id: LineManager.java 5484 2006-06-23 21:59:52Z kpouer $
042: * @since jEdit 4.2pre3
043: */
044: public class LineManager {
045: //{{{ LineManager constructor
046: public LineManager() {
047: endOffsets = new int[1];
048: endOffsets[0] = 1;
049: foldLevels = new short[1];
050: lineContext = new TokenMarker.LineContext[1];
051: lineCount = 1;
052: } //}}}
053:
054: //{{{ getLineCount() method
055: public final int getLineCount() {
056: return lineCount;
057: } //}}}
058:
059: //{{{ getLineOfOffset() method
060: public int getLineOfOffset(int offset) {
061: int start = 0;
062: int end = lineCount - 1;
063:
064: for (;;) {
065: switch (end - start) {
066: case 0:
067: if (getLineEndOffset(start) <= offset)
068: return start + 1;
069: else
070: return start;
071: case 1:
072: if (getLineEndOffset(start) <= offset) {
073: if (getLineEndOffset(end) <= offset)
074: return end + 1;
075: else
076: return end;
077: } else
078: return start;
079: default:
080: int pivot = (end + start) / 2;
081: int value = getLineEndOffset(pivot);
082: if (value == offset)
083: return pivot + 1;
084: else if (value < offset)
085: start = pivot + 1;
086: else
087: end = pivot - 1;
088: break;
089: }
090: }
091: } //}}}
092:
093: //{{{ getLineEndOffset() method
094: public final int getLineEndOffset(int line) {
095: if (gapLine != -1 && line >= gapLine)
096: return endOffsets[line] + gapWidth;
097: else
098: return endOffsets[line];
099: } //}}}
100:
101: //{{{ getFoldLevel() method
102: public final int getFoldLevel(int line) {
103: return foldLevels[line];
104: } //}}}
105:
106: //{{{ setFoldLevel() method
107: // Also sets 'fold level valid' flag
108: public final void setFoldLevel(int line, int level) {
109: if (level > 0xffff) {
110: // limitations...
111: level = 0xffff;
112: }
113:
114: foldLevels[line] = (short) level;
115: } //}}}
116:
117: //{{{ setFirstInvalidFoldLevel() method
118: public void setFirstInvalidFoldLevel(int firstInvalidFoldLevel) {
119: this .firstInvalidFoldLevel = firstInvalidFoldLevel;
120: } //}}}
121:
122: //{{{ getFirstInvalidFoldLevel() method
123: public int getFirstInvalidFoldLevel() {
124: return firstInvalidFoldLevel;
125: } //}}}
126:
127: //{{{ getLineContext() method
128: public final TokenMarker.LineContext getLineContext(int line) {
129: return lineContext[line];
130: } //}}}
131:
132: //{{{ setLineContext() method
133: public final void setLineContext(int line,
134: TokenMarker.LineContext context) {
135: lineContext[line] = context;
136: } //}}}
137:
138: //{{{ setFirstInvalidLineContext() method
139: public void setFirstInvalidLineContext(int firstInvalidLineContext) {
140: this .firstInvalidLineContext = firstInvalidLineContext;
141: } //}}}
142:
143: //{{{ getFirstInvalidLineContext() method
144: public int getFirstInvalidLineContext() {
145: return firstInvalidLineContext;
146: } //}}}
147:
148: //{{{ _contentInserted() method
149: public void _contentInserted(IntegerArray endOffsets) {
150: gapLine = -1;
151: gapWidth = 0;
152: firstInvalidLineContext = firstInvalidFoldLevel = 0;
153: lineCount = endOffsets.getSize();
154: this .endOffsets = endOffsets.getArray();
155: foldLevels = new short[lineCount];
156:
157: lineContext = new TokenMarker.LineContext[lineCount];
158: } //}}}
159:
160: //{{{ contentInserted() method
161: public void contentInserted(int startLine, int offset,
162: int numLines, int length, IntegerArray endOffsets) {
163: int endLine = startLine + numLines;
164:
165: //{{{ Update line info and line context arrays
166: if (numLines > 0) {
167: //moveGap(-1,0,"contentInserted");
168:
169: lineCount += numLines;
170:
171: if (this .endOffsets.length <= lineCount) {
172: int[] endOffsetsN = new int[(lineCount + 1) * 2];
173: System.arraycopy(this .endOffsets, 0, endOffsetsN, 0,
174: this .endOffsets.length);
175: this .endOffsets = endOffsetsN;
176: }
177:
178: if (foldLevels.length <= lineCount) {
179: short[] foldLevelsN = new short[(lineCount + 1) * 2];
180: System.arraycopy(foldLevels, 0, foldLevelsN, 0,
181: foldLevels.length);
182: foldLevels = foldLevelsN;
183: }
184:
185: if (lineContext.length <= lineCount) {
186: TokenMarker.LineContext[] lineContextN = new TokenMarker.LineContext[(lineCount + 1) * 2];
187: System.arraycopy(lineContext, 0, lineContextN, 0,
188: lineContext.length);
189: lineContext = lineContextN;
190: }
191:
192: System.arraycopy(this .endOffsets, startLine,
193: this .endOffsets, endLine, lineCount - endLine);
194: System.arraycopy(foldLevels, startLine, foldLevels,
195: endLine, lineCount - endLine);
196: System.arraycopy(lineContext, startLine, lineContext,
197: endLine, lineCount - endLine);
198:
199: if (startLine <= gapLine)
200: gapLine += numLines;
201: else if (gapLine != -1)
202: offset -= gapWidth;
203:
204: if (startLine < firstInvalidLineContext)
205: firstInvalidLineContext += numLines;
206:
207: for (int i = 0; i < numLines; i++) {
208: this .endOffsets[startLine + i] = (offset + endOffsets
209: .get(i));
210: foldLevels[startLine + i] = 0;
211: }
212: } //}}}
213:
214: if (firstInvalidFoldLevel == -1
215: || firstInvalidFoldLevel > startLine)
216: firstInvalidFoldLevel = startLine;
217: moveGap(endLine, length, "contentInserted");
218: } //}}}
219:
220: //{{{ contentRemoved() method
221: public void contentRemoved(int startLine, int offset, int numLines,
222: int length) {
223: int endLine = startLine + numLines;
224:
225: //{{{ Update line info and line context arrays
226: if (numLines > 0) {
227: //moveGap(-1,0,"contentRemoved");
228:
229: if (startLine + numLines < gapLine)
230: gapLine -= numLines;
231: else if (startLine < gapLine)
232: gapLine = startLine;
233:
234: if (startLine + numLines < firstInvalidLineContext)
235: firstInvalidLineContext -= numLines;
236: else if (startLine < firstInvalidLineContext)
237: firstInvalidLineContext = startLine - 1;
238:
239: lineCount -= numLines;
240:
241: System.arraycopy(endOffsets, endLine, endOffsets,
242: startLine, lineCount - startLine);
243: System.arraycopy(foldLevels, endLine, foldLevels,
244: startLine, lineCount - startLine);
245: System.arraycopy(lineContext, endLine, lineContext,
246: startLine, lineCount - startLine);
247: } //}}}
248:
249: if (firstInvalidFoldLevel == -1
250: || firstInvalidFoldLevel > startLine)
251: firstInvalidFoldLevel = startLine;
252: moveGap(startLine, -length, "contentRemoved");
253: } //}}}
254:
255: //{{{ Private members
256:
257: //{{{ Instance variables
258: private int[] endOffsets;
259: private short[] foldLevels;
260: private TokenMarker.LineContext[] lineContext;
261:
262: private int lineCount;
263:
264: /**
265: * If -1, then there is no gap.
266: * Otherwise, all lines from this line onwards need to have gapWidth
267: * added to their end offsets.
268: */
269: private int gapLine;
270: private int gapWidth;
271:
272: /**
273: * If -1, all contexts are valid. Otherwise, all lines after this have
274: * an invalid context.
275: */
276: private int firstInvalidLineContext;
277:
278: /**
279: * If -1, all fold levels are valid. Otherwise, all lines after this
280: * have an invalid fold level.
281: */
282: private int firstInvalidFoldLevel;
283:
284: //}}}
285:
286: //{{{ setLineEndOffset() method
287: private final void setLineEndOffset(int line, int end) {
288: endOffsets[line] = end;
289: } //}}}
290:
291: //{{{ moveGap() method
292: private final void moveGap(int newGapLine, int newGapWidth,
293: String method) {
294: if (gapLine == -1)
295: gapWidth = newGapWidth;
296: else if (newGapLine == -1) {
297: if (gapWidth != 0) {
298: if (Debug.OFFSET_DEBUG && gapLine != lineCount)
299: Log.log(Log.DEBUG, this , method + ": update from "
300: + gapLine + " to " + lineCount + " width "
301: + gapWidth);
302: for (int i = gapLine; i < lineCount; i++)
303: setLineEndOffset(i, getLineEndOffset(i));
304: }
305:
306: gapWidth = newGapWidth;
307: } else if (newGapLine < gapLine) {
308: if (gapWidth != 0) {
309: if (Debug.OFFSET_DEBUG && newGapLine != gapLine)
310: Log.log(Log.DEBUG, this , method + ": update from "
311: + newGapLine + " to " + gapLine + " width "
312: + gapWidth);
313: for (int i = newGapLine; i < gapLine; i++)
314: setLineEndOffset(i, getLineEndOffset(i) - gapWidth);
315: }
316: gapWidth += newGapWidth;
317: } else //if(newGapLine >= gapLine)
318: {
319: if (gapWidth != 0) {
320: if (Debug.OFFSET_DEBUG && gapLine != newGapLine)
321: Log.log(Log.DEBUG, this , method + ": update from "
322: + gapLine + " to " + newGapLine + " width "
323: + gapWidth);
324: for (int i = gapLine; i < newGapLine; i++)
325: setLineEndOffset(i, getLineEndOffset(i));
326: }
327:
328: gapWidth += newGapWidth;
329: }
330:
331: if (newGapLine == lineCount)
332: gapLine = -1;
333: else
334: gapLine = newGapLine;
335: } //}}}
336:
337: //}}}
338: }
|