diff --git a/docs/README.md b/docs/README.md
index 300075e8..9f53dca3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -22,7 +22,7 @@ listed in are supported (including support for *
**markdown tables**, **images** and **syntax highlight**).
Since version **Markwon** comes with an [editor] to _highlight_ markdown input
-as user types for example in **EditText**.
+as user types (for example in **EditText**).
[editor]: /docs/v4/editor/
diff --git a/docs/docs/v4/editor/README.md b/docs/docs/v4/editor/README.md
index 9af9bf6c..6477643b 100644
--- a/docs/docs/v4/editor/README.md
+++ b/docs/docs/v4/editor/README.md
@@ -18,8 +18,116 @@ video {
## Getting started with editor
+```java
+// obtain Markwon instance
+final Markwon markwon = Markwon.create(this);
+
+// create editor
+final MarkwonEditor editor = MarkwonEditor.create(markwon);
+
+// set edit listener
+editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor));
+```
+
+The code above _highlights_ in-place which is OK for relatively small markdown inputs.
+If you wish to offload main thread and highlight in background use `withPreRender`
+`MarkwonEditorTextWatcher`:
+
+```java
+editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(
+ editor,
+ Executors.newCachedThreadPool(),
+ editText));
+```
+
+`MarkwonEditorTextWatcher` automatically triggers markdown highlight when text in `EditText` changes.
+But you still can invoke `MarkwonEditor` manually:
+
+```java
+editor.process(editText.getText());
+
+// please note that MarkwonEditor operates on caller thread,
+// if you wish to execute this operation in background - this method
+// must be called from background thread
+editor.preRender(editText.getText(), new MarkwonEditor.PreRenderResultListener() {
+ @Override
+ public void onPreRenderResult(@NonNull MarkwonEditor.PreRenderResult result) {
+ // it's wise to check if rendered result is for the same input,
+ // for example by matching raw input
+ if (editText.getText().toString().equals(result.resultEditable().toString())) {
+
+ // if you are in background thread do not forget
+ // to execute dispatch in main thread
+ result.dispatchTo(editText.getText());
+ }
+ }
+});
+```
+
:::warning Implementation Detail
It must be mentioned that highlight is implemented via text diff. Everything
-that is present in raw markdown input and missing from rendered result is considered
+that is present in raw markdown input but missing from rendered result is considered
to be _punctuation_.
-:::
\ No newline at end of file
+:::
+
+:::danger Tables and LaTeX
+Tables and LaTeX nodes won't be rendered correctly. They will be treated as _punctuation_
+as whole. This comes from their implementation - they are _mocked_ and do not present
+in final result as text and thus cannot be _diffed_.
+:::
+
+## Custom punctuation span
+
+By default `MarkwonEditor` uses lighter text color of widget to customize punctuation.
+If you wish to use a different span you can use `withPunctuationSpan` configuration step:
+
+```java
+final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
+ .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new)
+ .build();
+```
+
+```java
+public class CustomPunctuationSpan extends ForegroundColorSpan {
+ CustomPunctuationSpan() {
+ super(0xFFFF0000); // RED
+ }
+}
+```
+
+## Additional handling
+
+In order to additionally highlight portions of markdown input (for example make text wrapped with `**`
+symbols **bold**) `EditSpanHandler` can be used:
+
+```java
+final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
+ // This is required for edit-span cache
+ // We could use Markwon `StrongEmphasisSpan` here, but I use a different
+ // one to indicate that those are completely unrelated spans and must be
+ // treated differently.
+ .includeEditSpan(Bold.class, Bold::new)
+ .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() {
+ @Override
+ public void handle(
+ @NonNull MarkwonEditor.EditSpanStore store,
+ @NonNull Editable editable,
+ @NonNull String input,
+ @NonNull Object span,
+ int spanStart,
+ int spanTextLength) {
+ if (span instanceof StrongEmphasisSpan) {
+ editable.setSpan(
+ // `includeEditSpan(Bold.class, Bold::new)` ensured that we have
+ // a span here to use (either reuse existing or create a new one)
+ store.get(Bold.class),
+ spanStart,
+ // we know that strong emphasis is delimited with 2 characters on both sides
+ spanStart + spanTextLength + 4,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+ );
+ }
+ }
+ })
+ .build();
+```
diff --git a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java
index 271b6b69..33c79ad8 100644
--- a/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java
+++ b/sample/src/main/java/io/noties/markwon/sample/editor/EditorActivity.java
@@ -64,13 +64,13 @@ public class EditorActivity extends Activity {
private void simple_process() {
// Process highlight in-place (right after text has changed)
- // obtain Markwon instance
+// obtain Markwon instance
final Markwon markwon = Markwon.create(this);
- // create editor
+// create editor
final MarkwonEditor editor = MarkwonEditor.create(markwon);
- // set edit listener
+// set edit listener
editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor));
}
@@ -80,6 +80,25 @@ public class EditorActivity extends Activity {
final Markwon markwon = Markwon.create(this);
final MarkwonEditor editor = MarkwonEditor.create(markwon);
+editor.process(editText.getText());
+
+// please note that MarkwonEditor operates on caller thread,
+// fi you wish to execute this operation in background - this method
+// must be called from background thread
+editor.preRender(editText.getText(), new MarkwonEditor.PreRenderResultListener() {
+ @Override
+ public void onPreRenderResult(@NonNull MarkwonEditor.PreRenderResult result) {
+ // it's wise to check if rendered result is for the same input,
+ // for example by matching raw input
+ if (editText.getText().toString().equals(result.resultEditable().toString())) {
+
+ // if you are in background thread do not forget
+ // to execute dispatch in main thread
+ result.dispatchTo(editText.getText());
+ }
+ }
+});
+
editText.addTextChangedListener(MarkwonEditorTextWatcher.withPreRender(
editor,
Executors.newCachedThreadPool(),
@@ -89,9 +108,9 @@ public class EditorActivity extends Activity {
private void custom_punctuation_span() {
// Use own punctuation span
- final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
- .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new)
- .build();
+final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
+ .withPunctuationSpan(CustomPunctuationSpan.class, CustomPunctuationSpan::new)
+ .build();
editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor));
}
@@ -99,35 +118,35 @@ public class EditorActivity extends Activity {
private void additional_edit_span() {
// An additional span is used to highlight strong-emphasis
- final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
- // This is required for edit-span cache
- // We could use Markwon `StrongEmphasisSpan` here, but I use a different
- // one to indicate that those are completely unrelated spans and must be
- // treated differently.
- .includeEditSpan(Bold.class, Bold::new)
- .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() {
- @Override
- public void handle(
- @NonNull MarkwonEditor.EditSpanStore store,
- @NonNull Editable editable,
- @NonNull String input,
- @NonNull Object span,
- int spanStart,
- int spanTextLength) {
- if (span instanceof StrongEmphasisSpan) {
- editable.setSpan(
- // `includeEditSpan(Bold.class, Bold::new)` ensured that we have
- // a span here to use (either reuse existing or create a new one)
- store.get(Bold.class),
- spanStart,
- // we know that strong emphasis is delimited with 2 characters on both sides
- spanStart + spanTextLength + 4,
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
- );
- }
- }
- })
- .build();
+final MarkwonEditor editor = MarkwonEditor.builder(Markwon.create(this))
+ // This is required for edit-span cache
+ // We could use Markwon `StrongEmphasisSpan` here, but I use a different
+ // one to indicate that those are completely unrelated spans and must be
+ // treated differently.
+ .includeEditSpan(Bold.class, Bold::new)
+ .withEditSpanHandler(new MarkwonEditor.EditSpanHandler() {
+ @Override
+ public void handle(
+ @NonNull MarkwonEditor.EditSpanStore store,
+ @NonNull Editable editable,
+ @NonNull String input,
+ @NonNull Object span,
+ int spanStart,
+ int spanTextLength) {
+ if (span instanceof StrongEmphasisSpan) {
+ editable.setSpan(
+ // `includeEditSpan(Bold.class, Bold::new)` ensured that we have
+ // a span here to use (either reuse existing or create a new one)
+ store.get(Bold.class),
+ spanStart,
+ // we know that strong emphasis is delimited with 2 characters on both sides
+ spanStart + spanTextLength + 4,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+ );
+ }
+ }
+ })
+ .build();
editText.addTextChangedListener(MarkwonEditorTextWatcher.withProcess(editor));
}
@@ -316,11 +335,11 @@ public class EditorActivity extends Activity {
}
}
- private static class CustomPunctuationSpan extends ForegroundColorSpan {
- CustomPunctuationSpan() {
- super(0xFFFF0000); // RED
- }
+private static class CustomPunctuationSpan extends ForegroundColorSpan {
+ CustomPunctuationSpan() {
+ super(0xFFFF0000); // RED
}
+}
private static class Bold extends MetricAffectingSpan {
public Bold() {