/** * We know that there are problems with incremental soft wraps cache update at the moment. Hence, * we may implement full cache reconstruction when the problem is encountered in order to avoid * customer annoyance. * * <p>However, the problems still should be fixed, hence, we report them only if dedicated flag is * set. * * <p>Current method encapsulates the logic mentioned above. * * @param task command object that which execution may trigger incremental update of update soft * wraps cache */ @SuppressWarnings({"UseOfArchaicSystemPropertyAccessors"}) private void executeSafely(SoftWrapAwareTask task) { try { task.run(true); } catch (Throwable e) { if (Boolean.getBoolean(DEBUG_PROPERTY_NAME) || ApplicationManager.getApplication().isUnitTestMode()) { String info = myEditor.dumpState(); LOG.error( String.format("Unexpected exception occurred during performing '%s'", task), e, info); } myEditor.getFoldingModel().rebuild(); myDataMapper.release(); myApplianceManager.reset(); myStorage.removeAll(); myApplianceManager.recalculateIfNecessary(); try { task.run(true); } catch (Throwable e1) { String info = myEditor.dumpState(); LOG.error( String.format("Can't perform %s even with complete soft wraps cache re-parsing", task), e1, info); myEditor.getSettings().setUseSoftWraps(false); task.run(false); } } }
@Override public boolean isSoftWrappingEnabled() { if (!myUseSoftWraps || (!myEditor.myUseNewRendering && myEditor.isOneLineMode()) || myEditor.isPurePaintingMode()) { return false; } // We check that current thread is EDT because attempt to retrieve information about visible // area width may fail otherwise Application application = ApplicationManager.getApplication(); Thread lastEdt = myLastEdt.get(); Thread currentThread = Thread.currentThread(); if (lastEdt != currentThread) { if (application.isDispatchThread()) { myLastEdt = new SoftReference<Thread>(currentThread); } else { myLastEdt = new SoftReference<Thread>(null); return false; } } Rectangle visibleArea = myEditor.getScrollingModel().getVisibleArea(); return visibleArea.width > 0 && visibleArea.height > 0; }
/** * Called on editor settings change. Current model is expected to drop all cached information * about the settings if any. */ public void reinitSettings() { boolean softWrapsUsedBefore = myUseSoftWraps; myUseSoftWraps = areSoftWrapsEnabledInEditor(); int tabWidthBefore = myTabWidth; myTabWidth = EditorUtil.getTabSize(myEditor); boolean fontsChanged = false; if (!myFontPreferences.equals(myEditor.getColorsScheme().getFontPreferences()) && myEditorTextRepresentationHelper instanceof DefaultEditorTextRepresentationHelper) { fontsChanged = true; myEditor.getColorsScheme().getFontPreferences().copyTo(myFontPreferences); ((DefaultEditorTextRepresentationHelper) myEditorTextRepresentationHelper) .clearSymbolWidthCache(); myPainter.reinit(); } if ((myUseSoftWraps ^ softWrapsUsedBefore) || (tabWidthBefore >= 0 && myTabWidth != tabWidthBefore) || fontsChanged) { myApplianceManager.reset(); myDeferredFoldRegions.clear(); myStorage.removeAll(); myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); } }
public SoftWrapModelImpl(@NotNull EditorImpl editor) { myEditor = editor; myStorage = new SoftWrapsStorage(); myPainter = new CompositeSoftWrapPainter(editor); myEditorTextRepresentationHelper = new DefaultEditorTextRepresentationHelper(editor); myDataMapper = new CachingSoftWrapDataMapper(editor, myStorage); myApplianceManager = new SoftWrapApplianceManager(myStorage, editor, myPainter, myDataMapper); myVisualSizeManager = new SoftWrapAwareVisualSizeManager(myPainter); myApplianceManager.addListener(myVisualSizeManager); myApplianceManager.addListener( new SoftWrapAwareDocumentParsingListenerAdapter() { @Override public void recalculationEnds() { for (SoftWrapChangeListener listener : mySoftWrapListeners) { listener.recalculationEnds(); } } }); myUseSoftWraps = areSoftWrapsEnabledInEditor(); myEditor.getColorsScheme().getFontPreferences().copyTo(myFontPreferences); editor.addPropertyChangeListener(this, this); myApplianceManager.addListener(myDataMapper); }
private boolean isInsideSoftWrap(@NotNull VisualPosition visual, boolean countBeforeSoftWrap) { if (!isSoftWrappingEnabled()) { return false; } SoftWrapModel model = myEditor.getSoftWrapModel(); if (!model.isSoftWrappingEnabled()) { return false; } LogicalPosition logical = myEditor.visualToLogicalPosition(visual); int offset = myEditor.logicalPositionToOffset(logical); if (offset <= 0) { // Never expect to be here, just a defensive programming. return false; } SoftWrap softWrap = model.getSoftWrap(offset); if (softWrap == null) { return false; } // We consider visual positions that point after the last symbol before soft wrap and the first // symbol after soft wrap to not // belong to soft wrap-introduced virtual space. VisualPosition visualAfterSoftWrap = myEditor.offsetToVisualPosition(offset); if (visualAfterSoftWrap.line == visual.line && visualAfterSoftWrap.column <= visual.column) { return false; } if (myEditor.myUseNewRendering) { VisualPosition beforeSoftWrap = myEditor.offsetToVisualPosition(offset, true, true); return visual.line > beforeSoftWrap.line || visual.column > beforeSoftWrap.column || visual.column == beforeSoftWrap.column && countBeforeSoftWrap; } else { VisualPosition visualBeforeSoftWrap = myEditor.offsetToVisualPosition(offset - 1); int x = 0; LogicalPosition logLineStart = myEditor.visualToLogicalPosition(new VisualPosition(visualBeforeSoftWrap.line, 0)); if (logLineStart.softWrapLinesOnCurrentLogicalLine > 0) { int offsetLineStart = myEditor.logicalPositionToOffset(logLineStart); softWrap = model.getSoftWrap(offsetLineStart); if (softWrap != null) { x = softWrap.getIndentInPixels(); } } int width = EditorUtil.textWidthInColumns( myEditor, myEditor.getDocument().getCharsSequence(), offset - 1, offset, x); int softWrapStartColumn = visualBeforeSoftWrap.column + width; if (visual.line > visualBeforeSoftWrap.line) { return true; } return countBeforeSoftWrap ? visual.column >= softWrapStartColumn : visual.column > softWrapStartColumn; } }
@Override public List<? extends SoftWrap> getRegisteredSoftWraps() { if (!isSoftWrappingEnabled()) { return Collections.emptyList(); } List<SoftWrapImpl> softWraps = myStorage.getSoftWraps(); if (!softWraps.isEmpty() && softWraps.get(softWraps.size() - 1).getStart() >= myEditor.getDocument().getTextLength()) { LOG.error( "Unexpected soft wrap location", new Attachment("editorState.txt", myEditor.dumpState())); } return softWraps; }
public void recalculate() { myApplianceManager.reset(); myStorage.removeAll(); myDeferredFoldRegions.clear(); myEditor.getScrollingModel().scrollToCaret(ScrollType.CENTER); myApplianceManager.recalculateIfNecessary(); }
@Override public int paint( @NotNull Graphics g, @NotNull SoftWrapDrawingType drawingType, int x, int y, int lineHeight) { if (!isSoftWrappingEnabled()) { return 0; } if (!myEditor.getSettings().isAllSoftWrapsShown()) { int visualLine = y / lineHeight; LogicalPosition position = myEditor.visualToLogicalPosition(new VisualPosition(visualLine, 0)); if (position.line != myEditor.getCaretModel().getLogicalPosition().line) { return myPainter.getDrawingHorizontalOffset(g, drawingType, x, y, lineHeight); } } return doPaint(g, drawingType, x, y, lineHeight); }
@Override public void beforeDocumentChangeAtCaret() { CaretModel caretModel = myEditor.getCaretModel(); VisualPosition visualCaretPosition = caretModel.getVisualPosition(); if (!isInsideSoftWrap(visualCaretPosition)) { return; } SoftWrap softWrap = myStorage.getSoftWrap(caretModel.getOffset()); if (softWrap == null) { return; } myEditor .getDocument() .replaceString(softWrap.getStart(), softWrap.getEnd(), softWrap.getText()); caretModel.moveToVisualPosition(visualCaretPosition); }
@Override public int logicalPositionToOffset(@NotNull LogicalPosition logicalPosition) { if (myBulkUpdateInProgress || myUpdateInProgress || !prepareToMapping()) { return myEditor.logicalPositionToOffset(logicalPosition, false); } myActive++; try { myLogicalToOffsetTask.input = logicalPosition; executeSafely(myLogicalToOffsetTask); return myLogicalToOffsetTask.output; } finally { myActive--; } }
@NotNull @Override public LogicalPosition offsetToLogicalPosition(int offset) { if (myBulkUpdateInProgress || myUpdateInProgress || !prepareToMapping()) { return myEditor.offsetToLogicalPosition(offset, false); } myActive++; try { myOffsetToLogicalTask.input = offset; executeSafely(myOffsetToLogicalTask); return myOffsetToLogicalTask.output; } finally { myActive--; } }
@NotNull @Override public LogicalPosition visualToLogicalPosition(@NotNull VisualPosition visual) { if (myBulkUpdateInProgress || myUpdateInProgress || !prepareToMapping()) { return myEditor.visualToLogicalPosition(visual, false); } myActive++; try { myVisualToLogicalTask.input = visual; executeSafely(myVisualToLogicalTask); return myVisualToLogicalTask.output; } finally { myActive--; } }
@Override public boolean isVisible(SoftWrap softWrap) { FoldingModel foldingModel = myEditor.getFoldingModel(); int start = softWrap.getStart(); if (foldingModel.isOffsetCollapsed(start)) { return false; } // There is a possible case that soft wrap and collapsed folding region share the same offset, // i.e. soft wrap is represented // before the folding. We need to return 'true' in such situation. Hence, we check if offset // just before the soft wrap // is collapsed as well. return start <= 0 || !foldingModel.isOffsetCollapsed(start - 1); }
/** * Encapsulates preparations for performing document dimension mapping (e.g. visual to logical * position) and answers if soft wraps-aware processing should be used (e.g. there is no need to * consider soft wraps if user configured them not to be used). * * @return <code>true</code> if soft wraps-aware processing should be used; <code>false</code> * otherwise */ private boolean prepareToMapping() { boolean useSoftWraps = myActive <= 0 && isSoftWrappingEnabled() && myEditor.getDocument().getTextLength() > 0; if (!useSoftWraps) { return false; } if (myDirty) { myApplianceManager.reset(); myDeferredFoldRegions.clear(); myDirty = false; } return myApplianceManager.recalculateIfNecessary(); }
/** * Encapsulates preparations for performing document dimension mapping (e.g. visual to logical * position) and answers if soft wraps-aware processing should be used (e.g. there is no need to * consider soft wraps if user configured them not to be used). * * @return <code>true</code> if soft wraps-aware processing should be used; <code>false</code> * otherwise */ private boolean prepareToMapping() { if (myUpdateInProgress || myBulkUpdateInProgress || myActive > 0 || !isSoftWrappingEnabled() || myEditor.getDocument().getTextLength() <= 0) { return false; } if (myDirty) { myApplianceManager.reset(); myDeferredFoldRegions.clear(); myDirty = false; } return myApplianceManager.recalculateIfNecessary(); }
@Override @NotNull public List<? extends SoftWrap> getSoftWrapsForLine(int documentLine) { if (!isSoftWrappingEnabled() || documentLine < 0) { return Collections.emptyList(); } Document document = myEditor.getDocument(); if (documentLine >= document.getLineCount()) { return Collections.emptyList(); } int start = document.getLineStartOffset(documentLine); int end = document.getLineEndOffset(documentLine); return getSoftWrapsForRange( start, end + 1 /* it's theoretically possible that soft wrap is registered just before the line feed, * hence, we add '1' here assuming that end line offset points to line feed symbol */); }
private boolean areSoftWrapsEnabledInEditor() { return myEditor.getSettings().isUseSoftWraps() && !myEditor.myUseNewRendering && (!(myEditor.getDocument() instanceof DocumentImpl) || !((DocumentImpl) myEditor.getDocument()).acceptsSlashR()); }