/* * Copyright 2000-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.openapi.editor.impl.softwrap.mapping; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.SoftWrap; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.editor.impl.SoftWrapModelImpl; import org.jetbrains.annotations.NotNull; /** * Encapsulates information about incremental soft wraps cache update. * * @author Denis Zhdanov * @since 11/17/10 9:33 AM */ public class IncrementalCacheUpdateEvent { private final int myStartOffset; private final int myMandatoryEndOffset; private int myActualEndOffset = -1; private final int myLengthDiff; @NotNull private final LogicalPosition myStartLogicalPosition; private final int myOldEndLogicalLine; private int myNewEndLogicalLine = -1; /** * Creates new <code>IncrementalCacheUpdateEvent</code> object on the basis on the given event object that describes * document change that caused cache update. * <p/> * This constructor is assumed to be used <b>before</b> the document change, {@link #updateAfterDocumentChange(Document)} * should be called <b>'after'</b> document change to complete object creation. * * @param event object that describes document change that caused cache update */ IncrementalCacheUpdateEvent(@NotNull DocumentEvent event, @NotNull EditorImpl editor) { this(event.getOffset(), event.getOffset() + event.getOldLength(), event.getOffset() + event.getNewLength(), editor); } /** * Creates new <code>IncrementalCacheUpdateEvent</code> object for the event not changing document length * (like expansion of folded region). */ IncrementalCacheUpdateEvent(int startOffset, int endOffset, @NotNull EditorImpl editor) { this(startOffset, endOffset, endOffset, editor); myNewEndLogicalLine = myOldEndLogicalLine; } /** * Creates new <code>IncrementalCacheUpdateEvent</code> object that is configured to perform whole reparse of the given * document. * * @param document target document to reparse */ IncrementalCacheUpdateEvent(@NotNull Document document) { myStartOffset = 0; myMandatoryEndOffset = document.getTextLength(); myLengthDiff = 0; myStartLogicalPosition = new LogicalPosition(0, 0, 0, 0, 0, 0, 0); myOldEndLogicalLine = myNewEndLogicalLine = Math.max(0, document.getLineCount() - 1); } private IncrementalCacheUpdateEvent(int startOffset, int oldEndOffset, int newEndOffset, @NotNull EditorImpl editor) { VisualLineInfo info = getVisualLineInfo(editor, startOffset, false); if (info.startsWithSoftWrap) { info = getVisualLineInfo(editor, info.startOffset, true); } myStartOffset = info.startOffset; myStartLogicalPosition = editor.offsetToLogicalPosition(myStartOffset); myMandatoryEndOffset = newEndOffset; myLengthDiff = newEndOffset - oldEndOffset; myOldEndLogicalLine = editor.getDocument().getLineNumber(oldEndOffset); } private static VisualLineInfo getVisualLineInfo(@NotNull EditorImpl editor, int offset, boolean beforeSoftWrap) { Document document = editor.getDocument(); int textLength = document.getTextLength(); if (offset <= 0 || textLength == 0) return new VisualLineInfo(0, false); offset = Math.min(offset, textLength); int startOffset = EditorUtil.getNotFoldedLineStartOffset(editor, offset); SoftWrapModelImpl softWrapModel = editor.getSoftWrapModel(); int wrapIndex = softWrapModel.getSoftWrapIndex(offset); int prevSoftWrapIndex = wrapIndex < 0 ? (- wrapIndex - 2) : wrapIndex - (beforeSoftWrap ? 1 : 0); SoftWrap prevSoftWrap = prevSoftWrapIndex < 0 ? null : softWrapModel.getRegisteredSoftWraps().get(prevSoftWrapIndex); int visualLineStartOffset = prevSoftWrap == null ? startOffset : Math.max(startOffset, prevSoftWrap.getStart()); return new VisualLineInfo(visualLineStartOffset, prevSoftWrap != null && prevSoftWrap.getStart() == visualLineStartOffset); } private static class VisualLineInfo { private final int startOffset; private final boolean startsWithSoftWrap; private VisualLineInfo(int startOffset, boolean wrap) { this.startOffset = startOffset; startsWithSoftWrap = wrap; } } public void updateAfterDocumentChange(@NotNull Document document) { myNewEndLogicalLine = document.getLineNumber(myMandatoryEndOffset); } /** * Returns offset, from which soft wrap recalculation should start */ public int getStartOffset() { return myStartOffset; } /** * Returns logical position, from which soft wrap recalculation should start */ @NotNull public LogicalPosition getStartLogicalPosition() { return myStartLogicalPosition; } /** * Returns offset, till which soft wrap recalculation should proceed */ public int getMandatoryEndOffset() { return myMandatoryEndOffset; } /** * Returns offset, till which soft wrap recalculation actually was performed. It can be larger that the value returned by * {@link #getMandatoryEndOffset()}. */ public int getActualEndOffset() { return myActualEndOffset; } void setActualEndOffset(int actualEndOffset) { myActualEndOffset = actualEndOffset; } /** * Returns change in document length for the event causing soft wrap recalculation. */ public int getLengthDiff() { return myLengthDiff; } @Override public String toString() { return "startOffset=" + myStartOffset + ", mandatoryEndOffset=" + myMandatoryEndOffset + ", actualEndOffset=" + myActualEndOffset + ", lengthDiff=" + myLengthDiff + ", startLogicalPosition=" + myStartLogicalPosition + ", oldEndLogicalLine=" + myOldEndLogicalLine + ", newEndLogicalLine=" + myNewEndLogicalLine; } }