Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IndexOutOfBoundsException when deleting whole text if last line is empty #579

Closed
JFormDesigner opened this issue Sep 7, 2017 · 7 comments

Comments

@JFormDesigner
Copy link
Contributor

Discovered an exception when first pressing Ctrl+A to select whole text and then press DEL key. It occurs only if the last line in the text area is empty.

Can be easily reproduced by starting this little app and pressing the DELkey:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.fxmisc.flowless.VirtualizedScrollPane;
import org.fxmisc.richtext.CodeArea;

public class ClearBug extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {
        CodeArea area = new CodeArea("abc\n");
        VirtualizedScrollPane<CodeArea> vsPane = new VirtualizedScrollPane<>(area);

        area.selectAll();
        
        Scene scene = new Scene(vsPane, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

The exception:

Sep 07, 2017 5:34:37 PM javafx.beans.binding.Bindings$6 computeValue
WARNING: Exception while evaluating binding
java.lang.IndexOutOfBoundsException: 4 not in [0, 1]
	at org.reactfx.util.Lists.checkPosition(Lists.java:110)
	at org.reactfx.util.Lists.checkPosition(Lists.java:105)
	at org.reactfx.util.FingerTree$NonEmptyFingerTree.locateProgressively(FingerTree.java:51)
	at org.fxmisc.richtext.model.ReadOnlyStyledDocument$Pos.offsetBy(ReadOnlyStyledDocument.java:378)
	at org.fxmisc.richtext.SelectionImpl.lambda$2(SelectionImpl.java:133)
	at org.reactfx.value.MappedVal.computeValue(MappedVal.java:23)
	at org.reactfx.value.ValBase.getValue(ValBase.java:17)
	at org.reactfx.value.MappedVal.computeValue(MappedVal.java:22)
	at org.reactfx.value.ValBase.getValue(ValBase.java:17)
	at org.reactfx.value.MappedVal.computeValue(MappedVal.java:22)
	at org.reactfx.value.ValBase.getValue(ValBase.java:17)
	at org.reactfx.value.SuspendableValWrapper.getValue(SuspendableValWrapper.java:23)
	at org.fxmisc.richtext.SelectionImpl.getStartParagraphIndex(SelectionImpl.java:67)
	at org.fxmisc.richtext.CaretSelectionBindImpl.getStartParagraphIndex(CaretSelectionBindImpl.java:81)
	at org.fxmisc.richtext.GenericStyledArea.getParagraphSelection(GenericStyledArea.java:889)
	at org.fxmisc.richtext.GenericStyledArea.getParagraphSelection(GenericStyledArea.java:885)
	at org.fxmisc.richtext.GenericStyledArea.lambda$34(GenericStyledArea.java:1218)
	at javafx.beans.binding.Bindings$6.computeValue(Bindings.java:338)
	at javafx.beans.binding.ObjectBinding.get(ObjectBinding.java:153)
	at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:50)
	at javafx.beans.property.ObjectPropertyBase.get(ObjectPropertyBase.java:132)
	at javafx.beans.binding.ObjectExpression.getValue(ObjectExpression.java:50)
	at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:178)
	at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
	at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:105)
	at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
	at javafx.beans.property.ObjectPropertyBase.access$000(ObjectPropertyBase.java:51)
	at javafx.beans.property.ObjectPropertyBase$Listener.invalidated(ObjectPropertyBase.java:233)
	at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:137)
	at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
	at javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:172)
	at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:51)
	at org.reactfx.value.InvalidationListenerWrapper.accept(Val.java:765)
	at org.reactfx.util.AbstractReducingStreamNotifications.lambda$head$0(NotificationAccumulator.java:248)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
	at org.reactfx.value.ValBase.invalidate(ValBase.java:32)
	at org.reactfx.value.SimpleVar.setValue(SimpleVar.java:59)
	at org.fxmisc.richtext.ParagraphBox.setIndex(ParagraphBox.java:71)
	at org.fxmisc.richtext.GenericStyledArea$2.updateIndex(GenericStyledArea.java:1231)
	at org.fxmisc.flowless.CellWrapper.updateIndex(CellWrapper.java:126)
	at org.fxmisc.flowless.CellWrapper.updateIndex(CellWrapper.java:126)
	at org.fxmisc.flowless.CellListManager.presentCellsChanged(CellListManager.java:99)
	at org.reactfx.util.ListNotifications.lambda$takeHead$0(NotificationAccumulator.java:317)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.collection.MemoizationListImpl$MemoizedView.publishNotifications(MemoizationList.java:58)
	at org.reactfx.collection.MemoizationListImpl$MemoizedView.access$300(MemoizationList.java:30)
	at org.reactfx.collection.MemoizationListImpl.sourceChanged(MemoizationList.java:89)
	at org.reactfx.util.ListNotifications.lambda$takeHead$0(NotificationAccumulator.java:317)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
	at org.reactfx.collection.MappedList.sourceChanged(MappedList.java:41)
	at org.reactfx.util.ListNotifications.lambda$takeHead$0(NotificationAccumulator.java:317)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.SuspendableBase.resume(SuspendableBase.java:64)
	at org.reactfx.CloseableOnceGuard.close(Guard.java:49)
	at org.reactfx.MultiGuard.close(Guard.java:83)
	at org.reactfx.Suspendable$1.resumeSource(Suspendable.java:118)
	at org.reactfx.Suspendable$1.suspendSource(Suspendable.java:104)
	at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
	at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
	at org.reactfx.EventStreams$3.lambda$observeInputs$0(EventStreams.java:105)
	at org.reactfx.value.ChangeListenerWrapper.accept(Val.java:786)
	at org.reactfx.util.AbstractReducingStreamNotifications.lambda$head$0(NotificationAccumulator.java:248)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
	at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
	at org.reactfx.value.ValBase.invalidate(ValBase.java:32)
	at org.reactfx.SuspendableBoolean.release(SuspendableBoolean.java:24)
	at org.reactfx.CloseableOnceGuard.close(Guard.java:49)
	at org.reactfx.Suspendable.suspendWhile(Suspendable.java:49)
	at org.fxmisc.richtext.model.GenericEditableStyledDocumentBase.update(GenericEditableStyledDocumentBase.java:218)
	at org.reactfx.util.Tuple3.exec(Tuple3.java:43)
	at org.fxmisc.richtext.model.GenericEditableStyledDocumentBase.replace(GenericEditableStyledDocumentBase.java:125)
	at org.fxmisc.richtext.model.SimpleEditableStyledDocument.replace(SimpleEditableStyledDocument.java:1)
	at org.fxmisc.richtext.GenericStyledArea.replace(GenericStyledArea.java:1132)
	at org.fxmisc.richtext.GenericStyledArea.replaceText(GenericStyledArea.java:1127)
	at org.fxmisc.richtext.model.TextEditingArea.replaceText(TextEditingArea.java:292)
	at org.fxmisc.richtext.model.EditActions.replaceSelection(EditActions.java:164)
	at org.fxmisc.richtext.GenericStyledAreaBehavior.deleteForward(GenericStyledAreaBehavior.java:348)
	at org.fxmisc.wellbehaved.event.template.InputMapTemplate.lambda$consume$32(InputMapTemplate.java:107)
	at org.fxmisc.wellbehaved.event.template.PatternActionTemplate.lambda$null$44(InputMapTemplate.java:242)
	at java.util.Optional.map(Optional.java:215)
	at org.fxmisc.wellbehaved.event.template.PatternActionTemplate.lambda$getInputHandlerTemplateMap$45(InputMapTemplate.java:242)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputMapTemplate$2.lambda$null$35(InputMapTemplate.java:180)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputMapTemplate$1.lambda$null$30(InputMapTemplate.java:92)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputMapTemplate$1.lambda$null$30(InputMapTemplate.java:92)
	at org.fxmisc.wellbehaved.event.template.InputHandlerTemplateMap.lambda$sequence$39(InputHandlerTemplateMap.java:24)
	at org.fxmisc.wellbehaved.event.template.InputMapTemplate$HandlerTemplateConsumer$1.lambda$accept$29(InputMapTemplate.java:36)
	at org.fxmisc.wellbehaved.event.InputHandler.handle(InputHandler.java:15)
	at com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:218)
	at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:238)
	at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)
	at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)
	at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)
	at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)
	at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)
	at javafx.event.Event.fireEvent(Event.java:198)
	at javafx.scene.Scene$KeyHandler.process(Scene.java:3964)
	at javafx.scene.Scene$KeyHandler.access$1800(Scene.java:3910)
	at javafx.scene.Scene.impl_processKeyEvent(Scene.java:2040)
	at javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2501)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:217)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:149)
	at java.security.AccessController.doPrivileged(Native Method)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$353(GlassViewEventHandler.java:248)
	at com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:389)
	at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:247)
	at com.sun.glass.ui.View.handleKeyEvent(View.java:546)
	at com.sun.glass.ui.View.notifyKey(View.java:966)
	at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
	at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
	at java.lang.Thread.run(Thread.java:748)
