Skip to content

Feature/big file fast load recyclerview#2770

Draft
lrq3000 wants to merge 18 commits intogsantner:masterfrom
lrq3000:feature/big-file-fast-load-recyclerview
Draft

Feature/big file fast load recyclerview#2770
lrq3000 wants to merge 18 commits intogsantner:masterfrom
lrq3000:feature/big-file-fast-load-recyclerview

Conversation

@lrq3000
Copy link
Copy Markdown
Contributor

@lrq3000 lrq3000 commented Mar 14, 2026

This PR implements a new EditText using RecyclerView to load and allow to edit large files instantaneously

Summary

In summary, this is done by making a new shared interface MarkorEditor and a subclass RecyclerTextEditor the classic editor HighlightingEditor to also be a child of MarkorEditor, and wire DocumentEditAndViewFragment to use _activeEditor for both editor types, so format features work in recycler mode while undo/redo remains non-recycler-only.

All features are implemented because inherited.

The RecyclerTextEditor is only enabled for large files. The threshold is about 512 KB and is defined here:

RECYCLER_EDITOR_FILE_BYTES_THRESHOLD = 512L * 1024L (1,048,576 bytes)

I defined this threshold empirically from my own dataset and phone. Of course this will probably be different for each phone, but as a first implementation, I think it's best to keep it simple, the threshold can be made dynamical later if the static one is not good enough. Or i can be made into a user-facing option, that would be easy I think.

Implementation details

  • Introduce and wire MarkorEditor as the common editor abstraction across normal and large-file modes.
  • Implement full MarkorEditor support in RecyclerTextEditor, including global selection/cursor mapping, token-aware text insertion, search/highlight state hooks, and watcher forwarding across line-based cells.
  • Update DocumentEditAndViewFragment to use _activeEditor for both editor types, enabling format actions and search in large-file mode while keeping undo/redo limited to non-recycler mode.

This was achieved through composition + common contract (since multi-subclass inheritance is not possible in Java), so we implemented a shared interface: MarkorEditor. Both concrete editors implement it:

  • HighlightingEditor (classic EditText)
  • RecyclerTextEditor (large-file RecyclerView)

Format behaviors already live int he format system (FormatRegistry + ActionButtonBase + format action classes) so we reused that.

DocumentEditAndViewFragment now routes those format features through _activeEditor (MarkorEditor), which points to either editor depending on file size.

Fixes

This PR is a follow-up to PR #2769 (so #2769's commits are also in the history here, this will require a rebase after #2769 gets merged).

This fixes definitely issues related to performance with large files, including #2739, #2716, #2652 .

Test builds

Test builds are available here: https://github.com/lrq3000/markor/actions/runs/23093127494

Authorship

This PR was made by Stephen Karl Larroque (lrq3000) using AI agentic coding (OpenCode and Oh My Opencode with ChatGPT Codex-5.3 and ulw mode in the prompt).

No third-party codebase was used and no internet connection was established by the AI agent during the development of this PR.

Todo

  • Maintainers careful review of all the changes, as this is quite a big PR with a lot of changes.

Restore one-shot min-height initialization and avoid per-layout min-height churn, with guardrail comments to prevent reintroducing startup relayout regressions.

This fixes a major speed regression introduced in commit 64ff85b in PR gsantner#2521 (squashed into b2f4407) that made loading of big files and opening the keyboard incredibly slow and proportional to the file size.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode with ChatGPT Codex-5.3 + git-bisect
lrq3000 added 5 commits March 17, 2026 01:03
…ditorMinHeightOnce() to do one-shot editor min-height sync to prevent large-file open slowdown + cover more cases as originally intended in 64ff85b

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Signed-off-by: Stephen L. <LRQ3000@gmail.com>
…ditor to load and edit big files instantaneously

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
…in RecyclerTextEditor

Implement MarkorEditor in RecyclerTextEditor and wire DocumentEditAndViewFragment to use _activeEditor for both editor types, so format features work in recycler mode while undo/redo remains non-recycler-only.

Details:

* Introduce and wire MarkorEditor as the common editor abstraction across normal and large-file modes.
* Implement full MarkorEditor support in RecyclerTextEditor, including global selection/cursor mapping, token-aware text insertion, search/highlight state hooks, and watcher forwarding across line-based cells.
* Update DocumentEditAndViewFragment to use _activeEditor for both editor types, enabling format actions and search in large-file mode while keeping undo/redo limited to non-recycler mode.

This was achieved through composition + common contract (since multi-subclass inheritance is not possible in Java), so we implemented a shared interface: `MarkorEditor`. Both concrete editors implement it:
* `HighlightingEditor` (classic EditText)
* `RecyclerTextEditor` (large-file RecyclerView)

Format behaviors already live int he format system (FormatRegistry + ActionButtonBase + format action classes) so we reused that.

DocumentEditAndViewFragment now routes those format features through _activeEditor (MarkorEditor), which points to either editor depending on file size.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode and Oh My Opencode with ChatGPT Codex-5.3
Signed-off-by: Stephen L. <LRQ3000@gmail.com>
@lrq3000 lrq3000 force-pushed the feature/big-file-fast-load-recyclerview branch from 9305ca5 to f71936f Compare March 17, 2026 00:51
…s editor using RecyclerView

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
@lrq3000 lrq3000 marked this pull request as draft March 17, 2026 01:59
@lrq3000
Copy link
Copy Markdown
Contributor Author

lrq3000 commented Mar 17, 2026

PR not yet ready, there are missing features such as syntax highlighting and the actions are not working.

I will make this PR ready for review when I myself will be using this large files PR as my main Markor instance for a couple of days.

lrq3000 added 5 commits March 17, 2026 03:22
…yclerTextEditor large files text editor

The root causes were a mix of hard feature gates in the fragment (search/title actions/action-bar recreation disabled in recycler mode), headline dialogs still requiring `EditText`, and a non-live recycler `Editable` path that made many action-button edits not persist. I fixed those and also wired recycler highlighting startup so syntax highlighting now actually turns on for large files.

**What is now fixed**
- Recycler mode now supports top-bar title click and long-click actions (TOC + jump top/bottom) through the same format action pipeline as classic editor via `app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java`.
- Search action is enabled in recycler mode (removed large-file block) in `app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java`.
- Bottom formatting bar now recreates correctly on edit/preview toggles in recycler mode in `app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java`.
- Headline dialog was generalized to `MarkorEditor` (no unsafe recycler cast), and markdown/wikitext now call that path:
  - `app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java`
  - `app/src/main/java/net/gsantner/markor/format/markdown/MarkdownActionButtons.java`
  - `app/src/main/java/net/gsantner/markor/format/wikitext/WikitextActionButtons.java`
- `RecyclerTextEditor` now uses a live editor-backed `Editable` and syncs mutations back into line rows; action-button text mutations persist and highlighting spans are rendered per visible row:
  - `app/src/main/java/net/gsantner/markor/frontend/textview/RecyclerTextEditor.java`
- Highlighting is now enabled on startup for whichever editor is active (classic or recycler) in `app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java`.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
using OpenCode and Oh My OpenCode with ChatGPT-Codex-5.3 and ultrawork mode
…yclerTextEditor

The cursor/focus drift came from recycler highlighting refresh rebinding rows and writing selection back during programmatic setText

RecyclerTextEditor now suppresses selection sync during programmatic row text rebinding and no longer full-rebinds on each highlight recompute; it refreshes visible non-focused rows only. Changes are in app/src/main/java/net/gsantner/markor/frontend/textview/RecyclerTextEditor.java.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode and Oh My OpenCode with ChatGPT Codex-5.3 and ultrawork mode
… assumptions that depended on the classical MarkorEditor

Removed the remaining classic-view assumptions from todo actions so TodoTxtActionButtons no longer uses (EditText)/(TextView) _hlEditor.getView() in recycler mode. The changes are in app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtActionButtons.java (context/project long-press, title-click filtering, search, archive selection restore, selected-task extraction, due-date lookup), plus supporting MarkorEditor-safe overloads in app/src/main/java/net/gsantner/markor/frontend/MarkorDialogFactory.java, a text+selection overload in app/src/main/java/net/gsantner/markor/format/todotxt/TodoTxtTask.java, and MarkorEditor line-selection support in app/src/main/java/net/gsantner/markor/frontend/textview/TextViewUtils.java.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode with Oh My OpenCode and ChatGPT-Codex-5.3 in ultrawork mode
Signed-off-by: Stephen L. <LRQ3000@gmail.com>
…actory

Introduce MarkorEditor-based overloads for todo.txt dialog flows so RecyclerTextEditor can use
the same filtering/search/key-selection features without relying on EditText-only APIs.

This adds MarkorEditor variants of:
- showSttFilteringDialog(...)
- showSttKeySearchDialog(...)
- makeSttLineSelectionDialog(...)
- showSttSearchDialog(...)

The new overloads preserve existing behavior (saved filters, advanced filtering, key aggregation,
AND/ANY toggle, selection dialog rendering, and replace flow) while operating on editor text and
selection through MarkorEditor abstractions.

This change removes a key source of recycler-mode crashes and keeps classic EditText callers
compatible through existing method signatures.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode and Oh My OpenCode with ChatGPT Codex-5.3 and ultrawork mode
@lrq3000 lrq3000 force-pushed the feature/big-file-fast-load-recyclerview branch from b81249e to f0ba331 Compare March 17, 2026 11:59
lrq3000 and others added 2 commits March 17, 2026 13:16
Introduced by merging stashed changes across multiple parallel branches.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
…cyclerTextEditor

Merge pull request #1 from lrq3000/fix-todotxt-crash-6284688702615462401

*   Added `getEditorPaint()` to `RecyclerTextEditor` to provide a properly configured `Paint` instance to the syntax highlighter.
*   Modified `TodoTxtSyntaxHighlighter.configure(Paint)` to initialize a new `Paint` object if `null` is passed, preventing `NullPointerException` when fetching font metrics.

By lrq3000
Using Jules with Gemini 3.1 Pro Preview
@lrq3000
Copy link
Copy Markdown
Contributor Author

lrq3000 commented Mar 17, 2026

Progress update: at this point, the app is now stable again, it can open large markdown files but also large todo.txt files, and all actions work by tapping buttons and also by autoformatting (eg, newline in a bullet list will create a new bullet item). In-file navigation such as filter by task priority or markdown table of contents are working too.

Syntax highlighting and undo/redo are still missing.

Test build (will install as Marder, not Markor) available here: https://github.com/lrq3000/markor/actions/runs/23205791477

@harshad1
Copy link
Copy Markdown
Collaborator

what is the bottleneck for the existing editor? iirc it was layout. Can we just speed that up?

@lrq3000
Copy link
Copy Markdown
Contributor Author

lrq3000 commented Mar 19, 2026

