@NotNull private SoftWrapImpl registerSoftWrap(int offset, int spaceSize, LogicalLineData lineData) { int indentInColumns = 0; int indentInPixels = myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); if (myCustomIndentUsedLastTime) { indentInColumns = myCustomIndentValueUsedLastTime + lineData.indentInColumns; indentInPixels += lineData.indentInPixels + (myCustomIndentValueUsedLastTime * spaceSize); } SoftWrapImpl result = new SoftWrapImpl( new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', indentInColumns), offset, offset), indentInColumns + 1/* for 'after soft wrap' drawing */, indentInPixels ); myStorage.storeOrReplace(result); return result; }
boolean matchesOldSoftWrap(SoftWrap newSoftWrap, int lengthDiff) { return Collections.binarySearch(myAffectedByUpdateSoftWraps, new SoftWrapImpl(new TextChangeImpl(newSoftWrap.getText(), newSoftWrap.getStart() - lengthDiff, newSoftWrap.getEnd() - lengthDiff), newSoftWrap.getIndentInColumns(), newSoftWrap.getIndentInPixels()), new Comparator<SoftWrapImpl>() { @Override public int compare(SoftWrapImpl o1, SoftWrapImpl o2) { int offsetDiff = o1.getStart() - o2.getStart(); if (offsetDiff != 0) { return offsetDiff; } int textDiff = o1.getText().toString().compareTo(o2.getText().toString()); if (textDiff != 0) { return textDiff; } int colIndentDiff = o1.getIndentInColumns() - o2.getIndentInColumns(); if (colIndentDiff != 0) { return colIndentDiff; } return o1.getIndentInPixels() - o2.getIndentInPixels(); } }) >= 0; }
/** * Applies given offsets diff to all soft wraps that lay after the given offset * * @param offsetsDiff offset diff to apply to the target soft wraps * @param offset offset to use for filtering soft wraps to advance. All soft wraps which offsets are strictly greater * than the given one should be advanced */ private void advanceSoftWrapOffsets(int offsetsDiff, int offset) { if (offsetsDiff == 0) { return; } int softWrapIndex = myStorage.getSoftWrapIndex(offset); if (softWrapIndex >= 0) { softWrapIndex++; // We want to process only soft wraps which offsets are strictly more than the given one. } else { softWrapIndex = -softWrapIndex - 1; } List<SoftWrapImpl> softWraps = myStorage.getSoftWraps(); for (int i = softWrapIndex; i < softWraps.size(); i++) { softWraps.get(i).advance(offsetsDiff); } }
@Nonnull private SoftWrapImpl registerSoftWrap(int offset, int spaceSize, LogicalLineData lineData) { assert !DocumentUtil.isInsideSurrogatePair(myEditor.getDocument(), offset); int indentInColumns = 0; int indentInPixels = myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP); if (myCustomIndentUsedLastTime) { indentInColumns = myCustomIndentValueUsedLastTime + lineData.indentInColumns; indentInPixels += lineData.indentInPixels + (myCustomIndentValueUsedLastTime * spaceSize); } SoftWrapImpl result = new SoftWrapImpl( new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', indentInColumns), offset, offset), indentInColumns + 1/* for 'after soft wrap' drawing */, indentInPixels ); myStorage.storeOrReplace(result); return result; }
boolean matchesOldSoftWrap(SoftWrap newSoftWrap, int lengthDiff) { return Collections.binarySearch(myAffectedByUpdateSoftWraps, new SoftWrapImpl(new TextChangeImpl(newSoftWrap.getText(), newSoftWrap.getStart() - lengthDiff, newSoftWrap.getEnd() - lengthDiff), newSoftWrap.getIndentInColumns(), newSoftWrap.getIndentInPixels()), (o1, o2) -> { int offsetDiff = o1.getStart() - o2.getStart(); if (offsetDiff != 0) { return offsetDiff; } int textDiff = o1.getText().toString().compareTo(o2.getText().toString()); if (textDiff != 0) { return textDiff; } int colIndentDiff = o1.getIndentInColumns() - o2.getIndentInColumns(); if (colIndentDiff != 0) { return colIndentDiff; } return o1.getIndentInPixels() - o2.getIndentInPixels(); }) >= 0; }
private boolean checkIsDoneAfterSoftWrap() { SoftWrapImpl lastSoftWrap = myDataMapper.getLastSoftWrap(); LOG.assertTrue(lastSoftWrap != null); if (myContext.currentPosition.offset > myContext.rangeEndOffset && myDataMapper.matchesOldSoftWrap(lastSoftWrap, myEventBeingProcessed.getLengthDiff())) { myDataMapper.removeLastCacheEntry(); return true; } return false; }
/** * This method is assumed to be called in a situation when visible area width is exceeded. It tries to create and register * new soft wrap which data is defined in accordance with the given parameters. * <p/> * There is a possible case that no soft wrap is created and registered. That is true, for example, for a situation when * we have a long line of text that doesn't contain white spaces, operators or any other symbols that may be used * as a <code>'wrap points'</code>. We just left such lines as-is. * * @param minOffset min line <code>'wrap point'</code> offset * @param preferredOffset preferred <code>'wrap point'</code> offset, i.e. max offset which symbol doesn't exceed right margin * @param maxOffset max line <code>'wrap point'</code> offset * @param spaceSize current space width in pixels * @param lineData object that encapsulates information about currently processed logical line * @return newly created and registered soft wrap if any; <code>null</code> otherwise */ @Nullable private SoftWrapImpl registerSoftWrap(int minOffset, int preferredOffset, int maxOffset, int spaceSize, LogicalLineData lineData) { int softWrapOffset = calculateBackwardSpaceOffsetIfPossible(minOffset, preferredOffset); if (softWrapOffset < 0) { softWrapOffset = calculateBackwardOffsetForEasternLanguageIfPossible(minOffset, preferredOffset); } if (softWrapOffset < 0) { Document document = myEditor.getDocument(); // Performance optimization implied by profiling results analysis. if (myLineWrapPositionStrategy == null) { myLineWrapPositionStrategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(myEditor); } softWrapOffset = myLineWrapPositionStrategy.calculateWrapPosition( document, myEditor.getProject(), minOffset, maxOffset, preferredOffset, true, true ); } if (softWrapOffset >= lineData.endLineOffset || softWrapOffset < 0 || (myCustomIndentUsedLastTime && softWrapOffset == lineData.nonWhiteSpaceSymbolOffset) || (softWrapOffset > preferredOffset && myContext.lastFoldStartPosition != null // Prefer to wrap on fold region backwards && myContext.lastFoldStartPosition.offset <= preferredOffset)) // to wrapping forwards. { return null; } return registerSoftWrap(softWrapOffset, spaceSize, lineData); }
/** * Determines which soft wraps were not affected by recalculation, and shifts them to their new offsets. * * @return Change in soft wraps count after recalculation */ private int advanceSoftWrapOffsets(@NotNull IncrementalCacheUpdateEvent event) { int lengthDiff = event.getLengthDiff(); int recalcEndOffsetTranslated = event.getActualEndOffset() - lengthDiff; int firstIndex = -1; int softWrappedLinesDiff = myStorage.getNumberOfSoftWrapsInRange(event.getStartOffset() + 1, myEditor.getDocument().getTextLength()); boolean softWrapsChanged = softWrappedLinesDiff > 0; for (int i = 0; i < myAffectedByUpdateSoftWraps.size(); i++) { SoftWrap softWrap = myAffectedByUpdateSoftWraps.get(i); if (firstIndex < 0) { if (softWrap.getStart() > recalcEndOffsetTranslated) { firstIndex = i; if (lengthDiff == 0) { break; } } else { softWrappedLinesDiff--; softWrapsChanged = true; } } if (firstIndex >= 0 && i >= firstIndex) { ((SoftWrapImpl)softWrap).advance(lengthDiff); } } if (firstIndex >= 0) { List<SoftWrapImpl> updated = myAffectedByUpdateSoftWraps.subList(firstIndex, myAffectedByUpdateSoftWraps.size()); SoftWrapImpl lastSoftWrap = getLastSoftWrap(); if (lastSoftWrap != null && lastSoftWrap.getStart() >= updated.get(0).getStart()) { LOG.error("Invalid soft wrap recalculation", new Attachment("state.txt", myEditor.getSoftWrapModel().toString())); } myStorage.addAll(updated); } myAffectedByUpdateSoftWraps.clear(); if (softWrapsChanged) { myStorage.notifyListenersAboutChange(); } return softWrappedLinesDiff; }
/** * This method is assumed to be called in a situation when visible area width is exceeded. It tries to create and register * new soft wrap which data is defined in accordance with the given parameters. * <p/> * There is a possible case that no soft wrap is created and registered. That is true, for example, for a situation when * we have a long line of text that doesn't contain white spaces, operators or any other symbols that may be used * as a <code>'wrap points'</code>. We just left such lines as-is. * * @param minOffset min line <code>'wrap point'</code> offset * @param preferredOffset preferred <code>'wrap point'</code> offset, i.e. max offset which symbol doesn't exceed right margin * @param maxOffset max line <code>'wrap point'</code> offset * @param spaceSize current space width in pixels * @param lineData object that encapsulates information about currently processed logical line * @return newly created and registered soft wrap if any; <code>null</code> otherwise */ @Nullable private SoftWrapImpl registerSoftWrap(int minOffset, int preferredOffset, int maxOffset, int spaceSize, LogicalLineData lineData) { int softWrapOffset = calculateBackwardSpaceOffsetIfPossible(minOffset, preferredOffset); if (softWrapOffset < 0) { softWrapOffset = calculateBackwardOffsetForEasternLanguageIfPossible(minOffset, preferredOffset); } if (softWrapOffset < 0) { Document document = myEditor.getDocument(); // Performance optimization implied by profiling results analysis. if (myLineWrapPositionStrategy == null) { myLineWrapPositionStrategy = LanguageLineWrapPositionStrategy.INSTANCE.forEditor(myEditor); } softWrapOffset = myLineWrapPositionStrategy.calculateWrapPosition( document, myEditor.getProject(), minOffset, maxOffset, preferredOffset, true, true ); if (DocumentUtil.isInsideSurrogatePair(document, softWrapOffset)) softWrapOffset--; } if (softWrapOffset >= lineData.endLineOffset || softWrapOffset < 0 || softWrapOffset <= minOffset || (myCustomIndentUsedLastTime && softWrapOffset == lineData.nonWhiteSpaceSymbolOffset) || (softWrapOffset > preferredOffset && myContext.lastFoldStartPosition != null // Prefer to wrap on fold region backwards && myContext.lastFoldStartPosition.offset <= preferredOffset)) // to wrapping forwards. { return null; } return registerSoftWrap(softWrapOffset, spaceSize, lineData); }
/** * Determines which soft wraps were not affected by recalculation, and shifts them to their new offsets. * * @return Change in soft wraps count after recalculation */ private void advanceSoftWrapOffsets(@Nonnull IncrementalCacheUpdateEvent event) { int lengthDiff = event.getLengthDiff(); int recalcEndOffsetTranslated = event.getActualEndOffset() - lengthDiff; int firstIndex = -1; int softWrappedLinesDiff = myStorage.getNumberOfSoftWrapsInRange(event.getStartOffset() + 1, myEditor.getDocument().getTextLength()); boolean softWrapsChanged = softWrappedLinesDiff > 0; for (int i = 0; i < myAffectedByUpdateSoftWraps.size(); i++) { SoftWrap softWrap = myAffectedByUpdateSoftWraps.get(i); if (firstIndex < 0) { if (softWrap.getStart() > recalcEndOffsetTranslated) { firstIndex = i; if (lengthDiff == 0) { break; } } else { softWrappedLinesDiff--; softWrapsChanged = true; } } if (firstIndex >= 0 && i >= firstIndex) { ((SoftWrapImpl)softWrap).advance(lengthDiff); } } if (firstIndex >= 0) { List<SoftWrapImpl> updated = myAffectedByUpdateSoftWraps.subList(firstIndex, myAffectedByUpdateSoftWraps.size()); SoftWrapImpl lastSoftWrap = getLastSoftWrap(); if (lastSoftWrap != null && lastSoftWrap.getStart() >= updated.get(0).getStart()) { LOG.error("Invalid soft wrap recalculation", new Attachment("state.txt", myEditor.getSoftWrapModel().toString())); } myStorage.addAll(updated); } myAffectedByUpdateSoftWraps.clear(); if (softWrapsChanged) { myStorage.notifyListenersAboutChange(); } }
@Override protected int getSortingKey(@NotNull SoftWrapImpl data) { return data.getStart(); }
@Nullable SoftWrapImpl getLastSoftWrap() { List<SoftWrapImpl> softWraps = myStorage.getSoftWraps(); return softWraps.isEmpty() ? null : softWraps.get(softWraps.size() - 1); }
private boolean checkIsDoneAfterSoftWrap() { SoftWrapImpl lastSoftWrap = myDataMapper.getLastSoftWrap(); LOG.assertTrue(lastSoftWrap != null); return myContext.currentPosition.offset > myContext.rangeEndOffset && myDataMapper.matchesOldSoftWrap(lastSoftWrap, myEventBeingProcessed.getLengthDiff()); }