Core functionality plugin
This commit is contained in:
		
							parent
							
								
									498c811987
								
							
						
					
					
						commit
						3526e16565
					
				| @ -41,17 +41,17 @@ | |||||||
|                 <!--<data--> |                 <!--<data--> | ||||||
|                 <!--android:host="*"--> |                 <!--android:host="*"--> | ||||||
|                 <!--android:scheme="http"--> |                 <!--android:scheme="http"--> | ||||||
|                 <!--android:mimeType="text/markdown"/>--> |                 <!--android:mimeType="text/toMarkdown"/>--> | ||||||
| 
 | 
 | ||||||
|                 <!--<data--> |                 <!--<data--> | ||||||
|                 <!--android:host="*"--> |                 <!--android:host="*"--> | ||||||
|                 <!--android:scheme="file"--> |                 <!--android:scheme="file"--> | ||||||
|                 <!--android:mimeType="text/markdown"/>--> |                 <!--android:mimeType="text/toMarkdown"/>--> | ||||||
| 
 | 
 | ||||||
|                 <!--<data--> |                 <!--<data--> | ||||||
|                 <!--android:host="*"--> |                 <!--android:host="*"--> | ||||||
|                 <!--android:scheme="https"--> |                 <!--android:scheme="https"--> | ||||||
|                 <!--android:mimeType="text/markdown"/>--> |                 <!--android:mimeType="text/toMarkdown"/>--> | ||||||
| 
 | 
 | ||||||
