Add fallbackToRawInputWhenEmpty configuration
This commit is contained in:
		
							parent
							
								
									c450765ab4
								
							
						
					
					
						commit
						d42ae41409
					
				| @ -9,11 +9,13 @@ | ||||
| * Expose `enabledBlockTypes` in `CorePlugin` | ||||
| * Update `jlatexmath-android` dependency ([#225]) | ||||
| * Update `image-coil` module (Coil version `0.10.1`) ([#244])<br>Thanks to [@tylerbwong] | ||||
| * Rename `UrlProcessor` to `ImageDestinationProcessor` (`io.noties.markwon.urlprocessor` -> `io.noties.markwon.image.destination`) and limit its usage to process **only** destination URL of images (was used to also process links before)  | ||||
| * Rename `UrlProcessor` to `ImageDestinationProcessor` (`io.noties.markwon.urlprocessor` -> `io.noties.markwon.image.destination`) and limit its usage to process **only** destination URL of images (was used to also process links before) | ||||
| * `fallbackToRawInputWhenEmpty` `Markwon.Builder` configuration to fallback to raw input if rendered markdown is empty ([#242])  | ||||
| 
 | ||||
| [#235]: https://github.com/noties/Markwon/issues/235 | ||||
| [#225]: https://github.com/noties/Markwon/issues/225 | ||||
| [#244]: https://github.com/noties/Markwon/pull/244 | ||||
| [#242]: https://github.com/noties/Markwon/issues/242 | ||||
| [@tylerbwong]: https://github.com/tylerbwong | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -192,6 +192,17 @@ public abstract class Markwon { | ||||
|         @NonNull | ||||
|         Builder usePlugins(@NonNull Iterable<? extends MarkwonPlugin> plugins); | ||||
| 
 | ||||
|         /** | ||||
|          * Control if small chunks of non-finished markdown sentences (for example, a single `*` character) | ||||
|          * should be displayed/rendered as raw input instead of an empty string. | ||||
|          * <p> | ||||
|          * Since $nap; {@code true} by default, versions prior - {@code false} | ||||
|          * | ||||
|          * @since $nap; | ||||
|          */ | ||||
|         @NonNull | ||||
|         Builder fallbackToRawInputWhenEmpty(boolean fallbackToRawInputWhenEmpty); | ||||
| 
 | ||||
|         @NonNull | ||||
|         Markwon build(); | ||||
|     } | ||||
|  | ||||
| @ -27,6 +27,9 @@ class MarkwonBuilderImpl implements Markwon.Builder { | ||||
| 
 | ||||
|     private Markwon.TextSetter textSetter; | ||||
| 
 | ||||
|     // @since $nap; | ||||
|     private boolean fallbackToRawInputWhenEmpty = true; | ||||
| 
 | ||||
|     MarkwonBuilderImpl(@NonNull Context context) { | ||||
|         this.context = context; | ||||
|     } | ||||
| @ -71,6 +74,13 @@ class MarkwonBuilderImpl implements Markwon.Builder { | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon.Builder fallbackToRawInputWhenEmpty(boolean fallbackToRawInputWhenEmpty) { | ||||
|         this.fallbackToRawInputWhenEmpty = fallbackToRawInputWhenEmpty; | ||||
|         return this; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Markwon build() { | ||||
| @ -114,7 +124,8 @@ class MarkwonBuilderImpl implements Markwon.Builder { | ||||
|                 parserBuilder.build(), | ||||
|                 visitorFactory, | ||||
|                 configuration, | ||||
|                 Collections.unmodifiableList(plugins) | ||||
|                 Collections.unmodifiableList(plugins), | ||||
|                 fallbackToRawInputWhenEmpty | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,8 @@ | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.text.SpannableStringBuilder; | ||||
| import android.text.Spanned; | ||||
| import android.text.TextUtils; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| @ -28,19 +30,25 @@ class MarkwonImpl extends Markwon { | ||||
|     @Nullable | ||||
|     private final TextSetter textSetter; | ||||
| 
 | ||||
|     // @since $nap; | ||||
|     private final boolean fallbackToRawInputWhenEmpty; | ||||
| 
 | ||||
|     MarkwonImpl( | ||||
|             @NonNull TextView.BufferType bufferType, | ||||
|             @Nullable TextSetter textSetter, | ||||
|             @NonNull Parser parser, | ||||
|             @NonNull MarkwonVisitorFactory visitorFactory, | ||||
|             @NonNull MarkwonConfiguration configuration, | ||||
|             @NonNull List<MarkwonPlugin> plugins) { | ||||
|             @NonNull List<MarkwonPlugin> plugins, | ||||
|             boolean fallbackToRawInputWhenEmpty | ||||
|     ) { | ||||
|         this.bufferType = bufferType; | ||||
|         this.textSetter = textSetter; | ||||
|         this.parser = parser; | ||||
|         this.visitorFactory = visitorFactory; | ||||
|         this.configuration = configuration; | ||||
|         this.plugins = plugins; | ||||
|         this.fallbackToRawInputWhenEmpty = fallbackToRawInputWhenEmpty; | ||||
|     } | ||||
| 
 | ||||
|     @NonNull | ||||
| @ -86,7 +94,18 @@ class MarkwonImpl extends Markwon { | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public Spanned toMarkdown(@NonNull String input) { | ||||
|         return render(parse(input)); | ||||
|         final Spanned spanned = render(parse(input)); | ||||
| 
 | ||||
|         // @since $nap; | ||||
|         // if spanned is empty, we are configured to use raw input and input is not empty | ||||
|         if (TextUtils.isEmpty(spanned) | ||||
|                 && fallbackToRawInputWhenEmpty | ||||
|                 && !TextUtils.isEmpty(input)) { | ||||
|             // let's use SpannableStringBuilder in order to keep backward-compatibility | ||||
|             return new SpannableStringBuilder(input); | ||||
|         } | ||||
| 
 | ||||
|         return spanned; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package io.noties.markwon; | ||||
| 
 | ||||
| import android.text.Spanned; | ||||
| import android.text.TextUtils; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import org.commonmark.node.Node; | ||||
| @ -50,7 +51,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.singletonList(plugin)); | ||||
|                 Collections.singletonList(plugin), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         impl.parse("whatever"); | ||||
| 
 | ||||
| @ -74,7 +77,9 @@ public class MarkwonImplTest { | ||||
|                 parser, | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Arrays.asList(first, second)); | ||||
|                 Arrays.asList(first, second), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         impl.parse("zero"); | ||||
| 
 | ||||
| @ -102,7 +107,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 visitorFactory, | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.singletonList(plugin)); | ||||
|                 Collections.singletonList(plugin), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         when(visitorFactory.create()).thenReturn(visitor); | ||||
|         when(visitor.builder()).thenReturn(builder); | ||||
| @ -149,7 +156,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 visitorFactory, | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.<MarkwonPlugin>emptyList()); | ||||
|                 Collections.<MarkwonPlugin>emptyList(), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         impl.render(mock(Node.class)); | ||||
| 
 | ||||
| @ -185,7 +194,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 visitorFactory, | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.singletonList(plugin)); | ||||
|                 Collections.singletonList(plugin), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         final AtomicBoolean flag = new AtomicBoolean(false); | ||||
|         final Node node = mock(Node.class); | ||||
| @ -224,7 +235,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class, RETURNS_MOCKS), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.singletonList(plugin)); | ||||
|                 Collections.singletonList(plugin), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         final TextView textView = mock(TextView.class); | ||||
|         final AtomicBoolean flag = new AtomicBoolean(false); | ||||
| @ -272,7 +285,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 plugins); | ||||
|                 plugins, | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         assertTrue("First", impl.hasPlugin(First.class)); | ||||
|         assertFalse("Second", impl.hasPlugin(Second.class)); | ||||
| @ -295,7 +310,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.singletonList(plugin)); | ||||
|                 Collections.singletonList(plugin), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         final TextView textView = mock(TextView.class); | ||||
|         final Spanned spanned = mock(Spanned.class); | ||||
| @ -339,7 +356,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 plugins); | ||||
|                 plugins, | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         // should be returned | ||||
|         assertNotNull(impl.requirePlugin(MarkwonPlugin.class)); | ||||
| @ -370,7 +389,9 @@ public class MarkwonImplTest { | ||||
|                 mock(Parser.class), | ||||
|                 mock(MarkwonVisitorFactory.class), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 plugins); | ||||
|                 plugins, | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         final List<? extends MarkwonPlugin> list = impl.getPlugins(); | ||||
| 
 | ||||
| @ -385,4 +406,42 @@ public class MarkwonImplTest { | ||||
|             assertTrue(e.getMessage(), true); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void fallback_to_raw() { | ||||
|         final String md = "*"; | ||||
| 
 | ||||
|         final MarkwonImpl impl = new MarkwonImpl( | ||||
|                 TextView.BufferType.SPANNABLE, | ||||
|                 null, | ||||
|                 mock(Parser.class, RETURNS_MOCKS), | ||||
|                 // it must be sufficient to just return mocks and thus empty rendering result | ||||
|                 mock(MarkwonVisitorFactory.class, RETURNS_MOCKS), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.<MarkwonPlugin>emptyList(), | ||||
|                 true | ||||
|         ); | ||||
| 
 | ||||
|         final Spanned spanned = impl.toMarkdown(md); | ||||
|         assertEquals(md, spanned.toString()); | ||||
|     } | ||||
| 
 | ||||
|     @Test | ||||
|     public void fallback_to_raw_false() { | ||||
|         final String md = "*"; | ||||
| 
 | ||||
|         final MarkwonImpl impl = new MarkwonImpl( | ||||
|                 TextView.BufferType.SPANNABLE, | ||||
|                 null, | ||||
|                 mock(Parser.class, RETURNS_MOCKS), | ||||
|                 // it must be sufficient to just return mocks and thus empty rendering result | ||||
|                 mock(MarkwonVisitorFactory.class, RETURNS_MOCKS), | ||||
|                 mock(MarkwonConfiguration.class), | ||||
|                 Collections.<MarkwonPlugin>emptyList(), | ||||
|                 false | ||||
|         ); | ||||
| 
 | ||||
|         final Spanned spanned = impl.toMarkdown(md); | ||||
|         assertTrue(spanned.toString(), TextUtils.isEmpty(spanned)); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Dimitry Ivanov
						Dimitry Ivanov