@JordanMartinez
Copy link
Contributor

I'm going to guess that the bug occurs due to #564.

@JordanMartinez
Copy link
Contributor

Actually, no. It probably occurs because getSelectionInParagraph now adds 1 to the returned IndexRange's end to account for the newline issue I addressed in 564

It'd take a closer look.

@JordanMartinez
Copy link
Contributor

Aye, the bug is caused by an order issue. The emitted plainTextChange that updates the selection's index range occurs after SelectionImpl uses its own outdated selection value to determine what its (TwoDimensional) positions are.

@JordanMartinez
Copy link
Contributor

SelectionImpl uses its outdated selection value because the paragraph update causes a new ParagraphBox to be created, which needs to know the area's selection range so that it can update the selection value whenever that should change. In other words, we have a cyclical situation going on here...

@JordanMartinez
Copy link
Contributor

The quick and dirty approach is just to remove the 2DPostion-related properties in Selection. The correct fix will take much longer as it will probably entail refactoring the code a bit more so that Selection is responsible for rendering itself in a ParagraphBox, rather than relying up ParagraphBox to do that.

@JordanMartinez
Copy link
Contributor

JordanMartinez commented Sep 10, 2017

I tried running the test before the CaretSelectionBind code was integrated into the area. This console output shows that the code was handled differently than it is now:

 ==== Replacing text with 'abc\n' ===
