Conversation
Preview only mode hides the editor, and the VCL refuses to focus a control that is not visible. Clicking Bold, Italic, Link or Code then raised EInvalidOperation with 'Cannot focus a disabled or invisible window', and so did every command on the shared format path, clicking an entry in the contents list, and the palette's undo, redo, select all, indent, outdent and delete word. Route every path that returns focus to the editor through a FocusEditor helper carrying the CanFocus guard that the zen mode code already used. The VCL applied that guard in four places out of sixteen. The FMX studio applied it nowhere, so it could raise from leaving zen mode and from closing the command palette as well.
First step towards formatting a selection made in the preview. To turn a position in the rendered preview back into a source range, the parser has to know where each character of a block's inline content came from. That is not a simple offset. A block's content is assembled line by line with markers stripped, a block quote losing its '> ' and a list item its bullet, and the result is trimmed before the inline parser sees it, so content and source drift apart repeatedly. TMarkdownSourceMap records the relationship as runs, each run being one stretch of content that is contiguous in the source. Characters with no source, such as the spaces a tab expands into, fall in no run and report as unmappable rather than guessing. This commit adds the unit and its tests only. Nothing calls it yet.
Carries the content to source map through the parser so a text node knows which characters of the markdown it came from. The staging block collects the map as lines are appended, the block parser adjusts it for the trim it does before parsing inlines, and the inline parser sets a segment on each text node as the buffer flushes. The span is taken from positions in the content rather than from the length of the buffered text, because escapes and entities make the rendered text a different length from its source. A run that is not contiguous in the source, such as one crossing a line break, gets no segment rather than a wrong one. Four tests parse real documents and assert that the recorded range points at the right characters, including a block quote whose marker is stripped before the inline parser ever sees the content, and a list item bullet. Table cells and ATX headings are not mapped yet.
Heading content is appended straight from the matcher rather than through the line path, so it had no entry in the source map and heading text ended up with no segment. The matcher now also reports where the caption starts in the line, past the hashes and the space after them, which is enough to record the run.
Adds the lookup that closes the gap between what is on screen and what is in the markdown. A display run already knew its node and where it starts inside that node's text, and the node now carries its source range, so a character in the preview can be traced back to a character in the source. TMarkdownSourceLookup holds the rule, including the case it refuses: when a node's rendered text is a different length from the source it came from, an escape or an entity has changed the length and positions inside it no longer line up, so it reports nothing rather than landing on the wrong character. TMarkdownViewerModel.TrySelectedSourceSpan walks the selected runs and returns the span they cover, failing if any run in the selection cannot be mapped. Eight tests, including a selection sitting after emphasis, where the asterisks are in the source but never on screen.
Completes the path from a selection on screen to an edit in the source. The editor control gains a public SetSelection so a caller that has worked out where it wants to act can act there, and the viewer control exposes the source span of its selection. Both studios now adopt that span before a formatting command runs, so selecting a word in the rendered preview and pressing Bold wraps exactly those characters. A selection that cannot be traced back exactly is left alone and the command falls back to the editor's own selection, rather than guessing at the wrong characters. When the command came from the preview while preview only mode is active, the view switches to split first, so the change lands where it can be seen instead of out of sight. Verified end to end: selecting a word in the preview, pressing Bold and saving writes the markers around exactly that word.
Author
|
Withdrawing this for now: it needs more work before it is worth your time. I will reopen or resubmit once it is verified end to end. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #10, which is included here until that merges. Independent of #7, #8 and #9.
What this does
Select text in the rendered preview, press Bold, and exactly those characters become bold in the markdown.
Before this, a formatting command always acted on the editor's own selection, so in preview only mode it applied at a caret you could not see.
Why it needed the parser
The rendered preview is not the source. Emphasis markers, link syntax, escapes and entities are gone by the time text reaches the screen, so a character offset in the preview does not line up with one in the markdown.
Worse, the mapping could not be a single offset. A block's inline content is assembled line by line with markers already stripped, a block quote losing its
>and a list item its bullet, then trimmed before the inline parser sees it. Content and source drift apart repeatedly. Only block nodes carried a source range; inline nodes had none.How it is built, one commit per layer
Markdown4D.Parser.SourceMaprecords the content to source relationship as runs, each run being one stretch that is contiguous in the source. Characters with no source, such as the spaces a tab expands into, fall in no run.TMarkdownSourceLookupandTrySelectedSourceSpanturn a selection into a source span. A display run already knew its node and where it starts inside that node's text, so no new field was needed on the display list.What it refuses to do
Anything it cannot trace exactly reports nothing rather than guessing, and the command falls back to the editor's own selection:
Testing
19 new tests across four suites, each written before the code and confirmed failing first.
The FMX studio has identical wiring and shares every tested rule, but was not driven interactively, since its Skia canvas does not capture reliably for scripted checks on my machine.