int getVisualLineWidth( VisualLinesIterator visualLinesIterator, @Nullable Runnable quickEvaluationListener) { assert !visualLinesIterator.atEnd(); int visualLine = visualLinesIterator.getVisualLine(); FoldRegion[] topLevelRegions = myEditor.getFoldingModel().fetchTopLevel(); if (quickEvaluationListener != null && (topLevelRegions == null || topLevelRegions.length == 0) && myEditor.getSoftWrapModel().getRegisteredSoftWraps().isEmpty() && !myView.getTextLayoutCache().hasCachedLayoutFor(visualLine)) { // fast path - speeds up editor opening quickEvaluationListener.run(); return myView .getLogicalPositionCache() .offsetToLogicalColumn( visualLine, myDocument.getLineEndOffset(visualLine) - myDocument.getLineStartOffset(visualLine)) * myView.getMaxCharWidth(); } float x = 0; int maxOffset = visualLinesIterator.getVisualLineStartOffset(); for (VisualLineFragmentsIterator.Fragment fragment : VisualLineFragmentsIterator.create(myView, visualLinesIterator, quickEvaluationListener)) { x = fragment.getEndX(); maxOffset = Math.max(maxOffset, fragment.getMaxOffset()); } if (myEditor.getSoftWrapModel().getSoftWrap(maxOffset) != null) { x += myEditor .getSoftWrapModel() .getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); } return (int) x; }
@Nullable("null means we were unable to calculate") LogicalPosition hostToInjectedInVirtualSpace(@NotNull LogicalPosition hPos) { // beware the virtual space int hLineStartOffset = hPos.line >= myDelegate.getLineCount() ? myDelegate.getTextLength() : myDelegate.getLineStartOffset(hPos.line); int iLineStartOffset = hostToInjected(hLineStartOffset); int iLine = getLineNumber(iLineStartOffset); synchronized (myLock) { for (int i = myShreds.size() - 1; i >= 0; i--) { PsiLanguageInjectionHost.Shred shred = myShreds.get(i); if (!shred.isValid()) continue; Segment hostRangeMarker = shred.getHostRangeMarker(); if (hostRangeMarker == null) continue; int hShredEndOffset = hostRangeMarker.getEndOffset(); int hShredStartOffset = hostRangeMarker.getStartOffset(); int hShredStartLine = myDelegate.getLineNumber(hShredStartOffset); int hShredEndLine = myDelegate.getLineNumber(hShredEndOffset); if (hShredStartLine <= hPos.line && hPos.line <= hShredEndLine) { int hColumnOfShredEnd = hShredEndOffset - hLineStartOffset; int iColumnOfShredEnd = hostToInjected(hShredEndOffset) - iLineStartOffset; int iColumn = iColumnOfShredEnd + hPos.column - hColumnOfShredEnd; return new LogicalPosition(iLine, iColumn); } } } return null; }
private void doSync( @NotNull final PsiTreeChangeEvent event, boolean force, @NotNull final DocSyncAction syncAction) { if (!toProcessPsiEvent()) return; final PsiFile psiFile = event.getFile(); if (!(psiFile instanceof PsiFileEx) || !((PsiFileEx) psiFile).isContentsLoaded()) return; final DocumentEx document = getCachedDocument(psiFile, force); if (document == null) return; performAtomically( psiFile, new Runnable() { @Override public void run() { syncAction.syncDocument(document, (PsiTreeChangeEventImpl) event); } }); final boolean insideTransaction = myTransactionsMap.containsKey(document); if (!insideTransaction) { document.setModificationStamp(psiFile.getViewProvider().getModificationStamp()); } }
private RangeMarkerImpl( @NotNull DocumentEx document, int start, int end, boolean register, boolean greedyToLeft, boolean greedyToRight) { if (start < 0) { throw new IllegalArgumentException("Wrong start: " + start + "; end=" + end); } if (end > document.getTextLength()) { throw new IllegalArgumentException( "Wrong end: " + end + "; document length=" + document.getTextLength() + "; start=" + start); } if (start > end) { throw new IllegalArgumentException("start > end: start=" + start + "; end=" + end); } myDocument = document; myId = counter.next(); if (register) { registerInTree(start, end, greedyToLeft, greedyToRight, 0); } }
@Override public final void documentChanged(@NotNull DocumentEvent e) { int oldStart = intervalStart(); int oldEnd = intervalEnd(); int docLength = myDocument.getTextLength(); if (!isValid()) { LOG.error( "Invalid range marker " + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + ". Event = " + e + ". Doc length=" + docLength + "; " + getClass()); return; } if (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength - e.getNewLength() + e.getOldLength()) { LOG.error( "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + " is invalid before update. Event = " + e + ". Doc length=" + docLength + "; " + getClass()); invalidate(e); return; } changedUpdateImpl(e); if (isValid() && (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength)) { LOG.error( "Update failed. Event = " + e + ". " + "old doc length=" + docLength + "; real doc length = " + myDocument.getTextLength() + "; " + getClass() + "." + " After update: '" + this + "'"); invalidate(e); } }
public void testSwap() { RangeMarkerEx marker1 = createMarker("012345678901234567", 5, 6); DocumentEx document = (DocumentEx) marker1.getDocument(); document.createRangeMarker(3, 5); document.createRangeMarker(6, 7); document.createRangeMarker(4, 4); marker1.dispose(); }
public void testNested3() { RangeMarker marker1 = createMarker("01[23]4567890123"); DocumentEx document = (DocumentEx) marker1.getDocument(); RangeMarker marker2 = document.createRangeMarker(9, 11); RangeMarker marker3 = document.createRangeMarker(1, 12); marker3.dispose(); document.deleteString(marker1.getEndOffset(), marker2.getStartOffset()); }
public void testSoftWrapModeUpdateDuringBulkModeChange() throws Exception { initText("long long line<caret>"); configureSoftWraps(12); DocumentEx document = (DocumentEx) myEditor.getDocument(); document.setInBulkUpdate(true); document.replaceString(4, 5, "-"); document.setInBulkUpdate(false); assertEquals(new VisualPosition(1, 5), myEditor.getCaretModel().getVisualPosition()); }
public void testUpdatingCaretPositionAfterBulkMode() throws Exception { initText("a<caret>bc"); DocumentEx document = (DocumentEx) myEditor.getDocument(); document.setInBulkUpdate(true); document.insertString( 0, "\n "); // we're changing number of visual lines, and invalidating text layout for caret // line document.setInBulkUpdate(false); checkResultByText("\n a<caret>bc"); }
public void testMoveTextRetargetsMarkers() throws Exception { RangeMarkerEx marker1 = createMarker("01234567890", 1, 3); DocumentEx document = (DocumentEx) marker1.getDocument(); RangeMarker marker2 = document.createRangeMarker(2, 4); document.moveText(0, 5, 8); assertEquals("56701234890", document.getText()); assertValidMarker(marker1, 4, 6); assertValidMarker(marker2, 5, 7); }
@Override @Nullable public Document getDocument(@NotNull final VirtualFile file) { DocumentEx document = (DocumentEx) getCachedDocument(file); if (document == null) { if (file.isDirectory() || isBinaryWithoutDecompiler(file) || SingleRootFileViewProvider.isTooLargeForContentLoading(file)) { return null; } final CharSequence text = LoadTextUtil.loadText(file); synchronized (lock) { document = (DocumentEx) getCachedDocument(file); if (document != null) return document; // Double checking document = (DocumentEx) createDocument(text); document.setModificationStamp(file.getModificationStamp()); final FileType fileType = file.getFileType(); document.setReadOnly(!file.isWritable() || fileType.isBinary()); file.putUserData(DOCUMENT_KEY, new WeakReference<Document>(document)); document.putUserData(FILE_KEY, file); if (!(file instanceof LightVirtualFile || file.getFileSystem() instanceof DummyFileSystem)) { document.addDocumentListener( new DocumentAdapter() { @Override public void documentChanged(DocumentEvent e) { final Document document = e.getDocument(); myUnsavedDocuments.add(document); final Runnable currentCommand = CommandProcessor.getInstance().getCurrentCommand(); Project project = currentCommand == null ? null : CommandProcessor.getInstance().getCurrentCommandProject(); String lineSeparator = CodeStyleFacade.getInstance(project).getLineSeparator(); document.putUserData(LINE_SEPARATOR_KEY, lineSeparator); // avoid documents piling up during batch processing if (areTooManyDocumentsInTheQueue(myUnsavedDocuments)) { saveAllDocumentsLater(); } } }); } } myMultiCaster.fileContentLoaded(file, document); } return document; }
public void testX() { RangeMarkerEx marker1 = createMarker(StringUtil.repeatSymbol(' ', 10), 3, 6); DocumentEx document = (DocumentEx) marker1.getDocument(); document.createRangeMarker(2, 3); document.createRangeMarker(3, 8); document.createRangeMarker(7, 9); RangeMarkerEx r1 = (RangeMarkerEx) document.createRangeMarker(6, 8); r1.dispose(); marker1.dispose(); }
public void testNoExceptionDuringBulkModeDocumentUpdate() throws Exception { initText("something"); DocumentEx document = (DocumentEx) myEditor.getDocument(); document.setInBulkUpdate(true); try { document.setText("something\telse"); } finally { document.setInBulkUpdate(false); } checkResultByText("something\telse"); }
public void testLL() { RangeMarker marker1 = createMarker("012345678901234567", 5, 6); DocumentEx document = (DocumentEx) marker1.getDocument(); document.createRangeMarker(4, 5); document.createRangeMarker(6, 7); document.createRangeMarker(0, 4); document.deleteString(1, 2); document.createRangeMarker(0, 7); document.createRangeMarker(0, 7); }
public void testMoveTextToTheBeginningRetargetsMarkers() throws Exception { RangeMarkerEx marker1 = createMarker("01234567890", 5, 5); DocumentEx document = (DocumentEx) marker1.getDocument(); RangeMarker marker2 = document.createRangeMarker(5, 7); document.moveText(4, 7, 1); assertEquals("04561237890", document.getText()); assertValidMarker(marker1, 2, 2); assertValidMarker(marker2, 2, 4); }
public void _testRandomAddRemove() { int N = 100; for (int ti = 0; ; ti++) { if (ti % 10000 == 0) System.out.println(ti); DocumentEx document = (DocumentEx) EditorFactory.getInstance().createDocument(StringUtil.repeatSymbol(' ', N)); Random gen = new Random(); List<Pair<RangeMarker, TextRange>> adds = new ArrayList<Pair<RangeMarker, TextRange>>(); List<Pair<RangeMarker, TextRange>> dels = new ArrayList<Pair<RangeMarker, TextRange>>(); try { for (int i = 0; i < 30; i++) { int x = gen.nextInt(N); int y = x + gen.nextInt(N - x); if (gen.nextBoolean()) { x = 0; y = document.getTextLength(); } RangeMarkerEx r = (RangeMarkerEx) document.createRangeMarker(x, y); adds.add(Pair.create((RangeMarker) r, TextRange.create(r))); } List<Pair<RangeMarker, TextRange>> candidates = new ArrayList<Pair<RangeMarker, TextRange>>(adds); while (!candidates.isEmpty()) { int size = candidates.size(); int x = gen.nextInt(size); Pair<RangeMarker, TextRange> c = candidates.remove(x); RangeMarkerEx r = (RangeMarkerEx) c.first; assertEquals(size - 1, candidates.size()); dels.add(c); r.dispose(); } } catch (AssertionError e) { String s = "adds: "; for (Pair<RangeMarker, TextRange> c : adds) { TextRange t = c.second; s += t.getStartOffset() + "," + t.getEndOffset() + ", "; } s += "\ndels: "; for (Pair<RangeMarker, TextRange> c : dels) { int index = adds.indexOf(c); assertSame(c, adds.get(index)); s += index + ", "; } System.err.println(s); throw e; } } }
private static void doCommitTransaction( @NotNull Document document, @NotNull DocumentChangeTransaction documentChangeTransaction) { DocumentEx ex = (DocumentEx) document; ex.suppressGuardedExceptions(); try { boolean isReadOnly = !document.isWritable(); ex.setReadOnly(false); final Set<Pair<MutableTextRange, StringBuffer>> affectedFragments = documentChangeTransaction.getAffectedFragments(); for (final Pair<MutableTextRange, StringBuffer> pair : affectedFragments) { final StringBuffer replaceBuffer = pair.getSecond(); final MutableTextRange range = pair.getFirst(); if (replaceBuffer.length() == 0) { ex.deleteString(range.getStartOffset(), range.getEndOffset()); } else if (range.getLength() == 0) { ex.insertString(range.getStartOffset(), replaceBuffer); } else { ex.replaceString(range.getStartOffset(), range.getEndOffset(), replaceBuffer); } } ex.setReadOnly(isReadOnly); // if(documentChangeTransaction.getChangeScope() != null) { // // LOG.assertTrue(document.getText().equals(documentChangeTransaction.getChangeScope().getText()), // "Psi to document synchronization failed (send to IK)"); // } } finally { ex.unSuppressGuardedExceptions(); } }
void invalidateRange(int startOffset, int endOffset) { if (myDocument.isInBulkUpdate()) return; if (myDocument.isInEventsHandling()) { myDocumentChangeStartOffset = Math.min(myDocumentChangeStartOffset, startOffset); myDocumentChangeEndOffset = Math.max(myDocumentChangeEndOffset, endOffset); } else if (myFoldingChangeEndOffset != Integer.MIN_VALUE) { // during batch folding processing we delay invalidation requests, as we cannot perform // coordinate conversions immediately myFoldingChangeStartOffset = Math.min(myFoldingChangeStartOffset, startOffset); myFoldingChangeEndOffset = Math.max(myFoldingChangeEndOffset, endOffset); } else { doInvalidateRange(startOffset, endOffset); } }
/** * @return position <code>x</code> for which <code> * myDocument.getText().substring(x, startOffset)</code> contains <code>blankLinesNumber * </code> line feeds and <code>myDocument.getText.charAt(x-1) == '\n'</code> */ private int getBlankLineOffset(int blankLinesNumber, int startOffset) { int startLine = myDocument.getLineNumber(startOffset); if (startLine <= 0) { return 0; } CharSequence text = myDocument.getCharsSequence(); for (int i = myDocument.getLineStartOffset(startLine - 1) - 1; i >= 0; i = CharArrayUtil.lastIndexOf(text, "\n", i - 1)) { if (--blankLinesNumber <= 0) { return i + 1; } } return 0; }
void lineLayoutCreated(int logicalLine) { int startVisualLine = myView.offsetToVisualLine(myDocument.getLineStartOffset(logicalLine), false); int endVisualLine = myView.offsetToVisualLine(myDocument.getLineEndOffset(logicalLine), false); boolean sizeInvalidated = false; for (int i = startVisualLine; i <= endVisualLine; i++) { if (myLineWidths.get(i) < 0) { myLineWidths.set(i, UNKNOWN_WIDTH); sizeInvalidated = true; } } if (sizeInvalidated) { myWidthInPixels = -1; myEditor.getContentComponent().revalidate(); } }
@Override public int getLineStartOffset(int line) { LOG.assertTrue(line >= 0, line); if (line == 0) return 0; String hostText = myDelegate.getText(); int[] pos = new int[2]; // pos[0] = curLine; pos[1] == offset; synchronized (myLock) { for (PsiLanguageInjectionHost.Shred shred : myShreds) { Segment hostRange = shred.getHostRangeMarker(); if (hostRange == null) continue; int found = countNewLinesIn(shred.getPrefix(), pos, line); if (found != -1) return found; CharSequence text = hostText.subSequence(hostRange.getStartOffset(), hostRange.getEndOffset()); found = countNewLinesIn(text, pos, line); if (found != -1) return found; found = countNewLinesIn(shred.getSuffix(), pos, line); if (found != -1) return found; } } return pos[1]; }
void textLayoutPerformed(int startOffset, int endOffset) { if (myDocument.isInBulkUpdate()) return; if (myEditor.getFoldingModel().isInBatchFoldingOperation()) { myDeferredRanges.add(new TextRange(startOffset, endOffset)); } else { onTextLayoutPerformed(startOffset, endOffset); } }
private int getPreferredWidth() { if (myWidthInPixels < 0) { assert !myDocument.isInBulkUpdate(); myWidthInPixels = calculatePreferredWidth(); } validateMaxLineWithExtension(); return Math.max(myWidthInPixels, myMaxLineWithExtensionWidth); }
protected boolean unregisterInTree() { if (!isValid()) return false; IntervalTreeImpl tree = myNode.getTree(); tree.checkMax(true); boolean b = myDocument.removeRangeMarker(this); tree.checkMax(true); return b; }
@Override public void onFoldRegionStateChange(@NotNull FoldRegion region) { if (myDocument.isInBulkUpdate()) return; if (region.isValid()) { myFoldingChangeStartOffset = Math.min(myFoldingChangeStartOffset, region.getStartOffset()); myFoldingChangeEndOffset = Math.max(myFoldingChangeEndOffset, region.getEndOffset()); } }
private void onSoftWrapRecalculationEnd(IncrementalCacheUpdateEvent event) { if (myDocument.isInBulkUpdate()) return; boolean invalidate = true; if (myEditor.getFoldingModel().isInBatchFoldingOperation()) { myFoldingChangeStartOffset = Math.min(myFoldingChangeStartOffset, event.getStartOffset()); myFoldingChangeEndOffset = Math.max(myFoldingChangeEndOffset, event.getActualEndOffset()); invalidate = false; } if (myDocument.isInEventsHandling()) { myDocumentChangeStartOffset = Math.min(myDocumentChangeStartOffset, event.getStartOffset()); myDocumentChangeEndOffset = Math.max(myDocumentChangeEndOffset, event.getActualEndOffset()); invalidate = false; } if (invalidate) { doInvalidateRange(event.getStartOffset(), event.getActualEndOffset()); } }
EditorSizeManager(EditorView view) { myView = view; myEditor = view.getEditor(); myDocument = myEditor.getDocument(); myDocument.addDocumentListener(this, this); myEditor.getFoldingModel().addListener(this, this); myEditor.getSoftWrapModel().getApplianceManager().addListener(mySoftWrapChangeListener); }
private void assertValidState() { if (myDocument.isInBulkUpdate() || myDirty) return; if (myLineWidths.size() != myEditor.getVisibleLineCount()) { LOG.error("Inconsistent state", new Attachment("editor.txt", myEditor.dumpState())); reset(); } assert myLineWidths.size() == myEditor.getVisibleLineCount(); }
@Override @NotNull public RangeMarker createRangeMarker(final int startOffset, final int endOffset) { ProperTextRange hostRange = injectedToHost(new ProperTextRange(startOffset, endOffset)); RangeMarker hostMarker = myDelegate.createRangeMarker(hostRange); int startShift = Math.max(0, hostToInjected(hostRange.getStartOffset()) - startOffset); int endShift = Math.max(0, endOffset - hostToInjected(hostRange.getEndOffset()) - startShift); return new RangeMarkerWindow(this, (RangeMarkerEx) hostMarker, startShift, endShift); }
private static void edit(DocumentEx document, int... offsets) { for (int i = 0; i < offsets.length; i += 3) { int offset = offsets[i]; int oldlength = offsets[i + 1]; int newlength = offsets[i + 2]; document.replaceString(offset, offset + oldlength, StringUtil.repeatSymbol(' ', newlength)); } }