|                 <data android:pathPattern=".*\\.markdown" /> |                 <data android:pathPattern=".*\\.markdown" /> | ||||||
|                 <data android:pathPattern=".*\\.mdown" /> |                 <data android:pathPattern=".*\\.mdown" /> | ||||||
|  | |||||||
| @ -71,7 +71,7 @@ public class MainActivity extends Activity { | |||||||
|                     .use(new CorePlugin()) |                     .use(new CorePlugin()) | ||||||
|                     .use(TaskListPlugin.create(new TaskListDrawable(0xffff0000, 0xffff0000, -1))) |                     .use(TaskListPlugin.create(new TaskListDrawable(0xffff0000, 0xffff0000, -1))) | ||||||
|                     .build(); |                     .build(); | ||||||
|             final CharSequence markdown = markwon2.markdown("**hello _dear_** `code`\n\n- [ ] first\n- [x] second"); |             final CharSequence markdown = markwon2.toMarkdown("**hello _dear_** `code`\n\n- [ ] first\n- [x] second"); | ||||||
|             textView.setText(markdown); |             textView.setText(markdown); | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -104,7 +104,7 @@ public class MarkdownRenderer { | |||||||
| 
 | 
 | ||||||
|                 final long end = SystemClock.uptimeMillis(); |                 final long end = SystemClock.uptimeMillis(); | ||||||
| 
 | 
 | ||||||
|                 Debug.i("markdown rendered: %d ms", end - start); |                 Debug.i("toMarkdown rendered: %d ms", end - start); | ||||||
| 
 | 
 | ||||||
|                 if (!isCancelled()) { |                 if (!isCancelled()) { | ||||||
|                     handler.post(new Runnable() { |                     handler.post(new Runnable() { | ||||||
|  | |||||||
| @ -60,7 +60,7 @@ public class MarkwonViewHelper implements IMarkwonView { | |||||||
|         this.provider = provider; |         this.provider = provider; | ||||||
|         this.configuration = provider.provide(textView.getContext()); |         this.configuration = provider.provide(textView.getContext()); | ||||||
|         if (!TextUtils.isEmpty(markdown)) { |         if (!TextUtils.isEmpty(markdown)) { | ||||||
|             // invalidate rendered markdown |             // invalidate rendered toMarkdown | ||||||
|             setMarkdown(markdown); |             setMarkdown(markdown); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -35,12 +35,12 @@ public abstract class AbstractMarkwonPlugin implements MarkwonPlugin { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { |     public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void afterSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { |     public void afterSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { | ||||||
| 
 | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -48,12 +48,12 @@ public abstract class Markwon { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Parses submitted raw markdown, converts it to CharSequence (with Spannables) |      * Parses submitted raw toMarkdown, converts it to CharSequence (with Spannables) | ||||||
|      * and applies it to view |      * and applies it to view | ||||||
|      * |      * | ||||||
|      * @param view          {@link TextView} to set markdown into |      * @param view          {@link TextView} to set toMarkdown into | ||||||
|      * @param configuration a {@link MarkwonConfiguration} instance |      * @param configuration a {@link MarkwonConfiguration} instance | ||||||
|      * @param markdown      raw markdown String (for example: {@code `**Hello**`}) |      * @param markdown      raw toMarkdown String (for example: {@code `**Hello**`}) | ||||||
|      * @see #markdown(MarkwonConfiguration, String) |      * @see #markdown(MarkwonConfiguration, String) | ||||||
|      * @see #setText(TextView, CharSequence) |      * @see #setText(TextView, CharSequence) | ||||||
|      * @see MarkwonConfiguration |      * @see MarkwonConfiguration | ||||||
| @ -69,13 +69,13 @@ public abstract class Markwon { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Helper method to apply parsed markdown. |      * Helper method to apply parsed toMarkdown. | ||||||
|      * <p> |      * <p> | ||||||
|      * Since 1.0.6 redirects it\'s call to {@link #setText(TextView, CharSequence, MovementMethod)} |      * Since 1.0.6 redirects it\'s call to {@link #setText(TextView, CharSequence, MovementMethod)} | ||||||
|      * with LinkMovementMethod as an argument to preserve current API. |      * with LinkMovementMethod as an argument to preserve current API. | ||||||
|      * |      * | ||||||
|      * @param view {@link TextView} to set markdown into |      * @param view {@link TextView} to set toMarkdown into | ||||||
|      * @param text parsed markdown |      * @param text parsed toMarkdown | ||||||
|      * @see #setText(TextView, CharSequence, MovementMethod) |      * @see #setText(TextView, CharSequence, MovementMethod) | ||||||
|      * @since 1.0.0 |      * @since 1.0.0 | ||||||
|      */ |      */ | ||||||
| @ -84,13 +84,13 @@ public abstract class Markwon { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Helper method to apply parsed markdown with additional argument of a MovementMethod. Used |      * Helper method to apply parsed toMarkdown with additional argument of a MovementMethod. Used | ||||||
|      * to workaround problems that occur when using system LinkMovementMethod (for example: |      * to workaround problems that occur when using system LinkMovementMethod (for example: | ||||||
|      * https://issuetracker.google.com/issues/37068143). As a better alternative to it consider |      * https://issuetracker.google.com/issues/37068143). As a better alternative to it consider | ||||||
|      * using: https://github.com/saket/Better-Link-Movement-Method |      * using: https://github.com/saket/Better-Link-Movement-Method | ||||||
|      * |      * | ||||||
|      * @param view           TextView to set markdown into |      * @param view           TextView to set toMarkdown into | ||||||
|      * @param text           parsed markdown |      * @param text           parsed toMarkdown | ||||||
|      * @param movementMethod an implementation if MovementMethod or null |      * @param movementMethod an implementation if MovementMethod or null | ||||||
|      * @see #scheduleDrawables(TextView) |      * @see #scheduleDrawables(TextView) | ||||||
|      * @see #scheduleTableRows(TextView) |      * @see #scheduleTableRows(TextView) | ||||||
| @ -102,7 +102,7 @@ public abstract class Markwon { | |||||||
|         unscheduleTableRows(view); |         unscheduleTableRows(view); | ||||||
| 
 | 
 | ||||||
|         // @since 2.0.1 we must measure ordered-list-item-spans before applying text to a TextView. |         // @since 2.0.1 we must measure ordered-list-item-spans before applying text to a TextView. | ||||||
|         // if markdown has a lot of ordered list items (or text size is relatively big, or block-margin |         // if toMarkdown has a lot of ordered list items (or text size is relatively big, or block-margin | ||||||
|         // is relatively small) then this list won't be rendered properly: it will take correct |         // is relatively small) then this list won't be rendered properly: it will take correct | ||||||
|         // layout (width and margin) but will be clipped if margin is not _consistent_ between calls. |         // layout (width and margin) but will be clipped if margin is not _consistent_ between calls. | ||||||
|         OrderedListItemSpan.measure(view, text); |         OrderedListItemSpan.measure(view, text); | ||||||
| @ -117,11 +117,11 @@ public abstract class Markwon { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Returns parsed markdown with default {@link MarkwonConfiguration} obtained from {@link Context} |      * Returns parsed toMarkdown with default {@link MarkwonConfiguration} obtained from {@link Context} | ||||||
|      * |      * | ||||||
|      * @param context  {@link Context} |      * @param context  {@link Context} | ||||||
|      * @param markdown raw markdown |      * @param markdown raw toMarkdown | ||||||
|      * @return parsed markdown |      * @return parsed toMarkdown | ||||||
|      * @since 1.0.0 |      * @since 1.0.0 | ||||||
|      */ |      */ | ||||||
|     @NonNull |     @NonNull | ||||||
| @ -131,11 +131,11 @@ public abstract class Markwon { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Returns parsed markdown with provided {@link MarkwonConfiguration} |      * Returns parsed toMarkdown with provided {@link MarkwonConfiguration} | ||||||
|      * |      * | ||||||
|      * @param configuration a {@link MarkwonConfiguration} |      * @param configuration a {@link MarkwonConfiguration} | ||||||
|      * @param markdown      raw markdown |      * @param markdown      raw toMarkdown | ||||||
|      * @return parsed markdown |      * @return parsed toMarkdown | ||||||
|      * @see MarkwonConfiguration |      * @see MarkwonConfiguration | ||||||
|      * @since 1.0.0 |      * @since 1.0.0 | ||||||
|      */ |      */ | ||||||
|  | |||||||
| @ -2,6 +2,7 @@ package ru.noties.markwon; | |||||||
| 
 | 
 | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
|  | import android.widget.TextView; | ||||||
| 
 | 
 | ||||||
| import org.commonmark.node.Node; | import org.commonmark.node.Node; | ||||||
| 
 | 
 | ||||||
| @ -18,8 +19,13 @@ public abstract class Markwon2 { | |||||||
|     @NonNull |     @NonNull | ||||||
|     public abstract CharSequence render(@NonNull Node node); |     public abstract CharSequence render(@NonNull Node node); | ||||||
| 
 | 
 | ||||||
|  |     // parse + render | ||||||
|     @NonNull |     @NonNull | ||||||
|     public abstract CharSequence markdown(@NonNull String input); |     public abstract CharSequence toMarkdown(@NonNull String input); | ||||||
|  | 
 | ||||||
|  |     public abstract void setMarkdown(@NonNull TextView textView, @NonNull String markdown); | ||||||
|  | 
 | ||||||
|  |     public abstract void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown); | ||||||
| 
 | 
 | ||||||
|     public interface Builder { |     public interface Builder { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,7 +46,7 @@ class MarkwonBuilderImpl implements Markwon2.Builder { | |||||||
| 
 | 
 | ||||||
|         return new MarkwonImpl( |         return new MarkwonImpl( | ||||||
|                 parserBuilder.build(), |                 parserBuilder.build(), | ||||||
|                 visitorBuilder.build(themeBuilder.build(), configurationBuilder.build()), |                 visitorBuilder.build(configurationBuilder.build(themeBuilder.build())), | ||||||
|                 Collections.unmodifiableList(plugins) |                 Collections.unmodifiableList(plugins) | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ public class MarkwonConfiguration { | |||||||
|     // creates default configuration |     // creates default configuration | ||||||
|     @NonNull |     @NonNull | ||||||
|     public static MarkwonConfiguration create(@NonNull Context context) { |     public static MarkwonConfiguration create(@NonNull Context context) { | ||||||
|         return new Builder(context).build(); |         return new Builder(context).build(MarkwonTheme.create(context)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
| @ -28,9 +28,8 @@ public class MarkwonConfiguration { | |||||||
|         return new Builder(context); |         return new Builder(context); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Deprecated |  | ||||||
|     private final MarkwonTheme theme; |  | ||||||
| 
 | 
 | ||||||
|  |     private final MarkwonTheme theme; | ||||||
|     private final AsyncDrawable.Loader asyncDrawableLoader; |     private final AsyncDrawable.Loader asyncDrawableLoader; | ||||||
|     private final SyntaxHighlight syntaxHighlight; |     private final SyntaxHighlight syntaxHighlight; | ||||||
|     private final LinkSpan.Resolver linkResolver; |     private final LinkSpan.Resolver linkResolver; | ||||||
| @ -65,7 +64,6 @@ public class MarkwonConfiguration { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Deprecated |  | ||||||
|     public MarkwonTheme theme() { |     public MarkwonTheme theme() { | ||||||
|         return theme; |         return theme; | ||||||
|     } |     } | ||||||
| @ -137,7 +135,6 @@ public class MarkwonConfiguration { | |||||||
| 
 | 
 | ||||||
|         private final Context context; |         private final Context context; | ||||||
| 
 | 
 | ||||||
|         @Deprecated |  | ||||||
|         private MarkwonTheme theme; |         private MarkwonTheme theme; | ||||||
|         private AsyncDrawable.Loader asyncDrawableLoader; |         private AsyncDrawable.Loader asyncDrawableLoader; | ||||||
|         private SyntaxHighlight syntaxHighlight; |         private SyntaxHighlight syntaxHighlight; | ||||||
| @ -169,12 +166,12 @@ public class MarkwonConfiguration { | |||||||
|             this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags; |             this.htmlAllowNonClosedTags = configuration.htmlAllowNonClosedTags; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @NonNull | //        @NonNull | ||||||
|         @Deprecated | //        @Deprecated | ||||||
|         public Builder theme(@NonNull MarkwonTheme theme) { | //        public Builder theme(@NonNull MarkwonTheme theme) { | ||||||
|             this.theme = theme; | //            this.theme = theme; | ||||||
|             return this; | //            return this; | ||||||
|         } | //        } | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         public Builder asyncDrawableLoader(@NonNull AsyncDrawable.Loader asyncDrawableLoader) { |         public Builder asyncDrawableLoader(@NonNull AsyncDrawable.Loader asyncDrawableLoader) { | ||||||
| @ -263,11 +260,9 @@ public class MarkwonConfiguration { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         public MarkwonConfiguration build() { |         public MarkwonConfiguration build(@NonNull MarkwonTheme theme) { | ||||||
| 
 | 
 | ||||||
|             if (theme == null) { |             this.theme = theme; | ||||||
|                 theme = MarkwonTheme.create(context); |  | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             if (asyncDrawableLoader == null) { |             if (asyncDrawableLoader == null) { | ||||||
|                 asyncDrawableLoader = new AsyncDrawableLoaderNoOp(); |                 asyncDrawableLoader = new AsyncDrawableLoaderNoOp(); | ||||||
|  | |||||||
| @ -1,6 +1,7 @@ | |||||||
| package ru.noties.markwon; | package ru.noties.markwon; | ||||||
| 
 | 
 | ||||||
| import android.support.annotation.NonNull; | import android.support.annotation.NonNull; | ||||||
|  | import android.widget.TextView; | ||||||
| 
 | 
 | ||||||
| import org.commonmark.node.Node; | import org.commonmark.node.Node; | ||||||
| import org.commonmark.parser.Parser; | import org.commonmark.parser.Parser; | ||||||
| @ -42,7 +43,26 @@ class MarkwonImpl extends Markwon2 { | |||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public CharSequence markdown(@NonNull String input) { |     public CharSequence toMarkdown(@NonNull String input) { | ||||||
|         return render(parse(input)); |         return render(parse(input)); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void setMarkdown(@NonNull TextView textView, @NonNull String markdown) { | ||||||
|  |         setParsedMarkdown(textView, toMarkdown(markdown)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @Override | ||||||
|  |     public void setParsedMarkdown(@NonNull TextView textView, @NonNull CharSequence markdown) { | ||||||
|  | 
 | ||||||
|  |         for (MarkwonPlugin plugin : plugins) { | ||||||
|  |             plugin.beforeSetText(textView, markdown); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         textView.setText(markdown); | ||||||
|  | 
 | ||||||
|  |         for (MarkwonPlugin plugin : plugins) { | ||||||
|  |             plugin.afterSetText(textView, markdown); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -23,7 +23,7 @@ public interface MarkwonPlugin { | |||||||
|     @NonNull |     @NonNull | ||||||
|     String processMarkdown(@NonNull String markdown); |     String processMarkdown(@NonNull String markdown); | ||||||
| 
 | 
 | ||||||
|     void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder); |     void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown); | ||||||
| 
 | 
 | ||||||
|     void afterSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder); |     void afterSetText(@NonNull TextView textView, @NonNull CharSequence markdown); | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,14 +20,17 @@ public interface MarkwonVisitor extends Visitor { | |||||||
|         <N extends Node> Builder on(@NonNull Class<N> node, @NonNull NodeVisitor<N> nodeVisitor); |         <N extends Node> Builder on(@NonNull Class<N> node, @NonNull NodeVisitor<N> nodeVisitor); | ||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         MarkwonVisitor build(@NonNull MarkwonTheme theme, @NonNull MarkwonConfiguration configuration); |         MarkwonVisitor build(@NonNull MarkwonConfiguration configuration); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @NonNull | ||||||
|  |     MarkwonConfiguration configuration(); | ||||||
|  | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     MarkwonTheme theme(); |     MarkwonTheme theme(); | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     MarkwonConfiguration configuration(); |     SpannableFactory factory(); | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     SpannableBuilder builder(); |     SpannableBuilder builder(); | ||||||
|  | |||||||
| @ -37,19 +37,21 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | |||||||
| 
 | 
 | ||||||
|     private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes; |     private final Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes; | ||||||
| 
 | 
 | ||||||
|     private final MarkwonTheme theme; |  | ||||||
|     private final MarkwonConfiguration configuration; |     private final MarkwonConfiguration configuration; | ||||||
|  |     private final MarkwonTheme theme; | ||||||
|  |     private final SpannableFactory factory; | ||||||
|  | 
 | ||||||
|     private final SpannableBuilder builder = new SpannableBuilder(); |     private final SpannableBuilder builder = new SpannableBuilder(); | ||||||
| 
 | 
 | ||||||
|     private int blockQuoteIndent; |     private int blockQuoteIndent; | ||||||
|     private int listLevel; |     private int listLevel; | ||||||
| 
 | 
 | ||||||
|     private MarkwonVisitorImpl( |     private MarkwonVisitorImpl( | ||||||
|             @NonNull MarkwonTheme theme, |  | ||||||
|             @NonNull MarkwonConfiguration configuration, |             @NonNull MarkwonConfiguration configuration, | ||||||
|             @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { |             @NonNull Map<Class<? extends Node>, NodeVisitor<? extends Node>> nodes) { | ||||||
|         this.theme = theme; |  | ||||||
|         this.configuration = configuration; |         this.configuration = configuration; | ||||||
|  |         this.theme = configuration.theme(); | ||||||
|  |         this.factory = configuration.factory(); | ||||||
|         this.nodes = nodes; |         this.nodes = nodes; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -173,6 +175,12 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     @NonNull | ||||||
|  |     @Override | ||||||
|  |     public MarkwonConfiguration configuration() { | ||||||
|  |         return configuration; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public MarkwonTheme theme() { |     public MarkwonTheme theme() { | ||||||
| @ -181,8 +189,8 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | |||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
|     @Override |     @Override | ||||||
|     public MarkwonConfiguration configuration() { |     public SpannableFactory factory() { | ||||||
|         return configuration; |         return factory; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @NonNull |     @NonNull | ||||||
| @ -280,9 +288,8 @@ class MarkwonVisitorImpl implements MarkwonVisitor { | |||||||
| 
 | 
 | ||||||
|         @NonNull |         @NonNull | ||||||
|         @Override |         @Override | ||||||
|         public MarkwonVisitor build(@NonNull MarkwonTheme theme, @NonNull MarkwonConfiguration configuration) { |         public MarkwonVisitor build(@NonNull MarkwonConfiguration configuration) { | ||||||
|             return new MarkwonVisitorImpl( |             return new MarkwonVisitorImpl( | ||||||
|                     theme, |  | ||||||
|                     configuration, |                     configuration, | ||||||
|                     Collections.unmodifiableMap(nodes)); |                     Collections.unmodifiableMap(nodes)); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -9,17 +9,24 @@ import org.commonmark.node.BulletList; | |||||||
| import org.commonmark.node.Code; | import org.commonmark.node.Code; | ||||||
| import org.commonmark.node.Emphasis; | import org.commonmark.node.Emphasis; | ||||||
| import org.commonmark.node.FencedCodeBlock; | import org.commonmark.node.FencedCodeBlock; | ||||||
|  | import org.commonmark.node.HardLineBreak; | ||||||
|  | import org.commonmark.node.Heading; | ||||||
|  | import org.commonmark.node.Image; | ||||||
| import org.commonmark.node.IndentedCodeBlock; | import org.commonmark.node.IndentedCodeBlock; | ||||||
|  | import org.commonmark.node.Link; | ||||||
|  | import org.commonmark.node.ListBlock; | ||||||
| import org.commonmark.node.ListItem; | import org.commonmark.node.ListItem; | ||||||
| import org.commonmark.node.Node; | import org.commonmark.node.Node; | ||||||
| import org.commonmark.node.OrderedList; | import org.commonmark.node.OrderedList; | ||||||
|  | import org.commonmark.node.Paragraph; | ||||||
|  | import org.commonmark.node.SoftLineBreak; | ||||||
| import org.commonmark.node.StrongEmphasis; | import org.commonmark.node.StrongEmphasis; | ||||||
| import org.commonmark.node.Text; | import org.commonmark.node.Text; | ||||||
| import org.commonmark.node.ThematicBreak; | import org.commonmark.node.ThematicBreak; | ||||||
| 
 | 
 | ||||||
| import ru.noties.markwon.AbstractMarkwonPlugin; | import ru.noties.markwon.AbstractMarkwonPlugin; | ||||||
|  | import ru.noties.markwon.MarkwonConfiguration; | ||||||
| import ru.noties.markwon.MarkwonVisitor; | import ru.noties.markwon.MarkwonVisitor; | ||||||
| import ru.noties.markwon.SpannableBuilder; |  | ||||||
| import ru.noties.markwon.spans.OrderedListItemSpan; | import ru.noties.markwon.spans.OrderedListItemSpan; | ||||||
| 
 | 
 | ||||||
| public class CorePlugin extends AbstractMarkwonPlugin { | public class CorePlugin extends AbstractMarkwonPlugin { | ||||||
| @ -30,6 +37,25 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|     // todo: softBreak adds new line should be here (or maybe removed even?) |     // todo: softBreak adds new line should be here (or maybe removed even?) | ||||||
| 
 | 
 | ||||||
|  |     // todo: add a simple HTML handler | ||||||
|  |     // todo: configure primitive images (without okhttp -> just HttpUrlConnection and simple types (static, data) | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     public static CorePlugin create() { | ||||||
|  |         return create(false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     @NonNull | ||||||
|  |     public static CorePlugin create(boolean softBreakAddsNewLine) { | ||||||
|  |         return new CorePlugin(softBreakAddsNewLine); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private final boolean softBreakAddsNewLine; | ||||||
|  | 
 | ||||||
|  |     protected CorePlugin(boolean softBreakAddsNewLine) { | ||||||
|  |         this.softBreakAddsNewLine = softBreakAddsNewLine; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { |     public void configureVisitor(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|         text(builder); |         text(builder); | ||||||
| @ -43,11 +69,17 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|         orderedList(builder); |         orderedList(builder); | ||||||
|         listItem(builder); |         listItem(builder); | ||||||
|         thematicBreak(builder); |         thematicBreak(builder); | ||||||
|  |         heading(builder); | ||||||
|  |         softLineBreak(builder); | ||||||
|  |         hardLineBreak(builder); | ||||||
|  |         paragraph(builder); | ||||||
|  |         image(builder); | ||||||
|  |         link(builder); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Override |     @Override | ||||||
|     public void beforeSetText(@NonNull TextView textView, @NonNull SpannableBuilder builder) { |     public void beforeSetText(@NonNull TextView textView, @NonNull CharSequence markdown) { | ||||||
|         OrderedListItemSpan.measure(textView, builder); |         OrderedListItemSpan.measure(textView, markdown); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     protected void text(@NonNull MarkwonVisitor.Builder builder) { |     protected void text(@NonNull MarkwonVisitor.Builder builder) { | ||||||
| @ -65,7 +97,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) { |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull StrongEmphasis strongEmphasis) { | ||||||
|                 final int length = visitor.length(); |                 final int length = visitor.length(); | ||||||
|                 visitor.visitChildren(strongEmphasis); |                 visitor.visitChildren(strongEmphasis); | ||||||
|                 visitor.setSpans(length, visitor.configuration().factory().strongEmphasis()); |                 visitor.setSpans(length, visitor.factory().strongEmphasis()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -76,7 +108,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) { |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Emphasis emphasis) { | ||||||
|                 final int length = visitor.length(); |                 final int length = visitor.length(); | ||||||
|                 visitor.visitChildren(emphasis); |                 visitor.visitChildren(emphasis); | ||||||
|                 visitor.setSpans(length, visitor.configuration().factory().emphasis()); |                 visitor.setSpans(length, visitor.factory().emphasis()); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -95,7 +127,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|                 final int length = visitor.length(); |                 final int length = visitor.length(); | ||||||
|                 visitor.incrementBlockQuoteIndent(); |                 visitor.incrementBlockQuoteIndent(); | ||||||
|                 visitor.visitChildren(blockQuote); |                 visitor.visitChildren(blockQuote); | ||||||
|                 visitor.setSpans(length, visitor.configuration().factory().blockQuote(visitor.theme())); |                 visitor.setSpans(length, visitor.factory().blockQuote(visitor.theme())); | ||||||
|                 visitor.decrementBlockQuoteIndent(); |                 visitor.decrementBlockQuoteIndent(); | ||||||
| 
 | 
 | ||||||
|                 if (visitor.hasNext(blockQuote)) { |                 if (visitor.hasNext(blockQuote)) { | ||||||
| @ -122,7 +154,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|                         .append(code.getLiteral()) |                         .append(code.getLiteral()) | ||||||
|                         .append('\u00a0'); |                         .append('\u00a0'); | ||||||
| 
 | 
 | ||||||
|                 visitor.setSpans(length, visitor.configuration().factory().code(visitor.theme(), false)); |                 visitor.setSpans(length, visitor.factory().code(visitor.theme(), false)); | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -163,7 +195,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
| 
 | 
 | ||||||
|         visitor.builder().append('\u00a0'); |         visitor.builder().append('\u00a0'); | ||||||
| 
 | 
 | ||||||
|         visitor.setSpans(length, visitor.configuration().factory().code(visitor.theme(), true)); |         visitor.setSpans(length, visitor.factory().code(visitor.theme(), true)); | ||||||
| 
 | 
 | ||||||
|         if (visitor.hasNext(node)) { |         if (visitor.hasNext(node)) { | ||||||
|             visitor.ensureNewLine(); |             visitor.ensureNewLine(); | ||||||
| @ -220,7 +252,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|                     final int start = ((OrderedList) parent).getStartNumber(); |                     final int start = ((OrderedList) parent).getStartNumber(); | ||||||
| 
 | 
 | ||||||
|                     visitor.visitChildren(listItem); |                     visitor.visitChildren(listItem); | ||||||
|                     visitor.setSpans(length, visitor.configuration().factory().orderedListItem(visitor.theme(), start)); |                     visitor.setSpans(length, visitor.factory().orderedListItem(visitor.theme(), start)); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|                     // after we have visited the children increment start number |                     // after we have visited the children increment start number | ||||||
| @ -230,7 +262,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|                 } else { |                 } else { | ||||||
| 
 | 
 | ||||||
|                     visitor.visitChildren(listItem); |                     visitor.visitChildren(listItem); | ||||||
|                     visitor.setSpans(length, visitor.configuration().factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1)); |                     visitor.setSpans(length, visitor.factory().bulletListItem(visitor.theme(), visitor.listLevel() - 1)); | ||||||
| 
 | 
 | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
| @ -256,7 +288,7 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|                 // without space it won't render |                 // without space it won't render | ||||||
|                 visitor.builder().append('\u00a0'); |                 visitor.builder().append('\u00a0'); | ||||||
| 
 | 
 | ||||||
|                 visitor.setSpans(length, visitor.configuration().factory().thematicBreak(visitor.theme())); |                 visitor.setSpans(length, visitor.factory().thematicBreak(visitor.theme())); | ||||||
| 
 | 
 | ||||||
|                 if (visitor.hasNext(thematicBreak)) { |                 if (visitor.hasNext(thematicBreak)) { | ||||||
|                     visitor.ensureNewLine(); |                     visitor.ensureNewLine(); | ||||||
| @ -265,4 +297,132 @@ public class CorePlugin extends AbstractMarkwonPlugin { | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     protected void heading(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(Heading.class, new MarkwonVisitor.NodeVisitor<Heading>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Heading heading) { | ||||||
|  | 
 | ||||||
|  |                 visitor.ensureNewLine(); | ||||||
|  | 
 | ||||||
|  |                 final int length = visitor.length(); | ||||||
|  |                 visitor.visitChildren(heading); | ||||||
|  |                 visitor.setSpans(length, visitor.factory().heading(visitor.theme(), heading.getLevel())); | ||||||
|  | 
 | ||||||
|  |                 if (visitor.hasNext(heading)) { | ||||||
|  |                     visitor.ensureNewLine(); | ||||||
|  |                     visitor.forceNewLine(); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void softLineBreak(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(SoftLineBreak.class, new MarkwonVisitor.NodeVisitor<SoftLineBreak>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull SoftLineBreak softLineBreak) { | ||||||
|  |                 if (softBreakAddsNewLine) { | ||||||
|  |                     visitor.ensureNewLine(); | ||||||
|  |                 } else { | ||||||
|  |                     visitor.builder().append(' '); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void hardLineBreak(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(HardLineBreak.class, new MarkwonVisitor.NodeVisitor<HardLineBreak>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull HardLineBreak hardLineBreak) { | ||||||
|  |                 visitor.ensureNewLine(); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void paragraph(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(Paragraph.class, new MarkwonVisitor.NodeVisitor<Paragraph>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Paragraph paragraph) { | ||||||
|  | 
 | ||||||
|  |                 final boolean inTightList = isInTightList(paragraph); | ||||||
|  | 
 | ||||||
|  |                 if (!inTightList) { | ||||||
|  |                     visitor.ensureNewLine(); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 final int length = visitor.length(); | ||||||
|  |                 visitor.visitChildren(paragraph); | ||||||
|  | 
 | ||||||
|  |                 // @since 1.1.1 apply paragraph span | ||||||
|  |                 visitor.setSpans(length, visitor.factory().paragraph(inTightList)); | ||||||
|  | 
 | ||||||
|  |                 if (!inTightList && visitor.hasNext(paragraph)) { | ||||||
|  |                     visitor.ensureNewLine(); | ||||||
|  |                     if (visitor.blockQuoteIndent() == 0) { | ||||||
|  |                         visitor.forceNewLine(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void image(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(Image.class, new MarkwonVisitor.NodeVisitor<Image>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Image image) { | ||||||
|  | 
 | ||||||
|  |                 final int length = visitor.length(); | ||||||
|  | 
 | ||||||
|  |                 visitor.visitChildren(image); | ||||||
|  | 
 | ||||||
|  |                 // we must check if anything _was_ added, as we need at least one char to render | ||||||
|  |                 if (length == visitor.length()) { | ||||||
|  |                     visitor.builder().append('\uFFFC'); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 final MarkwonConfiguration configuration = visitor.configuration(); | ||||||
|  | 
 | ||||||
|  |                 final Node parent = image.getParent(); | ||||||
|  |                 final boolean link = parent instanceof Link; | ||||||
|  |                 final String destination = configuration | ||||||
|  |                         .urlProcessor() | ||||||
|  |                         .process(image.getDestination()); | ||||||
|  | 
 | ||||||
|  |                 final Object spans = visitor.factory().image( | ||||||
|  |                         visitor.theme(), | ||||||
|  |                         destination, | ||||||
|  |                         configuration.asyncDrawableLoader(), | ||||||
|  |                         configuration.imageSizeResolver(), | ||||||
|  |                         null, | ||||||
|  |                         link); | ||||||
|  | 
 | ||||||
|  |                 visitor.setSpans(length, spans); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     protected void link(@NonNull MarkwonVisitor.Builder builder) { | ||||||
|  |         builder.on(Link.class, new MarkwonVisitor.NodeVisitor<Link>() { | ||||||
|  |             @Override | ||||||
|  |             public void visit(@NonNull MarkwonVisitor visitor, @NonNull Link link) { | ||||||
|  |                 final int length = visitor.length(); | ||||||
|  |                 visitor.visitChildren(link); | ||||||
|  |                 final MarkwonConfiguration configuration = visitor.configuration(); | ||||||
|  |                 final String destination = configuration.urlProcessor().process(link.getDestination()); | ||||||
|  |                 visitor.setSpans(length, visitor.factory().link(visitor.theme(), destination, configuration.linkResolver())); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     private static boolean isInTightList(@NonNull Paragraph paragraph) { | ||||||
|  |         final Node parent = paragraph.getParent(); | ||||||
|  |         if (parent != null) { | ||||||
|  |             final Node gramps = parent.getParent(); | ||||||
|  |             if (gramps instanceof ListBlock) { | ||||||
|  |                 ListBlock list = (ListBlock) gramps; | ||||||
|  |                 return list.isTight(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,8 +20,8 @@ public class OrderedListItemSpan implements LeadingMarginSpan { | |||||||
|      * NB, this method must be called <em>before</em> setting text to a TextView (`TextView#setText` |      * NB, this method must be called <em>before</em> setting text to a TextView (`TextView#setText` | ||||||
|      * internally can trigger new Layout creation which will ask for leading margins right away) |      * internally can trigger new Layout creation which will ask for leading margins right away) | ||||||
|      * |      * | ||||||
|      * @param textView to which markdown will be applied |      * @param textView to which toMarkdown will be applied | ||||||
|      * @param text     parsed markdown to process |      * @param text     parsed toMarkdown to process | ||||||
|      * @since 2.0.1 |      * @since 2.0.1 | ||||||
|      */ |      */ | ||||||
|     public static void measure(@NonNull TextView textView, @NonNull CharSequence text) { |     public static void measure(@NonNull TextView textView, @NonNull CharSequence text) { | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ import android.text.Spanned; | |||||||
| 
 | 
 | ||||||
| public abstract class LeadingMarginUtils { | public abstract class LeadingMarginUtils { | ||||||
| 
 | 
 | ||||||
|  |     @SuppressWarnings("BooleanMethodIsAlwaysInverted") | ||||||
|     public static boolean selfStart(int start, CharSequence text, Object span) { |     public static boolean selfStart(int start, CharSequence text, Object span) { | ||||||
|         return text instanceof Spanned && ((Spanned) text).getSpanStart(span) == start; |         return text instanceof Spanned && ((Spanned) text).getSpanStart(span) == start; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -47,7 +47,7 @@ public class MainActivity extends Activity { | |||||||
| 
 | 
 | ||||||
|         final SpannableBuilder builder = new SpannableBuilder(); |         final SpannableBuilder builder = new SpannableBuilder(); | ||||||
| 
 | 
 | ||||||
|         // please note that here I am passing `0` as fallback it means that if markdown references |         // please note that here I am passing `0` as fallback it means that if toMarkdown references | ||||||
|         // unknown icon, it will try to load fallback one and will fail with ResourceNotFound. It's |         // unknown icon, it will try to load fallback one and will fail with ResourceNotFound. It's | ||||||
|         // better to provide a valid fallback option |         // better to provide a valid fallback option | ||||||
|         final IconSpanProvider spanProvider = IconSpanProvider.create(this, 0); |         final IconSpanProvider spanProvider = IconSpanProvider.create(this, 0); | ||||||
| @ -59,7 +59,7 @@ public class MainActivity extends Activity { | |||||||
|                         .headingTextSizeMultipliers(textSizeMultipliers) |                         .headingTextSizeMultipliers(textSizeMultipliers) | ||||||
|                         .build()) |                         .build()) | ||||||
|                 .build(); |                 .build(); | ||||||
|         // create an instance of visitor to process parsed markdown |         // create an instance of visitor to process parsed toMarkdown | ||||||
|         final IconVisitor visitor = new IconVisitor( |         final IconVisitor visitor = new IconVisitor( | ||||||
|                 configuration, |                 configuration, | ||||||
|                 builder, |                 builder, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov