mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-58716 When typing commit message and it exceedes line lenght, soft wrap triggers, however editor stay scrolled to the right
Added support for situation when long line exceeds visible area width and there is no convenient position for soft wrap
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
abcdefghijklmnopqrstuvwxyz<caret>
|
||||
123
|
||||
ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||
+71
-27
@@ -31,6 +31,7 @@ import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import gnu.trove.TIntIntHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -161,6 +162,7 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
int softWrapStartOffset = startLineContext.offset;
|
||||
|
||||
int reservedWidth = myPainter.getMinDrawingWidth(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED);
|
||||
SoftWrap delayedSoftWrap = null;
|
||||
|
||||
// Perform soft wraps calculation.
|
||||
outer:
|
||||
@@ -175,10 +177,8 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
}
|
||||
if (newX + reservedWidth >= myVisibleAreaWidth) {
|
||||
logicalLineData.update(currentFold.getStartOffset(), spaceWidth);
|
||||
SoftWrap softWrap = registerSoftWrap(
|
||||
softWrapStartOffset, start, start, logicalLineData.indentInColumns,
|
||||
logicalLineData.indentInPixels, spaceWidth
|
||||
);
|
||||
SoftWrap softWrap = registerSoftWrap(softWrapStartOffset, start, start, spaceWidth, logicalLineData);
|
||||
assert softWrap != null; // We expect that it's always possible to wrap collapsed fold region placeholder text
|
||||
softWrapStartOffset = softWrap.getStart();
|
||||
if (softWrap.getStart() < start) {
|
||||
revertListeners(softWrap.getStart(), context.visualLine);
|
||||
@@ -242,6 +242,14 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
fontType = offset2fontType.get(i);
|
||||
}
|
||||
context.symbol = c;
|
||||
|
||||
if (delayedSoftWrap != null && delayedSoftWrap.getStart() == i) {
|
||||
processSoftWrap(delayedSoftWrap, context);
|
||||
softWrapStartOffset = delayedSoftWrap.getStart();
|
||||
startLineContext.from(context);
|
||||
delayedSoftWrap = null;
|
||||
}
|
||||
|
||||
if (c == '\n') {
|
||||
processSymbol(context, startLineContext, logicalLineData, fontType, 0, fontType2spaceWidth, offset2widthInPixels, offset2fontType);
|
||||
softWrapStartOffset = startLineContext.offset;
|
||||
@@ -258,15 +266,29 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
if (newX + reservedWidth >= myVisibleAreaWidth) {
|
||||
logicalLineData.update(i, spaceWidth);
|
||||
SoftWrap softWrap = registerSoftWrap(
|
||||
softWrapStartOffset, Math.max(softWrapStartOffset, i - 1), calculateSoftWrapEndOffset(softWrapStartOffset, end),
|
||||
logicalLineData.indentInColumns, logicalLineData.indentInPixels, spaceWidth
|
||||
softWrapStartOffset, Math.max(softWrapStartOffset, i - 1),
|
||||
calculateSoftWrapEndOffset(softWrapStartOffset, logicalLineData.endLineOffset), spaceWidth, logicalLineData
|
||||
);
|
||||
if (softWrap == null) {
|
||||
processSymbol(context, startLineContext, logicalLineData, fontType, newX, fontType2spaceWidth, offset2widthInPixels,
|
||||
offset2fontType);
|
||||
continue;
|
||||
}
|
||||
int newI = softWrap.getStart();
|
||||
|
||||
// There are two possible options: soft wrap offset is located before/after the current offset (it may be
|
||||
// located after offset in situation when it's not possible to wrap in [softWrapStartOffset; currentOffset)
|
||||
// interval). We should process that accordingly.
|
||||
if (newI < i) {
|
||||
// There are three possible options:
|
||||
// 1. Soft wrap offset is located before the current offset;
|
||||
// 2. Soft wrap offset is located after the current offset but doesn't exceed current token end offset
|
||||
// (it may occur if there are no convenient wrap positions before the current offset);
|
||||
// 3. Soft wrap offset is located after the current offset and exceeds current token end offset;
|
||||
// We should process that accordingly.
|
||||
if (newI > end) {
|
||||
delayedSoftWrap = softWrap;
|
||||
processSymbol(context, startLineContext, logicalLineData, fontType, newX, fontType2spaceWidth, offset2widthInPixels,
|
||||
offset2fontType);
|
||||
continue;
|
||||
}
|
||||
else if (newI < i) {
|
||||
revertListeners(newI, context.visualLine);
|
||||
for (int j = i - 1; j >= newI; j--) {
|
||||
int pixelsDiff = offset2widthInPixels.get(j);
|
||||
@@ -287,18 +309,8 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
}
|
||||
}
|
||||
|
||||
notifyListenersOnBeforeSoftWrap(context);
|
||||
processSoftWrap(softWrap, context);
|
||||
softWrapStartOffset = newI;
|
||||
|
||||
context.visualColumn = 0;
|
||||
context.softWrapColumnDiff = context.visualColumn - context.foldingColumnDiff - context.logicalColumn;
|
||||
context.softWrapLinesCurrent++;
|
||||
context.visualLine++;
|
||||
notifyListenersOnAfterSoftWrapLineFeed(context);
|
||||
|
||||
context.x = softWrap.getIndentInPixels();
|
||||
context.visualColumn = softWrap.getIndentInColumns();
|
||||
context.softWrapColumnDiff += softWrap.getIndentInColumns();
|
||||
i = newI - 1/* because of loop increment */;
|
||||
startLineContext.from(context);
|
||||
}
|
||||
@@ -383,9 +395,23 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
return result;
|
||||
}
|
||||
|
||||
private SoftWrap registerSoftWrap(int minOffset, int preferredOffset, int maxOffset, int indentInColumns, int indentInPixels,
|
||||
int spaceSize)
|
||||
{
|
||||
/**
|
||||
* This method is assumed to be called in 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 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 SoftWrap registerSoftWrap(int minOffset, int preferredOffset, int maxOffset, int spaceSize, LogicalLineData lineData) {
|
||||
Document document = myEditor.getDocument();
|
||||
|
||||
// Performance optimization implied by profiling results analysis.
|
||||
@@ -395,19 +421,37 @@ public class SoftWrapApplianceManager implements FoldingListener, DocumentListen
|
||||
int softWrapOffset = myLineWrapPositionStrategy.calculateWrapPosition(
|
||||
document.getCharsSequence(), minOffset, maxOffset, preferredOffset, minOffset != preferredOffset
|
||||
);
|
||||
if (softWrapOffset >= lineData.endLineOffset) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int indent = 0;
|
||||
if (myCustomIndentUsedLastTime) {
|
||||
indent = myCustomIndentValueUsedLastTime;
|
||||
}
|
||||
SoftWrapImpl softWrap = new SoftWrapImpl(
|
||||
new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', indentInColumns + indent), softWrapOffset, softWrapOffset),
|
||||
indentInColumns + indent + 1/* for 'after soft wrap' drawing */,
|
||||
indentInPixels + (indent * spaceSize) + myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP)
|
||||
new TextChangeImpl("\n" + StringUtil.repeatSymbol(' ', lineData.indentInColumns + indent), softWrapOffset, softWrapOffset),
|
||||
lineData.indentInColumns + indent + 1/* for 'after soft wrap' drawing */,
|
||||
lineData.indentInPixels + (indent * spaceSize) + myPainter.getMinDrawingWidth(SoftWrapDrawingType.AFTER_SOFT_WRAP)
|
||||
);
|
||||
myStorage.storeOrReplace(softWrap, true);
|
||||
return softWrap;
|
||||
}
|
||||
|
||||
private void processSoftWrap(SoftWrap softWrap, ProcessingContext context) {
|
||||
notifyListenersOnBeforeSoftWrap(context);
|
||||
|
||||
context.visualColumn = 0;
|
||||
context.softWrapColumnDiff = context.visualColumn - context.foldingColumnDiff - context.logicalColumn;
|
||||
context.softWrapLinesCurrent++;
|
||||
context.visualLine++;
|
||||
notifyListenersOnAfterSoftWrapLineFeed(context);
|
||||
|
||||
context.x = softWrap.getIndentInPixels();
|
||||
context.visualColumn = softWrap.getIndentInColumns();
|
||||
context.softWrapColumnDiff += softWrap.getIndentInColumns();
|
||||
}
|
||||
|
||||
/**
|
||||
* There is a possible case that we need to reparse the whole document (e.g. visible area width is changed or user-defined
|
||||
* soft wrap indent is changed etc). This method encapsulates that logic, i.e. it checks if necessary conditions are satisfied
|
||||
|
||||
+18
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl.softwrap.mapping;
|
||||
|
||||
import com.intellij.openapi.editor.ex.SoftWrapModelEx;
|
||||
import com.intellij.openapi.editor.impl.SoftWrapModelImpl;
|
||||
import com.intellij.testFramework.LightPlatformCodeInsightTestCase;
|
||||
|
||||
@@ -38,11 +39,23 @@ public class SoftWrapApplianceManagerTest extends LightPlatformCodeInsightTestCa
|
||||
init(800);
|
||||
|
||||
int offset = myEditor.getDocument().getTextLength() + 1;
|
||||
assertNull(myEditor.getSoftWrapModel().getSoftWrap(offset));
|
||||
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
|
||||
type(" thisisalongtokenthatisnotexpectedtobebrokenintopartsduringsoftwrapping");
|
||||
assertNotNull(myEditor.getSoftWrapModel().getSoftWrap(offset));
|
||||
}
|
||||
|
||||
public void testLongLineOfIdSymbolsIsNotSoftWrapped() throws Exception {
|
||||
init(100);
|
||||
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
|
||||
type('1');
|
||||
assertTrue(getSoftWrapModel().getRegisteredSoftWraps().isEmpty());
|
||||
|
||||
int offset = myEditor.getDocument().getText().indexOf("\n");
|
||||
type(" test");
|
||||
assertEquals(1, getSoftWrapModel().getRegisteredSoftWraps().size());
|
||||
assertNotNull(getSoftWrapModel().getSoftWrap(offset));
|
||||
}
|
||||
|
||||
private void init(final int visibleWidth) throws Exception {
|
||||
configureByFile(PATH + getTestName(false) + ".txt");
|
||||
myEditor.getSettings().setUseSoftWraps(true);
|
||||
@@ -58,4 +71,8 @@ public class SoftWrapApplianceManagerTest extends LightPlatformCodeInsightTestCa
|
||||
});
|
||||
applianceManager.registerSoftWrapIfNecessary(new Rectangle(visibleWidth, visibleWidth * 2), 0);
|
||||
}
|
||||
|
||||
private static SoftWrapModelEx getSoftWrapModel() {
|
||||
return (SoftWrapModelEx)myEditor.getSoftWrapModel();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user