The AutoFormat causes several tricky bugs, some of which are already present in v2.16.0 (hence before merging PR #2769 speed regression fix).

One example:

If you have a list like this:

1. item a
2. item b


(the two newlines after the list are necessary)

Then select these two lines and copy, then close Markor, reopen it, and paste these two lines at the end, you will get:

1. item a
2. item b

3. item a
4. item b

So far so good. Now try to undo. You get this:

1. item a
2. item b

1. item a
2. item b

And the undo history is exhausted at this point, so you cannot restore the true original state before the multi-lines paste.

This happens because of Autoformat messing up the undo history by modifying the input, and it has various side effects that are different for single lines and multiple lines edits/pastes.

I spotted this because the AI tipped me on Autoformat acting differently for multi-line edits compared to single line edits while improving this PR.

I am not opening a separate issue as I suggest that I fix as many of these autoformat side effects for the classical Markor editor while doing the same for the RecyclerTextEditor (since in this PR both are inheriting from a single parent editor class, so we can try to make fixes that work for both).

@lrq3000
Copy link
Copy Markdown
Contributor Author

lrq3000 commented Mar 19, 2026

@harshad1 the UI redraw bottleneck was fixed in #2769, so we got back the same speed as before now that Markor v2.16.1 got released, which is already very good.

However, there will always be a limitation with the current implementation because the whole file needs to be loaded in RAM and drawn all at once, so the loading speed is proportional to the file size.

This PR implements a second editor class that leverages the RecyclerView class which allows to load and draw only a part of a file if I understood correctly.

In practice, this allows to decouple the loading delay from the file size, so that opening files of any size is effectively instantaneous.

This is however an experimental approach than no other stable editor ever implemented on Android, the only precedent is BigEditText which was just a proof of concept. This is however not a new concept at all in computer science, it's actually quite old, and lots of editors implement it on desktop OSes.

google-labs-jules bot and others added 3 commits March 20, 2026 01:57
…rTextEditor

* Replaced `notifyDataSetChangedPreservingFocus()` with `refreshVisibleLineEditors(false)` inside `recomputeHighlighting()`. This prevents the currently focused EditText from completely rebinding while the user is typing or composing text, preserving IME composing spans and thus fixing erratic cursor jumps when word corrections are offered.
* Added a check for newlines (`\n`) in `LineViewHolder`'s `afterTextChanged` method. When a newline is detected, `syncFromEditorText()` is explicitly called. This ensures that the RecyclerTextEditor immediately parses and splits the single line into multiple lines when the `AutoTextFormatter` inserts list prefixes, resulting in the cursor being properly placed at the end of the newly created list item instead of appearing stuck on the current line.

Co-authored-by: lrq3000 <1118942+lrq3000@users.noreply.github.com>
…ewline

* Replaced `notifyDataSetChangedPreservingFocus()` with `refreshVisibleLineEditors(false)` inside `recomputeHighlighting()`. This stops the focused `EditText` from fully rebinding while typing, preserving active IME composing states and fixing the erratic cursor jump when spell check autocorrect is offered.
* Added logic in `LineViewHolder.afterTextChanged` to manually split lines when a newline is inserted by `AutoTextFormatter` without forcing a full document sync or `notifyDataSetChanged()`. This successfully shifts the cursor to the end of the newly pushed string block and uses `notifyItemInserted()` to let the cursor reliably transition down seamlessly without locking up on the previous line.

Co-authored-by: lrq3000 <1118942+lrq3000@users.noreply.github.com>
… handling

Refresh visible editors including the focused line after highlight recomputation to prevent stale spans while typing. Improve newline split behavior by using pre-edit line length for cursor offset calculation, falling back to full sync for multi-line edits, and preserving cursor handoff when the next row view holder is not yet available.

This implements fixes for all the 3 issues detected by AI reviewers (coderabbitai and chatgpt-codex).

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode and Oh My OpenCode with ChatGPT Codex-5.3 and ultrawork mode + plan mode.
… line in RecyclerTextEditor

Traced the issue to a missing key-event path: when a row is empty and cursor is at column 0, TextWatcher does not fire a text deletion, so line-removal logic never runs. I fixed this in RecyclerTextEditor.java:788 by adding a minimal KEYCODE_DEL handler in LineViewHolder.bind(...) that only triggers for Backspace at start-of-line on non-first rows, merges current line into previous line (generalizable behavior, including empty-line delete), updates adapter rows, recalculates global selection, syncs _editorText, notifies text watchers, and restores focus/cursor to the previous line with a fallback when holder is not yet laid out.

Signed-off-by: Stephen L. <LRQ3000@gmail.com>
Using OpenCode and Oh My OpenCode with ChatGPT Codex-5.3 and ultrawork mode
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

Successfully merging this pull request may close these issues.

2 participants