either box's index or the selection changed
Getting paragraph selection for paragraph: 1
Selection start 2d: 0 | 0
Selection end 2d:   0 | 0
Emitting plain text change: PlainTextChange{
	position: 0
	removed: 
	inserted: abc

}
internal selection invalidated and is now: 4, 4
selectionStart2D is: major: 1, minor: 0
selectionEnd2D is:   major: 1, minor: 0
either box's index or the selection changed
Getting paragraph selection for paragraph: 1
Selection start 2d: 1 | 0
Selection end 2d:   1 | 0
either box's index or the selection changed
Getting paragraph selection for paragraph: 0
Selection start 2d: 1 | 0
Selection end 2d:   1 | 0

 ==== Selecting all text ===
internal selection invalidated and is now: 0, 4
selectionStart2D is: major: 0, minor: 0
selectionEnd2D is:   major: 1, minor: 0
either box's index or the selection changed
Getting paragraph selection for paragraph: 1
Selection start 2d: 0 | 0
Selection end 2d:   1 | 0
either box's index or the selection changed
Getting paragraph selection for paragraph: 0
Selection start 2d: 0 | 0
Selection end 2d:   1 | 0

 ==== Pushing delete ===
either box's index or the selection changed
Getting paragraph selection for paragraph: 0
Selection start 2d: 0 | 0
Selection end 2d:   1 | 0
Emitting plain text change: PlainTextChange{
	position: 0
	removed: abc

	inserted: 
}
internal selection invalidated and is now: 0, 0
selectionStart2D is: major: 0, minor: 0
selectionEnd2D is:   major: 0, minor: 0
either box's index or the selection changed
Getting paragraph selection for paragraph: 0
Selection start 2d: 0 | 0
Selection end 2d:   0 | 0

@JFormDesigner
Copy link
Contributor Author

Thx 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants