1 19 20 package org.netbeans.editor; 21 22 import java.util.Collections ; 23 import java.util.List ; 24 import java.util.ArrayList ; 25 import javax.swing.event.DocumentEvent ; 26 import javax.swing.text.AbstractDocument ; 27 import javax.swing.text.Element ; 28 import javax.swing.text.Document ; 29 import javax.swing.text.AttributeSet ; 30 import javax.swing.text.Position ; 31 import javax.swing.undo.AbstractUndoableEdit ; 32 import javax.swing.undo.CannotRedoException ; 33 import javax.swing.undo.CannotUndoException ; 34 import javax.swing.undo.UndoableEdit ; 35 import javax.swing.text.BadLocationException ; 36 import javax.swing.text.Segment ; 37 import javax.swing.text.StyleContext ; 38 import org.openide.ErrorManager; 39 40 59 60 final class FixLineSyntaxState { 61 62 private static final boolean debug = false; 63 64 private final DocumentEvent evt; 65 66 private int syntaxUpdateOffset; 67 68 private List syntaxUpdateTokenList = Collections.EMPTY_LIST; 69 70 71 FixLineSyntaxState(DocumentEvent evt) { 72 this.evt = evt; 73 } 74 75 final int getSyntaxUpdateOffset() { 76 return syntaxUpdateOffset; 77 } 78 79 final List getSyntaxUpdateTokenList() { 80 return syntaxUpdateTokenList; 81 } 82 83 84 static void invalidateAllSyntaxStateInfos(BaseDocument doc) { 85 LineRootElement lineRoot = getLineRoot(doc); 86 int elemCount = lineRoot.getElementCount(); 87 for (int i = elemCount - 1; i >= 0; i--) { 88 LineElement line = (LineElement)lineRoot.getElement(i); 89 line.clearSyntaxStateInfo(); 90 } 91 } 92 93 111 static void prepareSyntax(BaseDocument doc, Segment text, Syntax syntax, int reqPos, int reqLen, 112 boolean forceLastBuffer, boolean forceNotLastBuffer) throws BadLocationException { 113 114 if (reqPos < 0 || reqLen < 0 || reqPos + reqLen > doc.getLength()) { 115 throw new BadLocationException ("reqPos=" + reqPos + ", reqLen=" + reqLen + ", doc.getLength()=" + doc.getLength(), -1 ); 119 } 120 121 LineRootElement lineRoot = getLineRoot(doc); 123 int reqPosLineIndex = lineRoot.getElementIndex(reqPos); 124 Element reqPosLineElem = lineRoot.getElement(reqPosLineIndex); 125 Syntax.StateInfo stateInfo = getValidSyntaxStateInfo(doc, reqPosLineIndex); 126 int lineStartOffset = reqPosLineElem.getStartOffset(); 127 int preScan = (stateInfo != null) ? stateInfo.getPreScan() : 0; 128 if (preScan > lineStartOffset) { 136 if (debug) { 138 System.err.println(lineInfosToString(doc)); 139 } 140 preScan = lineStartOffset; } 142 int intraLineLength = reqPos - lineStartOffset; 144 doc.getText(lineStartOffset - preScan, preScan + intraLineLength + reqLen, text); 145 text.offset += preScan; 146 text.count -= preScan; 147 148 syntax.load(stateInfo, text.array, text.offset, intraLineLength, false, reqPos); 150 152 while (syntax.nextToken() != null) { } 154 155 text.offset += intraLineLength; 156 text.count -= intraLineLength; 157 boolean forceLB = forceNotLastBuffer 158 ? false 159 : (forceLastBuffer || (reqPos + reqLen >= doc.getLength())); 160 161 syntax.relocate(text.array, text.offset, text.count, forceLB, reqPos + reqLen); 162 } 163 164 176 private static Syntax.StateInfo getValidSyntaxStateInfo( 177 BaseDocument doc, int lineIndex) throws BadLocationException { 178 179 if (lineIndex == 0) { 180 return null; 181 } 182 183 LineRootElement lineRoot = getLineRoot(doc); 184 LineElement lineElem = (LineElement)lineRoot.getElement(lineIndex); 185 Syntax.StateInfo stateInfo = lineElem.getSyntaxStateInfo(); 186 187 if (lineIndex > 0 && stateInfo == null) { int validLineIndex = lineIndex - 1; LineElement validLineElem = null; 191 while (validLineIndex > 0) { 192 validLineElem = (LineElement)lineRoot.getElement(validLineIndex); 193 stateInfo = validLineElem.getSyntaxStateInfo() ; 194 if (stateInfo != null) { 195 break; 196 } 197 validLineIndex--; 198 } 199 200 207 208 Segment text = DocumentUtilities.SEGMENT_CACHE.getSegment(); 209 try { 210 Syntax syntax = doc.getFreeSyntax(); 211 try { 212 int lineElemOffset = lineElem.getStartOffset(); 213 int preScan = 0; 214 int validLineOffset; 215 if (validLineIndex > 0) { 216 validLineOffset = validLineElem.getStartOffset(); 217 preScan = stateInfo.getPreScan(); 218 } else { validLineOffset = 0; 220 stateInfo = null; 221 } 222 223 doc.getText(validLineOffset - preScan, 224 (lineElemOffset - validLineOffset) + preScan, 225 text 226 ); 227 228 text.offset += preScan; 229 text.count -= preScan; 230 233 234 syntax.load(stateInfo, text.array, text.offset, 235 text.count, false, lineElemOffset); 236 237 int textEndOffset = text.offset + text.count; 238 do { 239 validLineIndex++; 240 validLineElem = (LineElement)lineRoot.getElement(validLineIndex); 241 int scanLength = validLineOffset; validLineOffset = validLineElem.getStartOffset(); 243 scanLength = validLineOffset - scanLength; 244 syntax.relocate(text.array, syntax.getOffset(), 245 scanLength, 246 false, validLineOffset 247 ); 248 249 while (syntax.nextToken() != null) { 250 } 252 253 validLineElem.updateSyntaxStateInfo(syntax); 254 255 } while (validLineIndex != lineIndex); 256 257 } finally { 258 doc.releaseSyntax(syntax); 259 } 260 } finally { 261 DocumentUtilities.SEGMENT_CACHE.releaseSegment(text); 262 } 263 } 264 265 return lineElem.getSyntaxStateInfo(); 266 } 267 268 void update(boolean undo) { 269 SyntaxUpdateTokens suTokens = (SyntaxUpdateTokens)evt.getDocument().getProperty(SyntaxUpdateTokens.class); 270 if (suTokens != null) { 271 suTokens.syntaxUpdateStart(); 272 } 273 try { 274 syntaxUpdateOffset = fixSyntaxStateInfos(undo); 276 } finally { 277 if (suTokens != null) { 278 syntaxUpdateTokenList = Collections.unmodifiableList( 279 new ArrayList (suTokens.syntaxUpdateEnd())); 280 } 281 } 282 } 283 284 290 private int fixSyntaxStateInfos(boolean undo) { 291 int offset = evt.getOffset(); 292 if (offset < 0) { 293 throw new IllegalStateException ("offset=" + offset); } 295 296 BaseDocument doc = (BaseDocument)evt.getDocument(); 297 LineRootElement lineRoot = getLineRoot(doc); 298 int lineCount = lineRoot.getElementCount(); 299 DocumentEvent.ElementChange lineChange = evt.getChange(lineRoot); 300 int lineIndex; 301 if (lineChange != null) { 302 lineIndex = lineChange.getIndex(); 303 } else { lineIndex = lineRoot.getElementIndex(offset); 305 } 306 307 int addedLinesCount = (lineChange != null) 313 ? lineChange.getChildrenAdded().length 314 : 0; 315 316 int maybeMatchLineIndex = lineIndex + addedLinesCount + 1; 317 if (lineIndex > 0) { 318 lineIndex--; } 320 if (lineIndex + 1 == lineCount) { return doc.getLength(); 322 } 323 324 LineElement lineElem = (LineElement)lineRoot.getElement(lineIndex); 325 326 Segment text = DocumentUtilities.SEGMENT_CACHE.getSegment(); 327 try { 328 Syntax.StateInfo stateInfo = getValidSyntaxStateInfo(doc, lineIndex); 329 int lineStartOffset = lineElem.getStartOffset(); 330 int preScan = (stateInfo != null) ? stateInfo.getPreScan() : 0; 331 332 if (debug) { 333 System.err.println("fixSyntaxStateInfos(): lineIndex=" + lineIndex 334 + ", maybeMatch=" + maybeMatchLineIndex + ", lineStartOffset=" + lineStartOffset + ", preScan=" + preScan + ", addedLines=" + addedLinesCount + ", lineCount=" + lineCount ); 340 } 341 342 Syntax syntax = doc.getFreeSyntax(); 343 try { 344 lineIndex++; LineElement nextLineElem = (LineElement)lineRoot.getElement(lineIndex); int nextLineStartOffset = nextLineElem.getStartOffset(); 347 348 doc.getText(lineStartOffset - preScan, 349 (nextLineStartOffset - lineStartOffset) + preScan, text); 350 351 text.offset += preScan; 352 text.count -= preScan; 353 354 syntax.load(stateInfo, text.array, text.offset, text.count, 355 false, nextLineStartOffset); 356 357 SyntaxUpdateTokens suTokens = (SyntaxUpdateTokens)doc.getProperty( 358 SyntaxUpdateTokens.class); 359 369 int textLength = -1; 370 int textStartOffset = -1; 371 int textBufferStartOffset = -1; 372 while (true) { 373 int tbStartOffset = lineStartOffset - text.offset; 375 TokenID tokenID = syntax.nextToken(); 376 while (tokenID != null) { 377 if (suTokens != null) { 378 suTokens.syntaxUpdateToken(tokenID, 380 syntax.getTokenContextPath(), 381 tbStartOffset + syntax.getTokenOffset(), 382 syntax.getTokenLength() 383 ); 384 } 385 386 tokenID = syntax.nextToken(); 387 } 388 389 stateInfo = nextLineElem.getSyntaxStateInfo(); if (lineIndex >= maybeMatchLineIndex) { 391 if (stateInfo != null 392 && syntax.compareState(stateInfo) == Syntax.EQUAL_STATE 393 ) { 394 lineStartOffset = nextLineStartOffset; 397 if (debug) { 398 System.err.println("fixSyntaxStateInfos(): MATCHED at lineIndex=" 399 + lineIndex 400 + ": preScan=" + syntax.getPreScan() + ", return lineStartOffset=" + lineStartOffset ); 403 } 404 break; 405 } 406 } 407 408 nextLineElem.updateSyntaxStateInfo(syntax); 409 if (debug) { 410 System.err.println("fixSyntaxStateInfos(): Updated info at line " 411 + lineIndex + " from " + stateInfo + " to " + nextLineElem.getSyntaxStateInfo()); } 414 415 lineIndex++; 416 if (lineIndex >= lineCount) { return doc.getLength(); 418 } 419 420 lineElem = nextLineElem; 421 lineStartOffset = nextLineStartOffset; 422 423 nextLineElem = (LineElement)lineRoot.getElement(lineIndex); 424 nextLineStartOffset = nextLineElem.getStartOffset(); 425 426 preScan = syntax.getPreScan(); 427 428 433 434 int requestedTextLength = (nextLineStartOffset - lineStartOffset) + preScan; 435 if (textLength == -1) { textStartOffset = lineStartOffset - preScan; 438 textLength = requestedTextLength; 439 doc.getText(textStartOffset, textLength, text); 440 textBufferStartOffset = textStartOffset - text.offset; 441 442 } else { 444 if (lineStartOffset - preScan < textStartOffset 445 || nextLineStartOffset > textStartOffset + textLength 446 ) { textLength = Math.max(textLength, requestedTextLength); 448 textLength *= 2; textStartOffset = lineStartOffset - preScan; 450 textLength = Math.min(textStartOffset + textLength, 451 doc.getLength()) - textStartOffset; 452 doc.getText(textStartOffset, textLength, text); 453 textBufferStartOffset = textStartOffset - text.offset; 454 456 } else { text.offset = lineStartOffset - preScan - textBufferStartOffset; 458 } 459 text.count = requestedTextLength; 460 } 461 462 463 474 475 text.offset += preScan; 476 text.count -= preScan; 477 478 syntax.relocate(text.array, text.offset, text.count, 479 false, nextLineStartOffset); 480 } 481 482 return lineStartOffset; 483 484 } finally { 485 doc.releaseSyntax(syntax); 486 487 } 491 } catch (BadLocationException e) { 492 throw new IllegalStateException (e); 493 } finally { 494 DocumentUtilities.SEGMENT_CACHE.releaseSegment(text); 495 } 496 } 497 498 505 static int getTokenSafeOffset(BaseDocument doc, int offset) { 506 if (offset == 0) { return offset; 508 } 509 510 try { 511 LineRootElement lineRoot = getLineRoot(doc); 512 int lineIndex = lineRoot.getElementIndex(offset); 513 Element lineElem = lineRoot.getElement(lineIndex); 514 int lineStartOffset = lineElem.getStartOffset(); 515 Syntax.StateInfo stateInfo = getValidSyntaxStateInfo(doc, lineIndex); 516 if (offset == lineStartOffset && stateInfo.getPreScan() == 0) { 517 return offset; 519 } 520 521 int lineCount = lineRoot.getElementCount(); 524 while (++lineIndex < lineCount) { 525 lineElem = lineRoot.getElement(lineIndex); 526 stateInfo = getValidSyntaxStateInfo(doc, lineIndex); 527 lineStartOffset = lineElem.getStartOffset(); 528 if (lineStartOffset - stateInfo.getPreScan() >= offset) { 529 return lineStartOffset; 530 } 531 } 532 } catch (BadLocationException e) { 533 throw new IllegalStateException (e.toString()); 534 } 535 536 return doc.getLength(); 537 } 538 539 private static LineRootElement getLineRoot(Document doc) { 540 return (LineRootElement)doc.getDefaultRootElement(); 541 } 542 543 private static void checkConsistency(Document doc) { 544 LineRootElement lineRoot = getLineRoot(doc); 546 int lineCount = lineRoot.getElementCount(); 547 for (int i = 1; i < lineCount; i++) { LineElement elem = (LineElement)lineRoot.getElement(i); 549 assert (elem.getSyntaxStateInfo() != null) : "Syntax state null at line " + i + " of " + lineCount; } 551 } 552 553 public static String lineInfosToString(Document doc) { 554 StringBuffer sb = new StringBuffer (); 555 LineRootElement lineRoot = getLineRoot(doc); 556 int lineCount = lineRoot.getElementCount(); 557 for (int i = 0; i < lineCount; i++) { 558 LineElement elem = (LineElement)lineRoot.getElement(i); 559 sb.append("[" + i + "]: lineStartOffset=" + elem.getStartOffset() + ", info: " + elem.getSyntaxStateInfo() + "\n"); } 562 return sb.toString(); 563 } 564 565 UndoableEdit createBeforeLineUndo() { 566 return new BeforeLineUndo(); 567 } 568 569 UndoableEdit createAfterLineUndo() { 570 return new AfterLineUndo(); 571 } 572 573 final class BeforeLineUndo extends AbstractUndoableEdit { 574 575 FixLineSyntaxState getMaster() { 576 return FixLineSyntaxState.this; 577 } 578 579 public void undo() throws CannotUndoException { 580 update(true); 581 super.undo(); 582 } 583 584 } 585 586 final class AfterLineUndo extends AbstractUndoableEdit { 587 588 public void redo() throws CannotRedoException { 589 update(false); 590 super.redo(); 591 } 592 593 } 594 } 595 | Popular Tags |