cengels / skywriter-javafx

A lightweight distraction-free writer built on top of TornadoFX (Java) that exports to markdown for easy integration with version control.
Apache License 2.0
2 stars 2 forks source link

Automatic Smart Quotes #4

Closed cengels closed 4 years ago

cengels commented 4 years ago

Description

As a rich-text editor designed primarily for novelists, Skywriter should have an option to automatically replace neutral quotation marks (', ", ...) to their smart quote equivalent. Other symbols, like a triple dash (---) may also be replaced to their proper typeset equivalents.

Detection

Most word processors simply use word boundaries as detection for which curly quote to use. If there is a space before the word, an opening quote is used; else a closing quote is used. This algorithm is imperfect and does not consider special cases such as dashes. In the following sentence

"My goodness, you"—he took a drag of his cigarette—"really are a gem, aren't you?"

the opening quote before the word really should be an opening quotes, not a closing one.

cengels commented 4 years ago

I'm finding it to be significantly difficult to find the right place to call RichTextFX's replaceText() method. Doing it inside the plainTextChanges() observer results in an IndexOutOfBoundsException when the caret is at the end (apparently RichTextFX assumes the text still retains its original size and tries to place the caret at the end, which is now out of bounds). The same happens inside a listener for selectionProperty() and isBeingUpdatedProperty().

cengels commented 4 years ago

Solution

The core problem with why using beingUpdatedProperty() as a hook didn't work is that that property is updated twice per insertion: once to add the text and once to set the new selection. As a result, it only ever tried replacing the text when the first change (the text addition) was done, but before updating the selection, hence the selection inconsistencies -> exception.

I solved the problem by introducing a new observable property to the WriterTextArea called ready that is true only if all queued changes are done. This might not work for every type of change on a RichTextFX document, but it at least certifiably works for insertions and removals. The text replacements are now queued and only executed once ready is true.