0001: /*
0002: * Sun Public License Notice
0003: *
0004: * The contents of this file are subject to the Sun Public License
0005: * Version 1.0 (the "License"). You may not use this file except in
0006: * compliance with the License. A copy of the License is available at
0007: * http://www.sun.com/
0008: *
0009: * The Original Code is NetBeans. The Initial Developer of the Original
0010: * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
0011: * Microsystems, Inc. All Rights Reserved.
0012: */
0013:
0014: package org.netbeans.editor;
0015:
0016: import javax.swing.text.AbstractDocument;
0017: import javax.swing.text.BadLocationException;
0018: import javax.swing.text.Position;
0019: import javax.swing.text.Segment;
0020: import javax.swing.undo.AbstractUndoableEdit;
0021: import javax.swing.undo.CannotRedoException;
0022: import javax.swing.undo.CannotUndoException;
0023: import javax.swing.undo.UndoableEdit;
0024:
0025: /**
0026: * Document operations. This class enhances basic marks operations by
0027: * interaction with the data of the document. All mark operations should go
0028: * through this class. This class implements <tt>AbstractDocument.Content</tt>
0029: * interface. BOL means Begin of Line position. EOL means End of Line position.
0030: *
0031: * @author Miloslav Metelka
0032: * @version 1.00
0033: */
0034:
0035: class DocOp implements AbstractDocument.Content {
0036:
0037: /** Number of line cache entries */
0038: private static final int CACHE_LEN = 5;
0039:
0040: private static final String WRONG_POSITION = "Wrong position "; // NOI18N
0041:
0042: private static final String DOC_LEN = ". Document length is "; // NOI18N
0043:
0044: /**
0045: * Default mark distance for inserting to the document. If the insert is
0046: * made then the distance between nearest marks around insertion point is
0047: * checked and if it's greater than the max mark distance then another
0048: * mark(s) are inserted.
0049: */
0050: private int MARK_DISTANCE;
0051:
0052: /**
0053: * Maximum mark distance. When there is an insertion done in document and
0054: * the distance between marks get greater than this value, another mark will
0055: * be inserted.
0056: */
0057: private int MAX_MARK_DISTANCE;
0058:
0059: /**
0060: * Minimum mark distance for removals. When there is a removal done in
0061: * document and it makes the marks to get closer than this value, then the
0062: * second mark will be removed.
0063: */
0064: private int MIN_MARK_DISTANCE;
0065:
0066: /**
0067: * Size of one batch for updating syntax marks. It's used in two cases. When
0068: * the prepareSyntax() is called it may be necessary to update the state
0069: * infos of the syntax marks that were not updated yet. In this case the
0070: * SYNTAX_UPDATE_BATCH_SIZE is the minimum area after the required position
0071: * that will be updated (required length is often 0 so this prevents
0072: * updating marks one by one). The other case is when insert or remove is
0073: * performed it's not clear how many marks will it be necessary to update so
0074: * it serves as the batching constant in this case.
0075: */
0076: private int SYNTAX_UPDATE_BATCH_SIZE;
0077:
0078: /** Document cache */
0079: private DocCache cache;
0080:
0081: /** Document cache support */
0082: private DocCacheSupport cacheSupport;
0083:
0084: /** Document marks handling */
0085: DocMarks marks;
0086:
0087: /** Document */
0088: private BaseDocument doc;
0089:
0090: /** Document len */
0091: private int docLen;
0092:
0093: /** End of current line forward finder */
0094: private FinderFactory.EOLFwdFinder eolFwdFinder;
0095:
0096: /** Current line begining backward finder */
0097: private FinderFactory.BOLBwdFinder bolBwdFinder;
0098:
0099: /** Find both BOL and EOL one or more lines forward in text */
0100: private FinderFactory.BEOLLineFwdFinder beolLineFwdFinder;
0101:
0102: /** Find both BOL and EOL when position is known */
0103: private FinderFactory.BEOLPosFwdFinder beolPosFwdFinder;
0104:
0105: /** Finder for visual x-coord to position conversion */
0106: private FinderFactory.VisColPosFwdFinder visColPosFwdFinder;
0107:
0108: /** Finder for position to x-coord conversion */
0109: private FinderFactory.PosVisColFwdFinder posVisColFwdFinder;
0110:
0111: /** Line and column information cache */
0112: private CacheEntry lineCache[];
0113:
0114: /**
0115: * End of document mark. This mark should speed up getting the line number
0116: * of the end of document so that the cache fragment doesn't have to move.
0117: */
0118: private Mark endMark;
0119:
0120: /**
0121: * Mark that is inserted at the end of line (better said after the end of
0122: * line) where the insertion currently occurs. The mark is possibly moved
0123: * and/or updated before the insertion/removal occurs. Then after the
0124: * insertion/removal is done mark syntax state is checked again. If it's the
0125: * same as before it's not necessary to paint the next line after insertion.
0126: * If it's not the same the painting must continue till the syntax mark with
0127: * matching state.
0128: */
0129: MarkFactory.SyntaxMark eolMark;
0130:
0131: /**
0132: * Position of the end of the already scanned area. When the initial read is
0133: * performed, it's set to zero. The <tt>prepareSyntax()</tt> updates this
0134: * position if necessary. The value -1 means that either the document is
0135: * empty or all the syntax marks are already up-to-date. It happens by
0136: * requesting the scanning at the end of the document or after the first
0137: * modification. The particular value means that all the syntax marks
0138: * including those right at the position given by the value of the variable
0139: * are up-to-date even if there would be more than one mark right at that
0140: * position.
0141: */
0142: private int updatedAreaEnd = -1;
0143:
0144: /** Renderer for doing syntax mark updates after insertion/removal. */
0145: private SyntaxUpdateRenderer suRenderer;
0146:
0147: /**
0148: * First syntax mark that is laying in the left direction from the update
0149: * point.
0150: */
0151: private MarkFactory.SyntaxMark leftUpdateMark;
0152:
0153: /** Statistics */
0154: int statCacheHit;
0155: int statCacheMiss;
0156:
0157: /**
0158: * Construct new document marks operations. Since this class uses both cache
0159: * and marks both these classes must be already created in document.
0160: */
0161: DocOp() {
0162: marks = new DocMarks();
0163: marks.startMark.setOp(this );
0164:
0165: cacheSupport = new MemCacheSupport();
0166: cache = new DocCache(cacheSupport, 2048, true); // !!!
0167:
0168: // initialize cache
0169: lineCache = new CacheEntry[CACHE_LEN];
0170: for (int i = 0; i < CACHE_LEN; i++) {
0171: lineCache[i] = new CacheEntry();
0172: }
0173:
0174: // create necessary finders
0175: bolBwdFinder = new FinderFactory.BOLBwdFinder();
0176: eolFwdFinder = new FinderFactory.EOLFwdFinder();
0177: beolLineFwdFinder = new FinderFactory.BEOLLineFwdFinder();
0178: beolPosFwdFinder = new FinderFactory.BEOLPosFwdFinder();
0179: visColPosFwdFinder = new FinderFactory.VisColPosFwdFinder();
0180: posVisColFwdFinder = new FinderFactory.PosVisColFwdFinder();
0181:
0182: // init endMark and eolMark
0183: try {
0184:
0185: endMark = insertMark(docLen, false);
0186: eolMark = new MarkFactory.SyntaxMark() {
0187: protected void removeUpdateAction(int pos, int len) {
0188: // prevent default removing of this mark
0189: }
0190: };
0191: insertMark(eolMark, docLen);
0192: } catch (BadLocationException e) {
0193: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0194: e.printStackTrace();
0195: }
0196: } catch (InvalidMarkException e) {
0197: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0198: e.printStackTrace();
0199: }
0200: }
0201:
0202: // create shared syntax update renderer
0203: suRenderer = new SyntaxUpdateRenderer();
0204: }
0205:
0206: void setDocument(BaseDocument doc) {
0207: this .doc = doc;
0208: MARK_DISTANCE = ((Integer) doc
0209: .getProperty(SettingsNames.MARK_DISTANCE)).intValue();
0210: MAX_MARK_DISTANCE = ((Integer) doc
0211: .getProperty(SettingsNames.MAX_MARK_DISTANCE))
0212: .intValue();
0213: MIN_MARK_DISTANCE = ((Integer) doc
0214: .getProperty(SettingsNames.MIN_MARK_DISTANCE))
0215: .intValue();
0216: SYNTAX_UPDATE_BATCH_SIZE = ((Integer) doc
0217: .getProperty(SettingsNames.SYNTAX_UPDATE_BATCH_SIZE))
0218: .intValue();
0219:
0220: }
0221:
0222: public synchronized Position createPosition(int offset)
0223: throws BadLocationException {
0224: boolean insertAfter = (offset == 0); // support AbstractDocument keep
0225: // marks at position 0 behavior
0226: if (offset == docLen + 1) { // support AbstractDocument's content
0227: // initial "\n" behavior
0228: offset = docLen;
0229: }
0230:
0231: return new BasePosition(this , offset,
0232: insertAfter ? Position.Bias.Backward
0233: : Position.Bias.Forward);
0234: }
0235:
0236: public synchronized Position createPosition(int offset,
0237: Position.Bias bias) throws BadLocationException {
0238: return new BasePosition(this , offset, bias);
0239: }
0240:
0241: public synchronized int length() {
0242: return docLen;
0243: }
0244:
0245: public String getString(int where, int len)
0246: throws BadLocationException {
0247: return new String(getChars(where, len));
0248: }
0249:
0250: public void getChars(int where, int len, Segment txt)
0251: throws BadLocationException {
0252: txt.array = getChars(where, len);
0253: txt.offset = 0;
0254: txt.count = len;
0255: }
0256:
0257: /** Retrieve the characters from the document cache. */
0258: synchronized char[] getChars(int pos, int len)
0259: throws BadLocationException {
0260: return cache.read(pos, len, null); // no cache fragment optimization
0261: // yet
0262: }
0263:
0264: /**
0265: * Get the characters into the given buffer. The buffer must contain enough
0266: * space for the requested data.
0267: */
0268: synchronized void getChars(int pos, char ret[], int offset, int len)
0269: throws BadLocationException {
0270: cache.read(pos, ret, offset, len, null);
0271: }
0272:
0273: public UndoableEdit insertString(int offset, String text)
0274: throws BadLocationException {
0275: ModifyUndoEdit undoEdit = new ModifyUndoEdit(false, offset,
0276: text);
0277: insertEdit(undoEdit);
0278: return undoEdit;
0279: }
0280:
0281: public UndoableEdit insert(int offset, char[] chars)
0282: throws BadLocationException {
0283: ModifyUndoEdit undoEdit = new ModifyUndoEdit(false, offset,
0284: chars);
0285: insertEdit(undoEdit);
0286: return undoEdit;
0287: }
0288:
0289: synchronized void insertEdit(ModifyUndoEdit undoEdit)
0290: throws BadLocationException {
0291: int offset = undoEdit.getOffset();
0292: checkEOLMark(offset);
0293: if (undoEdit.isTextValid()) {
0294: cache.insertString(offset, undoEdit.getText(), null);
0295: } else { // chars buffer valid
0296: cache.insert(offset, undoEdit.getChars(), null);
0297: }
0298: insertUpdate(undoEdit); // always done to update line cache
0299: }
0300:
0301: public UndoableEdit remove(int offset, int len)
0302: throws BadLocationException {
0303: ModifyUndoEdit undoEdit = new ModifyUndoEdit(true, offset,
0304: getChars(offset, len));
0305: removeEdit(undoEdit);
0306: return undoEdit;
0307: }
0308:
0309: synchronized void removeEdit(ModifyUndoEdit undoEdit)
0310: throws BadLocationException {
0311: checkEOLMark(undoEdit.getOffset());
0312: cache.remove(undoEdit.getOffset(), undoEdit.getLength(), null); // no
0313: // cache
0314: // fragment
0315: // optimization
0316: // yet
0317: removeUpdate(undoEdit);
0318: }
0319:
0320: synchronized int find(Finder finder, int startPos, int limitPos)
0321: throws BadLocationException {
0322: return cache.find(finder, startPos, limitPos, null);
0323: }
0324:
0325: /**
0326: * Insert new mark at specified position. This function finds the
0327: * appropriate line number.
0328: */
0329: synchronized void insertMark(Mark mark, int offset)
0330: throws BadLocationException, InvalidMarkException {
0331: if (offset < 0 || offset > docLen) {
0332: throw new BadLocationException(WRONG_POSITION + offset
0333: + DOC_LEN + docLen, offset);
0334: }
0335: insertMark(mark, offset, getLineImpl(offset));
0336: }
0337:
0338: /**
0339: * Insert mark when knowing even the line offset. This method is used solely
0340: * by <tt>Analyzer</tt> when document is read.
0341: */
0342: void insertMark(Mark mark, int offset, int line)
0343: throws BadLocationException, InvalidMarkException {
0344: mark.offset = offset;
0345: mark.line = line;
0346: marks.insert(mark);
0347: mark.setOp(this );
0348: }
0349:
0350: /**
0351: * Write directly to the cache-support. This method is used solely by
0352: * <tt>Analyzer</tt> when document is read.
0353: */
0354: void directCacheWrite(int pos, char cache[], int offset, int len)
0355: throws BadLocationException {
0356: cacheSupport.write(pos, cache, offset, len);
0357: }
0358:
0359: /**
0360: * Initialize the default fragment of the cache by the given data. This
0361: * method is used solely by <tt>Analyzer</tt> when document is read.
0362: */
0363: void initCacheContent(char initCache[], int offset, int cacheLen) {
0364: cache.initCacheContent(initCache, offset, cacheLen);
0365: }
0366:
0367: /**
0368: * Insert new mark at specified position. The function finds appropriate
0369: * line number.
0370: */
0371: Mark insertMark(int pos, boolean insertAfter)
0372: throws BadLocationException {
0373: Mark mark = new Mark(insertAfter);
0374: try {
0375: insertMark(mark, pos);
0376: } catch (InvalidMarkException e) {
0377: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0378: e.printStackTrace();
0379: }
0380: }
0381: return mark;
0382: }
0383:
0384: /** Moves the mark to different position */
0385: synchronized void moveMark(Mark mark, int newPos)
0386: throws BadLocationException, InvalidMarkException {
0387: if (newPos < 0 || newPos > docLen) {
0388: throw new BadLocationException(WRONG_POSITION + newPos
0389: + DOC_LEN + docLen, newPos);
0390: }
0391: mark.remove();
0392: insertMark(mark, newPos);
0393: }
0394:
0395: /**
0396: * Get the first mark of the specified class that lies on the given
0397: * position.
0398: *
0399: * @param offset
0400: * position at which the mark is searched
0401: * @param markClass
0402: * the class that the mark must be of. It can be also an instance
0403: * of a descendant of this class (<tt>Class.isInstance</tt> is
0404: * used).
0405: * @return mark that matches the condition or null.
0406: */
0407: synchronized Mark getOffsetMark(int offset, Class markClass) {
0408: return marks.getOffsetMark(offset, markClass);
0409: }
0410:
0411: synchronized void renderMarks(DocMarks.Renderer r) {
0412: marks.render(r);
0413: }
0414:
0415: /**
0416: * Get begin of line from position on that line
0417: *
0418: * @param position
0419: * where the search begins
0420: * @return position of begin of the same line
0421: */
0422: synchronized int getBOL(int pos) throws BadLocationException {
0423: return getBOLImpl(pos);
0424: }
0425:
0426: /** Is the position at the begining of the line? */
0427: synchronized boolean isBOL(int pos) throws BadLocationException {
0428: return (pos == getBOLImpl(pos));
0429: }
0430:
0431: /** Get end of line position from position on that line. */
0432: synchronized int getEOL(int pos) throws BadLocationException {
0433: return getEOLImpl(pos);
0434: }
0435:
0436: /**
0437: * Get end of line after the new-line position from position on that line.
0438: * This is in fact the begining of the next line except the last line.
0439: */
0440: synchronized int getEOLNL(int pos) throws BadLocationException {
0441: int eol = getEOLImpl(pos);
0442: if (eol < docLen) {
0443: return eol++;
0444: }
0445: return eol;
0446: }
0447:
0448: /** Is the position at the end of the line? */
0449: synchronized boolean isEOL(int pos) throws BadLocationException {
0450: return (pos == getEOLImpl(pos));
0451: }
0452:
0453: /**
0454: * Get the begining position of specified line
0455: *
0456: * @param line
0457: * line offset for which the BOL should be determined
0458: * @return position of the begining of line or -1 if line is invalid
0459: */
0460: synchronized int getBOLFromLine(int line) {
0461: return getBOLFromLineImpl(line);
0462: }
0463:
0464: /** Get end of line position from specified line */
0465: synchronized int getEOLFromLine(int line) {
0466: int pos = getBOLFromLineImpl(line);
0467: if (pos < 0) {
0468: return 0;
0469: }
0470: try {
0471: return getEOLImpl(pos);
0472: } catch (BadLocationException e) {
0473: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0474: e.printStackTrace();
0475: }
0476: return -1;
0477: }
0478: }
0479:
0480: /** Advance given position n-lines forward/backward and return BOL. */
0481: synchronized int getBOLRelLine(int pos, int relLine)
0482: throws BadLocationException {
0483: int line = getLineImpl(pos);
0484: line += relLine;
0485: return getBOLFromLineImpl(line);
0486: }
0487:
0488: /** Advance given position n-lines forward/backward and return BOL. */
0489: synchronized int getEOLRelLine(int pos, int relLine)
0490: throws BadLocationException {
0491: int line = getLineImpl(pos);
0492: line += relLine;
0493: pos = getBOLFromLineImpl(line);
0494: if (pos < 0) {
0495: return pos;
0496: }
0497: try {
0498: return getEOLImpl(pos);
0499: } catch (BadLocationException e) {
0500: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0501: e.printStackTrace();
0502: }
0503: return -1;
0504: }
0505: }
0506:
0507: /**
0508: * Get line from position. This is used for example when removal from
0509: * document is made to find right line number for marks update.
0510: */
0511: synchronized int getLine(int pos) throws BadLocationException {
0512: return getLineImpl(pos);
0513: }
0514:
0515: synchronized int getLineCount() {
0516: int lineCnt;
0517: try {
0518: lineCnt = endMark.getLine() + 1;
0519: } catch (InvalidMarkException e) {
0520: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0521: e.printStackTrace();
0522: }
0523: return 0;
0524: }
0525:
0526: return lineCnt;
0527: }
0528:
0529: /**
0530: * Get position on line from visual column. This method can be used only for
0531: * superfixed font i.e. all characters of all font styles have the same
0532: * width.
0533: *
0534: * @param visCol
0535: * visual column
0536: * @param startLinePos
0537: * position of line start
0538: * @return position on line for particular x-coord
0539: */
0540: synchronized int getOffsetFromVisCol(int visCol, int startLinePos)
0541: throws BadLocationException {
0542: if (visCol <= 0) {
0543: return startLinePos;
0544: }
0545: visColPosFwdFinder.setVisCol(visCol);
0546: visColPosFwdFinder.setTabSize(doc.getTabSize());
0547: int pos = cache
0548: .find(visColPosFwdFinder, startLinePos, -1, null);
0549: return (pos != -1) ? pos : getEOLImpl(startLinePos);
0550: }
0551:
0552: /**
0553: * Get visual column from position. This method can be used only for
0554: * superfixed font i.e. all characters of all font styles have the same
0555: * width.
0556: *
0557: * @param pos
0558: * position for which the visual column should be returned the
0559: * function itself computes the begining of the line first
0560: */
0561: synchronized int getVisColFromPos(int pos)
0562: throws BadLocationException {
0563: int startLinePos = getBOLImpl(pos);
0564: posVisColFwdFinder.setTabSize(doc.getTabSize());
0565: cache.find(posVisColFwdFinder, startLinePos, pos, null);
0566: return posVisColFwdFinder.getVisCol();
0567: }
0568:
0569: private int getBOLImpl(int pos) throws BadLocationException {
0570: if (pos <= 0) { // must be first line
0571: if (pos == 0) {
0572: return 0;
0573: }
0574: throw new BadLocationException(WRONG_POSITION + pos
0575: + DOC_LEN + length(), pos);
0576: }
0577:
0578: // search cache
0579: for (int i = 0; i < CACHE_LEN; i++) {
0580: if (pos >= lineCache[i].bol && pos <= lineCache[i].eol) {
0581: // if (lineCache[i].bol != getCheckBOL(pos)) { // !!! remove
0582: // checkCache();
0583: // }
0584: cacheMoveFirst(i);
0585: statCacheHit++;
0586: return lineCache[0].bol;
0587: }
0588: }
0589: statCacheMiss++;
0590:
0591: // search document when not found
0592: return cache.find(bolBwdFinder, pos, 0, null) + 1;
0593: }
0594:
0595: private int getEOLImpl(int pos) throws BadLocationException {
0596: if (pos < 0) {
0597: throw new BadLocationException(WRONG_POSITION + pos
0598: + DOC_LEN + length(), pos);
0599: }
0600:
0601: // search cache
0602: for (int i = 0; i < CACHE_LEN; i++) {
0603: if (pos >= lineCache[i].bol && pos <= lineCache[i].eol) {
0604: // if (lineCache[i].eol != getCheckEOL(pos)) {
0605: // checkCache();
0606: // }
0607: cacheMoveFirst(i);
0608: statCacheHit++;
0609: return lineCache[0].eol;
0610: }
0611: }
0612: statCacheMiss++;
0613:
0614: // search document
0615: pos = cache.find(eolFwdFinder, pos, -1, null);
0616: return (pos != -1) ? pos : docLen;
0617: }
0618:
0619: private int getBOLFromLineImpl(int line) {
0620: if (line < 0) {
0621: return -1;
0622: }
0623:
0624: // check cache first
0625: for (int i = 0; i < CACHE_LEN; i++) {
0626: if (line == lineCache[i].line) {
0627: // if (lineCache[i].bol != getCheckBOLFromLine(line)) { // !!!
0628: // remove
0629: // checkCache();
0630: // }
0631: cacheMoveFirst(i);
0632: statCacheHit++;
0633: return lineCache[0].bol;
0634: }
0635: }
0636: statCacheMiss++;
0637:
0638: // load line into cache
0639: cacheMoveFirst(CACHE_LEN - 1);
0640: return cacheLoadLine(line); // returns -1 for wrong pos
0641:
0642: }
0643:
0644: private int getLineImpl(int pos) throws BadLocationException {
0645: if (pos < 0 || pos > docLen) {
0646: throw new BadLocationException(WRONG_POSITION + pos
0647: + DOC_LEN + length(), pos);
0648: }
0649:
0650: // search cache
0651: for (int i = 0; i < CACHE_LEN; i++) {
0652: if (pos >= lineCache[i].bol && pos <= lineCache[i].eol) {
0653: // if (lineCache[i].line != getCheckLine(pos)) {
0654: // checkCache();
0655: // }
0656: cacheMoveFirst(i);
0657: statCacheHit++;
0658: return lineCache[0].line;
0659: }
0660: }
0661: statCacheMiss++;
0662:
0663: // load line into cache
0664: cacheMoveFirst(CACHE_LEN - 1);
0665: return cacheLoadLineByPos(pos);
0666: }
0667:
0668: /**
0669: * If this position is at BOL leave it as it is if returnIfBOL is set.
0670: * Otherwise get BOL of the next line or end of document.
0671: *
0672: * @param returnIfBOL
0673: * return immediately if the position is already on BOL
0674: */
0675: private int adjustNextBOL(int pos, boolean returnIfBOL)
0676: throws BadLocationException {
0677: if (returnIfBOL && pos == getBOLImpl(pos)) {
0678: return pos;
0679: }
0680: pos = getEOLImpl(pos);
0681: if (pos < docLen) {
0682: pos++;
0683: }
0684: return pos;
0685: }
0686:
0687: /**
0688: * Get the first syntax mark that is in the left direction from the desired
0689: * position.
0690: */
0691: MarkFactory.SyntaxMark getLeftSyntaxMark(int pos) {
0692: return (MarkFactory.SyntaxMark) marks.getLeftMark(pos,
0693: MarkFactory.SyntaxMark.class);
0694: }
0695:
0696: private void update(boolean remove, ModifyUndoEdit undoEdit) {
0697: int pos = undoEdit.getOffset();
0698: SyntaxSeg.invalidate(doc, pos);
0699: SyntaxSeg.Slot slot = SyntaxSeg.getFreeSlot();
0700: Syntax syntax = doc.getFreeSyntax();
0701:
0702: try {
0703: // #19429
0704: // Removing syntax marks at pos, eolMark must be saved
0705: if (remove) {
0706: boolean eolMarkRemoved = false;
0707: while (true) {
0708: Mark mark = getOffsetMark(pos,
0709: MarkFactory.SyntaxMark.class);
0710: if (mark == eolMark) {
0711: eolMarkRemoved = true;
0712: }
0713: if (mark == null) {
0714: break;
0715: }
0716: mark.remove();
0717: }
0718: if (eolMarkRemoved) {
0719: insertMark(eolMark, pos);
0720: }
0721: // Removing line marks at pos
0722: while (true) {
0723: Mark mark = getOffsetMark(pos,
0724: MarkFactory.LineMark.class);
0725: if (mark == null) {
0726: break;
0727: }
0728: mark.remove();
0729: }
0730: }
0731:
0732: cacheUpdate(remove, undoEdit); // Update line cache
0733: leftUpdateMark = getLeftSyntaxMark(pos - 1); // compute left
0734: // syntax update
0735: // mark
0736: int leftUpdatePos = (leftUpdateMark != null) ? leftUpdateMark
0737: .getOffset()
0738: : 0;
0739: // Add or remove syntax marks that are too close or too far away
0740: updateEvenly();
0741:
0742: // Prepare syntax for updating the mark states of the syntax marks
0743: prepareSyntax(slot, syntax, leftUpdateMark, leftUpdatePos,
0744: 0, false);
0745:
0746: // Update the syntax marks by mark renderer
0747: suRenderer.slot = slot;
0748: suRenderer.syntax = syntax;
0749: suRenderer.undoEdit = undoEdit;
0750: suRenderer.remove = remove;
0751: marks.render(suRenderer);
0752: } catch (InvalidMarkException e) {
0753: e.printStackTrace();
0754: } catch (BadLocationException e) {
0755: e.printStackTrace();
0756: } finally {
0757: doc.releaseSyntax(syntax);
0758: SyntaxSeg.releaseSlot(slot);
0759: }
0760: }
0761:
0762: /**
0763: * Invalidate the state-infos in all the syntax-marks in the whole document.
0764: */
0765: synchronized void invalidateSyntaxMarks() {
0766: // Render the marks by mark renderer
0767: marks.render(new DocMarks.Renderer() {
0768: protected void render() {
0769: Mark markArray[] = getMarkArray();
0770: int len = getMarkArrayLength();
0771: for (int i = 0; i < len; i = getNextIndex(i)) {
0772: if (markArray[i] instanceof MarkFactory.SyntaxMark) {
0773: ((MarkFactory.SyntaxMark) markArray[i])
0774: .setStateInfo(null);
0775: }
0776: }
0777: }
0778: });
0779:
0780: updatedAreaEnd = 0; // force to validate when necessary
0781: }
0782:
0783: void initialReadUpdate() { // doesn't need to be synced
0784: docLen = cache.getDocLength();
0785: invalidateCache(); // invalidate the line cache
0786: updatedAreaEnd = 0; // signal that all the syntax marks need to be
0787: // updated
0788: }
0789:
0790: /**
0791: * This function is called after document insertion to update marks. Event
0792: * parameter contains inserted chars and also how many lines was inserted.
0793: */
0794: synchronized void insertUpdate(ModifyUndoEdit undoEdit) {
0795: docLen += undoEdit.getLength();
0796: marks.update(undoEdit.getOffset(), 0, undoEdit.getLength(),
0797: undoEdit.getLFCount());
0798: update(false, undoEdit);
0799: }
0800:
0801: /**
0802: * This function is called after document removal to update marks. Event
0803: * parameter contains removed chars and also how many lines was removed.
0804: */
0805: synchronized void removeUpdate(ModifyUndoEdit undoEdit) {
0806: docLen -= undoEdit.getLength();
0807: marks.update(undoEdit.getOffset(), undoEdit.getLine(),
0808: -undoEdit.getLength(), -undoEdit.getLFCount());
0809: update(true, undoEdit);
0810: }
0811:
0812: /**
0813: * Update marks after insert/removal so that they are well evenly
0814: * distributed. The method is not synchronized on mark renderer as all
0815: * changes must be made under document being write-locked.
0816: */
0817: private void updateEvenly() {
0818: // Render the marks by mark renderer
0819: marks.render(new DocMarks.Renderer() {
0820: protected void render() {
0821: int leftMarkIndex = -1;
0822: int leftMarkPos = 0;
0823: if (leftUpdateMark != null) {
0824: leftMarkIndex = getMarkIndex(leftUpdateMark);
0825: leftMarkPos = getMarkOffset(leftUpdateMark);
0826: }
0827:
0828: Mark markArray[] = getMarkArray();
0829: int cnt = getMarkArrayLength(); // total mark count
0830: int dist = 0; // distance of mark from the current leftMarkPos
0831: Mark m;
0832: int i = getNextIndex(leftMarkIndex);
0833: boolean found = false;
0834: // First remove all the marks that are on the same position
0835: for (; i < cnt; i = getNextIndex(i)) {
0836: m = markArray[i];
0837: dist = getMarkOffset(m) - leftMarkPos;
0838: if (m.getClass() == MarkFactory.SyntaxMark.class) { // syntax
0839: // mark
0840: // found
0841: if (m != eolMark) { // eol mark is ignored here
0842: found = true;
0843: break;
0844: }
0845: }
0846: }
0847: if (!found) {
0848: dist = length() - leftMarkPos;
0849: }
0850: // test for too small distance
0851: if (dist < MIN_MARK_DISTANCE && found) {
0852: try {
0853: markArray[i].remove();
0854: } catch (InvalidMarkException e) {
0855: if (Boolean
0856: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
0857: e.printStackTrace();
0858: }
0859: }
0860: }
0861:
0862: // test for too large distance
0863: if (dist > MAX_MARK_DISTANCE) {
0864: int insCnt = dist / MARK_DISTANCE;
0865: if (insCnt > 0) {
0866: int startInsPos = leftMarkPos
0867: + (dist - (insCnt - 1) * MARK_DISTANCE)
0868: / 2;
0869: try {
0870: for (int j = 0; j < insCnt; j++) {
0871: MarkFactory.SyntaxMark newMark = new MarkFactory.SyntaxMark();
0872: insertMark(newMark, startInsPos + j
0873: * MARK_DISTANCE);
0874: }
0875: } catch (InvalidMarkException e) {
0876: if (Boolean
0877: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
0878: e.printStackTrace();
0879: }
0880: } catch (BadLocationException e) {
0881: if (Boolean
0882: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
0883: e.printStackTrace();
0884: }
0885: }
0886: }
0887: }
0888: }
0889: });
0890: }
0891:
0892: /**
0893: * Check if the EOL syntax mark is at the right place. If not, move it to
0894: * the end of current line and update it with the right syntax information.
0895: * It uses instance of syntax used in document.
0896: */
0897: void checkEOLMark(int pos) {
0898: try {
0899: int eolPos = adjustNextBOL(pos, false);
0900: if (eolMark.getOffset() != eolPos) { // mark at the wrong place
0901: SyntaxSeg.Slot slot = SyntaxSeg.getFreeSlot();
0902: Syntax syntax = doc.getFreeSyntax();
0903:
0904: if (updatedAreaEnd >= 0) { // whole doc still not fully updated
0905: // Force scanning till the end of document
0906: prepareSyntax(slot, syntax,
0907: getLeftSyntaxMark(docLen), docLen, 0, false);
0908: }
0909:
0910: MarkFactory.SyntaxMark mark = getLeftSyntaxMark(eolPos - 1);
0911:
0912: try {
0913: prepareSyntax(slot, syntax, mark, eolPos, 0, false); // scan
0914: // up
0915: // to
0916: // eolMark
0917: // ignoring
0918: // it
0919: // in
0920: // search
0921: // Fix of #11130 - need to move the mark AFTER the syntax is
0922: // prepared
0923: moveMark(eolMark, eolPos);
0924: eolMark.updateStateInfo(syntax);
0925: } finally {
0926: doc.releaseSyntax(syntax);
0927: SyntaxSeg.releaseSlot(slot);
0928: }
0929: }
0930: } catch (BadLocationException e) {
0931: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0932: e.printStackTrace();
0933: }
0934: } catch (InvalidMarkException e) {
0935: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0936: e.printStackTrace();
0937: }
0938: }
0939: }
0940:
0941: /**
0942: * Prepare syntax scanner so that it's ready to scan from requested
0943: * position.
0944: *
0945: * @param slot
0946: * syntax segment slot to be used
0947: * @param syntax
0948: * syntax scanner to be used
0949: * @param leftSyntaxMark
0950: * first syntax mark in the left direction from the reqPos, can
0951: * be obtained by getLeftSyntaxMark()
0952: * @param reqPos
0953: * position to which the syntax should be prepared
0954: * @param reqLen
0955: * length that will be scanned by the caller after the syntax is
0956: * prepared. The prepareSyntax() automatically preloads this area
0957: * into the syntax segment slot.
0958: * @param forceLastBuffer
0959: * force the syntax to think that the scanned area is the last in
0960: * the document. This is useful for forcing the syntax to process
0961: * all the characters in the given area.
0962: * @param forceNotLastBuffer
0963: * force the syntax to think that the scanned area is NOT the
0964: * last buffer in the document. This is useful when the syntax
0965: * will continue scanning on another buffer.
0966: */
0967: void prepareSyntax(final SyntaxSeg.Slot slot, final Syntax syntax,
0968: MarkFactory.SyntaxMark leftSyntaxMark, final int reqPos,
0969: int reqLen, boolean forceLastBuffer,
0970: boolean forceNotLastBuffer) throws BadLocationException {
0971:
0972: /*
0973: * Check whether the syntax marks in the area are already updated The
0974: * end of the requested area is checked although it would be enough to
0975: * scan till markPos. However it's better for drawing because it doesn't
0976: * know the end of the last token to draw and therefore it searches
0977: * through syntax-marks at the end of the drawn area to find the
0978: * syntax-mark that lays (including pre-scan) after the area end.
0979: */
0980: if (updatedAreaEnd >= 0 && updatedAreaEnd < reqPos + reqLen) {
0981: final MarkFactory.SyntaxMark updSyntaxMark = getLeftSyntaxMark(updatedAreaEnd);
0982: int updMarkPos = 0;
0983: int updPreScan = 0;
0984: Syntax.StateInfo updStateInfo = null;
0985: if (updSyntaxMark != null) {
0986: try {
0987: updMarkPos = updSyntaxMark.getOffset();
0988: } catch (InvalidMarkException e) {
0989: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
0990: e.printStackTrace();
0991: }
0992: }
0993:
0994: updStateInfo = updSyntaxMark.getStateInfo();
0995: updPreScan = updStateInfo.getPreScan();
0996: }
0997:
0998: // load the whole area so the next load below will have no work
0999: final int slotLoadPos = updMarkPos - updPreScan;
1000: // length from left mark to reqPos plus preScan
1001: int updLen = reqPos - slotLoadPos;
1002: // Compute req-len for marks updating. It is batched to update more
1003: // marks at once
1004: final int umReqLen = Math.min(reqLen
1005: + SYNTAX_UPDATE_BATCH_SIZE, docLen - reqPos);
1006: slot.load(doc, slotLoadPos, updLen + umReqLen);
1007: syntax.load(updStateInfo, slot.array, slot.offset
1008: + updPreScan, updLen - updPreScan,
1009: (reqPos == docLen), updMarkPos);
1010:
1011: final int umPos = updMarkPos; // helper final variable
1012: marks.render(new DocMarks.Renderer() {
1013: public void render() {
1014: int markCnt = getMarkArrayLength();
1015: int index = (updSyntaxMark != null) ? (getNextIndex(getMarkIndex(updSyntaxMark)))
1016: : getNextIndex(-1);
1017: int pos = umPos;
1018: Mark markArray[] = getMarkArray();
1019:
1020: int lastPos = pos; // position of the last syntax mark
1021: // found
1022: while (index < markCnt) { // possibly till end of mark
1023: // array
1024: Mark mark = markArray[index];
1025: int prevIndex = index;
1026: index = getNextIndex(index);
1027: pos = getMarkOffset(mark);
1028:
1029: if (pos > reqPos + umReqLen) { // reached the end of
1030: // area available in
1031: // slot
1032: index = prevIndex; // this could be a syntax mark,
1033: // so go back for further test
1034: break;
1035: }
1036:
1037: if (mark instanceof MarkFactory.SyntaxMark) {
1038: MarkFactory.SyntaxMark syntaxMark = (MarkFactory.SyntaxMark) mark;
1039: syntax.relocate(slot.array, slot.offset
1040: + lastPos - slotLoadPos, pos
1041: - lastPos, (pos == docLen), pos);
1042:
1043: while (syntax.nextToken() != null) {
1044: } // scan till this mark
1045:
1046: syntaxMark.updateStateInfo(syntax); // update
1047: // state-info of
1048: // the mark
1049: updatedAreaEnd = pos; // this area is up-to-date
1050: lastPos = pos; // move to next area between syntax
1051: // marks
1052: }
1053: }
1054:
1055: // Try to find next syntax-mark in the array
1056: while (index < markCnt) {
1057: if (markArray[index] instanceof MarkFactory.SyntaxMark) {
1058: break;
1059: }
1060: index = getNextIndex(index);
1061: }
1062: // If there's no more SM, finally mark whole doc up-to-date
1063: if (index >= markCnt) { // reached the end of the mark array
1064: updatedAreaEnd = -1;
1065: }
1066:
1067: }
1068: });
1069: }
1070:
1071: // Get nearest previous syntax mark
1072: int markPos = 0;
1073: int preScan = 0;
1074: Syntax.StateInfo stateInfo = null;
1075: if (leftSyntaxMark != null) {
1076: try {
1077: markPos = leftSyntaxMark.getOffset(); // get position to scan
1078: // from
1079: } catch (InvalidMarkException e) {
1080: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
1081: e.printStackTrace();
1082: }
1083: }
1084:
1085: stateInfo = leftSyntaxMark.getStateInfo();
1086: preScan = stateInfo.getPreScan();
1087: }
1088:
1089: // load syntax segment
1090: int loadPos = markPos - preScan;
1091: int prepareLen = reqPos - loadPos;
1092: slot.load(doc, loadPos, prepareLen + reqLen);
1093:
1094: // load state into syntax scanner - will scan from mark up to reqPos
1095: syntax.load(stateInfo, slot.array, slot.offset + preScan,
1096: prepareLen - preScan, forceNotLastBuffer ? false
1097: : (reqPos >= docLen), reqPos);
1098:
1099: // go through all the tokens till the required position
1100: while (syntax.nextToken() != null) {
1101: }
1102:
1103: syntax
1104: .relocate(slot.array, slot.offset + prepareLen, reqLen,
1105: forceNotLastBuffer ? false : forceLastBuffer
1106: || (reqPos + reqLen >= docLen), reqPos
1107: + reqLen);
1108: }
1109:
1110: void prepareSyntax(final SyntaxSeg.Slot slot, final Syntax syntax,
1111: MarkFactory.SyntaxMark leftSyntaxMark, final int reqPos,
1112: int reqLen, boolean forceLastBuffer)
1113: throws BadLocationException {
1114: prepareSyntax(slot, syntax, leftSyntaxMark, reqPos, reqLen,
1115: forceLastBuffer, false);
1116: }
1117:
1118: /**
1119: * Load the line info into lineCache[0]. The line offset must be >= 0.
1120: *
1121: * @return BOL of loaded line or -1 if line offset is too high
1122: */
1123: private int cacheLoadLine(int line) {
1124: if (line == 0) { // handle line 0 specially
1125: try {
1126: int eol = cache.find(eolFwdFinder, 0, -1, null);
1127: if (eol == -1) {
1128: eol = docLen;
1129: }
1130: lineCache[0].fill(0, eol, 0);
1131: } catch (BadLocationException e) {
1132: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
1133: e.printStackTrace();
1134: }
1135: return -1; // invalid offset
1136: }
1137: return 0;
1138: }
1139:
1140: int markLine;
1141: int markPos;
1142: try {
1143: Mark mark = marks.getMarkFromLine(line - 1);
1144: markPos = mark.getOffset();
1145: markLine = mark.getLine();
1146: } catch (InvalidMarkException e) {
1147: return cacheLoadLine(line); // try again
1148: }
1149:
1150: try {
1151: beolLineFwdFinder.fwdLines = line - markLine;
1152: markPos = cache.find(beolLineFwdFinder, markPos, -1, null);
1153: int bolPos = beolLineFwdFinder.bolPos;
1154: if (bolPos == -1) { // wrong line
1155: return -1;
1156: }
1157: if (markPos == -1) { // correct eolPos
1158: markPos = docLen;
1159: }
1160: lineCache[0].fill(bolPos, markPos, line);
1161: return bolPos;
1162: } catch (BadLocationException e) {
1163: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
1164: e.printStackTrace();
1165: }
1166: return -1;
1167: }
1168: }
1169:
1170: /*
1171: * Load line when position is known. @return line offset of the loaded line
1172: */
1173: private int cacheLoadLineByPos(int pos) throws BadLocationException {
1174: int markPos;
1175: int markLine;
1176: try {
1177: Mark mark = marks.getLeftMark(pos, null);
1178: markPos = mark.getOffset();
1179: markLine = mark.getLine();
1180:
1181: } catch (InvalidMarkException e) {
1182: return cacheLoadLineByPos(pos); // recall
1183: }
1184:
1185: beolPosFwdFinder.tgtPos = pos;
1186: markPos = cache.find(beolPosFwdFinder, markPos, -1, null);
1187: if (markPos == -1) { // correct eolPos
1188: markPos = docLen;
1189: }
1190: int bolPos = beolPosFwdFinder.bolPos;
1191: if (bolPos == -1) { // mark was on the same line with pos
1192: if (pos > 0) {
1193: bolPos = cache.find(bolBwdFinder, pos, 0, null) + 1;
1194: } else {
1195: bolPos = 0;
1196: }
1197: }
1198: int line = markLine + beolPosFwdFinder.line;
1199: lineCache[0].fill(bolPos, markPos, line);
1200: return line;
1201: }
1202:
1203: /** Move the entry with some index to be the first in the array */
1204: private void cacheMoveFirst(int ind) {
1205: if (ind == 0) {
1206: return;
1207: }
1208: CacheEntry ent = lineCache[ind];
1209: System.arraycopy(lineCache, 0, lineCache, 1, ind);
1210: lineCache[0] = ent;
1211: }
1212:
1213: private void invalidateCache() {
1214: for (int i = 0; i < CACHE_LEN; i++) {
1215: lineCache[i].invalidate();
1216: }
1217: }
1218:
1219: /** Update cache after document change. */
1220: private void cacheUpdate(boolean remove, ModifyUndoEdit undoEdit) {
1221: int pos = undoEdit.getOffset();
1222: int len = undoEdit.getLength();
1223: int bolRemovalPos = -1;
1224: int eolRemovalPos = -1;
1225: for (int i = 0; i < CACHE_LEN; i++) {
1226: CacheEntry ent = lineCache[i];
1227: if (ent.line != -1) {
1228: if (!remove) { // insert done
1229: if (pos >= ent.bol) {
1230: if (pos <= ent.eol) { // change inside line
1231: int eolOffset = undoEdit.getFirstLFOffset();
1232: if (eolOffset == -1) { // no LF inside text
1233: ent.eol += len;
1234: } else { // at least one LF inside text
1235: ent.eol = eolOffset;
1236: }
1237: } else { // change after end -> do nothing
1238: }
1239: } else { // pos <= bol -> only move
1240: ent.update(len, undoEdit.getLFCount());
1241: }
1242: } else { // remove done
1243: if (pos + len >= ent.bol) {
1244: if (pos <= ent.eol) { // change before line end
1245: ent.line = undoEdit.getLine();
1246: if (pos + len > ent.eol) { // end of change after
1247: // EOL
1248: if (eolRemovalPos == -1) {
1249: try {
1250: eolRemovalPos = cache.find(
1251: eolFwdFinder, pos, -1,
1252: null);
1253: } catch (BadLocationException e) {
1254: if (Boolean
1255: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
1256: e.printStackTrace();
1257: }
1258: }
1259: if (eolRemovalPos == -1) {
1260: eolRemovalPos = docLen;
1261: }
1262: }
1263: ent.eol = eolRemovalPos;
1264: if (pos < ent.bol) { // change before line
1265: // begin
1266: if (bolRemovalPos == -1) {
1267: try {
1268: bolRemovalPos = cache.find(
1269: bolBwdFinder, pos,
1270: 0, null) + 1;
1271: } catch (BadLocationException e) {
1272: if (Boolean
1273: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
1274: e.printStackTrace();
1275: }
1276: }
1277: }
1278: ent.bol = bolRemovalPos;
1279: }
1280: } else { // end of change before EOL
1281: ent.eol -= len;
1282: if (pos < ent.bol) { // change before line
1283: // begin
1284: if (bolRemovalPos == -1) {
1285: try {
1286: bolRemovalPos = cache.find(
1287: bolBwdFinder, pos,
1288: 0, null) + 1;
1289: } catch (BadLocationException e) {
1290: if (Boolean
1291: .getBoolean("netbeans.debug.exceptions")) { // NOI18N
1292: e.printStackTrace();
1293: }
1294: }
1295: }
1296: ent.bol = bolRemovalPos;
1297: }
1298: }
1299: } else { // change after line end
1300: }
1301: } else { // pos + len <= bol -> only move
1302: ent.update(-len, -undoEdit.getLFCount());
1303: }
1304: }
1305: }
1306: }
1307: }
1308:
1309: /**
1310: * This mark renderer is used to cycle through syntax marks to update them.
1311: * It also helps to compute the mark position in the fast way.
1312: */
1313: class SyntaxUpdateRenderer extends DocMarks.Renderer {
1314:
1315: /** Document event for which the marks are updated */
1316: ModifyUndoEdit undoEdit;
1317:
1318: /** Whether removal instead of insert is being done */
1319: boolean remove;
1320:
1321: /** Slot for scanning the document */
1322: SyntaxSeg.Slot slot;
1323:
1324: /** Syntax for scanning the document */
1325: Syntax syntax;
1326:
1327: /** Get all syntax marks in a given range */
1328: public void render() {
1329: int markCnt = getMarkArrayLength();
1330: int pos = 0;
1331: Mark markArray[] = getMarkArray();
1332: int syntaxUpdatePos = -1;
1333: int index;
1334:
1335: if (leftUpdateMark != null) {
1336: try {
1337: index = getNextIndex(getMarkIndex(leftUpdateMark));
1338: pos = leftUpdateMark.getOffset();
1339: } catch (InvalidMarkException e) {
1340: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
1341: e.printStackTrace();
1342: }
1343: index = getNextIndex(-1);
1344: }
1345: } else {
1346: index = getNextIndex(-1);
1347: }
1348:
1349: int endPos = pos; // ending position of the rescanning
1350: if (!remove) { // insert done
1351: endPos = undoEdit.getOffset() + undoEdit.getLength(); // inserted
1352: // area
1353: // that
1354: // can
1355: // contain
1356: // new
1357: // marks
1358: }
1359:
1360: try {
1361: // initially load the slot
1362: slot.load(doc, pos - syntax.getPreScan(), endPos - pos
1363: + syntax.getPreScan());
1364:
1365: int lastPos = pos; // position of the last syntax mark scanned
1366: while (index < markCnt) { // possibly till end of mark array
1367: Mark mark = markArray[index];
1368: index = getNextIndex(index);
1369: pos = getMarkOffset(mark);
1370:
1371: if (mark instanceof MarkFactory.SyntaxMark) {
1372: MarkFactory.SyntaxMark syntaxMark = (MarkFactory.SyntaxMark) mark;
1373: int preScan = syntax.getPreScan();
1374: int loadPos = lastPos - preScan;
1375: int scanLen = pos - loadPos;
1376:
1377: if (!slot.isAreaInside(doc, loadPos, scanLen)) {
1378: // load the whole area into syntax segment
1379: int loadSize = Math.min(docLen - loadPos,
1380: Math.max(scanLen,
1381: SYNTAX_UPDATE_BATCH_SIZE));
1382: slot.load(doc, loadPos, loadSize);
1383: }
1384:
1385: // this load should transfer no data but will adjust
1386: // scanning offset
1387: slot.load(doc, loadPos, scanLen);
1388:
1389: // Relocate scanning for the good offsets
1390: syntax.relocate(slot.array, slot.offset
1391: + preScan, scanLen - preScan,
1392: (pos == docLen), pos);
1393: // System.out.println("DocOp.java:600 syntax renderer:
1394: // scan relocated to buffer='" +
1395: // EditorDebug.debugChars(slot.array, slot.offset +
1396: // preScan, scanLen - preScan) + "', slot.offset=" +
1397: // slot.offset + ", scanLen=" + scanLen + ", preScan=" +
1398: // preScan); // NOI18N
1399:
1400: while (syntax.nextToken() != null) {
1401: }
1402:
1403: if (syntax.compareState(syntaxMark
1404: .getStateInfo()) == Syntax.EQUAL_STATE) {
1405: if (syntaxUpdatePos < 0) {
1406: syntaxUpdatePos = pos;
1407: }
1408: if (pos >= endPos && syntaxMark != eolMark) {
1409: break;
1410: }
1411: } else { // state stored in mark is different
1412: syntaxUpdatePos = -1;
1413: syntaxMark.updateStateInfo(syntax);
1414: }
1415:
1416: lastPos = pos;
1417: }
1418: }
1419:
1420: // Update syntax update position
1421: if (syntaxUpdatePos < 0) {
1422: syntaxUpdatePos = docLen;
1423: }
1424:
1425: undoEdit.setSyntaxUpdateOffset(adjustNextBOL(
1426: syntaxUpdatePos, true));
1427:
1428: } catch (BadLocationException e) {
1429: if (Boolean.getBoolean("netbeans.debug.exceptions")) { // NOI18N
1430: e.printStackTrace();
1431: }
1432: }
1433: }
1434: }
1435:
1436: /**
1437: * Cache entry for line and column information caching. Currently each entry
1438: * caches begin and end of line and corresponding line number.
1439: */
1440: private static class CacheEntry {
1441:
1442: /** Begin of line pos */
1443: int bol = -1;
1444:
1445: /** End of line pos */
1446: int eol = -1;
1447:
1448: /** Line offset */
1449: int line = -1;
1450:
1451: void fill(int bol, int eol, int line) {
1452: this .bol = bol;
1453: this .eol = eol;
1454: this .line = line;
1455: }
1456:
1457: void update(int deltaLen, int deltaLFCount) {
1458: this .bol += deltaLen;
1459: this .eol += deltaLen;
1460: this .line += deltaLFCount;
1461: }
1462:
1463: void invalidate() {
1464: bol = eol = line = -1;
1465: }
1466:
1467: public String toString() {
1468: return "line=" + line + ", bol=" + bol + ", eol=" + eol; // NOI18N
1469: }
1470:
1471: }
1472:
1473: /** Dump the line cache */
1474: public String cacheToString() {
1475: StringBuffer sb = new StringBuffer();
1476: for (int i = 0; i < CACHE_LEN; i++) {
1477: sb.append("\ncache[" + i + "]: " + lineCache[i].toString()); // NOI18N
1478: }
1479: return sb.toString();
1480: }
1481:
1482: public String toString() {
1483: return "length()=" + length() + ", cache.getDocLength()="
1484: + cache.getDocLength()
1485: + "\nstatCacheHit="
1486: + statCacheHit // NOI18N
1487: + ", statCacheMiss="
1488: + statCacheMiss // NOI18N
1489: + ", Line cache hit ratio="
1490: + (Math.round(1000.0 * statCacheHit / (statCacheHit // NOI18N
1491: + statCacheMiss)) / 10d) + cacheToString();
1492: }
1493:
1494: public String markPlanesToString(Class markClasses[],
1495: char markChars[]) {
1496: // !!! return marks.planesToString(markClasses, markChars);
1497: return "";
1498: }
1499:
1500: public String infoToString() {
1501: return "\n------------------------------ Statistics ------------------------------\n" // NOI18N
1502: + "cacheSupport: statCharsRead="
1503: + cacheSupport.statCharsRead // NOI18N
1504: + ", statCharsWritten=" + cacheSupport.statCharsWritten // NOI18N
1505: + "\nCache: " + cache // NOI18N
1506: + "\nMarks: " + marks // NOI18N
1507: + "\nDocOp: " + this ; // NOI18N
1508: }
1509:
1510: /**
1511: * UnoableEdit created for inserts and removals. The <tt>remove</tt> flag
1512: * determines whether it's insert or removal
1513: */
1514: class ModifyUndoEdit extends AbstractUndoableEdit {
1515:
1516: /** Whether removal was done instead of insertion */
1517: boolean remove;
1518:
1519: /** Offset where the characters was inserted */
1520: private int offset;
1521:
1522: /**
1523: * The inserted characters. If the string was used for the insertion
1524: * this will be lazily initialized.
1525: */
1526: private char[] chars;
1527:
1528: /**
1529: * The inserted string. If the character buffer was used for the
1530: * insertion this will be lazily initialized.
1531: */
1532: private String text;
1533:
1534: /**
1535: * The number of the '\n' (line-feed) characters contained in the
1536: * inserted text. It's lazily initialized.
1537: */
1538: private int lfCount = -1;
1539:
1540: /** Line offset of the insert/removal */
1541: private int line;
1542:
1543: /** Offset of the end of the syntax updating */
1544: private int syntaxUpdateOffset;
1545:
1546: ModifyUndoEdit(boolean remove, int offset, char[] chars) {
1547: this .remove = remove;
1548: this .offset = offset;
1549: this .chars = chars;
1550: try {
1551: this .line = DocOp.this .getLine(offset);
1552: } catch (BadLocationException e) {
1553: }
1554: }
1555:
1556: ModifyUndoEdit(boolean remove, int offset, String text) {
1557: this .remove = remove;
1558: this .offset = offset;
1559: this .text = text;
1560: try {
1561: this .line = DocOp.this .getLine(offset);
1562: } catch (BadLocationException e) {
1563: }
1564: }
1565:
1566: boolean isInsert() {
1567: return !remove;
1568: }
1569:
1570: boolean isRemove() {
1571: return remove;
1572: }
1573:
1574: final int getOffset() {
1575: return offset;
1576: }
1577:
1578: /** Get the length of the inserted/removed text */
1579: int getLength() {
1580: return (chars != null) ? chars.length : text.length();
1581: }
1582:
1583: /**
1584: * Get the inserted text. If the character buffer was used for the
1585: * insertion then the appropriate variable will be lazily initialized
1586: * first.
1587: */
1588: String getText() {
1589: if (text == null) {
1590: text = new String(chars);
1591: }
1592: return text;
1593: }
1594:
1595: /**
1596: * The inserted characters. If the string was used for the insertion
1597: * then the appropriate variable will be lazily initialized first.
1598: */
1599: char[] getChars() {
1600: if (chars == null) {
1601: chars = text.toCharArray();
1602: }
1603: return chars;
1604: }
1605:
1606: /**
1607: * Whether the text is valid instead of chars. This method helps to
1608: * avoid the possible conversion from chars to string.
1609: */
1610: boolean isTextValid() {
1611: return (text != null);
1612: }
1613:
1614: /**
1615: * Get the number of the '\n' (line-feed) characters contained in the
1616: * inserted text. It's lazily initialized if necessary.
1617: */
1618: int getLFCount() {
1619: if (lfCount == -1) {
1620: if (chars != null) { // chars valid valid
1621: lfCount = Analyzer.getLFCount(chars);
1622: } else { // string valid
1623: lfCount = Analyzer.getLFCount(text);
1624: }
1625: }
1626: return lfCount;
1627: }
1628:
1629: /**
1630: * Get the document offset of the first LF contained in the
1631: * inserted/removed text or -1 for no LFs. This value is not cached.
1632: */
1633: int getFirstLFOffset() {
1634: if (getLFCount() <= 0) {
1635: return -1;
1636: }
1637:
1638: int flfOffset;
1639: if (chars != null) { // chars valid valid
1640: flfOffset = Analyzer.findFirstLFOffset(chars, 0,
1641: chars.length);
1642: } else { // string valid
1643: flfOffset = Analyzer.findFirstLFOffset(text);
1644: }
1645:
1646: if (flfOffset >= 0) {
1647: flfOffset += offset; // shift by the insert/update offset
1648: }
1649:
1650: return flfOffset;
1651: }
1652:
1653: int getLine() {
1654: return line;
1655: }
1656:
1657: int getSyntaxUpdateOffset() {
1658: return syntaxUpdateOffset;
1659: }
1660:
1661: void setSyntaxUpdateOffset(int syntaxUpdateOffset) {
1662: this .syntaxUpdateOffset = syntaxUpdateOffset;
1663: }
1664:
1665: public void undo() throws CannotUndoException {
1666: super .undo();
1667: try {
1668: if (remove) {
1669: insertEdit(this );
1670: } else { // insertion
1671: removeEdit(this );
1672: }
1673: } catch (BadLocationException bl) {
1674: throw new CannotUndoException();
1675: }
1676: }
1677:
1678: public void redo() throws CannotRedoException {
1679: super .redo();
1680: try {
1681: if (remove) {
1682: removeEdit(this );
1683: } else { // insertion
1684: insertEdit(this );
1685: }
1686: } catch (BadLocationException bl) {
1687: throw new CannotRedoException();
1688: }
1689: }
1690:
1691: }
1692:
1693: }
|